ZX81 Listing for z80digrain.asm


ZX81 assembly listing for Z80 DIGITAL RAIN / SLR 2021

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