Skip to content

Instantly share code, notes, and snippets.

@coolbutuseless
Created June 27, 2025 11:05
Show Gist options
  • Save coolbutuseless/9e5f4127e4e24f49de5ce122a0b04050 to your computer and use it in GitHub Desktop.
Save coolbutuseless/9e5f4127e4e24f49de5ce122a0b04050 to your computer and use it in GitHub Desktop.
6502 ASM code for a text scroller which says "Hello #RStats" (ACME assembler)
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Tiny bit of basic with a SYS command to jump to main.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*=$0801
!byte $0c,$08,$b5,$07,$9e,$20,$32,$30,$36,$32,$00,$00,$00
jmp main
clrscreen=$e544
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Setup code for IRQ and screen setting
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main
jsr clrscreen
sei ; suspend interupts during init
lda #%01111111 ; clear CIA interupts
sta $dc0d
lda $d01a ; Enable raster interupts
ora #$01
sta $d01a
lda $d011 ; Clear the 9th-bit for raster line
and #$7f ; position
sta $d011
lda #200 ; lower 8-bits of raster interupt line
sta $d012
lda #<irq ; load the address of the interupt routine
sta $0314 ; into the IRQ vector
lda #>irq
sta $0315
; set screen to 38 columns
lda $d016
and #%11110111 ; unset the bit-3
sta $d016
lda #00
sta $d020
lda #00
sta $d021
jsr colorsetup ; set a color gradient
cli
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; busy loop waitkng for IRQ
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
loop
jmp loop
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; IRQ servicing
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
irq
; turn on character rom access
lda $01 // Save original $01 state
sta original_01+1
lda #$32 // Set memory configuration to
sta $01 // character ROM access.
jsr scroller
; reset mem configuration
original_01
lda #$00 // Retrieve the original memory configuration
sta $01 // state before the scroll routine was executed.
; sane exit from IRQ
irqdone
lda #$ff ; Acknowledge IRQ
sta $d019
jmp $ea81 ; Minimal sane Return from interupt. Pulls y,x,a from stack and RTI
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Setup the color ram with a grayscale gradient
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
colorsetup
ldy #40
-
lda colorseq2,y
sta $d800+(08 * 40),y ; set colour
sta $d800+(09 * 40),y ; set colour
sta $d800+(10 * 40),y ; set colour
sta $d800+(11 * 40),y ; set colour
sta $d800+(12 * 40),y ; set colour
sta $d800+(13 * 40),y ; set colour
sta $d800+(14 * 40),y ; set colour
sta $d800+(15 * 40),y ; set colour
dey
bne -
rts
colorseq
!byte 11, 11, 11, 11, 11
!byte 12, 12, 12, 12, 12, 12
!byte 15, 15, 15, 15, 15, 15
!byte 1, 1, 1, 1, 1, 1
!byte 15, 15, 15, 15, 15, 15
!byte 12, 12, 12, 12, 12, 12
!byte 11, 11, 11, 11, 11
colorseq2
!byte 11, 11, 11
!byte 12, 12, 12
!byte 15, 15, 15
!byte 1, 1, 1
!byte 3, 3, 3
!byte 14, 14, 14
!byte 2, 2, 2, 2
!byte 14, 14, 14
!byte 3, 3, 3
!byte 1, 1, 1
!byte 15, 15, 15
!byte 12, 12, 12
!byte 11, 11, 11
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Scroll 1 character
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
scroller
ldy #00
scrollerloop
; copy all characters from left to right
lda $400+(08*40 + 1),y ; get char
sta $400+(08*40 + 0),y ; put char
lda $400+(09*40 + 1),y ; get char
sta $400+(09*40 + 0),y ; put char
lda $400+(10*40 + 1),y ; get char
sta $400+(10*40 + 0),y ; put char
lda $400+(11*40 + 1),y ; get char
sta $400+(11*40 + 0),y ; put char
lda $400+(12*40 + 1),y ; get char
sta $400+(12*40 + 0),y ; put char
lda $400+(13*40 + 1),y ; get char
sta $400+(13*40 + 0),y ; put char
lda $400+(14*40 + 1),y ; get char
sta $400+(14*40 + 0),y ; put char
lda $400+(15*40 + 1),y ; get char
sta $400+(15*40 + 0),y ; put char
;sta $d800+(11 * 40)+ofs,y ; set colour
iny
cpy #40
bne scrollerloop
; Load in the base address for the character set in char rom
; not strctly needed as address is set for a particular character
; later
.mychar = $fc
lda #$00
sta .mychar
lda #$d0
sta .mychar+1
; Pull the next char out of my string. String is limited to 16 chars
lda .nextchar
and #$0f
tay
lda .hellotext,y
; Multiply character byte value by 8 and store in ".mychar" to
; be used as base address for grabbing character bytes
asl
asl
asl
sta .mychar
lda .hellotext,y
;lda #.thischar
lsr
lsr
lsr
lsr
lsr
lsr
adc #$d0
sta .mychar+1
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Depending on the cycle, pull the character bytes one-by-one,
; AND to extract the current bit, and place a "dot" or a "space" at the
; y height for this particular byte
; Repeat for 8 lines in total - one line for each chracter byte.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ldx .cycle
line0
ldy #0
lda (.mychar),y
;lda .mychar3 + 0
and .and,x ; is this pixel set?
beq +
ldy #81 ; dot
sty $400+(09*40 - 2)
jmp line1
+
ldy #32 ; space
sty $400+(09*40 - 2)
line1
ldy #1
lda (.mychar),y
;lda .mychar3 + 1
and .and,x
beq +
ldy #81 ; dot
sty $400+(10*40 - 2)
jmp line2
+
ldy #32 ; space
sty $400+(10*40 - 2)
line2
ldy #2
lda (.mychar),y
;lda .mychar3 + 2
and .and,x
beq +
ldy #81 ; dot
sty $400+(11*40 - 2)
jmp line3
+
ldy #32 ; space
sty $400+(11*40 - 2)
line3
ldy #3
lda (.mychar),y
and .and,x
beq +
ldy #81 ; dot
sty $400+(12*40 - 2)
jmp line4
+
ldy #32 ; space
sty $400+(12*40 - 2)
line4
ldy #4
lda (.mychar),y
and .and,x
beq +
ldy #81 ; dot
sty $400+(13*40 - 2)
jmp line5
+
ldy #32 ; space
sty $400+(13*40 - 2)
line5
ldy #5
lda (.mychar),y
and .and,x
beq +
ldy #81 ; dot
sty $400+(14*40 - 2)
jmp line6
+
ldy #32 ; space
sty $400+(14*40 - 2)
line6
ldy #6
lda (.mychar),y
and .and,x
beq +
ldy #81 ; dot
sty $400+(15*40 - 2)
jmp line7
+
ldy #32 ; space
sty $400+(15*40 - 2)
line7
ldy #7
lda (.mychar),y
and .and,x
beq +
ldy #81 ; dot
sty $400+(16*40 - 2)
jmp lineend
+
ldy #32 ; space
sty $400+(16*40 - 2)
lineend
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; cycle = cycle + 1. If cycle = 8, then reset to 0, and advance to next char
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
inc .cycle
lda #08
cmp .cycle
beq .advancechar
rts
.advancechar
inc .nextchar
lda #0
sta .cycle
rts
.cycle
!byte 0
.nextchar
!byte 0
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; each cycle needs to AND with a different bit-mask to get the
; necessary bit in each byte to put on screen
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.and
!byte %10000000
!byte %01000000
!byte %00100000
!byte %00010000
!byte %00001000
!byte %00000100
!byte %00000010
!byte %00000001
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Scrolly text. Hardcoded at 16chars for now
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.hellotext
!scr "hello #rstats. "
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment