Moving Rainbow in 6502 Assembly
I've been wanting to get into assembly language forever but haven't found a way to iterate and test in a satisfying way to makes learning for me really easy. Recently, I saw flame from aeriform.io had developed the Minicube64, which is a fantasy console based on the 6502.
The 6502 is the same chip that was found in early Commodores, the BBC Micro, the NES/Famicom, the Apple ][, and zillions of others. It's a simplified amount of assembly instructions, which lends itself to picking it up much more easily and quickly than would be possible with a more expansive set like on modern processors. Perfect.
After installing Minicube64, I did a tutorial, read a ton of reference materials, and took some notes while going through some of the demo files.
Not too far after this, I saw that a gamejam for the Minicube64 was being held at itch.io and I set out to have a rainbow render on the screen, and if possible, animate it left to right. This felt like a manageable goal and through a LOT of trial and error, was finally able to get there.
This is the code:
include "64cube.inc" ; Include the helper functions
ENUM $0 ; Enumerate values starting at $0000
initColor rBYTE 1 ; set initColor as 1 byte
ENDE ; End enumeration
org $200 ; Set program origin to $0200
sei ; Set interrupt disable flag
; Set the stack pointer to be #$ff (low bits of $0100-01ff)
ldx #$ff ; Load value $ff into X
txs ; Transfer value of X into the stack
; pointer
; Set the video buffer page in the $f000 page in memory
lda #$f ; Load high byte of page into A
; register
sta VIDEO ; Store value in A at 'VIDEO'
; Set the colors buffer page in the $5000 page in memory
lda #$5 ; Load highbyte of page into 'a'
; register
sta COLORS ; Store value in A at 'COLORS'
_setw IRQ, VBLANK_IRQ ; Set value of VBLANK_IRQ to address of
; 'IRQ' label
cli ; Clear interrupt disable flag
Infinite: ; Set 'Infinite' label
dec initColor ; Decrement initColor
lda initColor ; Load initColor into A
and #$3f ; Ensure A never goes above #$3f
jsr Draw ; Jump to 'Draw' subroutine
jmp Infinite ; Create infinite loop
IRQ: ; Set 'IRQ' label
rti ; Return from interrupt
Draw: ; Set 'Draw' label
; This will draw a column of each color from one end of the color spectrum to
; the other. Eventually, I hope to have this scale from left to right with
; animation.
;
; A = Address of color within the COLORS page
; X = Col counter (ff-00: ff being the last col of the last row within the
; four subrows, 00 being the first col of the first row in the subrows
; Y = Row counter (ff-f0: ff being the last four subrows, ff being the first)
ldx #$0 ; Initialize column counter (X)
ldy #$f ; Initialize row counter (Y)
Loop:
lda initColor ; Set accumulator to color value
SubLoop: ; Create a subloop label
and #$3f ; Ensure A never goes above #$3f
sta $f000,X ; Store A in video page plus X for
sta $f100,X ; all columns
sta $f200,X
sta $f300,X
sta $f400,X
sta $f500,X
sta $f600,X
sta $f700,X
sta $f800,X
sta $f900,X
sta $fa00,X
sta $fb00,X
sta $fc00,X
sta $fd00,X
sta $fe00,X
sta $ff00,X
sec ; Set carry flag to wrangle SBC
sbc #1 ; Decrement the color value
dex ; Decrement column counter (X)
bne SubLoop ; If color value is not zero, go to subloop
dex ; Decrement col counter (X)
dey ; Decrement row counter (Y)
bne Loop ; If Y is not zero, go to Loop
rts ; Else return from the subroutine
Palette: ; Set 'Palette' label
; This will set the palette of colors available in the COLORS page, enumerating
; from $00 upward within that page.
org $0500 ; Set program origin to $0500
hex FF0000 ; Set these colors in colors page
hex FF1500
hex FF2F00
hex FF4400
hex FF5E00
hex FF7700
hex FF8C00
hex FFA600
hex FFBF00
hex FFD500
hex FFEE00
hex FBFF00
hex E1FF00
hex C8FF00
hex B3FF00
hex 99FF00
hex 80FF00
hex 6AFF00
hex 51FF00
hex 3CFF00
hex 22FF00
hex 09FF00
hex 00FF0D
hex 00FF26
hex 00FF40
hex 00FF55
hex 00FF6E
hex 00FF84
hex 00FF9D
hex 00FFB7
hex 00FFCC
hex 00FFE5
hex 00FFFF
hex 00EAFF
hex 00D0FF
hex 00BBFF
hex 00A2FF
hex 0088FF
hex 0073FF
hex 0059FF
hex 0040FF
hex 002AFF
hex 0011FF
hex 0400FF
hex 1E00FF
hex 3700FF
hex 4C00FF
hex 6600FF
hex 8000FF
hex 9500FF
hex AE00FF
hex C300FF
hex DD00FF
hex F700FF
hex FF00F2
hex FF00D9
hex FF00BF
hex FF00AA
hex FF0090
hex FF007B
hex FF0062
hex FF0048
hex FF0033
hex FF0019
I've added quite a bit of annotation to this, but will probably dig into it more in depth at a later date. To get it running:
- install Minicube64
- create a new file called `rainbow.s` and put the above contents within it
- run this in your shell: `./minicube ./path/to/rainbow.s`