Add routines for dirty tile rendering

This commit is contained in:
Lucas Scharenbroich 2022-07-04 23:55:32 -05:00
parent f0e75f25b5
commit 48fa068dfd
5 changed files with 250 additions and 385 deletions

View File

@ -19,6 +19,7 @@ SHADOW_SCREEN_PALETTES equ $019E00
SHR_SCREEN equ $E12000
SHR_SCB equ $E19D00
SHR_PALETTES equ $E19E00
SHR_LINE_WIDTH equ 160
; Direct page locations used by the engine
ScreenHeight equ 0 ; Height of the playfield in scan lines
@ -152,9 +153,10 @@ DP2_DIRTY_TILE_COUNT equ 160 ; Local copy of dirty tile count to avo
DP2_DIRTY_TILE_CALLBACK equ 162
; Some pre-defined bank values
DP2_TILEDATA_AND_TILESTORE_BANKS equ 164
DP2_TILEDATA_AND_TILESTORE_BANKS equ 164
DP2_SPRITEDATA_AND_TILESTORE_BANKS equ 166
DP2_TILEDATA_AND_SPRITEDATA_BANKS equ 168
DP2_TILEDATA_AND_SPRITEDATA_BANKS equ 168
DP2_BANK01_AND_TILESTORE_BANKS equ 170
SPRITE_VBUFF_PTR equ 224 ; 32 bytes of adjusted pointers to VBuffArray addresses
; End direct page values

View File

