Tile rendering reorganization

This significantly simplifies the dispatch process by creating a
proper backing store for the tiles.  Most values that were
calcualted on the fly are now stored as constants in the tile
store.

Also, all tile updated are run through the dirty tile list which
solved a checken-and-egg problem of which order to do sprites vs
new tiles and affords a lot of optimizations since tile rendering
is deferred and each tile is only drawn at most once per frame.
This commit is contained in:
Lucas Scharenbroich 2021-10-21 08:50:07 -05:00
parent c4762888ed
commit 4e779e71d2
22 changed files with 1627 additions and 623 deletions

View File

@ -8,12 +8,16 @@
use .\Defs.s
; Feature flags
NO_INTERRUPTS equ 1 ; turn off for crossrunner debugging
NO_INTERRUPTS equ 0 ; turn off for crossrunner debugging
NO_MUSIC equ 1 ; turn music + tool loading off
; External data provided by the main program segment
tiledata EXT
; Sprite plane data and mask banks are provided as an exteral segment
spritedata EXT
spritemask EXT
; IF there are overlays, they are provided as an external
Overlay EXT
@ -247,6 +251,8 @@ EngineReset
jsr BuildBank
]step equ ]step+4
--^
jsr _InitDirtyTiles
rts
; Allow the user to dynamically select one of the pre-configured screen sizes, or pass
@ -370,6 +376,7 @@ ReadControl ENT
put Memory.s
put Graphics.s
put Sprite.s
put Render.s
put Timer.s
put Script.s
@ -379,7 +386,9 @@ ReadControl ENT
put blitter/Tables.s
put blitter/Template.s
put blitter/Tiles.s
put blitter/Tiles00000.s
put blitter/Vert.s
put blitter/BG0.s
put blitter/BG1.s
put TileMap.s

View File

@ -36,6 +36,9 @@ DoTimers EXT
StartScript EXT
StopScript EXT
; Sprite functions
AddSprite EXT
; Direct access to internals
DoScriptSeq EXT
GetTileAddr EXT

View File

@ -67,7 +67,8 @@ InitMemory PushLong #0 ; space for result
]step equ ]step+4
--^
; Fill in a tables with the adddress of all 208 scanlines across all 13 banks
; Fill in a table with the adddress of all 208 scanlines across all 13 banks. Also fill in
; a shorter table that just holds the starting address of the 26 tile block rows.
ldx #0
ldy #0
@ -99,9 +100,38 @@ InitMemory PushLong #0 ; space for result
adc #4 ; move to the next bank address
tay
cmp #4*13
bcs :exit
bcs :exit1
brl :bloop
:exit1
ldx #0
ldy #0
:bloop2
lda BlitBuff+2,y ; Copy the high word first
sta BRowTableHigh,x ; Two rows per bank
sta BRowTableHigh+{26*2},x
sta BRowTableHigh+2,x
sta BRowTableHigh+{26*2}+2,x
lda BlitBuff,y
sta BRowTableLow,x
sta BRowTableLow+{26*2},x
clc
adc #$8000
sta BRowTableLow+2,x
sta BRowTableLow+{26*2}+2,x
txa
adc #4
tax
tya
adc #4 ; move to the next bank address
tay
cmp #4*13
bcs :exit
brl :bloop2
:exit
rts

View File

@ -78,22 +78,39 @@ _Render
jsr _ApplyBG0XPosPre
jsr _ApplyBG1XPosPre
jsr _UpdateBG0TileMap
jsr _UpdateBG1TileMap
jsr _RenderSprites ; Once the BG0 X and Y positions are committed, update sprite data
jsr _UpdateBG0TileMap ; and the tile maps. These subroutines build up a list of tiles
jsr _UpdateBG1TileMap ; that need to be updated in the code field
jsr _ApplyBG0XPos ; Patch the PEA instructions with exit BRA opcode
jsr _ApplyBG1XPos ; Patch the PEA instructions with exit BRA opcode
jsr _ApplyTiles ; This function actually draws the new tiles into the code field
; The code fields are locked in now and reder to be rendered
jsr _ApplyBG0XPos ; Patch the code field instructions with exit BRA opcode
jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position
; The code fields are locked in now and ready to be rendered
jsr _ShadowOff
ldx #0 ; Blit the full virtual buffer to the screen
ldy #8
jsr _BltRange
; Shadowing is turned off. Render all of the scan lines that need a second pass. These
; are the lines that have a masked overlay, or a sprite. One optimization that can
; be done here is that the lines can be rendered in any order since it is not shown
; on-screen yet.
; jsr _RenderPhaseA ; Draw all of the background lines
; jsr _RenderSprites ; Draw all of the sprites
; ldx #152 ; Blit the full virtual buffer to the screen
; ldy #160
; jsr _BltRange
; Turn shadowing back on
jsr _ShadowOn
; Now render all of the remaining lines in top-to-bottom (or bottom-to-top) order
; jsr _RenderPhaseB ; Draw the mix of background lines overlays and PEI slams
; ldx #0 ; Expose the top 8 rows
; ldy #8
; jsr _PEISlam
@ -102,15 +119,21 @@ _Render
; ldy #16
; jsr _BltRange
lda ScreenY0 ; pass the address of the first line of the overlay
asl
tax
lda ScreenAddr,x
clc
adc ScreenX0
jsl Overlay
; ldx #0 ; Blit the full virtual buffer to the screen
; ldy #152
; jsr _BltRange
ldx #8 ; Blit the full virtual buffer to the screen
; lda ScreenY0 ; pass the address of the first line of the overlay
; clc
; adc #152
; asl
; tax
; lda ScreenAddr,x
; clc
; adc ScreenX0
; jsl Overlay
ldx #0 ; Blit the full virtual buffer to the screen
ldy ScreenHeight
jsr _BltRange
@ -131,3 +154,82 @@ _Render
stz DirtyBits
rts
MAX_SEGMENTS equ 128
PhaseACount ds 0
PhaseATop ds 2*MAX_SEGMENTS
PhaseABot ds 2*MAX_SEGMENTS
PhaseAOp ds 2*MAX_SEGMENTS
PhaseBCount ds 0
PhaseBTop ds 2*MAX_SEGMENTS
PhaseBBot ds 2*MAX_SEGMENTS
PhaseBOp ds 2*MAX_SEGMENTS
; Initialize the rendering tree to just render all of the code fields
_InitRenderTree
lda #1 ; Put the whole screen into Phase B
sta PhaseBCount
lda #0
sta PhaseBTop
lda ScreenHeight
sta PhaseBBot
lda #_BltRange
sta PhaseBOp
stz PhaseACount ; Phase A is initially empty
rts
; Solid overlays are called in Phase B, but do not require the screen
; to be drawn underneath, so this provides an opportunity to optimize
; the rendering pipeline
_AddSolidOverlay
rts
; A mixed overlay signals that the underlying scan line data must be
; redered first.
_AddMixedOverlay
rts
_RenderPhaseA
ldy #0
:loop
cpy PhaseACount
bcs :out
phy ; save the counter
lda PhaseAOp,y ; dispatch to the appropriate function
sta :op+1
ldx PhaseATop,y
lda PhaseABot,y
tay
:op jsr $0000
ply ; restore the counter
iny
iny
bra :loop
:out
rts
_RenderPhaseB
ldy #0
:loop
cpy PhaseBCount
bcs :out
phy ; save the counter
lda PhaseBOp,y ; dispatch to the appropriate function
sta :op+1
ldx PhaseBTop,y
lda PhaseBBot,y
tay
:op jsr $0000
ply ; restore the counter
iny
iny
bra :loop
:out
rts

View File

