diff --git a/.gitignore b/.gitignore index 100c169..765db6a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ tests/*.s examples/*.s !runtime.s !stdlibwrap.s +!sprites.s *.o *.exe *.bin diff --git a/examples/background.pict b/examples/background.pict new file mode 100644 index 0000000..76b99f6 Binary files /dev/null and b/examples/background.pict differ diff --git a/examples/sprites.inc b/examples/sprites.inc new file mode 100644 index 0000000..2b25366 --- /dev/null +++ b/examples/sprites.inc @@ -0,0 +1,5 @@ +type SpritePixels = array[0..31] of integer; +type BackgroundPixels = array[0..31999] of integer; + +procedure PutSprite(x,y:integer; var sprite: SpritePixels); external; +procedure UndrawSprite(x,y:integer; var background: BackgroundPixels); external; diff --git a/examples/sprites.s b/examples/sprites.s new file mode 100644 index 0000000..42a3d0e --- /dev/null +++ b/examples/sprites.s @@ -0,0 +1,363 @@ + .EQU SPRITE_HEIGHT 16 + + .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_FS 28 +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 + + ; + ; process spilled bits and right half of sprite data + ; + + ; 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 + + ; 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 + + ; write spilled bits 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_S_FS 24 +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 + + ; load 2nd word of background data + LOAD UD_S_BGDATA + INC 4 + DUP + STORE UD_S_BGDATA + LOADI + STOREI ; and write it to vmem + DROP + + ; if pixel shift is zero, no 3rd word + LOAD UD_S_PXS + CBRANCH.Z UD_S_L2 + + ; load 3rd word of background data + LOADCP FB_IO + LOAD UD_S_BGDATA + INC 4 + DUP + STORE UD_S_BGDATA + 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 + diff --git a/examples/sprites.sprt b/examples/sprites.sprt new file mode 100644 index 0000000..475bd4d Binary files /dev/null and b/examples/sprites.sprt differ diff --git a/examples/xmas.pas b/examples/xmas.pas new file mode 100644 index 0000000..12b2da3 --- /dev/null +++ b/examples/xmas.pas @@ -0,0 +1,220 @@ +program XmasAnimation; +uses sprites; + +type PictData = record + magic, mode:integer; + palette: array [0..15] of integer; + pixeldata: array [0..31999] of integer; + end; + + Sprite = record + x,y:integer; + oldX,oldY:integer; + xdelta,ydelta:integer; + curFrame:integer; + frameCount:integer; + frameTime:integer; + frameLeft:integer; + changed:boolean; + frame:array [0..3] of SpritePixels; + end; + +var pic:PictData; + filename:string; + infile:file; + ch:char; + santaSprite: Sprite; + deerSprite: Sprite; + ohDeerSprite: Sprite; + rudolfSprite: Sprite; + smokeSprite: Sprite; + +procedure WaitVSync; external; + +procedure loadPalette(var pic:PictData); +var i:integer; +begin + for i := 0 to 15 do + setpalette(i, pic.palette[i]); +end; + +procedure showPic(var pic:PictData); +begin + PutScreen(pic.pixeldata); +end; + +procedure loadSpriteFrame(var aSprite:Sprite;spriteIndex:integer; + var sheetFile:file;sheetIndex:integer); +begin + seek(sheetFile, 8 + sheetIndex * 128); + read(sheetFile, aSprite.frame[spriteIndex]); + if aSprite.frameCount <= spriteIndex then + aSprite.frameCount := spriteIndex + 1; + + aSprite.curFrame := 0; + writeln('loaded sprite frame ', spriteIndex, ' from ', sheetIndex); +end; + +procedure animateSprite(var aSprite:Sprite); +var frameIndex:integer; + frameTime,frameLeft:integer; + ydelta:integer; + oldX,oldY:integer; +begin + ydelta := aSprite.ydelta; + frameIndex := aSprite.curFrame; + frameTime := aSprite.frameTime; + frameLeft := aSprite.frameLeft; + oldX := aSprite.x; oldY := aSprite.y; + aSprite.oldX := oldX; aSprite.oldY := oldY; + + frameLeft := frameLeft - 1; + if frameLeft <= 0 then + begin + frameIndex := frameIndex + 1; + frameLeft := aSprite.frameTime; + aSprite.frameLeft := frameLeft; + aSprite.curFrame := frameIndex; + if frameIndex >= aSprite.frameCount + then + aSprite.curFrame := 0; + + if frameIndex = 1 then + begin + ydelta := - ydelta; + aSprite.ydelta := ydelta; + end; + aSprite.y := aSprite.y + ydelta; + end; + + aSprite.frameLeft := frameLeft; + + aSprite.x := aSprite.x + aSprite.xdelta; + if aSprite.x > 620 then aSprite.x := 0; +end; + +procedure animate; +var i:integer; + ydelta:integer; + frameIndex:integer; + frameTime:integer; + oldX,oldY:integer; +begin + santaSprite.x := 0; + santaSprite.y := 60; + santaSprite.frameTime := 10; + santaSprite.xdelta := 2; + santaSprite.ydelta := 1; + + smokeSprite.x := 434; + smokeSprite.y := 252; + smokeSprite.frameTime := 20; + + deerSprite.x := 18; + deerSprite.y := 60; + deerSprite.frameTime := 10; + deerSprite.xdelta := 2; + deerSprite.ydelta := 1; + + ohDeerSprite.x := 33; + ohDeerSprite.y := 61; + ohDeerSprite.frameTime := 10; + ohDeerSprite.xdelta := 2; + ohDeerSprite.ydelta := 1; + + rudolfSprite.x := 49; + rudolfSprite.y := 60; + rudolfSprite.frameTime := 10; + rudolfSprite.xdelta := 2; + rudolfSprite.ydelta := 1; + + ydelta := 1; + + frameTime := santaSprite.frameTime; + + while not ConAvail do + begin + frameIndex := santaSprite.curFrame; + oldX := santaSprite.x; oldY := santaSprite.y; + PutSprite(oldX, oldY, santaSprite.frame[frameIndex]); + i := i + 1; + frameTime := frameTime - 1; + if frameTime = 0 then + begin + frameTime := santaSprite.frameTime; + santaSprite.curFrame := frameIndex + 1; + if frameIndex >= santaSprite.frameCount + then + santaSprite.curFrame := 0; + + if frameIndex = 0 then ydelta := - ydelta; + santaSprite.y := santaSprite.y + ydelta; + end; + santaSprite.x := santaSprite.x + 2; + if santaSprite.x > 620 then santaSprite.x := 0; + + PutSprite(deerSprite.x, deerSprite.y, + deerSprite.frame[deerSprite.curFrame]); + + PutSprite(ohDeerSprite.x, ohDeerSprite.y, + ohDeerSprite.frame[ohDeerSprite.curFrame]); + + PutSprite(rudolfSprite.x, rudolfSprite.y, + rudolfSprite.frame[rudolfSprite.curFrame]); + + PutSprite(smokeSprite.x, smokeSprite.y, + smokeSprite.frame[smokeSprite.curFrame]); + + animateSprite(deerSprite); + animateSprite(ohDeerSprite); + animateSprite(rudolfSprite); + animateSprite(smokeSprite); + + Delay(10); + WaitVSync; + + UndrawSprite(oldX, oldY, pic.pixeldata); + UndrawSprite(deerSprite.oldX, deerSprite.oldY, pic.pixeldata); + UndrawSprite(ohDeerSprite.oldX, ohDeerSprite.oldY, pic.pixeldata); + UndrawSprite(rudolfSprite.oldX, rudolfSprite.oldY, pic.pixeldata); + UndrawSprite(smokeSprite.oldX, smokeSprite.oldY, pic.pixeldata); + end; +end; + + +begin + filename := 'background.pict'; + open(infile, filename, ModeReadonly); + read(infile, pic); + close(infile); + + writeln('magic: ', pic.magic, ' mode:', pic.mode); + + loadPalette(pic); + showPic(pic); + + open(infile, 'sprites.sprt', ModeReadOnly); + loadSpriteFrame(santaSprite, 0, infile, 0); + loadSpriteFrame(santaSprite, 1, infile, 1); + + loadSpriteFrame(deerSprite, 0, infile, 5); + loadSpriteFrame(deerSprite, 1, infile, 6); + loadSpriteFrame(deerSprite, 2, infile, 7); + + loadSpriteFrame(ohDeerSprite, 0, infile, 7); + loadSpriteFrame(ohDeerSprite, 1, infile, 5); + loadSpriteFrame(ohDeerSprite, 2, infile, 6); + + loadSpriteFrame(rudolfSprite, 0, infile, 3); + loadSpriteFrame(rudolfSprite, 1, infile, 4); + loadSpriteFrame(rudolfSprite, 2, infile, 2); + + loadSpriteFrame(smokeSprite, 0, infile, 8); + loadSpriteFrame(smokeSprite, 1, infile, 9); + loadSpriteFrame(smokeSprite, 2, infile, 10); + loadSpriteFrame(smokeSprite, 3, infile, 11); + + close(infile); + + animate; +end. diff --git a/tridoracpu/tridoracpu.xpr b/tridoracpu/tridoracpu.xpr index 5e4a64f..a4c6545 100644 --- a/tridoracpu/tridoracpu.xpr +++ b/tridoracpu/tridoracpu.xpr @@ -25,7 +25,7 @@