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 ; ---