ZX81 Assembly Listing for zx81rain.asm


ZX81 assembly listing for **ZX81 RAIN**SLR/2023*

**ZX81 RAIN**SLR/2023* (zx81rain.asm)

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.


ASSEMBLY PROGRAM LISTING

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