Merge branch 'dirty-tiles'

This commit is contained in:
Lucas Scharenbroich 2022-07-07 17:44:21 -05:00
commit 530cdbd07f
21 changed files with 985 additions and 803 deletions

View File

@ -3,15 +3,14 @@
REL
DSK MAINSEG
use Load.Macs.s
use Locator.Macs.s
use Misc.Macs.s
use EDS.GSOS.MACS.s
use Tool222.Macs.s
use Util.Macs.s
use CORE.MACS.s
use ../../src/GTE.s
use ../../src/Defs.s
use Locator.Macs
use Load.Macs
use Mem.Macs
use Misc.Macs
use Tool222.Macs.s
use Util.Macs
use EDS.GSOS.Macs
use GTE.Macs
mx %00
@ -25,20 +24,38 @@ RIGHT_ARROW equ $15
UP_ARROW equ $0B
DOWN_ARROW equ $0A
StartX equ 4
StartY equ 6
TSet EXT
; Typical init
phk
plb
jsl EngineStartUp
stz StartX
stz StartY
lda #^MyPalette ; Fill Palette #0 with our colors
ldx #MyPalette
ldy #0
jsl SetPalette
ldx #256 ; 32 x 22 playfield (704 tiles, $580 tiles)
ldy #176
jsl SetScreenMode
sta MyUserId ; GS/OS passes the memory manager user ID for the application into the program
_MTStartUp ; GTE requires the miscellaneous toolset to be running
jsr GTEStartUp ; Load and install the GTE User Tool
; Load a tileset
pea #^TSet
pea #TSet
_GTELoadTileSet
pea $0000
pea #^MyPalette
pea #MyPalette
_GTESetPalette
pea #256
pea #176
_GTESetScreenMode
; Set up our level data
jsr BG0SetUp
@ -51,90 +68,117 @@ DOWN_ARROW equ $0A
stz MapScreenY
; Add a sprite to the engine and save its sprite
SPRITE_ID equ {SPRITE_16X16+1}
OKTOROK equ {SPRITE_16X16+79}
HERO_DOWN_ID equ {SPRITE_16X16+1}
HERO_DOWN_VBUFF equ VBUFF_SPRITE_START+0*VBUFF_SPRITE_STEP
HERO_SIDE_ID equ {SPRITE_16X16+5}
HERO_SIDE_VBUFF equ VBUFF_SPRITE_START+1*VBUFF_SPRITE_STEP
HERO_UP_ID equ {SPRITE_16X16+9}
HERO_UP_VBUFF equ VBUFF_SPRITE_START+2*VBUFF_SPRITE_STEP
lda PlayerX
xba
ora PlayerY
tay ; (x, y) position
ldx #0
lda #SPRITE_ID ; 16x16 sprite
jsl AddSprite
sta PlayerID
HERO_SLOT equ 0
OKTOROK_ID equ {SPRITE_16X16+79}
OKTOROK_VBUFF equ VBUFF_SPRITE_START+3*VBUFF_SPRITE_STEP
OKTOROK_SLOT_1 equ 1
OKTOROK_SLOT_2 equ 2
OKTOROK_SLOT_3 equ 3
OKTOROK_SLOT_4 equ 4
; Create the sprite stamps for this scene
pea HERO_DOWN_ID ; sprint id
pea HERO_DOWN_VBUFF ; vbuff address
_GTECreateSpriteStamp
pea HERO_SIDE_ID ; sprint id
pea HERO_SIDE_VBUFF ; vbuff address
_GTECreateSpriteStamp
pea HERO_UP_ID ; sprint id
pea HERO_UP_VBUFF ; vbuff address
_GTECreateSpriteStamp
pea OKTOROK_ID ; sprint id
pea OKTOROK_VBUFF ; vbuff address
_GTECreateSpriteStamp
pea HERO_DOWN_ID
lda PlayerX
pha
lda PlayerY
pha
pea HERO_SLOT
_GTEAddSprite
pea HERO_SLOT
pea $0000 ; with these flags (h/v flip)
pea HERO_DOWN_VBUFF ; and use this stamp
_GTEUpdateSprite
; Add 4 octoroks
lda #OKTOROK
ldx #1
ldy #{32*256}+48
jsl AddSprite
pea OKTOROK_ID
lda OktorokX
pha
lda OktorokY
pha
pea OKTOROK_SLOT_1
_GTEAddSprite
lda #OKTOROK
ldx #2
ldy #{32*256}+96
jsl AddSprite
lda #OKTOROK
ldx #3
ldy #{96*256}+56
jsl AddSprite
lda #OKTOROK
ldx #4
ldy #{96*256}+72
jsl AddSprite
pea OKTOROK_SLOT_1
pea $0000 ; with these flags (h/v flip)
pea OKTOROK_VBUFF ; and use this stamp
_GTEUpdateSprite
; Draw the initial screen
lda #DIRTY_BIT_BG0_REFRESH ; Redraw all of the tiles on the next Render
tsb DirtyBits
jsl Render
_GTERender
; Set up a very specific test. First, we draw a sprite into the sprite plane, and then
; leave it alone. We are just testing the ability to merge sprite plane data into
; the play field tiles.
EvtLoop
jsl ReadControl
pha
_GTEReadControl
pla
; Check the buttons first
pha
pha
bit #$0100
beq :no_sword
bit #$0100
beq :no_sword
:no_sword
; Enable/disable v-sync
lda 1,s
bit #$0400
beq :no_key_down
and #$007F
cmp #'v'
bne :not_v
lda #$0001
eor vsync
sta vsync
lda 1,s
bit #PAD_KEY_DOWN
beq :no_key_down
and #$007F
cmp #'v'
bne :not_v
lda #$0001
eor vsync
sta vsync
:not_v
:no_key_down
pla
and #$007F ; Ignore the buttons for now
pla
and #$007F ; Ignore the buttons for now
cmp #'q'
bne :not_q
brl Exit
cmp #'q'
bne :not_q
brl Exit
:not_q
cmp #'d'
bne :not_d
inc PlayerX
lda PlayerX
cmp #128-8
bcc *+5
jsr TransitionRight
cmp #'d'
bne :not_d
inc PlayerX
lda PlayerX
cmp #128-8
bcc *+5
jsr TransitionRight
lda PlayerID
ldx #SPRITE_16X16+5
jsl UpdateSprite
pea HERO_SLOT
pea $0000 ; no flags
pea HERO_SIDE_VBUFF ; and use this stamp
_GTEUpdateSprite
bra :do_render
:not_d
@ -145,9 +189,10 @@ EvtLoop
bpl *+5
jsr TransitionLeft
lda PlayerID
ldx #SPRITE_16X16+SPRITE_HFLIP+5
jsl UpdateSprite
pea HERO_SLOT
pea SPRITE_HFLIP
pea HERO_SIDE_VBUFF
_GTEUpdateSprite
bra :do_render
:not_a
@ -155,79 +200,84 @@ EvtLoop
cmp #'s'
bne :not_s
inc PlayerY
lda PlayerID
ldx #SPRITE_16X16+1
jsl UpdateSprite
pea HERO_SLOT
pea $0000
pea HERO_DOWN_VBUFF
_GTEUpdateSprite
bra :do_render
:not_s
cmp #'w'
bne :not_w
dec PlayerY
lda PlayerID
ldx #SPRITE_16X16+9
jsl UpdateSprite
pea HERO_SLOT
pea $0000
pea HERO_UP_VBUFF
_GTEUpdateSprite
bra :do_render
:not_w
:do_render
lda PlayerID
ldx PlayerX
ldy PlayerY
jsl MoveSprite ; Move the sprite to the current position
pea HERO_SLOT
lda PlayerX
pha
lda PlayerY
pha
_GTEMoveSprite
; Based on the frame count, move an oktorok
jsl GetVBLTicks
pha
and #$0003
asl
tax
pla
and #$007C
lsr
tay
; jsr _GetVBLTicks
; pha
; and #$0003
; asl
; tax
lda OktorokX,x
clc
adc OktorokDelta,y
; pla
; and #$007C
; lsr
; tay
phx
; lda OktorokX,x
; clc
; adc OktorokDelta,y
ldy OktorokY,x
tax
pla
inc
inc
jsl MoveSprite
; phx
; ldy OktorokY,x
; tax
; pla
; inc
; inc
; jsl MoveSprite
; Let's see what it looks like!
lda vsync
beq :no_vsync
:vsyncloop jsl GetVerticalCounter ; 8-bit value
cmp ScreenY0
:vsyncloop jsr _GetVBL ; 8-bit value
cmp #12
bcc :vsyncloop
sec
sbc ScreenY0
cmp #4
bcs :vsyncloop ; Wait until we're within the top 8 scanlines
cmp #16
bcs :vsyncloop ; Wait until we're within the top 4 scanlines
lda #1
jsl SetBorderColor
jsr _SetBorderColor
:no_vsync
jsl RenderDirty
_GTERenderDirty
lda vsync
beq :no_vsync2
lda #0
jsl SetBorderColor
jsr _SetBorderColor
:no_vsync2
brl EvtLoop
; Exit code
Exit
jsl EngineShutDown
_GTEShutDown
_QuitGS qtRec
@ -249,7 +299,10 @@ TransitionRight
bcs :out
clc
adc #4
jsl SetBG0XPos
sta StartX
pha
pei StartY
_GTESetBG0Origin
lda PlayerX
sec
@ -257,18 +310,18 @@ TransitionRight
bmi :nosprite
sta PlayerX
lda PlayerID
ldx PlayerX
ldy PlayerY
jsl MoveSprite
pea HERO_SLOT
lda PlayerX
pha
lda PlayerY
pha
_GTEMoveSprite
:nosprite
jsl Render ; Do full renders since the playfield is scrolling
_GTERender ; Do full renders since the playfield is scrolling
bra :loop
:out
lda #0 ; Move the player back to the left edge
sta PlayerX
inc MapScreenX ; Move the index to the next screen
:done
rts
@ -289,7 +342,10 @@ TransitionLeft
beq :out
sec
sbc #4
jsl SetBG0XPos
sta StartX
pha
pei StartY
_GTESetBG0Origin
lda PlayerX
clc
@ -298,22 +354,24 @@ TransitionLeft
bcs :nosprite
sta PlayerX
lda PlayerID
ldx PlayerX
ldy PlayerY
jsl MoveSprite
pea HERO_SLOT
lda PlayerX
pha
lda PlayerY
pha
_GTEMoveSprite
:nosprite
jsl Render
_GTERender
bra :loop
:out
; lda #128-8 ; Move the player back to the right edge
; sta PlayerX
dec MapScreenX ; Move the index to the next screen
:done
rts
rts
ToolPath str '1/Tool160'
MyUserId ds 2
; Color palette
;MyPalette dw $068F,$0EDA,$0000,$0000,$0BF1,$00A0,$0EEE,$0456,$0FA4,$0F59,$0E30,$01CE,$02E3,$0870,$0F93,$0FD7
MyPalette dw $0FDA,$08C1,$0C41,$0F93,$0777,$0FDA,$00A0,$0000,$0D20,$0FFF,$023E,$01CE,$02E3,$0870,$0F93,$0FD7
MapScreenX ds 2
@ -337,6 +395,84 @@ qtRec adrl $0000
vsync dw $8000
PUT gen/App.TileMapBG0.s
_GetVBLTicks
PushLong #0
_GetTick
pla
plx
rts
ANGLEBNK ENT
; Load the GTE User Tool and install it
GTEStartUp
pea $0000
_LoaderStatus
pla
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000 ; result space
lda MyUserId
pha
pea #^ToolPath
pea #ToolPath
pea $0001 ; do not load into special memory
_InitialLoad
bcc :ok1
brk $01
:ok1
ply
pla ; Address of the loaded tool
plx
ply
ply
pea $8000 ; User toolset
pea $00A0 ; Set the tool set number
phx
pha ; Address of function pointer table
_SetTSPtr
bcc :ok2
brk $02
:ok2
clc ; Give GTE a page of direct page memory
tdc
adc #$0100
pha
pea #0 ; Fast Mode
lda MyUserId ; Pass the userId for memory allocation
pha
_GTEStartUp
bcc :ok3
brk $03
:ok3
rts
BORDER_REG equ $E0C034 ; 0-3 = border, 4-7 Text color
VBL_VERT_REG equ $E0C02E
VBL_HORZ_REG equ $E0C02F
_GetVBL
sep #$20
ldal VBL_HORZ_REG
asl
ldal VBL_VERT_REG
rol ; put V5 into carry bit, if needed. See TN #39 for details.
rep #$20
and #$00FF
rts
_SetBorderColor sep #$20 ; ACC = $X_Y, REG = $W_Z
eorl BORDER_REG ; ACC = $(X^Y)_(Y^Z)
and #$0F ; ACC = $0_(Y^Z)
eorl BORDER_REG ; ACC = $W_(Y^Z^Z) = $W_Y
stal BORDER_REG
rep #$20
rts
PUT gen/App.TileMapBG0.s

