eswartz / emul

Emulator (V9t9 and others)
Other
28 stars 4 forks source link

Validate odd PC behavior in Bigfoot #10

Open eswartz opened 9 years ago

eswartz commented 9 years ago

The Bigfoot cartridge exhibits some bugs if the emulator does not properly ignore the LSB of the PC.

By Tursi / Mike:

Found it. I’ll write up a technical explanation that you can share, please.

Bigfoot’s a somewhat buggy program that relies heavily on undocumented side effects. I wonder if it would work at all on a 9995? It’s also very strangely programmed, and mixes code and data indiscriminately.

The good news (from my perspective) is that this answers a hardware question I was waiting to get home to test, about whether the CPU preserves the LSB of the address counter. It does not!

The problem is that the code is very fond of mixing up code and data addresses. In particular, the text display routine emits a string of text, then branches to the address immediately after the text. This address is VERY frequently odd. On the real 9900, the least significant bit is ignored, and the code is executed correctly. This is also true in Classic99 (and JS99er and likely V9T9).

Where this falls down is here:

A3C9 06A0 bl @>a89c

A89C C03B mov *R11+,R0

A89E C0BB mov *R11+,R2

A8A0 C3CB mov R11,R15

A8A2 06A0 bl @>a9dc

A8A6 D83F movb *R15+,@>8c00

This is a little confusing, but what happens is that a block of code was executing at odd addresses (A3C9). This was working correctly and fetching the correct (even) words, and everything was okay. Then the BL occurred.

R11 got the /odd/ address for the return address. Again, this would normally be no big deal, because the CPU fetches would still get the correct even addresses. The MOVs at A89C and A89E both do the correct thing with the odd R11, fetching the correct even data. Then we move R11 to R15 so we can branch to A8A6, which is the actual display routine.

Now R15 is fetching data with a 1 byte offset, and because it’s using bytes, we see that onscreen. Worse than that, at the end of the display loop, this happens:

A8AA 0602 dec R2

A8AC 15FC jgt >a8a6

A8AE 058F inc R15

A8B0 045F b *R15

A3E6 A89C a *R12,@>0128(R2)

A3EA 0011 data >0011

A3EC 484F szc R15,@>5720(R1)

R2 correctly counts down the number of bytes to display, then we increment R15 one last time and branch directly to it! The problem is, because we started off by one, we’re actually now off by two, and we branch to the wrong address – we should have branched one word earlier (because the code was expecting to end up with an odd number, and have it ignored by the system). This is why there is an illegal opcode at A3EA, it’s actually part of a different instruction.

The fix is simple – the Program Counter in the 9900 is 15-bits wide and should never include the LSB. It’s funny this topic came up recently at AA since MESS apparently has it right. :) To recap: the bug is that an odd PC is being stored in R11 for BL (presumably BLWP and interrupt calls would have a similar bug). The best fix is to just strip the least significant bit from the PC in all cases.

It is highly likely, though I don’t have hardware to test, that the Workspace Pointer is also 15 bits wide.

Please feel free to share this with the other affected emulator authors.