From fa307e55420f662972f648d53f779f0d9cb16cf0 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Fri, 2 Jun 2023 00:38:35 -0500 Subject: [PATCH] Better support for user-defined tiles --- src/Defs.s | 9 ++++ src/Sprite.s | 4 ++ src/SpriteRender.s | 91 ++++++++++++++++++++++---------------- src/Tiles.s | 56 ++++++++++++++++++++--- src/Tool.s | 23 +++++++++- src/render/Dynamic.s | 6 +-- src/render/Fast.s | 18 ++++++++ src/render/Render.s | 1 + src/static/TileStoreDefs.s | 4 +- 9 files changed, 162 insertions(+), 50 deletions(-) diff --git a/src/Defs.s b/src/Defs.s index 906e59a..a9e63bb 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -161,6 +161,14 @@ DP2_TILEDATA_AND_TILESTORE_BANKS equ 164 DP2_SPRITEDATA_AND_TILESTORE_BANKS equ 166 DP2_TILEDATA_AND_SPRITEDATA_BANKS equ 168 DP2_BANK01_AND_TILESTORE_BANKS equ 170 +DP2_TILEDATA_AND_BANK01_BANKS equ 172 + +; Tile record passed to user-defined tile callback +USER_TILE_RECORD equ 178 +USER_TILE_ID equ 178 ; copy of the tile id in the tile store +USER_TILE_CODE_PTR equ 180 ; pointer to the code bank in which to patch +USER_TILE_ADDR equ 184 ; address in the tile data bank +USER_FREE_SPACE equ 186 ; a few bytes of scratch space SPRITE_VBUFF_PTR equ 224 ; 32 bytes of adjusted pointers to VBuffArray addresses ; End direct page values @@ -197,6 +205,7 @@ vblCallback equ $0004 ; User routine to be called by VBL int extSpriteRenderer equ $0005 rawDrawTile equ $0006 extBG0TileUpdate equ $0007 +userTileCallback equ $0008 ; CopyPicToBG1 flags COPY_PIC_NORMAL equ $0000 ; Copy into BG1 buffer in "normal mode" treating the buffer as a 164x208 pixmap with stride of 256 diff --git a/src/Sprite.s b/src/Sprite.s index 478a344..a996ba1 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -913,6 +913,10 @@ _CacheSpriteBanks ora #^tiledata sta DP2_TILEDATA_AND_SPRITEDATA_BANKS,x + lda #$0100 + ora #^tiledata + sta DP2_TILEDATA_AND_BANK01_BANKS,x + lda #>spritedata and #$FF00 ora #^TileStore diff --git a/src/SpriteRender.s b/src/SpriteRender.s index c9c72e1..a7a5faa 100644 --- a/src/SpriteRender.s +++ b/src/SpriteRender.s @@ -13,6 +13,7 @@ _CompileStamp :vbuffAddr equ tmp5 :rtnval equ tmp6 +LDA_IND_LONG_IDX equ $B7 LDA_IMM_OPCODE equ $A9 LDA_ABS_X_OPCODE equ $BD AND_IMM_OPCODE equ $29 @@ -139,7 +140,7 @@ RTL_OPCODE equ $6B ; 4 palettes for the sprite data. Converts 4 pixels at a time from 0000 0000w wxxy yzz0 -> gggg hhhh iiii jjjj ; each swizzle table is 512 bytes long, 2048 bytes for all four. They need to be prec -swizzle + ; Draw a tile directly to the graphics screen as a sprite ; ; Y = screen address @@ -147,49 +148,65 @@ swizzle ; A = $0001 = ignore mask ; = $0080 = vflip ; = $0600 = palette select -* _DrawSwizzleTileToScreen -* :palette equ 240 -* ; Tile data must be 0000 000w wxxy yzz0 -* ; Tile mask is normal pixel data -* and #$0600 -* sta :palette ; cache the palette bits +_DrawTileToScreenX + rtl -* lda #^swizzle -* sta tmp+2 + phb + phd ; Save the curren direct page and bank -* ldal tiledata+{]line*4},x -* ora :palette -* sta tmp -* lda: {]line*SHR_LINE_WIDTH},y -* andl tiledata+{]line*4}+32,x -* ora [tmp] -* sta: {]line*SHR_LINE_WIDTH},y + ldal tool_direct_page ; Can't assume where we are + clc + adc #$100 ; Sprite space is on the second page + tcd -* tay -* lda swizzle,y + pei DP2_TILEDATA_AND_BANK01_BANKS ; Push the two bank we need + plb ; Pop off the tile data bank +; lda #W11_S0 +; stz SwizzlePtr +; lda #^W11_S0 +; sta SwizzlePtr+2 -* phb -* pea $0101 -* plb -* plb +]line equ 0 + lup 8 + ldy tiledata+{]line*4}+2,x ; load the 8-bit NES tile data +; lda [SwizzlePtr],y ; lookup the swizzle value +; db LDA_IND_LONG_IDX,SwizzlePtr +; lda tiledata+{]line*4}+2,x + sta tmp_sprite_data+{]line*4}+2 - -* ]line equ 0 -* lup 8 -* lda: {]line*SHR_LINE_WIDTH}+2,y -* andl tiledata+{]line*4}+32+2,x -* oral tiledata+{]line*4}+2,x -* sta: {]line*SHR_LINE_WIDTH}+2,y -* lda: {]line*SHR_LINE_WIDTH},y -* andl tiledata+{]line*4}+32,x -* oral tiledata+{]line*4},x -* sta: {]line*SHR_LINE_WIDTH},y -* ]line equ ]line+1 -* --^ -* plb -* rtl ; special exit + ldy tiledata+{]line*4},x ; load the 8-bit NES tile data +; lda [SwizzlePtr],y ; lookup the swizzle value +; db LDA_IND_LONG_IDX,SwizzlePtr +; lda tiledata+{]line*4},x + sta tmp_sprite_data+{]line*4} +]line equ ]line+1 + --^ + + plb ; Pop off bank 01 + +]line equ 0 + lup 8 + ldal tiledata+{]line*4}+32+2,x + eor #$FFFF + and: {]line*SHR_LINE_WIDTH}+2,y + ora tmp_sprite_data+{]line*4}+2 + sta: {]line*SHR_LINE_WIDTH}+2,y + + ldal tiledata+{]line*4}+32,x + eor #$FFFF + and: {]line*SHR_LINE_WIDTH},y + ora tmp_sprite_data+{]line*4} + sta: {]line*SHR_LINE_WIDTH},y +]line equ ]line+1 + --^ + +; Restore the direct page and bank + + pld + plb + rtl pal_select dw $3333,$6666,$9999,$CCCC diff --git a/src/Tiles.s b/src/Tiles.s index 432bf6a..125bc82 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -404,25 +404,67 @@ _SetTile jmp _PushDirtyTileX :set_user_tile + and #$01FF + jsr _GetTileAddr ; The user tile callback needs access to this info, too, but + sta TileStore+TS_TILE_ADDR,x ; we just give the base tile address (hflip bit is ignored) + lda #UserTileDispatch stal K_TS_BASE_TILE_DISP,x - lda #UserTileDispatch stal K_TS_SPRITE_TILE_DISP,x - lda #UserTileDispatch stal K_TS_ONE_SPRITE,x jmp _PushDirtyTileX -; Trampoline / Dispatch table for user-defined tiles. If the user calls the GTESetCustomTileCallback toolbox routine, -; then this value is patched out. Calling GTESetCustomTileCallback with NULL will disconnect the user's routine. +; Trampoline / Dispatch table for user-defined tiles. If the user calls the GTESetAddress toolbox routine, +; then this value is patched out. Calling GTESetAddress with NULL will disconnect the user's routine. +; +; This hook copies essential information from the TileStore into a local record on the direct page and then +; passes that record to callback function. We are in the second direct page of the GTE bank 0 space. UserTileDispatch - ldal TileStore+TS_TILE_ID,x ; Replace the tile data address (unset) with the tile id -_UTDPatch jsl UserHook1 ; Call the users code - plb ; Restore the data bank + lda TileStore+TS_TILE_ID,x + sta USER_TILE_ID + lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line + sta USER_TILE_CODE_PTR+2 + lda TileStore+TS_CODE_ADDR_LOW,x + sta USER_TILE_CODE_PTR + lda TileStore+TS_TILE_ADDR,x + sta USER_TILE_ADDR + + ldx #USER_TILE_RECORD ; pass the direct page offset to the user + pei DP2_TILEDATA_AND_TILESTORE_BANKS ; set the bank to the tiledata bank + plb +_UTDPatch jsl UserHook1 ; Call the users code + plb ; Restore the curent bank + +; When the user's code returns, it can ask GTE to invoke a utility routine to copy data from the +; temporary buffer space into the code field + + cmp #0 + beq :done + jmp FastCopyTmpDataA ; Non-zero value to continue additional work +:done rts ; Stub to have a valid address for initialization / reset UserHook1 rtl +; Fast utility function to just copy tile data straight from the tmp_tile_data buffer to the code field. This is only +; used by the user callback, so we can assume knowledge of the values on the direct page. +FastCopyTmpDataA + pei USER_TILE_CODE_PTR+2 + ldy USER_TILE_CODE_PTR + plb + +]line equ 0 + lup 8 + lda tmp_tile_data+{]line*4} + sta: $0004+{]line*$1000},y + lda tmp_tile_data+{]line*4}+2 + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + plb + rts + ; X = Tile Store offset ; Y = Engine Mode Base Table address ; A = Table proc index diff --git a/src/Tool.s b/src/Tool.s index 07bf153..9ef55b1 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -176,6 +176,7 @@ zpToUse = userId+4 pld ; Restore the caller's direct page +:exit ldx #0 ; No error ldy #6 ; Remove the 6 input bytes jml ToStrip @@ -1011,8 +1012,28 @@ _TSSetAddress lda :ptr+2,s sta ExtUpdateBG0Tiles+2 bra :out -:next_5 +:next_5 cmp #userTileCallback + bne :next_6 + + lda :ptr,s + ora :ptr+2,s + beq :utd_restore + + lda :ptr,s + stal _UTDPatch+1 ; long addressing because we're patching code in the K bank + lda :ptr+1,s + stal _UTDPatch+2 + brl :out + +:utd_restore + lda #UserHook1 + stal _UTDPatch+1 + lda #>UserHook1 + stal _UTDPatch+2 + brl :out + +:next_6 :out _TSExit #0;#6 diff --git a/src/render/Dynamic.s b/src/render/Dynamic.s index 228a534..d2c3ba9 100644 --- a/src/render/Dynamic.s +++ b/src/render/Dynamic.s @@ -608,11 +608,11 @@ CopyTileToDyn tax tya - and #$001F ; Maximum of 32 dynamic tiles + and #$001F ; Maximum of 32 dynamic tiles asl - asl ; 4 bytes per page + asl ; 4 bytes per page adc BlitterDP ; Add to the bank 00 base address - adc #$0100 ; Go to the next page + adc #$0100 ; Go to the next page tay jsr CopyTileDToDyn ; Copy the tile data jmp CopyTileMToDyn ; Copy the tile mask diff --git a/src/render/Fast.s b/src/render/Fast.s index 671ece6..76bb6a3 100644 --- a/src/render/Fast.s +++ b/src/render/Fast.s @@ -4,6 +4,12 @@ ; not needed to improve rendering speed. ConstTile0Fast + 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. + ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field +; lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) + plb + lda #0 sta: $0001,y sta: $0004,y @@ -273,6 +279,12 @@ FastCopyTileDataAndMaskV ; This is called via a JMP (abs,x) with an extra byte on the stack that holds the bank ; register value. This must be restored prior to returning CopyTileAFast + 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. + ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field + lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) + plb + tax _CopyTileAFast ]line equ 0 @@ -288,6 +300,12 @@ _CopyTileAFast CopyTileVFast + 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. + ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field + lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) + plb + tax _CopyTileVFast ]src equ 7 diff --git a/src/render/Render.s b/src/render/Render.s index f5a3fb7..7ddc054 100644 --- a/src/render/Render.s +++ b/src/render/Render.s @@ -3,6 +3,7 @@ _RenderTile lda TileStore+TS_SPRITE_FLAG,x ; any sprites on this tile? bne _HasSprites + jmp (K_TS_BASE_TILE_DISP,x) ; go to the tile copy routine ; Probably best to rework this to just jump to the tile routine directly, even if there ; is some boilerplate code because it is useful to be able to access the data bank before diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index 488f47d..7b229ea 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -10,8 +10,8 @@ TS_DIRTY equ {TILE_STORE_SIZE*1} ; Flag. Used to prevent a ti TS_SPRITE_FLAG equ {TILE_STORE_SIZE*2} ; Bitfield of all sprites that intersect this tile. 0 if no sprites. TS_TILE_ADDR equ {TILE_STORE_SIZE*3} ; cached value, the address of the tiledata for this tile TS_CODE_ADDR_LOW equ {TILE_STORE_SIZE*4} ; const value, address of this tile in the code fields -TS_CODE_ADDR_HIGH equ {TILE_STORE_SIZE*5} -TS_WORD_OFFSET equ {TILE_STORE_SIZE*6} ; const value, word offset value for this tile if LDA (dp),y instructions re used +TS_CODE_ADDR_HIGH equ {TILE_STORE_SIZE*5} ; the top byte of this address holds the GTE data bank for easy restore +TS_WORD_OFFSET equ {TILE_STORE_SIZE*6} ; const value, word offset value for this tile if LDA (dp),y instructions are used TS_JMP_ADDR equ {TILE_STORE_SIZE*7} ; const value, address of the 32-byte snippet space for this tile TS_SCREEN_ADDR equ {TILE_STORE_SIZE*8} ; cached value of on-screen location of tile. Used for DirtyRender.