View File

@ -7,37 +7,9 @@
; Segment #1 -- Main execution block
ASM App.Main.s
DS 0 ; Number of bytes of 0's to add at the end of the Segment
KND #$1100 ; Type and Attributes ($11=Static+Bank Relative,$00=Code)
ALI None ; Boundary Alignment (None)
SNA Main
; Segment #2 -- Core GTE Code
ASM ..\..\src\Core.s
SNA Core
; Segment #3 -- 64KB Tile Memory
; Segment #2 -- Tileset
ASM gen\App.TileSet.s
DS 0
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
SNA Tiles
; Segment #4 -- 64KB Sprite Plane Data
ASM SprData.s
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
SNA SPRDATA
; Segment #5 -- 64KB Sprite Mask Data
ASM SprMask.s
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
SNA SPRMASK
; Segment #6 -- 64KB Tile Store
ASM TileStore.s
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
SNA TSTORE
SNA TSET

View File

@ -14,5 +14,6 @@ REM Cadius does not overwrite files, so clear the root folder first
REM Now copy files and folders as needed
%CADIUS% ADDFILE %IMAGE% %FOLDER% .\GTEZelda
%CADIUS% ADDFILE %IMAGE% %FOLDER% ..\..\src\Tool160
REM Copy in the image assets

View File

