Tridora-CPU/examples/sprites.s
2025-06-20 01:19:40 +02:00

388 lines
7.3 KiB
ArmAsm

.EQU SPRITE_HEIGHT 32 ; height in lines
.EQU SPRITE_STRIPES 4 ; width in words i.e. 8-pixel stripes
.EQU WORDS_PER_LINE 80
.EQU FB_RA $900
.EQU FB_WA $901
.EQU FB_IO $902
.EQU FB_PS $903
; calculate mask for a word of pixels
; args: word of pixels with four bits per pixel
; returns: value that masks out all pixels that are set
CALC_MASK:
LOADC $F ; pixel mask
C_M_L0:
SWAP ; swap mask and pixels value
AND.S1.X2Y ; isolate one pixel, keep args
CBRANCH.Z C_M_L1 ; if pixel is zero, dont set mask bits
OVER ; copy current mask
OR ; or into pixels value
C_M_L1:
SWAP ; swap back, ToS is now mask bits
SHL 2 ; shift mask for next pixel to the left
SHL 2
DUP
CBRANCH.NZ C_M_L0 ; if mask is zero, we are done
DROP ; remove mask bits
NOT ; invert result
RET
; calculate vmem address from coordinates
; args: x,y
; returns: vmem word number
CALC_VMEM_ADDR:
; only works if WORDS_PER_LINE is 80
; and pixels per word is 8
DUP
; y
SHL 2
SHL 2
SHL 2 ; * 64
SWAP
; + y
SHL 2
SHL 2 ; * 16
ADD
SWAP
; word offset = X/8
SHR
SHR
SHR
ADD
RET
; put a sprite on screen
; arg: x,y pointer to sprite data
.EQU PS_VMEM_ADDR 0
.EQU PS_SPRITE_DATA 4
.EQU PS_SPRITE_LINES 8
.EQU PS_X 12
.EQU PS_Y 16
.EQU PS_SHIFT_C 20
.EQU PS_SPILL 24
.EQU PS_STRIPE_C 28
.EQU PS_FS 32
PUTSPRITE:
FPADJ -PS_FS
STORE PS_SPRITE_DATA
STORE PS_Y
STORE PS_X
; calculate vmem address
LOAD PS_X
LOAD PS_Y
LOADCP CALC_VMEM_ADDR
CALL
STORE PS_VMEM_ADDR
LOAD PS_X ; shift count = x mod 8
LOADC 7
AND
STORE PS_SHIFT_C
LOADC SPRITE_HEIGHT
STORE PS_SPRITE_LINES
; loop over each line of the sprite
PS_LOOP1:
; set read and write address
; in the vga controller
LOADC FB_RA ; read address register
LOAD PS_VMEM_ADDR
STOREI 1 ; use autoincrement to get to the next register
LOAD PS_VMEM_ADDR
STOREI
DROP
LOAD PS_SPRITE_DATA ; address of sprite data
DUP
INC 4 ; increment pointer
STORE PS_SPRITE_DATA ; and store it again
LOADI ; load word from orig. address
LOADC 0
STORE PS_SPILL
; loop to shift pixel data to right
LOAD PS_SHIFT_C ; load shift count
PS_LOOP2:
DUP ; test it for zero
CBRANCH.Z PS_LOOP2_X
SWAP ; swap count with pixels
; save the pixel that is shifted out
LOADC $F ; mask the four bits
AND.S0 ; keep original value on stack
BROT ; and move them to MSB
BROT
BROT
SHL 2
SHL 2 ; shift by 28 in total
LOAD PS_SPILL ; load spill bits
SHR ; shift by four to make space
SHR
SHR
SHR
OR ; or with orig value
STORE PS_SPILL ; store new value
SHR ; shift pixels right
SHR ; four bits per pixel
SHR
SHR
SWAP ; swap back, count now ToS
DEC 1
BRANCH PS_LOOP2
PS_LOOP2_X:
DROP ; remove shift count, shifted pixels now in ToS
DUP
LOADCP CALC_MASK ; calculate sprite mask for this word
CALL
LOADCP FB_IO ; address of the i/o register
LOADI ; read word from video mem
AND ; and word with mask
OR ; OR sprite data with original pixels
LOADCP FB_IO
SWAP
STOREI ; store result into i/o reg
DROP
; set counter for remaining stripes
LOADC SPRITE_STRIPES - 1
STORE PS_STRIPE_C
;
; process spilled bits and next vertical stripe of sprite data
;
PS_NEXT_STRIPE:
; put spill bits on stack for later
LOAD PS_SPILL
LOAD PS_SPRITE_DATA ; address of sprite data
DUP
INC 4 ; increment pointer
STORE PS_SPRITE_DATA ; and store it again
LOADI ; load word from orig. address
; reset spill bits
LOADC 0
STORE PS_SPILL
; last spill bits are on ToS now
; shift pixel data to right
LOAD PS_SHIFT_C ; load shift count
PS_LOOP3: ; test it for zero
DUP
CBRANCH.Z PS_LOOP3_X
SWAP ; swap count with pixels
; save the pixel that is shifted out
LOADC $F ; mask the four bits
AND.S0 ; keep original value on stack
BROT ; and move them to MSB
BROT
BROT
SHL 2
SHL 2 ; shift by 28 in total
LOAD PS_SPILL ; load spill bits
SHR ; shift by four to make space
SHR
SHR
SHR
OR ; or with orig value
STORE PS_SPILL ; store new value
SHR ; shift pixels right
SHR ; four bits per pixel
SHR
SHR
SWAP ; swap back, count now ToS
DEC 1
BRANCH PS_LOOP3
PS_LOOP3_X:
DROP ; remove shift count, shifted pixels now in ToS
OR ; or together with spill bits
DUP
LOADCP CALC_MASK ; calculate sprite mask
CALL
LOADCP FB_IO ; load original pixels
LOADI
AND ; and with mask
OR ; or together with original pixels
LOADCP FB_IO
SWAP
STOREI
DROP
LOAD PS_STRIPE_C ; decrement stripe count
DEC 1
DUP
STORE PS_STRIPE_C
CBRANCH.NZ PS_NEXT_STRIPE ; if non-zero, next stripe
; write spilled bits of the last stripe into next vmem word
LOAD PS_SPILL ; get spill bits
DUP
LOADCP CALC_MASK ; calculate sprite mask for spill bits
CALL
LOADCP FB_IO
LOADI ; load next vmem word
AND ; apply sprite mask
OR ; OR in spill bits
LOADCP FB_IO
SWAP ; swap pixels and addr
STOREI ; write back
DROP
LOAD PS_SPRITE_LINES ; decrement lines count
DEC 1
DUP
CBRANCH.Z PS_L_XT ; exit if zero
STORE PS_SPRITE_LINES
; prepare next line
LOAD PS_VMEM_ADDR
LOADC WORDS_PER_LINE ; increment to next screen line
ADD
STORE PS_VMEM_ADDR
BRANCH PS_LOOP1
PS_L_XT:
DROP
FPADJ PS_FS
RET
; undraw a sprite, i.e. draw background data
; over a sprite location
; args: x,y, ptr to background data
.EQU UD_S_X 0
.EQU UD_S_Y 4
.EQU UD_S_PXS 8
.EQU UD_S_BGDATA 12
.EQU UD_S_OFFSET 16
.EQU UD_S_BGORIG 20
.EQU UD_STRIPE_C 24
.EQU UD_S_FS 28
UNDRAWSPRITE:
FPADJ -UD_S_FS
STORE UD_S_BGORIG
STORE UD_S_Y
STORE UD_S_X
; calculate pixel shift
LOAD UD_S_X
LOADC $7
AND
STORE UD_S_PXS
; calculate vmem offset
LOAD UD_S_X
LOAD UD_S_Y
LOADCP CALC_VMEM_ADDR
CALL
DUP
STORE UD_S_OFFSET
; calculate background data address from offset
SHL 2
LOAD UD_S_BGORIG
ADD
STORE UD_S_BGDATA
LOADC SPRITE_HEIGHT ; line count
UD_S_L1:
; store vmem offset into write addr reg
LOADCP FB_WA
LOAD UD_S_OFFSET
STOREI 1 ; ugly but fast: reuse addr
; with postincrement to
; get to FB_IO for STOREI below
; load a word of background data
LOAD UD_S_BGDATA
LOADI
; and write it to vmem
STOREI
; reuse addr from STOREI
LOADC SPRITE_STRIPES - 1 ; set remaining stripe count
STORE UD_STRIPE_C
UD_NEXT_STRIPE:
; load next word of background data
LOAD UD_S_BGDATA
INC 4
DUP
STORE UD_S_BGDATA
LOADI
STOREI ; and write it to vmem
; reuse addr from STOREI
LOAD UD_STRIPE_C ; decrease remaining stripe count
DEC 1
DUP
STORE UD_STRIPE_C
CBRANCH.NZ UD_NEXT_STRIPE ; if non-zero, next stripe
DROP ; remove addr from STOREI
; if pixel shift is zero, no spill word
LOAD UD_S_PXS
CBRANCH.Z UD_S_L2
; load next word of background data
LOADCP FB_IO
LOAD UD_S_BGDATA
INC 4
LOADI
STOREI ; and write it to vmem
DROP
UD_S_L2:
LOAD UD_S_OFFSET
LOADCP WORDS_PER_LINE
ADD
DUP
STORE UD_S_OFFSET
SHL 2
LOAD UD_S_BGORIG
ADD
STORE UD_S_BGDATA
DEC 1 ; decrement counter
DUP
CBRANCH.NZ UD_S_L1 ; check for zero
DROP ; remove counter
FPADJ UD_S_FS
RET