Move most data storage to separate bank; fix many banking bugs

This commit is contained in:
Lucas Scharenbroich 2022-05-18 21:00:06 -05:00
parent 01e92a7b62
commit 755ac3fbfd
30 changed files with 850 additions and 461 deletions

View File

@ -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

View File

@ -8,3 +8,8 @@
ASM App.Main.s
SNA Main
; Segment #2 -- Tileset
ASM Zelda.TileSet.s
SNA TSET

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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...

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

116
src/static/TileStoreDefs.s Normal file
View File

@ -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

View File

@ -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

View File

@ -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