For this project, I have aimed to demonstrate not just what I have learned this semester, but attempt a challenge that would test much of what I’ve learned in my years at this university as well as my two-year internship. It has required both complex programming and detailed hardware design, and demonstrates countless concepts learned in my time as a Computer Science major and EET minor.
The project is an emulator of Apple’s original kit computer. The Apple 1 was a very simple hobbyist computer that was little more than an MOS6502 Microprocessor hooked up to a peripheral interface adapter that gave its programs input from an ASCII keyboard and could output to a character-based display. Some kits could also read from and write to a cassette deck, though I did not include this feature in my emulator.
The system’s BIOS, called the “Woz Monitor” by the community, only had a few simple functions. The user could type in a memory address in hexadecimal format, and the Monitor would display the value at that location. It could also display a range of locations, record keyboard input starting at a specified location, and run the program that started at a given location.
Simulation of this functionality was actually split between two Arduinos (which, in the final project, are just the bare ATMega328P MCU chips on a board, with clocks and pin headers to be programmed in-system by an Arduino running the ArduinoISP firmware). The first one interfaces with an old Dell PS/2 keyboard. It reads data in using the Arduino PS2Keyboard library, which converts the keyboard’s scan codes into ASCII values, and my program echoes those characters to serial output. The TX line is connected to the RX line of the main Arduino.
The main microcontroller serves several purposes. It runs the core 6502 emulation code, which is in the main “.ino” file. This file is nearly a thousand lines in length, and to keep my code organized, I had to split the rest into a few other files, using C+ classes. These other classes help it interface with the 64k RAM and SD card on the SPI bus, the shift register that displays the status lights, and the Bluetooth module used for the final output.
The main 6502 emulator code is fairly simple in theory, but large because of how much it has to do. The 6502 instruction set has nearly sixty instructions, and the emulator code has a C function for every one of them. It has many variables, representing the various registers, flags, and the program counter of the CPU. The actual main loop is fairly simple:
- Read in the byte from memory at the location pointed to by the program counter.
- Increment the program counter.
- Decode the byte read in as an instruction
- Execute the instruction.
Fetching the instruction and incrementing the counter are straightforward. Decoding and executing make up the majority of code. The instructions are just a single byte value, but there is a certain pattern to them. Instructions that involve additional reads or writes to memory have several different “address modes,” where the byte to be written to or read from could be at a location that is an absolute value specified by the next two bytes, a value relative to one of the CPU registers, or even a value stored at another location to be read in. I take advantage of the pattern that helps indicate this to avoid having to write a function for the nearly one hundred total unique opcodes.
All program instructions and data are stored in an external 64k static RAM module that uses the SPI bus. The 6502 CPU has a total 64k address space, but the ATMega328P only has 2k of program memory, which makes the external RAM necessary. There are four special memory locations (from D010 to D013) that control input and output that the Woz Monitor and other Apple 1 programs know to use. There are “readMemory” and “writeMemory” functions in the “MotherBoard” class I created that, when...Read more »