@ -1,5 +1,398 @@
; Some sample code / utilities to help integrate compiled sprites int the GTE rendering
; pipeline.
; Functions for sprie handling. Mostly maintains the sprite list and provides
; utility functions to calculate sprite/tile intersections
;
; The main point of this file to to establish calling conventions and provide a framework
; for blitting a range of sprite lines, instead of always the full sprite.
; The sprite plane actually covers two banks so that more than 32K can be used as a virtual
; screen buffer. In order to be able to draw sprites offscreen, the virtual screen must be
; wider and taller than the physical graphics screen.
;
; Initialize the sprite plane data and mask banks (all data = $0000, all masks = $FFFF)
_InitSprite
ldx #$FFFE
lda #0
:loop stal spritedata,x
dex
dex
bpl :loop
ldx #$FFFE
lda #$FFFF
:loop stal spritemask,x
dex
dex
bpl :loop
rts
; This function looks at the sprite list and renders the sprite plane data into the appropriate
; tiles in the code field
_RenderSprites
ldx #0
:loop lda _Sprites+SPRITE_STATUS,x
beq :out
cmp #SPRITE_STATUS_DIRTY
beq :render
:next inx
inx
bra :loop
:out rts
; This is the complicated part; we need to draw the sprite into the sprite place, but then
; calculate the code field tiles that this sprite potentially overlaps with and mark those
; tiles as dirty.
:render
jsr _DrawTileSprite ; draw the sprite into the sprite plane
stz tmp0 ; flags to mark if the sprite is aligned to the code field grid or not
stz tmp1
lda _Sprites+SPRITE_X,x ; Will need some special handling for X < 0
clc
adc StartXMod164
bit #$0003 ; If the botton bit are zero, then we're aligned
beq *+4
inc tmp0
cmp #164
bcc *+5
sbc #164
lsr
lsr
pha ; Save the tile
lda _Sprites+SPRITE_Y,x
clc
adc StartYMod208
bit #$0007
beq *+4
inc tmp1
cmp #208
bcc *+5
sbc #208
lsr
lsr
lsr
pha
; We have the code field tile that needs to be filled; calculate the address of the corresponding
; location in the sprite plane
;
; Corner_X = -StartXMod164; if < -3, add 164
; Corner_Y = -StartYMod208; if < -7, add 208
; lda StartXMod164
; cmp #4
; bcc *+5
; sbc #164
; eor #$FFFF
; inc
; pha
; lda StartYMod208
; cmp #8
; bcc *+5
; sbc #208
; eor #$FFFF
; inc
; clc
; adc #NUM_BUFF_LINES
; xba
; clc
; adc 1,s
; Copy the tile from the direct page scratch space into the playfield
ply
plx
lda #$FFFF ; Sentinel value to pick direct page rendering
jsr _PushDirtyTile ; Enqueue for processing
; jsr _CopyBG0Tile
brl :next
; X = address of sprite _plane
; Y = address of tile
_ComposeSpriteAndTileNoMask
phb
pea #^tiledata
plb
]line equ 0
lup 8
lda: tiledata+{]line*4},y
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
oral spritedata+{]line*SPRITE_PLANE_SPAN},x
sta blttmp+{]line*4}
lda: tiledata+{]line*4}+2,y
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta blttmp+{]line*4}+2
--^
plb
plb
rts
; X = address of sprite plane
; Y = address of tile
_ComposeSpriteAndTileWithMask
phb
pea #^tiledata
plb
]line equ 0
lup 8
lda: tiledata+{]line*4},y
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
oral spritedata+{]line*SPRITE_PLANE_SPAN},x
sta blttmp+{]line*4}
lda: tiledata+{]line*4}+32,y
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
sta blttmp+{]line*4}+32
lda: tiledata+{]line*4}+2,y
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta blttmp+{]line*4}+2
lda: tiledata+{]line*4}+32+2,y
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
sta blttmp+{]line*4}+32+2
--^
plb
plb
rts
; _GetTileAt
;
; Given a relative playfield coordinate [0, ScreenWidth), [0, ScreenHeight) return the
; X = horizontal point [0, ScreenTileWidth]
; Y = vertical point [0, ScreenTileHeight]
;
; Return
; C = 1, out of range
; C = 0, X = column, Y = row
_GetTileAt
cpx ScreenWidth
bcc *+3
rts
cpy ScreenHeight
bcc *+3
rts
tya ; carry is clear here
adc StartYMod208 ; This is the code field line that is at the top of the screen
cmp #208
bcc *+5
sbc #208
lsr
lsr
lsr
tay ; This is the code field row for this point
clc
txa
adc StartXMod164
cmp #164
bcc *+5
sbc #164
lsr
lsr
tax ; Could call _CopyBG0Tile with these arguments
clc
rts
; _DrawSprite
;
; Draw the sprites on the _Sprite list into the Sprite Plane data and mask buffers. This is using the
; tile data right now, but could be replaced with compiled sprite routines.
_DrawSprites
ldx #0
:loop lda _Sprites+SPRITE_STATUS,x
bne :draw ; The first open slot is the end of the list
rts
:draw cmp #SPRITE_STATUS_DIRTY
bne :loop
jsr _DrawTileSprite
bra :loop
_DrawTileSprite
phx ; preserve the x register
; Copy the tile data + mask into the sprite plane
lda _Sprites+VBUFF_ADDR,x ; Load the address in the sprite plane
ldy _Sprites+TILE_DATA_OFFSET,x
tax
phb
pea #^tiledata ; Set the bank to the tile data
plb
]line equ 0
lup 8
lda: tiledata+32+{]line*4},y
andl spritemask+{]line*256},x
stal spritemask+{]line*256},x
ldal spritedata+{]line*SPRITE_PLANE_SPAN},x
ora: tiledata+{]line*4},y
and: tiledata+32+{]line*4},y
stal spritedata+{]line*SPRITE_PLANE_SPAN},x
lda: tiledata+32+{]line*4}+2,y
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
stal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
ora: tiledata+{]line*4}+2,y
and: tiledata+32+{]line*4}+2,y
stal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
]line equ ]line+1
--^
plb ; pop extra byte
plb
plx
rts
; Erase is easy -- set an 8x8 area of the data region to all $0000 and the corresponding mask
; resgion to all $FFFF
;
; A = sprite ID
SPRITE_PLANE_SPAN equ 256
_EraseSprite
asl
tay
ldx _Sprites+VBUFF_ADDR,y
phb
pea #^spritedata
plb
lda #0
sta: {0*SPRITE_PLANE_SPAN}+0,x
sta: {0*SPRITE_PLANE_SPAN}+2,x
sta: {1*SPRITE_PLANE_SPAN}+0,x
sta: {1*SPRITE_PLANE_SPAN}+2,x
sta: {2*SPRITE_PLANE_SPAN}+0,x
sta: {2*SPRITE_PLANE_SPAN}+2,x
sta: {3*SPRITE_PLANE_SPAN}+0,x
sta: {3*SPRITE_PLANE_SPAN}+2,x
sta: {4*SPRITE_PLANE_SPAN}+0,x
sta: {4*SPRITE_PLANE_SPAN}+2,x
sta: {5*SPRITE_PLANE_SPAN}+0,x
sta: {5*SPRITE_PLANE_SPAN}+2,x
sta: {6*SPRITE_PLANE_SPAN}+0,x
sta: {6*SPRITE_PLANE_SPAN}+2,x
sta: {7*SPRITE_PLANE_SPAN}+0,x
sta: {7*SPRITE_PLANE_SPAN}+2,x
pea #^spritemask
plb
lda #$FFFF
sta: {0*SPRITE_PLANE_SPAN}+0,x
sta: {0*SPRITE_PLANE_SPAN}+2,x
sta: {1*SPRITE_PLANE_SPAN}+0,x
sta: {1*SPRITE_PLANE_SPAN}+2,x
sta: {2*SPRITE_PLANE_SPAN}+0,x
sta: {2*SPRITE_PLANE_SPAN}+2,x
sta: {3*SPRITE_PLANE_SPAN}+0,x
sta: {3*SPRITE_PLANE_SPAN}+2,x
sta: {4*SPRITE_PLANE_SPAN}+0,x
sta: {4*SPRITE_PLANE_SPAN}+2,x
sta: {5*SPRITE_PLANE_SPAN}+0,x
sta: {5*SPRITE_PLANE_SPAN}+2,x
sta: {6*SPRITE_PLANE_SPAN}+0,x
sta: {6*SPRITE_PLANE_SPAN}+2,x
sta: {7*SPRITE_PLANE_SPAN}+0,x
sta: {7*SPRITE_PLANE_SPAN}+2,x
pla
plb
rts
; Add a new sprite to the rendering pipeline
;
; A = tileId
; X = x position
; Y = y position
AddSprite ENT
phb
phk
plb
jsr _AddSprite
plb
rtl
_AddSprite
phx ; Save the parameters
pha
ldx #0
:loop lda _Sprites+SPRITE_STATUS,x ; Look for an open slot
beq :open
inx
inx
cpx #MAX_SPRITES*2
bcc :loop
pla ; Early out
pla
rts
:open lda #SPRITE_STATUS_DIRTY
sta _Sprites+SPRITE_STATUS,x ; Mark this sprite slot as occupied and that it needs to be drawn
pla
jsr _GetTileAddr
sta _Sprites+TILE_DATA_OFFSET,x
tya
clc
adc #NUM_BUFF_LINES ; The virtual buffer has 24 lines of off-screen space
xba ; Each virtual scan line is 256 bytes wide for overdraw space
clc
adc 1,s
sta _Sprites+VBUFF_ADDR,x
pla
rts
; 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.
;
; Each sprite record contains the following properties:
;
; +0: Sprite status word (0 = unoccupied)
; +2: Tile data address
; +4: Screen offset address (used for data and masks)
; Number of "off-screen" lines above logical (0,0)
NUM_BUFF_LINES equ 24
MAX_SPRITES equ 64
SPRITE_REC_SIZE equ 10
SPRITE_STATUS_EMPTY equ 0
SPRITE_STATUS_CLEAN equ 1
SPRITE_STATUS_DIRTY equ 2
SPRITE_STATUS equ 0
TILE_DATA_OFFSET equ {MAX_SPRITES*2}
VBUFF_ADDR equ {MAX_SPRITES*4}
SPRITE_X equ {MAX_SPRITES*6}
SPRITE_Y equ {MAX_SPRITES*8}
_Sprites ds SPRITE_REC_SIZE*MAX_SPRITES

View File

