ZX81 Assembly Program Listing
**RUN 2 ML*SLR/2022** (run2ml.asm)
;
; Run 2 ML
; v1.03 Steven Reid (c) 2022
;
; A simple little running animation and moving sky.
;
; 1.00 - 2/16/2022 - Initial build.
; 1.01 - 3/19/2022 - Added pixel movement with more skys.
; 1.02 - 3/20/2022 - Adding in some objects. Too slow.
; 1.03 - 3/20/2022 - Moved score to back side.
;
; +++
;
; 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 $17,$17,$37,$3a,$33,$00,$32,$31,$17,$17
db $17,$38,$31,$37,$18,$1e,$1c,$1e,$1e,$17
db $17,$76 ; **RUN ML***SLR/2022**
start:
call slow ; SLOW is required.
; end header and startup
;
; ---
; +++
;
; Main Loop
;
mainloop:
; reset variables
xor a ; ld a,0
ld (runframe),a ; reset run frame
ld (skycopy),a ; reset sky copy time
inc a ; ld a,1
ld (skyframe),a ; reset sky frame
ld (worldnum),a ; reset world number
ld (worldframe),a ; reset world frame
ld hl,skybuf0upper ; clear buffers
ld bc,320
clearskyloop:
ld (hl),$00
inc hl
dec bc
ld a,b ; test 16-bit counter
or c
jr nz,clearskyloop
; print static elements
call cls
ld bc,$0302 ; head
call printat
ld a,$80
rst $10
ld bc,$0700 ; floor
call printat
ld b,32
ld a,3
floorlp:
rst $10
djnz floorlp
animate:
; deal with runner
ld hl,runner
ld (runframe),hl
ld b,4
runloop:
push bc
call printrunner
call printsky
call printscore
; call printbox
; call printcoins
ld hl,50
call newpause
pop bc
djnz runloop
jp animate
; End of loop!
;
; +++
;
; Routines
;
printrunner:
ld b,4 ; load position
ld c,1
call printrunrow
call printrunrow
call printrunrow
ret
printrunrow:
push bc
call printat ; print location
ld hl,(runframe) ; load run location
ld b,3 ; print row
printrunrowlp:
ld a,(hl)
rst $10
inc hl
djnz printrunrowlp
ld (runframe),hl ; save next row
pop bc
inc b ; move down a row
ret
printbox:
ld bc,$0519
call printat
ld hl,boxtop
call print
ld bc,$0618
call printat
ld hl,boxbottom
jp print
boxtop: db $b1,$ff
boxbottom: db $b1,$b1,$b1,$ff
printscore:
; since we don't have movement yet, clear score area
ld a,3
ld bc,$0204
blankloop:
push af
push bc
call printat
ld hl,blank
call print
pop bc
inc b
pop af
dec a
jr nz,blankloop
ld a,(scoreframe)
dec a
cp 1
jr z,scoreframe1
cp 2
jr z,scoreframe2
cp 3
jr z,scoreframe3
cp 4
jr z,scoreframe4
; no frame, reset counter
ld a,5 ; reset scoreframe anex return
ld (scoreframe),a
ret
scoreframe4:
ld bc,$0404
ld hl,score20_1
jr printscoreframe
scoreframe3:
ld bc,$0405
ld hl,score20_1
jr printscoreframe
scoreframe2:
ld bc,$0305
ld hl,score20_2
jr printscoreframe
scoreframe1:
ld bc,$0206
ld hl,score20_2
printscoreframe:
ld (scoreframe),a
push hl
call printat
pop hl
jp print
scoreframe: db 0
score20_1: db $95,$9e,$9c,$ff
score20_2: db $15,$1e,$1c,$ff
blank: db $00,$00,$00,$00,$00,$ff
printcoins:
ld bc,$0410
call printat
ld hl,coinsrot0
ld a,(skyframe)
and a ; is a 0?
jr nz,printcoinstring
ld hl,coinsrot1
printcoinstring:
jp print
coinsrot0: db $34,$00,$34,$00,$34,$ff
coinsrot1: db $2e,$00,$2e,$00,$2e,$ff
; this is the new routine
printsky:
ld a,(skycopy)
and a ; is a 0?
jr nz,getskyframe ; no
; yes, copy in screen
call copysky ; copy sky into buffer
ld a,80 ; and reset sky counter (double due to frames)
getskyframe:
dec a ; decrement a
ld (skycopy),a ; and save it
ld a,(skyframe) ; get sky frame (0 or 1)
xor 1 ; flip it (0 to 1, 1 to 0)
ld (skyframe),a ; and save it!
and a ; is it 0?
jr nz,printskyframe1
printskyframe0: ; print sky from buffer 0
ld hl,skybuf0upper
call shiftsky
ld hl,skybuf0lower
call shiftsky
; trying to poke screen instead of print it
ld hl,skybuf0upper
ld de,(d_file)
inc de ; set to 0,0 of screen (1st row)
ld bc,32 ; 32 characters to copy
ldir ; copy row into screen
ld hl,skybuf0lower
inc de ; set to 1,0 of screen (2nd row)
ld bc,32 ; 32 characters to copy
ldir ; copy row into screen
ret
; ld bc,$0000
; call printat
; ld hl,skybuf0upper
; call printskyrow
; ld bc,$0100
; call printat
; ld hl,skybuf0lower
; jp printskyrow
printskyframe1: ; print sky from buffer 1
ld hl,skybuf1upper
call shiftsky
ld hl,skybuf1lower
call shiftsky
; trying to poke screen instead of print it
ld hl,skybuf1upper
ld de,(d_file)
inc de ; set to 0,0 of screen (1st row)
ld bc,32 ; 32 characters to copy
ldir ; copy row into screen
ld hl,skybuf1lower
inc de ; set to 1,0 of screen (2nd row)
ld bc,32 ; 32 characters to copy
ldir ; copy row into screen
ret
; ld bc,$0000
; call printat
; ld hl,skybuf1upper
; call printskyrow
; ld bc,$0100
; call printat
; ld hl,skybuf1lower
; jp printskyrow
; old routine...
printskyrow:
ld b,32
printskyrowlp:
ld a,(hl)
rst $10
inc hl
djnz printskyrowlp
ret
shiftsky:
ld a,(hl) ; grab first character
ld de,hl
inc hl
ld bc,79
ldir ; shift line to the left
ld (de),a ; replace last char with first
ret
; copy sky (hl) into sky buffer (de)
copysky:
; first, check routines
ld a,(worldframe) ; get which frame of world we are on
dec a
jr nz,saveworldframe ; not zero, keep going
; is zero, update world
ld hl,(world) ; load in world
ld bc,10 ; skip transition and 4 addresses (10 bytes)
add hl,bc ; set new world address
ld (world),hl ; and then save it
ld a,(worldnum) ; check world number
dec a
jr nz,getnextworld ; not zero, get next world
; is zero, reset worlds
ld hl,worlds
ld (world),hl ; set address to world start
ld a,numworlds ; reset world number
getnextworld:
ld (worldnum),a ; save new world number
ld hl,(world) ; load in new sky
ld e,(hl) ; sky upper frame 0
inc hl
ld d,(hl)
inc hl
ex de,hl
ld (skyupper0),hl
ex de,hl
ld e,(hl) ; sky lower frame 0
inc hl
ld d,(hl)
inc hl
ex de,hl
ld (skylower0),hl
ex de,hl
ld e,(hl) ; sky upper frame 1
inc hl
ld d,(hl)
inc hl
ex de,hl
ld (skyupper1),hl
ex de,hl
ld e,(hl) ; sky lower frame 1
inc hl
ld d,(hl)
inc hl
ex de,hl
ld (skylower1),hl
ex de,hl
; okay, copy in transition
ld e,(hl) ; get upper transition byte
inc hl
ld d,(hl) ; get lower transition byte
ld hl,skybuf1uppertrans
ld (hl),e ; set upper transition
ld hl,skybuf1lowertrans
ld (hl),d ; set lower transition
ld a,worldrepeat ; reset world frame
saveworldframe:
ld (worldframe),a
ld hl,(skyupper0)
ld de,skybuf0uppercopy
ld bc,40 ; 40 characters to copy
ldir
ld hl,(skylower0)
ld de,skybuf0lowercopy
ld bc,40 ; 40 characters to copy
ldir
ld hl,(skyupper1)
ld de,skybuf1uppercopy
ld bc,40 ; 40 characters to copy
ldir
ld hl,(skylower1)
ld de,skybuf1lowercopy
ld bc,40 ; 40 characters to copy
ldir ; shift line to the left
ret
;
; Print string at HL
; stops when reaches $FF
;
print:
ld a,(hl) ; load character
inc hl ; increment memory
cp $ff ; last character?
ret z ; yep, return
rst $10 ; nope, print it!
jr print ; loop
;
; Delay
;
; set bc to speed
; press space to
pframe: dw $0000
newpause:
ld (pframe),hl
call kscan ; get key press
inc l
jr z,lppause ; not yet loop
dec l
ld b,h
ld c,l
call findchar ; yep, grab character pressed
ld a,(hl)
push af
call wait
pop af
and a ; pressed space (break)
jp z,stop ; stop!
ret ; or return!
; loop!
lppause:
ld hl,(pframe)
dec hl
ld a,h
or l
jr nz,newpause ; not zero, keep going!
ret ; pause is done!
wait: call kscan ; Wait for human to take finger off of key.
inc l
jr nz,wait
ret
; +++
;
; 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
cls: equ $0a2a
; end defines
; ---
; +++
; Data
;
;
; constants
;
worldrepeat: equ 2 ; repeate each world's sky 10 times
numworlds: equ 4 ; currently two worlds
;
; vars
;
runframe: dw 0 ; runner frame to display (0-3)
skyframe: db 0 ; sky frame to display (0-1)
skycopy: db 0 ; time to copy in sky
world: dw 0 ; world we are on
worldnum: db 0 ; at first world
worldframe: db 0 ; frames to display
; these determe sky to use
skyupper0: dw 0
skylower0: dw 0
skyupper1: dw 0
skylower1: dw 0
worlds:
dw skyupper0_cloudy
dw skylower0_cloudy
dw skyupper1_cloudy
dw skylower1_cloudy
db $00,$00 ; transition from day
dw skyupper0_mountain
dw skylower0_mountain
dw skyupper1_mountain
dw skylower1_mountain
db $00,$87 ; transition from cloudy
dw skyupper0_dark
dw skylower0_dark
dw skyupper1_dark
dw skylower1_dark
db $85,$85 ; transition from mountain
dw skyupper0_day
dw skylower0_day
dw skyupper1_day
dw skylower1_day
db $05,$05 ; transition from dark
;
; runner
;
runner:
db $87, $82, $04, $01, $05, $00, $06, $02, $04
db $87, $82, $00, $02, $05, $01, $87, $84, $00
db $87, $82, $00, $00, $07, $00, $00, $07, $00
db $87, $82, $00, $02, $05, $01, $87, $84, $00
;
; sky
;
skyupper0_cloudy:
db $00, $00, $01, $03, $80, $07, $01, $01, $00, $00
db $00, $00, $83, $00, $00, $87, $07, $04, $00, $00
db $83, $00, $00, $00, $00, $00, $87, $07, $04, $00
db $87, $04, $00, $00, $00, $00, $00, $87, $07, $04
skylower0_cloudy:
db $00, $00, $87, $01, $87, $00, $86, $00, $00, $00
db $83, $81, $82, $01, $03, $03, $03, $00, $87, $81
db $82, $01, $00, $00, $00, $02, $03, $03, $87, $83
db $80, $06, $00, $00, $00, $00, $03, $03, $03, $00
skyupper1_cloudy:
db $00, $02, $02, $84, $80, $03, $02, $00, $00, $00
db $00, $87, $04, $00, $00, $81, $86, $00, $00, $87
db $04, $00, $00, $00, $00, $00, $81, $86, $00, $00
db $83, $00, $00, $00, $00, $00, $00, $81, $86, $00
skylower1_cloudy:
db $00, $00, $06, $00, $04, $02, $04, $00, $00, $87
db $83, $80, $06, $02, $03, $03, $01, $00, $83, $80
db $06, $00, $00, $00, $00, $03, $03, $01, $83, $81
db $82, $01, $00, $00, $00, $02, $03, $03, $01, $00
; old cloud sky
;skyupper0_cloudy:
; db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
; db $83, $00, $00, $87, $07, $04, $00, $00, $00, $00
; db $00, $02, $04, $02, $00, $06, $00, $00, $00, $00
; db $00, $00, $00, $00, $00, $00, $00, $87, $07, $04
;skylower0_cloudy:
; db $00, $00, $00, $00, $00, $00, $00, $00, $83, $81
; db $82, $01, $03, $03, $03, $00, $00, $00, $00, $00
; db $00, $04, $83, $80, $82, $04, $04, $00, $00, $00
; db $00, $00, $00, $00, $00, $00, $03, $03, $03, $00
;skyupper1_cloudy:
; db $00, $00, $00, $00, $00, $00, $00, $00, $00, $87
; db $04, $00, $00, $81, $86, $00, $00, $00, $00, $00
; db $00, $86, $00, $01, $87, $01, $00, $00, $00, $00
; db $00, $00, $00, $00, $00, $00, $00, $81, $86, $00
;skylower1_cloudy:
; db $00, $00, $00, $00, $00, $00, $00, $87, $83, $80
; db $06, $02, $03, $03, $01, $00, $00, $00, $00, $00
; db $87, $87, $81, $80, $83, $87, $00, $00, $00, $00
; db $00, $00, $00, $00, $00, $02, $03, $03, $01, $00
skyupper0_mountain:
db $00, $06, $83, $00, $02, $04, $00, $01, $00, $06
db $00, $87, $06, $86, $04, $87, $86, $00, $00, $06
db $86, $00, $87, $86, $87, $86, $00, $00, $87, $87
db $03, $04, $00, $00, $87, $03, $83, $87, $04, $00
skylower0_mountain:
db $06, $00, $00, $03, $83, $87, $81, $80, $83, $87
db $06, $01, $00, $00, $02, $86, $04, $86, $06, $00
db $00, $86, $01, $00, $86, $00, $86, $87, $01, $86
db $00, $85, $00, $87, $01, $00, $87, $01, $02, $04
skyupper1_mountain:
db $87, $86, $04, $00, $86, $00, $02, $00, $87, $01
db $00, $83, $03, $83, $00, $06, $04, $00, $87, $03
db $04, $00, $06, $04, $06, $04, $00, $00, $04, $06
db $86, $00, $00, $00, $06, $86, $04, $83, $00, $00
skylower1_mountain:
db $01, $00, $02, $86, $04, $83, $80, $82, $04, $83
db $03, $00, $00, $00, $03, $83, $02, $83, $01, $00
db $02, $06, $00, $02, $04, $02, $04, $06, $02, $04
db $00, $05, $00, $06, $00, $00, $06, $00, $86, $87
skyupper0_dark:
db $80, $84, $80, $07, $80, $07, $87, $02, $80, $80
db $03, $80, $80, $84, $80, $07, $80, $80, $80, $80
db $03, $07, $01, $83, $80, $07, $84, $80, $80, $80
db $82, $80, $84, $80, $07, $01, $83, $07, $84, $80
skylower0_dark:
db $80, $80, $81, $80, $80, $00, $01, $04, $85, $83
db $81, $80, $81, $80, $80, $80, $80, $81, $80, $83
db $81, $80, $80, $80, $83, $83, $80, $80, $81, $07
db $80, $80, $80, $81, $80, $80, $82, $83, $80, $84
skyupper1_dark:
db $07, $80, $80, $84, $80, $01, $04, $84, $80, $07
db $84, $80, $07, $80, $80, $84, $80, $80, $80, $07
db $84, $03, $87, $81, $80, $03, $80, $80, $80, $80
db $81, $07, $80, $80, $03, $87, $81, $03, $80, $80
skylower1_dark:
db $80, $82, $80, $80, $05, $02, $87, $00, $82, $83
db $80, $82, $80, $80, $80, $80, $82, $80, $82, $83
db $80, $80, $80, $82, $83, $81, $80, $82, $80, $84
db $80, $80, $82, $80, $80, $80, $83, $81, $07, $80
skyupper0_day:
db $00, $00, $00, $02, $04, $00, $02, $00, $00, $06
db $00, $00, $00, $00, $00, $87, $81, $03, $00, $83
db $83, $00, $00, $00, $00, $87, $81, $01, $00, $87
db $83, $00, $00, $00, $00, $00, $03, $84, $83, $04
skylower0_day:
db $00, $00, $87, $83, $00, $83, $80, $82, $04, $87
db $83, $00, $00, $00, $00, $00, $00, $02, $03, $01
db $00, $00, $00, $03, $84, $83, $00, $00, $02, $03
db $00, $00, $00, $87, $83, $81, $03, $00, $00, $00
skyupper1_day:
db $00, $00, $00, $86, $00, $00, $01, $00, $87, $01
db $00, $00, $00, $00, $00, $83, $07, $01, $87, $83
db $04, $00, $00, $00, $00, $83, $07, $00, $00, $83
db $04, $00, $00, $00, $00, $02, $03, $82, $83, $00
skylower1_day:
db $00, $00, $83, $04, $87, $81, $80, $83, $00, $83
db $04, $00, $00, $00, $00, $00, $00, $03, $03, $00
db $00, $00, $02, $03, $82, $04, $00, $00, $03, $01
db $00, $00, $00, $83, $83, $07, $01, $00, $00, $00
;
; Buffers
;
skybuf0upper:
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
skybuf0uppercopy:
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
skybuf0lower:
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
skybuf0lowercopy:
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
skybuf1upper:
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00
skybuf1uppertrans:
db $00
skybuf1uppercopy:
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
skybuf1lower:
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00
skybuf1lowertrans:
db $00
skybuf1lowercopy:
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
; end data
; ---