As far as robots go, this is not a very useful one since all it does is blink its eyes every few seconds. The interesting part is how it does the blinking. Rather than switching the LEDs on then delaying a while before switching them off, the robot runs several layers of emulation with the innermost emulated system blinking the LEDs as fast as possible. All the layers of emulators within emulators combine to produce enough overhead to slow the blinking down to a rate that is visible.
The first level of emulation is the 6502 emulator written in MIPS assembly mentioned in my post about Tali Forth 2 on Linux. The steps of each 6502 instruction are abstracted into a series of MIPS assembly macros and stored in a spreadsheet which let me design the system very quickly:
Op code | Instruction | Mode | Step 1 | Step 2 | Step 3 |
0x6E | ROR | ABS | DEREF_MEM_T1 | ROR_MEM | T1_NZC |
0x6F | BBR6 | ZPR | DEREF_MEM | AND_BIT6 | BBR |
0x70 | BVS | REL | T0_V | BNE_T0 | |
0x71 | ADC | IZY | DEREF_MEM | ADC_MEM | A_NZC |
0x72 | ADC | IZP | DEREF_MEM | ADC_MEM | A_NZC |
A Python script takes the spreadsheet saved as a CSV file and outputs an assembly file with the macros for each instruction:
#0x6E - ROR ABS
ROR_ABS:
OP_MACRO 110, ROR, ABS, ROR_ABS, DEREF_MEM_T1, ROR_MEM, T1_NZC
NEXT_MACRO
All that was left after that was filling out each macro for the instruction, address mode, and steps which didn't take long.
Working with MIPS assembly for this was interesting since it differs from the 6502 in a lot of ways. For one, there is no flags register on MIPS so all the 6502 flags need to be calculated manually. One shortcut the emulator uses is to save the values that would generate flags but to delay the calculation until the flag is needed. The MIPS instructions INS and EXT are perfect for this since one register can store multiple saved byte values.
The second layer of emulation below the MIPS program emulating a 6502 is a 6502 program that emulates a 6502. Like the emulator described above, this one was very fast to set up using a spreadsheet of macro names for each instruction. Unlike most emulators, this one uses the same address range as the system it's emulating rather than setting aside a portion of memory for the emulated system. This lets emulated programs access system resources directly so that a program that writes to a memory-mapped peripheral, like the screen for example, works the same in emulation as outside of emulation without the overhead of address translation or range checks. Along those lines, I made a program that draws a bouncing ball in my JavaScript 6502 emulator to verify the functionality:
After this was working reliably, the next step was to implement eight bouncing balls at once. Each ball is assigned its own copy of emulator variables such as 6502 registers. A slight modification to the emulator makes it execute only one instruction before saving the emulator variables for the current ball then loading them for the next ball. This lets the emulator execute eight "threads" (one for each ball) one instruction at a time in round-robin fashion giving the illusion of multitasking. Because the emulator doesn't do any address translation, as mentioned above, all eight threads can share the same copy of the ball drawing program rather than having a separate copy in memory for each of the eight threads:
The design for the robot is an SVG file like the schematic for my 7400 Logic Calculator. Unfortunately, coordinates in the SVG format are all hardcoded, so it's tough to adjust anything after writing the file out by hand. This time, I generated the SVG with a Python script. It only took 100 lines or so of Python to define a simple system for outputting SVG shapes using relative coordinates. My wife printed out the design on blue cardstock with silver lines on her Cricut machine. 100 units in the SVG correlated to 1.39 inches printed out instead of 1 inch or to 1 centimeter as I hoped. These seem to be PostScript points which are 72 per inch. Scaling the SVG to compensate was easy. The red lines are for planning and weren't printed.
Although the final version is a simple paper cutout on a PCB, the first version was 3D printed. The original plan was to put an OLED screen in the robot's belly to show the value of registers and let the user switch between different levels of emulation. In the end, this seemed like too much extra work on a project that has already stretched on much longer than intended. The 3D printed version also had a few problems: two of the tabs to hold the halves of the head together broke off, the finish on the downward facing side of the print came out very rough and needs to be sanded, and the DIP28 PIC32 is a little too big to fit in the space inside the body. Maybe I can glue the halves of the head together without the tabs and use this for another project.
The PCB is like the Electronic Birthday Card where some components go through the paper and all the wires are soldered flat on the back side. A 3xAAA battery pack is attached with headers on a separate board.
Now that this is finished, I can move on to the other projects on my list and use what I learned about programming the PIC32.
No comments:
Post a Comment