@ -290,7 +290,7 @@ _UpdateBG0TileMap
sbc #MAX_TILE_Y+1
sta :BlkY ; This is the Y-block we start drawing from
lda StartXMod164 ; Dx the same thing for X, except only need to clamp by 4
lda StartXMod164 ; Do the same thing for X, except only need to clamp by 4
and #$FFFC
lsr
lsr
@ -316,25 +316,27 @@ _UpdateBG0TileMap
; Handle fringe tiles -- if the fringe bit is set, then we need to get the fringe tile index
; and merge the tiles before rendering
bit #TILE_FRINGE_BIT
beq :no_fringe
jsr _GetTileAddr
tax
lda FringeMapPtr
ora FringeMapPtr+2
beq :no_fringe
lda [FringeMapPtr],y
jsr _GetTileAddr
tay
jsr _MergeTiles
:no_fringe
; bit #TILE_FRINGE_BIT
; beq :no_fringe
; jsr _GetTileAddr
; tax
; lda FringeMapPtr
; ora FringeMapPtr+2
; beq :no_fringe
; lda [FringeMapPtr],y
; jsr _GetTileAddr
; tay
; jsr _MergeTiles
;
;:no_fringe
inc :Offset ; pre-increment the address.
inc :Offset
ldx :BlkX
ldy :BlkY
jsr _CopyBG0Tile
; jsr _CopyBG0Tile
jsr _PushDirtyTile ; queue this tile for processing
lda :BlkX
inc

View File

@ -106,7 +106,7 @@ _SetBG1YPos
; Everytime either BG1 or BG0 X-position changes, we have to update the direct page values. We
; *could* do this by adjusting the since address offset, but we have to change up to 200 values
; *could* do this by adjusting the since the address offset, but we have to change up to 200 values
; when the vertical position changes, and only 41 when the horizontal value changes. Plus
; these are all direct page values
;

View File

@ -90,8 +90,3 @@ stk_save lda #0000 ; load the stack
plb ; restore the bank
rts
; Placeholder for actual sprite drawing. The implementation will be simple because
; we don't do anything sprite related; just call function pointers provided to us.
_RenderSprites
rts

View File

@ -221,6 +221,16 @@ ScreenAddr ENT
]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.
]step equ 0
TileStoreYTable ENT
lup 26
dw ]step
]step = ]step+{41*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
@ -238,6 +248,10 @@ BlitBuff ENT
BTableHigh ds 208*2*2
BTableLow ds 208*2*2
; A shorter table that just holds the blitter row addresses
BRowTableHigh ds 26*2*2
BRowTableLow 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 cenrer the buffer in the bank
]step equ $1800

View File

