After extensive debugging and comparison of execution logs between the FPGA CPU and the results of Classic99 emulator with the same ROMs, I found and fixed four bugs, one of them being quite nasty to find. But the results were very pleasing, now with my own boot ROM and Defender cartridge loaded I get this picture (story continues after the picture):
For the first time the FPGA CPU renders the opening screen correctly! Interrupts were disabled (at hardware level) for this run.
Even more pleasing, I tested the bug fixes with the normal TI-99/4A ROMs, and got this boot picture for the very first time (story continues after the picture):
Personally this was a wow moment!
So what were the bugs? Three related to flags, and one to addressing modes:
- The logical greater than flag (ST0, also known as L>) was set incorrectly for the compare instructions (C and CB). Similarly the arithmetic greater than flag (ST1, also known as A>) was set incorrectly. I did not find this bug in the past, because in many scenarios the flags were set correctly. I had read sloppily the data sheet, and in the VHDL code I was had accidentally swapped the source argument and destination argument inputs in the flag setting code when comparing their MSBs to detect certain conditions.
- Related to the above, my flag setting code treated comparison (C and CB) and subtract (S and SB) instructions identically. For most CPUs this would be true, but for the TMS9900 family the aforementioned flags ST0 and ST1 rather strangely only compare against zero for the subtract instruction. So I modified the code to properly distinguish S and C instructions, this required a number of changes.
- In the data sheet The carry flag ST3 is documented for subtract instruction to be set when "CARRY OUT" is set. However, "CARRY OUT" is not defined anywhere. I used simply ALU output bit 16 (i.e. the 17th bit of the ALU) as carry. This is fine for addition instruction, but subtract actually inverts that bit. I guess in the original CPU implementation this was the most effective way to implement the ALU (normally done by inverting the number to be subtracted, tweaking carry so that an "add" operation becomes a "subtract").
- Hardest of all to find, I could not understand why the compare bytes instruction "CB R5, @>6049" in the defender game cartridge set flags incorrectly with my FPGA CPU. I modified my boot ROM to run this instruction among the very first instructions, so that I could check the behaviour both under simulation and actual FPGA by running only a few instructions - and it worked properly. But the same instruction much later on - as instruction 11 460, did not set the flags properly. This was a very hard bug to find, but I finally found the problem after adding ALU input debug registers and making them available for my debug software. I could see that in the latter instance this instruction was producing different ALU inputs, despite the actual inputs being exactly the same. I finally traced down this problem to the operation of the byte aligner. It used an internal register simply called "EA" for effective address to perform the alignment of input bytes i.e. conversion of an input byte to 16-bit ALU input. Now this register was not set at all if the source operand was a register operand, i.e. in this case R5. Thus the byte alignment was random and depended on whatever code was being run before. The problem was actually generic to all instructions in the TMS9900 instruction set that used byte operands.
After fixing all of the above the FPGA CPU runs the TI99/4A boot ROMs and renders the familiar boot picture! It then stops at address >0296 where it finds the opcode >3D06. This is a divide instruction, and the FPGA CPU does not support it yet, but rather simply stops and leaves the program counter pointing at the unimplemented instruction, making the problem easy to spot. I knew that this instruction was still not implemented, so I was happy to see that the reason for the CPU halting was the "right" one. Hopefully after implementing this last thing the CPU fully works and finally marks the implementation of the entire TI-99/4A computer on the FPGA!