erc an emulator for retro computers

Small changes, large effects

The most interesting thing, programming erc, is that small mistakes in understanding can have large, difficult-to-debug effects. To set the stage, I have been able to load software from the disk drive abstraction built into the emulator, and I can “boot” that software the way an Apple II would do; but I had not been able to read any other data. Or, rather, I should say the software was trying to, but managed to catch itself in an infinite loop inside the RWTS method that reads data from the disk. Clearly, that shouldn’t be happening, so what did I miss?

In these cases, I tend to go back over the implementation of the assembly codes for the processor. Something I’m doing isn’t right; and, it turns out, that was so: subtraction (via the SBC instruction) was removing more than it should be when carry was set, and less when it wasn’t. Oops.

And, suddenly, the infinite loop ended. But now I had a different problem; it seemed like I could read data from track 0, but couldn’t move to any other track. My tests showed that I could move the disk drive head using the right motor phases, but the software was never activating the motor phases in the right way to do so.

So now what? I began poring over the code each instruction executes; it occurred to me that status flag changes, which were handled with the mos6502_modify_status() function, were communicating the results of operations with an int. This was the Right Thing to see if carry should be set, because in many cases, carry is set if the result would require a value greater than a byte can hold.1

But it was the Wrong Thing to properly measure other conditions. Things that, in a real MOS CPU chip, would overflow 8 bits and wrap around, were not always doing so in the way expected. Suppose you had $01 in the A register, and you do SBC #$02. In an int variable, you would get -1, but aligned to 32 bits. Even if you took that result and masked it to just a byte (e.g. result & 0xff), you wouldn’t see the sign bit set. With an 8bit type, you would see the value go from $01 to $FF, which is what software written for the Apple II would expect.

So I spent some time last evening to change that throughout the app, and voilá: my software that I’m loading is actually moving the head to track 1 and to track 2.

Slowly but surely, we’re getting there.

  1. Carry is an unusual property in the MOS 6502 chip family. Different instructions imply different carry conditions; carry is set in ADC if the result is greater than $FF, but it is set in CMP if the A register is greater than DATA (for whatever you are comparing); in LSR, carry is set of bit 0 is set; in ASL, carry is set of bit 7 is set; context matters, and it’s hard to codify the meaning of that bit in a neutral function.