Skip to content

Instantly share code, notes, and snippets.

@unbibium
Created February 25, 2019 06:27
Show Gist options
  • Save unbibium/67a1b47eb338b7c1e716bbbfc3ac71c9 to your computer and use it in GitHub Desktop.
Save unbibium/67a1b47eb338b7c1e716bbbfc3ac71c9 to your computer and use it in GitHub Desktop.
Matrix rain effect for Commodore 64 and unexpanded Vic 20
processor 6502
; This is supposed to generate a Matrix code rain effect.
;
; It is designed to be run inside a loop in BASIC, so that
; RUN/STOP and keyboard checks can be done from BASIC.
; For the C64 $C000 build, for example, you could do
; something like this:
; FOR X=1 TO 3000:SYS 49152:NEXT X
;
; Compiler flags (-D in dasm):
;
; BASIC:
; builds as a BASIC program. SYS 2063:RUN
; VIC20:
; builds for an unexpanded VIC-20. This version
; fits in the tape buffer so you can call it with
; SYS 828.
;
; the arcane method of performing the row multiplication is
; important, not only to be able to build both C64 and VIC
; versions, but also to eventually build a version for the
; VIC that will fill the screen by declaring more rows and
; columns.
mscr = $FB
mcolor = $FD
IFCONST VIC20
WIDTH = 22
HEIGHT = 23
; unexpanded vic
SCRMEM = 7680
COLMEM = 38400
PAGES = 2
IFCONST BASIC
ORG $1001
ELSE
ORG $033c
ENDIF
ELSE
; screen dimensions
WIDTH = 40
HEIGHT = 25
SCRMEM = 1024
COLMEM = 55296
PAGES = 4
IFCONST BASIC
ORG $0801
ELSE
ORG $c000
ENDIF
ENDIF
IFCONST BASIC
word ENDBASIC ; pointer to next line
word 2019 ; line number
byte $9e ; SYS
; INIT address in ASCII decimal. calculating
; it this way instead of using dasm expressions
; because dasm gets confused, and we know it's
; always 4 bytes.
byte INIT / 1000 | $30
byte INIT % 1000 / 100 | $30
byte INIT % 100 / 10 | $30
byte INIT % 10 | $30
byte ':
byte $8a ; RUN
byte $00 ; end of statement
ENDBASIC:
byte $00,$00
ENDIF
INIT:
; turn screen black
IFCONST VIC20
lda #8
sta 36879
ELSE
lda #0
sta $d020
sta $d021
ENDIF
; main loop
LOOP
ldy #WIDTH-1
LOOP1:
lda raindrops,y ;get raindrop position
; reset if >64
bmi LOOPA
cmp #HEIGHT+numcolors
bmi LOOPA
; get random number -16 <= n <= -1
jsr GETRAND
ora #$F0
sta raindrops,y
LOOPA:
jsr MULROW
; see if (mscr),y falls within screen boundary
ldx mscr+1
tya
clc
adc mscr
bcc LOOPB
inx
LOOPB:
cpx #>SCRMEM
bmi DRAWTRAIL ; lower bound check
cpx #>SCRMEM+PAGES
bpl DRAWTRAIL
jsr GETRAND
sta (mscr),y
DRAWTRAIL:
; draw color trail
ldx #numcolors-1 ; color index
TRAILLOOP:
; see if (mcolor),y falls within screen boundary
tya
clc
adc mcolor
lda #0
adc mcolor+1
cmp #>COLMEM
bmi NEXTTRAIL
cmp #>COLMEM+PAGES
bpl NEXTTRAIL
lda COLORS,x
sta (mcolor),y
NEXTTRAIL:
lda mcolor
sec
sbc #WIDTH
sta mcolor
bcs NEXTTRAIL2
dec mcolor+1
NEXTTRAIL2:
dex
bpl TRAILLOOP
LOOP2
tya
tax
inc raindrops,x
dey
bpl LOOP1
ENDUT:
rts
;
; Multiply a row number by WIDTH
;
MULROW:
; store value to be multiplied into
; low byte temporarily.
sta mscr
ldx #0
stx mscr+1
; use a loop to build the appropriate
; multiplication routine
MASKBIT SET $80 ; will cover each bit
TRAILING SET 1 ; trailing zeroes flag, will clear on
; first 1 bit found.
; loop through each bit
REPEAT 8
IF MASKBIT <= WIDTH ; skip if trailing 0
IF TRAILING
; found leftmost '1' bit.
TRAILING SET 0 ; no longer need to skip trailing 0s
; we can start shifting more bits from the right
ELSE ; multiply by two
asl
rol mscr+1
IF WIDTH & MASKBIT ; push a 1
adc mscr
bcc .+4
inc mscr+1
ENDIF ; whether to push a 1
ENDIF ; whether we're flipping the trailing bit
ENDIF ; whether we're in a trailing zero
MASKBIT SET MASKBIT >> 1
REPEND
; write low byte
sta mscr
sta mcolor
; add high byte for screen memory
lda mscr+1
clc
adc #>SCRMEM
sta mscr+1
; add high byte for color memory
adc #>COLMEM->SCRMEM
sta mcolor+1
rts
; use ROM as a substitute for random bytes
ROMRAND = $e000
GETRAND:
inc RANDPTR
bne GETRAND2
inc RANDPTR+1
bne GETRAND2
lda #>ROMRAND
sta RANDPTR+1
GETRAND2:
lda ROMRAND
rts
RANDPTR = GETRAND2+1
COLORS:
IFCONST VIC20
byte 0,5,0,5,5,3,5,3,3,1
ELSE
byte 0,5,0,5,5,13,5,13,13,1
ENDIF
ENDCOLORS
numcolors = ENDCOLORS-COLORS
; TODO: randomize in assembler?
; Memory for raindrop's vertical positions
; one raindrop for each column on the screen
raindrops:
byte 3,9,13,7,10,8,4,16
byte 2,19,3,0,12,10,9,4
byte 3,9,12,5,13,1
IF WIDTH > 22
byte 4,16,2,19,3
IF WIDTH > 27
byte 1,12,10,9,4
byte 3,19,14,8,10,1,4,6
ENDIF
ENDIF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment