I've let some of the work I finished pile up, so here are several updates:
I redesigned the memory, which now uses a 16 bit address, and provides 4KiB of ROM, 4KiB of RAM, and 4B of I/O (maybe need some more). The upper byte of the address is determined by the page address register (r7). The lower byte is the same as the old method, 8 bit immediate plus the base address register specified in the opcode. This is (sometimes) annoying because it takes two instructions to access a location in memory. Most of the time, however, there is only one instruction to set r7, and each memory access requires only one instruction. This occurs when the data being accessed is somewhat densely packed in memory. When the data is spread out, then it takes about 2 instructions on average to access each byte.
I also decided to scrap the receive portion of the spi controller, and finished up the send portion. I did this because currently, I have no use for spi inputs (but probably will soon), and so it was difficult to determine exactly how I wanted the controller to behave.
Finally, I added several new instructions to the instruction set. I discovered that it was going to be a problem to write a program with lots of simple loops without a load immediate instruction. Before the load immediate instruction, each parameter of each loop was stored in a separate memory address. This made it rather complicated to write programs with more than just a few loops. This instruction, out of all of my other ones, is the least faithful to the MIPS instruction set. To load an immediate value in MIPS, one simply uses one of the many i-type instructions that MIPS supports and specifies r0 as the second operand (the immediate being the first one). However, due to my shortened instruction word, I only have 2 bits for opcode, which allows for 4 instructions. 2 of these are used by loads and stores, and the 3rd is used by r-type instructions, which have a 5 bit function code (32 functions). However, the load immediate requires an immediate value in the instruction word (duh...). To increase the amount of functions supported by an instruction with an immediate value, the second register address is used as another opcode (functionality that MIPS actually employs in some of its branch relative to zero instructions). I used this register field for my final (opcode of 0b10) instruction type. Prior to this iteration, I used 6 of the 8 available function codes with a 3 bit register address for branches. Now, the 7th function code is used for load immediates. Phew...hopefully that wasn't too boring. Upload to github coming soon.
On to screenshots:
Assembler with syntax highlighting and improved refresh features: i.e. An asterisk appears on either side of the filename at the top of the window when a key is pressed after the file has been saved or a new one has been opened. However, this includes arrow keys and nonprinting keys, so I'll probably fix this soon.
iSim waveform of SPI master (again, more for show than actual documentation):