@ -20,7 +20,7 @@
; It's important to do _ApplyBG0YPos first because it calculates the value of StartY % 208 which is
; used in all of the other loops
_Render
jsr _DoTimers ; Run any pending timer tasks
jsr _DoTimers ; Run any pending timer tasks
stz SpriteRemovedFlag ; If we remove a sprite, then we need to flag a rebuild for the next frame
@ -225,331 +225,3 @@ _ApplyDirtyTiles
bne :loop
rts
; Only render solid tiles and sprites
_RenderDirtyTile
lda TileStore+TS_SPRITE_FLAG,y
beq NoSpritesDirty ; This is faster if there are no sprites
; TODO: handle sprite drawing
; The rest of this function handles that non-sprite blit, which is super fast since it blits directly from the
; tile data store to the graphics screen with no masking. The only extra work is selecting a blit function
; based on the tile flip flags.
;
; B is set to Bank 01
; Y is set to the top-left address of the tile in SHR screen
; A is set to the address of the tile data
NoSpritesDirty
; lda TileStore+TS_DIRTY_TILE_DISP,y
; stal :nsd+1
ldx TileStore+TS_SCREEN_ADDR,y ; Get the on-screen address of this tile
lda TileStore+TS_TILE_ADDR,y ; load the address of this tile's data (pre-calculated)
plb ; set the code field bank
:nsd jmp $0000
; Use some temporary space for the spriteIdx array (maximum of 4 entries)
stkSave equ tmp9
screenAddr equ tmp10
tileAddr equ tmp11
spriteIdx equ tmp12
; If there are two or more sprites at a tile, we can still be fast, but need to do extra work because
; the VBUFF values need to be read from the direct page. Thus, the direct page cannot be mapped onto
; the graphics screen. We use the stack instead, but have to do extra work to save and restore the
; stack value.
FourSpritesDirty
ThreeSpritesDirty
TwoSpritesDirty
sta tileAddr
stx screenAddr
plb
tsc
sta stkSave ; Save the stack on the direct page
sei
clc
ldy tileAddr
lda screenAddr ; Saved in direct page locations
tcs
_R0W1
lda tiledata+{0*TILE_DATA_SPAN},y
ldx spriteIdx+2
andl spritemask+{0*SPRITE_PLANE_SPAN},x
oral spritedata+{0*SPRITE_PLANE_SPAN},x
ldx spriteIdx
andl spritemask+{0*SPRITE_PLANE_SPAN},x
oral spritedata+{0*SPRITE_PLANE_SPAN},x
sta $00,s
lda tiledata+{0*TILE_DATA_SPAN}+2,y
ldx spriteIdx+2
andl spritemask+{0*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{0*SPRITE_PLANE_SPAN}+2,x
ldx spriteIdx
andl spritemask+{0*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{0*SPRITE_PLANE_SPAN}+2,x
sta $02,s
lda tiledata+{1*TILE_DATA_SPAN},y
ldx spriteIdx+2
andl spritemask+{1*SPRITE_PLANE_SPAN},x
oral spritedata+{1*SPRITE_PLANE_SPAN},x
ldx spriteIdx
andl spritemask+{1*SPRITE_PLANE_SPAN},x
oral spritedata+{1*SPRITE_PLANE_SPAN},x
sta $A0,s
lda tiledata+{1*TILE_DATA_SPAN}+2,y
ldx spriteIdx+2
andl spritemask+{1*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{1*SPRITE_PLANE_SPAN}+2,x
ldx spriteIdx
andl spritemask+{1*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{1*SPRITE_PLANE_SPAN}+2,x
sta $A2,s
tsc
adc #320
tcs
lda tiledata+{2*TILE_DATA_SPAN},y
ldx spriteIdx+2
andl spritemask+{2*SPRITE_PLANE_SPAN},x
oral spritedata+{2*SPRITE_PLANE_SPAN},x
ldx spriteIdx
andl spritemask+{2*SPRITE_PLANE_SPAN},x
oral spritedata+{2*SPRITE_PLANE_SPAN},x
sta $00,s
lda tiledata+{2*TILE_DATA_SPAN}+2,y
ldx spriteIdx+2
andl spritemask+{2*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{2*SPRITE_PLANE_SPAN}+2,x
ldx spriteIdx
andl spritemask+{2*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{2*SPRITE_PLANE_SPAN}+2,x
sta $02,s
lda tiledata+{3*TILE_DATA_SPAN},y
ldx spriteIdx+2
andl spritemask+{3*SPRITE_PLANE_SPAN},x
oral spritedata+{3*SPRITE_PLANE_SPAN},x
ldx spriteIdx
andl spritemask+{3*SPRITE_PLANE_SPAN},x
oral spritedata+{3*SPRITE_PLANE_SPAN},x
sta $A0,s
lda tiledata+{3*TILE_DATA_SPAN}+2,y
ldx spriteIdx+2
andl spritemask+{3*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{3*SPRITE_PLANE_SPAN}+2,x
ldx spriteIdx
andl spritemask+{3*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{3*SPRITE_PLANE_SPAN}+2,x
sta $A2,s
tsc
adc #320
tcs
lda tiledata+{4*TILE_DATA_SPAN},y
ldx spriteIdx+2
andl spritemask+{4*SPRITE_PLANE_SPAN},x
oral spritedata+{4*SPRITE_PLANE_SPAN},x
ldx spriteIdx
andl spritemask+{4*SPRITE_PLANE_SPAN},x
oral spritedata+{4*SPRITE_PLANE_SPAN},x
sta $00,s
lda tiledata+{4*TILE_DATA_SPAN}+2,y
ldx spriteIdx+2
andl spritemask+{4*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{4*SPRITE_PLANE_SPAN}+2,x
ldx spriteIdx
andl spritemask+{4*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{4*SPRITE_PLANE_SPAN}+2,x
sta $02,s
lda tiledata+{5*TILE_DATA_SPAN},y
ldx spriteIdx+2
andl spritemask+{5*SPRITE_PLANE_SPAN},x
oral spritedata+{5*SPRITE_PLANE_SPAN},x
ldx spriteIdx
andl spritemask+{5*SPRITE_PLANE_SPAN},x
oral spritedata+{5*SPRITE_PLANE_SPAN},x
sta $A0,s
lda tiledata+{5*TILE_DATA_SPAN}+2,y
ldx spriteIdx+2
andl spritemask+{5*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{5*SPRITE_PLANE_SPAN}+2,x
ldx spriteIdx
andl spritemask+{5*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{5*SPRITE_PLANE_SPAN}+2,x
sta $A2,s
tsc
adc #320
tcs
lda tiledata+{6*TILE_DATA_SPAN},y
ldx spriteIdx+2
andl spritemask+{6*SPRITE_PLANE_SPAN},x
oral spritedata+{6*SPRITE_PLANE_SPAN},x
ldx spriteIdx
andl spritemask+{6*SPRITE_PLANE_SPAN},x
oral spritedata+{6*SPRITE_PLANE_SPAN},x
sta $00,s
lda tiledata+{6*TILE_DATA_SPAN}+2,y
ldx spriteIdx+2
andl spritemask+{6*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{6*SPRITE_PLANE_SPAN}+2,x
ldx spriteIdx
andl spritemask+{6*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{6*SPRITE_PLANE_SPAN}+2,x
sta $02,s
lda tiledata+{7*TILE_DATA_SPAN},y
ldx spriteIdx+2
andl spritemask+{7*SPRITE_PLANE_SPAN},x
oral spritedata+{7*SPRITE_PLANE_SPAN},x
ldx spriteIdx
andl spritemask+{7*SPRITE_PLANE_SPAN},x
oral spritedata+{7*SPRITE_PLANE_SPAN},x
sta $A0,s
lda tiledata+{7*TILE_DATA_SPAN}+2,y
ldx spriteIdx+2
andl spritemask+{7*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{7*SPRITE_PLANE_SPAN}+2,x
ldx spriteIdx
andl spritemask+{7*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{7*SPRITE_PLANE_SPAN}+2,x
sta $A2,s
_R0W0
lda stkSave
tcs
cli
rts
; There is only one sprite at this tile, so do a fast blit that directly combines a tile with a single
; sprite and renders directly to the screen
;
; NOTE: Expect X-register to already have been set to the correct VBUFF address
OneSpriteDirty
ldy tileAddr ; load the address of this tile's data
lda screenAddr ; Get the on-screen address of this tile
plb
phd
sei
clc
tcd
_R0W1
lda tiledata+{0*TILE_DATA_SPAN},y
andl spritemask+{0*SPRITE_PLANE_SPAN},x
oral spritedata+{0*SPRITE_PLANE_SPAN},x
sta $00
lda tiledata+{0*TILE_DATA_SPAN}+2,y
andl spritemask+{0*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{0*SPRITE_PLANE_SPAN}+2,x
sta $02
lda tiledata+{1*TILE_DATA_SPAN},y
andl spritemask+{1*SPRITE_PLANE_SPAN},x
oral spritedata+{1*SPRITE_PLANE_SPAN},x
sta $A0
lda tiledata+{1*TILE_DATA_SPAN}+2,y
andl spritemask+{1*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{1*SPRITE_PLANE_SPAN}+2,x
sta $A2
tdc
adc #320
tcd
lda tiledata+{2*TILE_DATA_SPAN},y
andl spritemask+{2*SPRITE_PLANE_SPAN},x
oral spritedata+{2*SPRITE_PLANE_SPAN},x
sta $00
lda tiledata+{2*TILE_DATA_SPAN}+2,y
andl spritemask+{2*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{2*SPRITE_PLANE_SPAN}+2,x
sta $02
lda tiledata+{3*TILE_DATA_SPAN},y
andl spritemask+{3*SPRITE_PLANE_SPAN},x
oral spritedata+{3*SPRITE_PLANE_SPAN},x
sta $A0
lda tiledata+{3*TILE_DATA_SPAN}+2,y
andl spritemask+{3*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{3*SPRITE_PLANE_SPAN}+2,x
sta $A2
tdc
adc #320
tcd
lda tiledata+{4*TILE_DATA_SPAN},y
andl spritemask+{4*SPRITE_PLANE_SPAN},x
oral spritedata+{4*SPRITE_PLANE_SPAN},x
sta $00
lda tiledata+{4*TILE_DATA_SPAN}+2,y
andl spritemask+{4*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{4*SPRITE_PLANE_SPAN}+2,x
sta $02
lda tiledata+{5*TILE_DATA_SPAN},y
andl spritemask+{5*SPRITE_PLANE_SPAN},x
oral spritedata+{5*SPRITE_PLANE_SPAN},x
sta $A0
lda tiledata+{5*TILE_DATA_SPAN}+2,y
andl spritemask+{5*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{5*SPRITE_PLANE_SPAN}+2,x
sta $A2
tdc
adc #320
tcd
lda tiledata+{6*TILE_DATA_SPAN},y
andl spritemask+{6*SPRITE_PLANE_SPAN},x
oral spritedata+{6*SPRITE_PLANE_SPAN},x
sta $00
lda tiledata+{6*TILE_DATA_SPAN}+2,y
andl spritemask+{6*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{6*SPRITE_PLANE_SPAN}+2,x
sta $02
lda tiledata+{7*TILE_DATA_SPAN},y
andl spritemask+{7*SPRITE_PLANE_SPAN},x
oral spritedata+{7*SPRITE_PLANE_SPAN},x
sta $A0
lda tiledata+{7*TILE_DATA_SPAN}+2,y
andl spritemask+{7*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{7*SPRITE_PLANE_SPAN}+2,x
sta $A2
_R0W0
cli
pld
rts

View File

@ -605,6 +605,9 @@ _CacheSpriteBanks
xba
ldx #$100
sta DP2_TILEDATA_AND_TILESTORE_BANKS,x ; put a reversed copy in the second direct page
and #$FF00
ora #$0001
sta DP2_BANK01_AND_TILESTORE_BANKS,x ; put a value with bank 01 and the tile store
lda #>spritedata
and #$FF00

226
src/render/Dirty.s Normal file
View File

@ -0,0 +1,226 @@
; Special routines for the dirty tile renderer that draws directly to the graphics screen
; A = tile address
; Y = screen address
DirtyTileZero
lda TileStore+TS_SCREEN_ADDR,x ; Get the on-screen address of this tile
tax
pei DP2_BANK01_AND_TILESTORE_BANKS
plb
]line equ 0
lup 8
stz: {]line*SHR_LINE_WIDTH}+0,x
stz: {]line*SHR_LINE_WIDTH}+2,x
]line equ ]line+1
plb
rts
DirtyTileA
ldy TileStore+TS_SCREEN_ADDR,x ; Get the on-screen address of this tile
lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated)
tax
pei DP2_BANK01_AND_TILESTORE_BANKS
plb
]line equ 0
lup 8
ldal tiledata+{]line*4}+0,x
sta: {]line*SHR_LINE_WIDTH}+0,y
ldal tiledata+{]line*4}+2,x
sta: {]line*SHR_LINE_WIDTH}+2,y
]line equ ]line+1
plb
rts
DirtyTileV
ldy TileStore+TS_SCREEN_ADDR,x ; Get the on-screen address of this tile
lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated)
tax
pei DP2_BANK01_AND_TILESTORE_BANKS
plb
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4}+0,x
sta: {]line*SHR_LINE_WIDTH}+0,y
ldal tiledata+{]src*4}+2,x
sta: {]line*SHR_LINE_WIDTH}+2,y
]src equ ]src-1
]dest equ ]dest+1
--^
plb
rts
; DirtySpriteLine srcLine,destLine,dpAddr,offset
DirtySpriteLine mac
lda tiledata+{]1*TILE_DATA_SPAN}+]4,y
andl spritemask+{]2*SPRITE_PLANE_SPAN}+]4,x
oral spritedata+{]2*SPRITE_PLANE_SPAN}+]4,x
sta ]3+]4
<<<
; Special routine for a single sprite
OneSpriteDirtyA
ldy TileStore+TS_TILE_ADDR,x
lda TileStore+TS_SCREEN_ADDR,x
ldx sprite_ptr0
phd
pei DP2_TILEDATA_AND_TILESTORE_BANKS
plb
sei
clc
tcd
_R0W1
DirtySpriteLine 0,0,$00,0
DirtySpriteLine 0,0,$00,2
DirtySpriteLine 1,1,$A0,0
DirtySpriteLine 1,1,$A0,2
tdc
adc #320
tcd
DirtySpriteLine 2,2,$00,0
DirtySpriteLine 2,2,$00,2
DirtySpriteLine 3,3,$A0,0
DirtySpriteLine 3,3,$A0,2
tdc
adc #320
tcd
DirtySpriteLine 4,4,$00,0
DirtySpriteLine 4,4,$00,2
DirtySpriteLine 5,5,$A0,0
DirtySpriteLine 5,5,$A0,2
tdc
adc #320
tcd
DirtySpriteLine 6,6,$00,0
DirtySpriteLine 6,6,$00,2
DirtySpriteLine 7,7,$A0,0
DirtySpriteLine 7,7,$A0,2
_R0W0
cli
plb
pld
rts
OneSpriteDirtyV
ldy TileStore+TS_TILE_ADDR,x
lda TileStore+TS_SCREEN_ADDR,x
ldx sprite_ptr0
phd
pei DP2_TILEDATA_AND_TILESTORE_BANKS
plb
sei
clc
tcd
_R0W1
DirtySpriteLine 7,0,$00,0
DirtySpriteLine 7,0,$00,2
DirtySpriteLine 6,1,$A0,0
DirtySpriteLine 6,1,$A0,2
tdc
adc #320
tcd
DirtySpriteLine 5,2,$00,0
DirtySpriteLine 5,2,$00,2
DirtySpriteLine 4,3,$A0,0
DirtySpriteLine 4,3,$A0,2
tdc
adc #320
tcd
DirtySpriteLine 3,4,$00,0
DirtySpriteLine 3,4,$00,2
DirtySpriteLine 2,5,$A0,0
DirtySpriteLine 2,5,$A0,2
tdc
adc #320
tcd
DirtySpriteLine 1,6,$00,0
DirtySpriteLine 1,6,$00,2
DirtySpriteLine 0,7,$A0,0
DirtySpriteLine 0,7,$A0,2
_R0W0
cli
plb
pld
rts
; Generic routine for multiple sprites -- expect sprites to be in tmp_sprite_data and tmp_sprite_mask
SpriteDirtyA
ldy TileStore+TS_SCREEN_ADDR,x
lda TileStore+TS_TILE_ADDR,x
tax
pei DP2_TILEDATA_AND_TILESTORE_BANKS
plb
]line equ 0
lup 8
ldal tiledata+{]line*TILE_DATA_SPAN}+0,x
andl tmp_sprite_mask+{]line*4}+0
oral tmp_sprite_data+{]line*4}+0
sta: {]line*SHR_LINE_WIDTH}+0,y
ldal tiledata+{]line*TILE_DATA_SPAN}+2,x
andl tmp_sprite_mask+{]line*4}+2
oral tmp_sprite_data+{]line*4}+2
sta: {]line*SHR_LINE_WIDTH}+2,y
]line equ ]line+1
--^
plb
rts
SpriteDirtyV
ldy TileStore+TS_SCREEN_ADDR,x
lda TileStore+TS_TILE_ADDR,x
tax
pei DP2_TILEDATA_AND_TILESTORE_BANKS
plb
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*TILE_DATA_SPAN}+0,x
andl tmp_sprite_mask+{]dest*4}+0
oral tmp_sprite_data+{]dest*4}+0
sta: {]dest*SHR_LINE_WIDTH}+0,y
ldal tiledata+{]src*TILE_DATA_SPAN}+2,x
andl tmp_sprite_mask+{]dest*4}+2
oral tmp_sprite_data+{]dest*4}+2
sta: {]dest*SHR_LINE_WIDTH}+2,y
]src equ ]src-1
]dest equ ]dest+1
--^
plb
rts