@ -8,10 +8,10 @@
; CopyTileLinear -- copies the tile data from the tile bank in linear order, e.g.
; 32 consecutive bytes are copied
; RenderTile
; _RenderTile
;
; A high-level function that takes a 16-bit tile descriptor and dispatched to the
; appropriate tile copy courinte based on the descritor flags
; appropriate tile copy routine based on the descriptor flags
;
; Bit 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
; +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
@ -19,13 +19,13 @@
; +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
; \____/ | | | | | \________________________/
; | | | | | | Tile ID (0 to 511)
; | | | | | |
; | | | | | +-- H : Flip tile horizontally
; | | | | +----- V : Flip tile vertically
; | | | +-------- D : Render as a Dynamic Tile (Tile ID < 32, V and H have no effect)
; | | +----------- M : Apply tile mask
; | +-------------- F : Overlay a fringe tile
; +----------------- Reserved
; | | | | | |
; | | | | | +-- H : Flip tile horizontally
; | | | | +----- V : Flip tile vertically
; | | | +-------- D : Render as a Dynamic Tile (Tile ID < 32, V and H have no effect)
; | | +----------- M : Apply tile mask
; | +-------------- F : Overlay a fringe tile
; +------------------- Reserved (must be zero)
;
; Each logical tile (corresponding to each Tile ID) actually takes up 128 bytes of memory in the
; tile bank
@ -38,13 +38,23 @@
; It is simply too slow to try to horizontally reverse the pixel data on the fly. This still allows
; for up to 512 tiles to be stored in a single bank, which should be sufficient.
TILE_ID_MASK equ $01FF
TILE_FRINGE_BIT equ $2000
TILE_MASK_BIT equ $1000
TILE_DYN_BIT equ $0800
TILE_VFLIP_BIT equ $0400
TILE_HFLIP_BIT equ $0200
TILE_CTRL_MASK equ $1E00 ; Deliberately ignore the Fringe bit in the dispatch
TILE_ID_MASK equ $01FF
TILE_SPRITE_BIT equ $8000 ; Set if this tile intersects an active sprite
TILE_PRIORITY_BIT equ $4000 ; Put tile on top of sprite
TILE_FRINGE_BIT equ $2000
TILE_MASK_BIT equ $1000
TILE_DYN_BIT equ $0800
TILE_VFLIP_BIT equ $0400
TILE_HFLIP_BIT equ $0200
TILE_CTRL_MASK equ $FE00
TILE_PROC_MASK equ $F800 ; Select tile proc for rendering
; Temporary direct page locatinos used by some of the complext tile renderers
_X_REG equ tiletmp
_Y_REG equ tiletmp+2
_T_PTR equ tiletmp+4 ; Copy of the tile address pointer
_BASE_ADDR equ tiletmp+6 ; Copy of BTableLow for this tile
; Low-level function to take a tile descriptor and return the address in the tiledata
; bank. This is not too useful in the fast-path because the fast-path does more
@ -77,147 +87,105 @@ _GetTileAddr
; Y is set to the top-left address of the tile in the BG1 data bank
;
; tmp0/tmp1 is reserved
RenderTileBG1
tax ; Save the tile descriptor
and #TILE_ID_MASK ; Mask out the ID and save just that
_Mul128 ; multiplied by 128
pha
_RenderTileBG1
pha ; Save the tile descriptor
txa
and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; Only horizontal and vertical flips are supported for BG1
xba
tax
jmp (:actions,x)
ldal :actions,x
stal :tiledisp+1
:actions dw bg1_noflip,bg1_hflip,bg1_vflip,bg1_hvflip
bg1_noflip
pla
brl _CopyTileBG1
bg1_hflip
pla
clc
adc #64 ; Advance to the flipped version
brl _CopyTileBG1
bg1_vflip
pla
brl _CopyTileBG1V
bg1_hvflip
pla
clc
adc #64 ; Advance to the flipped version
brl _CopyTileBG1V
_CopyTileBG1 tax
ldal tiledata+0,x
sta: $0000,y
ldal tiledata+2,x
sta: $0002,y
ldal tiledata+4,x
sta $0100,y
ldal tiledata+6,x
sta $0102,y
ldal tiledata+8,x
sta $0200,y
ldal tiledata+10,x
sta $0202,y
ldal tiledata+12,x
sta $0300,y
ldal tiledata+14,x
sta $0302,y
ldal tiledata+16,x
sta $0400,y
ldal tiledata+18,x
sta $0402,y
ldal tiledata+20,x
sta $0500,y
ldal tiledata+22,x
sta $0502,y
ldal tiledata+24,x
sta $0600,y
ldal tiledata+26,x
sta $0602,y
ldal tiledata+28,x
sta $0700,y
ldal tiledata+30,x
sta $0702,y
rts
_CopyTileBG1V tax
ldal tiledata+0,x
sta: $0700,y
ldal tiledata+2,x
sta: $0702,y
ldal tiledata+4,x
sta $0600,y
ldal tiledata+6,x
sta $0602,y
ldal tiledata+8,x
sta $0500,y
ldal tiledata+10,x
sta $0502,y
ldal tiledata+12,x
sta $0400,y
ldal tiledata+14,x
sta $0402,y
ldal tiledata+16,x
sta $0300,y
ldal tiledata+18,x
sta $0302,y
ldal tiledata+20,x
sta $0200,y
ldal tiledata+22,x
sta $0202,y
ldal tiledata+24,x
sta $0100,y
ldal tiledata+26,x
sta $0102,y
ldal tiledata+28,x
sta $0000,y
ldal tiledata+30,x
sta $0002,y
rts
; On entry
;
; B is set to the correct code field bank
; A is set to the the tile descriptor
; Y is set to the top-left address of the tile in the code field
; X is set to the tile word offset (0 through 80 in steps of 4)
;
; tmp0/tmp1 is reserved
RenderTile
bit #TILE_CTRL_MASK ; Fast path for "normal" tiles
beq _CopyTile
cmp #TILE_MASK_BIT ; Tile 0 w/mask bit set is special, too
bne *+5
brl ClearTile
phx ; Save the tile offset
tax
and #TILE_ID_MASK ; Mask out the ID and save just that
_Mul128 ; multiplied by 128
pha
txa
and #TILE_CTRL_MASK ; Mask out the different modifiers
xba
tax
jmp (:actions,x)
:tiledisp jmp $0000
:actions dw solid,solid_hflip,solid_vflip,solid_hvflip
; dw dynamic,dynamic,dynamic,dynamic
dw dyn_masked,dyn_masked,dyn_masked,dyn_masked
dw masked,masked_hflip,masked_vflip,masked_hvflip
dw dyn_masked,dyn_masked,dyn_masked,dyn_masked
:actions dw _TBSolidBG1_00,_TBSolidBG1_0H,_TBSolidBG1_V0,_TBSolidBG1_VH
FillWord0
; Given an address to a Tile Store record, dispatch to the appropriate tile renderer. The Tile
; Store record contains all of the low-level information that's needed to call the renderer.
;
; Y = address of tile
_RenderTile2
lda TileStore+TS_TILE_ID,y ; build the finalized tile descriptor
ora TileStore+TS_SPRITE_FLAG,y
and #TILE_CTRL_MASK
tax
lda TileProcs,x ; load and patch in the appropriate subroutine
sta :tiledisp+1
ldx TileStore+TS_TILE_ADDR,y ; load the address of this tile's data (pre-calculated)
sep #$20 ; load the bank of the target code field line
lda TileStore+TS_CODE_ADDR_HIGH,y
pha
rep #$20
lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field
sta _BASE_ADDR
lda TileStore+TS_WORD_OFFSET,y
ldy _BASE_ADDR
plb ; set the bank
; B is set to the correct code field bank
; A is set to the tile word offset (0 through 80 in steps of 4)
; Y is set to the top-left address of the tile in the code field
; X is set to the address of the tile data
:tiledisp jmp $0000 ; render the tile
; Reference all of the tile rendering subroutines defined in the TileXXXXX files. Each file defines
; 8 entry points:
;
; One set for normal, horizontally flipped, vertically flipped and hors & vert flipped.
; A second set that are optimized for when EngineMode has BG1 disabled.
TileProcs dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 00000 : normal tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 00001 : dynamic tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 00010 : masked normal tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 00011 : masked dynamic tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 00100 : fringed normal tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 00101 : fringed dynamic tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 00110 : fringed masked normal tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 00111 : fringed masked dynamic tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 01000 : high-priority normal tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 01001 : high-priority dynamic tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 01010 : high-priority masked normal tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 01011 : high-priority masked dynamic tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 01100 : high-priority fringed normal tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 01101 : high-priority fringed dynamic tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 01110 : high-priority fringed masked normal tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 01111 : high-priority fringed masked dynamic tiles
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 10000 : normal tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 10001 : dynamic tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 10010 : masked normal tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 10011 : masked dynamic tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 10100 : fringed normal tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 10101 : fringed dynamic tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 10110 : fringed masked normal tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 10111 : fringed masked dynamic tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 11000 : high-priority normal tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 11001 : high-priority dynamic tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 11010 : high-priority masked normal tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 11011 : high-priority masked dynamic tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 11100 : high-priority fringed normal tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 11101 : high-priority fringed dynamic tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 11110 : high-priority fringed masked normal tiles w/sprite
dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH ; 11111 : high-priority fringed masked dynamic tiles w/sprite
; _TBConstTile
;
; A specialized routine that fills in a tile with a single constant value. It's intended to be used to
; fill in solid colors, so there are no specialized horizontal or verical flipped variants
_TBConstTile
sta: $0001,y
sta: $0004,y
sta $1001,y
@ -234,195 +202,9 @@ FillWord0
sta $6004,y
sta $7001,y
sta $7004,y
bra FillPEAOpcode
jmp _TBFillPEAOpcode
; _CopyTile
;
; Copy a solid tile into one of the code banks
;
; B = bank of the code field
; A = Tile ID (0 - 1023)
; Y = Base Adddress in the code field
_CopyTile cmp #$0000 ; Fast-path the special zero tile
beq FillWord0
CopyTileMem
_Mul128 ; Take care of getting the right tile address
CopyTileMem0
tax
ldal tiledata+0,x ; The low word goes in the *next* instruction
sta: $0004,y
ldal tiledata+2,x
sta: $0001,y
ldal tiledata+4,x
sta $1004,y
ldal tiledata+6,x
sta $1001,y
ldal tiledata+8,x
sta $2004,y
ldal tiledata+10,x
sta $2001,y
ldal tiledata+12,x
sta $3004,y
ldal tiledata+14,x
sta $3001,y
ldal tiledata+16,x
sta $4004,y
ldal tiledata+18,x
sta $4001,y
ldal tiledata+20,x
sta $5004,y
ldal tiledata+22,x
sta $5001,y
ldal tiledata+24,x
sta $6004,y
ldal tiledata+26,x
sta $6001,y
ldal tiledata+28,x
sta $7004,y
ldal tiledata+30,x
sta $7001,y ; Fall through
; For solid tiles
FillPEAOpcode
sep #$20
lda #$F4
sta: $0000,y
sta: $0003,y
sta $1000,y
sta $1003,y
sta $2000,y
sta $2003,y
sta $3000,y
sta $3003,y
sta $4000,y
sta $4003,y
sta $5000,y
sta $5003,y
sta $6000,y
sta $6003,y
sta $7000,y
sta $7003,y
rep #$20
rts
; Masked tiles
;
; Can result in one of three different code sequences
;
; If mask === $0000, then insert PEA $DATA
; If mask === $FFFF, then insert LDA (DP),y / PHA
; Else then insert JMP and patch exception handler
;
; Because every word of the tile can lead to different opcodes, we
; do the entire setup for each word rather than breaking them up into
; 16-bit and 8-bit operations.
; Macro to make the loop simpler. Takes three arguments
;
; ]1 = address of tile data
; ]2 = address of tile mask
; ]3 = address of target in code field
_X_REG equ tiletmp
_Y_REG equ tiletmp+2
_T_PTR equ tiletmp+4 ; Copy of the tile address pointer
_BASE_ADDR equ tiletmp+6 ; Copy of BTableLow for this tile
CopyTileMemM
stx _X_REG ; Save these values as we will need to reload them
sty _Y_REG ; at certain points
sta _T_PTR
tax
; Do the left column first
CopyMaskedWord tiledata+0;tiledata+32+0;$0003
CopyMaskedWord tiledata+4;tiledata+32+4;$1003
CopyMaskedWord tiledata+8;tiledata+32+8;$2003
CopyMaskedWord tiledata+12;tiledata+32+12;$3003
CopyMaskedWord tiledata+16;tiledata+32+16;$4003
CopyMaskedWord tiledata+20;tiledata+32+20;$5003
CopyMaskedWord tiledata+24;tiledata+32+24;$6003
CopyMaskedWord tiledata+28;tiledata+32+28;$7003
; Move the index for the JTableOffset array. This is the same index used for transparent words,
; so, if _X_REG is zero, then we would be patching out the last word in the code field with LDA (0),y
; and then increment _X_REG by two to patch the next-to-last word in the code field with LDA (2),y
inc _X_REG
inc _X_REG
; Do the right column
CopyMaskedWord tiledata+2;tiledata+32+2;$0000
CopyMaskedWord tiledata+6;tiledata+32+6;$1000
CopyMaskedWord tiledata+10;tiledata+32+10;$2000
CopyMaskedWord tiledata+14;tiledata+32+14;$3000
CopyMaskedWord tiledata+18;tiledata+32+18;$4000
CopyMaskedWord tiledata+22;tiledata+32+22;$5000
CopyMaskedWord tiledata+26;tiledata+32+26;$6000
CopyMaskedWord tiledata+30;tiledata+32+30;$7000
rts
CopyTileMemMV
stx _X_REG ; Save these values as we will need to reload them
sty _Y_REG ; at certain points
sta _T_PTR
tax
CopyMaskedWord tiledata+0;tiledata+32+0;$7003
CopyMaskedWord tiledata+2;tiledata+32+2;$7000
CopyMaskedWord tiledata+4;tiledata+32+4;$6003
CopyMaskedWord tiledata+6;tiledata+32+6;$6000
CopyMaskedWord tiledata+8;tiledata+32+8;$5003
CopyMaskedWord tiledata+10;tiledata+32+10;$5000
CopyMaskedWord tiledata+12;tiledata+32+12;$4003
CopyMaskedWord tiledata+14;tiledata+32+14;$4000
CopyMaskedWord tiledata+16;tiledata+32+16;$3003
CopyMaskedWord tiledata+18;tiledata+32+18;$3000
CopyMaskedWord tiledata+20;tiledata+32+20;$2003
CopyMaskedWord tiledata+22;tiledata+32+22;$2000
CopyMaskedWord tiledata+24;tiledata+32+24;$1003
CopyMaskedWord tiledata+28;tiledata+32+26;$1000
CopyMaskedWord tiledata+30;tiledata+32+28;$0003
CopyMaskedWord tiledata+32;tiledata+32+30;$0000
rts
TilePatterns dw $0000,$1111,$2222,$3333
dw $4444,$5555,$6666,$7777
dw $8888,$9999,$AAAA,$BBBB
dw $CCCC,$DDDD,$EEEE,$FFFF
ClearTile sep #$20
lda #$B1 ; This is a special case where we can set all the words to LDA (DP),y
sta: $0000,y
sta: $0003,y
sta $1000,y
sta $1003,y
sta $2000,y
sta $2003,y
sta $3000,y
sta $3003,y
sta $4000,y
sta $4003,y
sta $5000,y
sta $5003,y
sta $6000,y
sta $6003,y
sta $7000,y
sta $7003,y
rep #$20
txa
ClearTile
and #$00FF
ora #$4800
sta: $0004,y
@ -443,126 +225,9 @@ ClearTile sep #$20
sta $5001,y
sta $6001,y
sta $7001,y
rts
; Copy a tile, but vertically flip the data
CopyTileMemV
tax
ldal tiledata+0,x ; The low word goes in the *next* instruction
sta $7004,y
ldal tiledata+2,x
sta $7001,y
ldal tiledata+4,x
sta $6004,y
ldal tiledata+6,x
sta $6001,y
ldal tiledata+8,x
sta $5004,y
ldal tiledata+10,x
sta $5001,y
ldal tiledata+12,x
sta $4004,y
ldal tiledata+14,x
sta $4001,y
ldal tiledata+16,x
sta $3004,y
ldal tiledata+18,x
sta $3001,y
ldal tiledata+20,x
sta $2004,y
ldal tiledata+22,x
sta $2001,y
ldal tiledata+24,x
sta $1004,y
ldal tiledata+26,x
sta $1001,y
ldal tiledata+28,x
sta: $0004,y
ldal tiledata+30,x
sta: $0001,y
rts
; Primitives to render a dynamic tile
;
; LDA 00,x / PHA where the operand is fixed when the tile is rendered
; $B5 $00 $48
;
; A = dynamic tile id (must be <32)
DynamicTile
and #$007F ; clamp to < (32 * 4)
ora #$4800
sta: $0004,y
sta $1004,y
sta $2004,y
sta $3004,y
sta $4004,y
sta $5004,y
sta $6004,y
sta $7004,y
inc
inc
sta: $0001,y
sta $1001,y
sta $2001,y
sta $3001,y
sta $4001,y
sta $5001,y
sta $6001,y
sta $7001,y
sep #$20
lda #$B5
sta: $0000,y
sta: $0003,y
sta $1000,y
sta $1003,y
sta $2000,y
sta $2003,y
sta $3000,y
sta $3003,y
sta $4000,y
sta $4003,y
sta $5000,y
sta $5003,y
sta $6000,y
sta $6003,y
sta $7000,y
sta $7003,y
rep #$20
rts
DynamicTileM
and #$007F ; clamp to < (32 * 4)
sta _T_PTR
stx _X_REG
CopyMaskedDWord $0003
CopyMaskedDWord $1003
CopyMaskedDWord $2003
CopyMaskedDWord $3003
CopyMaskedDWord $4003
CopyMaskedDWord $5003
CopyMaskedDWord $6003
CopyMaskedDWord $7003
inc _T_PTR ; Move to the next column
inc _T_PTR
inc _X_REG ; Move to the next column
inc _X_REG
CopyMaskedDWord $0000
CopyMaskedDWord $1000
CopyMaskedDWord $2000
CopyMaskedDWord $3000
CopyMaskedDWord $4000
CopyMaskedDWord $5000
CopyMaskedDWord $6000
CopyMaskedDWord $7000
sep #$20
lda #$4C ; Set everything to JMP instructions
lda #$B1 ; This is a special case where we can set all the words to LDA (DP),y
sta: $0000,y
sta: $0003,y
sta $1000,y
@ -598,26 +263,7 @@ CopyTileToDyn ENT
adc #$0100 ; Go to the next page
tay
jsr CopyTileDToDyn ; Copy the tile data
jsr CopyTileMToDyn ; Copy the tile data
rtl
; Helper functions to copy tile data and mask to the appropriate location in Bank 0
; X = tile ID
; Y = dynamic tile ID
CopyTileAndMaskToDyn ENT
txa
jsr _GetTileAddr
tax
tya
and #$001F ; Maximum of 32 dynamic tiles
asl
asl ; 4 bytes per page
adc BlitterDP ; Add to the bank 00 base address
adc #$0100 ; Go to the next page
tay
jsr CopyTileDToDyn ; Copy the tile data
jsr CopyTileMToDyn ; Copy the tile data
jsr CopyTileMToDyn ; Copy the tile mask
rtl
; X = address of tile
@ -718,105 +364,6 @@ CopyTileMToDyn
plb
rts
; This should never be called, because empty control value should be fast-pathed
solid
pla
plx
brl CopyTileMem
solid_hflip
pla
clc
adc #64 ; Advance to the flipped version
plx
brl CopyTileMem
solid_vflip
pla
plx
brl CopyTileMemV
solid_hvflip
pla
clc
adc #64 ; Advance to the flipped version
plx
brl CopyTileMemV
masked
pla
plx
brl CopyTileMemM
masked_hflip
pla
clc
adc #64 ; Advance to the flipped version
plx
brl CopyTileMemM
masked_vflip
pla
plx
brl CopyTileMemMV
masked_hvflip
pla
clc
adc #64 ; Advance to the flipped version
plx
brl CopyTileMemMV
dynamic
pla
asl
asl
asl
xba ; Undo the x128 we just need x4
plx
brl DynamicTile
dyn_masked
pla
asl
asl
asl
xba ; Undo the x128 we just need x4
plx
brl DynamicTileM
; Merge
;
; For fringe support -- takes a pointer to two tiles and composites them into
; some scratch space.
;
; X = primary tile address
; Y = fringe tile address
tilescratch equ $FF80
_MergeTiles
; Merge the tile data
]step equ 0
lup 16
lda: tiledata+]step,x
and: tiledata+32+]step,y
ora: tiledata+]step,y
sta: tilescratch+]step
]step equ ]step+2
--^
; Merge the tile masks
]step equ 0
lup 16
lda: tiledata+32+]step,x
and: tiledata+32+]step,y
sta: tilescratch+32+]step
]step equ ]step+2
--^
lda #tilescratch/128
rts
; CopyBG0Tile
;
; A low-level function that copies 8x8 tiles directly into the code field space.
@ -841,7 +388,7 @@ _CopyBG0Tile
asl
asl
asl
asl
asl ; x2 because the table contains words, not
tay
sep #$20 ; set the bank register
@ -860,20 +407,17 @@ _CopyBG0Tile
adc Col2CodeOffset+2,x ; Get the right edge (which is the lower physical address)
tay
; Optimization note: We could make a Tile2CodeOffset table that is pre-reversed, which should simplify
; the code starting after the 'rep #$20' to just be this. Saves around 16 cycles / tile...
;
; There would need to be a similar modification made to the JTable as well.
plb ; set the bank
pla ; pop the tile ID
jsr RenderTile
; jsr _RenderTile
:exit
plx ; pop the x-register
plb ; restore the data bank and return
rts
; CopyTileBG1
; CopyBG1Tile
;
; A low-level function that copies 8x8 tiles directly into the BG1 data buffer.
;
@ -914,26 +458,179 @@ _CopyBG1Tile
rep #$20
pla ; pop the tile ID
jsr RenderTileBG1
jsr _RenderTileBG1
plx ; pop the x-register
plb ; restore the data bank and return
rts
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
; Tile Store that holds tile records which contain all the essential information for rendering
; a tile.
;
; TileStore+TS_TILE_ID : Tile descriptor
; TileStore+TS_DIRTY : $FFFF is clean, otherwise stores a back-reference to the DirtyTiles array
; TileStore+TS_SPRITE_FLAG : Set to TILE_SPRITE_BIT is a sprite is present at this tile location
; TileStore+TS_TILE_ADDR : Address of the tile in the tile data buffer
; TIleStore+TS_CODE_ADDR_LOW : Low word of the address in the code field that receives the tile
; TileStore+TS_CODE_ADDR_HIGH : High word of the address in the code field that receives the tile
; TileStore+TS_WORD_OFFSET : Logical number of word for this location
TileStore ds TILE_STORE_SIZE*7
TS_TILE_ID equ TILE_STORE_SIZE*0
TS_DIRTY equ TILE_STORE_SIZE*1
TS_SPRITE_FLAG equ TILE_STORE_SIZE*2
TS_TILE_ADDR equ TILE_STORE_SIZE*3 ; const value
TS_CODE_ADDR_LOW equ TILE_STORE_SIZE*4 ; const value
TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5 ; const value
TS_WORD_OFFSET equ TILE_STORE_SIZE*6
; 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
; Initialize the tile storage data structures. This takes care of populating the tile records with the
; appropriate constant values.
_InitDirtyTiles
ldx #TILE_STORE_SIZE-2 ; Initialize the tile backing store with zeros
:loop lda #0
sta TileStore+TS_TILE_ID,x
lda #$FFFF
sta TileStore+TS_DIRTY
dex
dex
bpl :loop
rts
_ClearDirtyTiles
:loop
lda DirtyTileCount
beq :done
jsr _PopDirtyTile
bra :loop
:done
rts
; Helper function to get the address offset into the tile cachce / tile backing store
; X = tile column [0, 40] (41 columns)
; Y = tile row [0, 25] (26 rows)
_GetTileStoreOffset
phx ; preserve the registers
phy
jsr _GetTileStoreOffset0
ply
plx
rts
_GetTileStoreOffset0
tya
asl
tay
txa
asl
clc
adc TileStoreYTable,y
rts
; Set a tile value in the tile backing store. Mark dirty if the value changes
;
; A = tile id
; X = tile column [0, 40] (41 columns)
; Y = tile row [0, 25] (26 rows)
_SetTile
pha
jsr _GetTileStoreOffset0
tay
pla
cmp TileStore+TS_TILE_ID,y
beq :nochange
sta TileStore+TS_TILE_ID,y
tya
jmp _PushDirtyTile
:nochange rts
; Append a new dirty tile record
;
; A = result of _GetTileStoreOffset for X, Y
; X = tile column [0, 40] (41 columns)
; Y = tile row [0, 25] (26 rows)
;
; The main purposed of this function is to
;
; 1. Avoid marking the same tile dirty multiple times, and
; 2. Pre-calculating all of the information necessary to render the tile
_PushDirtyTile
tay ; check if this already marked immediately
lda TileStore+TS_DIRTY,y ; If the lookup === $FFFF (<$8000), it is free.
bpl :occupied
; At this point, keep the Y register value because it is the correct offset to all of the tile
; record fields.
ldx DirtyTileCount
txa
sta TileStore+TS_DIRTY,y ; Store a back-link to this record
tya
sta DirtyTiles,x ; Store the lookup address in the list
inx
inx
stx DirtyTileCount ; Commit
rts
:occupied
ply
rts
; Remove a dirty tile from the list and return it in state ready to be rendered. It is important
; that the core rendering functions *only* use _PopDirtyTile to get a list of tiles to update,
; because this routine merges the tile IDs stored in the Tile Store with the Sprite
; information to set the TILE_SPRITE_BIT. This is the *only* place in the entire code base that
; applies this bit to a tile descriptor.
_PopDirtyTile
ldx DirtyTileCount
bne _PopDirtyTile2
rts
_PopDirtyTile2 ; alternate entry point
dex
dex
stx DirtyTileCount ; remove last item from the list
ldy DirtyTiles,x ; load the offset into the Tile Store
lda #$FFFF
sta DirtyTileCache,y ; clear the occupied backlink
rts
; Run through the dirty tile list and render them into the code field
_ApplyTiles
bra :begin
:loop
; Retrieve the offset of the next dirty Tile Store items
jsr _PopDirtyTile2
; Call the generic dispatch with the Tile Store record pointer at by the Y-register.
phb
jsr _RenderTile2
plb
; Loop again until the list of dirty tiles is empty
:begin ldx DirtyTileCount
bne :loop
rts

