ZX81 Assembly Program Listing
Z80 DIGITAL RAIN / SLR 2021 (z80digrain.asm)
;
; Z80 Digital Rain
;
; Steven Reid, 9/15/2021
; version 2 of revision 1
;
; This is my attempt to recreate my digital rain program using
; just z80 assembler.
;
; This version adds in a variable speed component.
;
; Compiled on JZeddy
; http://rullf2.xs4all.nl/jszeddy/jszeddy.html
; note this will add a 2 REM RAND' statement to run after compiled
; I can modify this after the fact, or grab the BASIC listing
; and modify there. Note that this assember is lowercase and you
; have to define your own variables.
;
; a basic routine for saving:
;
; 2 rand usr 16557
; 3 stop
; 4 save "gemquest"
; 5 run
;
; To turn into a .P file, save in JSZeddy (using RUN 4) to great an
; autorun ZX81 hex file. You can then copy and save the code into a .hex
; file. I wrote a hex2bin perl program to convert to binary (.P) format.
; Or you can use an online converter:
; http://tomeko.net/online_tools/hex_to_file.php?lang=en
;
; You can also use z80asm +zx81 to compile the program. Note that program
; won't autorun. You'll need to add the lines above to save it.
;
; To make the program more compatable with other compilers, I only use
; the hex character values. For example, the ZX81 'A' is 53 decimal or
; $35 or 35h in hex. You can use my zxencode.pl script to encode lines of
; text. Note, all the message and map data is compressed!
; +++
;
; Header and startup
;
; start up stuff
org 16514 ; stored in REM at top (ZX81)
jr start ; needed for z80asm
; title and copyright (will show on listing)
copy:
db $00,$10,$28,$11,$00,$1e,$1c,$1e,$1d,$00,$38,$39,$2a,$3b,$2a,$33,$00,$37,$2a,$2e,$29 ; (C)2021 STEVEN REID
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
db $00,$00,$15,$00,$3f,$24,$1c,$00,$29,$2e,$2c,$2e,$39,$26,$31,$00,$37,$26,$2e,$33,$00,$15,$76 ; + Z80 DIGITAL RAIN +
start:
call slow ; SLOW is required.
call ichroma ; is chroma installed?
; end header and startup
;
; ---
; +++
;
; Main Game Loop
;
mainloop:
; get a random number from 1-32
ld a,32 ; set a to limit
call rnd ; and get random number between 1-32
dec a ; make between 0-31
; get address at that location
ld hl,raindrop ; set hl to rain address
ld b,0 ; turn a into 16 bit number
ld c,a ; and set to c
add hl,bc ; get new address
; then make it rain if not already
ld a,(hl) ; get drop
or a ; is a zero?
jp nz,makerain ; no, go make it rain
; it isn't raining
inc (hl) ; so make it drop by adding one
ld a,variation ; get speed variation
ex de,hl
call rnd ; number from 1 to variation
ex de,hl
ld bc,32 ; move address to drop speed
add hl,bc
ld (hl),a ; and save speed
add hl,bc
ld (hl),0 ; and reset counter
makerain: ; main rain loop!
ld hl,raindrop+31 ; reset rain
ld b,32 ; test all columns
checkrain: ; check if raining in that column
ld a,b
ld (atdrop),a ; save drop we are at
ld (whichdrop),hl ; save which drop we are using
; check if we have a drop to display
ld a,(hl) ; get rain column
or a ; is a zero?
jp z,raindone ; yes, not raining
; now check if it is time to move drop
call adjustcounter
ld hl,(whichdrop) ; restore drop
jp nz,raindone ; skip if counter didn't reset
; not zero, first see if we want to change a character
ld a,100 ; get a big number from 1-100
call rnd
cp 50 ; 50% chance of making a change
jp c,rainfall ; nope, skp to next!
; now decide what to change
ld a,24 ; set range
call rnd ; get number
ld c,a
ld a,(atdrop)
ld b,a ; restore drop loop
ld a,c
call getloc ; calculate row
; get character at that location
ld a,(hl) ; get character there
or a ; is it zero?
jp z,rainfall ; nothing to chnage
call getdrop ; get drop to print
ld b,a
ld a,(achroma) ; do we add color?
and 20h
ld a,b
jp z,skipinvert ; skip invert!
add a,128
skipinvert:
ld b,newdropcolor ; color
call savedrop ; and save it
rainfall:
ld hl,(whichdrop) ; restore drop
ld a,(atdrop)
ld b,a ; restore drop loop
ld a,(hl) ; get rain fall
cp 0
jp p,adddrop
; drop tail
removedrop:
add a,25 ; then add 23 to make it positve
call getloc ; get location
xor a ; set a to blank
ld b,defcolor ; default color
call saveclear
jp movedrop
adddrop:
ld a,(hl)
call getloc
call getdrop
ld b,dropcolor
call savedrop
movedrop:
ld hl,(whichdrop) ; restore drop
ld a,(atdrop)
ld b,a ; restore drop loop
; is drop at bottom?
inc (hl) ; increment rain
ld a,(hl) ; restore a
cp 25 ; at bottom?
jp nz,raindone ; no, keep going
; yes, deal with change
ld (hl),255-25 ; and save it
raindone:
dec hl ; move to next column
dec b
jp nz,checkrain
; djnz checkrain ; loop over remaining drops
call delay
; done with rain loop, see if playe wants to continue
call kscan ; get key press
ld d,l
inc d ; this is what we'll test
jp z,mainloop ; no, repeat main loop
; clear chroma settings!
call resetchroma
jp stop ; all done!
; end Main Game Loop
;
; ---
; +++
;
; Data and Defines
;
; ZX81 system vars
d_file: equ $400c
d_fcc: equ 16398
frames: equ 16436
; ZX81 ROM functions
kscan: equ $02bb
findchar: equ $07bd
stop: equ $0cdc
slow: equ $0f2b
fast: equ $02e7
save: equ $02f9
printat: equ $08f5
pause: equ $0f35
; Digital Rain defines
speed: equ 600 ; delay amount
variation: equ 8 ; variation of drop speed
; colors
defcolor: equ $8c ; green on black
dropcolor: equ $8f ; white
newdropcolor: equ $8d ; cyan
; Variables
counter: db 00
atdrop: db 00
whichdrop: dw 00
raindrop: db 00,00,00,00,00
db 00,00,00,00,00
db 00,00,00,00,00
db 00,00,00,00,00
db 00,00,00,00,00
db 00,0,00,00,00
db 00,00
dropspeed: db 00,00,00,00,00
db 00,00,00,00,00
db 00,00,00,00,00
db 00,00,00,00,00
db 00,00,00,00,00
db 00,0,00,00,00
db 00,00
dropcounter: db 00,00,00,00,00
db 00,00,00,00,00
db 00,00,00,00,00
db 00,00,00,00,00
db 00,00,00,00,00
db 00,0,00,00,00
db 00,00
; end data and defines
;
; ---
; +++
;
; Get Drop
;
; in: nothing
; out: a = character between " and Z
; preserves: hl
; destroys: af, bc, de
;
getdrop:
; let's change one of the characters
ex de,hl ; save location
ld a,53 ; set a to character range
call rnd ; then pick one
add a,10 ; and then make it between " and Z
ex de,hl ; restore location
ret
; end get drop
;
; ---
; +++
;
; Get location
;
; in: b = column
; a = row
; out: hl = location
; destroys: af, bc, hl, de
;
getloc:
ld hl,(d_file) ; load screen location
ld e,b
ld d,0
add hl,de
ld e,33
ld b,a
dec b
ret z
rowloop:
add hl,de
djnz rowloop
ret
; end get location
;
; ---
; +++
;
; Save Drop
;
; in: b = color
; a = drop
; hl = location
; out: nothing
; destroys: af, bc, hl, de
;
savedrop:
ld (hl),a
ld a,(achroma) ; do we add color?
and 20h
ret nz ; no chroma, return
set 7,h
ld (hl),b ; set drop color
res 7,h
ld a,b
cp dropcolor ; was this drop color?
ret nz ; no, we are done
ld de,65536-33 ; previous row
add hl,de ; move back a line
push hl
and a
ld de,(d_file)
sbc hl,de
pop hl
ret c ; retun if we are past top
set 7,h
ld (hl),defcolor ; restore drop color
ret
; end save drop
;
; ---
; +++
;
; Save Drop
;
; in: b = color
; a = drop
; hl = location
; out: nothing
; destroys: af, bc, hl, de
;
saveclear:
ld (hl),a
; for this darken the tail
ld a,(achroma) ; do we add color?
and 20h
ret nz ; no chroma, return
; clear bottom
ld de,(d_file) ; get d_file
ld hl,759 ; bottom
add hl,de ; screen location
ld a,(atdrop) ; get row
ld e,a
ld d,0
add hl,de ; get row
set 7,h
ld (hl),b ; color
ret
; end save drop
;
; ---
; +++
;
; Random
;
; Returns a random number between 1 and range value (a)
;
; in: a = range value (high)
; out: a = a number between 1 and range value
; preserves: de
; destroys: af, bc, hl
;
rnd:
ld bc,0 ; get the pointer in ROM
push af ; save the range value
ld hl,(frames) ; for randomness get framecounter
add hl,bc ; add framecounter
dec hl ; when framecounter is unchanged make sure a change is done
ld a,h ; H can have any value, but ROM is #0000-#1FFF
and $0f ; only #0000-#0f00 is what we use
ld h,a ; set pointer back within ROM
ld (rnd+1),hl ; save current pointer (selfmodifying code!!!)
pop af ; grab range value
ld b,a ; put in b
ld a,(hl) ; get value in ROM
rrange: sub b ; we need 0-rval only
jr nc,rrange ; repeat unitl within range of value
adc a,b ; undo last subtraction, range 1-rval
ret ; back to mainprogram
; end random
;
; ---
; +++
;
; Adjust Counter
;
; in: counter
; out: counter is incremented or reset to zero
; preserves: hl,de
; destroys: af, bc
;
adjustcounter:
ld de,32 ; move to drop speed
add hl,de ; grab speed
ld a,(hl) ; grab speed
add hl,de ; move to counter
inc (hl) ; increment it
cp (hl) ; compare it with speed
ret nz ; return if not
ld (hl),0 ; otherwise, set counter to zero
ret
; end adjust speed
;
; ---
; +++
;
; Delay
;
; in: nothing
; out: delay time
; preserves: hl,de
; destroys: af, bc
;
delay:
ld bc,speed ; a timed delay. Altering the
lpdel: dec bc ; initial value of BC changes
nop
ld a,b
or c
jr nz,lpdel
ret ; retun from printgamescreen!
; end delay
;
; ---
; +++
;
; In Chroma
;
; in: nothing
; out: colors enabled
; preserves: hl,e
; destroys: af, bc
;
; pheripherals
achroma: db $ff
;+---+---+---+---+---+---+---+---+
;| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
;+---+---+---+---+---+---+---+---+
; | | | | | | | |
; | | | | | +---+---+-------- Ink colour (format: GRB).
; | | | | +-------------------- Ink colour bright bit.
; | +---+---+------------------------ Paper colour (format: GRB).
; +------------------------------------ Paper colour bright bit.
; blue = 1001-9, red = 1010-a, green = 1100-c
; yellow = 1110-e, cyan = 1101-d, purple = 1011-b
; white = 1111-f, black = 1000-8
; dkblue = 0001-1, dkred = 0010-2, dkgreen = 0100-4
; dkyellow = 0110-6, teal = 0101-5, dkpurple = 0011-3
; gray = 0111-7, black = 0000-0
; add in chroma colors!
ichroma:
ld bc,$7fef ; Chroma port.
in a,(c)
ld (achroma),a ; Save port contents.
and $20
ret nz ; Check if Chroma is available.
ld a,$38 ; Use attributes file and black border.
out (c),a
ret
; end ichroma
;
; ---
; +++
;
; Reset Chroma
;
; in: nothing
; out: screen colors reset
; preserves: hl,de
; destroys: af, bc
;
resetchroma:
ld bc,07fefh ;Go back to B/W.
in a,(c)
and 20h
ret nz
xor a
out (c),a
ld a,$ff
ld (achroma),a
ret
; end chroma routines
;
; ---