View File

@ -1,8 +1,8 @@
; If there are no sprites, then we copy the tile data into the code field as fast as possible.
; If there are sprites, then additional work is required
_RenderTile
lda TileStore+TS_SPRITE_FLAG,x ; any sprites on this line?
bne :sprites
lda TileStore+TS_SPRITE_FLAG,x ; any sprites on this tile?
bne _HasSprites
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 TileStore bank in high byte.
@ -11,11 +11,24 @@ _RenderTile
plb ; set the code field bank
jmp (K_TS_BASE_TILE_DISP,x) ; go to the tile copy routine
; This is the specialized rederer for the dirty tile rendering mode. The difference is that
; it is assumed that the screen is static and the tiles are aligned with the graphics screen.
; The engine must be in "Fast" tile mode for dirty tile rendering to work. It is possible
; to switch the engine into this mode by rendering a full screen of solid tiles and then
; doing a dirty tile rendering.
;
; The main result is that this renderer skips copying tile data into the play field and
; just draws to the screen directly.
_RenderDirtyTile
lda TileStore+TS_SPRITE_FLAG,x ; any sprites on this tile?
bne _HasSprites
jmp (K_TS_BASE_TILE_DISP,x) ; This is just to select between H/V flips
; Execute the sprite tree. If there is only one sprite, control will immediately be passed to
; the routine at K_TS_ONE_SPRITE. Otherwise, the control passed to the routines with a different
; number of sprites. These routines need to copy the flattened sprite data and mask into the
; direct page workspace to be used by the K_TS_SPRITE_TILE_DISP routine
:sprites txy
_HasSprites txy
SpriteBitsToVBuffAddrs $0000;TwoSprites;ThreeSprites;FourSprites
; Dispatch vectors for the two, three and four sprite functions. These just
@ -33,54 +46,3 @@ ThreeSprites tyx
FourSprites tyx
jmp CopyFourSpritesDataAndMaskToDP
; Now, implement the generic Two, Three and Four sprite routines for both Over and Under rendering. These
; are fairly involved, so we try to only have a single implementation of them for now without excessve
; specialization.
FourSpriteLine mac
; and [sprite_ptr3],y
db $37,sprite_ptr3
ora (sprite_ptr3),y
; and [sprite_ptr2],y
db $37,sprite_ptr2
ora (sprite_ptr2),y
; and [sprite_ptr1],y
db $37,sprite_ptr1
ora (sprite_ptr1),y
; and [sprite_ptr0],y
db $37,sprite_ptr0
ora (sprite_ptr0),y
<<<
FourSpritesFast
tyx ; save for after compositing the sprites
ldy TileStore+TS_TILE_ADDR,x
pei DP2_TILEDATA_AND_TILESTORE_BANKS
plb
jsr (K_TS_COPY_TILE_DATA,x)
plb
pei DP2_SPRITEDATA_AND_TILESTORE_BANKS
plb ; set the sprite data bank
]line equ 0
lup 8
ldy #{]line*SPRITE_PLANE_SPAN}
lda tmp_tile_data+{]line*4}
FourSpriteLine
sta tmp_tile_data+{]line*4}
ldy #{]line*SPRITE_PLANE_SPAN}+2
lda tmp_tile_data+{]line*4}+2
FourSpriteLine
sta tmp_tile_data+{]line*4}+2
]line equ ]line+1
--^
plb
jmp (K_TS_APPLY_TILE_DATA,x)