112
src/blitter/Tiles00000.s Normal file
View File

@ -0,0 +1,112 @@
; _TBSolidTile
;
; Define the addresses of the subroutines that draw the normal and flipped variants of the tiles, both
; in the optimized (no second background) and normal cases.
;
; On entry, the following register values need to be set
;
; X : address of base tile in the tiledata bank (tileId * 128)
; Y : address of the top-left corder of the tile location in the code field
; B : set to the code field bank
;_TBSolidTile dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH
; dw _TBCopyData,_TBCopyDataH,_TBCopyDataV,_TBCopyDataVH
_TBSolidTile_00
jsr _TBCopyData
jmp _TBFillPEAOpcode
_TBSolidTile_0H
jsr _TBCopyDataH
jmp _TBFillPEAOpcode
_TBSolidTile_V0
jsr _TBCopyDataV
jmp _TBFillPEAOpcode
_TBSolidTile_VH
jsr _TBCopyDataVH
jmp _TBFillPEAOpcode
; The workhorse blitter. This blitter copies tile data into the code field without masking. This is the
; most common blitter function. It is slightly optimized to fall through to the code that sets the PEA
; opcodes in order to be slightly more efficient given it's frequent usage.
;
; There is a small variation of this blitter that just copies the data without setting the PEA opcodes. This
; is used by the engine when the capabilitiy bits have turned off the second background layer. In fact, most
; of the tile rendering routines have an optimized version for this important use case. Skipping the opcode
; step results in a 37% speed boost in tile rendering.
;
; 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.
_TBCopyData
]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
--^
rts
_TBCopyDataH
]line equ 0
lup 8
ldal tiledata+{]line*4}+64,x
sta: $0004+{]line*$1000},y
ldal tiledata+{]line*4}+66,x
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
rts
_TBCopyDataV
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4},x
sta: $0004+{]dest*$1000},y
ldal tiledata+{]src*4}+2,x
sta: $0001+{]dest*$1000},y
]src equ ]src-1
]dest equ ]dest+1
--^
rts
_TBCopyDataVH
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4}+64,x
sta: $0004+{]dest*$1000},y
ldal tiledata+{]src*4}+66,x
sta: $0001+{]dest*$1000},y
]src equ ]src-1
]dest equ ]dest+1
--^
rts
; A simple helper function that fill in all of the opcodes of a tile with the PEA opcode. This is
; a common function since a tile must be explicitly flagged to use a mask, so this routine is used
; quite frequently in a well-designed tile map.
_TBFillPEAOpcode
sep #$20
lda #$F4
sta: $0000,y
sta: $0003,y
sta $1000,y
sta $1003,y
sta $2000,y
sta $2003,y
sta $3000,y
sta $3003,y
sta $4000,y
sta $4003,y
sta $5000,y
sta $5003,y
sta $6000,y
sta $6003,y
sta $7000,y
sta $7003,y
rep #$20
rts

