Over the past few weeks I’ve been spending some time implementing a module called PropeddleTerm, which is a partial emulation of the Apple-1 keyboard and video interface. Today I finally fixed the last bugs in that module, and created an Apple-1 emulator by inserting the ROM image of Ken Wessen’s Krusader into the code. And as you can see above, it works!
In the Apple-1 computer, the processor was connected to the video and keyboard via a 6820 PIA. In Vince Briel’s Replica 1, the PIA was replaced by a 6821. One port of the PIA was used to read the keyboard, and one port was used to send a character to the video terminal. To check whether the user had hit a key on the keyboard, bit 7 of a control port could be checked, and to check whether the video terminal had processed the character, it was possible to read data back from the output port and check if bit 7 had been reset by the hardware.
This may not seem like the most logical way to implement a user interface (most early 6502 systems such as the Commodore PET-2001 used a memory mapped screen buffer), but in the world where Steve Wozniak designed the Apple-1, it made total sense: he basically designed a video terminal with a built-in computer, and with the 8-bit parallel connection between the two parts of the schematic, the speed must have seemed pretty amazing back then, compared to let’s say a Teletype that would print 10 characters per second.
The advantage of having such a simple interface is that it takes almost no code to print a text on the screen: printing one character is a matter of just three 6502 assembly instructions, and waiting for a key from the keyboard also only takes 3 (different) instructions. This is how Woz was able to get an entire machine language monitor into just 256 bytes, with two bytes to spare.
There is another advantage to the Apple-1 video/keyboard interface: it’s very simple to translate the “stream of characters” idea of the Apple-1 video/keyboard interface to a serial data stream. I already used a self-written driver called SerInTvOut that allows me to use either a PS/2 keyboard and a TV, or the serial port as user interface for debugging the Propeddle software. This has come in very handy while I was away from home (and even at home I don’t always have space to hook up a PS/2 keyboard and a monitor). My SerInTvOut driver adds a layer between the main program and the video / serial driver so that when the Spin software prints a character to the SerInTvOut object, it appears on the serial port as well as on the screen. Similarly when it reads a keystroke, it checks for characters from the serial port as well as from the PS/2 keyboard.
I figured it would be easy to emulate the Apple-1 interface in Propeller assembly, and it was. Well… maybe it wasn’t as easy as I thought at first: as it turns out, it was incredibly difficult to get the data to and from the data bus in time, and it was simply impossible to do all the extra processing (e.g. resetting the “new key” flag when the 6502 reads from port A) within 80 Propeller clock cycles. With the Propeller running at 80MHz, that’s how much time each cog has to do its work for one 6502 clock cycle at 1MHz. The Propeller has a great assembly language with nice features, but some operations just take a couple of instructions and each instruction is at least 4 clock cycles. And to do its work, the cog that emulates the PIA must access the hub because that’s where some other cog stores the data from the keyboard or where another cog would expect the character that needs to be printed on the screen. Hub instructions potentially take a long time and the timing around them can be tricky to predict.
In the end, I had to resort to a little bit of cheating. The “Terminal cog” that emulates the PIA tests for a valid address and handles the read or write request from the 6502 immediately of course, but whenever some extra processing needs to be done, it’s done during the next cycle, when the 6502 has already moved on to the next step in its execution, perhaps even the next instruction. So whenever the 6502 accesses one of the memory locations that are mapped to the Terminal cog, it gets the full attention of the Propeller for one 6502 cycle, but on the next cycle, the terminal cog will be busy doing… “other stuff”. It may seem that this is not going to work: what if the 6502 accesses the PIA locations two clock cycles in a row? Well… after some research I realized that the only way that the 6502 needs to access the same location (or adjacent locations) on two subsequent clock cycles is when it’s executing code from there (which would be silly) or when it’s executing an INC or DEC instruction (which would also be a silly thing to do with I/O). So it’s actually quite alright in this case, just because of the fact that the Propeller is emulating an input/output device, not simply memory.
Another bit of good news about timing is this: when Propeddle started, it was an expansion board for the Propeller Platform and the Propeller circuit was more or less out of my control. But now the Propeller is part of the project, I can easily decree that the crystal shall be 6.25MHz, the internal frequency of the Propeller shall be 100MHz, and there shall be 100 propeller cycles (not 80) available per 6502 cycle. Using 100 Propeller clocks per 6502 cycle means that the 6502 can’t run at 1.25MHz anymore if you change to a faster crystal, but it will make things a lot easier when it comes to getting the timing right for many sorts of functionality that I may not have thought of yet. For now, the Spin and PASM firmware on Github is still based on a minimum of 80 Prop cycles per 6502 cycle, but I’ve given myself (and hopefully other engineers in the future) some more leeway to implement some cool software-defined hardware. It will take a while to modify all the timings to take this change into account.