Intro to the bass (badass) 6502 assembler
bass is a new 6502 assembler. It is designed to be powerful (like Kickassembler) but with a less complex syntax, and more unified, functional design.
Let’s start with a minimal C64 program, a 3 opcode routine to make a rainbow border:
!org $c000 loop: lda $d012 sta $d020 jmp loop
You should be able to load this into a C64 emulator and start it with
Now let’s add a basic start snippet so we can just run the prg instead:
!section "main", $801 !byte $0b,$08,$01,$00,$9e,str(start)," BY SASQ",$00,$00,$00 start: lda #3 sta $d020
What happens here ? Well the
!byte meta command accepts both numbers
and strings, and for strings it inserts each character one by one.
str() function takes a value and converts it to a string. So this
is how we insert the correct sys adress into the basic, regardless of
start: label is placed.
Let’s add some more code;
!section "main", $801 !byte $0b,$08,$01,$00,$9e,str(start)," BY SASQ",$00,$00,$00 start: jsr $1000 .loop lda $d012 cmp #100 bne .loop lda #3 sta $d020 jsr $1003 lda #0 sta $d020 jmp .loop !section "music", $1000 !incbin "music.raw"
This is a short program that wait for a certain scan line, calls a music routine, and loops. The music is expected to reside in the file "music.raw" and must be placed at address $1000.
But what if music is a PRG with 2 bytes load address first. Or a sid file ?
The functional approach of bass lets us manipulate not only numeric data, but also byte arrays. So to strip out the 2 first byte of a file and place it in memory we can do:
data = load("music.bin") !fill data[2:]
A few new things here, the
data symbol now refers to a byte array, and we use
!fill meta command to insert it into memory.
[2:] part is called slicing. To extract a range from a byte array you use
data[start:end] (where start is inclusive and end exclusive).
So what about the load address, can we use it? Sure;
loadAdr = word(data[0:2]) !section "music", loadAdr music: !fill data[2:]
As you may have figured out, the
word() function decodes a little endian 16-bit word from an array.
Except what if the load address is not compatible with our layout? If we load another song at a different adress it will probably break our code.
Instead we can just verify the load address:
musicStart = $1000 !assert word(data[0:2]) == musicStart
Lets put it all together:
musicStart = $1000 !section "main", $801 !byte $0b, $08,$01,$00,$9e,str(start)," BY SASQ", $00,$00,$00 start: jsr musicStart .loop lda $d012 cmp #100 bne .loop lda #3 sta $d020 jsr musicStart+3 lda #0 sta $d020 jmp .loop data = load("music.prg") !assert word(data[0:2]) == musicStart !section "music", musicStart !fill data[2:]