A long time ago, I set out a list of goals for my Project:65 breadboard computer. It looked like this:
- Get the 65C02 on a breadboard and see if I can get it to do anything at all. This is to see if I understand how it needs to be connected, and see if I can come up with a workable way to clock the thing.
- Get instructions to the 65C02. I think I know how to program an EEPROM using the hardware I have on hand. I’m sure you can expect at least one post describing my experiments – and more, depending on how many snags I hit.
- Figure out how to handle the memory addressing, and hook the 65C02 up with actual RAM and ROM and validate that it is in fact running some kind of program.
- Add the 6522 VIA into the mix and write a program that can talk to another computer.
As of last week, I can check off all of those items. I have a working microcomputer. By 1976 standards, Project:65 is pretty impressive, and blazingly fast. There’s just one more thing it really needs: software.
The test code I’d been using in my EEPROM didn’t do much more than echo the characters on its input stream. I knew I could write a more interesting program, but after several weeks of debugging I’ve gotten a little tired of pulling the EEPROM out and reprogramming it every time I needed to make a change. I needed a better way to get software into the computer.
I decided to write a simple bootloader – a piece of code that would start at system reset and download the actual program to be run over the serial connection. That raised a few questions, though. The bootloader would need to figure out how many bytes of data to download and where to put the data in memory. It would need to distinguish between reading a zero byte and not having any data available to be read, a distinction that my current IO software didn’t make. And it’d probably need some kind of error correction. Plus, on the terminal side, I’d need a way to send the data to the computer.
I knew I wasn’t the first person to have this problem, so I went online to do a little research. At 6502.org, I found a couple of interesting projects. The one that really caught my fancy was Daryl Rictor’s implementation of the XMODEM protocol. XMODEM was the first really popular transfer protocol for microcomputer telecommunications – pretty much every BBS system and terminal package supported it. Now, XMODEM thrived more thanks to its simplicity than to its feature set, which is pretty minimal. And since it was designed for modem transfers across noisy 1970s-era phone lines, it’s kind of overkill for my purposes. But the idea of using an XMODEM protocol was so retro-tastically delicious that I just couldn’t resist.
I gave Daryl’s code a once-over and realized that it was going to be pretty easy to adapt it to my purposes. For one thing, the code to receive a file was specifically designed to store the received file in memory, which was exactly what I needed. It even followed the practice of using the first two bytes of the file as the address to begin storing the file at. That’s the same technique used by programs on the C64, and so it’s the same format that CBM Prg Studio uses for its output files.
Of course, Daryl’s code knew nothing of my (admittedly kind of funky) 6522-to-MAX3100 Input/Output system, but it was designed to be flexible. All it needed was one machine-language routine to write a single character, and another to read a single character. The particular requirements were close enough to the IO routines I’d already written that I only needed to make some minor adjustments to plug them in.
For example, the read routine isn’t expected to wait for a character to be available. Instead, it uses the carry flag to indicate that a character was read, and clears the carry flag to indicate that nothing was available yet. That way you can distinguish between reading the byte $00 and not reading anything. The carry flag can be used for things like this on the 6502 because it can be manually set and cleared (unlike some of the other status flags), and only a small number of instructions affect it. For those interested in seeing the code, I ended up with something like this:
; Here's the pin mapping for the parallel port of the ; user port: ; PIN BIT VALUE USE ; C $01 Clock ; D $02 CS (chip select) ; E $04 Output (MOSI) ; F $08 Input (MISO) ; Reads a single byte from input and puts it in ; the accumulator. Carry is set high if a byte was ; read, and low if no byte was available readchar$ ; toggle slave select briefly lda #2 sta PORT_DATA lda #0 sta PORT_DATA ; send 8 bit read command; read status lda #$00 ; read command is $00 sta writebuffer jsr rwbit jsr rwbit jsr rwbit jsr rwbit jsr rwbit jsr rwbit jsr rwbit jsr rwbit ; save the status byte we just read lda readbuffer sta statusbyte lda #$00 ; write $00 and read actual data sta writebuffer jsr rwbit jsr rwbit jsr rwbit jsr rwbit jsr rwbit jsr rwbit jsr rwbit jsr rwbit ; high bit of statusbyte is 1 iff we read ; a real character. rol to put that bit in ; carry, and load whatever we read into A rol statusbyte lda readbuffer rts ; rwbit writes one byte of writebuffer and reads ; one bit into readbuffer. Both buffers are ; left shifted by one place. rwbit rol writebuffer ; high bit --> carry bcs nonzero lda #0 ; set MOSI low nzret sta PORT_DATA ; set port inc PORT_DATA ; set clock high ; receive bit asl readbuffer lda PORT_DATA ; read port and #$8 ; check MISO bit beq reczero inc readbuffer ; add a 1 bit reczero dec PORT_DATA ; set clock low rts nonzero lda #4 ; set MOSI high jmp nzret
That’s still a long way from being optimal, or even pretty; but there will be time for that later. The final change I made was to store the start address of the program somewhere safe. That way, when the XMODEM subroutine returns to my bootloader, we can jump into the program and start running.
Of course, in order to take advantage of this, I needed a terminal program that understands XMODEM, and that’s not as universal as it used to be. Heck, terminal programs aren’t as universal as they used to be. When I first started working on the IO code, I used the terminal built into the Arduino IDE. Like everything else about that IDE, it’s pretty minimalistic, so I quickly switched over to using PuTTY. That was an improvement because PuTTY understood the ANSI control codes I was using for things like text color, but it’s still more of a terminal emulator than a telecommunications program.
Most recently, I’ve started using Tera Term, which handles the ANSI stuff but also supports all the X/Y/ZMODEM protocols, and it has a few other features that are going to come in handy pretty soon. Using it does kind of make me miss Terminus, my preferred Amiga terminal software from back in the day, but it’ll get the job done.
The last thing I needed was a test program, and we all know what that’s going to look like:
; these are routines in my EEPROM monitor outputstring = $FFE3 commandloop= $FFE0 *=$5000 lda #<message1 ldx #>message1 jsr outputstring jmp commandloop message1 byte 13,10 TEXT "hello, world!" byte 13,10,0
The best thing about this code is that it all came together really easily. There were only two real bugs when I tried to run things. The first time around I wasn’t quite saving the start address correctly, but that was easily fixed. A more tricky problem was that for a while my XMODEM transfers would occasionally stall out. Ultimately that seems to have been down to my own initialization code. I was being careless and not initializing the stack pointer, and maybe not initializing my own globals very well. I paused to clean things up before debugging it, but that was enough to make it all work reliably.
Here’s a picture of the transfer in progress. I actually had to pad out my “hello world” program so that I could get the screenshot; otherwise I could’t hit “Print Screen” fast enough. Up at the top of the post you can see the final result, with some of my debugging statements still in place.
Now that I can easily download code to the Project:65 computer I can get started on some real software. Every retrocomputer needs a BASIC interpreter, and there are some options for the 6502 that won’t get Bill Gates mad at me. More on that next week!