Today and yesterday I had more time to work on the project. I refactored the code, learned some more VHDL. I also greatly improved my workflow by creating a python script which takes a TMS9900 binary file and spits out the definition of a 64 word ROM in VHDL with the code. This allows for very quickly (10 seconds or so) code changes and simulation reruns, without any manual work.
The TMS9900 supports a bunch of single operand instructions (i.e. the source and destination are the same, for example):
Here the R1 register is incremented, so the source is R1 and destination is also R1. I refactored the VHDL code to calculate the effective address of the source operand and also properly handling all side effects, allowing the effective address to be used twice after operand calculation (once for value read, second time for result write, in between there is computation).
Now I added support for all of the addressing modes for the single operand instructions, so all of the following work (tested with the CLR instruction, which clears the operand). The asterisk * is the comment in TMS9900 assembler, but also used to flag indirect operations:
CLR R5 * Clear R5 CLR *R5 * Clear memory word pointed to by R5 CLR *R5+ * As above, also increment R5 by 2 to point to next word CLR @MEM1 * Clear the word with the 16-bit address MEM1 CLR @4(R5) * Clear the word in the address R5+4There are 14 single operand instructions, I implemented all of them except one, the BLWP instruction, which probably will be the next one. So as additional instructions I now have (with the full suite of address modes):
B @LABEL * Jump to 16-bit address LABEL BL @LABEL * As above, but with link: PC stored to R11 first CLR R4 * Clear R4 SETO *R5 * Set memory word at address R5 to >FFFF INV R9 * Invert bits of R9 NEG R10 * Negate R10 (i.e. 0-R10) ABS R10 * Take the absolute value of R10 SWPB R5 * Swap bytes of R5 INC R1 * Increment R1 by 1 INCT R1 * Increment R1 by 2 DEC R1 * Decrement R1 by 1 DECT R1 * Decrement R1 by 2 X R3 * Execute the opcode in R3 (UNTESTED)Some things to note from above:
- All instructions above support all 5 addressing modes (although for B and BL the direct register operand does not really make sense)
- BL is a subroutine call. TMS9900 does not support a hardware stack. Instead the previous PC is stored to register R11.
- The absolute jump instruction B can be used to implement a return from subroutine, by B *R11. With these two the core can now handle subroutines. Although only one level can be handled, or R11 has to be stored elsewhere.
- The almighty BLWP instruction is not yet done. This stores not only the PC, but also the workspace pointer W and status register ST, but I don't have that support yet :)