I decided to revisit my Digital Rain program, but using z80 assembler. This version includes support for Chroma as well as a stock ZX81.
; ; 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 ; ; ---