70
src/blitter/Tiles00001.s Normal file
View File

@ -0,0 +1,70 @@
; _TBDynamicTile
;
; These subroutines fill in the code field with the instructions to render data from the dynamic
; code buffer. This is a bit different, because no tile data is manipulated. It is the
; responsibiliy of the user of the API to use the CopyTileToDyn subroutine to get data
; into the correct location.
;
; This tile type does not explicitly support horizontal or vertical flipping. An appropriate tile
; descriptor should be passed into CopyTileToDyn to put the horizontally or vertically flipped source
; data into the dynamic tile buffer
_TBDynamicTile dw _TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00
dw _TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00
_TBDynamicTile_00
jsr _TBDynamicData
jmp _TBFillLdaDpOpcode
; Primitives to render a dynamic tile
;
; LDA 00,x / PHA where the operand is fixed when the tile is rendered
; $B5 $00 $48
;
; A = dynamic tile id (must be <32)
_TBDynamicData
txa
asl
asl
asl
xba ; Undo the x128 we just need x4
and #$007F ; clamp to < (32 * 4)
ora #$4800 ; insert the PHA instruction
]line equ 0 ; render the first column
lup 8
sta: $0004+{]line*$1000},y
]line equ ]line+1
--^
inc ; advance to the next word
inc
]line equ 0 ; render the second column
lup 8
sta: $0001+{]line*$1000},y
]line equ ]line+1
rts
; A simple helper function that fill in all of the opcodes of a tile with the LDA dp,x opcode.
_TBFillLdaDpOpcode
sep #$20
lda #$B5
sta: $0000,y
sta: $0003,y
sta $1000,y
sta $1003,y
sta $2000,y
sta $2003,y
sta $3000,y
sta $3003,y
sta $4000,y
sta $4003,y
sta $5000,y
sta $5003,y
sta $6000,y
sta $6003,y
sta $7000,y
sta $7003,y
rep #$20
rts

130
src/blitter/Tiles00010.s Normal file
View File

