.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 $904 .EQU FB_IO $908 .EQU FB_PS $90C .EQU FB_PD $910 .EQU FB_CTL $914 .EQU FB_SHIFTER $918 .EQU FB_SHIFTCOUNT $91C .EQU FB_SHIFTERM $920 .EQU FB_SHIFTERSP $924 .EQU FB_MASKGEN $928 ; 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_BPSAVE 32 .EQU PS_FS 36 PUTSPRITE: FPADJ -PS_FS STORE PS_SPRITE_DATA STORE PS_Y STORE PS_X LOADREG BP STORE PS_BPSAVE LOADC 0 STOREREG BP ; calculate vmem address LOAD PS_X LOAD PS_Y LOADCP CALC_VMEM_ADDR CALL STORE PS_VMEM_ADDR 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 LOAD PS_VMEM_ADDR DUP STORE.B FB_RA STORE.B FB_WA 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 ; ------- one word of sprite pixels on stack STORE.B FB_SHIFTER LOAD PS_X STORE.B FB_SHIFTCOUNT LOAD.B FB_SHIFTERM ; get shifted mask NOT LOAD.B FB_IO ; and background pixel data AND ; remove foreground pixels LOAD.B FB_SHIFTER ; get shifted pixels OR ; combine with background STORE.B FB_IO ; store into vmem ; 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: ;use spill bits from first column LOAD.B FB_SHIFTERSP 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 STORE.B FB_SHIFTER ; store into shifter LOAD PS_X STORE.B FB_SHIFTCOUNT ; shift stuff LOAD.B FB_SHIFTER ; get shifted pixels OR ; combine with spill bits (see above) DUP STORE.B FB_MASKGEN ; store to mask reg to get new mask LOAD.B FB_MASKGEN ; get mask for spill bits + shifted pixels NOT LOAD.B FB_IO ; get vmem data AND ; remove foreground pixels from bg OR ; combine with shifted pixels STORE.B FB_IO ; write to vmem 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.B FB_SHIFTERSP ; get spill bits DUP STORE.B FB_MASKGEN LOAD.B FB_MASKGEN ; get sprite mask for spill bits NOT LOAD.B FB_IO ; load next vmem word AND ; apply sprite mask OR ; OR in spill bits STORE.B FB_IO ; write to vmem 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 LOAD PS_BPSAVE STOREREG BP 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 4 ; 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