6502 homebrew: the software toolchain

Now that the Apatco kit is working – at least the basic version (having trouble with the keyboard and display upgrade) – it’s time to mess around with writing code for it.

Many people swear by the now-abandoned CC65 as their compiler/assembler of choice for 6502 homebrew computers. But I’m going with Beebasm.

Although inspired by and largely aimed at the BBC Micro, and sharing many of the features of that machine’s outstanding inline assembler, Beebasm can be used for any 6502 computer.

The assembler has macros, conditional assembly (with IF statements), variables (much like BBC Basic’s assembler features), your choice of ‘$’ or ‘&’ for hex literals (don’t know why that matters to me, but it does), a lot of useful assembler directives, and even output to a BBC Micro-compatible DFS floppy disk image, if that floats your boat. (It might.)

I’ve no idea how it stacks up against CC65’s assembler features, but it’s more than enough for me right now. I’ll dig into CC65 more when I want to move to C and need a compiler.

The toolchain will be simple enough – VS Code and Beebasm. I can call Beebasm right from a VS Code terminal window. I’ll probably do this in a Bash script that will also copy the executable to a shared folder on my Nextcloud-based home cloud. That’s because I’ll need to switch to my Windows machine to actually burn the EEPROM.

Too few bits

There was one slight snag, though. If you Google for Beebasm, you’re likely to be taken to the page on the Retro Software website. That’s what I did and was glad to find a downloadable executable for the Mac. The only hitch is that an upgrade to macOS Catalina is in my near future and the executable I’d downloaded was 32-bit. Catalina spits out 32-bit programs with the contempt they deserve. So last century…

It’s easy to check if an executable is 32-bit or 64-bit. In Terminal, use ‘file’ followed by the executable name (and path if necessary). I have Beebasm in /usr/local/bin, which is in my $PATH. Having cd’d to that directory, this is what I found with file:

$ file beebasm
beebasm: Mach-O executable i386

The ‘i386’ is the giveaway. That means 32-bit.

Never mind. I headed over to the Beebasm GitHub instead and downloaded the source. In Terminal I used:

git clone https://github.com/stardot/beebasm
cd beebasm/src
make code

This builds the code and drops the executable in the beebasm directory. I checked the new executable:

$ file beebasm
beebasm: Mach-O 64-bit executable x86_64

Yay! Beebasm is now 64-bit.

Assemble. Burn. Rinse. Repeat.

The coding process is simple enough. Write the assembler code in VS Code. What follows is the code that Apatco supplies with the kit which I’d entered into a file called ‘bincount.asm’. When building the kit, you have to toggle it into the EEPROM in a laborious manner. The manual provides a binary listing for that, but also gives the assembler code for reference. Actually, this isn’t that code – I’ve modified it slightly to have the right syntax for Beebasm.

; Apatco demo code for 6502 breadboard computer kit.
; Modified for compiling with Beebasm assembler.

ORG $E000
Out_B = $8000       ; Port B register address on VIA - not used here
Out_A = $8001       ; Port A register address on VIA
DDR_B = $8002       ; Port B data direction register address - not used here
DDR_A = $8003       ; Port A data direction register address

.start
        LDA #$FF    ; setting data direction for port A to all outputs
        STA DDR_A

        LDA #$FF    ; store 255 in memory address 1024
        STA $400
.main   LDA $400    ; load A with contents of addr $400
        INC $400    ; increment value at $400 for next time
        STA Out_A   ; write A to Port A register
        JSR delay   ; pause using subroutine below
        JMP main    ; loop forever

.delay  LDA #$00
        LDX #$C4    ; start counter for outer loop
.loopB  LDY #$FF    ; start counter for inner loop
.loopA  DEY
        BNE loopA   ; result of DEY is not 0, so go round inner loop again
        DEX
        BNE loopB   ; result of DEX is not 0, so go round outer loop again
        RTS
.end

SAVE "bincount", start, end

Note the SAVE command at the end. This tells the assembler to use the filename ‘bincount’ for the executable.

From the command line, I use:

beebasm -i bincount.asm

This assembles the code and saves the binary ‘bincount’. I could take this file and burn it to the EEPROM, but it wouldn’t run. See that ORG $E000 at the beginning? $E000 is the address of the start of the ROM in the memory map this machine uses and therefore the start address of this code. But the 6502 doesn’t know to look there for code when it first runs – it looks at $FFFC and loads whatever 16-bit address it finds there (ie, the two bytes in $FFFC and $FFFD) as the program counter. Because this is a little endian world, the byte at $FFFC is the low byte and the one at $FFFD is the high byte. We need to have the value $E000 in these two bytes, so $FFFC should be $00 and $FFFD should be $E0.

To confuse matters slightly more, when looking at the bytes in a hex editor, such as that used in the software for the MiniPro EEPROM programmer, the start address for the code you’re viewing is $0000, not $E000. So, as far as the ROM code is concerned, the addresses of the two bytes we’ve just been discussing are $1FFC (which needs to have the value $00) and $1FFD (which should be $E0). The addresses change, as far as the 6502 is concerned because of how things are memory mapped via decoding.

With the binary code in hand, I read it into the software for the MiniPro EEPROM programmer, then manually edit the bytes at $1FFC and $1FFD. Then burn the EEPROM.

It’s all simple enough, but a bit clunky and time consuming, especially when you’re bug hunting. Maybe I need to look for some kind of 6502 emulator. Any suggestions?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.