@ -0,0 +1,130 @@
; _TBMaskedTile
;
; These tile renderes are for "normal" tiles that also apply their mask data. If the case of the second
; background being disabled, the optimized variants are the same as Tile00000
_TBMaskedTile dw _TBMaskedTile_00,_TBMaskedTile_0H,_TBMaskedTile_V0,_TBMaskedTile_VH
dw _TBCopyData,_TBCopyDataH,_TBCopyDataV,_TBCopyDataVH
_TBMaskedTile_00
stx _X_REG ; Save these values as we will need to reload them
sty _Y_REG ; at certain points
sta _T_PTR
tax
; Do the left column first
CopyMaskedWord tiledata+0;tiledata+32+0;$0003
CopyMaskedWord tiledata+4;tiledata+32+4;$1003
CopyMaskedWord tiledata+8;tiledata+32+8;$2003
CopyMaskedWord tiledata+12;tiledata+32+12;$3003
CopyMaskedWord tiledata+16;tiledata+32+16;$4003
CopyMaskedWord tiledata+20;tiledata+32+20;$5003
CopyMaskedWord tiledata+24;tiledata+32+24;$6003
CopyMaskedWord tiledata+28;tiledata+32+28;$7003
; Move the index for the JTableOffset array. This is the same index used for transparent words,
; so, if _X_REG is zero, then we would be patching out the last word in the code field with LDA (0),y
; and then increment _X_REG by two to patch the next-to-last word in the code field with LDA (2),y
inc _X_REG
inc _X_REG
; Do the right column
CopyMaskedWord tiledata+2;tiledata+32+2;$0000
CopyMaskedWord tiledata+6;tiledata+32+6;$1000
CopyMaskedWord tiledata+10;tiledata+32+10;$2000
CopyMaskedWord tiledata+14;tiledata+32+14;$3000
CopyMaskedWord tiledata+18;tiledata+32+18;$4000
CopyMaskedWord tiledata+22;tiledata+32+22;$5000
CopyMaskedWord tiledata+26;tiledata+32+26;$6000
CopyMaskedWord tiledata+30;tiledata+32+30;$7000
rts
_TBMaskedTile_0H
stx _X_REG ; Save these values as we will need to reload them
sty _Y_REG ; at certain points
sta _T_PTR
tax
CopyMaskedWord tiledata+64+0;tiledata+64+32+0;$0003
CopyMaskedWord tiledata+64+4;tiledata+64+32+4;$1003
CopyMaskedWord tiledata+64+8;tiledata+64+32+8;$2003
CopyMaskedWord tiledata+64+12;tiledata+64+32+12;$3003
CopyMaskedWord tiledata+64+16;tiledata+64+32+16;$4003
CopyMaskedWord tiledata+64+20;tiledata+64+32+20;$5003
CopyMaskedWord tiledata+64+24;tiledata+64+32+24;$6003
CopyMaskedWord tiledata+64+28;tiledata+64+32+28;$7003
inc _X_REG
inc _X_REG
CopyMaskedWord tiledata+64+2;tiledata+64+32+2;$0000
CopyMaskedWord tiledata+64+6;tiledata+64+32+6;$1000
CopyMaskedWord tiledata+64+10;tiledata+64+32+10;$2000
CopyMaskedWord tiledata+64+14;tiledata+64+32+14;$3000
CopyMaskedWord tiledata+64+18;tiledata+64+32+18;$4000
CopyMaskedWord tiledata+64+22;tiledata+64+32+22;$5000
CopyMaskedWord tiledata+64+26;tiledata+64+32+26;$6000
CopyMaskedWord tiledata+64+30;tiledata+64+32+30;$7000
rts
_TBMaskedTile_V0
stx _X_REG ; Save these values as we will need to reload them
sty _Y_REG ; at certain points
sta _T_PTR
tax
CopyMaskedWord tiledata+0;tiledata+32+0;$7003
CopyMaskedWord tiledata+4;tiledata+32+4;$6003
CopyMaskedWord tiledata+8;tiledata+32+8;$5003
CopyMaskedWord tiledata+12;tiledata+32+12;$4003
CopyMaskedWord tiledata+16;tiledata+32+16;$3003
CopyMaskedWord tiledata+20;tiledata+32+20;$2003
CopyMaskedWord tiledata+24;tiledata+32+24;$1003
CopyMaskedWord tiledata+28;tiledata+32+28;$0003
inc _X_REG
inc _X_REG
CopyMaskedWord tiledata+2;tiledata+32+2;$7000
CopyMaskedWord tiledata+6;tiledata+32+6;$6000
CopyMaskedWord tiledata+10;tiledata+32+10;$5000
CopyMaskedWord tiledata+14;tiledata+32+14;$4000
CopyMaskedWord tiledata+18;tiledata+32+18;$3000
CopyMaskedWord tiledata+22;tiledata+32+22;$2000
CopyMaskedWord tiledata+26;tiledata+32+26;$1000
CopyMaskedWord tiledata+30;tiledata+32+30;$0000
rts
_TBMaskedTile_VH
stx _X_REG ; Save these values as we will need to reload them
sty _Y_REG ; at certain points
sta _T_PTR
tax
CopyMaskedWord tiledata+64+0;tiledata+64+32+0;$7003
CopyMaskedWord tiledata+64+4;tiledata+64+32+4;$6003
CopyMaskedWord tiledata+64+8;tiledata+64+32+8;$5003
CopyMaskedWord tiledata+64+12;tiledata+64+32+12;$4003
CopyMaskedWord tiledata+64+16;tiledata+64+32+16;$3003
CopyMaskedWord tiledata+64+20;tiledata+64+32+20;$2003
CopyMaskedWord tiledata+64+24;tiledata+64+32+24;$1003
CopyMaskedWord tiledata+64+28;tiledata+64+32+28;$0003
inc _X_REG
inc _X_REG
CopyMaskedWord tiledata+64+2;tiledata+64+32+2;$7000
CopyMaskedWord tiledata+64+6;tiledata+64+32+6;$6000
CopyMaskedWord tiledata+64+10;tiledata+64+32+10;$5000
CopyMaskedWord tiledata+64+14;tiledata+64+32+14;$4000
CopyMaskedWord tiledata+64+18;tiledata+64+32+18;$3000
CopyMaskedWord tiledata+64+22;tiledata+64+32+22;$2000
CopyMaskedWord tiledata+64+26;tiledata+64+32+26;$1000
CopyMaskedWord tiledata+64+30;tiledata+64+32+30;$0000
rts

64
src/blitter/Tiles00011.s Normal file
View File

@ -0,0 +1,64 @@
; _TBDynamicMaskTile
;
; Insert a code sequence to mask the dynamic tile against the background. This is quite a slow process because
; every word needs to be handled with a JMP exception; but it looks good!
_TBDynamicMaskTile dw _TBDynamicMaskTile_00,_TBDynamicMaskTile_00,_TBDynamicMaskTile_00,_TBDynamicMaskTile_00
dw _TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00
_TBDynamicMaskTile_00
jsr _TBDynamicDataAndMask
jmp _TBFillJMPOpcode
; A = dynamic tile id (must be <32)
_TBDynamicDataAndMask
and #$007F ; clamp to < (32 * 4)
sta _T_PTR
stx _X_REG
CopyMaskedDWord $0003
CopyMaskedDWord $1003
CopyMaskedDWord $2003
CopyMaskedDWord $3003
CopyMaskedDWord $4003
CopyMaskedDWord $5003
CopyMaskedDWord $6003
CopyMaskedDWord $7003
inc _T_PTR ; Move to the next column
inc _T_PTR
inc _X_REG ; Move to the next column
inc _X_REG
CopyMaskedDWord $0000
CopyMaskedDWord $1000
CopyMaskedDWord $2000
CopyMaskedDWord $3000
CopyMaskedDWord $4000
CopyMaskedDWord $5000
CopyMaskedDWord $6000
CopyMaskedDWord $7000
rts
; A simple helper function that fill in all of the opcodes of a tile with the JMP opcode.
_TBFillJMPOpcode
sep #$20
lda #$4C
sta: $0000,y
sta: $0003,y
sta $1000,y
sta $1003,y
sta $2000,y
sta $2003,y
sta $3000,y
sta $3003,y
sta $4000,y
sta $4003,y
sta $5000,y
sta $5003,y
sta $6000,y
sta $6003,y
sta $7000,y
sta $7003,y
rep #$20
rts

141
src/blitter/Tiles01000.s Normal file
View File

@ -0,0 +1,141 @@
; _TBSolidSpriteTile
;
; Renders solid tiles with sprites layered on top of the tile data. Because we need to combine
; data from the sprite plane, tile data and write to the code field (which are all in different banks),
; there is no way to do everything inline, so a composite tile is created on the fly and written to
; a direct page buffer. This direct page buffer is then used to render the tile.
_TBSolidSpriteTile dw _TBSolidSpriteTile_00
dw _TBSolidSpriteTile_0H
dw _TBSolidSpriteTile_V0
dw _TBSolidSpriteTile_VH
dw _TBFastSpriteTile_00
dw _TBFastSpriteTile_0H
dw _TBFastSpriteTile_V0
dw _TBFastSpriteTile_VH
_TBSolidSpriteTile_00
jsr _TBCopyTileDataToCBuff ; Copy the tile into the compositing buffer
jsr _TBApplySpriteData ; Overlay the data form the sprite plane (and copy into the code field)
jmp _TBFillPEAOpcode ; Fill in the code field opcodes
_TBSolidSpriteTile_0H
jsr _TBCopyTileDataToCBuffH
jsr _TBApplySpriteData
jmp _TBFillPEAOpcode
_TBSolidSpriteTile_V0
jsr _TBCopyTileDataToCBuffV
jsr _TBApplySpriteData
jmp _TBFillPEAOpcode
_TBSolidSpriteTile_VH
jsr _TBCopyTileDataToCBuffVH
jsr _TBApplySpriteData
jmp _TBFillPEAOpcode
; Fast variation that does not need to set the opcode
_TBFastSpriteTile_00
jsr _TBCopyTileDataToCBuff ; Copy the tile into the compositing buffer
jmp _TBApplySpriteData ; Overlay the data form the sprite plane (and copy into the code field)
_TBFastSpriteTile_0H
jsr _TBCopyTileDataToCBuffH
jmp _TBApplySpriteData
_TBFastSpriteTile_V0
jsr _TBCopyTileDataToCBuffV
jmp _TBApplySpriteData
_TBFastSpriteTile_VH
jsr _TBCopyTileDataToCBuffVH
jmp _TBApplySpriteData
; Need to update the X-register before calling this
_TBApplySpriteData
ldx _SPR_X_REG ; set to the unaligned tile block address in the sprite plane
]line equ 0
lup 8
lda blttmp+{]line*4}
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
oral spritedata+{]line*SPRITE_PLANE_SPAN},x
sta: $0004+{]line*$1000},y
lda blttmp+{]line*4}+2
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
ldx _X_REG ; restore the original value
rts
; Copy tile data into the direct page compositing buffer. The main reason to do this in full passes is
; because we can avoid needing to use both the X and Y registers during the compositing process and
; reserve Y to hold the code field address.
;
; Also, we can get away with not setting the bank register, this is a wash in terms of speed, but results
; in simpler, more composable subroutines
_TBCopyTileDataToCBuff
]line equ 0
lup 8
ldal tiledata+{]line*4},x
sta blttmp+{]line*4}
ldal tiledata+{]line*4}+2,x
sta blttmp+{]line*4}+2
]line equ ]line+1
--^
rts
_TBCopyTileDataToCBuffH
]line equ 0
lup 8
ldal tiledata+{]line*4}+64,x
sta blttmp+{]line*4}
ldal tiledata+{]line*4}+64+2,x
sta blttmp+{]line*4}+2
]line equ ]line+1
--^
rts
_TBCopyTileDataToCBuffV
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4},x
sta blttmp+{]dest*4}
ldal tiledata+{]src*4}+2,x
sta blttmp+{]dest*4}+2
]src equ ]src-1
]dest equ ]dest+1
rts
_TBCopyTileDataToCBuffVH
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4}+64,x
sta blttmp+{]dest*4}
ldal tiledata+{]src*4}+64+2,x
sta blttmp+{]dest*4}+2
]src equ ]src-1
]dest equ ]dest+1
rts
; Copy just the data into the code field from the composite buffer
_TBSolidComposite
]line equ 0
lup 8
lda blttmp+{]line*4}
sta: $0004+{]line*$1000},y
lda blttmp+{]line*4}+2
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
rts

