A further refinement of my z80 Digital Rain. For normal ZX81 users you’ll notice the drops start at random locations in the top half of the screen. For Chroma users, it adds different drop shades that are closer to the original.
;
; ZX81 Digital Rain
;
; An updated version of my Digital Rain with a random
; start position and better colors in Chroma mode.
;
; 9/15/2021 - Steven Reid
; 9/16/2021 - adds in a variable speed component.
; 12/30/2022 - changing up colors, adding gradient drop.
; 1/13/2023 - differing height change, other clean up.
;
; +++
; Header
;
; 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 _as,_z_,_x_,_8_,_1_,_sp,_r_,_a_,_i_,_n_
db _as,_as,_s_,_l_,_r_,_sl,_2_,_0_,_2_,_3_
db _as,$76,$76 ; *ZX81 RAIN**SLR/2023*
start:
call slow ; SLOW is required.
call ichroma ; is chroma installed?
; end header and startup
; ---
; +++
; Main Game Loop
;
mainloop:
; grab frames now
ld hl,frames ; make hl point to the timecounter
ld a,(hl) ; get the timecounter in A
push af ; save current frame values
push hl ; save location (faster than reloading later)
; add a new drop
call add_drop
; make it rain!
ld hl,raindrop+31 ; reset rain
ld b,32 ; test all columns
next_drop: ; check if raining in that column
call check_rain
dec hl ; move to next column
djnz next_drop ; loop over remaining drops
; now delay
pop hl ; restore frame location
pop af ; restore previous frame value
sub 3 ; number of frames to delay
wfr: cp (hl) ; test if frames match
jr nz,wfr ; nope, loop until they do
call check_break ; check if we are done?
jr mainloop ; repeat main loop
; end Main Game Loop
; ---
; +++
; Game Data and Defines
;
; Digital Rain defines
variation: equ 6 ; variation of drop speed
bottom: equ 24 ; bottom of screen
; colors
defcolor: equ $04 ; dk green on black
dropcolor: equ $0f ; white
newdropcolor: equ $0c ; green
; Variables
counter: db 00
atdrop: db 00
whichdrop: dw 00
raindrop: defs 32
dropspeed: defs 32
dropcounter: defs 32
; end data and defines
; ---
; +++
; Add Drop
;
add_drop:
; get a random number from 1-32
ld b,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
add a,l ; add a to hl
ld l,a
adc a,h
sub l
ld h,a
; then make it rain if not already
ld a,(hl) ; get drop
or a ; is a zero?
ret nz ; no, then retun
; it isn't raining
ld b,11 ; random starting location
ex de,hl
call rnd
ex de,hl
add a,(hl) ; increment starting location
ld (hl),a
ld b,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
ret
; end add drop
; ---
; +++
; Check Rain
;
check_rain:
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?
ret z ; yes, not raining
; now check if it is time to move drop
call adjustcounter
ld hl,(whichdrop) ; restore drop
ret nz ; skip if counter didn't reset
; not zero, first see if we want to change a character
ld b,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 b,bottom ; 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 change
call getdrop ; get drop to print
ld b,a
ld a,(achroma) ; do we add color?
and $20
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,bottom ; then add 25 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 bottom+2 ; at bottom?
ret nz ; no, keep going
; yes, deal with change
ld (hl),255-bottom ; and save it
ret
; end make rain
; ---
; +++
; 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 b,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 c,a
ex de,hl
ld hl,(whichdrop) ; grab where at
ld a,(hl) ; grab drop location
cp bottom ; at bottom?
ex de,hl
ld a,c
jp nc,drop_tail ; skip if a >=
; save character
ld (hl),a
drop_tail:
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,newdropcolor
call resetcolor
ld a,defcolor
resetcolor:
ld de,65536-33 ; previous row
add hl,de ; move back a line
set 7,h
ld (hl),a ; restore drop color
ret
; end save drop
; ---
; +++
; Save Clear
;
; 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: ; random routine with refresh
ld hl,0 ; seed value
ld a,r ; grab the refresh register
add a,l ; add msb of seed (l)
ld l,a ; and save it as the new value
ld a,r ; grab the refresh register again
add a,h ; add lsb of seed (h)
and $1f ; mask it to stay in ZX81 ROM (lower 8K)
ld h,a ; set pointer back within ROM
ld (rnd+1),hl ; save current pointer (self modifying code!!!)
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
; ---
; +++
; Break
;
; preserves state, but will exit if SPACE is pushed
;
check_break:
exx ; save register states
; did the player press break key (space)?
call $0f46 ; was break pressed? (break-1 ROM routine)
jr nc,break ; no, exit as normal
exx ; restore registers
ret ; and return
; yes, exit the program as normal
break:
call resetchroma ; clear reset
rst $0008 ; call ERROR-1 reset
db $ff ; with error code 0 (normal exit)
; end break
; ---
; +++
; In Chroma
;
; in: nothing
; out: colors enabled
; preserves: hl,e
; destroys: af, bc
;
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.
; black = 0000-0, dkblue = 0001-1, dkred = 0010-2
; dkprpl = 0011-3, dkgrn = 0100-4, dkcyan = 0101-5
; dkyel = 0110-6, gray = 0111-7
; black = 1000-8, blue = 1001-9, red = 1010-a
; purple = 1011-b, green = 1100-c, cyan = 1101-d
; yellow = 1110-e, white = 1111-f
; 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,$7fef ;Go back to B/W.
in a,(c)
and $20
ret nz
xor a
out (c),a
ld a,$ff
ld (achroma),a
ret
; end chroma routines
; ---
; +++
; ZX81 Defines
; ZX81 system vars
d_file: equ $400c
df_cc: equ 16398
last_k: equ 16421
margin: equ 16424
s_posn: equ 16441
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
cls: equ $0a2a
; ZX81 Characters (not ASCII)
_sp: equ $00
_qu: equ $0b
_lb: equ $0c
_dl: equ $0d
_cl: equ $0e
_lp: equ $10
_rp: equ $11
_gt: equ $12
_lt: equ $13
_eq: equ $14
_pl: equ $15
_mi: equ $16
_as: equ $17
_sl: equ $18
_sc: equ $19
_cm: equ $1a
_pr: equ $1b
_0_: equ $1c
_1_: equ $1d
_2_: equ $1e
_3_: equ $1f
_4_: equ $20
_5_: equ $21
_6_: equ $22
_7_: equ $23
_8_: equ $24
_9_: equ $25
_a_: equ $26
_b_: equ $27
_c_: equ $28
_d_: equ $29
_e_: equ $2a
_f_: equ $2b
_g_: equ $2c
_h_: equ $2d
_i_: equ $2e
_j_: equ $2f
_k_: equ $30
_l_: equ $31
_m_: equ $32
_n_: equ $33
_o_: equ $34
_p_: equ $35
_q_: equ $36
_r_: equ $37
_s_: equ $38
_t_: equ $39
_u_: equ $3a
_v_: equ $3b
_w_: equ $3c
_x_: equ $3d
_y_: equ $3e
_z_: equ $3f
; end defines
; ---