-
A Software Only CHESSmate
04/20/2024 at 14:46 • 0 commentsI said I was not going to jump into a software only CHESSmate right away but it turns out I couldn't resist.
My starting point was a Python based 6502 emulator that I had used for my Challenger 4P project. My decision was based on Python's portability, my current infatuation with Python, and my positive experience with this particular 6502 emulator (solid).
It turns out that making a software only emulation was more challenging than I thought it would be. With my hardware reproduction, the CHESSmate emulation consisted of observing the CHESSmate program's interaction with the 6530-0024 RRIOT chip (which controlled all the I/O to the 7-Segment displays, buttons, buzzer, and LEDs) and mapping those interactions the to the correct ESP-32 I/O lines that controlled my reproduction hardware. When I finally got the mapping correct everything just worked.
A software emulation is a different beast. In this case, you have to map the CHESSmate program's interaction with the 6530-0024 RIOT chip to an on screen CHESSmate representation. There are three major areas that have to be addressed, the Display, Keyboard, and Sound.
Display
Since I already had an Inkscape created SVG file for the CHESSmate "membrane", the first part of creating a program was pretty easy. I'm using PyGame to create the software interface. With a few lines of code I was able to create a "window" and load the membrane SVG as a background image. Great start, but it's just a static image. To bring to life, I had to be able to light up the on screen 7-Segment displays and the LEDs.
For the 7-Segment displays, my first thought was to create graphics for each of the seven segments and "light them up" by drawing them on the background based directly on the which lines of the virtual 6530-0024 RRIOT chip were enabled, but this felt like a lot of complexity. Then I discovered that there was a 7-Segment TrueType font available. I ended up downloading the file DSEG7ClassicMini-BoldItalic.ttf and using PyGame's built in font engine to render the display characters all at once based on the which seven virtual 6530-0024 RRIOT chip lines were enabled.
The LEDs were much simpler. Just draw a red circle in the appropriate spot and size when the RRIOT code indicated that a particular LED was to be lit.
For both the 7-Segment displays and LEDs, at system startup before anything had been written to the background image, I captured and saved small image "patches" of the areas that were to be overdrawn, and bitted them back to the screen to "erase" the character or LED when required.
Keyboard
For the keyboard I recorded the coordinates of the 19 on screen buttons, and monitored PyGame mouse click events to see if they were within one of the button rectangles when a click occurred. It was then relatively easy to map those clicks back to the appropriate RRIOT register response. In addition I used PyGame keyboard events to also map the following physical keys: A, B, C, D, E, F,G, H, 1, 2, 3, 4, 5, 6, 7, 8, Return (ENTER), Backspace (CLEAR) and Esc (NEW GAME).
Sound
Sound turned out to be the most work. With the hardware emulation I just had to connect the two leads of a piezo "buzzer" to two RRIOT mapped pins. Done. With software emulation I ended having to create WAV files for each of the 14 unique sounds that CHESSmate uses. To determine when a sound was starting I monitored the two RRIOT sound pins for "activity" (one or the other pin set high). Note that when no sound was playing both pins would be set low. The sound to be played was set as a number in a zero page data byte ($69). Based on that number I used the PyGame sound library to play the appropriate wave file. Calls to play a sound return immediately and the sounds play asynchronously with continued program execution (nice). To determine when the CHESSmate program is finished playing the sound the activity on the RRIOT sound pins will stop (both low) for at least 5ms and the emulation is reset to wait for the next sound.
CHESSmate Software
The CHESSmate program can be found on GitHub. Simple copy all of the files in the Python folder into a folder on your target machine and run:
python main.py
I believe that the only dependency is that you must have PyGame installed. I've tested the program on Windows and the latest Pi OS using Python 3.x and PyGame 2.1.2.
If you don't want to build the CHESSmate hardware but would like to check out a CHESSmate "experience" this is your best bet.
Caveats
- The program runs fine on my 6 year old Acer Aspire i7 laptop, but it's a little sluggish on my 12 year old Acer Aspire i5 based laptop. Similarly CHESSmate runs a bit slow on my Raspberry Pi 4. I didn't try, but I suspect that PyPy would help with this.
- I have not yet implemented the chess clock feature.
- At some point I still intend to add a proper chess board to the interface.
-
Another Minimalist Frame
03/16/2024 at 14:56 • 0 commentsI had already designed an alternate frame to support the CHESSmate should one not want to go the the extra work of printing the reproduction's case. Problem is I didn't like it and it wasn't growing on me. Too rushed, too clunky. So I took another run at it and this is what I came up with.
It's even more minimal and IMHO more ascetically pleasing.
I populated a second CHESSmate PCB and attached it to the new frame with eight M3 x 6 mm bolts and nuts.
This time I used bare buttons without the caps to accentuate the minimalist vibe. I'm much happier with this version.
-
Final Assembly
03/14/2024 at 16:35 • 4 commentsThe new and improved backpack PCB arrived. Better late than never I guess. I started by soldering on some headers.
I soldered a couple of female straight headers to hold the ESP-32, a couple of male pins to connect to the +5V and GND on the ESP, and a 90 degree Female header to attach the backpack to the CHESSmate PCB. In the rightmost image I have mounted the ESP-32 onto the board.
With the backpack ready I attached the stack comprised of the:
- Membrane
- 3D printed standoff with cutouts for the displays, LEDs, and buttons
- CHESSmate PCB
to the case with eight M3 x 14mm bolts.
Then I mounted the backpack CPU.
Once it was in place I connected the micro USB connector power leads to the backpack PCB and mounted the piezo speaker to the case with two sided tape.
Popped the bottom of the case on, applied power, and viola a working CHESSmate reproduction.
-
Lost In Translation
03/06/2024 at 15:21 • 0 commentsMy replacement backpack PCBs appear to be lost somewhere between Memphis, TN and me. Sigh. So I decided while I wait to try and use one of my messed up boards after all.
The tricky part was to carefully pry the leads on the ESP-32 apart an additional 2.54 mm. Since they are now at an angle, getting them into the holes on the PCB was tricky and they ended up being pretty shallowly in place, which made soldering difficult. But I got it done and then proceeded to fix the power traces that I had crossed.
I had to install the 90 degree header such that the CPU board would extend straight back from the CHESSmate board so that I could solder the jumper wires.
The alternative is to install the header rotated 180 degrees so that the daughter board tucks underneath the CHESSmate board. I think that I kind of like the setup as pictured above where you can see all of the components including the ESP-32 board. For installing inside the CHESSmate case I will probably go the other way.
-
Messing Up
02/26/2024 at 17:37 • 4 commentsI'll admit that when I get close to finishing a project I can get a little careless. That was the case when I sent the CPU backpack daughter board in for fabrication. There were two issues.
- The footprint that I used for the ESP-32 WROOM development board was wrong. The two rows of 19 pins were 2.45 mm too far apart. No excuses, I usually print a PCB layout on paper when done and set the parts on top to check this. This time I did not and got burned. I love the ESP-32 but I get that distinct impression that there is a lot more variance between boards than there is in the Arduino world. Be careful.
- Worse perhaps is that I reversed the VCC and GND lines going to the CHESSmate PCB header. If this was the only problem I could have salvaged the boards with a couple of trace snips and some jumper wires, but with the pins offset wrong there's no point.
At any rate here is the fixed PCB that I just sent off for fabrication.
Sigh. At least I was able to add a power header to the board that I missed in the first version.
-
Re Openings
02/18/2024 at 16:32 • 0 commentsIn a previous log I said, "I'm not going to overthink this. Random selection from the chess openings is working.". Well I lied. I am going to overthink this. (Well at least think it.)
Something I should have done when working on the opening moves was to actually see what they were. So I dumped the 32 chess openings in a format I could read. Here is the Python script I used:
col_table = ['H','G','F','E','D','C','B','A'] row_table = ['1','2','3','4','5','6','7','8'] with open("Opening Book.bin", 'rb') as f: buffer = f.read() # For each line. ent_file = [] even = 0 address = 0x8C00 for i in range(0,len(buffer),2): if i % 32 == 0: ent_file.append("\n") ent_file.append(hex(address)) ent_file.append(": ") address += 32 # Assume bytes are contiguous. if (even % 2) == 0: ent_file.append(col_table[buffer[i]&0x0F]+row_table[buffer[i]>>4]+"-"+ col_table[buffer[i+1]&0x0F]+row_table[buffer[i+1]>>4] +", ") else: ent_file.append(col_table[7-(buffer[i]&0x0F)]+row_table[7-(buffer[i]>>4)]+"-"+ col_table[7-(buffer[i+1]&0x0F)]+row_table[7-(buffer[i+1]>>4)] +", ") even += 1 ent_file.append('\n') # Output the result. with open("Opening Book Dump.txt", 'w') as f: f.write(''.join(ent_file))
It wasn't to hard to figure out the format.
8c00: E2-E4, E7-E5, G1-F3, B8-C6, B1-C3, G8-F6, F1-B5, F8-B4, E1-G1, E8-G8, D2-D3, D7-D6, C1-G5, B4-C3, B2-C3, D8-E7 8c20: D2-D4, D7-D5, C2-C4, D5-C4, G1-F3, G8-F6, E2-E3, E7-E6, F1-C4, C7-C5, E1-G1, A7-A6, D1-E2, B8-C6, B1-C3, C5-D4 8c40: F2-F4, D7-D5, E2-E3, G8-F6, G1-F3, C7-C5, B2-B3, E7-E6, C1-B2, B8-C6, F1-B5, C8-D7, E1-G1, F8-D6, D2-D3, D8-C7 8c60: E2-E4, E7-E5, F1-C4, G8-F6, D2-D4, E5-D4, G1-F3, F6-E4, D1-D4, E4-F6, C1-G5, F8-E7, B1-C3, C7-C6, E1-C1, D7-D5 8c80: E2-E4, C7-C5, G1-F3, B8-C6, D2-D4, C5-D4, F3-D4, G8-F6, B1-C3, D7-D6, F1-E2, G7-G6, C1-E3, F8-G7, E1-G1, E8-G8 8ca0: E2-E4, C7-C5, B1-C3, B8-C6, G2-G3, G7-G6, F1-G2, F8-G7, D2-D3, E7-E6, C1-E3, D7-D6, G1-E2, C6-D4, E1-G1, G8-E7 8cc0: E2-E4, E7-E5, G1-F3, B8-C6, F1-C4, F8-C5, C2-C3, G8-F6, D2-D4, E5-D4, C3-D4, C5-B4, B1-C3, F6-E4, E1-G1, E4-C3 8ce0: G1-F3, G8-F6, C2-C4, C7-C5, D2-D4, C5-D4, F3-D4, E7-E6, B1-C3, F8-B4, C1-D2, E8-G8, E2-E3, B8-C6, F1-E2, D7-D5 8d00: E2-E4, E7-E5, G1-F3, G8-F6, F3-E5, D7-D6, E5-F3, F6-E4, D2-D4, D6-D5, F1-D3, F8-D6, E1-G1, E8-G8, C2-C4, C7-C6 8d20: D2-D4, G8-F6, C2-C4, E7-E6, G2-G3, D7-D5, F1-G2, D5-C4, D1-A4, B8-D7, A4-C4, A7-A6, G1-F3, B7-B5, C4-C6, A8-A7 8d40: E2-E4, G8-F6, E4-E5, F6-D5, D2-D4, D7-D6, C2-C4, D5-B6, F2-F4, D6-E5, F4-E5, B8-C6, C1-E3, C8-F5, B1-C3, E7-E6 8d60: D2-D4, F7-F5, C2-C4, E7-E6, G1-F3, G8-F6, G2-G3, F8-E7, F1-G2, E8-G8, E1-G1, D7-D5, B1-C3, C7-C6, C1-F4, D8-E8 8d80: E2-E4, E7-E5, G1-F3, B8-C6, F1-C4, G8-F6, D2-D4, E5-D4, E1-G1, F6-E4, F1-E1, D7-D5, C4-D5, D8-D5, B1-C3, D5-A5 8da0: D2-D4, G8-F6, C2-C4, E7-E6, B1-C3, F8-B4, D1-C2, B8-C6, G1-F3, D7-D6, C1-D2, E6-E5, A2-A3, B4-C3, D2-C3, D8-E7 8dc0: E2-E4, B8-C6, D2-D4, D7-D5, E4-D5, D8-D5, G1-F3, E7-E5, B1-C3, F8-B4, C1-E3, C8-G4, F1-E2, E8-C8, E1-G1, D5-A5 8de0: D2-D4, D7-D5, C2-C4, E7-E6, B1-C3, G8-F6, C1-G5, B8-D7, G1-F3, F8-B4, C4-D5, E6-D5, E2-E3, C7-C5, F1-D3, D8-A5 8e00: D2-D4, D7-D5, C2-C4, C7-C6, G1-F3, G8-F6, B1-C3, D5-C4, A2-A4, C8-F5, F3-E5, B8-D7, E5-C4, D8-C7, G2-G3, E7-E5 8e20: E2-E4, E7-E5, G1-F3, B8-C6, F1-B5, D7-D6, D2-D4, C8-D7, B1-C3, G8-F6, E1-G1, F8-E7, F1-E1, E5-D4, F3-D4, E8-G8 8e40: D2-D4, D7-D5, C2-C4, E7-E6, B1-C3, C7-C5, C4-D5, E6-D5, G1-F3, B8-C6, G2-G3, G8-F6, F1-G2, C5-D4, F3-D4, F8-C5 8e60: E2-E4, E7-E5, G1-F3, B8-C6, F1-B5, A7-A6, B5-C6, D7-C6, D2-D4, E5-D4, D1-D4, D8-D4, F3-D4, C8-D7, B1-C3, E8-C8 8e80: D2-D4, C7-C5, D4-D5, D7-D6, C2-C4, G7-G6, B1-C3, F8-G7, E2-E4, G8-F6, F1-E2, E7-E6, C1-G5, E8-G8, G1-F3, E6-D5 8ea0: E2-E4, E7-E5, G1-F3, B8-C6, D2-D4, E5-D4, F3-D4, G8-F6, B1-C3, F8-B4, D4-C6, B7-C6, F1-D3, D7-D5, E4-D5, C6-D5 8ec0: D2-D4, G8-F6, C2-C4, G7-G6, B1-C3, F8-G7, E2-E4, D7-D6, F2-F3, E7-E5, D4-D5, E8-G8, C1-G5, H7-H6, G5-E3, F6-H5 8ee0: E2-E4, C7-C6, D2-D4, D7-D5, B1-C3, D5-E4, C3-E4, C8-F5, E4-G3, F5-G6, H2-H4, H7-H6, G1-F3, B8-D7, F1-D3, G6-D3 8f00: D2-D4, G8-F6, C2-C4, E7-E6, G1-F3, B7-B6, G2-G3, C8-B7, F1-G2, F8-E7, E1-G1, E8-G8, B1-C3, F6-E4, D1-C2, E4-C3 8f20: C2-C4, G8-F6, B1-C3, E7-E6, E2-E4, C7-C5, G1-F3, B8-C6, D2-D4, C5-D4, F3-D4, F8-B4, D4-C6, D7-C6, D1-D8, E8-D8 8f40: E2-E4, E7-E5, G1-F3, B8-C6, F1-B5, G8-F6, E1-G1, F6-E4, D2-D4, F8-E7, D1-E2, E4-D6, B5-C6, B7-C6, D4-E5, D6-B7 8f60: E2-E4, E7-E6, D2-D4, D7-D5, B1-C3, G8-F6, C1-G5, F8-E7, E4-E5, F6-D7, G5-E7, D8-E7, D1-D2, E8-G8, F2-F4, C7-C5 8f80: E2-E4, E7-E5, D2-D4, E5-D4, D1-D4, B8-C6, D4-E3, G8-F6, B1-C3, F8-B4, C1-D2, E8-G8, E1-C1, F8-E8, F1-C4, D7-D6 8fa0: E2-E4, E7-E5, D2-D4, E5-D4, C2-C3, D4-C3, F1-C4, C3-B2, C1-B2, G8-F6, B1-C3, B8-C6, G1-F3, F8-B4, D1-C2, D7-D6 8fc0: C2-C4, E7-E5, B1-C3, G8-F6, G1-F3, B8-C6, E2-E3, D7-D5, C4-D5, F6-D5, F1-B5, D5-C3, B2-C3, F8-D6, D2-D4, C8-D7 8fe0: E2-E4, E7-E5, G1-F3, B8-C6, F1-B5, A7-A6, B5-A4, B7-B5, A4-B3, C6-A5, B3-F7, E8-F7, F3-E5, F7-E7, D2-D4, G8-F6
The first thing that pops out is that 28 of the 32 openings start with either D2-D4 (10) or E2-E4 (18). That explains the very high percentage of the time that these occur when openings are randomly selected.
The Operation Instructions explicitly states that CHESSmate chooses one opening at random and tries to follow it for 16 moves. I verified that this is the case by playing a few games and using the table above to pick moves in and out of the opening chosen. Dropping out of the opening book when the first difference is encountered might be a missed opportunity. Many of the E4 and D4 openings have the same first moves before diverging. A better strategy might have been to search the other openings, when a move diverges from the current opening, and switch a new opening if one can be found with the same previous moves and the next move that matches the new move.
That feels like a lot of new code, and I don't see a lot of unused space in the ROM, so the simpler solution might be all that would fit. Fun to think about.
Another thought. Knowing the format of the opening book would allow someone to replace the openings with their own. Might be cool to try. Are there more "modern" openings that could be substituted? Maybe a TODO.
-
CHESSmate Lite
02/17/2024 at 20:40 • 0 commentsI guess I'm getting a little antsy waiting for the daughterboard PCB to arrive (it has been shipped). To pass the time I thought I would try out an idea I had for a stripped down version of CHESSmate. My goal of course is to make a full blown reproduction that provides a truly authentic CHESSmate experience, but that may not be everyone's aesthetic. To that end I present CHESSmate Lite, a minimalist take on the 1976 classic.
When I laid out the PCB, I made sure that all of the labels from the membrane were copied onto the silkscreen layer. This allows one to make a working version of CHESSmate without the membrane. In fact you could just make a version that had only the main PCB with the CPU daughterboard attached to the back and nothing else. The only problem with this configuration is that the daughterboard prevents the combined unit from lying flat. So to fix that I designed this minimal frame.
Not much to it. You attach the PCB to the frame with eight 3M x 4mm bolts. There is a platform on the back onto which the piezo speaker can be mounted with two sided tape. With the daughterboard attached, power would be provided via the ESP-32's USB-C connector. The rubber feet work well keeping the unit from sliding as keys are pressed.
I like that the insides are on the outside. It's a great look IMHO. Not having to print the case and make the membrane cuts considerable time and effort off of the build. I have added the frame design along with the other models to GitHub.
-
Manual Intervention
02/16/2024 at 16:51 • 0 commentsOne thing that I try to do with my projects is to make an authentic looking reproduction of the manuals as well. For instance I was especially pleased with the way that this ZX80 Operating Manual turned out.
Making a CHESSmate Operation Instructions manual was pretty trivial. It's an 8 page (2 sheets of 8.5 x 11 inch paper) booklet with a simple staple binding. The hardest part was figuring out how to setup the print dialog to produce the desired result.
Once the booklet was together I had to trim 1/4 of an inch off the top and bottom edges to get the dimensions right. The PDF for the manual has been added to the GitHub for this project.
-
Random Thoughts
02/15/2024 at 23:11 • 0 commentsWhen I was looking at the opening book issue I read this in the little 8 page CHESSmate Operation Instructions:
CHESSmate contains a collection of 32 familiar chess openings. Out of these it chooses one at random and tries to follow it for 16 moves.
It then lists the 32 openings by name.
What jumped out at me was "chooses one at random". I had not really noticed this. Sure enough if you push the following sequence of buttons on the original:
- NEW GAME
- H - ENTER (CHESSmate plays WHITE.)
- G - ENTER (CHESSmate moves)
CHESSmate will make the following opening moves (randomly ?) plus others:
- E2-E4
- D2-D4
- C2-C4
Now it's hard to tell because it's random after all, but the moves tend to skew towards E2-E4 and D2-D4. That is to say that they seem to come up more frequently. The others much less frequently.
My reproduction only ever played E2-E4 when CHESSmate plays first.
So how is random working? I looked for other RRIOT reads happening around the same time as the opening book lookups. I noticed a read to the 8B0E register (labeled READ TIMER INTERRUPT) that I had assumed was to clear interrupts (since the current player was switching), but maybe is does more. I was always just returning FF from this read.
On a hunch I started returning a random number between 0 and 31 inclusive instead and guess what, I'm getting random behavior now very similar to what is happening on the original machine. My guess is that the value being returned by the RRIOT is the "time" remaining before the next interrupt will be triggered.
I'm not going to overthink this. Random selection from the chess openings is working.
That was the last item on my firmware TODO list. I believe at this point that I have a very accurate CHESSmate software experience. All that's left is to put the whole package together. For that I'll wait until my ESP-32 backpack daughter board arrives (it's in final inspection at the fabricator). Light at the end of the tunnel and all that.
-
Booking It
02/15/2024 at 20:04 • 0 commentsOn to the next mystery "eureka" reads. A reminder.
Number R/W Program Counter Address Value Notes 4 R F305 8xxx FF Occurs when move made. 5 R F309 8xxx FF Occurs when move made. When I dumped the information from each of these I started to see a pattern.
The FFs are what I return no matter what.
When CHESSmate moves first. ~~~~~~~~~~~~~~~~~~~~~~~~~~~ R F305 8C00 FF R F309 8C01 FF When CHESSmate moves second. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ R F305 8C00 FF R F309 8C01 FF R F305 8C20 FF R F309 8C21 FF R F305 8C40 FF R F309 8C41 FF R F305 8C60 FF R F309 8C61 FF R F305 8C80 FF R F309 8C81 FF R F305 8CA0 FF R F309 8CA1 FF R F305 8CC0 FF R F309 8CC1 FF R F305 8CE0 FF R F309 8CE1 FF R F305 8D00 FF R F309 8D01 FF ... R F305 8FE0 FF R F309 8FE1 FF
Now the 8xxx address space implies that this has something to do with the 6530-024 RRIOT chip, but I had been led to believe that the 1K RRIOT ROM was not being used by CHESSmate. Here is the only place in the code again that makes these reads.
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
You will notice that the two reads are made (indirectly) through a memory address. This would make it difficult to determine what memory was actually being accessed by just looking at the code, hence the misleading no RRIOT ROM assumption.
The pattern of the reads suggest a search, skipping 32 bytes after each step. The CHESSmate manual suggests that the game has a cache of 32 classic chess openings for up to 16 moves each. So the RRIOT ROM most likely contains these opening moves since (32 bytes per opening) x (32 openings) = 1024 the size of the ROM. I had always assumed that the opening moves was part of the main ROM.
Another little eureka moment for me. Of the four sites that had CHESSmate ROMs only one of them had a second 1K "additional" ROM. I assumed this must be the opening book so I included it in the emulator and mapped it to the 8C00 to 8FFF memory space, and it worked.
When I make my first move of the game now, by dumping the read info I can see CHESSmate searching through the book until it finds my move. You can tell it's "in book" because it will counter move immediately without the blinking the display while "thinking". Different opening moves show a different search "depth".
Another mystery solved.