132
src/blitter/Tiles01010.s Normal file
View File

@ -0,0 +1,132 @@
; _TBMaskedSpriteTile
;
; Renders a composited tile with masking to the code field.
_TBMaskedSpriteTile dw _TBMaskedSpriteTile_00
dw _TBMaskedSpriteTile_0H
dw _TBMaskedSpriteTile_V0
dw _TBMaskedSpriteTile_VH
; dw _TBCopyData,_TBCopyDataH,_TBCopyDataV,_TBCopyDataVH
_TBSolidSpriteTile_00
jsr _TBCreateComposite
jsr _TBSolidComposite
jmp _TBFillPEAOpcode
_TBSolidSpriteTile_0H
jsr _TBCreateCompositeH
jsr _TBSolidComposite
jmp _TBFillPEAOpcode
_TBSolidSpriteTile_V0
jsr _TBCreateCompositeV
jsr _TBSolidComposite
jmp _TBFillPEAOpcode
_TBSolidSpriteTile_VH
jsr _TBCreateCompositeVH
jsr _TBSolidComposite
jmp _TBFillPEAOpcode
_TBCreateCompositeDataAndMask
phb
pea #^tiledata
plb
]line equ 0
lup 8
lda: tiledata+{]line*4},y
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
oral spritedata+{]line*SPRITE_PLANE_SPAN},x
sta blttmp+{]line*4}
lda: tiledata+{]line*4}+32,y
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
sta blttmp+{]line*4}+32
lda: tiledata+{]line*4}+2,y
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta blttmp+{]line*4}+2
lda: tiledata+{]line*4}+32+2,y
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
sta blttmp+{]line*4}+32+2
]line equ ]line+1
--^
plb
plb
rts
_TBCreateCompositeH
phb
pea #^tiledata
plb
]line equ 0
lup 8
lda: tiledata+{]line*4}+64,y
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
oral spritedata+{]line*SPRITE_PLANE_SPAN},x
sta blttmp+{]line*4}
lda: tiledata+{]line*4}+64+2,y
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta blttmp+{]line*4}+2
]line equ ]line+1
--^
plb
plb
rts
_TBCreateCompositeV
]src equ 7
]dest equ 0
lup 8
lda: tiledata+{]src*4},y
andl spritemask+{]dest*SPRITE_PLANE_SPAN},x
oral spritedata+{]dest*SPRITE_PLANE_SPAN},x
sta blttmp+{]dest*4}
lda: tiledata+{]src*4}+2,y
andl spritemask+{]dest*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]dest*SPRITE_PLANE_SPAN}+2,x
sta blttmp+{]dest*4}+2
]src equ ]src-1
]dest equ ]dest+1
--^
rts
_TBCreateCompositeVH
]src equ 7
]dest equ 0
lup 8
lda: tiledata+{]src*4}+64,y
andl spritemask+{]dest*SPRITE_PLANE_SPAN},x
oral spritedata+{]dest*SPRITE_PLANE_SPAN},x
sta blttmp+{]dest*4}
lda: tiledata+{]src*4}+64+2,y
andl spritemask+{]dest*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]dest*SPRITE_PLANE_SPAN}+2,x
sta blttmp+{]dest*4}+2
]src equ ]src-1
]dest equ ]dest+1
--^
rts
; Copy just the data into the code field from the composite buffer
_TBSolidComposite
]line equ 0
lup 8
lda blttmp+{]line*4}
sta: $0004+{]line*$1000},y
lda blttmp+{]line*4}+2
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
rts

6
src/blitter/Tiles10000.s Normal file
View File

@ -0,0 +1,6 @@
; _TBPriorityTile
;
; The priority bit allows the tile to be rendered in front of sprites. If there's no sprite
; in this tile area, then just fallback to the Tile00000.s implementation
_TBPriorityTile dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH
dw _TBCopyData,_TBCopyDataH,_TBCopyDataV,_TBCopyDataVH

6
src/blitter/Tiles10001.s Normal file
View File

@ -0,0 +1,6 @@
; _TBPriorityDynamicTile
;
; The priority bit allows the tile to be rendered in front of sprites. If there's no sprite
; in this tile area, then just fallback to the Tile00001.s implementation
_TBPriorityDynamicTile dw _TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00
dw _TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00

19
src/blitter/Tiles10010.s Normal file
View File

@ -0,0 +1,19 @@
; _TBMaskedPriorityTile
;
; The priority bit allows the tile to be rendered in front of sprites. If there's no sprite
; in this tile area, then just fallback to the Tile00000.s implementation
_TBMaskedPriorityTile dw _TBMaskedTile_00,_TBMaskedTile_0H,_TBMaskedTile_V0,_TBMaskedTile_VH
dw _TBCopyData,_TBCopyDataH,_TBCopyDataV,_TBCopyDataVH
; NOTE: Eventually, we want a way to support this use-case
;
; When the high-priority bit is set for a tile, then the BG0 tile will be rendered behind the BG1 data. In
; order to support this, the optional BG1 mask buffer needs to be enabled and *every* word in the tile
; becomes a JMP handler (similar to masked dynamic tiles)
;
; The 8 bytes of code that is generated in the JMP handler is
;
; lda #tiledata
; and [dp],y
; ora (dp),y
; nop

6
src/blitter/Tiles10011.s Normal file
View File

@ -0,0 +1,6 @@
; _TBPriorityDynamicMaskTile
;
; The priority bit allows the tile to be rendered in front of sprites. If there's no sprite
; in this tile area, then just fallback to the Tile00000.s implementation
_TBPriorityDynamicMaskTile dw _TBDynamicMaskTile_00,_TBDynamicMaskTile_00,_TBDynamicMaskTile_00,_TBDynamicMaskTile_00
dw _TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00

25
src/blitter/Tiles11000.s Normal file
View File

@ -0,0 +1,25 @@
; _TBPrioritySpriteTile
;
; When the sprite is composited with the tile data, the tile mask is used to place the tile data on top of
; any sprite data
; Need to update the X-register before calling this
_TBApplyPrioritySpriteData
ldx _SPR_X_REG ; set to the unaligned tile block address in the sprite plane
]line equ 0
lup 8
ldal spritedata+{]line*SPRITE_PLANE_SPAN},x
and blttmp+{]line*4}+32
ora blttmp+{]line*4}
sta: $0004+{]line*$1000},y
ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
and blttmp+{]line*4}+32+2
ora blttmp+{]line*4}+2
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
ldx _X_REG ; restore the original value
rts

48
src/blitter/TilesBG1.s Normal file
View File

@ -0,0 +1,48 @@
_TBSolidBG1_00
]line equ 0
lup 8
ldal tiledata+{]line*4},x
sta: $0000+{]line*$0100},y
ldal tiledata+{]line*4}+2,x
sta: $0002+{]line*$0100},y
]line equ ]line+1
--^
rts
_TBSolidBG1_0H
]line equ 0
lup 8
ldal tiledata+{]line*4}+64,x
sta: $0000+{]line*$0100},y
ldal tiledata+{]line*4}+64+2,x
sta: $0002+{]line*$0100},y
]line equ ]line+1
--^
rts
_TBSolidBG1_V0
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4},x
sta: $0000+{]dest*$0100},y
ldal tiledata+{]src*4}+2,x
sta: $0002+{]dest*$0100},y
]src equ ]src-1
]dest equ ]dest+1
--^
rts
_TBSolidBG1_VH
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4}+64,x
sta: $0000+{]dest*$0100},y
ldal tiledata+{]src*4}+64+2,x
sta: $0002+{]dest*$0100},y
]src equ ]src-1
]dest equ ]dest+1
--^
rts