After I convinced myself that the emulator was probably OK, I turned back to the ROM. But after I found yet another ROM image online, and it turned out to be identical to the other three, I have to rule out bad ROM as well. Just as well as I was reluctant to put my original CHESSmate at risk.
So that means that my emulation of the CHESSmate hardware must be wrong. However, as far as I could tell, I was handling all of the reads and writes from the firmware. I knew this because I had code in place to detect unhandled I/O. And that would have been a valid assumption if I had put the unhandled code in the right place!
Whoops. Embarrassing. When I relocated the code to the correct spot I discovered that I had missed a few interactions.
Number | R/W | Program Counter | Address | Value | Notes |
---|---|---|---|---|---|
1 | R | F390 | 8B0E | Is Timer Interrupt? | Occurs when a move is made |
2 | W | F0B2 | 8B0F | F5 Set Interrupt | Occurs when move is made |
3 | R | FEF0 | 8009 | Doesn't matter | Occurs constantly. Part of the main keyboard / display loop. |
4 | R | F305 | 8xxx | FF | Occurs when move made. |
5 | R | F309 | 8xxx | FF | Occurs when move made. |
So what does this mean?
1, 2 - 8B0E and 8B0F are part of the RRIOT Control Registers. I was surprised that I was not picking these up. When I looked at the code I found my second error.
#define RRIOT_REG_SIZE 12
There are 16 registers not 12. The 8B0E and 8B0F registers control the RRIOT's Timer. Because of my error, I had not seen these before so I thought that CHESSmate did not use the timer. I was wrong. I did notice that the chess clock feature was not working though. So I fixed the define and am now processing these interactions but will have to spend some time figuring out how they should work. TODO.
3 - I'm not sure exactly what this is doing. It's related to this piece of code:
FEEB 29 7F AND #$7F
FEED 2C .byte $2C ; BIT skip next instruction
FEEE 09 80 LFEEE ORA #$80
FEF0 A0 00 LDY #$00 ; lookup conversion
FEF2 8C 00 8B STY SAD ; turn off segments
FEF5 8E 02 8B STX SBD ; output digit enable
FEF8 8D 00 8B STA SAD ; output segments
FEFB E8 INX
FEFC A0 7F LDY #$7F ; delay 500 cycles
FEFE 88 CONVD1 DEY
FEFF D0 FD BNE CONVD1
FF01 60 RTS
The 2C 09 80 sequence is treated as a BIT operation on the absolute address 8009. This sets the Negative flag in status to bit-7 of the byte read, and the Overflow flag in status to bit-6 of the byte read. Since there are no subsequent branch instructions I'm pretty sure this sequence has no effect on the code. This looks like a patch of some sort. Ignored.
4, 5 - These occur when a move is being made. The address is in the 8xxx range but varies quite a bit. They are associated with this piece of code.
F303 B1 76 LF303 LDA ($76),Y
F305 48 PHA
F306 C8 INY
F307 B1 76 LDA ($76),Y
F309 85 2A STA $2A
F30B 68 PLA
F30C A2 11 LDX #$11
So I do the sequence H - ENTER (switch CHESSmate to WHITE), G - ENTER (CHESSmate MOVES). If I return a 0 to these two reads CHESSmate fails returning the move H1 - H1. If I return FF to the reads, CHESSmate plays E2 - E4, the same as the original game. I think that this has something to do with the opening book. I'll have to dig in a bit to figure it out. TODO
The bottom line here is that with these changes the game plays pretty well now. I played a couple of full games today with no issues, from the opening move to the annoying musical sequence CHESSmate plays when it wins (both games sigh).
I still have some work to do but I'm much more confident now of a great result.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
The .byte $2C is just a little hack to allow two distinct entry points into the following LDY etc. code. A BPL $FEF0 would have done essentially the same thing but would have consumed an additional precious byte with its $10 $02. When literally every byte counts, pulling a little trick like that can sometimes mean the difference between squeezing in a feature and having to abandon it.
Are you sure? yes | no
Re 3: Since the 2C takes in a couple of instructions following my theory is it was used to null an erroneous opcode there but NOP couldn't be used because they could only flip 1-bits to 0 to avoid having to erase EPROMs, not possible with OTP ones anyway.
Are you sure? yes | no