I'm going to be quiet for a little while. Good things are happening on the backend. We actually ran into a couple of issues on hardware (and not the emulators) which was a little scary for a bit, but we were able to recover, though I'm still retooling one final hack in that regard.
Since Piratero is going to ask, the key issues are the following things emulators apparently don't account for:
- Byte alignment: I don't fully get this as the emulators respect the fact that MIPS won't load unaligned words or half-words, so it shouldn't have worked in the emulators, but it somehow did. At any rate, we had to make sure the patching we were doing led to byte aligned data blocks (i.e. all locations in memory should start at locations which are multiples of 0x04, like 0x00, 0x04, 0x08, 0x0C, 0x10, etc.) Once we figured out the problem, it wasn't a terribly hard fix.
- Pipelining: This was huge. I'll just post what I wrote to Artemio when we figured it out:
I think the problem is pipelining.
The following instruction set failed for me:
addiu r20, r0, 0x006E ; r20 = 6E, the control code
...
lbu r2, 0x0000(r16) ; read a byte
beq r2, r20, 0x0005; if the byte read == 6E, jump ahead
The branch was failing even when I
knew the data was a 6E and should have been succeeding.
This fixed it:
lbu r2, 0x0000(r16) ; read a byte
nop
beq r2, r20, 0x0005; if the byte read == 6E, jump ahead
Throwing a nop in there meant the branch was now true. Why?
I think because the MIPS architecture pipelines the instructions. In an emulator "lbu" happens atomically, i.e. it definitely begins and ends before the next instruction is processed.
In a real chip, though, it's doing stuff in parallel, so:
(Fetch instruction, Instruction decode, Execute, Memory access, Writeback - in previous example, lbu is doing memory access simultaneously while bne is actually executing.)
So the branch executes (E) while the previous instruction is doing memory access (M). In other words, the branch fails because the register hasn't actually been loaded with memory yet. The nop makes it so:
(In this example, the lbu is doing a Writeback to memory - which is effectively nothing, since that only counts for a "store" instruction - while the branch executes. Memory access has now happened while the branch was decoding instructions.)
So NOW the instruction has "time" to do memory access. I rewrote the in-game text hack with some extra nops after memory reads and hoping that it magically makes it all work.
(It did. Right now, we're trying to recompile the LZO decompressor so that it will pipeline correctly in the PSX - i.e. all the modified graphics work.)