diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index db6741b..903dba7 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -11,6 +11,8 @@ mx %00 +TSZelda EXT ; tileset buffer + ScreenX equ 0 ScreenY equ 2 @@ -29,12 +31,41 @@ ScreenY equ 2 pea #160 _GTESetScreenMode -; Load a tileset in from an uncompressed $C1 picture. The top-left 256x128 rectangle is used -; to populate the 512 tiles. +; Load a tileset + pea #^TSZelda + pea #TSZelda + _GTELoadTileSet ; Manually fill in the 41x26 tiles of the TileStore with a test pattern. + ldx #0 + ldy #0 + +:loop + phx + phy + + phx + phy + pei 0 + _GTESetTile + + lda 0 + inc + and #$001F + sta 0 + + ply + plx + inx + cpx #41 + bcc :loop + + ldx #0 + iny + cpy #26 + bcc :loop ; Set the origin of the screen stz ScreenX @@ -53,7 +84,7 @@ ScreenY equ 2 pei ScreenY _GTESetBG0Origin -; _GTERender + _GTERender inc ScreenX ; Just keep incrementing, it's OK bra :loop diff --git a/demos/tool/App.s b/demos/tool/App.s index 3bd4b35..097e4d1 100644 --- a/demos/tool/App.s +++ b/demos/tool/App.s @@ -8,3 +8,8 @@ ASM App.Main.s SNA Main + +; Segment #2 -- Tileset + + ASM Zelda.TileSet.s + SNA TSET diff --git a/macros/CORE.MACS.S b/macros/CORE.MACS.S index bb29051..29fe055 100644 --- a/macros/CORE.MACS.S +++ b/macros/CORE.MACS.S @@ -325,3 +325,57 @@ transparent sta: ]3+1,y next eom + +; Large code blocks that can be used in sprite blitters +; ]1: line number +OneSpriteToCodeField mac + lda blttmp+{]1*4} + andl spritemask+{]1*SPRITE_PLANE_SPAN},x + oral spritedata+{]1*SPRITE_PLANE_SPAN},x + sta: $0004+{]1*$1000},y + + lda blttmp+{]1*4}+2 + andl spritemask+{]1*SPRITE_PLANE_SPAN}+2,x + oral spritedata+{]1*SPRITE_PLANE_SPAN}+2,x + sta: $0001+{]1*$1000},y + eom + +TwoSpritesToCodeField mac + ldy #{]1*SPRITE_PLANE_SPAN} + lda blttmp+{]1*4} + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0004+{]1*$1000},x + + ldy #{]1*SPRITE_PLANE_SPAN}+2 + lda blttmp+{]1*4}+2 + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0001+{]1*$1000},x + eom + +ThreeSpritesToCodeField mac + ldy #{]1*SPRITE_PLANE_SPAN} + lda blttmp+{]1*4} + andl [spritemask_2],y + oral [spritedata_2],y + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0004+{]1*$1000},x + + ldy #{]1*SPRITE_PLANE_SPAN}+2 + lda blttmp+{]1*4}+2 + andl [spritemask_2],y + oral [spritedata_2],y + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0001+{]1*$1000},x + eom diff --git a/src/CoreImpl.s b/src/CoreImpl.s index 46ce903..04931a4 100644 --- a/src/CoreImpl.s +++ b/src/CoreImpl.s @@ -150,8 +150,8 @@ IntShutDown OneSecHandler mx %11 phb pha - phk - plb + + jsr _SetDataBank rep #$20 inc OneSecondCounter @@ -167,10 +167,7 @@ OneSecHandler mx %11 rtl mx %00 -OneSecondCounter ENT - dw 0 -OldOneSecVec ds 4 - +; This is OK, it's referenced by a long address VBLTASK hex 00000000 dw 0 hex 5AA5 @@ -270,11 +267,6 @@ ClearKbdStrobe sep #$20 rts ; Read the keyboard and paddle controls and return in a game-controller-like format -LastKey db 0 -ReadControl ENT - jsr _ReadControl - rtl - _ReadControl pea $0000 ; low byte = key code, high byte = %------AB diff --git a/src/Defs.s b/src/Defs.s index 468528b..6c5070c 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -92,7 +92,10 @@ TileStoreBankAndTileDataBank equ 108 TileStoreBankDoubled equ 110 UserId equ 112 ; Memory manager user Id to use ToolNum equ 114 ; Tool number assigned to us -Next equ 116 +LastKey equ 116 +LastTick equ 118 + +Next equ 120 activeSpriteList equ 128 ; 32 bytes for the active sprite list (can persist across frames) ; tiletmp equ 178 ; 16 bytes of temp storage for the tile renderers @@ -141,8 +144,9 @@ _OP_CACHE equ 156 ; Cache of a relevant operand / oeprato _TILE_ID equ 158 ; Copy of the tile descriptor ; Define free space the the application to use -FREE_SPACE_DP2 equ 160 - +; FREE_SPACE_DP2 equ 160 +DP2_DIRTY_TILE_COUNT equ 160 ; Local copy of dirty tile count to avoid banking +DP2_DIRTY_TILE_CALLBACK equ 162 ; End direct page values ; EngineMode definitions @@ -197,6 +201,7 @@ SPRITE_HFLIP equ $0200 ; Stamp storage parameters VBUFF_STRIDE_BYTES equ 12*4 ; Each line has 4 slots of 16 pixels + 8 buffer pixels VBUFF_TILE_ROW_BYTES equ 8*VBUFF_STRIDE_BYTES ; Each row is comprised of 8 lines +VBUFF_TILE_COL_BYTES equ 4 VBUFF_SPRITE_STEP equ VBUFF_TILE_ROW_BYTES*3 ; Allocate space fo 16 rows + 8 rows of buffer VBUFF_SPRITE_START equ {8*VBUFF_TILE_ROW_BYTES}+4 ; Start at an offset so $0000 can be used as an empty value VBUFF_SLOT_COUNT equ 48 ; Have space for this many stamps @@ -204,31 +209,32 @@ VBUFF_SLOT_COUNT equ 48 ; Have space for this m ; This is 13 blocks wide SPRITE_PLANE_SPAN equ VBUFF_STRIDE_BYTES -; Tile storage parameters -TILE_DATA_SPAN equ 4 -TILE_STORE_WIDTH equ 41 -TILE_STORE_HEIGHT equ 26 -MAX_TILES equ {26*41} ; Number of tiles in the code field (41 columns * 26 rows) -TILE_STORE_SIZE equ {MAX_TILES*2} ; The tile store contains a tile descriptor in each slot - -TS_TILE_ID equ TILE_STORE_SIZE*0 ; tile descriptor for this location -TS_DIRTY equ TILE_STORE_SIZE*1 ; Flag. Used to prevent a tile from being queued multiple times per frame -TS_SPRITE_FLAG equ TILE_STORE_SIZE*2 ; Bitfield of all sprites that intersect this tile. 0 if no sprites. -TS_TILE_ADDR equ TILE_STORE_SIZE*3 ; cached value, the address of the tiledata for this tile -TS_CODE_ADDR_LOW equ TILE_STORE_SIZE*4 ; const value, address of this tile in the code fields -TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5 -TS_WORD_OFFSET equ TILE_STORE_SIZE*6 ; const value, word offset value for this tile if LDA (dp),y instructions re used -TS_BASE_ADDR equ TILE_STORE_SIZE*7 ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000. -TS_SCREEN_ADDR equ TILE_STORE_SIZE*8 ; cached value of on-screen location of tile. Used for DirtyRender. -;TS_VBUFF_ARRAY_ADDR equ TILE_STORE_SIZE*9 ; const value to an aligned 32-byte array starting at $8000 in TileStore bank - -TS_BASE_TILE_COPY equ TILE_STORE_SIZE*9 ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering -TS_BASE_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function -TS_DIRTY_TILE_DISP equ TILE_STORE_SIZE*11 ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function - -; Hold values for up to 4 sprites per tile -TS_VBUFF_ADDR_0 equ TILE_STORE_SIZE*12 -TS_VBUFF_ADDR_1 equ TILE_STORE_SIZE*13 -TS_VBUFF_ADDR_2 equ TILE_STORE_SIZE*14 -TS_VBUFF_ADDR_3 equ TILE_STORE_SIZE*15 -TS_VBUFF_ADDR_COUNT equ TILE_STORE_SIZE*16 ; replace usage of TS_VBUFF_ARRAY_ADDR with this later +; External references to data bank +TileStore EXT +DirtyTileCount EXT +DirtyTiles EXT +_Sprites EXT +TileStore EXT +TileStoreLookupYTable EXT +TileStoreLookup EXT +Col2CodeOffset EXT +JTableOffset EXT +CodeFieldEvenBRA EXT +CodeFieldOddBRA EXT +ScreenAddr EXT +TileStoreYTable EXT +NextCol EXT +RTable EXT +BlitBuff EXT +BTableHigh EXT +BTableLow EXT +BRowTableHigh EXT +BRowTableLow EXT +BG1YTable EXT +BG1YOffsetTable EXT +OldOneSecVec EXT +OneSecondCounter EXT +Timers EXT +DefaultPalette EXT +ScreenModeWidth EXT +ScreenModeHeight EXT diff --git a/src/GTE.s b/src/GTE.s index 803c929..dd30f36 100644 --- a/src/GTE.s +++ b/src/GTE.s @@ -73,21 +73,3 @@ AllocBank EXT ScreenAddr EXT OneSecondCounter EXT BlitBuff EXT - -;; Helper function to load the GTE User Toolset -;GTEInstall -; php -; ~InitialLoad userId;localToolPath;#0 - -; pea $8000 ; User tool -; pea $00A5 ; Tool 165 -; PushLong toolPtr -; _SetTSPtr - -; plp -; rtl - -; Look for the tool set in the System Tools folder and then next to the application -;sysToolPath strl '*:System:Tools:ToolGTE' -;localToolPath strl '9:ToolGTE' -;toolPtr adrl 0 \ No newline at end of file diff --git a/src/Graphics.s b/src/Graphics.s index 8e9f175..25166f2 100644 --- a/src/Graphics.s +++ b/src/Graphics.s @@ -24,11 +24,6 @@ InitGraphics :no_bg1 rts -DefaultPalette dw $0000,$007F,$0090,$0FF0 - dw $000F,$0080,$0f70,$0FFF - dw $0fa9,$0ff0,$00e0,$04DF - dw $0d00,$078f,$0ccc,$0FFF - ; Allow the user to dynamically select one of the pre-configured screen sizes, or pass ; in a specific width and height. The screen is automatically centered. If this is ; not desired, then SetScreenRect should be used directly @@ -47,18 +42,6 @@ DefaultPalette dw $0000,$007F,$0090,$0FF0 ; ; X = mode number OR width in pixels (must be multiple of 2) ; Y = height in pixels (if X > 8) - -ScreenModeWidth dw 320,272,256,256,280,256,240,288,160,288,160,320 -ScreenModeHeight dw 200,192,200,176,160,160,160,128,144,192,102,1 - -SetScreenMode ENT - phb - phk - plb - jsr _SetScreenMode - plb - rtl - _SetScreenMode cpx #11 bcs :direct ; if x > 10, then assume X and Y are the dimensions @@ -113,10 +96,6 @@ _GetBorderColor lda #0000 rts ; Set the border color to the accumulator value. -SetBorderColor ENT - jsr _SetBorderColor - rtl - _SetBorderColor sep #$20 ; ACC = $X_Y, REG = $W_Z eorl BORDER_REG ; ACC = $(X^Y)_(Y^Z) and #$0F ; ACC = $0_(Y^Z) @@ -135,17 +114,6 @@ _ClearToColor rts ; Set a palette values -; A = high word of palette data pointer, X = low word of palette data pointer, Y = palette number -SetPalette ENT - phb ; save old data bank - pha ; push 16-bit value - plb ; pop 8-bit bank register - tya - jsr _SetPalette - plb ; pop the other half of the 16-bit push off - plb ; restore the original data bank - rtl - ; A = palette number, X = palette address _SetPalette and #$000F ; palette values are 0 - 15 and each palette is 32 bytes @@ -307,7 +275,7 @@ SetScreenRect sty ScreenHeight ; Save the screen height and ldx #0 ldy #0 :tsloop - stal TileStore+TS_SCREEN_ADDR,x + sta TileStore+TS_SCREEN_ADDR,x clc adc #4 ; Go to the next tile diff --git a/src/Memory.s b/src/Memory.s index 8572897..441e2ae 100644 --- a/src/Memory.s +++ b/src/Memory.s @@ -28,9 +28,9 @@ InitMemory lda EngineMode _NewHandle ; returns LONG Handle on stack plx ; base address of the new handle pla ; high address 00XX of the new handle (bank) - _Deref - stx Buff00 - sta Buff00+2 +; _Deref +; stx Buff00 +; sta Buff00+2 :no_bnk0_buff PushLong #0 ; space for result @@ -41,9 +41,9 @@ InitMemory lda EngineMode _NewHandle ; returns LONG Handle on stack plx ; base address of the new handle pla ; high address 00XX of the new handle (bank) - _Deref - stx Buff01 - sta Buff01+2 +; _Deref +; stx Buff01 +; sta Buff01+2 PushLong #0 ; space for result @@ -153,8 +153,8 @@ InitMemory lda EngineMode :exit rts -Buff00 ds 4 -Buff01 ds 4 +;Buff00 ds 4 +;Buff01 ds 4 ; Bank allocator (for one full, fixed bank of memory. Can be immediately deferenced) @@ -172,14 +172,6 @@ AllocOneBank PushLong #0 rts ; Variation that returns the pointer in the X/A registers (X = low, A = high) -AllocBank ENT - phb - phk - plb - jsr AllocOneBank2 - plb - rtl - AllocOneBank2 PushLong #0 PushLong #$10000 PushWord UserId @@ -190,5 +182,3 @@ AllocOneBank2 PushLong #0 pla ; high address 00XX of the new handle (bank) _Deref rts - - diff --git a/src/Render.s b/src/Render.s index cba02a2..cb95762 100644 --- a/src/Render.s +++ b/src/Render.s @@ -21,12 +21,12 @@ ; used in all of the other loops _Render jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen - jsr _ApplyBG1YPos +; jsr _ApplyBG1YPos ; _ApplyBG0Xpos need to be split because we have to set the offsets, then draw in any updated tiles, and ; finally patch out the code field. Right now, the BRA operand is getting overwritten by tile data. jsr _ApplyBG0XPosPre - jsr _ApplyBG1XPosPre +; jsr _ApplyBG1XPosPre ; jsr _RenderSprites ; Once the BG0 X and Y positions are committed, update sprite data @@ -36,7 +36,7 @@ _Render jsr _ApplyTilesFast ; This function actually draws the new tiles into the code field jsr _ApplyBG0XPos ; Patch the code field instructions with exit BRA opcode - jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position +; jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position ; The code fields are locked in now and ready to be rendered @@ -99,26 +99,13 @@ _ApplyTilesFast adc #$100 ; move to the next page tcd - ldy DirtyTileCount - beq :out - -:loop -; Retrieve the offset of the next dirty Tile Store items in the X-register + lda DirtyTileCount ; Cache the dirty tile count + sta DP2_DIRTY_TILE_COUNT jsr _PopDirtyTile2 -; Call the generic dispatch with the Tile Store record pointer at by the X-register. + stz DirtyTileCount - phb - jsr _RenderTileFast - plb - -; Loop again until the list of dirty tiles is empty - - ldy DirtyTileCount - bne :loop - -:out tdc ; Move back to the original direct page sec sbc #$100 diff --git a/src/Script.s b/src/Script.s index bbeb01d..2771c8b 100644 --- a/src/Script.s +++ b/src/Script.s @@ -35,8 +35,7 @@ ; timer's user data section. StartScript ENT phb - phk - plb + jsr _SetDataBank phx ; Save the script array address pha @@ -69,14 +68,6 @@ ARG1 equ 2 ARG2 equ 4 ARG3 equ 6 -DoScriptSeq ENT - phb - phk - plb - jsl _DoScriptSeq ; Yes, this is a special JSL, because _DoScriptSeq is a time callback - plb - rtl - _DoScriptSeq phx ; save the timer index; will need to update user data at the end phb ; save the current data bank @@ -180,9 +171,9 @@ _SetDTile _UserCallback lda: ARG1,y - sta :dispatch+1 + stal :dispatch+1 lda: ARG1+1,y - sta :dispatch+2 + stal :dispatch+2 lda: ARG3,y :dispatch jsl $000000 brl _dss_cmd_rtn diff --git a/src/Sprite.s b/src/Sprite.s index 7419d26..506ed77 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -27,24 +27,6 @@ InitSprites ; dex ; bpl :loop3 -; Initialize the VBUFF address offsets in the data and mask banks for each sprite -; -; The internal grid 12 tiles wide where each sprite has a 2x2 interior square with a -; tile-size buffer all around. We pre-render each sprite with all four vert/horz flips -; -; Eventually we should be able to have a separate rendering path for vertically flipped -; sprites and will be able to double the capacity of the stamp buffer - - ldx #0 - lda #VBUFF_SPRITE_START - clc -:loop4 sta VBuffAddrTable,x - adc #VBUFF_SPRITE_STEP - inx - inx - cpx #VBUFF_SLOT_COUNT*2 - bcc :loop4 - ; Precalculate some bank values jsr _CacheSpriteBanks rts @@ -68,38 +50,25 @@ InitSprites ; the stamp every time. So this allows users to create stamps in advance and then ; assign them to the sprites as needed. ; -; Currently, we support a maximum of 48 stamps. +; Note that the user had full freedom to create a stamp at any VBUFF address, however, +; without leaving a buffer around each stamp, graphical corruption will occur. It is +; recommended that the defines for VBUFF_SPRITE_START, VBUFF_TILE_ROW_BYTES and +; VBUFF_TILE_COL_BYTES to calculate tile-aligned corner locations to lay out the +; sprite stamps in VBUFF memory. ; ; Input: ; A = sprite descriptor -; X = stamp slot -; Return: -; A = vbuff address to be assigned to Sprite[VBUFF_ADDR] -CreateSpriteStamp ENT - phb - phk - plb - jsr _CreateSpriteStamp - plb - rtl - +; Y = vbuff address +; +; The Sprite[VBUFF_ADDR] property must be set to the vbuff address passed into this function +; to bind the sprite stamp to the sprite record. _CreateSpriteStamp pha ; Save the descriptor jsr _GetBaseTileAddr ; Get the address of the tile data - pha - txa - asl - tax - ldy VBuffAddrTable,x ; Load the address of the stamp slot - - plx ; Pop the tile address + tax ; Tile data address pla ; Pop the sprite ID - phy ; VBUFF_ADDR value - jsr _DrawSpriteStamp ; Render the sprite data and create a stamp - - pla ; Pop the VBUFF_ADDR and return - rts + jmp _DrawSpriteStamp ; Render the sprite data and create a stamp ; Add a new sprite to the rendering pipeline ; @@ -123,14 +92,6 @@ _CreateSpriteStamp ; A = tileId + flags ; Y = High Byte = x-pos, Low Byte = y-pos ; X = Sprite Slot (0 - 15) -AddSprite ENT - phb - phk - plb - jsr _AddSprite - plb - rtl - _AddSprite pha txa @@ -888,14 +849,6 @@ _PrecalcAllSpriteInfo ; picked up in the next AddSprite. ; ; A = Sprite ID -RemoveSprite ENT - phb - phk - plb - jsr _RemoveSprite - plb - rtl - _RemoveSprite cmp #MAX_SPRITES bcc :ok @@ -917,14 +870,6 @@ _RemoveSprite ; A = Sprite ID ; X = New Sprite Flags ; Y = New Sprite Stamp Address -UpdateSprite ENT - phb - phk - plb - jsr _UpdateSprite - plb - rtl - _UpdateSprite cmp #MAX_SPRITES bcc :ok @@ -961,14 +906,6 @@ _UpdateSprite ; A = sprite ID ; X = x position ; Y = y position -MoveSprite ENT - phb - phk - plb - jsr _MoveSprite - plb - rtl - _MoveSprite cmp #MAX_SPRITES bcc :ok @@ -999,57 +936,3 @@ _MoveSprite sta _Sprites+SPRITE_STATUS,x jmp _PrecalcAllSpriteInfo ; Can be specialized to only update (x,y) values - -; Sprite data structures. We cache quite a few pieces of information about the sprite -; to make calculations faster, so this is hidden from the caller. -; -; -; Number of "off-screen" lines above logical (0,0) -; NUM_BUFF_LINES equ 24 - -MAX_SPRITES equ 16 -SPRITE_REC_SIZE equ 52 - -; Mark each sprite as ADDED, UPDATED, MOVED, REMOVED depending on the actions applied to it -; on this frame. Quick note, the same Sprite ID cannot be removed and added in the same frame. -; A REMOVED sprite if removed from the sprite list during the Render call, so it's ID is not -; available to the AddSprite function until the next frame. - -SPRITE_STATUS_EMPTY equ $0000 ; If the status value is zero, this sprite slot is available -SPRITE_STATUS_OCCUPIED equ $8000 ; Set the MSB to flag it as occupied -SPRITE_STATUS_ADDED equ $0001 ; Sprite was just added (new sprite) -SPRITE_STATUS_MOVED equ $0002 ; Sprite's position was changed -SPRITE_STATUS_UPDATED equ $0004 ; Sprite's non-position attributes were changed -SPRITE_STATUS_REMOVED equ $0008 ; Sprite has been removed. - -SPRITE_STATUS equ {MAX_SPRITES*0} -; TILE_DATA_OFFSET equ {MAX_SPRITES*2} -VBUFF_ADDR equ {MAX_SPRITES*4} ; Base address of the sprite's stamp in the data/mask banks -SPRITE_ID equ {MAX_SPRITES*6} -SPRITE_X equ {MAX_SPRITES*8} -SPRITE_Y equ {MAX_SPRITES*10} -; TILE_STORE_ADDR_1 equ {MAX_SPRITES*12} -TS_LOOKUP_INDEX equ {MAX_SPRITES*12} ; The index into the TileStoreLookup table corresponding to the top-left corner of the sprite -; TILE_STORE_ADDR_2 equ {MAX_SPRITES*14} -TS_COVERAGE_SIZE equ {MAX_SPRITES*14} ; Index into the lookup table of how many TileStore tiles are covered by this sprite -;TILE_STORE_ADDR_3 equ {MAX_SPRITES*16} -TS_VBUFF_BASE_ADDR equ {MAX_SPRITES*16} ; Fixed address of the TS_VBUFF_X memory locations -;TILE_STORE_ADDR_4 equ {MAX_SPRITES*18} -;TILE_STORE_ADDR_5 equ {MAX_SPRITES*20} -;TILE_STORE_ADDR_6 equ {MAX_SPRITES*22} -;TILE_STORE_ADDR_7 equ {MAX_SPRITES*24} -;TILE_STORE_ADDR_8 equ {MAX_SPRITES*26} -;TILE_STORE_ADDR_9 equ {MAX_SPRITES*28} -;TILE_STORE_ADDR_10 equ {MAX_SPRITES*30} -SPRITE_DISP equ {MAX_SPRITES*32} ; cached address of the specific stamp based on flags -SPRITE_CLIP_LEFT equ {MAX_SPRITES*34} -SPRITE_CLIP_RIGHT equ {MAX_SPRITES*36} -SPRITE_CLIP_TOP equ {MAX_SPRITES*38} -SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*40} -IS_OFF_SCREEN equ {MAX_SPRITES*42} -SPRITE_WIDTH equ {MAX_SPRITES*44} -SPRITE_HEIGHT equ {MAX_SPRITES*46} -SPRITE_CLIP_WIDTH equ {MAX_SPRITES*48} -SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*50} - -_Sprites ds SPRITE_REC_SIZE*MAX_SPRITES diff --git a/src/Tiles.s b/src/Tiles.s index 9fe7896..5758bb2 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -134,8 +134,11 @@ InitTiles lda :row ; Set the long address of where this tile asl ; exists in the code fields tay - lda BRowTableHigh,y + lda #>TileStore ; get middle 16 bits: "00 -->BBHH<-- LL" + and #$FF00 ; merge with code field bank + ora BRowTableHigh,y stal TileStore+TS_CODE_ADDR_HIGH,x ; High word of the tile address (just the bank) + lda BRowTableLow,y stal TileStore+TS_BASE_ADDR,x ; May not be needed later if we can figure out the right constant... diff --git a/src/Timer.s b/src/Timer.s index fbb2b53..a1ca960 100644 --- a/src/Timer.s +++ b/src/Timer.s @@ -1,37 +1,5 @@ -; Timer implementation -; -; The engire provides four timer slot that can be used by one-shot or -; recurring timers. Each timer is given an initial tick count, a -; reset tick count (0 = one-shot), and an action to perform. -; -; The timers handle overflow, so if a recurring timer has a tick count of 3 -; and 7 VBL ticks have passed, then the timer will be fired twice and -; a tick count of 2 will be set. -; -; As such, the timers are appropriate to drive physical and other game -; behaviors at a frame-independent rate. -; -; A collection of 4 timers that are triggered when their countdown -; goes below zero. Each timer takes up 16 bytes -; -; A timer can fire multiple times during a singular evaluation. For example, if the -; timer delay is set to 1 and 3 VBL ticks happen, then the timer delta is -2, will fire, -; have the delay added and get -1, fire again, increment to zero, first again and then -; finally reset to 1. -; -; +0 counter decremented by the number of ticks since last run -; +2 reset copied into counter when triggered. 0 turns off the timer. -; +4 addr long address of timer routine -; +8 user 8 bytes of user data space for timer state -MAX_TIMERS equ 4 -TIMER_REC_SIZE equ 16 + mx %00 -lastTick ds 2 -Timers ds TIMER_REC_SIZE*MAX_TIMERS - -GetVBLTicks ENT - jsr _GetVBLTicks - rtl _GetVBLTicks PushLong #0 _GetTick @@ -42,7 +10,7 @@ _GetVBLTicks ; Initialize the timers InitTimers jsr _GetVBLTicks - sta lastTick + sta LastTick lda #0 ldx #{TIMER_REC_SIZE*MAX_TIMERS}-2 @@ -71,8 +39,7 @@ AddTimer ENT pha phy - phk - plb + jsr _SetDataBank ldx #0 :loop lda Timers,x ; If the counter is zero, timer is free @@ -125,8 +92,7 @@ AddTimer ENT ; A = Timer ID RemoveTimer ENT phb - phk - plb + jsr _SetDataBank cmp #{TIMER_REC_SIZE*{MAX_TIMERS-1}}+1 bcs :exit @@ -143,17 +109,16 @@ RemoveTimer ENT ; Execute the timer functions DoTimers ENT phb - phk - plb + jsr _SetDataBank jsr _GetVBLTicks - cmp lastTick ; Throttle to 60 fps + cmp LastTick ; Throttle to 60 fps beq :exit tax ; Calculate the increment sec - sbc lastTick - stx lastTick + sbc LastTick + stx LastTick ; We don't want times to fire excessively. If the timer has nt been evaluated for over ; one second, then just skip processing and wait for the next call. @@ -186,9 +151,9 @@ _DoTimers phx ; Save our index lda Timers+4,x ; execute the timer callback - sta :dispatch+1 + stal :dispatch+1 lda Timers+5,x - sta :dispatch+2 + stal :dispatch+2 :dispatch jsl $000000 plx diff --git a/src/Tool.s b/src/Tool.s index 1c3204f..e08e555 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -10,6 +10,7 @@ use Core.MACS.s use Defs.s + use static/TileStoreDefs.s ToStrip equ $E10184 @@ -18,8 +19,7 @@ _TSEntry mac phd phb tcd - phk ; Default to setting the data back to the current bank. - plb + jsr _SetDataBank <<< _TSExit mac @@ -53,6 +53,14 @@ _CallTable adrl _TSLoadTileSet-1 _CTEnd +; Helper function to set the data back to the toolset default +_SetDataBank sep #$20 + lda #^TileStore + pha + plb + rep #$20 + rts + ; Do nothing when the tool set is installed _TSBootInit lda #0 @@ -86,8 +94,7 @@ zpToUse = userId+4 sta EngineMode phb - phk - plb + jsr _SetDataBank jsr _CoreStartUp ; Initialize the library plb @@ -114,8 +121,7 @@ _TSShutDown tcd ; Set the direct page for the toolset phb - phk - plb + jsr _SetDataBank jsr _CoreShutDown ; Shut down the library plb @@ -201,7 +207,7 @@ xTile equ FirstParam+4 tax lda yTile,s ; Valid range [0, 25] (26 rows) tay - lda tileId + lda tileId,s jsr _SetTile _TSExit #0;#6 @@ -255,7 +261,6 @@ TSPtr equ FirstParam put blitter/BG1.s put blitter/Template.s put blitter/TemplateUtils.s - put blitter/Tables.s put blitter/Blitter.s put blitter/TileProcs.s put blitter/Tiles00000.s diff --git a/src/blitter/BG0.s b/src/blitter/BG0.s index 081f7d8..32519af 100644 --- a/src/blitter/BG0.s +++ b/src/blitter/BG0.s @@ -8,14 +8,6 @@ _InitBG0 ; ; A=low word of picture address ; X=high word of pixture address -CopyBinToField ENT - phb - phk - plb - jsr _CopyBinToField - plb - rtl - _CopyBinToField :srcptr equ tmp0 :line_cnt equ tmp2 @@ -222,14 +214,6 @@ _CopyBinToField ; X=high workd of pixture address ; ; Picture must be within one bank -CopyPicToField ENT - phb - phk - plb - jsr _CopyPicToField - plb - rtl - _CopyPicToField :srcptr equ tmp0 :line_cnt equ tmp2 diff --git a/src/blitter/BG1.s b/src/blitter/BG1.s index d6a078f..71639fa 100644 --- a/src/blitter/BG1.s +++ b/src/blitter/BG1.s @@ -9,14 +9,6 @@ _InitBG1 ; A=low word of picture address ; X=high word of pixture address ; Y=high word of BG1 bank -CopyBinToBG1 ENT - phb - phk - plb - jsr _CopyBinToBG1 - plb - rtl - _CopyBinToBG1 :src_width equ tmp6 :src_height equ tmp7 @@ -39,14 +31,6 @@ _CopyBinToBG1 ; A=low word of picture address ; X=high word of pixture address ; Y=high word of BG1 bank -CopyPicToBG1 ENT - phb - phk - plb - jsr _CopyPicToBG1 - plb - rtl - _CopyPicToBG1 :src_width equ tmp6 :src_height equ tmp7 @@ -221,6 +205,8 @@ _ApplyBG1YPos :draw_count equ tmp2 :ytbl_idx equ tmp3 + phb ; Save the bank + lda BG1StartY jsr Mod208 sta BG1StartYMod208 @@ -277,7 +263,6 @@ _ApplyBG1YPos jne :loop - phk plb rts @@ -369,16 +354,14 @@ CopyBG1YTableToBG1Addr2 phx phb - phk ; restore access to this bank - plb + jsr _SetDataBank ; restore access to this bank ldy BG1OffsetIndex ; Get the offset and save the values jsr SaveBG1OffsetValues plb plx ; x is used directly in this routine ply - jsr ApplyBG1OffsetValues - rts + jmp ApplyBG1OffsetValues SaveBG1OffsetValues jmp (:tbl,x) diff --git a/src/blitter/Blitter.s b/src/blitter/Blitter.s index 7afe2bf..d490e3c 100644 --- a/src/blitter/Blitter.s +++ b/src/blitter/Blitter.s @@ -33,10 +33,10 @@ _BltRange sep #$20 ; 8-bit Acc lda BTableHigh,x ; patch in the bank - sta blt_entry+3 + stal blt_entry+3 lda BTableLow+1,x ; patch in the page - sta blt_entry+2 + stal blt_entry+2 ; The way we patch the exit code is subtle, but very fast. The CODE_EXIT offset points to ; an JMP/JML instruction that transitions to the next line after all of the code has been @@ -74,6 +74,7 @@ _BltRange tsc ; save the stack pointer stal stk_save+1 + bra blt_return blt_entry jml $000000 ; Jump into the blitter code $XX/YY00 blt_return _R0W0 @@ -88,5 +89,6 @@ stk_save lda #0000 ; load the stack sta [:exit_ptr],y rep #$20 +blt_out plb ; restore the bank rts diff --git a/src/blitter/Horz.s b/src/blitter/Horz.s index 9eaf5d8..dbe428b 100644 --- a/src/blitter/Horz.s +++ b/src/blitter/Horz.s @@ -21,6 +21,8 @@ _RestoreBG0Opcodes :draw_count_x2 equ tmp3 :exit_offset equ tmp4 + phb ; Save data bank + asl sta :virt_line_x2 ; Keep track of it @@ -75,7 +77,6 @@ _RestoreBG0Opcodes stz LastPatchOffset ; Clear the value once completed :out - phk plb rts @@ -279,6 +280,7 @@ _ApplyBG0XPos ; 2. Writes the BRA instruction to exit the code field ; 3. Writes the JMP entry point to enter the code field + phb ; Save the existing bank :loop lda :virt_line asl ; This will clear the carry bit @@ -353,7 +355,7 @@ _ApplyBG0XPos ldx :draw_count_x2 ldy :base_address ; Y-register is preserved, this can be removed pei :exit_address - jmp :SaveHighOperand ; Only used once, so "inline" it + jmp :SaveHighOperand ; Only used once, so "inline" it :save_high_op_rtn :not_odd @@ -374,7 +376,6 @@ _ApplyBG0XPos jne :loop - phk plb rts diff --git a/src/blitter/PEISlammer.s b/src/blitter/PEISlammer.s index 7eedffd..888335d 100644 --- a/src/blitter/PEISlammer.s +++ b/src/blitter/PEISlammer.s @@ -15,13 +15,13 @@ _PEISlam lda ScreenWidth dec - sta :screen_width_1 ; save the width-1 outside of the direct page + stal :screen_width_1 ; save the width-1 outside of the direct page lda #:pei_end ; patch the PEI entry address and #$FFFE ; should always be even, but.... sec sbc ScreenWidth - sta :inner+1 + stal :inner+1 phx tya @@ -43,7 +43,7 @@ _PEISlam tcd ; screen address to the direct page register tsc - sta :stk_save ; save the stack pointer to restore later + stal :stk_save ; save the stack pointer to restore later clc ; clear before the loop -- nothing in the loop affect the carry bit brl :outer ; hop into the entry point. @@ -57,7 +57,7 @@ _PEISlam tdc ; Move to the next line adc #160 tcd - adc :screen_width_1 + adcl :screen_width_1 tcs dey ; decrement the total counter, if zero then we're done @@ -79,7 +79,7 @@ _PEISlam :restore tsx ; save the current stack _R0W0 ; restore the execution environment and - lda :stk_save ; give a few cycles to catch some interrupts + ldal :stk_save ; give a few cycles to catch some interrupts tcs cli ; fall through here -- saves a BRA instruction @@ -92,7 +92,7 @@ _PEISlam :exit _R0W0 - lda :stk_save + ldal :stk_save tcs cli diff --git a/src/blitter/Rotation.s b/src/blitter/Rotation.s index e91cc9a..fed6a55 100644 --- a/src/blitter/Rotation.s +++ b/src/blitter/Rotation.s @@ -10,14 +10,6 @@ ; of the BG1 data buffer provides the "fill value". ANGLEBNK ext -ApplyBG1XPosAngle ENT - phb - phk - plb - jsr _ApplyBG1XPosAngle - plb - rtl - _ApplyBG1XPosAngle ; phy @@ -62,14 +54,6 @@ _ApplyBG1XPosAngle pld rts -ApplyBG1YPosAngle ENT - phb - phk - plb - jsr _ApplyBG1YPosAngle - plb - rtl - _ApplyBG1YPosAngle :virt_line equ tmp0 :lines_left equ tmp1 @@ -90,6 +74,7 @@ _ApplyBG1YPosAngle lda ScreenHeight sta :lines_left + phb :loop lda :virt_line asl @@ -141,7 +126,6 @@ _ApplyBG1YPosAngle jne :loop - phk plb rts @@ -155,14 +139,13 @@ CopyAngleYTableToBG1Addr phx phb - phk ; restore access to this bank - plb + + jsr _SetDataBank ; restore access to this bank jsr SaveBG1AngleValues plb plx ; x is used directly in this routine - jsr ApplyBG1OffsetValues - rts + jmp ApplyBG1OffsetValues SaveBG1AngleValues jmp (:tbl,x) diff --git a/src/blitter/SCB.s b/src/blitter/SCB.s index f60f3c9..f43e02c 100644 --- a/src/blitter/SCB.s +++ b/src/blitter/SCB.s @@ -9,15 +9,7 @@ ; on the SHR screen or the current value of StartY ; ; This could be made faster by forcing a SCB array to be copied into PEAs ahead of time, but this -; is a bit more flexible -BltSCB ENT - phb - phk - plb - jsr _BltSCB - plb - rtl - +; is a bit more flexible _BltSCBOut rts _BltSCB diff --git a/src/blitter/Tables.s b/src/blitter/Tables.s index b01a931..91a28e1 100644 --- a/src/blitter/Tables.s +++ b/src/blitter/Tables.s @@ -253,19 +253,6 @@ NextCol ]step = ]step+2 --^ -; A double-sized table of lookup values. This is basically the cross-product of TileStoreYTable and -; NextCol. If is double-width and double-height so that, if we know a tile's address position -; of (X + 41*Y), then any relative tile store address can be looked up by adding a constant value. -; -; 50 rows by 80 columns + 2 extra rows and columns -TS_LOOKUP_WIDTH equ 80 -TS_LOOKUP_HEIGHT equ 50 -TS_LOOKUP_SPAN equ {TS_LOOKUP_WIDTH+2} -TS_LOOKUP_ROWS equ {TS_LOOKUP_HEIGHT+2} - -TileStoreLookupYTable ds {TS_LOOKUP_HEIGHT*2} -TileStoreLookup ds {TS_LOOKUP_SPAN*TS_LOOKUP_ROWS*2} - ; This is a double-length table that holds the right-edge adresses of the playfield on the physical ; screen. At most, it needs to hold 200 addresses for a full height playfield. It is double-length ; so that code can pick any offset and copy values without needing to check for a wrap-around. If the @@ -305,6 +292,3 @@ BG1YTable lup 208 BG1YOffsetTable lup 26 dw 1,1,1,2,2,2,2,2,1,1,1,0,0,0,0,0 --^ - -; Table of base VBUFF addresses for each sprite stamp slot -VBuffAddrTable ds 2*VBUFF_SLOT_COUNT \ No newline at end of file diff --git a/src/blitter/TemplateUtils.s b/src/blitter/TemplateUtils.s index 121f3af..56b60bf 100644 --- a/src/blitter/TemplateUtils.s +++ b/src/blitter/TemplateUtils.s @@ -49,7 +49,7 @@ Counter equ tmp3 tax ; NOTE: Try to rework to use new TileStore2DLookup array lda OnScreenAddr - stal TileStore+TS_SCREEN_ADDR,x + sta TileStore+TS_SCREEN_ADDR,x clc adc #4 ; Go to the next tile @@ -278,6 +278,10 @@ BuildBank sta :target+2 :BuildLine2 + phb ; save bank and reset to the code bank because + phk ; the template is part of this bank + plb + lda #CODE_LEN ; round up to an even number of bytes inc and #$FFFE @@ -315,6 +319,6 @@ BuildBank cpx #PagePatchNum bcc :dopage -:out rep #$20 + plb rts \ No newline at end of file diff --git a/src/blitter/Tiles.s b/src/blitter/Tiles.s index c10c524..7a6f197 100644 --- a/src/blitter/Tiles.s +++ b/src/blitter/Tiles.s @@ -561,14 +561,6 @@ CopyTileMToDyn ; A = Tile ID (0 - 511) ; X = Tile column (0 - 40) ; Y = Tile row (0 - 25) -CopyBG0Tile ENT - phb - phk - plb - jsr _CopyBG0Tile - plb - rtl - _CopyBG0Tile phb ; save the current bank phx ; save the original x-value @@ -614,14 +606,6 @@ _CopyBG0Tile ; A = Tile ID (0 - 511) ; X = Tile column (0 - 40) ; Y = Tile row (0 - 25) -CopyBG1Tile - phb - phk - plb - jsr _CopyBG1Tile - plb - rtl - _CopyBG1Tile phb ; save the current bank phx ; save the original x-value diff --git a/src/blitter/Tiles00000.s b/src/blitter/Tiles00000.s index 854691d..7fa74bd 100644 --- a/src/blitter/Tiles00000.s +++ b/src/blitter/Tiles00000.s @@ -36,8 +36,24 @@ _TBSolidTile_VH ; ; This does not increase the FPS by 37% because only a small number of tiles are drawn each frame, but it ; has an impact and can significantly help out when sprites trigger more dirty tile updates than normal. + + +; This is called via a JMP (abs,x) with an extra byte on the stack that holds the bank +; register value. This must be restored prior to returning _TBCopyDataFast tax +]line equ 0 + lup 8 + ldal tiledata+{]line*4},x + sta: $0004+{]line*$1000},y + ldal tiledata+{]line*4}+2,x + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + plb + rts + + _TBCopyData ]line equ 0 lup 8 diff --git a/src/blitter/Vert.s b/src/blitter/Vert.s index c7b7bd6..0679cd3 100644 --- a/src/blitter/Vert.s +++ b/src/blitter/Vert.s @@ -45,6 +45,7 @@ _ApplyBG0YPos ; and ~2,500 per secord. This is ~1% of our total CPU budget and is *just* enough cycles to be ; interesting.... Another 8 cycles could be removed by doing all calculatinos pre-multiplied by 2 ; to avoid several 'asl' instructions + phb :loop lda :virt_line asl @@ -91,7 +92,6 @@ _ApplyBG0YPos jne :loop - phk plb rts diff --git a/src/static/TileStore.s b/src/static/TileStore.s index 7182fb3..7f65abb 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -1,4 +1,378 @@ ; Bank of memory that holds the core sprite and tile store data structures -TileStore ENT -; ds 65535 - ds 65536 + + put ../Defs.s + put TileStoreDefs.s + put ../blitter/Template.s + +;------------------------------------------------------------------------------------- +; +; Buffer space + + ds 256 + +;------------------------------------------------------------------------------------- + +TileStore ENT + ds {TILE_STORE_SIZE*17} + +;------------------------------------------------------------------------------------- +; +; A list of dirty tiles that need to be updated in a given frame + + ds \,$00 ; pad to the next page boundary +DirtyTileCount ENT + ds 2 +DirtyTiles ENT + ds TILE_STORE_SIZE ; At most this many tiles can possibly be update at once + +;------------------------------------------------------------------------------------- +; + + ds \,$00 ; pad to the next page boundary +_Sprites ENT + ds SPRITE_REC_SIZE*MAX_SPRITES + +;------------------------------------------------------------------------------------- +; +; A double-sized table of lookup values. It is double-width and double-height so that, +; if we know a tile's address position of (X + 41*Y), then any relative tile store address +; can be looked up by adding a constant value. + ds \,$00 ; pad to the next page boundary +TileStoreLookupYTable ENT + ds {TS_LOOKUP_HEIGHT*2} +TileStoreLookup ENT + ds {TS_LOOKUP_SPAN*TS_LOOKUP_ROWS*2} + +;------------------------------------------------------------------------------------- +; +; Other data tables + +; Col2CodeOffset +; +; Takes a column number (0 - 81) and returns the offset into the blitter code +; template. +; +; This is used for rendering tile data into the code field. For example, is we assume that +; we are filling in the operands for a bunch of PEA values, we could do this +; +; ldy tileColumn*2 +; lda #DATA +; ldx Col2CodeOffset,y +; sta $0001,x +; +; The table values are pre-reversed so that loop can go in logical order 0, 2, 4, ... +; and the resulting offsets will map to the code instructions in right-to-left order. +; +; Remember, because the data is pushed on to the stack, the last instruction, which is +; in the highest memory location, pushed data that apepars on the left edge of the screen. + +]step equ 0 + dw CODE_TOP ; There is a spot where we load Col2CodeOffet-2,x +Col2CodeOffset ENT + lup 82 + dw CODE_TOP+{{81-]step}*PER_TILE_SIZE} +]step equ ]step+1 + --^ + dw CODE_TOP+{81*PER_TILE_SIZE} + +; A parallel table to Col2CodeOffset that holds the offset to the exception handler address for each column +]step equ 0 + dw SNIPPET_BASE +JTableOffset ENT + lup 82 + dw SNIPPET_BASE+{{81-]step}*SNIPPET_SIZE} +]step equ ]step+1 + --^ + dw SNIPPET_BASE+{81*SNIPPET_SIZE} + +; Table of BRA instructions that are used to exit the code field. Separate tables for +; even and odd aligned cases. +; +; The even exit point is closest to the code field. The odd exit point is 3 bytes further +; +; These tables are reversed to be parallel with the JTableOffset and Col2CodeOffset tables above. The +; physical word index that each instruction is intended to be placed at is in the comment. +CodeFieldEvenBRA ENT + bra *+6 ; 81 -- need to skip over the JMP loop that passed control back + bra *+9 ; 80 + bra *+12 ; 79 + bra *+15 ; 78 + bra *+18 ; 77 + bra *+21 ; 76 + bra *+24 ; 75 + bra *+27 ; 74 + bra *+30 ; 73 + bra *+33 ; 72 + bra *+36 ; 71 + bra *+39 ; 70 + bra *+42 ; 69 + bra *+45 ; 68 + bra *+48 ; 67 + bra *+51 ; 66 + bra *+54 ; 65 + bra *+57 ; 64 + bra *+60 ; 63 + bra *+63 ; 62 + bra *+66 ; 61 + bra *+69 ; 60 + bra *+72 ; 59 + bra *+75 ; 58 + bra *+78 ; 57 + bra *+81 ; 56 + bra *+84 ; 55 + bra *+87 ; 54 + bra *+90 ; 53 + bra *+93 ; 52 + bra *+96 ; 51 + bra *+99 ; 50 + bra *+102 ; 49 + bra *+105 ; 48 + bra *+108 ; 47 + bra *+111 ; 46 + bra *+114 ; 45 + bra *+117 ; 44 + bra *+120 ; 43 + bra *+123 ; 42 + bra *+126 ; 41 + bra *-123 ; 40 + bra *-120 ; 39 + bra *-117 ; 38 + bra *-114 ; 37 + bra *-111 ; 36 + bra *-108 ; 35 + bra *-105 ; 34 + bra *-102 ; 33 + bra *-99 ; 32 + bra *-96 ; 31 + bra *-93 ; 30 + bra *-90 ; 29 + bra *-87 ; 28 + bra *-84 ; 27 + bra *-81 ; 26 + bra *-78 ; 25 + bra *-75 ; 24 + bra *-72 ; 23 + bra *-69 ; 22 + bra *-66 ; 21 + bra *-63 ; 20 + bra *-60 ; 19 + bra *-57 ; 18 + bra *-54 ; 17 + bra *-51 ; 16 + bra *-48 ; 15 + bra *-45 ; 14 + bra *-42 ; 13 + bra *-39 ; 12 + bra *-36 ; 11 + bra *-33 ; 10 + bra *-30 ; 9 + bra *-27 ; 8 + bra *-24 ; 7 + bra *-21 ; 6 + bra *-18 ; 5 + bra *-15 ; 4 + bra *-12 ; 3 + bra *-9 ; 2 + bra *-6 ; 1 + bra *-3 ; 0 + +CodeFieldOddBRA ENT + bra *+9 ; 81 -- need to skip over two JMP instructions + bra *+12 ; 80 + bra *+15 ; 79 + bra *+18 ; 78 + bra *+21 ; 77 + bra *+24 ; 76 + bra *+27 ; 75 + bra *+30 ; 74 + bra *+33 ; 73 + bra *+36 ; 72 + bra *+39 ; 71 + bra *+42 ; 70 + bra *+45 ; 69 + bra *+48 ; 68 + bra *+51 ; 67 + bra *+54 ; 66 + bra *+57 ; 65 + bra *+60 ; 64 + bra *+63 ; 64 + bra *+66 ; 62 + bra *+69 ; 61 + bra *+72 ; 60 + bra *+75 ; 59 + bra *+78 ; 58 + bra *+81 ; 57 + bra *+84 ; 56 + bra *+87 ; 55 + bra *+90 ; 54 + bra *+93 ; 53 + bra *+96 ; 52 + bra *+99 ; 51 + bra *+102 ; 50 + bra *+105 ; 49 + bra *+108 ; 48 + bra *+111 ; 47 + bra *+114 ; 46 + bra *+117 ; 45 + bra *+120 ; 44 + bra *+123 ; 43 + bra *+126 ; 42 + bra *+129 ; 41 + bra *-126 ; 40 + bra *-123 ; 39 + bra *-120 ; 38 + bra *-117 ; 37 + bra *-114 ; 36 + bra *-111 ; 35 + bra *-108 ; 34 + bra *-105 ; 33 + bra *-102 ; 32 + bra *-99 ; 31 + bra *-96 ; 30 + bra *-93 ; 29 + bra *-90 ; 28 + bra *-87 ; 27 + bra *-84 ; 26 + bra *-81 ; 25 + bra *-78 ; 24 + bra *-75 ; 23 + bra *-72 ; 22 + bra *-69 ; 21 + bra *-66 ; 20 + bra *-63 ; 19 + bra *-60 ; 18 + bra *-57 ; 17 + bra *-54 ; 16 + bra *-51 ; 15 + bra *-48 ; 14 + bra *-45 ; 13 + bra *-42 ; 12 + bra *-39 ; 11 + bra *-36 ; 10 + bra *-33 ; 9 + bra *-30 ; 8 + bra *-27 ; 7 + bra *-24 ; 6 + bra *-21 ; 5 + bra *-18 ; 4 + bra *-15 ; 3 + bra *-12 ; 2 + bra *-9 ; 1 + bra *-6 ; 0 -- branch back 6 to skip the JMP even path + +]step equ $2000 +ScreenAddr ENT + lup 200 + dw ]step +]step = ]step+160 + --^ + +; Table of offsets into each row of a Tile Store table. We currently have two tables defined; one +; that is the backing store for the tiles rendered into the code field, and another that holds +; backlink information on the sprite entries that overlap various tiles. +; +; This table is double-length to support accessing off the end modulo its legth +TileStoreYTable ENT +]step equ 0 + lup 26 + dw ]step +]step = ]step+{41*2} + --^ +]step equ 0 + lup 26 + dw ]step +]step = ]step+{41*2} + --^ + +; Create a table to look up the "next" column with modulo wraparound. Basically a[i] = i +; and the table is double-length. Use constant offsets to pick an amount to advance +NextCol ENT +]step equ 0 + lup 41 + dw ]step +]step = ]step+2 + --^ +]step equ 0 + lup 41 + dw ]step +]step = ]step+2 + --^ + +; This is a double-length table that holds the right-edge adresses of the playfield on the physical +; screen. At most, it needs to hold 200 addresses for a full height playfield. It is double-length +; so that code can pick any offset and copy values without needing to check for a wrap-around. If the +; playfield is less than 200 lines tall, then any values after 2 * PLAYFIELD_HEIGHT are undefined. +RTable ENT + ds 400 + ds 400 + +; Array of addresses for the banks that hold the blitter. +BlitBuff ENT + dw $5a5a + ds 4*13 + +; The blitter table (BTable) is a double-length table that holds the full 4-byte address of each +; line of the blit fields. We decompose arrays of pointers into separate high and low words so +; that everything can use the same indexing offsets +BTableHigh ENT + ds 208*2*2 +BTableLow ENT + ds 208*2*2 + +; A shorter table that just holds the blitter row addresses +BRowTableHigh ENT + ds 26*2*2 +BRowTableLow ENT + ds 26*2*2 + +; A double-length table of addresses for the BG1 bank. The BG1 buffer is 208 rows of 256 bytes each and +; the first row starts $1800 bytes in to center the buffer in the bank +]step equ $1800 +BG1YTable ENT + lup 208 + dw ]step +]step = ]step+256 + --^ +]step equ 256 + lup 208 + dw ]step +]step = ]step+256 + --^ + +; Repeat +BG1YOffsetTable ENT + lup 26 + dw 1,1,1,2,2,2,2,2,1,1,1,0,0,0,0,0 + --^ + +; Other Toolset variables +OneSecondCounter ENT + dw 0 +OldOneSecVec ENT + ds 4 +Timers ENT + ds TIMER_REC_SIZE*MAX_TIMERS +DefaultPalette ENT + dw $0000,$007F,$0090,$0FF0 + dw $000F,$0080,$0f70,$0FFF + dw $0fa9,$0ff0,$00e0,$04DF + dw $0d00,$078f,$0ccc,$0FFF + +; 0. Full Screen : 40 x 25 320 x 200 (32,000 bytes (100.0%)) +; 1. Sword of Sodan : 34 x 24 272 x 192 (26,112 bytes ( 81.6%)) +; 2. ~NES : 32 x 25 256 x 200 (25,600 bytes ( 80.0%)) +; 3. Task Force : 32 x 22 256 x 176 (22,528 bytes ( 70.4%)) +; 4. Defender of the World : 35 x 20 280 x 160 (22,400 bytes ( 70.0%)) +; 5. Rastan : 32 x 20 256 x 160 (20,480 bytes ( 64.0%)) +; 6. Game Boy Advanced : 30 x 20 240 x 160 (19,200 bytes ( 60.0%)) +; 7. Ancient Land of Y's : 36 x 16 288 x 128 (18,432 bytes ( 57.6%)) +; 8. Game Boy Color : 20 x 18 160 x 144 (11,520 bytes ( 36.0%)) +; 9. Agony (Amiga) : 36 x 24 288 x 192 (27,648 bytes ( 86.4%)) +; 10. Atari Lynx : 20 x 13 160 x 102 (8,160 bytes ( 25.5%)) +ScreenModeWidth ENT + dw 320,272,256,256,280,256,240,288,160,288,160,320 +ScreenModeHeight ENT + dw 200,192,200,176,160,160,160,128,144,192,102,1 + +blt_return +stk_save \ No newline at end of file diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s new file mode 100644 index 0000000..debdd63 --- /dev/null +++ b/src/static/TileStoreDefs.s @@ -0,0 +1,116 @@ +; Tile storage parameters +TILE_DATA_SPAN equ 4 +TILE_STORE_WIDTH equ 41 +TILE_STORE_HEIGHT equ 26 +MAX_TILES equ {26*41} ; Number of tiles in the code field (41 columns * 26 rows) +TILE_STORE_SIZE equ {MAX_TILES*2} ; The tile store contains a tile descriptor in each slot + +TS_TILE_ID equ TILE_STORE_SIZE*0 ; tile descriptor for this location +TS_DIRTY equ TILE_STORE_SIZE*1 ; Flag. Used to prevent a tile from being queued multiple times per frame +TS_SPRITE_FLAG equ TILE_STORE_SIZE*2 ; Bitfield of all sprites that intersect this tile. 0 if no sprites. +TS_TILE_ADDR equ TILE_STORE_SIZE*3 ; cached value, the address of the tiledata for this tile +TS_CODE_ADDR_LOW equ TILE_STORE_SIZE*4 ; const value, address of this tile in the code fields +TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5 +TS_WORD_OFFSET equ TILE_STORE_SIZE*6 ; const value, word offset value for this tile if LDA (dp),y instructions re used +TS_BASE_ADDR equ TILE_STORE_SIZE*7 ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000. +TS_SCREEN_ADDR equ TILE_STORE_SIZE*8 ; cached value of on-screen location of tile. Used for DirtyRender. +;TS_VBUFF_ARRAY_ADDR equ TILE_STORE_SIZE*9 ; const value to an aligned 32-byte array starting at $8000 in TileStore bank + +TS_BASE_TILE_COPY equ TILE_STORE_SIZE*9 ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering +TS_BASE_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function +TS_DIRTY_TILE_DISP equ TILE_STORE_SIZE*11 ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function + +; Hold values for up to 4 sprites per tile +TS_VBUFF_ADDR_0 equ TILE_STORE_SIZE*12 +TS_VBUFF_ADDR_1 equ TILE_STORE_SIZE*13 +TS_VBUFF_ADDR_2 equ TILE_STORE_SIZE*14 +TS_VBUFF_ADDR_3 equ TILE_STORE_SIZE*15 +TS_VBUFF_ADDR_COUNT equ TILE_STORE_SIZE*16 ; replace usage of TS_VBUFF_ARRAY_ADDR with this later + +; Sprite data structures. We cache quite a few pieces of information about the sprite +; to make calculations faster, so this is hidden from the caller. + +MAX_SPRITES equ 16 +SPRITE_REC_SIZE equ 52 + +; Mark each sprite as ADDED, UPDATED, MOVED, REMOVED depending on the actions applied to it +; on this frame. Quick note, the same Sprite ID cannot be removed and added in the same frame. +; A REMOVED sprite if removed from the sprite list during the Render call, so it's ID is not +; available to the AddSprite function until the next frame. + +SPRITE_STATUS_EMPTY equ $0000 ; If the status value is zero, this sprite slot is available +SPRITE_STATUS_OCCUPIED equ $8000 ; Set the MSB to flag it as occupied +SPRITE_STATUS_ADDED equ $0001 ; Sprite was just added (new sprite) +SPRITE_STATUS_MOVED equ $0002 ; Sprite's position was changed +SPRITE_STATUS_UPDATED equ $0004 ; Sprite's non-position attributes were changed +SPRITE_STATUS_REMOVED equ $0008 ; Sprite has been removed. + +SPRITE_STATUS equ {MAX_SPRITES*0} +; TILE_DATA_OFFSET equ {MAX_SPRITES*2} +VBUFF_ADDR equ {MAX_SPRITES*4} ; Base address of the sprite's stamp in the data/mask banks +SPRITE_ID equ {MAX_SPRITES*6} +SPRITE_X equ {MAX_SPRITES*8} +SPRITE_Y equ {MAX_SPRITES*10} +; TILE_STORE_ADDR_1 equ {MAX_SPRITES*12} +TS_LOOKUP_INDEX equ {MAX_SPRITES*12} ; The index into the TileStoreLookup table corresponding to the top-left corner of the sprite +; TILE_STORE_ADDR_2 equ {MAX_SPRITES*14} +TS_COVERAGE_SIZE equ {MAX_SPRITES*14} ; Index into the lookup table of how many TileStore tiles are covered by this sprite +;TILE_STORE_ADDR_3 equ {MAX_SPRITES*16} +TS_VBUFF_BASE_ADDR equ {MAX_SPRITES*16} ; Fixed address of the TS_VBUFF_X memory locations +;TILE_STORE_ADDR_4 equ {MAX_SPRITES*18} +;TILE_STORE_ADDR_5 equ {MAX_SPRITES*20} +;TILE_STORE_ADDR_6 equ {MAX_SPRITES*22} +;TILE_STORE_ADDR_7 equ {MAX_SPRITES*24} +;TILE_STORE_ADDR_8 equ {MAX_SPRITES*26} +;TILE_STORE_ADDR_9 equ {MAX_SPRITES*28} +;TILE_STORE_ADDR_10 equ {MAX_SPRITES*30} +SPRITE_DISP equ {MAX_SPRITES*32} ; cached address of the specific stamp based on flags +SPRITE_CLIP_LEFT equ {MAX_SPRITES*34} +SPRITE_CLIP_RIGHT equ {MAX_SPRITES*36} +SPRITE_CLIP_TOP equ {MAX_SPRITES*38} +SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*40} +IS_OFF_SCREEN equ {MAX_SPRITES*42} +SPRITE_WIDTH equ {MAX_SPRITES*44} +SPRITE_HEIGHT equ {MAX_SPRITES*46} +SPRITE_CLIP_WIDTH equ {MAX_SPRITES*48} +SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*50} + +; 50 rows by 80 columns + 2 extra rows and columns +TS_LOOKUP_WIDTH equ 80 +TS_LOOKUP_HEIGHT equ 50 +TS_LOOKUP_SPAN equ {TS_LOOKUP_WIDTH+2} +TS_LOOKUP_ROWS equ {TS_LOOKUP_HEIGHT+2} + +; Blitter template constancts +PER_TILE_SIZE equ 3 +SNIPPET_SIZE equ 32 + +;---------------------------------------------------------------------- +; +; Timer implementation +; +; The engire provides four timer slot that can be used by one-shot or +; recurring timers. Each timer is given an initial tick count, a +; reset tick count (0 = one-shot), and an action to perform. +; +; The timers handle overflow, so if a recurring timer has a tick count of 3 +; and 7 VBL ticks have passed, then the timer will be fired twice and +; a tick count of 2 will be set. +; +; As such, the timers are appropriate to drive physical and other game +; behaviors at a frame-independent rate. +; +; A collection of 4 timers that are triggered when their countdown +; goes below zero. Each timer takes up 16 bytes +; +; A timer can fire multiple times during a singular evaluation. For example, if the +; timer delay is set to 1 and 3 VBL ticks happen, then the timer delta is -2, will fire, +; have the delay added and get -1, fire again, increment to zero, first again and then +; finally reset to 1. +; +; +0 counter decremented by the number of ticks since last run +; +2 reset copied into counter when triggered. 0 turns off the timer. +; +4 addr long address of timer routine +; +8 user 8 bytes of user data space for timer state +MAX_TIMERS equ 4 +TIMER_REC_SIZE equ 16 diff --git a/src/tiles/DirtyTileQueue.s b/src/tiles/DirtyTileQueue.s index 5f078f2..7e4fcca 100644 --- a/src/tiles/DirtyTileQueue.s +++ b/src/tiles/DirtyTileQueue.s @@ -1,8 +1,3 @@ - -; A list of dirty tiles that need to be updated in a given frame -DirtyTileCount ds 2 -DirtyTiles ds TILE_STORE_SIZE ; At most this many tiles can possibly be update at once - _ClearDirtyTiles bra :hop :loop @@ -62,3 +57,110 @@ _PopDirtyTile2 ; alternate entry point lda #$FFFF stal TileStore+TS_DIRTY,x ; clear the occupied backlink rts + +; An optimized subroutine that runs through the dirty tile list and executes a callback function +; for each dirty tile. This is an unrolled loop, so we avoid the need to track a register and +; decrement on each iteration. +; +; Also, if we are handling less that 8 dirty tiles, we use a code path that does not +; need to use an index register +; +; Bank = Tile Store +; D = Page 2 +_PopDirtyTilesFast + ldx DP2_DIRTY_TILE_COUNT ; This is pre-multiplied by 2 + bne pdtf_not_empty ; If there are no items, exit +at_exit rts +pdtf_not_empty + cpx #16 ; If there are >= 8 elements, then + bcs full_chunk ; do a full chunk + + stz DP2_DIRTY_TILE_COUNT ; Otherwise, this pass will handle them all + jmp (at_table,x) +at_table da at_exit,at_one,at_two,at_three + da at_four,at_five,at_six,at_seven + +full_chunk txa + sbc #16 ; carry set from branch + sta DP2_DIRTY_TILE_COUNT ; fall through + tay ; use the Y-register for the index + +; Because all of the registers get used in the subroutine, we +; push the values from the DirtyTiles array onto the stack and then pop off +; the values as we go + + ldx DirtyTiles+14,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+12,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+10,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+8,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+6,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+4,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+2,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+0,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + jmp _PopDirtyTilesFast + +; These routines just handle between 1 and 7 dirty tiles +at_seven + ldx DirtyTiles+12 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_six + ldx DirtyTiles+10 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_five + ldx DirtyTiles+8 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_four + ldx DirtyTiles+6 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_three + ldx DirtyTiles+4 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_two + ldx DirtyTiles+2 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_one + ldx DirtyTiles+0 + stz TileStore+TS_DIRTY,x + jmp _RenderTileFast diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index 35f97a2..b285ecc 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -6,20 +6,12 @@ ; If there are sprites, then the sprite data is flattened and stored into a direct page buffer ; and then copied into the code field _RenderTileFast - ldx TileStore+TS_VBUFF_ADDR_COUNT,y ; How many sprites are on this tile? - beq NoSpritesFast ; This is faster if there are no sprites - jmp (fast_dispatch,x) ; Dispatch to the other routines -fast_dispatch - da NoSpritesFast - da OneSpriteFast - da TwoSpritesFast - da ThreeSpritesFast - da FourSpritesFast +; lda TileStore+TS_VBUFF_ADDR_COUNT,x ; How many sprites are on this tile? +; bne SpriteDispatch ; This is faster if there are no sprites -NoSpritesFast - tyx +NoSpriteFast lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line - pha ; and put on the stack for later. Has addl bank in high byte. + pha ; and put on the stack for later. Has TileStore bank in high byte. ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) plb ; set the code field bank @@ -27,7 +19,17 @@ NoSpritesFast ; The TS_BASE_TILE_DISP routines will come from this table when ENGINE_MODE_TWO_LAYER and ; ENGINE_MODE_DYN_TILES are both off. -FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast +FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast +; dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast + +SpriteDispatch + tax + jmp (:,x) ; Dispatch to the other routines +: da NoSpriteFast ; Placeholder + da OneSpriteFast + da TwoSpritesFast + da ThreeSpritesFast + da FourSpritesFast ; Pointers to sprite data and masks spritedata_0 equ tmp0