@ -5,14 +5,19 @@
BG0SetUp
lda #64
sta TileMapWidth
lda #44
sta TileMapHeight
lda #Tile_Layer_1
sta TileMapPtr
lda #^Tile_Layer_1
sta TileMapPtr+2
pea #64
pea #44
pea #^Tile_Layer_1
pea #Tile_Layer_1
_GTESetBG0TileMapInfo
; lda #64
; sta TileMapWidth
; lda #44
; sta TileMapHeight
; lda #Tile_Layer_1
; sta TileMapPtr
; lda #^Tile_Layer_1
; sta TileMapPtr+2
rts
Tile_Layer_1

View File

@ -2,7 +2,7 @@
; Palette:
; $0F0F,$08C1,$0C41,$0F93,$0777,$0FDA,$00A0,$0000,$0D20,$0FFF,$023E
; Converting to BG0 format...
tiledata ENT
TSet ENT
; Reserved space (tile 0 is special...
ds 128

View File

@ -15,9 +15,11 @@
"scripts": {
"test": "npm run build && build-image.bat %npm_package_config_cadius% && %npm_package_config_gsport%",
"debug": "%npm_package_config_crossrunner% GTEZelda -Source GTEZelda_S02_MAINSEG_Output.txt -Debug -CompatibilityLayer",
"build": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.s",
"build:sys16": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.s",
"build:map": "node %npm_package_config_tiled2iigs% ./assets/overworld.json --no-gen-tiles --output-dir ./gen",
"build:tiles": "node %npm_package_config_png2iigs% ./assets/sprites-256x128.png --max-tiles 256 --as-tile-data --transparent-color FF00FF > ./gen/App.TileSet.s"
"build:tiles": "node %npm_package_config_png2iigs% ./assets/sprites-256x128.png --max-tiles 256 --as-tile-data --transparent-color FF00FF > ./gen/App.TileSet.s",
"build": "npm run build:tool && npm run build:sys16",
"build:tool": "%npm_package_config_merlin32% -V %npm_package_config_macros% ../../src/Master.s"
},
"repository": {
"type": "git",

View File

@ -111,6 +111,9 @@ _GTEFillTileStore MAC
_GTERefresh MAC
UserTool $2600+GTEToolNum
<<<
_GTERenderDirty MAC
UserTool $2700+GTEToolNum
<<<
; EngineMode definitions
; Script definition
@ -123,9 +126,9 @@ SET_DYN_TILE equ $0006
CALLBACK equ $0010
; ReadControl return value bits
PAD_BUTTON_B equ $01
PAD_BUTTON_A equ $02
PAD_KEY_DOWN equ $04
PAD_BUTTON_B equ $0100
PAD_BUTTON_A equ $0200
PAD_KEY_DOWN equ $0400
ENGINE_MODE_TWO_LAYER equ $0001
ENGINE_MODE_DYN_TILES equ $0002
ENGINE_MODE_BNK0_BUFF equ $0004

View File

@ -190,7 +190,7 @@ EngineReset
; stz EngineMode
stz DirtyBits
stz LastRender
stz LastRender ; Initialize as is a full render was performed
stz LastPatchOffset
stz BG1StartX
stz BG1StartXMod164
@ -271,7 +271,7 @@ _ReadControl pea $0000 ; low byte = key code, high byte
and #$80
beq :BNotDown
lda #PAD_BUTTON_B
lda #>PAD_BUTTON_B
ora 2,s
sta 2,s
@ -280,7 +280,7 @@ _ReadControl pea $0000 ; low byte = key code, high byte
and #$80
beq :ANotDown
lda #PAD_BUTTON_A
lda #>PAD_BUTTON_A
ora 2,s
sta 2,s
@ -296,7 +296,7 @@ _ReadControl pea $0000 ; low byte = key code, high byte
beq :KbdDown
sta LastKey
lda #PAD_KEY_DOWN ; set the keydown flag
lda #>PAD_KEY_DOWN ; set the keydown flag
ora 2,s
sta 2,s
bra :KbdDown

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

@ -1,73 +0,0 @@
; Collection of the EXTernal labels exported by GTE. This is the closest thing
; we have to an API definition.
EngineStartUp EXT
EngineShutDown EXT
SetScreenMode EXT
ReadControl EXT
; Low-Level Functions
SetPalette EXT
GetVBLTicks EXT
GetVerticalCounter EXT
SetBorderColor EXT
; Tilemap functions
SetBG0XPos EXT
SetBG0YPos EXT
SetBG1XPos EXT
SetBG1YPos EXT
CopyBG0Tile EXT
CopyBG1Tile EXT
; SCB/Palette binding (high bit of array point indicates whether to bind to BG0 Y position (0)
; or BG1 Y position (1).
; SetSCBArray EXT
BltSCB EXT
; Rotation
ApplyBG1XPosAngle EXT
ApplyBG1YPosAngle EXT
CopyPicToField EXT
CopyBinToField EXT
CopyPicToBG1 EXT
CopyBinToBG1 EXT
AddTimer EXT
RemoveTimer EXT
DoTimers EXT
StartScript EXT
StopScript EXT
; Sprite functions
AddSprite EXT
MoveSprite EXT ; Set an existing sprite's position
UpdateSprite EXT ; Change an existing sprite's flags
RemoveSprite EXT
; Direct access to internals
DoScriptSeq EXT
GetTileAddr EXT
PushDirtyTile EXT ; A = address from GetTileStoreOffset, marks as dirty (will not mark the same tile more than once)
PopDirtyTile EXT ; No args, returns Y with tile store offset of the dirty tile
ApplyTiles EXT ; Drain the dirty tile queue and call RenderTile on each
GetTileStoreOffset EXT ; X = column, Y = row
TileStore EXT ; Tile store internal data structure
RenderDirty EXT ; Render only dirty tiles + sprites directly to the SHR screen
; GetSpriteVBuffAddr EXT ; X = x-coordinate (0 - 159), Y = y-coordinate (0 - 199). Return in Acc.
; Allocate a full 64K bank
AllocBank EXT
; Data references
;
; Super Hires line address lookup table for convenience
ScreenAddr EXT
OneSecondCounter EXT
BlitBuff EXT

View File

@ -174,9 +174,6 @@ _ShadowOff
rep #$20
rts
GetVerticalCounter ENT
jsr _GetVBL
rtl
_GetVBL
sep #$20
ldal VBL_HORZ_REG

View File

@ -20,7 +20,13 @@
; 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
lda LastRender ; Check to see what kind of rendering was done on the last frame. If
beq :no_change ; it was not this renderer,
jsr _ResetToNormalTileProcs
jsr _Refresh
:no_change
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
@ -37,7 +43,7 @@ _Render
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 _ApplyTilesFast ; This function actually draws the new tiles into the code field
jsr _ApplyTiles ; 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
@ -129,10 +135,11 @@ _DoOverlay
:disp jsl $000000
rts
; The _ApplyTilesFast is the same as _ApplyTiles, but we use the _RenderTileFast subroutine
_ApplyTilesFast
; Run through all of the tiles on the DirtyTile list and render them
_ApplyTiles
ldx DirtyTileCount
phd ; sve the current direct page
tdc
clc
adc #$100 ; move to the next page
@ -141,48 +148,10 @@ _ApplyTilesFast
stx DP2_DIRTY_TILE_COUNT ; Cache the dirty tile count
jsr _PopDirtyTilesFast
tdc ; Move back to the original direct page
sec
sbc #$100
tcd
pld ; Move back to the original direct page
stz DirtyTileCount ; Reset the dirty tile count
rts
; The _ApplyTiles function is responsible for rendering all of the dirty tiles into the code
; field. In this function we switch to the second direct page which holds the temporary
; working buffers for tile rendering.
;
_ApplyTiles
tdc
clc
adc #$100 ; move to the next page
tcd
bra :begin
:loop
; Retrieve the offset of the next dirty Tile Store items in the X-register
jsr _PopDirtyTile2
; Call the generic dispatch with the Tile Store record pointer at by the X-register.
phb
; jsr _RenderTile2
plb
; Loop again until the list of dirty tiles is empty
:begin ldy DirtyTileCount
bne :loop
tdc ; Move back to the original direct page
sec
sbc #$100
tcd
rts
; This is a specialized render function that only updates the dirty tiles *and* draws them
; directly onto the SHR graphics buffer. The playfield is not used at all. In some way, this
; ignores almost all of the capabilities of GTE, but it does provide a convenient way to use
@ -192,20 +161,26 @@ _ApplyTiles
; In this renderer, we assume that there is no scrolling, so no need to update any information about
; the BG0/BG1 positions
_RenderDirty
lda LastRender ; If the full renderer was last called, we assume that
bne :norecalc ; the scroll positions have likely changed, so recalculate
jsr _RecalcTileScreenAddrs ; them to make sure sprites draw at the correct screen address
lda LastRender ; If the full renderer was last called, we assume that
bne :norecalc ; the scroll positions have likely changed, so recalculate
jsr _RecalcTileScreenAddrs ; them to make sure sprites draw at the correct screen address
jsr _ResetToDirtyTileProcs ; Switch the tile procs to the dirty tile rendering functions
; jsr _ClearSpritesFromCodeField ; Restore the tiles to their non-sprite versions
:norecalc
; jsr _RenderSprites
; jsr _ApplyDirtyTiles
jsr _RenderSprites
jsr _ApplyDirtyTiles
lda #1
sta LastRender
rts
_ApplyDirtyTiles
phd ; save the current direct page
tdc
clc
adc #$100 ; move to the next page
tcd
bra :begin
:loop
@ -215,341 +190,14 @@ _ApplyDirtyTiles
; Call the generic dispatch with the Tile Store record pointer at by the Y-register.
phb
jsr _RenderDirtyTile
plb
; Loop again until the list of dirty tiles is empty
:begin ldy DirtyTileCount
bne :loop
pld ; Move back to the original direct page
stz DirtyTileCount ; Reset the dirty tile count
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

@ -223,32 +223,44 @@ _DoPhase1
trb SpriteMap
lda #SPRITE_STATUS_EMPTY ; Mark as empty so no error if we try to Add a sprite here again
sta _Sprites+SPRITE_STATUS,y
:hidden
jmp _ClearSpriteFromTileStore ; Clear the tile flags, add to the dirty tile list and done
; Need to calculate new VBUFF information. The could be required for UPDATED, ADDED or MOVED
; sprites, so we do it unconditionally, but we do need to mark the current sprite for erasure if
; needed
:no_clear
; If the sprite is hidden, then treat it like it's offscreen and clear it from the tile store. Because
; the hidden flag must be changed via the UpdateSprite function, if it's toggled, the SPRITE_STATUS_UPDATED
; flag will be set, too.
bit #SPRITE_STATUS_HIDDEN
bne :hidden
; If the sprite is marked as ADDED, then it does not need to have its old tile locations cleared
bit #SPRITE_STATUS_ADDED
bne :no_move
bne :added
; If the sprite was not ADDED and also not MOVED, then there is no reason to erase the old tiles
; because they will be overwritten anyway.
bit #SPRITE_STATUS_MOVED
beq :no_move
bne :moved
; Finally, see if it was updated. If not, return early
bit #SPRITE_STATUS_UPDATED
bne :updated
rts
:moved
phy
jsr _ClearSpriteFromTileStore
ply
; Anything else (MOVED, UPDATED, ADDED) will need to have the VBUFF information updated and the
; current tiles marked for update
:no_move
:added
:updated
jsr _CalcDirtySprite ; This function preserves Y
lda #SPRITE_STATUS_OCCUPIED ; Clear the dirty bits (ADDED, UPDATED, MOVED)
@ -605,6 +617,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

View File

@ -129,7 +129,7 @@ _CalcDirtySprite
adc StartYMod208
bpl :y_ok
clc
adc #208 ; Wrap the actual coordinat around
adc #208 ; Wrap the actual coordinate around
:y_ok and #$FFF8 ; mask first to ensure LSR will clear the carry
lsr
lsr
@ -218,10 +218,6 @@ tmp_out
mdsOut rts
; NOTE: The VBuffArray table is set up so that each sprite's vbuff address is stored in a
; parallel structure to the Tile Store. This allows up to use the same TileStoreLookup
; offset to index into the array of 16 sprite VBUFF addresses that are bound to a given tile
_MarkDirtySpriteTiles
lda _SpriteBits,y
sta SpriteBit

View File

@ -124,13 +124,6 @@ InitTiles
:out
; lda DirtyTileProcs ; Fill in with the first dispatch address
; stal TileStore+TS_DIRTY_TILE_DISP,x
;
; lda TileProcs ; Same for non-dirty, non-sprite base case
; stal TileStore+TS_BASE_TILE_DISP,x
; The next set of values are constants that are simply used as cached parameters to avoid needing to
; calculate any of these values during tile rendering
@ -144,7 +137,6 @@ InitTiles
lda BRowTableLow,y
sta :base
; sta TileStore+TS_BASE_ADDR,x ; May not be needed later if we can figure out the right constant...
lda :col ; Set the offset values based on the column
asl ; of this tile
@ -155,7 +147,6 @@ InitTiles
lda Col2CodeOffset+2,y
clc
adc :base
; adc TileStore+TS_BASE_ADDR,x
sta TileStore+TS_CODE_ADDR_LOW,x ; Low word of the tile address in the code field
lda JTableOffset,y
@ -175,6 +166,148 @@ InitTiles
bpl :loop
rts
; Put everything on the dirty tile list
_Refresh
ldx #TILE_STORE_SIZE-2
:loop jsr _PushDirtyTileX
dex
dex
bpl :loop
rts
; Reset all of the tile proc values in the playfield.
_ResetToDirtyTileProcs
ldx #TILE_STORE_SIZE-2
:loop
lda TileStore+TS_TILE_ID,x
jsr _SetDirtyTileProcs
dex
dex
bpl :loop
rts
_ResetToNormalTileProcs
ldx #TILE_STORE_SIZE-2
:loop
lda TileStore+TS_TILE_ID,x
jsr _SetNormalTileProcs
dex
dex
bpl :loop
rts
; A = tileID
; X = tile store index
_SetDirtyTileProcs
jsr _CalcTileProcIndex
ldy #DirtyProcs
jmp _SetTileProcs
; A = tileID
; X = tile store index
_SetNormalTileProcs
pha ; extra space
pha ; save the tile ID
jsr _CalcTileProcIndex
sta 3,s ; save for later
lda EngineMode
bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER
beq :setTileFast
bit #ENGINE_MODE_TWO_LAYER
beq :setTileDyn
pla ; restore newTileID
bit #TILE_DYN_BIT
beq :pickTwoLyrProc
ldy #TwoLyrDynProcs
brl :pickDynProc
:pickTwoLyrProc ldy #TwoLyrProcs
pla ; pull of the proc index
jmp _SetTileProcs
; Specialized check for when the engine is in "Fast" mode. If is a simple decision tree based on whether
; the tile priority bit is set, and whether this is the special tile 0 or not.
:setTileFast
pla ; Throw away tile ID copy
ldy #FastProcs
pla
jmp _SetTileProcs
; Specialized check for when the engine has enabled dynamic tiles. In this case we are no longer
; guaranteed that the opcodes in a tile are PEA instructions.
:setTileDyn
pla ; get the cached tile ID
bit #TILE_DYN_BIT
beq :pickSlowProc ; If the Dynamic bit is not set, select a tile proc that sets opcodes
ldy #DynProcs ; use this table
:pickDynProc
and #TILE_PRIORITY_BIT
beq :pickZeroDynProc ; If the Priority bit is not set, pick the first entry
pla
lda #1 ; If the Priority bit is set, pick the other one
jmp _SetTileProcs
:pickZeroDynProc pla
lda #0
jmp _SetTileProcs
:pickSlowProc ldy #SlowProcs
pla
jmp _SetTileProcs
; Helper method to calculate the index into the tile proc table given a TileID
; Calculate the base tile proc selector from the tile Id
_CalcTileProcIndex
bit #TILE_PRIORITY_BIT ; 4 if !0, 0 otherwise
beq :low_priority
bit #TILE_ID_MASK ; 2 if !0, 0 otherwise
beq :is_zero_a
bit #TILE_VFLIP_BIT ; 1 if !0, 0 otherwise
beq :no_flip_a
lda #7
rts
:no_flip_a lda #6
rts
:is_zero_a bit #TILE_VFLIP_BIT
beq :no_flip_b
lda #5
rts
:no_flip_b lda #4
rts
:low_priority bit #TILE_ID_MASK ; 2 if !0, 0 otherwise
beq :is_zero_b
bit #TILE_VFLIP_BIT ; 1 if !0, 0 otherwise
beq :no_flip_c
lda #3
rts
:no_flip_c lda #2
rts
:is_zero_b bit #TILE_VFLIP_BIT
beq :no_flip_d
lda #1
rts
:no_flip_d lda #0
rts
; Set a tile value in the tile backing store. Mark dirty if the value changes
;
; A = tile id
@ -216,79 +349,10 @@ _SetTile
; functionality. Sometimes it is simple, but in cases of the sprites overlapping Dynamic Tiles and other cases
; it can be more involved.
; Calculate the base tile proc selector from the tile Id
stz procIdx
; Calculate the base tile proc selector from the tile Id (need X-register set to tile store index)
lda #TILE_PRIORITY_BIT
bit newTileId
beq :low_priority
lda #4
sta procIdx
:low_priority
lda #TILE_ID_MASK
bit newTileId
beq :is_zero
lda #2
tsb procIdx
:is_zero
lda #TILE_VFLIP_BIT
bit newTileId
beq :no_vflip
lda #1
tsb procIdx
:no_vflip
; Now integrate with the engine mode indicator
lda EngineMode
bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER
beq :setTileFast
bit #ENGINE_MODE_TWO_LAYER
bne :not_dyn
brl :setTileDyn
:not_dyn
lda #TILE_DYN_BIT
bit newTileId
beq :pickTwoLyrProc
ldy #TwoLyrDynProcs
brl :pickDynProc
:pickTwoLyrProc ldy #TwoLyrProcs
lda procIdx
jsr _SetTileProcs
jmp _PushDirtyTileX
; Specialized check for when the engine is in "Fast" mode. If is a simple decision tree based on whether
; the tile priority bit is set, and whether this is the special tile 0 or not.
:setTileFast
ldy #FastProcs
lda procIdx
jsr _SetTileProcs
jmp _PushDirtyTileX
; Specialized check for when the engine has enabled dynamic tiles. In this case we are no longer
; guaranteed that the opcodes in a tile are PEA instructions.
:setTileDyn
lda #TILE_DYN_BIT
bit newTileId
beq :pickSlowProc ; If the Dynamic bit is not set, select a tile proc that sets opcodes
ldy #DynProcs ; use this table
:pickDynProc
lda newTileId ; Otherwise chose one of the two dynamic tuples
and #TILE_PRIORITY_BIT
beq *+5 ; If the Priority bit is not set, pick the first entry
lda #1 ; If the Priority bit is set, pick the other one
jsr _SetTileProcs
jmp _PushDirtyTileX
:pickSlowProc ldy #SlowProcs
lda procIdx
jsr _SetTileProcs
lda newTileId
jsr _SetNormalTileProcs
jmp _PushDirtyTileX
; X = Tile Store offset
@ -415,6 +479,17 @@ TwoLyrDynProcs
TwoLyrDynOver dw CopyDynamicTileTwoLyr,DynamicOverTwoLyr,OneSpriteDynamicOverTwoLyr
TwoLyrDynUnder dw CopyDynamicTileTwoLyr,DynamicUnderTwoLyr,OneSpriteDynamicUnderTwoLyr
; "Dirty" procs that are for dirty tile direct rendering. No moving background.
DirtyProcs
DirtyOverZA dw ConstTile0Dirty,SpriteOver0Dirty,OneSpriteDirtyOver0
DirtyOverZV dw ConstTile0Dirty,SpriteOver0Dirty,OneSpriteDirtyOver0
DirtyOverNA dw CopyTileADirty,SpriteOverADirty,OneSpriteDirtyOverA
DirtyOverNV dw CopyTileVDirty,SpriteOverVDirty,OneSpriteDirtyOverV
DirtyUnderZA dw ConstTile0Dirty,SpriteUnder0Dirty,SpriteUnder0Dirty
DirtyUnderZV dw ConstTile0Dirty,SpriteUnder0Dirty,SpriteUnder0Dirty
DirtyUnderNA dw CopyTileADirty,SpriteUnderADirty,OneSpriteDirtyUnderA
DirtyUnderNV dw CopyTileVDirty,SpriteUnderVDirty,OneSpriteDirtyUnderV
; SetBG0XPos
;
; Set the virtual horizontal position of the primary background layer. In addition to

View File

@ -90,6 +90,7 @@ _CallTable
adrl _TSGetTileDataAddr-1
adrl _TSFillTileStore-1
adrl _TSRefresh-1
adrl _TSRenderDirty-1
_CTEnd
_GTEAddSprite MAC
UserTool $1000+GTEToolNum
@ -282,6 +283,13 @@ _TSRender
jsr _Render
_TSExit #0;#0
; RenderDirty()
_TSRenderDirty
_TSEntry
jsr _RenderDirty
_TSExit #0;#0
; LoadTileSet(Pointer)
_TSLoadTileSet
TSPtr equ FirstParam
@ -697,13 +705,7 @@ _TSFillTileStore
; _TSRefresh()
_TSRefresh
_TSEntry
ldx #TILE_STORE_SIZE-2
:loop jsr _PushDirtyTileX
dex
dex
bpl :loop
jsr _Refresh
_TSExit #0;#0
; Insert the GTE code
@ -725,6 +727,7 @@ _TSRefresh
put render/Slow.s
put render/Dynamic.s
put render/TwoLayer.s
put render/Dirty.s
put render/Sprite1.s
put render/Sprite2.s
put tiles/DirtyTileQueue.s

View File

@ -46,10 +46,10 @@ Counter equ tmp3
lda (NextColPtr),y ; Need to recalculate each time since the wrap-around could
clc ; happen anywhere
adc (RowAddrPtr) ;
tax ; NOTE: Try to rework to use new TileStore2DLookup array
tax ; NOTE: Try to rework to use new TileStoreLookup array
lda OnScreenAddr
sta TileStore+TS_SCREEN_ADDR,x
sta TileStore+TS_SCREEN_ADDR,x
clc
adc #4 ; Go to the next tile

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

@ -0,0 +1,438 @@
; Special routines for the dirty tile renderer that draws directly to the graphics screen
; A = tile address
; Y = screen address
SpriteUnder0Dirty
ConstTile0Dirty
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
; Sprite over a zero tile
OneSpriteDirtyOver0
ldy TileStore+TS_SCREEN_ADDR,x
tax
pei DP2_BANK01_AND_TILESTORE_BANKS
plb
]line equ 0
lup 8
ldal spritedata+{]line*SPRITE_PLANE_SPAN}+0,x
sta: {]line*SHR_LINE_WIDTH}+0,y
ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta: {]line*SHR_LINE_WIDTH}+2,y
]line equ ]line+1
--^
plb
rts
; Multiple sprites (copied to direct page temp space)
SpriteOver0Dirty
ldy TileStore+TS_SCREEN_ADDR,x
pei DP2_BANK01_AND_TILESTORE_BANKS
plb
]line equ 0
lup 8
lda tmp_sprite_data+{]line*4}+0
sta: {]line*SHR_LINE_WIDTH}+0,y
lda tmp_sprite_data+{]line*4}+2
sta: {]line*SHR_LINE_WIDTH}+2,y
]line equ ]line+1
--^
plb
rts
CopyTileADirty
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
CopyTileVDirty
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
DirtySpriteOver 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
<<<
DirtySpriteUnder mac
ldal spritedata+{]2*SPRITE_PLANE_SPAN}+]4,x
and tiledata+{]1*TILE_DATA_SPAN}+32+]4,y
ora tiledata+{]1*TILE_DATA_SPAN}+]4,y
sta ]3+]4
<<<
; Special routine for a single sprite
OneSpriteDirtyOverA
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
DirtySpriteOver 0;0;$00;0
DirtySpriteOver 0;0;$00;2
DirtySpriteOver 1;1;$A0;0
DirtySpriteOver 1;1;$A0;2
tdc
adc #320
tcd
DirtySpriteOver 2;2;$00;0
DirtySpriteOver 2;2;$00;2
DirtySpriteOver 3;3;$A0;0
DirtySpriteOver 3;3;$A0;2
tdc
adc #320
tcd
DirtySpriteOver 4;4;$00;0
DirtySpriteOver 4;4;$00;2
DirtySpriteOver 5;5;$A0;0
DirtySpriteOver 5;5;$A0;2
tdc
adc #320
tcd
DirtySpriteOver 6;6;$00;0
DirtySpriteOver 6;6;$00;2
DirtySpriteOver 7;7;$A0;0
DirtySpriteOver 7;7;$A0;2
_R0W0
cli
plb
pld
rts
OneSpriteDirtyUnderA
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
DirtySpriteUnder 0;0;$00;0
DirtySpriteUnder 0;0;$00;2
DirtySpriteUnder 1;1;$A0;0
DirtySpriteUnder 1;1;$A0;2
tdc
adc #320
tcd
DirtySpriteUnder 2;2;$00;0
DirtySpriteUnder 2;2;$00;2
DirtySpriteUnder 3;3;$A0;0
DirtySpriteUnder 3;3;$A0;2
tdc
adc #320
tcd
DirtySpriteUnder 4;4;$00;0
DirtySpriteUnder 4;4;$00;2
DirtySpriteUnder 5;5;$A0;0
DirtySpriteUnder 5;5;$A0;2
tdc
adc #320
tcd
DirtySpriteUnder 6;6;$00;0
DirtySpriteUnder 6;6;$00;2
DirtySpriteOver 7;7;$A0;0
DirtySpriteUnder 7;7;$A0;2
_R0W0
cli
plb
pld
rts
OneSpriteDirtyOverV
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
DirtySpriteOver 7;0;$00;0
DirtySpriteOver 7;0;$00;2
DirtySpriteOver 6;1;$A0;0
DirtySpriteOver 6;1;$A0;2
tdc
adc #320
tcd
DirtySpriteOver 5;2;$00;0
DirtySpriteOver 5;2;$00;2
DirtySpriteOver 4;3;$A0;0
DirtySpriteOver 4;3;$A0;2
tdc
adc #320
tcd
DirtySpriteOver 3;4;$00;0
DirtySpriteOver 3;4;$00;2
DirtySpriteOver 2;5;$A0;0
DirtySpriteOver 2;5;$A0;2
tdc
adc #320
tcd
DirtySpriteOver 1;6;$00;0
DirtySpriteOver 1;6;$00;2
DirtySpriteOver 0;7;$A0;0
DirtySpriteOver 0;7;$A0;2
_R0W0
cli
plb
pld
rts
OneSpriteDirtyUnderV
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
DirtySpriteUnder 7;0;$00;0
DirtySpriteUnder 7;0;$00;2
DirtySpriteUnder 6;1;$A0;0
DirtySpriteUnder 6;1;$A0;2
tdc
adc #320
tcd
DirtySpriteUnder 5;2;$00;0
DirtySpriteUnder 5;2;$00;2
DirtySpriteUnder 4;3;$A0;0
DirtySpriteUnder 4;3;$A0;2
tdc
adc #320
tcd
DirtySpriteUnder 3;4;$00;0
DirtySpriteUnder 3;4;$00;2
DirtySpriteUnder 2;5;$A0;0
DirtySpriteUnder 2;5;$A0;2
tdc
adc #320
tcd
DirtySpriteUnder 1;6;$00;0
DirtySpriteUnder 1;6;$00;2
DirtySpriteUnder 0;7;$A0;0
DirtySpriteUnder 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
SpriteOverADirty
ldy TileStore+TS_SCREEN_ADDR,x
lda TileStore+TS_TILE_ADDR,x
tax
pei DP2_BANK01_AND_TILESTORE_BANKS
plb
]line equ 0
lup 8
ldal tiledata+{]line*TILE_DATA_SPAN}+0,x
and tmp_sprite_mask+{]line*4}+0
ora tmp_sprite_data+{]line*4}+0
sta: {]line*SHR_LINE_WIDTH}+0,y
; brk $00
ldal tiledata+{]line*TILE_DATA_SPAN}+2,x
and tmp_sprite_mask+{]line*4}+2
ora tmp_sprite_data+{]line*4}+2
sta: {]line*SHR_LINE_WIDTH}+2,y
]line equ ]line+1
--^
plb
rts
SpriteUnderADirty
ldy TileStore+TS_SCREEN_ADDR,x
lda TileStore+TS_TILE_ADDR,x
tax
pei DP2_BANK01_AND_TILESTORE_BANKS
plb
]line equ 0
lup 8
lda tmp_sprite_data+{]line*4}+0
andl tiledata+{]line*TILE_DATA_SPAN}+32,x
oral tiledata+{]line*TILE_DATA_SPAN}+0,x
sta: {]line*SHR_LINE_WIDTH}+0,y
lda tmp_sprite_data+{]line*4}+2
andl tiledata+{]line*TILE_DATA_SPAN}+32+2,x
oral tiledata+{]line*TILE_DATA_SPAN}+2,x
sta: {]line*SHR_LINE_WIDTH}+2,y
]line equ ]line+1
--^
plb
rts
SpriteOverVDirty
ldy TileStore+TS_SCREEN_ADDR,x
lda TileStore+TS_TILE_ADDR,x
tax
pei DP2_BANK01_AND_TILESTORE_BANKS
plb
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*TILE_DATA_SPAN}+0,x
and tmp_sprite_mask+{]dest*4}+0
ora tmp_sprite_data+{]dest*4}+0
sta: {]dest*SHR_LINE_WIDTH}+0,y
ldal tiledata+{]src*TILE_DATA_SPAN}+2,x
and tmp_sprite_mask+{]dest*4}+2
ora tmp_sprite_data+{]dest*4}+2
sta: {]dest*SHR_LINE_WIDTH}+2,y
]src equ ]src-1
]dest equ ]dest+1
--^
plb
rts
SpriteUnderVDirty
ldy TileStore+TS_SCREEN_ADDR,x
lda TileStore+TS_TILE_ADDR,x
tax
pei DP2_BANK01_AND_TILESTORE_BANKS
plb
]src equ 7
]dest equ 0
lup 8
lda tmp_sprite_data+{]dest*4}+0
andl tiledata+{]src*TILE_DATA_SPAN}+32,x
oral tiledata+{]src*TILE_DATA_SPAN}+0,x
sta: {]dest*SHR_LINE_WIDTH}+0,y
lda tmp_sprite_data+{]dest*4}+2
andl tiledata+{]src*TILE_DATA_SPAN}+32+2,x
oral tiledata+{]src*TILE_DATA_SPAN}+2,x
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)

View File

@ -40,6 +40,7 @@ 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_HIDDEN equ $0010 ; Sprite is in a hidden state
; These values are set by the user
SPRITE_STATUS equ {MAX_SPRITES*0}

View File

@ -70,8 +70,7 @@ _PopDirtyTile2 ; alternate entry point
sty DirtyTileCount ; remove last item from the list
ldx DirtyTiles,y ; load the offset into the Tile Store
lda #$FFFF
stal TileStore+TS_DIRTY,x ; clear the occupied backlink
stz TileStore+TS_DIRTY,x ; clear the occupied backlink
rts
; An optimized subroutine that runs through the dirty tile list and executes a callback function