From adceac139038437391f43a1c8c93bec238770002 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 14:51:18 +0000 Subject: [PATCH 01/82] Bump minimist from 1.2.5 to 1.2.6 in /demos/zelda Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6) --- updated-dependencies: - dependency-name: minimist dependency-type: indirect ... Signed-off-by: dependabot[bot] --- demos/zelda/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/demos/zelda/package-lock.json b/demos/zelda/package-lock.json index cd11399..2ae2659 100644 --- a/demos/zelda/package-lock.json +++ b/demos/zelda/package-lock.json @@ -69,9 +69,9 @@ "dev": true }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "nan": { From 8bb17895a9db16750c99698d3dc4cf2e03657c6c Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 20 Apr 2022 07:43:16 -0500 Subject: [PATCH 02/82] Rough outline of streamlined sprite subsystem * Split the creation of the sprite stamps from adding the sprites themselves. This allows for 48 stamps that can be pre-rendered and quickly reassigned to sprites for animations. * Inlined all calls to PushDirtyTile. This both removed significant overhead from calling the small function and, since almost all callers we checking multiple tiles, we were able to avoid incrementing the count each time and just add a single incrments at the end. * Switched from recording each tile that a sprite intersects with each from to only recording the top-left tile and the overlap size. This reduced overhead for larger sprites and removed the needs for an end-of-list marker. * Much more aggressive caching of Sprite and Tile Store values in order to streamline the inner tile dispatch routines. * Moving TileStore and Sprites (and other supporting data structures) into a separate data bank. Needed just for size purposes and provide micro-optimizations by opening up the use of abs,y addressing modes. * Revamped multi-sprite rendering code to avoid the need to copy any masks and all stacked sprites can be drawn via a sequence of and [addrX],y; ora (addrX),y where addrX is set once per tile. * General streamlining to reduct overhead. This work was focused on removing as much per-tile overhead as possible. --- src/Defs.s | 82 ++++++- src/Render.s | 14 +- src/Sprite.s | 472 ++++++++++++++++++++++++++++---------- src/Sprite2.s | 478 ++++++++++++++++++++++++++++++--------- src/SpriteRender.s | 45 ++-- src/blitter/Tables.s | 18 +- src/blitter/Template.s | 2 +- src/blitter/Tiles.s | 416 +++++++++++++++++++++++++++++----- src/blitter/Tiles10000.s | 41 ++++ 9 files changed, 1244 insertions(+), 324 deletions(-) diff --git a/src/Defs.s b/src/Defs.s index 568d504..d2d8017 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -87,11 +87,11 @@ ActiveSpriteCount equ 102 BankLoad equ 104 TileStoreBankAndBank01 equ 106 TileStoreBankAndTileDataBank equ 108 -Next equ 110 +TileStoreBankDoubled equ 110 +Next equ 112 activeSpriteList equ 128 ; 32 bytes for the active sprite list (can persist across frames) -AppSpace equ 160 ; 16 bytes of space reserved for application use -tiletmp equ 178 ; 16 bytes of temp storage for the tile renderers +; tiletmp equ 178 ; 16 bytes of temp storage for the tile renderers blttmp equ 192 ; 32 bytes of local cache/scratch space for blitter tmp8 equ 224 ; another 16 bytes of temporary space to be used as scratch @@ -112,6 +112,36 @@ tmp5 equ 250 tmp6 equ 252 tmp7 equ 254 +; Defines for the second direct page (used in the tile blitters) + +sprite_ptr0 equ 0 ; Each tile can render up to 4 sprite blocks. The sprite +sprite_ptr1 equ 4 ; data and mask values live in different banks, but have a +sprite_ptr2 equ 8 ; parallel structure. The high word of each point is set to +sprite_ptr3 equ 12 ; the mask bank. With the Bank register set, both data and mask +; ; can be accessed through the same pointer, e.g. lda (sprite_ptr0) +; ; and [sprite_ptr0] + +tmp_sprite_data equ 16 ; 32 byte temporary buffer to build up sprite data values +tmp_sprite_mask equ 48 ; 32 byte temporary buffer to build up sprite mask values +tmp_tile_data equ 80 ; 32 byte temporary buffer to build up tile data values +tmp_tile_mask equ 112 ; 32 byte temporary buffer to build up tile mask values + +; Temporary direct page locations used by some of the complex tile renderers +_X_REG equ 144 +_Y_REG equ 146 +_T_PTR equ 148 ; Copy of the tile address pointer +_BASE_ADDR equ 150 ; Copy of BTableLow for this tile +_SPR_X_REG equ 152 ; Cache address of sprite plane source for a tile +_JTBL_CACHE equ 154 ; Cache the offset to the exception handler for a column +_OP_CACHE equ 156 ; Cache of a relevant operand / oeprator +_TILE_ID equ 158 ; Copy of the tile descriptor + +; Define free space the the application to use +FREE_SPACE_DP2 equ 160 + +; End direct page values + + DIRTY_BIT_BG0_X equ $0001 DIRTY_BIT_BG0_Y equ $0002 DIRTY_BIT_BG1_X equ $0004 @@ -153,17 +183,55 @@ SPRITE_8X8 equ $0000 SPRITE_VFLIP equ $0400 SPRITE_HFLIP equ $0200 -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 +; Stamp storage parameters +VBUFF_STRIDE_BYTES equ 12*4 ; Each line has 4 slots of 16 pixels + 8 buffer pixels +VBUFF_TILE_ROW_BYTES equ 8*VBUFF_STRIDE_BYTES ; Each row is comprised of 8 lines +VBUFF_SPRITE_STEP equ VBUFF_TILE_ROW_BYTES*3 ; Allocate space fo 16 rows + 8 rows of buffer +VBUFF_SPRITE_START equ {8*VBUFF_TILE_ROW_BYTES}+4 ; Start at an offset so $0000 can be used as an empty value +VBUFF_SLOT_COUNT equ 48 ; Have space for this many stamps + +; Tile storage parameters +TILE_STORE_WIDTH equ 41 +TILE_STORE_HEIGHT equ 26 +MAX_TILES equ {26*41} ; Number of tiles in the code field (41 columns * 26 rows) +TILE_STORE_SIZE equ {MAX_TILES*2} ; The tile store contains a tile descriptor in each slot TS_TILE_ID equ TILE_STORE_SIZE*0 ; tile descriptor for this location TS_DIRTY equ TILE_STORE_SIZE*1 ; Flag. Used to prevent a tile from being queued multiple times per frame TS_SPRITE_FLAG equ TILE_STORE_SIZE*2 ; Bitfield of all sprites that intersect this tile. 0 if no sprites. TS_TILE_ADDR equ TILE_STORE_SIZE*3 ; cached value, the address of the tiledata for this tile TS_CODE_ADDR_LOW equ TILE_STORE_SIZE*4 ; const value, address of this tile in the code fields -TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5 ; const value +TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5 TS_WORD_OFFSET equ TILE_STORE_SIZE*6 ; const value, word offset value for this tile if LDA (dp),y instructions re used TS_BASE_ADDR equ TILE_STORE_SIZE*7 ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000. TS_SCREEN_ADDR equ TILE_STORE_SIZE*8 ; cached value of on-screen location of tile. Used for DirtyRender. TS_VBUFF_ARRAY_ADDR equ TILE_STORE_SIZE*9 ; const value to an aligned 32-byte array starting at $8000 in TileStore bank -TS_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize tile dispatch in the Render function +TS_BASE_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function +TS_DIRTY_TILE_DISP equ TILE_STORE_SIZE*11 ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function + +; 16 consecutive entries to provide directly addressable space for holding the VBUFF address for the +; sprites that may be rendered at a given tile. Given a tile store offset, X, the way to address the +; address for the Y'th sprite is +; +; lda TileStore+TS_VBUFF_0+{Y*TILE_STORE_SIZE},x +; +; Moving to the next tile can be done with a constant. +; +; lda TileStore+TS_VBUFF_0+{Y*TILE_STORE_SIZE}+{41*row}+{2*col},x + +TS_VBUFF_0 equ TILE_STORE_SIZE*12 +TS_VBUFF_1 equ TILE_STORE_SIZE*13 +TS_VBUFF_2 equ TILE_STORE_SIZE*14 +TS_VBUFF_3 equ TILE_STORE_SIZE*15 +TS_VBUFF_4 equ TILE_STORE_SIZE*16 +TS_VBUFF_5 equ TILE_STORE_SIZE*17 +TS_VBUFF_6 equ TILE_STORE_SIZE*18 +TS_VBUFF_7 equ TILE_STORE_SIZE*19 +TS_VBUFF_8 equ TILE_STORE_SIZE*20 +TS_VBUFF_9 equ TILE_STORE_SIZE*21 +TS_VBUFF_10 equ TILE_STORE_SIZE*22 +TS_VBUFF_11 equ TILE_STORE_SIZE*23 +TS_VBUFF_12 equ TILE_STORE_SIZE*22 +TS_VBUFF_13 equ TILE_STORE_SIZE*23 +TS_VBUFF_14 equ TILE_STORE_SIZE*24 +TS_VBUFF_15 equ TILE_STORE_SIZE*25 diff --git a/src/Render.s b/src/Render.s index b010f38..9e5e63f 100644 --- a/src/Render.s +++ b/src/Render.s @@ -195,15 +195,13 @@ _RenderDirtyTile pei TileStoreBankAndBank01 ; Special value that has the TileStore bank in LSB and $01 in MSB plb - txy - ldx TileStore+TS_TILE_DISP,y ; get the finalized tile descriptor - ldal DirtyTileProcs,x ; load and patch in the appropriate subroutine + lda TileStore+TS_DIRTY_TILE_DISP,x ; load and patch in the appropriate subroutine stal :tiledisp+1 - ldx TileStore+TS_TILE_ADDR,y ; load the address of this tile's data (pre-calculated) - lda TileStore+TS_SCREEN_ADDR,y ; Get the on-screen address of this tile - tay + ldy TileStore+TS_SCREEN_ADDR,x ; Get the on-screen address of this tile + lda TileStore+TS_TILE_ADDR,y ; load the address of this tile's data (pre-calculated) + tax plb ; set the bank @@ -671,7 +669,7 @@ dirty_sprite stx spriteIdx+6 jmp BlitFourSprites -DirtyTileProcs dw _TBDirtyTile_00,_TBDirtyTile_0H,_TBDirtyTile_V0,_TBDirtyTile_VH +DirtyTileProcs dw _TBDirtyTile_00,_TBDirtyTile_0H,_TBDirtyTile_V0,_TBDirtyTile_VH ;DirtyTileSpriteProcs dw _TBDirtySpriteTile_00,_TBDirtySpriteTile_0H,_TBDirtySpriteTile_V0,_TBDirtySpriteTile_VH ; Blit tiles directly to the screen. @@ -998,4 +996,4 @@ BlitOneSprite _R0W0 cli pld - rts + rts diff --git a/src/Sprite.s b/src/Sprite.s index b50cd47..a3bcb89 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -21,35 +21,85 @@ InitSprites ; Clear values in the sprite array - ldx #{MAX_SPRITES-1}*2 -:loop3 stz _Sprites+TILE_STORE_ADDR_1,x - dex - dex - bpl :loop3 +; ldx #{MAX_SPRITES-1}*2 +;:loop3 stz _Sprites+TILE_STORE_ADDR_1,x +; dex +; dex +; bpl :loop3 ; Initialize the VBUFF address offsets in the data and mask banks for each sprite ; -; The internal grid 13 tiles wide where each sprite has a 2x2 interior square with a +; The internal grid 12 tiles wide where each sprite has a 2x2 interior square with a ; tile-size buffer all around. We pre-render each sprite with all four vert/horz flips -VBUFF_STRIDE_BYTES equ 13*4 -VBUFF_TILE_ROW_BYTES equ 8*VBUFF_STRIDE_BYTES -VBUFF_SPRITE_STEP equ VBUFF_TILE_ROW_BYTES*3 -VBUFF_SPRITE_START equ {8*VBUFF_TILE_ROW_BYTES}+4 +; +; Eventually we should be able to have a separate rendering path for vertically flipped +; sprites and will be able to double the capacity of the stamp buffer ldx #0 lda #VBUFF_SPRITE_START clc -:loop4 sta _Sprites+VBUFF_ADDR,x +:loop4 sta VBuffAddrTable,x adc #VBUFF_SPRITE_STEP inx inx - cpx #MAX_SPRITES*2 + cpx #VBUFF_SLOT_COUNT*2 bcc :loop4 ; Precalculate some bank values jsr _CacheSpriteBanks rts +; Utility function to calculate the difference in tile positions between a sprite's current +; position and it's previous position. This gets interesting because the number of tiles +; that a sprite covers can change based on the relative alignemen of the sprite with the +; background. +; +; Ideally, we would be able to quickly calculate exactly which new background tiles a sprite +; intersects with and which ones it has left to minimize the number of TileStore entries +; that need to be updated. +; +; In the short-term, we just do an equality test which lets us know if the sprite is +; covering the exact same tiles. + + +; Render a sprite stamp into the sprite buffer. Stamps exits independent of the sprites +; and sprite reference a specific stamp. This is necessary because it's common for a +; spite to change its graphic as its animating, but it is too costly to have to set up +; the stamp every time. So this allows users to create stamps in advance and then +; assign them to the sprites as needed. +; +; Currently, we support a maximum of 48 stamps. +; +; Input: +; A = sprite descriptor +; X = stamp slot +; Return: +; A = vbuff address to be assigned to Sprite[VBUFF_ADDR] +CreateSpriteStamp ENT + phb + phk + plb + jsr _CreateSpriteStamp + plb + rtl + +_CreateSpriteStamp + pha ; Save the descriptor + jsr _GetBaseTileAddr ; Get the address of the tile data + pha + + txa + asl + tax + ldy VBuffAddrTable,x ; Load the address of the stamp slot + + plx ; Pop the tile address + pla ; Pop the sprite ID + phy ; VBUFF_ADDR value + jsr _DrawSpriteStamp ; Render the sprite data and create a stamp + + pla ; Pop the VBUFF_ADDR and return + rts ; Add a new sprite to the rendering pipeline ; @@ -90,12 +140,12 @@ _AddSprite pla sta _Sprites+SPRITE_ID,x ; Keep a copy of the full descriptor - jsr _GetBaseTileAddr ; This applies the TILE_ID_MASK - sta _Sprites+TILE_DATA_OFFSET,x lda #SPRITE_STATUS_OCCUPIED+SPRITE_STATUS_ADDED sta _Sprites+SPRITE_STATUS,x + stz _Sprites+VBUFF_ADDR,x ; Clear the VBUFF address, just to initialize it + phy tya and #$00FF @@ -106,7 +156,7 @@ _AddSprite sta _Sprites+SPRITE_X,x ; X coordinate jsr _PrecalcAllSpriteInfo ; Cache sprite property values (simple stuff) - jsr _DrawSpriteSheet ; Render the sprite into internal space +; jsr _DrawSpriteSheet ; Render the sprite into internal space ; Mark the dirty bit to indicate that the active sprite list needs to be rebuilt in the next ; render call @@ -117,11 +167,161 @@ _AddSprite lda _SpriteBits,x ; Get the bit flag for this sprite slot tsb SpriteMap ; Mark it in the sprite map bit field - txa ; And return the sprite ID - clc ; Mark that the sprite was successfully added +; txa ; And return the sprite ID +; clc ; Mark that the sprite was successfully added rts +; Alternate implementation that uses the TS_COVERAGE_SIZE and TS_LOOKUP_INDEX properties to +; load the old values directly from the TileStoreLookup table, rather than caching them. +; This is more efficient, because the work in MarkDirtySprite is independent of the +; sprite size and, by inlining the _PushDirtyTile logic, we can save a fair amount of overhead +_ClearSpriteFromTileStore2 + ldx _Sprites+TS_COVERAGE_SIZE,y + jmp (csfts_tbl,x) +csfts_tbl dw csfts_1x1,csfts_1x2,csfts_1x3,csfts_out + dw csfts_2x1,csfts_2x2,csfts_2x3,csfts_out + dw csfts_3x1,csfts_3x2,csfts_3x3,csfts_out + dw csfts_out,csfts_out,csfts_out,csfts_out + +; Just a single value to clear and add to the dirty tile list +csfts_1x1 ldx _Sprites+TS_LOOKUP_INDEX,y + lda TileStoreLookup,x + tax + + lda TileStore+TS_SPRITE_FLAG,x + and _SpriteBitsNot,y + sta TileStore+TS_SPRITE_FLAG,x + + lda TileStore+TS_DIRTY,x + bne csfts_1x1_out + + inc ; any non-zero value will work + sta TileStore+TS_DIRTY,x ; and is 1 cycle faster than loading a constant value + + txa + ldx DirtyTileCount + sta DirtyTiles,x + inx + inx + stx DirtyTileCount +csfts_1x2 +csfts_1x3 +csfts_2x1 +csfts_2x3 +csfts_3x1 +csfts_3x2 +csfts_3x3 +csfts_1x1_out + rts + +; This is a more interesting case where the ability to batch things up starts to produce some +; efficiency gains +csfts_2x2 ldx _Sprites+TS_LOOKUP_INDEX,y ; Get the address of the old top-left corner + tay + ldx TileStoreLookup,y + + lda TileStore+TS_SPRITE_FLAG,x + and _SpriteBits + sta TileStore+TS_SPRITE_FLAG,x + + lda TileStore+TS_DIRTY,x + beq *+3 + phx + + + ldx TileStoreLookup+2,y + + lda TileStore+TS_SPRITE_FLAG,x + and _SpriteBits + sta TileStore+TS_SPRITE_FLAG,x + + lda TileStore+TS_DIRTY,x + beq *+3 + phx + + + ldx TileStoreLookup+TS_LOOKUP_SPAN,y + + lda TileStore+TS_SPRITE_FLAG,x + and _SpriteBits + sta TileStore+TS_SPRITE_FLAG,x + + lda TileStore+TS_DIRTY,x + beq *+3 + phx + + + ldx TileStoreLookup+TS_LOOKUP_SPAN+2,y + + lda TileStore+TS_SPRITE_FLAG,x + and _SpriteBits + sta TileStore+TS_SPRITE_FLAG,x + + ldy DirtyTileCount + + lda TileStore+TS_DIRTY,x + beq skip_2x2 + + txa + sta DirtyTiles,y + sta TileStore+TS_DIRTY,x + +skip_2x2 + pla + beq :done1 + sta DirtyTiles+2,x + tay + sta TileStore+TS_DIRTY,y + + pla + beq :done2 + sta DirtyTiles+4,x + tay + sta TileStore+TS_DIRTY,y + + pla + beq :done3 + sta DirtyTiles+6,x + tay + sta TileStore+TS_DIRTY,y + +; Maximum number of dirty tiles reached. Just fall through. + + pla + txa + adc #8 + sta DirtyTileCount + rts +:done3 + txa + adc #6 + sta DirtyTileCount + rts +:done2 + txa + adc #4 + sta DirtyTileCount + rts +:done1 + inx + inx + stx DirtyTileCount + + rts + + + + lda _SpriteBitsNot,y ; Cache the bit value for this sprite + + ldy TileStoreLookup,x ; Get the tile store offset + + + and TileStore+TS_SPRITE_FLAG,y + sta TileStore+TS_SPRITE_FLAG,y + +csfts_out rts + ; Run through the list of tile store offsets that this sprite was last drawn into and mark ; those tiles as dirty. The largest number of tiles that a sprite could possibly cover is 20 ; (an unaligned 4x3 sprite), covering a 5x4 area of play field tiles. @@ -129,68 +329,68 @@ _AddSprite ; Y register = sprite record index _CSFTS_Out rts _ClearSpriteFromTileStore - ldx _Sprites+TILE_STORE_ADDR_1,y - beq _CSFTS_Out - ldal TileStore+TS_SPRITE_FLAG,x ; Clear the bit in the bit field. This seems wasteful, but - and _SpriteBitsNot,y ; there is no indexed form of TSB/TRB and caching the value in - stal TileStore+TS_SPRITE_FLAG,x ; a direct page location, only saves 1 or 2 cycles per and costs 10. - jsr _PushDirtyTileX +; ldx _Sprites+TILE_STORE_ADDR_1,y +; beq _CSFTS_Out +; ldal TileStore+TS_SPRITE_FLAG,x ; Clear the bit in the bit field. This seems wasteful, but +; and _SpriteBitsNot,y ; there is no indexed form of TSB/TRB and caching the value in +; stal TileStore+TS_SPRITE_FLAG,x ; a direct page location, only saves 1 or 2 cycles per and costs 10. +; jsr _PushDirtyTileX - ldx _Sprites+TILE_STORE_ADDR_2,y - beq _CSFTS_Out - ldal TileStore+TS_SPRITE_FLAG,x - and _SpriteBitsNot,y - stal TileStore+TS_SPRITE_FLAG,x - jsr _PushDirtyTileX +; ldx _Sprites+TILE_STORE_ADDR_2,y +; beq _CSFTS_Out +; ldal TileStore+TS_SPRITE_FLAG,x +; and _SpriteBitsNot,y +; stal TileStore+TS_SPRITE_FLAG,x +; jsr _PushDirtyTileX - ldx _Sprites+TILE_STORE_ADDR_3,y - beq _CSFTS_Out - ldal TileStore+TS_SPRITE_FLAG,x - and _SpriteBitsNot,y - stal TileStore+TS_SPRITE_FLAG,x - jsr _PushDirtyTileX +; ldx _Sprites+TILE_STORE_ADDR_3,y +; beq _CSFTS_Out +; ldal TileStore+TS_SPRITE_FLAG,x +; and _SpriteBitsNot,y +; stal TileStore+TS_SPRITE_FLAG,x +; jsr _PushDirtyTileX - ldx _Sprites+TILE_STORE_ADDR_4,y - beq _CSFTS_Out - ldal TileStore+TS_SPRITE_FLAG,x - and _SpriteBitsNot,y - stal TileStore+TS_SPRITE_FLAG,x - jsr _PushDirtyTileX +; ldx _Sprites+TILE_STORE_ADDR_4,y +; beq _CSFTS_Out +; ldal TileStore+TS_SPRITE_FLAG,x +; and _SpriteBitsNot,y +; stal TileStore+TS_SPRITE_FLAG,x +; jsr _PushDirtyTileX - ldx _Sprites+TILE_STORE_ADDR_5,y - beq :out - ldal TileStore+TS_SPRITE_FLAG,x - and _SpriteBitsNot,y - stal TileStore+TS_SPRITE_FLAG,x - jsr _PushDirtyTileX +; ldx _Sprites+TILE_STORE_ADDR_5,y +; beq :out +; ldal TileStore+TS_SPRITE_FLAG,x +; and _SpriteBitsNot,y +; stal TileStore+TS_SPRITE_FLAG,x +; jsr _PushDirtyTileX - ldx _Sprites+TILE_STORE_ADDR_6,y - beq :out - ldal TileStore+TS_SPRITE_FLAG,x - and _SpriteBitsNot,y - stal TileStore+TS_SPRITE_FLAG,x - jsr _PushDirtyTileX +; ldx _Sprites+TILE_STORE_ADDR_6,y +; beq :out +; ldal TileStore+TS_SPRITE_FLAG,x +; and _SpriteBitsNot,y +; stal TileStore+TS_SPRITE_FLAG,x +; jsr _PushDirtyTileX - ldx _Sprites+TILE_STORE_ADDR_7,y - beq :out - ldal TileStore+TS_SPRITE_FLAG,x - and _SpriteBitsNot,y - stal TileStore+TS_SPRITE_FLAG,x - jsr _PushDirtyTileX +; ldx _Sprites+TILE_STORE_ADDR_7,y +; beq :out +; ldal TileStore+TS_SPRITE_FLAG,x +; and _SpriteBitsNot,y +; stal TileStore+TS_SPRITE_FLAG,x +; jsr _PushDirtyTileX - ldx _Sprites+TILE_STORE_ADDR_8,y - beq :out - ldal TileStore+TS_SPRITE_FLAG,x - and _SpriteBitsNot,y - stal TileStore+TS_SPRITE_FLAG,x - jsr _PushDirtyTileX +; ldx _Sprites+TILE_STORE_ADDR_8,y +; beq :out +; ldal TileStore+TS_SPRITE_FLAG,x +; and _SpriteBitsNot,y +; stal TileStore+TS_SPRITE_FLAG,x +; jsr _PushDirtyTileX - ldx _Sprites+TILE_STORE_ADDR_9,y - beq :out - ldal TileStore+TS_SPRITE_FLAG,x - and _SpriteBitsNot,y - stal TileStore+TS_SPRITE_FLAG,x - jmp _PushDirtyTileX +; ldx _Sprites+TILE_STORE_ADDR_9,y +; beq :out +; ldal TileStore+TS_SPRITE_FLAG,x +; and _SpriteBitsNot,y +; stal TileStore+TS_SPRITE_FLAG,x +; jmp _PushDirtyTileX :out rts @@ -383,7 +583,7 @@ _DoPhase2 RebuildSpriteArray lda SpriteMap ; Get the bit field -; Unrolled loop to get the sprite index values that coorespond to the set bit positions +; Unrolled loop to get the sprite index values that correspond to the set bit positions pea $FFFF ; end-of-list marker ]step equ 0 @@ -442,6 +642,20 @@ _RenderSprites ; OPTIMIZATION NOTE: Should check that the sprite actually changes position. If the screen scrolls ; by +X, but the sprite moves by -X (so it's relative position is unchanged), then ; it does NOT need to be marked as dirty. +; +; OPTIMIZATION NOTE: At this point, a decent chunk of per-tile time is spent cupdating the sprite flgas +; for a given TileStore entry. When a sprite needs to be redrawn (such as when the +; screen scrolls), the code marks every tile the sprite was on as no longer occupied +; and then marks the occupied tiles. While simple, this is very redundent when the +; screen in scrolling slowly since it is very likely that the same sprite covers the +; exact same tiles. Each pair of markings requires 35 cycles, so a basic 16x16 sprite +; could save >300 cycles per frame. With 4 or 5 sprites on screen, the saving passes +; our 1% threshold for useful optimizations. +; +; Since we cache the tile location and effective sprite coverage, we need a fast +; way to compare the old and new positions and get a list of the new tiles the sprite +; occupies and old locations that it no longer covers. It's possible that just testing +; for equality would be the easiest win to know when we can skip everything. stz forceSpriteFlag lda StartX @@ -531,10 +745,15 @@ _CacheSpriteBanks ora #^TileStore sta TileStoreBankAndTileDataBank + lda #>TileStore + and #$FF00 + ora #^TileStore + sta TileStoreBankDoubled + rts ; This is 13 blocks wide -SPRITE_PLANE_SPAN equ VBUFF_STRIDE_BYTES ; 52 +SPRITE_PLANE_SPAN equ VBUFF_STRIDE_BYTES ; A = x coordinate ; Y = y coordinate @@ -574,11 +793,19 @@ SPRITE_PLANE_SPAN equ VBUFF_STRIDE_BYTES ; 52 ; it's tile information, or changing its position. ; ; X = sprite index +_stamp_step dw 0,12,24,36 _PrecalcAllSpriteInfo lda _Sprites+SPRITE_ID,x - and #$3E00 +; and #$3E00 xba - sta _Sprites+SPRITE_DISP,x ; use bits 9 through 13 for full dispatch + and #$0006 + tay + lda _Sprites+VBUFF_ADDR,x + clc + adc _stamp_step,y + sta _Sprites+SPRITE_DISP,x + +; Set the ; Set the sprite's width and height lda #4 @@ -673,19 +900,26 @@ RemoveSprite ENT rtl _RemoveSprite + cmp #MAX_SPRITES + bcc :ok + rts + +:ok + asl tax -_RemoveSpriteX lda _Sprites+SPRITE_STATUS,x ora #SPRITE_STATUS_REMOVED sta _Sprites+SPRITE_STATUS,x + rts ; Update the sprite's flags. We do not allow the size of a sprite to be changed. That requires ; the sprite to be removed and re-added. ; ; A = Sprite ID -; X = Sprite Tile ID and Flags +; X = New Sprite Flags +; Y = New Sprite Stamp Address UpdateSprite ENT phb phk @@ -695,36 +929,34 @@ UpdateSprite ENT rtl _UpdateSprite - phx ; swap X/A to be more efficient - tax - pla - -_UpdateSpriteX - cpx #MAX_SPRITES*2 ; Make sure we're in bounds + cmp #MAX_SPRITES bcc :ok rts :ok -_UpdateSpriteXnc - cmp _Sprites+SPRITE_ID,x ; Don't do anything if there is no change - beq :no_sprite_change + phx ; Save X to swap into A + asl + tax + pla + cmp _Sprites+SPRITE_ID,x ; If the flags changed, need to redraw the sprite + bne :sprite_flag_change ; on the next frame + tya + cmp _Sprites+VBUFF_ADDR,x ; Did the stamp change? + bne :sprite_stamp_change + rts ; Nothing changed, so just return + +:sprite_flag_change sta _Sprites+SPRITE_ID,x ; Keep a copy of the full descriptor - jsr _GetBaseTileAddr ; This applies the TILE_ID_MASK - cmp _Sprites+TILE_DATA_OFFSET,x - beq :no_tile_change - sta _Sprites+TILE_DATA_OFFSET,x + tya +:sprite_stamp_change + sta _Sprites+VBUFF_ADDR,x ; Just save this to stay in sync - jsr _PrecalcAllSpriteInfo ; Cache stuff - jsr _DrawSpriteSheet ; Render the sprite into internal space if the tile id has changed - -:no_tile_change - lda _Sprites+SPRITE_STATUS,x + lda _Sprites+SPRITE_STATUS,x ; Mark this sprite as updated ora #SPRITE_STATUS_UPDATED sta _Sprites+SPRITE_STATUS,x -:no_sprite_change - rts + jmp _PrecalcAllSpriteInfo ; Cache stuff and return ; Move a sprite to a new location. If the tile ID of the sprite needs to be changed, then ; a full remove/add cycle needs to happen @@ -741,17 +973,16 @@ MoveSprite ENT rtl _MoveSprite - phx ; swap X/A to be more efficient - tax - pla - -_MoveSpriteX - cpx #MAX_SPRITES*2 ; Make sure we're in bounds + cmp #MAX_SPRITES bcc :ok rts :ok -_MoveSpriteXnc + phx ; Save X to swap into A + asl + tax + pla + cmp _Sprites+SPRITE_X,x bne :changed1 sta _Sprites+SPRITE_X,x ; Update the X coordinate @@ -766,13 +997,11 @@ _MoveSpriteXnc :changed2 sta _Sprites+SPRITE_Y,x ; Update the Y coordinate - jsr _PrecalcAllSpriteInfo ; Can be specialized to only update (x,y) values - lda _Sprites+SPRITE_STATUS,x ora #SPRITE_STATUS_MOVED sta _Sprites+SPRITE_STATUS,x - rts + jmp _PrecalcAllSpriteInfo ; Can be specialized to only update (x,y) values ; Sprite data structures. We cache quite a few pieces of information about the sprite ; to make calculations faster, so this is hidden from the caller. @@ -797,22 +1026,25 @@ SPRITE_STATUS_UPDATED equ $0004 ; Sprite's non-position attributes were SPRITE_STATUS_REMOVED equ $0008 ; Sprite has been removed. SPRITE_STATUS equ {MAX_SPRITES*0} -TILE_DATA_OFFSET equ {MAX_SPRITES*2} -VBUFF_ADDR equ {MAX_SPRITES*4} ; Fixed address in sprite/mask banks +; TILE_DATA_OFFSET equ {MAX_SPRITES*2} +VBUFF_ADDR equ {MAX_SPRITES*4} ; Base address of the sprite's stamp in the data/mask banks SPRITE_ID equ {MAX_SPRITES*6} SPRITE_X equ {MAX_SPRITES*8} SPRITE_Y equ {MAX_SPRITES*10} -TILE_STORE_ADDR_1 equ {MAX_SPRITES*12} -TILE_STORE_ADDR_2 equ {MAX_SPRITES*14} -TILE_STORE_ADDR_3 equ {MAX_SPRITES*16} -TILE_STORE_ADDR_4 equ {MAX_SPRITES*18} -TILE_STORE_ADDR_5 equ {MAX_SPRITES*20} -TILE_STORE_ADDR_6 equ {MAX_SPRITES*22} -TILE_STORE_ADDR_7 equ {MAX_SPRITES*24} -TILE_STORE_ADDR_8 equ {MAX_SPRITES*26} -TILE_STORE_ADDR_9 equ {MAX_SPRITES*28} -TILE_STORE_ADDR_10 equ {MAX_SPRITES*30} -SPRITE_DISP equ {MAX_SPRITES*32} ; pre-calculated index for jmp (abs,x) based on sprite size +; TILE_STORE_ADDR_1 equ {MAX_SPRITES*12} +TS_LOOKUP_INDEX equ {MAX_SPRITES*12} ; The index into the TileStoreLookup table corresponding to the top-left corner of the sprite +; TILE_STORE_ADDR_2 equ {MAX_SPRITES*14} +TS_COVERAGE_SIZE equ {MAX_SPRITES*14} ; Index into the lookup table of how many TileStore tiles are covered by this sprite +;TILE_STORE_ADDR_3 equ {MAX_SPRITES*16} +TS_VBUFF_BASE_ADDR equ {MAX_SPRITES*16} ; Fixed address of the TS_VBUFF_X memory locations +;TILE_STORE_ADDR_4 equ {MAX_SPRITES*18} +;TILE_STORE_ADDR_5 equ {MAX_SPRITES*20} +;TILE_STORE_ADDR_6 equ {MAX_SPRITES*22} +;TILE_STORE_ADDR_7 equ {MAX_SPRITES*24} +;TILE_STORE_ADDR_8 equ {MAX_SPRITES*26} +;TILE_STORE_ADDR_9 equ {MAX_SPRITES*28} +;TILE_STORE_ADDR_10 equ {MAX_SPRITES*30} +SPRITE_DISP equ {MAX_SPRITES*32} ; cached address of the specific stamp based on flags SPRITE_CLIP_LEFT equ {MAX_SPRITES*34} SPRITE_CLIP_RIGHT equ {MAX_SPRITES*36} SPRITE_CLIP_TOP equ {MAX_SPRITES*38} diff --git a/src/Sprite2.s b/src/Sprite2.s index 82a6dd6..9d83bea 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -81,105 +81,206 @@ _LocalToTileStore ; ... ; ; For the Y-coordinate, we just use "mod 8" instead of "mod 4" -mdsOut rts +mdsOut2 + lda #6 ; Pick a value for a 0x0 tile sprite + sta _Sprites+TS_COVERAGE_SIZE,y ; zero the list of tile store addresses + rts + _MarkDirtySprite - - lda #0 - sta _Sprites+TILE_STORE_ADDR_1,y ; Clear this sprite's dirty tile list in case of an early exit - lda _SpriteBits,y ; Cache its bit flag to mark in the tile slots - sta SpriteBit - lda _Sprites+IS_OFF_SCREEN,y ; Check if the sprite is visible in the playfield - bne mdsOut + bne mdsOut2 -; At this point we know that we have to update the tiles that overlap the sprite's rectangle defined -; by (Top, Left), (Bottom, Right). First, calculate the row and column in the TileStore that -; encloses the top-left on-screen corner of the sprite +; Add the first visible row of the sprite to the Y-scroll offset to find the first line in the +; code field that needs to be drawn. The range of values is 0 to 199+207 = [0, 406] clc lda _Sprites+SPRITE_CLIP_TOP,y adc StartYMod208 ; Adjust for the scroll offset - tax ; cache - cmp #208 ; check if we went too far positive - bcc *+5 - sbc #208 + pha ; Cache + and #$FFF8 ; mask first to ensure LSR will clear the carry lsr - lsr ; This is the row in the Tile Store for top-left corner of the sprite - and #$FFFE ; Store the value pre-multiplied by 2 for indexing in the :mark_R_C routines + lsr + tax + lda TileStoreLookupYTable,x ; Even numbers from [0, 100] (50 elements) sta RowTop + pla -; Next, calculate how many tiles are covered by the sprite. This uses the table at the top of this function, but -; the idea is that for every increment of StartX or StartY, that can shift the sprite into the next tile, up to -; a maximum of mod 4 / mod 8. So the effective width of a sprite is (((StartX + Clip_Left) mod 4) + Clip_Width) / 4 +; Get the position of the top edge within the tile and then add it to the sprite's height +; to calculate the number of tiles that are overlapped. We use the actual width and height +; values here so small sprites (like 4x4 bullets) only force an update to the actual tiles +; that are intersected, rather than assuming an 8x8 sprite always takes up that amount of +; space. txa and #$0007 - sta tmp0 ; save to adjust sprite origin + tax ; cache again. This is a bit faster than recalculating - lda _Sprites+SPRITE_CLIP_HEIGHT,y ; Nominal value between 0 and 16+7 = 23 = 10111 + adc _Sprites+SPRITE_CLIP_HEIGHT,y ; Nominal value between 0 and 16+7 = 23 = 10111 dec - clc - adc tmp0 and #$0018 sta AreaIndex -; Repeat to get the same information for the columns + txa + asl + tax + lda :vbuff_mul,x + sta tmp0 + +; Add the horizontal position to the horizontal offset to find the first column in the +; code field that needs to be drawn. The range of values is 0 to 159+163 = [0, 322] clc lda _Sprites+SPRITE_CLIP_LEFT,y adc StartXMod164 tax - cmp #164 - bcc *+5 - sbc #164 + and #$FFFC lsr - and #$FFFE ; Same pre-multiply by 2 for later - sta ColLeft +; sta ColLeft ; Even numbers from [0, 160] (80 elements) + adc RowTop + sta _Sprites+TS_LOOKUP_INDEX,y ; This is the index into the TileStoreLookup table + + +; Calculate the final address of the sprite data in the stamp buffer. We have to move earlier +; in the buffer based on the horizontal offset and move up for each vertical offset. txa and #$0003 - sta tmp1 ; save to adjust sprite origin + tax - lda _Sprites+SPRITE_CLIP_WIDTH,y ; max width = 8 = 0x08 + adc tmp0 ; add to the vertical offset + +; Subtract this value from the SPRITE_DISP address + + eor #$FFFF ; A = -X - 1 + sec ; C = 1 + adc _Sprites+SPRITE_DISP,y ; A = SPRITE_DISP + (-X - 1) + 1 = SPRITE_DISP - X + + sta VBuffOrigin ; this is the final (adjusted) origin for this sprite + +; Load the base address of the appropriate TS_VBUFF_? offset for this sprite index and +; store it as an indirect address. + + lda _Sprites+TS_VBUFF_BASE_ADDR,y + sta tmp0 + +; We know the starting corner of the TileStore. Now, we need to figure out now many tiles +; the sprite covers. This is a function of the sprite's width and height and the specific +; location of the upper-left corner of the sprite within the corner tile. + + txa + adc _Sprites+SPRITE_CLIP_WIDTH,y ; max width = 8 = 0x08 dec - clc - adc tmp1 + and #$000C lsr ; max value = 4 = 0x04 - and #$0006 - ora AreaIndex - sta AreaIndex + ora AreaIndex ; merge into the area index + +; No need to copy the TileStore addresses into the Sprite's TILE_STORE_ADDR values. Just +; hold a copy of the corner offset into the lookup table and the sprite's size in tiles. +; Then, when we need to erase we can just lookup the values in the TileStoreLookup table. + + sta _Sprites+TS_COVERAGE_SIZE,y + tax +; lda TileStoreBaseIndex +; sta _Sprites+TS_LOOKUP_INDEX,y + +; Jump to the appropriate marking routine + + jmp (:mark,x) + +mdsOut rts +;_MarkDirtySprite +; +; lda #0 +; sta _Sprites+TILE_STORE_ADDR_1,y ; Clear this sprite's dirty tile list in case of an early exit +; lda _SpriteBits,y ; Cache its bit flag to mark in the tile slots +; sta SpriteBit + +; lda _Sprites+IS_OFF_SCREEN,y ; Check if the sprite is visible in the playfield +; bne mdsOut + +; At this point we know that we have to update the tiles that overlap the sprite's rectangle defined +; by (Top, Left), (Bottom, Right). First, calculate the row and column in the TileStore that +; encloses the top-left on-screen corner of the sprite + +; clc +; lda _Sprites+SPRITE_CLIP_TOP,y +; adc StartYMod208 ; Adjust for the scroll offset +; tax ; cache +; cmp #208 ; check if we went too far positive +; bcc *+5 +; sbc #208 +; lsr +; lsr ; This is the row in the Tile Store for top-left corner of the sprite +; and #$FFFE ; Store the value pre-multiplied by 2 for indexing in the :mark_R_C routines +; sta RowTop + +; Next, calculate how many tiles are covered by the sprite. This uses the table at the top of this function, but +; the idea is that for every increment of StartX or StartY, that can shift the sprite into the next tile, up to +; a maximum of mod 4 / mod 8. So the effective width of a sprite is (((StartX + Clip_Left) mod 4) + Clip_Width) / 4 + +; txa +; and #$0007 +; sta tmp0 ; save to adjust sprite origin + +; lda _Sprites+SPRITE_CLIP_HEIGHT,y ; Nominal value between 0 and 16+7 = 23 = 10111 +; dec +; clc +; adc tmp0 +; and #$0018 +; sta AreaIndex + +; Repeat to get the same information for the columns + +; clc +; lda _Sprites+SPRITE_CLIP_LEFT,y +; adc StartXMod164 +; tax +; cmp #164 +; bcc *+5 +; sbc #164 +; lsr +; and #$FFFE ; Same pre-multiply by 2 for later +; sta ColLeft + +; txa +; and #$0003 +; sta tmp1 ; save to adjust sprite origin; + +; lda _Sprites+SPRITE_CLIP_WIDTH,y ; max width = 8 = 0x08 +; dec +; clc +; adc tmp1 +; lsr ; max value = 4 = 0x04 +; and #$0006 +; ora AreaIndex +; sta AreaIndex ; Calculate the modified origin address for the sprite. We need to look at the sprite flip bits ; to determine which of the four sprite stamps is the correct one to use. Then, offset that origin ; based on the (x, y) and (startx, starty) positions. - lda _Sprites+SPRITE_DISP,y ; Each stamp is 12 bytes - and #$0006 - tax - lda :stamp_step,x - clc - adc _Sprites+VBUFF_ADDR,y - sec - sbc tmp1 ; Subtract the horizontal within-tile displacement - asl tmp0 - ldx tmp0 - sec - sbc :vbuff_mul,x - sta VBuffOrigin - lda #^TileStore - sta tmp1 +; lda _Sprites+SPRITE_DISP,y ; Get the sprite's base display address +; sec +; sbc tmp1 ; Subtract the horizontal within-tile displacement +; asl tmp0 +; ldx tmp0 +; sec +; sbc :vbuff_mul,x +; sta VBuffOrigin +; lda #^TileStore +; sta tmp1 ; Dispatch to cover the tiles - ldx AreaIndex - jmp (:mark,x) +; ldx AreaIndex +; jmp (:mark,x) :mark dw :mark1x1,:mark1x2,:mark1x3,mdsOut dw :mark2x1,:mark2x2,:mark2x3,mdsOut dw :mark3x1,:mark3x2,:mark3x3,mdsOut dw mdsOut,mdsOut,mdsOut,mdsOut -:stamp_step dw 0,12,24,36 :vbuff_mul dw 0,52,104,156,208,260,312,364 + ; Dispatch to the calculated sizing ; Begin a list of subroutines to cover all of the valid sprite size combinations. This is all unrolled code, @@ -191,11 +292,170 @@ _MarkDirtySprite ; ; There *might* be some speed gained by pushing a list of :mark_R_C addressed onto the stack in the clipping routing ; and dispatching that way, but probably not... + +:mark1x1_v2 + + tax ; Get the TileStoreBaseIndex + + ldy TileStoreLookup,x ; Get the offset into the TileStore for this tile + + lda SpriteBit ; Mark this tile as having this sprite + ora TileStore+TS_SPRITE_FLAG,y + sta TileStore+TS_SPRITE_FLAG,y + + lda VBuffOrigin + sta (tmp0),y ; Fill in the slot for this sprite on this tile + + lda TileStore+TS_DIRTY,y ; If this tile is not yet marked dirty, mark it + bne exit1x1 + + ldx DirtyTileCount + tya + sta DirtyTiles,x + sta TileStore+TS_DIRTY,y + inx + inx + stx DirtyTileCount + +exit1x1 + rts + +:mark2x2_v2 + +; Put the TileStoreBaseIndex into the X-register + + tax + +; Push a sentinel value of the stack that we use to inline all of the dirty tile array updates faster +; and the end of this routine. + + pea #$0000 + +; Now, move through each of the TileStore locations and set the necessary fields. We have to do the +; following +; +; 1. Set the marker bit in the TS_SPRITE_FLAG so the renderer knows which vbuff addresses to load +; 2. Set the address of the sprite stamp graphics that are used. This can change every frame. +; 3. Mark the tile as dirty and put it on the list if it was marked dirty for the first time. + + ldy TileStoreLookup,x ; Get the offset into the TileStore for this tile + + lda SpriteBit ; Mark this tile as having this sprite + ora TileStore+TS_SPRITE_FLAG,y + sta TileStore+TS_SPRITE_FLAG,y + + lda TileStore+TS_DIRTY,y ; If this tile is not yet marked dirty, queue it up + bne *+3 + phy + + lda VBuffOrigin + sta (tmp0),y ; Fill in the slot for this sprite on this tile + +; Move to the next tile + + ldy TileStoreLookup+2,x + + adc #4 ; Weave in the VBuffOrigin values to save a load every + sta (tmp0),y ; other iteration + + lda SpriteBit + ora TileStore+TS_SPRITE_FLAG,y + sta TileStore+TS_SPRITE_FLAG,y + + lda TileStore+TS_DIRTY,y + bne *+3 + phy + +; Third tile + + ldy TileStoreLookup+TS_LOOKUP_SPAN,x + + lda SpriteBit + ora TileStore+TS_SPRITE_FLAG,y + sta TileStore+TS_SPRITE_FLAG,y + + lda TileStore+TS_DIRTY,y + bne *+3 + phy + + lda VBuffOrigin + adc #SPRITE_PLANE_SPAN + sta (tmp0),y + +; Fourth tile + + ldy TileStoreLookup+TS_LOOKUP_SPAN+2,x + + adc #4+SPRITE_PLANE_SPAN + sta (tmp0),y + + lda SpriteBit + ora TileStore+TS_SPRITE_FLAG,y + sta TileStore+TS_SPRITE_FLAG,y + +; Lift this above the last TS_DIRTY check + + ldx DirtyTileCount + +; Check the TS_DIRTY flag for this tile. We handle it immediately, if needed + + lda TileStore+TS_DIRTY,y + bne skip + +; Now, update the Dirty Tile array + + tya + sta DirtyTiles,x + sta TileStore+TS_DIRTY,y + +skip + pla + beq :done1 + sta DirtyTiles+2,x + tay + sta TileStore+TS_DIRTY,y + + pla + beq :done2 + sta DirtyTiles+4,x + tay + sta TileStore+TS_DIRTY,y + + pla + beq :done3 + sta DirtyTiles+6,x + tay + sta TileStore+TS_DIRTY,y + +; Maximum number of dirty tiles reached. Just fall through. + + pla + txa + adc #8 + sta DirtyTileCount + rts +:done3 + txa + adc #6 + sta DirtyTileCount + rts +:done2 + txa + adc #4 + sta DirtyTileCount + rts +:done1 + inx + inx + stx DirtyTileCount + + rts + :mark1x1 jsr :mark_0_0 - sta _Sprites+TILE_STORE_ADDR_1,y - lda #0 - sta _Sprites+TILE_STORE_ADDR_2,y +; sta _Sprites+TILE_STORE_ADDR_1,y +; lda #0 +; sta _Sprites+TILE_STORE_ADDR_2,y rts ; NOTE: If we rework the _PushDirtyTile to use the Y register instead of the X register, we can @@ -209,112 +469,112 @@ _MarkDirtySprite :mark1x2 jsr :mark_0_0 - sta _Sprites+TILE_STORE_ADDR_1,y +; sta _Sprites+TILE_STORE_ADDR_1,y jsr :mark_0_1 - sta _Sprites+TILE_STORE_ADDR_2,y - lda #0 - sta _Sprites+TILE_STORE_ADDR_3,y +; sta _Sprites+TILE_STORE_ADDR_2,y +; lda #0 +; sta _Sprites+TILE_STORE_ADDR_3,y rts :mark1x3 jsr :mark_0_0 - sta _Sprites+TILE_STORE_ADDR_1,y +; sta _Sprites+TILE_STORE_ADDR_1,y jsr :mark_0_1 - sta _Sprites+TILE_STORE_ADDR_2,y +; sta _Sprites+TILE_STORE_ADDR_2,y jsr :mark_0_2 - sta _Sprites+TILE_STORE_ADDR_3,y - lda #0 - sta _Sprites+TILE_STORE_ADDR_4,y +; sta _Sprites+TILE_STORE_ADDR_3,y +; lda #0 +; sta _Sprites+TILE_STORE_ADDR_4,y rts :mark2x1 jsr :mark_0_0 - sta _Sprites+TILE_STORE_ADDR_1,y +; sta _Sprites+TILE_STORE_ADDR_1,y jsr :mark_1_0 - sta _Sprites+TILE_STORE_ADDR_2,y - lda #0 - sta _Sprites+TILE_STORE_ADDR_3,y +; sta _Sprites+TILE_STORE_ADDR_2,y +; lda #0 +; sta _Sprites+TILE_STORE_ADDR_3,y rts :mark2x2 jsr :mark_0_0 - sta _Sprites+TILE_STORE_ADDR_1,y +; sta _Sprites+TILE_STORE_ADDR_1,y jsr :mark_0_1 - sta _Sprites+TILE_STORE_ADDR_2,y +; sta _Sprites+TILE_STORE_ADDR_2,y jsr :mark_1_0 - sta _Sprites+TILE_STORE_ADDR_3,y +; sta _Sprites+TILE_STORE_ADDR_3,y jsr :mark_1_1 - sta _Sprites+TILE_STORE_ADDR_4,y - lda #0 - sta _Sprites+TILE_STORE_ADDR_5,y +; sta _Sprites+TILE_STORE_ADDR_4,y +; lda #0 +; sta _Sprites+TILE_STORE_ADDR_5,y rts :mark2x3 jsr :mark_0_0 - sta _Sprites+TILE_STORE_ADDR_1,y +; sta _Sprites+TILE_STORE_ADDR_1,y jsr :mark_0_1 - sta _Sprites+TILE_STORE_ADDR_2,y +; sta _Sprites+TILE_STORE_ADDR_2,y jsr :mark_0_2 - sta _Sprites+TILE_STORE_ADDR_3,y +; sta _Sprites+TILE_STORE_ADDR_3,y jsr :mark_1_0 - sta _Sprites+TILE_STORE_ADDR_4,y +; sta _Sprites+TILE_STORE_ADDR_4,y jsr :mark_1_1 - sta _Sprites+TILE_STORE_ADDR_5,y +; sta _Sprites+TILE_STORE_ADDR_5,y jsr :mark_1_2 - sta _Sprites+TILE_STORE_ADDR_6,y - lda #0 - sta _Sprites+TILE_STORE_ADDR_7,y +; sta _Sprites+TILE_STORE_ADDR_6,y +; lda #0 +; sta _Sprites+TILE_STORE_ADDR_7,y rts :mark3x1 jsr :mark_0_0 - sta _Sprites+TILE_STORE_ADDR_1,y +; sta _Sprites+TILE_STORE_ADDR_1,y jsr :mark_1_0 - sta _Sprites+TILE_STORE_ADDR_2,y +; sta _Sprites+TILE_STORE_ADDR_2,y jsr :mark_2_0 - sta _Sprites+TILE_STORE_ADDR_3,y - lda #0 - sta _Sprites+TILE_STORE_ADDR_4,y +; sta _Sprites+TILE_STORE_ADDR_3,y +; lda #0 +; sta _Sprites+TILE_STORE_ADDR_4,y rts :mark3x2 jsr :mark_0_0 - sta _Sprites+TILE_STORE_ADDR_1,y +; sta _Sprites+TILE_STORE_ADDR_1,y jsr :mark_1_0 - sta _Sprites+TILE_STORE_ADDR_2,y +; sta _Sprites+TILE_STORE_ADDR_2,y jsr :mark_2_0 - sta _Sprites+TILE_STORE_ADDR_3,y +; sta _Sprites+TILE_STORE_ADDR_3,y jsr :mark_0_1 - sta _Sprites+TILE_STORE_ADDR_4,y +; sta _Sprites+TILE_STORE_ADDR_4,y jsr :mark_1_1 - sta _Sprites+TILE_STORE_ADDR_5,y +; sta _Sprites+TILE_STORE_ADDR_5,y jsr :mark_2_1 - sta _Sprites+TILE_STORE_ADDR_6,y - lda #0 - sta _Sprites+TILE_STORE_ADDR_7,y +; sta _Sprites+TILE_STORE_ADDR_6,y +; lda #0 +; sta _Sprites+TILE_STORE_ADDR_7,y rts :mark3x3 jsr :mark_0_0 - sta _Sprites+TILE_STORE_ADDR_1,y +; sta _Sprites+TILE_STORE_ADDR_1,y jsr :mark_1_0 - sta _Sprites+TILE_STORE_ADDR_2,y +; sta _Sprites+TILE_STORE_ADDR_2,y jsr :mark_2_0 - sta _Sprites+TILE_STORE_ADDR_3,y +; sta _Sprites+TILE_STORE_ADDR_3,y jsr :mark_0_1 - sta _Sprites+TILE_STORE_ADDR_4,y +; sta _Sprites+TILE_STORE_ADDR_4,y jsr :mark_1_1 - sta _Sprites+TILE_STORE_ADDR_5,y +; sta _Sprites+TILE_STORE_ADDR_5,y jsr :mark_2_1 - sta _Sprites+TILE_STORE_ADDR_6,y +; sta _Sprites+TILE_STORE_ADDR_6,y jsr :mark_0_2 - sta _Sprites+TILE_STORE_ADDR_7,y +; sta _Sprites+TILE_STORE_ADDR_7,y jsr :mark_1_2 - sta _Sprites+TILE_STORE_ADDR_8,y +; sta _Sprites+TILE_STORE_ADDR_8,y jsr :mark_2_2 - sta _Sprites+TILE_STORE_ADDR_9,y - lda #0 - sta _Sprites+TILE_STORE_ADDR_10,y +; sta _Sprites+TILE_STORE_ADDR_9,y +; lda #0 +; sta _Sprites+TILE_STORE_ADDR_10,y rts ; Begin List of subroutines to mark each tile offset diff --git a/src/SpriteRender.s b/src/SpriteRender.s index 39041fd..4eed2a3 100644 --- a/src/SpriteRender.s +++ b/src/SpriteRender.s @@ -1,23 +1,42 @@ +; Alternate entry point that takes arguments in registers instead of using a _Sprite +; record +; +; Y = VBUFF address +; X = Tile Data address +; A = Sprite Flags +_DrawSpriteStamp + sty tmp1 + stx tmp2 + and #DISP_MASK ; dispatch to all of the different orientations + sta tmp3 + jmp _DSSCommon + ; Function to render a sprite from a sprite definition into the internal data buffers ; ; X = sprite index -_DrawSpriteSheet +; _DrawSpriteSheet DISP_VFLIP equ $0004 ; hard code these because they are internal values DISP_HFLIP equ $0002 DISP_MASK equ $0018 ; Isolate the size bits - phx - - lda _Sprites+VBUFF_ADDR,x - sta tmp1 - - lda _Sprites+TILE_DATA_OFFSET,x - sta tmp2 - - lda _Sprites+SPRITE_DISP,x - and #DISP_MASK ; dispatch to all of the different orientations - sta tmp3 +; phx +; +; lda _Sprites+VBUFF_ADDR,x +; sta tmp1 +; +; lda _Sprites+TILE_DATA_OFFSET,x +; sta tmp2 +; +; lda _Sprites+SPRITE_DISP,x +; and #DISP_MASK ; dispatch to all of the different orientations +; sta tmp3 +; +; jsr _DSSCommon +; +; plx +; rts +_DSSCommon ; Set bank phb pea #^tiledata ; Set the bank to the tile data @@ -58,8 +77,6 @@ DISP_MASK equ $0018 ; Isolate the size bits ; Restore bank plb ; pop extra byte plb - - plx rts ; ; X = _Sprites array offset diff --git a/src/blitter/Tables.s b/src/blitter/Tables.s index 8493199..44bf70c 100644 --- a/src/blitter/Tables.s +++ b/src/blitter/Tables.s @@ -254,8 +254,16 @@ NextCol ; A double-sized table of lookup values. This is basically the cross-product of TileStoreYTable and ; NextCol. If is double-width and double-height so that, if we know a tile's address position -; of (X + 41*Y), then any relative tile store address can be looked up by adding a constan value. -;TileStore2DLookup ds {26*41*2}*4 +; of (X + 41*Y), then any relative tile store address can be looked up by adding a constant value. +; +; 50 rows by 80 columns + 2 extra rows and columns +TS_LOOKUP_WIDTH equ 80 +TS_LOOKUP_HEIGHT equ 50 +TS_LOOKUP_SPAN equ {TS_LOOKUP_WIDTH+2} +TS_LOOKUP_ROWS equ {TS_LOOKUP_HEIGHT+2} + +TileStoreLookupYTable ds {TS_LOOKUP_HEIGHT*2} +TileStoreLookup ds {TS_LOOKUP_SPAN*TS_LOOKUP_ROWS*2} ; This is a double-length table that holds the right-edge adresses of the playfield on the physical ; screen. At most, it needs to hold 200 addresses for a full height playfield. It is double-length @@ -296,7 +304,5 @@ BG1YOffsetTable lup 26 dw 1,1,1,2,2,2,2,2,1,1,1,0,0,0,0,0 --^ - - - - +; Table of base VBUFF addresses for each sprite stamp slot +VBuffAddrTable ds 2*VBUFF_SLOT_COUNT \ No newline at end of file diff --git a/src/blitter/Template.s b/src/blitter/Template.s index ac7662b..82446a8 100644 --- a/src/blitter/Template.s +++ b/src/blitter/Template.s @@ -160,7 +160,7 @@ SetScreenRect sty ScreenHeight ; Save the screen height and ; Generalized routine that calculates the on-screen address of the tiles and takes the ; StartX and StartY values into consideration. This routine really exists to support -; the dirty tile rendering mode and the tiles *must* be aligned with the playfield. +; the dirty tile rendering mode and the tiles *must* be aligned with the playfield. ; That is, StartX % 4 == 0 and StartY % 8 == 0. If these conditions are not met, then ; screen will not render correctly. _RecalcTileScreenAddrs diff --git a/src/blitter/Tiles.s b/src/blitter/Tiles.s index ba76c3f..45c45ac 100644 --- a/src/blitter/Tiles.s +++ b/src/blitter/Tiles.s @@ -41,17 +41,6 @@ TILE_CTRL_MASK equ $FE00 TILE_PROC_MASK equ $F800 ; Select tile proc for rendering -; Temporary direct page locatinos used by some of the complex 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 -_SPR_X_REG equ tiletmp+8 ; Cache address of sprite plane source for a tile -_JTBL_CACHE equ tiletmp+10 ; Cache the offset to the exception handler for a column -_OP_CACHE equ tiletmp+12 ; Cache of a relevant operand / oeprator -_TILE_ID equ tiletmp+14 ; Copy of the tile descriptor - ; 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 ; incremental calculations, but it is handy for other utility functions @@ -113,56 +102,36 @@ _RenderTileBG1 ; 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. ; +; This routine sets the direct page register to the second page since we use that space to +; build and cache tile and sprite data, when necessary ; Y = address of tile _RenderTile2 - pea >TileStore ; Need that addressing flexibility here. Caller is responsible for restoring bank reg - plb - plb - txy ; We can be better than this.... + lda TileStore+TS_SPRITE_FLAG,x ; This is a bitfield of all the sprites that intersect this tile, only care if non-zero or not + bne do_dirty_sprite - lda TileStore+TS_TILE_ID,y ; build the finalized tile descriptor - ldx TileStore+TS_SPRITE_FLAG,y ; This is a bitfield of all the sprites that intersect this tile, only care if non-zero or not - beq :nosprite +; Handle the non-sprite tile blit -; txa -; jsr BuildActiveSpriteArray ; Build the max 4 array of active sprites for this tile -; sta ActiveSpriteCount + sep #$20 + lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line + pha ; and put on the stack for later - lda TileStore+TS_VBUFF_ARRAY_ADDR,y ; Scratch space - sta _SPR_X_REG - phy - ldy spriteIdx - lda (_SPR_X_REG),y - sta _SPR_X_REG - ply + lda TileStore+TS_BASE_ADDR+1,x ; load the base address of the code field ($0000 or $8000) + sta _BASE_ADDR+1 ; so we can get by just copying the high byte + rep #$20 - lda TileStore+TS_TILE_ID,y - ora #TILE_SPRITE_BIT -; ldx TileStore+TS_VBUFF_ARRAY_ADDR,y -; stx _SPR_X_REG - -:nosprite - sta _TILE_ID ; Some tile blitters need to get the tile descriptor - and #TILE_CTRL_MASK - xba - tax - ldal TileProcs,x ; load and patch in the appropriate subroutine + lda TileStore+TS_BASE_TILE_DISP,x ; Get the address of the renderer for this tile stal :tiledisp+1 - ldx TileStore+TS_TILE_ADDR,y ; load the address of this tile's data (pre-calculated) + lda TileStore+TS_TILE_ID,x + sta _TILE_ID ; Some tile blitters need to get the tile descriptor - sep #$20 ; load the bank of the target code field line - lda TileStore+TS_CODE_ADDR_HIGH,y + 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) pha - rep #$20 - lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field - pha - lda TileStore+TS_BASE_ADDR,y ; load the base address of the code field - sta _BASE_ADDR - lda TileStore+TS_WORD_OFFSET,y - ply - plb ; set the bank + lda TileStore+TS_WORD_OFFSET,x + plx + plb ; set the bank to the code field that will be updated ; B is set to the correct code field bank ; A is set to the tile word offset (0 through 80 in steps of 4) @@ -171,6 +140,194 @@ _RenderTile2 :tiledisp jmp $0000 ; render the tile +; Let's make a macro helper for the bit test tree +; dobit src_offset,dest,next_target,end_target +dobit MAC + beq last_bit + ldx: ]1,y + stx ]2 + jmp ]3 +last_bit ldx: ]1,y + stx ]2 + jmp ]4 + EOM + +; The sprite code is just responsible for quickly copying all of the sprite data +; into the direct page temp area. + +do_dirty_sprite + pei TileStoreBankAndTileDataBank ; Special value that has the TileStore bank in LSB and TileData bank in MSB + plb + +; Cache a couple of values into the direct page, but preserve the Accumulator + + ldy TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) + sty tileAddr + +; This is very similar to the code in the dirty tile renderer, but we can't reuse +; because that code draws directly to the graphics screen, and this code draws +; to a temporary budder that has a different stride. + + ldy TileStore+TS_VBUFF_ARRAY_ADDR,x ; base address of the VBUFF sprite address array for this tile + + lsr + bcc :loop_0_bit_1 + dobit $0000;sprite_ptr0;:loop_1_bit_1;CopyOneSprite + +:loop_0_bit_1 lsr + bcc :loop_0_bit_2 + dobit $0002;sprite_ptr0;:loop_1_bit_2;CopyOneSprite + +:loop_0_bit_2 lsr + bcc :loop_0_bit_3 + dobit $0004;sprite_ptr0;:loop_1_bit_3;CopyOneSprite + +:loop_0_bit_3 lsr + bcc :loop_0_bit_4 + dobit $0006;sprite_ptr0;:loop_1_bit_4;CopyOneSprite + +:loop_0_bit_4 lsr + bcc :loop_0_bit_5 + dobit $0008;sprite_ptr0;:loop_1_bit_5;CopyOneSprite + +:loop_0_bit_5 lsr + bcc :loop_0_bit_6 + dobit $000A;sprite_ptr0;:loop_1_bit_6;CopyOneSprite + +:loop_0_bit_6 lsr + bcc :loop_0_bit_7 + dobit $000C;sprite_ptr0;:loop_1_bit_7;CopyOneSprite + +:loop_0_bit_7 lsr + bcc :loop_0_bit_8 + dobit $000E;sprite_ptr0;:loop_1_bit_8;CopyOneSprite + +:loop_0_bit_8 lsr + bcc :loop_0_bit_9 + dobit $0010;sprite_ptr0;:loop_1_bit_9;CopyOneSprite + +:loop_0_bit_9 lsr + bcc :loop_0_bit_10 + ldx: $0012,y + stx spriteIdx + cmp #0 + jne :loop_1_bit_10 + jmp CopyOneSprite + +:loop_0_bit_10 lsr + bcc :loop_0_bit_11 + dobit $0014;sprite_ptr0;:loop_1_bit_11;CopyOneSprite + +:loop_0_bit_11 lsr + bcc :loop_0_bit_12 + dobit $0016;sprite_ptr0;:loop_1_bit_12;CopyOneSprite + +:loop_0_bit_12 lsr + bcc :loop_0_bit_13 + dobit $0018;sprite_ptr0;:loop_1_bit_13;CopyOneSprite + +:loop_0_bit_13 lsr + bcc :loop_0_bit_14 + dobit $001A;sprite_ptr0;:loop_1_bit_14;CopyOneSprite + +:loop_0_bit_14 lsr + bcc :loop_0_bit_15 + dobit $001C;sprite_ptr0;:loop_1_bit_15;CopyOneSprite + +:loop_0_bit_15 ldx: $001E,y + stx spriteIdx + jmp CopyOneSprite + +; We can optimize later, for now just copy the sprite data and mask into its own +; direct page buffer and combine with the tile data later + +; We set up direct page pointers to the mask bank and use the bank register for the +; data. +CopyFourSpritesAbove + +; Copy three sprites into a temporary direct page buffer +LDA_IL equ $A7 ; lda [dp] +LDA_ILY equ $B7 ; lda [dp],y +AND_IL equ $27 ; and [dp] +AND_ILY equ $37 ; and [dp],y + +CopyThreeSprites +]line equ 0 + lup 8 + ldy #]line*SPRITE_PLANE_SPAN + lda (spriteIdx+8),y + db AND_ILY,spriteIdx+4 ; Can't use long indirect inside LUP because of ']' + ora (spriteIdx+4),y + db AND_ILY,spriteIdx+0 + ora (spriteIdx+0),y + sta tmp_sprite_data+{]line*4} + + db LDA_ILY,spriteIdx+8 + db AND_ILY,spriteIdx+4 + db AND_ILY,spriteIdx+0 + sta tmp_sprite_mask+{]line*4} + + ldy #]line*SPRITE_PLANE_SPAN+2 + lda (spriteIdx+8),y + db AND_ILY,spriteIdx+4 + ora (spriteIdx+4),y + db AND_ILY,spriteIdx+0 + ora (spriteIdx+0),y + sta tmp_sprite_data+{]line*4}+2 + + db LDA_ILY,spriteIdx+8 + db AND_ILY,spriteIdx+4 + db AND_ILY,spriteIdx+0 + sta tmp_sprite_mask+{]line*4}+2 +]line equ ]line+1 + --^ +; jmp FinishTile + +; Copy two sprites into a temporary direct page buffer +CopyTwoSprites +]line equ 0 + lup 8 + ldy #]line*SPRITE_PLANE_SPAN + lda (spriteIdx+4),y + db AND_ILY,spriteIdx+0 + ora (spriteIdx+0),y + sta tmp_sprite_data+{]line*4} + + db LDA_ILY,spriteIdx+4 + db AND_ILY,spriteIdx+0 + sta tmp_sprite_mask+{]line*4} + + ldy #]line*SPRITE_PLANE_SPAN+2 + lda (spriteIdx+4),y + db AND_ILY,spriteIdx+0 + ora (spriteIdx+0),y + sta tmp_sprite_data+{]line*4}+2 + + db LDA_ILY,spriteIdx+4 + db AND_ILY,spriteIdx+0 + sta tmp_sprite_mask+{]line*4}+2 +]line equ ]line+1 + --^ +; jmp FinishTile + +; Copy a single piece of sprite data into a temporary direct page . X = spriteIdx +CopyOneSprite +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_data+{]line*4} + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_data+{]line*4}+2 + + ldal spritemask+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_mask+{]line*4} + ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_mask+{]line*4}+2 +]line equ ]line+1 + --^ + +; jmp FinishTile + ; Reference all of the tile rendering subroutines defined in the TileXXXXX files. Each file defines ; 8 entry points: ; @@ -518,7 +675,7 @@ _CopyBG1Tile ; 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_DIRTY : $0000 is clean, any other value indicated a dirty tile ; 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 @@ -590,11 +747,14 @@ InitTiles lda #0 stal TileStore+TS_TILE_ID,x ; clear the tile store with the special zero tile stal TileStore+TS_TILE_ADDR,x - stal TileStore+TS_TILE_DISP,x - stal TileStore+TS_SPRITE_FLAG,x ; no sprites are set at the beginning - lda #$FFFF ; none of the tiles are dirty - stal TileStore+TS_DIRTY,x + stal TileStore+TS_DIRTY,x ; none of the tiles are dirty + + 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 lda :vbuff ; array of sprite vbuff addresses per tile stal TileStore+TS_VBUFF_ARRAY_ADDR,x @@ -700,7 +860,16 @@ _SetTile ldal TileStore+TS_TILE_ID,x and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value xba - stal TileStore+TS_TILE_DISP,x + tay + lda DirtyTileProcs,y + stal TileStore+TS_DIRTY_TILE_DISP,x + + ldal TileStore+TS_TILE_ID,x ; Get the non-sprite dispatch address + and #TILE_CTRL_MASK + xba + tay + lda TileProcs,y + stal TileStore+TS_BASE_TILE_DISP,x ; txa ; Add this tile to the list of dirty tiles to refresh jmp _PushDirtyTileX ; on the next call to _ApplyTiles @@ -731,11 +900,12 @@ _PushDirtyTile ; alternate entry point if the x-register is already set _PushDirtyTileX ldal TileStore+TS_DIRTY,x - bpl :occupied2 + bne :occupied2 - txa ; any non-negative value will work, this saves work below + inc ; any non-zero value will work stal TileStore+TS_DIRTY,x ; and is 1 cycle faster than loading a constant value + txa ldx DirtyTileCount ; 4 sta DirtyTiles,x ; 6 inx ; 2 @@ -783,7 +953,15 @@ ApplyTiles ENT plb rtl +; 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 @@ -801,4 +979,124 @@ _ApplyTiles :begin ldy DirtyTileCount bne :loop - rts \ No newline at end of file + + tdc ; Move back to the original direct page + sec + sbc #$100 + tcd + rts + +; To make processing the tile faster, we do them in chunks of eight. This allows the loop to be +; unrolled, which means we don't have to keep track of the register value and makes it faster to +; clear the dirty tile flag after being processed. + + tdc ; Move to the dedicated direct page for tile rendering + clc + adc #$100 + tcd + + phb ; Save the current bank + tsc + sta tmp0 ; Save it on the direct page + bra at_loop + +; The DirtyTiles array and the TileStore information is in the Tile Store bank. Because we +; process up to 8 tiles as a time and the tile code sets the bank register to the target +; code field bank, we need to restore the bank register each time. So, we pre-push +; 8 copies of the TileStore bank onto the stack. + + +at_exit + tdc ; Move back to the original direct page + sec + sbc #$100 + tcd + + plb ; Restore the original data bank and return + rts +dt_base equ $FE ; top of second direct page space + +at_loop + lda tmp0 + tcs + + lda DirtyTileCount ; This is pre-multiplied by 2 + beq at_exit ; If there are no items, exit + + ldx TileStoreBankDoubled + phx + phx + phx + + cmp #16 ; If there are >= 8 elements, then + bcs at_chunk ; do a full chunk + + stz DirtyTileCount ; Otherwise, this pass will handle them all + tax + jmp (at_table,x) +at_table da at_exit,at_one,at_two,at_three + da at_four,at_five,at_six,at_seven + +at_chunk sec + sbc #16 + sta DirtyTileCount ; Fall through + +; Because all of the registers get used in the _RenderTile2 subroutine, we +; push the values from the DirtyTiles array onto the stack and then pop off +; the values as we go + + ldy dt_base ; Reload the base index + ldx DirtyTiles+14,y ; Load the TileStore offset + stz TileStore+TS_DIRTY,x ; Clear this tile's dirty flag + jsr _RenderTile2 ; Draw the tile + plb ; Reset the data bank to the TileStore bank + +at_seven + ldy dt_base + ldx DirtyTiles+12,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile2 + plb + +at_six + ldy dt_base + ldx DirtyTiles+10,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile2 + plb + +at_five + ldy dt_base + ldx DirtyTiles+8,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile2 + plb + +at_four + ldy dt_base + ldx DirtyTiles+6,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile2 + plb + +at_three + ldy dt_base + ldx DirtyTiles+4,y + jsr _RenderTile2 + plb + +at_two + ldy dt_base + ldx DirtyTiles+2,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile2 + plb + +at_one + ldy dt_base + ldx DirtyTiles+0,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile2 + plb + + jmp at_loop diff --git a/src/blitter/Tiles10000.s b/src/blitter/Tiles10000.s index df1b0ab..8e761d3 100644 --- a/src/blitter/Tiles10000.s +++ b/src/blitter/Tiles10000.s @@ -46,6 +46,47 @@ _TBApplySpriteData --^ rts +_TBApplySpriteDataOne + ldx spriteIdx +]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 + --^ + rts + +_TBApplySpriteDataTwo +]line equ 0 + lup 8 + lda blttmp+{]line*4} + ldx spriteIdx+2 + andl spritemask+{]line*SPRITE_PLANE_SPAN},x + oral spritedata+{]line*SPRITE_PLANE_SPAN},x + ldx spriteIdx + andl spritemask+{]line*SPRITE_PLANE_SPAN},x + oral spritedata+{]line*SPRITE_PLANE_SPAN},x + sta: $0004+{]line*$1000},y + + lda blttmp+{]line*4}+2 + ldx spriteIdx+2 + andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x + oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + ldx spriteIdx + 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 + --^ + 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. From e2710ace8530de0b47adc36fd78ed0926400a34c Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sat, 23 Apr 2022 00:47:13 -0500 Subject: [PATCH 03/82] Refactor to support building a tool and new demo to try and use it [not working] --- build-image.bat | 18 ++ demos/tool/App.Main.s | 83 +++++++ demos/tool/App.s | 10 + demos/tool/GTEToolDemo | Bin 0 -> 243 bytes demos/tool/_FileInformation.txt | 1 + demos/tool/build-image.bat | 19 ++ demos/tool/package.json | 30 +++ package.json | 29 +++ src/Core.s | 408 +------------------------------- src/CoreImpl.s | 369 +++++++++++++++++++++++++++++ src/Defs.s | 4 +- src/Graphics.s | 76 ++++++ src/Master.s | 34 +++ src/Sprite.s | 4 +- src/Sprite2.s | 43 ---- src/Tool.s | 101 +++++--- src/_FileInformation.txt | 2 + src/blitter/Tables.s | 33 +-- src/blitter/Template.s | 1 - src/blitter/Tiles.s | 93 +++++++- src/static/SprData.s | 3 + src/static/SprMask.s | 3 + src/static/TileData.s | 3 + src/static/TileStore.s | 3 + 24 files changed, 868 insertions(+), 502 deletions(-) create mode 100644 build-image.bat create mode 100644 demos/tool/App.Main.s create mode 100644 demos/tool/App.s create mode 100644 demos/tool/GTEToolDemo create mode 100644 demos/tool/_FileInformation.txt create mode 100644 demos/tool/build-image.bat create mode 100644 demos/tool/package.json create mode 100644 package.json create mode 100644 src/CoreImpl.s create mode 100644 src/Master.s create mode 100644 src/_FileInformation.txt create mode 100644 src/static/SprData.s create mode 100644 src/static/SprMask.s create mode 100644 src/static/TileData.s create mode 100644 src/static/TileStore.s diff --git a/build-image.bat b/build-image.bat new file mode 100644 index 0000000..343d3fb --- /dev/null +++ b/build-image.bat @@ -0,0 +1,18 @@ +echo off + +REM Copy all of the assets into the ProDOS image for emulator testing +REM +REM Pass the path of the Cadius tool as the first argument (%1) + +set CADIUS="%1" +set IMAGE=".\\emu\\Target.2mg" +set FOLDER="/GTEDEV/Toolbox" + +REM Cadius does not overwrite files, so clear the root folder first +%CADIUS% DELETEFOLDER %IMAGE% %FOLDER% +%CADIUS% CREATEFOLDER %IMAGE% %FOLDER% + +REM Now copy files and folders as needed +%CADIUS% ADDFILE %IMAGE% %FOLDER% .\src\GTETool + +REM Copy in the image assets diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s new file mode 100644 index 0000000..7f89d59 --- /dev/null +++ b/demos/tool/App.Main.s @@ -0,0 +1,83 @@ + REL + DSK MAINSEG + + use Locator.Macs + use Load.Macs + use Mem.Macs + use Misc.Macs + use Util.Macs + use EDS.GSOS.Macs + +_GTEStartUp MAC + UserTool $02A0 + <<< + +; Typical init + phk + plb + + jsr ToolStartUp ; Start up the basic tools: Locator, Memory Manager, Misc + jsr GTEStartUp + + _QuitGS qtRec + + bcs Fatal +Fatal brk $00 +qtRec adrl $0000 + da $00 + +ToolStartUp + _TLStartUp ; normal tool initialization + pha + _MMStartUp + pla + sta MasterId ; our master handle references the memory allocated to us + ora #$0100 ; set auxID = $01 (valid values $01-0f) + sta UserId ; any memory we request must use our own id + + _MTStartUp + rts + +; Load the GTE User Tool and register it +GTEStartUp + pea $0000 + pea $0000 + pea $0000 + pea $0000 + pea $0000 ; result space + + lda UserId + pha + + pea #^ToolPath + pea #ToolPath + pea $0001 ; do not load into special memory + pea $0001 ; GS/OS string for the argument + _InitialLoad2 + + 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 + + clc ; Give GTE a page of direct page memory + tdc + adc #$0100 + pha + lda UserId + pha + _GTEStartUp + + rts + + +MasterId ds 2 +UserId ds 2 +ToolPath strl '9:GTETool' \ No newline at end of file diff --git a/demos/tool/App.s b/demos/tool/App.s new file mode 100644 index 0000000..3bd4b35 --- /dev/null +++ b/demos/tool/App.s @@ -0,0 +1,10 @@ +; IIgs Sprite Testbed + + TYP $B3 ; S16 file + DSK GTEToolDemo + XPL + +; Segment #1 -- Main execution block + + ASM App.Main.s + SNA Main diff --git a/demos/tool/GTEToolDemo b/demos/tool/GTEToolDemo new file mode 100644 index 0000000000000000000000000000000000000000..466b7a7db08bbb37269ef4430eb4e29265038fc7 GIT binary patch literal 243 zcmey&009#~6c-B<0|O%ig8) 8) - -ScreenModeWidth dw 320,272,256,256,280,256,240,288,160,288,160,320 -ScreenModeHeight dw 200,192,200,176,160,160,160,128,144,192,102,1 - -SetScreenMode ENT - phb - phk - plb - jsr _SetScreenMode - plb - rtl - -_SetScreenMode - cpx #11 - bcs :direct ; if x > 10, then assume X and Y are the dimensions - - txa - asl - tax - - ldy ScreenModeHeight,x - lda ScreenModeWidth,x - tax - -:direct cpy #201 - bcs :exit - - cpx #321 - bcs :exit - - txa - lsr - pha ; Save X (width / 2) and Y (height) - phy - - lda #160 ; Center the screen - sec - sbc 3,s - lsr - xba - pha ; Save half the origin coordinate - - lda #200 - sec - sbc 3,s ; This is now Y because of the PHA above - lsr - ora 1,s - - plx ; Throw-away to pop the stack - ply - plx - - jsr SetScreenRect - jmp FillScreen ; tail return -:exit - rts - - -WaitForKey sep #$20 - stal KBD_STROBE_REG ; clear the strobe -:WFK ldal KBD_REG - bpl :WFK - rep #$20 - and #$007F - rts - -ClearKbdStrobe sep #$20 - stal KBD_STROBE_REG - rep #$20 - rts - -; Read the keyboard and paddle controls and return in a game-controller-like format -LastKey db 0 -ReadControl ENT - jsr _ReadControl - rtl - -_ReadControl - pea $0000 ; low byte = key code, high byte = %------AB - - sep #$20 - ldal OPTION_KEY_REG ; 'B' button - and #$80 - beq :BNotDown - - lda #PAD_BUTTON_B - ora 2,s - sta 2,s - -:BNotDown - ldal COMMAND_KEY_REG - and #$80 - beq :ANotDown - - lda #PAD_BUTTON_A - ora 2,s - sta 2,s - -:ANotDown - ldal KBD_STROBE_REG ; read the keyboard - bit #$80 - beq :KbdNotDwn ; check the key-down status - and #$7f - ora 1,s - sta 1,s - - cmpl LastKey - beq :KbdDown - stal LastKey - - lda #PAD_KEY_DOWN ; set the keydown flag - ora 2,s - sta 2,s - bra :KbdDown - -:KbdNotDwn - lda #0 - stal LastKey -:KbdDown - rep #$20 - pla +ToolShutDown rts + put CoreImpl.s put blitter/Template.s put Memory.s diff --git a/src/CoreImpl.s b/src/CoreImpl.s new file mode 100644 index 0000000..b4708ed --- /dev/null +++ b/src/CoreImpl.s @@ -0,0 +1,369 @@ +; Feature flags +NO_INTERRUPTS equ 1 ; turn off for crossrunner debugging +NO_MUSIC equ 1 ; turn music + tool loading off + +; External data space provided by the main program segment +tiledata EXT +TileStore EXT + +; Sprite plane data and mask banks are provided as an external segment +; +; The sprite data holds a set of pre-rendered sprites that are optimized to support the rendering pipeline. There +; are four copies of each sprite, along with the cooresponding mask laid out into 4x4 tile regions where the +; empty row and column is shared between adjacent blocks. +; +; Logically, the memory is laid out as 4 columns of sprites and 4 rows. +; +; +---+---+---+---+---+---+---+---+---+---+---+---+-... +; | | | | | | | | | | | | | ... +; +---+---+---+---+---+---+---+---+---+---+---+---+-... +; | | 0 | 0 | | 1 | 1 | | 2 | 2 | | 3 | 3 | ... +; +---+---+---+---+---+---+---+---+---+---+---+---+-... +; | | 0 | 0 | | 1 | 1 | | 2 | 2 | | 3 | 3 | ... +; +---+---+---+---+---+---+---+---+---+---+---+---+-... +; | | | | | | | | | | | | | ... +; +---+---+---+---+---+---+---+---+---+---+---+---+-... +; | | 4 | 4 | | 5 | 5 | | 6 | 6 | | 7 | 7 | ... +; +---+---+---+---+---+---+---+---+---+---+---+---+-... +; | | 4 | 4 | | 5 | 5 | | 6 | 6 | | 7 | 7 | ... +; +---+---+---+---+---+---+---+---+---+---+---+---+-... +; | | | | | | | | | | | | | ... +; +---+---+---+---+---+---+---+---+---+---+---+---+-... +; +; For each sprite, when it needs to be copied into an on-screen tile, it could exist at any offset compared to its +; natural alignment. By having a buffer around the sprite data, an address pointer can be set to a different origin +; and a simple 8x8 block copy can cut out the appropriate bit of the sprite. For example, here is a zoomed-in look +; at a sprite with an offset, O, at (-2,-3). As shown, by selecting an appropriate origin, just the top corner +; of the sprite data will be copied. +; +; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+.. +; | | || | || | | | || | | | | +; +---+-- O----------------+ --+---++---+---+---+---++---+---+---+---+.. +; | | | | | || | | | || | | | | +; +---+-- | | --+---++---+---+---+---++---+---+---+---+.. +; | | | | | || | | | || | | | | +; +---+-- | | --+---++---+---+---+---++---+---+---+---+.. +; | | | | | || | | | || | | | | +; +===+== | ++===+== | ==+===++===+===+===+===++===+===+===+===+.. +; | | | || | S | S | S || S | S | S | || | | | | +; +---+-- +----------------+ --+---++---+---+---+---++---+---+---+---+.. +; | | || S | S S | S || S | S | S | S || | | | | +; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+.. +; | | | | || S | S | S | S || S | S | S | S || | | | | +; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+.. +; | | | | || S | S | S | S || S | S | S | S || | | | | +; +===+===+===+===++===+===+===+===++===+===+===+===++===+===+===+===+.. +; | | | | || S | S | S | S || S | S | S | S || | | | | +; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+.. +; | | | | || S | S | S | S || S | S | S | S || | | | | +; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+.. +; | | | | || S | S | S | S || S | S | S | S || | | | | +; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+.. +; | | | | || | S | S | S || S | S | S | || | | | | +; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+.. +; . . . . . . . . . . . . . . . . . +; +; Each sprite will take up, effectively 9 tiles of storage space per +; instance (plus edges) and there are 4 instances for the H/V bits +; and 4 more for the masks. This results in a need for 43,264 bytes +; for all 16 sprites. + +spritedata EXT +spritemask EXT + +; If there are overlays, they are provided as an external +Overlay EXT + +; Core engine functionality. The idea is that that source file can be PUT into +; a main source file and all of the functionality will be available. +; +; There are some constancts that must be externally defined that can affect how +; the GTE runtime works +; +; NO_MUSIC : Set to non-zero to avoid using any source +; NO_INTERRUPTS : Set to non-zero to avoid installing custom interrupt handlers + + mx %00 + +; A = memory manager userId +; X = tool number +_CoreStartUp + sta UserId ; This is the first thing to do + stx ToolNum + + jsr IntStartUp ; Enable certain iterrupts + + jsr InitMemory ; Allocate and initialize memory for the engine + jsr EngineReset ; All of the resources are allocated, put the engine in a known state + +; jsr InitGraphics ; Initialize all of the graphics-related data +; jsr InitSprites ; Initialize the sprite subsystem +; jsr InitTiles ; Initialize the tile subsystem + + jsr InitTimers ; Initialize the timer subsystem + +_CoreShutDown + jsr IntShutDown + rts + +; Install interrupt handlers. We use the VBL interrupt to keep animations +; moving at a consistent rate, regarless of the rendered frame rate. The +; one-second timer is generally just used for counters and as a handy +; frames-per-second trigger. +IntStartUp + lda #NO_INTERRUPTS + bne :no_interrupts + PushLong #0 + pea $0015 ; Get the existing 1-second interrupt handler and save + _GetVector + PullLong OldOneSecVec + + pea $0015 ; Set the new handler and enable interrupts + PushLong #OneSecHandler + _SetVector + + pea $0006 + _IntSource + + PushLong #VBLTASK ; Also register a Heart Beat Task + _SetHeartBeat + +:no_interrupts + rts + +IntShutDown + lda #NO_INTERRUPTS + bne :no_interrupts + + pea $0007 ; disable 1-second interrupts + _IntSource + + PushLong #VBLTASK ; Remove our heartbeat task + _DelHeartBeat + + pea $0015 + PushLong OldOneSecVec ; Reset the interrupt vector + _SetVector + +:no_interrupts + rts + + +; Interrupt handlers. We install a heartbeat (1/60th second and a 1-second timer) +OneSecHandler mx %11 + phb + pha + phk + plb + + rep #$20 + inc OneSecondCounter + sep #$20 + + ldal $E0C032 + and #%10111111 ;clear IRQ source + stal $E0C032 + + pla + plb + clc + rtl + mx %00 + +OneSecondCounter ENT + dw 0 +OldOneSecVec ds 4 + +VBLTASK hex 00000000 + dw 0 + hex 5AA5 + +; Reset the engine to a known state +; Blitter initialization +EngineReset + stz ScreenHeight + stz ScreenWidth + stz ScreenY0 + stz ScreenY1 + stz ScreenX0 + stz ScreenX1 + stz ScreenTileHeight + stz ScreenTileWidth + stz StartX + stz OldStartX + stz StartXMod164 + + stz StartY + stz OldStartY + stz StartYMod208 + + stz EngineMode + stz DirtyBits + stz LastRender + stz LastPatchOffset + stz BG1StartX + stz BG1StartXMod164 + stz BG1StartY + stz BG1StartYMod208 + stz BG1OffsetIndex + + stz BG0TileOriginX + stz BG0TileOriginY + stz OldBG0TileOriginX + stz OldBG0TileOriginY + + stz BG1TileOriginX + stz BG1TileOriginY + stz OldBG1TileOriginX + stz OldBG1TileOriginY + + stz TileMapWidth + stz TileMapHeight + stz TileMapPtr + stz TileMapPtr+2 + stz FringeMapPtr + stz FringeMapPtr+2 + + stz BG1TileMapWidth + stz BG1TileMapHeight + stz BG1TileMapPtr + stz BG1TileMapPtr+2 + + stz SCBArrayPtr + stz SCBArrayPtr+2 + + stz SpriteBanks + stz SpriteMap + stz ActiveSpriteCount + + stz OneSecondCounter + + lda #13 + sta tmp15 + stz tmp14 + +:loop + ldx #BlitBuff + lda #^BlitBuff + ldy tmp14 +; jsr BuildBank + + lda tmp14 + clc + adc #4 + sta tmp14 + + dec tmp15 + bne :loop + + rts + + +WaitForKey sep #$20 + stal KBD_STROBE_REG ; clear the strobe +:WFK ldal KBD_REG + bpl :WFK + rep #$20 + and #$007F + rts + +ClearKbdStrobe sep #$20 + stal KBD_STROBE_REG + rep #$20 + rts + +; Read the keyboard and paddle controls and return in a game-controller-like format +LastKey db 0 +ReadControl ENT + jsr _ReadControl + rtl + +_ReadControl + pea $0000 ; low byte = key code, high byte = %------AB + + sep #$20 + ldal OPTION_KEY_REG ; 'B' button + and #$80 + beq :BNotDown + + lda #PAD_BUTTON_B + ora 2,s + sta 2,s + +:BNotDown + ldal COMMAND_KEY_REG + and #$80 + beq :ANotDown + + lda #PAD_BUTTON_A + ora 2,s + sta 2,s + +:ANotDown + ldal KBD_STROBE_REG ; read the keyboard + bit #$80 + beq :KbdNotDwn ; check the key-down status + and #$7f + ora 1,s + sta 1,s + + cmpl LastKey + beq :KbdDown + stal LastKey + + lda #PAD_KEY_DOWN ; set the keydown flag + ora 2,s + sta 2,s + bra :KbdDown + +:KbdNotDwn + lda #0 + stal LastKey +:KbdDown + rep #$20 + pla + rts + + +; Helper function to take a local pixel coordinate [0, ScreenWidth-1],[0, ScreenHeight-1] and return the +; row and column in the tile store that is corresponds to. This takes into consideration the StartX and +; StartY offsets. +; +; This is more specialized than the code in the _MarkDirtySprite routine below since it does not deal with +; negative or off-screen values. +_OriginToTileStore + lda StartYMod208 + lsr + lsr + and #$FFFE ; Store the pre-multiplied by 2 for indexing + tay + lda StartXMod164 + lsr + and #$FFFE ; Same pre-multiply by 2 for later + tax + rts + +; X = local x-coordinate (0, playfield width) +; Y = local y-coordinate (0, playfield height) +_LocalToTileStore + clc + tya + adc StartYMod208 ; Adjust for the scroll offset + cmp #208 ; check if we went too far positive + bcc *+5 + sbc #208 + lsr + lsr + and #$FFFE ; Store the pre-multiplied by 2 for indexing + tay + + clc + txa + adc StartXMod164 + cmp #164 + bcc *+5 + sbc #164 + lsr + and #$FFFE ; Same pre-multiply by 2 for later + tax + rts \ No newline at end of file diff --git a/src/Defs.s b/src/Defs.s index d2d8017..2f5e12c 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -88,7 +88,9 @@ BankLoad equ 104 TileStoreBankAndBank01 equ 106 TileStoreBankAndTileDataBank equ 108 TileStoreBankDoubled equ 110 -Next equ 112 +UserId equ 112 ; Memory manager user Id to use +ToolNum equ 114 ; Tool number assigned to us +Next equ 116 activeSpriteList equ 128 ; 32 bytes for the active sprite list (can persist across frames) ; tiletmp equ 178 ; 16 bytes of temp storage for the tile renderers diff --git a/src/Graphics.s b/src/Graphics.s index 5fe5f94..1ef1656 100644 --- a/src/Graphics.s +++ b/src/Graphics.s @@ -24,6 +24,82 @@ DefaultPalette dw $0000,$007F,$0090,$0FF0 dw $0fa9,$0ff0,$00e0,$04DF dw $0d00,$078f,$0ccc,$0FFF + +; Allow the user to dynamically select one of the pre-configured screen sizes, or pass +; in a specific width and height. The screen is automatically centered. If this is +; not desired, then SetScreenRect should be used directly +; +; 0. Full Screen : 40 x 25 320 x 200 (32,000 bytes (100.0%)) +; 1. Sword of Sodan : 34 x 24 272 x 192 (26,112 bytes ( 81.6%)) +; 2. ~NES : 32 x 25 256 x 200 (25,600 bytes ( 80.0%)) +; 3. Task Force : 32 x 22 256 x 176 (22,528 bytes ( 70.4%)) +; 4. Defender of the World : 35 x 20 280 x 160 (22,400 bytes ( 70.0%)) +; 5. Rastan : 32 x 20 256 x 160 (20,480 bytes ( 64.0%)) +; 6. Game Boy Advanced : 30 x 20 240 x 160 (19,200 bytes ( 60.0%)) +; 7. Ancient Land of Y's : 36 x 16 288 x 128 (18,432 bytes ( 57.6%)) +; 8. Game Boy Color : 20 x 18 160 x 144 (11,520 bytes ( 36.0%)) +; 9. Agony (Amiga) : 36 x 24 288 x 192 (27,648 bytes ( 86.4%)) +; 10. Atari Lynx : 20 x 13 160 x 102 (8,160 bytes ( 25.5%)) +; +; X = mode number OR width in pixels (must be multiple of 2) +; Y = height in pixels (if X > 8) + +ScreenModeWidth dw 320,272,256,256,280,256,240,288,160,288,160,320 +ScreenModeHeight dw 200,192,200,176,160,160,160,128,144,192,102,1 + +SetScreenMode ENT + phb + phk + plb + jsr _SetScreenMode + plb + rtl + +_SetScreenMode + cpx #11 + bcs :direct ; if x > 10, then assume X and Y are the dimensions + + txa + asl + tax + + ldy ScreenModeHeight,x + lda ScreenModeWidth,x + tax + +:direct cpy #201 + bcs :exit + + cpx #321 + bcs :exit + + txa + lsr + pha ; Save X (width / 2) and Y (height) + phy + + lda #160 ; Center the screen + sec + sbc 3,s + lsr + xba + pha ; Save half the origin coordinate + + lda #200 + sec + sbc 3,s ; This is now Y because of the PHA above + lsr + ora 1,s + + plx ; Throw-away to pop the stack + ply + plx + + jsr SetScreenRect + jmp FillScreen ; tail return +:exit + rts + ; Return the current border color ($0 - $F) in the accumulator _GetBorderColor lda #0000 sep #$20 diff --git a/src/Master.s b/src/Master.s new file mode 100644 index 0000000..223034c --- /dev/null +++ b/src/Master.s @@ -0,0 +1,34 @@ +; IIgs Generic Tile Engine User Toolset + + TYP $BA ; TOL file + DSK GTETool + XPL + +; Main toolbox interface and code + + ASM Tool.s + SNA Main + +; 64KB Tile Memory + + ASM static\TileData.s + KND #$1001 ; Type and Attributes ($10=Static,$01=Data) + SNA TDATA + +; 64KB Sprite Plane Data + + ASM static\SprData.s + KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data) + SNA SDATA + +;64KB Sprite Mask Data + + ASM static\SprMask.s + KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data) + SNA SMASK + +; 64KB Tile Store + + ASM static\TileStore.s + KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data) + SNA TSTORE diff --git a/src/Sprite.s b/src/Sprite.s index a3bcb89..b659ed8 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -62,9 +62,9 @@ InitSprites ; covering the exact same tiles. -; Render a sprite stamp into the sprite buffer. Stamps exits independent of the sprites +; Render a sprite stamp into the sprite buffer. Stamps exist independent of the sprites ; and sprite reference a specific stamp. This is necessary because it's common for a -; spite to change its graphic as its animating, but it is too costly to have to set up +; sprite to change its graphic as its animating, but it is too costly to have to set up ; the stamp every time. So this allows users to create stamps in advance and then ; assign them to the sprites as needed. ; diff --git a/src/Sprite2.s b/src/Sprite2.s index 9d83bea..404ade4 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -16,49 +16,6 @@ ColLeft equ tmp9 SpriteBit equ tmp10 ; set the bit of the value that if the current sprite index VBuffOrigin equ tmp11 -; Helper function to take a local pixel coordinate [0, ScreenWidth-1],[0, ScreenHeight-1] and return the -; row and column in the tile store that is corresponds to. This takes into consideration the StartX and -; StartY offsets. -; -; This is more specialized than the code in the _MarkDirtySprite routine below since it does not deal with -; negative or off-screen values. -_OriginToTileStore - lda StartYMod208 - lsr - lsr - and #$FFFE ; Store the pre-multiplied by 2 for indexing - tay - lda StartXMod164 - lsr - and #$FFFE ; Same pre-multiply by 2 for later - tax - rts - -; X = local x-coordinate (0, playfield width) -; Y = local y-coordinate (0, playfield height) -_LocalToTileStore - clc - tya - adc StartYMod208 ; Adjust for the scroll offset - cmp #208 ; check if we went too far positive - bcc *+5 - sbc #208 - lsr - lsr - and #$FFFE ; Store the pre-multiplied by 2 for indexing - tay - - clc - txa - adc StartXMod164 - cmp #164 - bcc *+5 - sbc #164 - lsr - and #$FFFE ; Same pre-multiply by 2 for later - tax - rts - ; Marks a sprite as dirty. The work here is mapping from local screen coordinates to the ; tile store indices. The first step is to adjust the sprite coordinates based on the current ; code field offsets and then cache variations of this value needed in the rest of the subroutine diff --git a/src/Tool.s b/src/Tool.s index c1ba0dd..2fdf11e 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -3,10 +3,19 @@ ; Ref: Toolbox Reference, Volume 2, Appendix A ; Ref: IIgs Tech Note #73 +; use Load.Macs.s + use Mem.Macs.s + use Misc.Macs.s + use Util.Macs + use Locator.Macs + use Core.MACS.s + + use Defs.s + ToStrip equ $E10184 _CallTable - dw {_CTEnd-_CallTable}/4,0 + adrl {_CTEnd-_CallTable}/4 adrl _TSBootInit-1 adrl _TSStartUp-1 adrl _TSShutDown-1 @@ -25,40 +34,58 @@ _TSBootInit clc rtl -; Call the regular GTE startup function after setting the Work Area Point (WAP). The caller much provide -; one page of Bank 0 memory for the tool set's private use +; Call the regular GTE startup function after setting the Work Area Pointer (WAP). The caller must provide +; one page of Bank 0 memory for the tool set's private use and a userId to use for allocating memory ; -; X = +; X = tool set number in low byte and function umber in high byte +; +; StartUp(dPageAddr, userId) _TSStartUp -:zpToUse equ 7 - pea #$8000 +rtll = 1 +rtl2 = rtl1+3 +userId = 7 +zpToUse = userId+2 + txa - and #$00FF - pha + and #$00FF ; Get just the tool number + tax - pea $0000 - lda :zpToUse+6,s - pha + lda userId,s ; Get the userId for memory allocations + tay + lda zpToUse,s ; Get the direct page address + + phd ; Save the current direct page + tcd ; Set to our working direct page space + + tya ; A = memory manager user Id, X = tool number + jsr _CoreStartUp ; Initialize the library + +; SetWAP(userOrSystem, tsNum, waptPtr) + + pea #$8000 ; $8000 = user tool set + pei ToolNum ; Push the tool number from the direct page + pea $0000 ; High word of WAP is zero (bank 0) + phd ; Low word of WAP is the direct page _SetWAP - jsr _CoreStartUp + pld ; Restore the caller's direct page + + lda #0 + clc + rtl _TSShutDown cmp #0 ; Acc is low word of the WAP (direct page) beq :inactive phd - pha - pld ; Set the direct page for the toolset + tcd ; Set the direct page for the toolset - phx ; Preserve the X register - jsr _CoreShutDown ; Shut down GTE - pla + jsr _CoreShutDown ; Shut down the library pea $8000 - and #$00FF - pha + pei ToolNum pea $0000 ; Set WAP to null pea $0000 _SetWAP @@ -71,7 +98,7 @@ _TSShutDown rtl _TSVersion - lda #$0100 ; Version 1 + lda #$0100 ; Version 1 sta 7,s lda #0 @@ -101,31 +128,45 @@ _TSReserved sec rtl +; SetScreenMode(width, height) _TSSetScreenMode - phd ; Preserve the direct page - pha - pld +:height equ 9 +:width equ 11 - lda 9,s + phd ; Preserve the direct page + tcd ; Set the tool set direct pafe from WAP + + lda :height,s tay - lda 9,s + lda :width,s tax - jsr _SetScreenMode - pld +; jsr _SetScreenMode ; Not implemented yet + + pld ; Restore direct page ldx #0 ; No error ldy #4 ; Remove the 4 input bytes jml ToStrip _TSReadControl +:output equ 9 + phd ; Preserve the direct page - pha - pld + tcd jsr _ReadControl - sta 9,s + sta :output,s pld ldx #0 ; No error ldy #0 ; Remove zero input bytes jml ToStrip + +; Insert the core GTE functions + + put CoreImpl.s + put Memory.s + put Timer.s +; put Graphics.s +; put blitter/Template.s + put blitter/Tables.s diff --git a/src/_FileInformation.txt b/src/_FileInformation.txt new file mode 100644 index 0000000..54452a1 --- /dev/null +++ b/src/_FileInformation.txt @@ -0,0 +1,2 @@ +Tool=Type(00),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) +GTETool=Type(BA),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) diff --git a/src/blitter/Tables.s b/src/blitter/Tables.s index 44bf70c..bbe0a3d 100644 --- a/src/blitter/Tables.s +++ b/src/blitter/Tables.s @@ -19,25 +19,26 @@ ; ; Remember, because the data is pushed on to the stack, the last instruction, which is ; in the highest memory location, pushed data that apepars on the left edge of the screen. -PER_TILE_SIZE equ 3 -]step equ 0 - dw CODE_TOP ; There is a spot where we load Col2CodeOffet-2,x -Col2CodeOffset lup 82 - dw CODE_TOP+{{81-]step}*PER_TILE_SIZE} -]step equ ]step+1 - --^ - dw CODE_TOP+{81*PER_TILE_SIZE} +;PER_TILE_SIZE equ 3 +;]step equ 0 + +; dw CODE_TOP ; There is a spot where we load Col2CodeOffet-2,x +;Col2CodeOffset lup 82 +; dw CODE_TOP+{{81-]step}*PER_TILE_SIZE} +;]step equ ]step+1 +; --^ +; dw CODE_TOP+{81*PER_TILE_SIZE} ; A parallel table to Col2CodeOffset that holds the offset to the exception handler address for each column -SNIPPET_SIZE equ 32 -]step equ 0 - dw SNIPPET_BASE -JTableOffset lup 82 - dw SNIPPET_BASE+{{81-]step}*SNIPPET_SIZE} -]step equ ]step+1 - --^ - dw SNIPPET_BASE+{81*SNIPPET_SIZE} +;SNIPPET_SIZE equ 32 +;]step equ 0 +; dw SNIPPET_BASE +;JTableOffset lup 82 +; dw SNIPPET_BASE+{{81-]step}*SNIPPET_SIZE} +;]step equ ]step+1 +; --^ +; dw SNIPPET_BASE+{81*SNIPPET_SIZE} ; Table of BRA instructions that are used to exit the code field. Separate tables for ; even and odd aligned cases. diff --git a/src/blitter/Template.s b/src/blitter/Template.s index 82446a8..651d62e 100644 --- a/src/blitter/Template.s +++ b/src/blitter/Template.s @@ -155,7 +155,6 @@ SetScreenRect sty ScreenHeight ; Save the screen height and cpx #TILE_STORE_SIZE-2 bcc :tsloop -; Return rts ; Generalized routine that calculates the on-screen address of the tiles and takes the diff --git a/src/blitter/Tiles.s b/src/blitter/Tiles.s index 45c45ac..0a9f66e 100644 --- a/src/blitter/Tiles.s +++ b/src/blitter/Tiles.s @@ -74,7 +74,7 @@ _GetBaseTileAddr asl ; x64 asl ; x128 rts - + ; On entry ; ; B is set to the correct BG1 data bank @@ -102,9 +102,14 @@ _RenderTileBG1 ; 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. ; +; There are two execution paths that are handled here. First, if there is no sprite, then +; the tile data is read directly and written into the code field in a single pass. If there +; are sprites that overlap the tile, then the sprite data is combined with the tile data +; and written to a temporary direct page buffer. If +; ; This routine sets the direct page register to the second page since we use that space to ; build and cache tile and sprite data, when necessary -; Y = address of tile + _RenderTile2 lda TileStore+TS_SPRITE_FLAG,x ; This is a bitfield of all the sprites that intersect this tile, only care if non-zero or not bne do_dirty_sprite @@ -159,10 +164,19 @@ do_dirty_sprite pei TileStoreBankAndTileDataBank ; Special value that has the TileStore bank in LSB and TileData bank in MSB plb -; Cache a couple of values into the direct page, but preserve the Accumulator +; Cache a couple of values into the direct page that are used across all copy routines - ldy TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) - sty tileAddr + lda TileStore+TS_TILE_ADDR,y ; load the address of this tile's data (pre-calculated) + sta tileAddr + + ldx TileStore+TS_VBUFF_ADDR_COUNT,y + jmp (dirty_sprite_dispatch,x) +dirty_sprite_dispatch + da CopyNoSprites + da CopyOneSprite + da CopyTwoSprites + da CopyThreeSprites + da CopyFourSprites ; MAX, don't bother with more than 4 sprites per tile ; This is very similar to the code in the dirty tile renderer, but we can't reuse ; because that code draws directly to the graphics screen, and this code draws @@ -243,7 +257,15 @@ do_dirty_sprite ; We set up direct page pointers to the mask bank and use the bank register for the ; data. -CopyFourSpritesAbove +CopyFourSprites + lda TileStore+TS_VBUFF_ADDR_0,y + sta spriteIdx + lda TileStore+TS_VBUFF_ADDR_1,y + sta spriteIdx+4 + lda TileStore+TS_VBUFF_ADDR_2,y + sta spriteIdx+8 + lda TileStore+TS_VBUFF_ADDR_3,y + sta spriteIdx+12 ; Copy three sprites into a temporary direct page buffer LDA_IL equ $A7 ; lda [dp] @@ -252,6 +274,13 @@ AND_IL equ $27 ; and [dp] AND_ILY equ $37 ; and [dp],y CopyThreeSprites + lda TileStore+TS_VBUFF_ADDR_0,y + sta spriteIdx + lda TileStore+TS_VBUFF_ADDR_1,y + sta spriteIdx+4 + lda TileStore+TS_VBUFF_ADDR_2,y + sta spriteIdx+8 + ]line equ 0 lup 8 ldy #]line*SPRITE_PLANE_SPAN @@ -285,6 +314,11 @@ CopyThreeSprites ; Copy two sprites into a temporary direct page buffer CopyTwoSprites + lda TileStore+TS_VBUFF_ADDR_0,y + sta spriteIdx + lda TileStore+TS_VBUFF_ADDR_1,y + sta spriteIdx+4 + ]line equ 0 lup 8 ldy #]line*SPRITE_PLANE_SPAN @@ -311,9 +345,56 @@ CopyTwoSprites ; jmp FinishTile ; Copy a single piece of sprite data into a temporary direct page . X = spriteIdx +; +; X register is the offset of the underlying tile data +; Y register is the line offset into the sprite data and mask buffers +; There is a pointer for each sprite on the direct page that can be used +; to access both the data and mask components of a sprite +; The Data Bank reigster points to the sprite data +; +; ldal tiledata,x +; and [spriteIdx],y +; ora (spriteIdx),y +; sta tmp_sprite_data +; +; For multiple sprites, we can chain together the and/ora instructions to stack the sprites +; +; ldal tiledata,x +; and [spriteIdx],y +; ora (spriteIdx),y +; and [spriteIdx+4],y +; ora (spriteIdx+4),y +; and [spriteIdx+8],y +; ora (spriteIdx+8),y +; sta tmp_sprite_data +; +; When the sprites need to be drawn on top of the background, then change the order of operations +; +; lda (spriteIdx),y +; and [spriteIdx+4],y +; ora (spriteIdx+4),y +; and [spriteIdx+8],y +; ora (spriteIdx+8),y +; sta tmp_sprite_data +; andl tiledata+32,x +; oral tiledata,x +; CopyOneSprite + clc + lda TileStore+TS_VBUFF_ADDR_0,y + sta spriteIdx + adc #2 + sta spriteIdx+4 + ]line equ 0 lup 8 + ldal tiledata,x + and [spriteIdx] + ora (spriteIdx) + sta tmp_sprite_data+{]line*4} + + + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x sta tmp_sprite_data+{]line*4} ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x diff --git a/src/static/SprData.s b/src/static/SprData.s new file mode 100644 index 0000000..0c3b85e --- /dev/null +++ b/src/static/SprData.s @@ -0,0 +1,3 @@ +; sprite stamp pixel data +spritedata ENT + ds 65536 \ No newline at end of file diff --git a/src/static/SprMask.s b/src/static/SprMask.s new file mode 100644 index 0000000..13de529 --- /dev/null +++ b/src/static/SprMask.s @@ -0,0 +1,3 @@ +; sprite stamp masks +spritemask ENT + ds 65536 \ No newline at end of file diff --git a/src/static/TileData.s b/src/static/TileData.s new file mode 100644 index 0000000..233da6c --- /dev/null +++ b/src/static/TileData.s @@ -0,0 +1,3 @@ +; Bank of memory that holds the 8x8 tile data +tiledata ENT + ds 65536 \ No newline at end of file diff --git a/src/static/TileStore.s b/src/static/TileStore.s new file mode 100644 index 0000000..101700f --- /dev/null +++ b/src/static/TileStore.s @@ -0,0 +1,3 @@ +; Bank of memory that holds the core sprite and tile store data structures +TileStore ENT + ds 65536 \ No newline at end of file From 96922776776d64afb2ec39ab41b44dd888050c17 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sat, 23 Apr 2022 12:48:13 -0500 Subject: [PATCH 04/82] Work on fixing linker / OMF errors --- src/Master.s | 8 ++++++-- src/static/SprData.s | 2 +- src/static/SprMask.s | 2 +- src/static/TileData.s | 2 +- src/static/TileStore.s | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Master.s b/src/Master.s index 223034c..175f3a7 100644 --- a/src/Master.s +++ b/src/Master.s @@ -1,6 +1,6 @@ ; IIgs Generic Tile Engine User Toolset - TYP $BA ; TOL file + TYP $BA ; Tool set file DSK GTETool XPL @@ -13,22 +13,26 @@ ASM static\TileData.s KND #$1001 ; Type and Attributes ($10=Static,$01=Data) + ALI BANK SNA TDATA ; 64KB Sprite Plane Data ASM static\SprData.s KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data) + ALI BANK SNA SDATA -;64KB Sprite Mask Data +; 64KB Sprite Mask Data ASM static\SprMask.s KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data) + ALI BANK SNA SMASK ; 64KB Tile Store ASM static\TileStore.s KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data) + ALI BANK SNA TSTORE diff --git a/src/static/SprData.s b/src/static/SprData.s index 0c3b85e..fa143cf 100644 --- a/src/static/SprData.s +++ b/src/static/SprData.s @@ -1,3 +1,3 @@ ; sprite stamp pixel data spritedata ENT - ds 65536 \ No newline at end of file + ds 65535 \ No newline at end of file diff --git a/src/static/SprMask.s b/src/static/SprMask.s index 13de529..c444d27 100644 --- a/src/static/SprMask.s +++ b/src/static/SprMask.s @@ -1,3 +1,3 @@ ; sprite stamp masks spritemask ENT - ds 65536 \ No newline at end of file + ds 65535 \ No newline at end of file diff --git a/src/static/TileData.s b/src/static/TileData.s index 233da6c..596afce 100644 --- a/src/static/TileData.s +++ b/src/static/TileData.s @@ -1,3 +1,3 @@ ; Bank of memory that holds the 8x8 tile data tiledata ENT - ds 65536 \ No newline at end of file + ds 65535 \ No newline at end of file diff --git a/src/static/TileStore.s b/src/static/TileStore.s index 101700f..b768a45 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -1,3 +1,3 @@ ; Bank of memory that holds the core sprite and tile store data structures TileStore ENT - ds 65536 \ No newline at end of file + ds 65535 \ No newline at end of file From 1f9f2aee0c607d2685b81e0ba12d746e8e0da0b4 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sat, 23 Apr 2022 15:41:25 -0500 Subject: [PATCH 05/82] Thank to Kelvin Sherlock for pointing out that the source files were not setting the register state directive --- demos/tool/App.Main.s | 41 +++++++++++++++++++++++++++++------------ src/CoreImpl.s | 5 +---- src/Tool.s | 39 +++++++++++++++++++-------------------- src/static/SprData.s | 4 +++- src/static/SprMask.s | 4 +++- src/static/TileData.s | 4 +++- src/static/TileStore.s | 3 ++- 7 files changed, 60 insertions(+), 40 deletions(-) diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 7f89d59..6809c62 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -12,10 +12,14 @@ _GTEStartUp MAC UserTool $02A0 <<< + mx %00 + ; Typical init phk plb + sta UserId ; GS/OS passed the memory manager user ID for the aoplication into the program + jsr ToolStartUp ; Start up the basic tools: Locator, Memory Manager, Misc jsr GTEStartUp @@ -27,19 +31,23 @@ qtRec adrl $0000 da $00 ToolStartUp - _TLStartUp ; normal tool initialization - pha - _MMStartUp - pla - sta MasterId ; our master handle references the memory allocated to us - ora #$0100 ; set auxID = $01 (valid values $01-0f) - sta UserId ; any memory we request must use our own id +; _TLStartUp ; normal tool initialization +; pha +; _MMStartUp +; pla +; sta MasterId ; our master handle references the memory allocated to us +; ora #$0100 ; set auxID = $01 (valid values $01-0f) +; sta UserId ; any memory we request must use our own id - _MTStartUp + _MTStartUp ; just start up the miscellaneous tools rts ; Load the GTE User Tool and register it GTEStartUp + pea $0000 + _LoaderStatus + pla + pea $0000 pea $0000 pea $0000 @@ -52,9 +60,13 @@ GTEStartUp pea #^ToolPath pea #ToolPath pea $0001 ; do not load into special memory - pea $0001 ; GS/OS string for the argument - _InitialLoad2 +; pea $0001 ; GS/OS string for the argument + _InitialLoad + bcc :ok1 + brk $01 + +:ok1 ply pla ; Address of the loaded tool plx @@ -66,7 +78,10 @@ GTEStartUp 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 @@ -74,10 +89,12 @@ GTEStartUp lda UserId pha _GTEStartUp + bcc :ok3 + brk $03 +:ok3 rts - MasterId ds 2 UserId ds 2 -ToolPath strl '9:GTETool' \ No newline at end of file +ToolPath str '1/GTETool' \ No newline at end of file diff --git a/src/CoreImpl.s b/src/CoreImpl.s index b4708ed..e901ef0 100644 --- a/src/CoreImpl.s +++ b/src/CoreImpl.s @@ -88,10 +88,7 @@ Overlay EXT ; A = memory manager userId ; X = tool number _CoreStartUp - sta UserId ; This is the first thing to do - stx ToolNum - - jsr IntStartUp ; Enable certain iterrupts + jsr IntStartUp ; Enable certain interrupts jsr InitMemory ; Allocate and initialize memory for the engine jsr EngineReset ; All of the resources are allocated, put the engine in a known state diff --git a/src/Tool.s b/src/Tool.s index 2fdf11e..4743867 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -14,6 +14,8 @@ ToStrip equ $E10184 + mx %00 + _CallTable adrl {_CTEnd-_CallTable}/4 adrl _TSBootInit-1 @@ -42,38 +44,35 @@ _TSBootInit ; StartUp(dPageAddr, userId) _TSStartUp -rtll = 1 -rtl2 = rtl1+3 userId = 7 zpToUse = userId+2 + lda zpToUse,s ; Get the direct page address + phd ; Save the current direct page + tcd ; Set to our working direct page space + txa - and #$00FF ; Get just the tool number - tax + and #$00FF ; Get just the tool number + sta ToolNum - lda userId,s ; Get the userId for memory allocations - tay - lda zpToUse,s ; Get the direct page address + lda userId+2,s ; Get the userId for memory allocations + sta UserId - phd ; Save the current direct page - tcd ; Set to our working direct page space - - tya ; A = memory manager user Id, X = tool number - jsr _CoreStartUp ; Initialize the library + jsr _CoreStartUp ; Initialize the library ; SetWAP(userOrSystem, tsNum, waptPtr) - pea #$8000 ; $8000 = user tool set - pei ToolNum ; Push the tool number from the direct page - pea $0000 ; High word of WAP is zero (bank 0) - phd ; Low word of WAP is the direct page + pea #$8000 ; $8000 = user tool set + pei ToolNum ; Push the tool number from the direct page + pea $0000 ; High word of WAP is zero (bank 0) + phd ; Low word of WAP is the direct page _SetWAP - pld ; Restore the caller's direct page + pld ; Restore the caller's direct page - lda #0 - clc - rtl + ldx #0 ; No error + ldy #4 ; Remove the 4 input bytes + jml ToStrip _TSShutDown cmp #0 ; Acc is low word of the WAP (direct page) diff --git a/src/static/SprData.s b/src/static/SprData.s index fa143cf..8c9150b 100644 --- a/src/static/SprData.s +++ b/src/static/SprData.s @@ -1,3 +1,5 @@ ; sprite stamp pixel data spritedata ENT - ds 65535 \ No newline at end of file +; ds 65535 + ds 65536 + diff --git a/src/static/SprMask.s b/src/static/SprMask.s index c444d27..d8e2f9b 100644 --- a/src/static/SprMask.s +++ b/src/static/SprMask.s @@ -1,3 +1,5 @@ ; sprite stamp masks spritemask ENT - ds 65535 \ No newline at end of file +; ds 65535 + ds 65536 + diff --git a/src/static/TileData.s b/src/static/TileData.s index 596afce..aa77f32 100644 --- a/src/static/TileData.s +++ b/src/static/TileData.s @@ -1,3 +1,5 @@ ; Bank of memory that holds the 8x8 tile data tiledata ENT - ds 65535 \ No newline at end of file +; ds 65535 + ds 65536 + diff --git a/src/static/TileStore.s b/src/static/TileStore.s index b768a45..7182fb3 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -1,3 +1,4 @@ ; Bank of memory that holds the core sprite and tile store data structures TileStore ENT - ds 65535 \ No newline at end of file +; ds 65535 + ds 65536 From 76180b6feb86777c2411f87afead2b0734e3fe67 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sun, 24 Apr 2022 14:45:07 -0500 Subject: [PATCH 06/82] Basic working user tool framework --- demos/tool/App.Main.s | 44 +++++++++++---------------- demos/tool/package.json | 2 +- macros/GTE.Macs.s | 26 ++++++++++++++++ src/CoreImpl.s | 5 ++-- src/Defs.s | 7 +++++ src/Memory.s | 21 +++++++++++-- src/Tool.s | 66 +++++++++++++++++++++++++---------------- 7 files changed, 114 insertions(+), 57 deletions(-) create mode 100644 macros/GTE.Macs.s diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 6809c62..40a3311 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -7,10 +7,7 @@ use Misc.Macs use Util.Macs use EDS.GSOS.Macs - -_GTEStartUp MAC - UserTool $02A0 - <<< + use GTE.Macs mx %00 @@ -18,31 +15,27 @@ _GTEStartUp MAC phk plb - sta UserId ; GS/OS passed the memory manager user ID for the aoplication into the program + sta UserId ; GS/OS passes the memory manager user ID for the aoplication into the program + _MTStartUp ; GTE requires the miscellaneous toolset to be running - jsr ToolStartUp ; Start up the basic tools: Locator, Memory Manager, Misc - jsr GTEStartUp + jsr GTEStartUp ; Load and install the GTE User Tool +; Very simple actions +:loop + pha ; space for result, with pattern + _GTEReadControl + pla + and #$00FF + cmp #'q' + bne :loop + +; Shut down eveything + _GTEShutDown _QuitGS qtRec - - bcs Fatal -Fatal brk $00 qtRec adrl $0000 da $00 -ToolStartUp -; _TLStartUp ; normal tool initialization -; pha -; _MMStartUp -; pla -; sta MasterId ; our master handle references the memory allocated to us -; ora #$0100 ; set auxID = $01 (valid values $01-0f) -; sta UserId ; any memory we request must use our own id - - _MTStartUp ; just start up the miscellaneous tools - rts - -; Load the GTE User Tool and register it +; Load the GTE User Tool and install it GTEStartUp pea $0000 _LoaderStatus @@ -60,12 +53,10 @@ GTEStartUp pea #^ToolPath pea #ToolPath pea $0001 ; do not load into special memory -; pea $0001 ; GS/OS string for the argument _InitialLoad bcc :ok1 brk $01 - :ok1 ply pla ; Address of the loaded tool @@ -86,7 +77,8 @@ GTEStartUp tdc adc #$0100 pha - lda UserId + pea $0000 ; No extra capabilities + lda UserId ; Pass the userId for memory allocation pha _GTEStartUp bcc :ok3 diff --git a/demos/tool/package.json b/demos/tool/package.json index 18de1c0..0c8f1e9 100644 --- a/demos/tool/package.json +++ b/demos/tool/package.json @@ -12,7 +12,7 @@ }, "scripts": { "test": "npm run build && build-image.bat %npm_package_config_cadius% && %npm_package_config_gsport%", - "debug": "%npm_package_config_crossrunner% GTEToolTest -Source GTEToolTest_S02_MAINSEG_Output.txt -Debug -CompatibilityLayer", + "debug": "%npm_package_config_crossrunner% GTEToolDemo -Source MAINSEG_Output.txt -Debug -CompatibilityLayer", "build": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.s" }, "repository": { diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s new file mode 100644 index 0000000..67f4a3d --- /dev/null +++ b/macros/GTE.Macs.s @@ -0,0 +1,26 @@ +* Generic Tile Engine Macros +* by Lucas Scharenbroich + +GTEToolNum equ $A0 + +_GTEBootInit MAC + UserTool $100+GTEToolNum + <<< +_GTEStartUp MAC + UserTool $200+GTEToolNum + <<< +_GTEShutDown MAC + UserTool $300+GTEToolNum + <<< +_GTEVersion MAC + UserTool $400+GTEToolNum + <<< +_GTEReset MAC + UserTool $500+GTEToolNum + <<< +_GTEStatus MAC + UserTool $600+GTEToolNum + <<< +_GTEReadControl MAC + UserTool $900+GTEToolNum + <<< \ No newline at end of file diff --git a/src/CoreImpl.s b/src/CoreImpl.s index e901ef0..7fae846 100644 --- a/src/CoreImpl.s +++ b/src/CoreImpl.s @@ -85,8 +85,7 @@ Overlay EXT mx %00 -; A = memory manager userId -; X = tool number +; Assumes the direct page is set and EngineMode and UserId has been initialized _CoreStartUp jsr IntStartUp ; Enable certain interrupts @@ -194,7 +193,7 @@ EngineReset stz OldStartY stz StartYMod208 - stz EngineMode +; stz EngineMode stz DirtyBits stz LastRender stz LastPatchOffset diff --git a/src/Defs.s b/src/Defs.s index 2f5e12c..f59e71f 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -34,6 +34,8 @@ StartX equ 16 ; Which code buffer byte is the left ed StartY equ 18 ; Which code buffer line is the top of the screen. Range = 0 to 207 EngineMode equ 20 ; Defined the mode/capabilities that are enabled ; bit 0: 0 = Single Background, 1 = Parallax + ; bit 1: 0 = No Dynamic Tiles, 1 = Allocate Bank 00 space for dynamic tiles + ; bit 2: 0 = No static buffer, 1 = Allocation Bank 00 space for a static screen buffer DirtyBits equ 22 ; Identify values that have changed between frames BG1DataBank equ 24 ; Data bank that holds BG1 layer data @@ -143,7 +145,12 @@ FREE_SPACE_DP2 equ 160 ; End direct page values +; EngineMode definitions +ENGINE_MODE_TWO_LAYER equ $0001 +ENGINE_MODE_DYN_TILES equ $0002 +ENGINE_MODE_BNK0_BUFF equ $0004 +; DirtyBits definitions DIRTY_BIT_BG0_X equ $0001 DIRTY_BIT_BG0_Y equ $0002 DIRTY_BIT_BG1_X equ $0004 diff --git a/src/Memory.s b/src/Memory.s index 59b75bc..b74790a 100644 --- a/src/Memory.s +++ b/src/Memory.s @@ -16,7 +16,11 @@ mx %00 -InitMemory PushLong #0 ; space for result +InitMemory lda EngineMode + bit #ENGINE_MODE_BNK0_BUFF + beq :no_bnk0_buff + + PushLong #0 ; space for result PushLong #$008000 ; size (32k) PushWord UserId PushWord #%11000000_00010111 ; Fixed location @@ -27,6 +31,7 @@ InitMemory PushLong #0 ; space for result _Deref stx Buff00 sta Buff00+2 +:no_bnk0_buff PushLong #0 ; space for result PushLong #$008000 ; size (32k) @@ -41,7 +46,15 @@ InitMemory PushLong #0 ; space for result sta Buff01+2 PushLong #0 ; space for result - PushLong #$000A00 ; size (10 pages) + + pea #0000 ; size (2 or 10 pages) + lda EngineMode + bit #ENGINE_MODE_DYN_TILES + beq :no_dyn_tiles + pea #$0A00 ; 10 pages if dynamic tiles are enabled + bra :dyn_done +:no_dyn_tiles pea #$0200 ; 2 pages if dynamic tiles are disabled +:dyn_done PushWord UserId PushWord #%11000000_00010101 ; Page-aligned, fixed bank PushLong #$000000 @@ -52,11 +65,15 @@ InitMemory PushLong #0 ; space for result stx BlitterDP ; Allocate banks of memory for BG1 + lda EngineMode + bit #ENGINE_MODE_TWO_LAYER + beq :no_bg1 jsr AllocOneBank2 sta BG1DataBank jsr AllocOneBank2 sta BG1AltBank +:no_bg1 ; Allocate the 13 banks of memory we need and store in double-length array ]step equ 0 diff --git a/src/Tool.s b/src/Tool.s index 4743867..da77a71 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -14,6 +14,25 @@ ToStrip equ $E10184 +; Define some macros to help streamline the entry and exit from the toolbox calls +_TSEntry mac + phd + phb + tcd + phk ; Default to setting the data back to the current bank. + plb + <<< + +_TSExit mac + plb + pld + ldx ]1 ; Error code + ldy ]2 ; Number of stack bytes to remove + jml ToStrip + <<< + +FirstParam equ 10 ; When using the _TSEntry macro, the first parameter is at 10,s + mx %00 _CallTable @@ -27,7 +46,8 @@ _CallTable adrl _TSReserved-1 adrl _TSReserved-1 - adrl _TSSetScreenMode + adrl _TSReadControl-1 + adrl _TSSetScreenMode-1 _CTEnd ; Do nothing when the tool set is installed @@ -39,13 +59,14 @@ _TSBootInit ; Call the regular GTE startup function after setting the Work Area Pointer (WAP). The caller must provide ; one page of Bank 0 memory for the tool set's private use and a userId to use for allocating memory ; -; X = tool set number in low byte and function umber in high byte +; X = tool set number in low byte and function number in high byte ; -; StartUp(dPageAddr, userId) +; StartUp(dPageAddr, capFlags, userId) _TSStartUp userId = 7 -zpToUse = userId+2 +capFlags = userId+2 +zpToUse = userId+4 lda zpToUse,s ; Get the direct page address phd ; Save the current direct page @@ -58,6 +79,9 @@ zpToUse = userId+2 lda userId+2,s ; Get the userId for memory allocations sta UserId + lda capFlags+2,s ; Get the engine capability bits + sta EngineMode + jsr _CoreStartUp ; Initialize the library ; SetWAP(userOrSystem, tsNum, waptPtr) @@ -71,9 +95,10 @@ zpToUse = userId+2 pld ; Restore the caller's direct page ldx #0 ; No error - ldy #4 ; Remove the 4 input bytes + ldy #6 ; Remove the 6 input bytes jml ToStrip +; ShutDown() _TSShutDown cmp #0 ; Acc is low word of the WAP (direct page) beq :inactive @@ -114,7 +139,7 @@ _TSStatus sta 1,s tya ora 1,s - sta 1,s ; 0 if WAP is null, non-zero if WAP is set + sta 1,s ; 0 if WAP is null, non-zero if WAP is set lda #0 clc @@ -129,37 +154,28 @@ _TSReserved ; SetScreenMode(width, height) _TSSetScreenMode -:height equ 9 -:width equ 11 +height equ FirstParam +width equ FirstParam+2 - phd ; Preserve the direct page - tcd ; Set the tool set direct pafe from WAP + _TSEntry - lda :height,s + lda height,s tay - lda :width,s + lda width,s tax ; jsr _SetScreenMode ; Not implemented yet - pld ; Restore direct page - - ldx #0 ; No error - ldy #4 ; Remove the 4 input bytes - jml ToStrip + _TSExit #0;#4 _TSReadControl -:output equ 9 +output equ FirstParam - phd ; Preserve the direct page - tcd + _TSEntry jsr _ReadControl - sta :output,s + sta output,s - pld - ldx #0 ; No error - ldy #0 ; Remove zero input bytes - jml ToStrip + _TSExit #0;#0 ; Insert the core GTE functions From 29d70dc567a597d2a811a4449587c0c8ebf1be60 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 25 Apr 2022 11:30:24 -0500 Subject: [PATCH 07/82] Break up large source code files to help with dependency ordering --- src/Math.s | 88 ++++++++++ src/blitter/Rotation.s | 237 ++++++++++++++++++++++++++ src/blitter/TemplateUtils.s | 320 ++++++++++++++++++++++++++++++++++++ 3 files changed, 645 insertions(+) create mode 100644 src/Math.s create mode 100644 src/blitter/Rotation.s create mode 100644 src/blitter/TemplateUtils.s diff --git a/src/Math.s b/src/Math.s new file mode 100644 index 0000000..91ca035 --- /dev/null +++ b/src/Math.s @@ -0,0 +1,88 @@ +; Math-y functions + + mx %00 + +; Special subroutine to divide the accumulator by 164 and return remainder in the Accumulator +; +; 164 = $A4 = 1010_0100 +Mod164 cmp #%1010010000000000 + bcc *+5 + sbc #%1010010000000000 + + cmp #%0101001000000000 + bcc *+5 + sbc #%0101001000000000 + + cmp #%0010100100000000 + bcc *+5 + sbc #%0010100100000000 + + cmp #%0001010010000000 + bcc *+5 + sbc #%0001010010000000 + + cmp #%0000101001000000 + bcc *+5 + sbc #%0000101001000000 + + cmp #%0000010100100000 + bcc *+5 + sbc #%0000010100100000 + + cmp #%0000001010010000 + bcc *+5 + sbc #%0000001010010000 + + cmp #%0000000101001000 + bcc *+5 + sbc #%0000000101001000 + + cmp #%0000000010100100 + bcc *+5 + sbc #%0000000010100100 + rts + +; Special subroutine to divide the accumulator by 208 and return remainder in the Accumulator +; +; 208 = $D0 = 1101_0000 +; +; There are probably faster hacks to divide a 16-bit unsigned value by 208 +; https://www.drdobbs.com/parallel/optimizing-integer-division-by-a-constan/184408499 +; https://embeddedgurus.com/stack-overflow/2009/06/division-of-integers-by-constants/ + +Mod208 cmp #%1101000000000000 + bcc *+5 + sbc #%1101000000000000 + + cmp #%0110100000000000 + bcc *+5 + sbc #%0110100000000000 + + cmp #%0011010000000000 + bcc *+5 + sbc #%0011010000000000 + + cmp #%0001101000000000 + bcc *+5 + sbc #%0001101000000000 + + cmp #%0000110100000000 + bcc *+5 + sbc #%0000110100000000 + + cmp #%0000011010000000 + bcc *+5 + sbc #%0000011010000000 + + cmp #%0000001101000000 + bcc *+5 + sbc #%0000001101000000 + + cmp #%0000000110100000 + bcc *+5 + sbc #%0000000110100000 + + cmp #%0000000011010000 + bcc *+5 + sbc #%0000000011010000 + rts diff --git a/src/blitter/Rotation.s b/src/blitter/Rotation.s new file mode 100644 index 0000000..e91cc9a --- /dev/null +++ b/src/blitter/Rotation.s @@ -0,0 +1,237 @@ +; Support rotating the BG1 graphics by leveraging the fact that a rotation function can be decomposed +; into an addition of two function parametertized by the angle of rotation: pixel = *(f(x, a) + f(y, a)) +; +; The pre-build a number of rotation tables and then populate the direct page values and Y-register values +; for each line of the blitter, such that a single lda (00),y instruction fetched the appropriate data +; +; This is about as fast of a rotation as we can do. +; +; When possible, off-screen locations are calculate to produce an address of $FFFE, so that the last two bytes +; of the BG1 data buffer provides the "fill value". + +ANGLEBNK ext +ApplyBG1XPosAngle ENT + phb + phk + plb + jsr _ApplyBG1XPosAngle + plb + rtl + +_ApplyBG1XPosAngle +; phy + +; lda BG1StartX +; jsr Mod164 +; sta BG1StartXMod164 + +; lda #162 +; sec +; sbc StartXMod164 +; bpl *+6 +; clc +; adc #164 +; clc +; adc BG1StartXMod164 +; cmp #164 +; bcc *+5 +; sbc #164 + +; clc +; adc 1,s +; tay ; cache the value + +; pla ; pop the value + phd ; save the direct page because we are going to switch to the + lda BlitterDP ; blitter direct page space and fill in the addresses + tcd + + lda #^ANGLEBNK + sta $fe + sty $fc ; Store in the new direct page + ldy #162 + tyx +:loop + lda [$fc],y + sta 00,x ; store the value + dey + dey + dex + dex + bpl :loop + pld + rts + +ApplyBG1YPosAngle ENT + phb + phk + plb + jsr _ApplyBG1YPosAngle + plb + rtl + +_ApplyBG1YPosAngle +:virt_line equ tmp0 +:lines_left equ tmp1 +:draw_count equ tmp2 +:ytbl_idx equ tmp3 +:angle_tbl equ tmp4 + + sty :angle_tbl + + lda BG1StartY + jsr Mod208 + sta BG1StartYMod208 + sta :ytbl_idx ; Start copying from the first entry in the table + + lda StartYMod208 ; This is the base line of the virtual screen + sta :virt_line ; Keep track of it + + lda ScreenHeight + sta :lines_left + +:loop + lda :virt_line + asl + tax + ldal BTableLow,x ; Get the address of the first code field line + tay + + sep #$20 + ldal BTableHigh,x + pha ; push the bank on the stack + plb + rep #$20 + + lda :virt_line + and #$000F + eor #$FFFF + inc + clc + adc #16 + min :lines_left + + sta :draw_count ; Do this many lines + asl + tax + + lda :ytbl_idx ; Read from this location (duplicate every 4 lines) + lsr + lsr + asl + clc + adc :angle_tbl + sec + sbc #ANGLEBNK + jsr CopyAngleYTableToBG1Addr ; or CopyBG1YTableToBG1Addr2 + + lda :virt_line ; advance to the virtual line after the segment we just + clc ; filled in + adc :draw_count + sta :virt_line + + lda :ytbl_idx ; advance the index into the YTable + adc :draw_count + sta :ytbl_idx + + lda :lines_left ; subtract the number of lines we just completed + sec + sbc :draw_count + sta :lines_left + + jne :loop + + phk + plb + rts + +; Unrolled copy routine to move y_angle entries into BG1_ADDR position with an additional +; shift. This has to be split into two +; +; A = index into the array (x2) +; Y = starting line * $1000 +; X = number of lines (x2) +CopyAngleYTableToBG1Addr + phx + phb + + phk ; restore access to this bank + plb + jsr SaveBG1AngleValues + + plb + plx ; x is used directly in this routine + jsr ApplyBG1OffsetValues + rts + +SaveBG1AngleValues + jmp (:tbl,x) +:tbl da :none + da :do01,:do02,:do03,:do04 + da :do05,:do06,:do07,:do08 + da :do09,:do10,:do11,:do12 + da :do13,:do14,:do15,:do16 +:do15 tax + bra :x15 +:do14 tax + bra :x14 +:do13 tax + bra :x13 +:do12 tax + bra :x12 +:do11 tax + bra :x11 +:do10 tax + bra :x10 +:do09 tax + bra :x09 +:do08 tax + bra :x08 +:do16 tax + ldal ANGLEBNK+06,x + sta BG1YCache+30 +:x15 ldal ANGLEBNK+06,x + sta BG1YCache+28 +:x14 ldal ANGLEBNK+06,x + sta BG1YCache+26 +:x13 ldal ANGLEBNK+06,x + sta BG1YCache+24 +:x12 ldal ANGLEBNK+04,x + sta BG1YCache+22 +:x11 ldal ANGLEBNK+04,x + sta BG1YCache+20 +:x10 ldal ANGLEBNK+04,x + sta BG1YCache+18 +:x09 ldal ANGLEBNK+04,x + sta BG1YCache+16 +:x08 ldal ANGLEBNK+02,x + sta BG1YCache+14 +:x07 ldal ANGLEBNK+02,x + sta BG1YCache+12 +:x06 ldal ANGLEBNK+02,x + sta BG1YCache+10 +:x05 ldal ANGLEBNK+02,x + sta BG1YCache+08 +:x04 ldal ANGLEBNK+00,x + sta BG1YCache+06 +:x03 ldal ANGLEBNK+00,x + sta BG1YCache+04 +:x02 ldal ANGLEBNK+00,x + sta BG1YCache+02 +:x01 ldal ANGLEBNK+00,x + sta BG1YCache+00 +:none rts +:do07 tax + bra :x07 +:do06 tax + bra :x06 +:do05 tax + bra :x05 +:do04 tax + bra :x04 +:do03 tax + bra :x03 +:do02 tax + bra :x02 +:do01 tax + bra :x01 diff --git a/src/blitter/TemplateUtils.s b/src/blitter/TemplateUtils.s new file mode 100644 index 0000000..121f3af --- /dev/null +++ b/src/blitter/TemplateUtils.s @@ -0,0 +1,320 @@ +; Untility function related to patching and manipulating the blitter template code + + mx %00 + +; Generalized routine that calculates the on-screen address of the tiles and takes the +; StartX and StartY values into consideration. This routine really exists to support +; the dirty tile rendering mode and the tiles *must* be aligned with the playfield. +; That is, StartX % 4 == 0 and StartY % 8 == 0. If these conditions are not met, then +; screen will not render correctly. +_RecalcTileScreenAddrs +NextColPtr equ tmp0 +RowAddrPtr equ tmp1 +OnScreenAddr equ tmp2 +Counter equ tmp3 + + jsr _OriginToTileStore ; Get the (col,row) of the tile in the upper-left corner of the playfield + +; Manually add the offsets to the NextCol and TileStoreYTable array address and put in a direct page +; location so we can free up the registers. + + clc + txa + adc #NextCol + sta NextColPtr + + tya + adc #TileStoreYTable + sta RowAddrPtr + +; Calculate the on-screen address of the upper-left corner of the playfiled + + lda ScreenY0 ; Calculate the address of the first byte + asl ; of the right side of the playfield + tax + lda ScreenAddr,x ; This is the address for the left edge of the physical screen + clc + adc ScreenX0 + sta OnScreenAddr + +; Now, loop through the tile store + + lda #MAX_TILES + sta Counter + ldy #0 +:tsloop + 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 + + lda OnScreenAddr + stal TileStore+TS_SCREEN_ADDR,x + + clc + adc #4 ; Go to the next tile + + iny + iny + cpy #2*41 ; If we've done 41 columns, move to the next line + bcc :nohop + + inc RowAddrPtr ; Advance the row address (with wrap-around) + inc RowAddrPtr + ldy #0 ; Reset the column counter + clc + adc #{8*160}-{4*41} +:nohop + sta OnScreenAddr ; Save the updated on-screen address + dec Counter + bne :tsloop + + rts + + +; Patch an 8-bit or 16-bit valueS into the bank. These are a set up unrolled loops to +; quickly patch in a constanct value, or a value from an array into a given set of +; templates. +; +; Because we have structured everything as parallel code blocks, most updates to the blitter +; reduce to storing a constant value and have an amortized cost of just a single store. +; +; The utility of these routines is that they also handle setting just a range of lines +; within a single bank. +; +; X = number of lines * 2, 0 to 32 +; Y = starting line * $1000 +; A = value +; +; Set M to 0 or 1 +SetConst ; Need a blank line here, otherwise the :tbl local variable resolveds backwards + jmp (:tbl,x) +:tbl da :bottom-00,:bottom-03,:bottom-06,:bottom-09 + da :bottom-12,:bottom-15,:bottom-18,:bottom-21 + da :bottom-24,:bottom-27,:bottom-30,:bottom-33 + da :bottom-36,:bottom-39,:bottom-42,:bottom-45 + da :bottom-48 +:top sta $F000,y + sta $E000,y + sta $D000,y + sta $C000,y + sta $B000,y + sta $A000,y + sta $9000,y + sta $8000,y + sta $7000,y + sta $6000,y + sta $5000,y + sta $4000,y + sta $3000,y + sta $2000,y + sta $1000,y + sta: $0000,y +:bottom rts + +; SetDPAddrs +; +; A = absolute address (largest) +; Y = offset +; +; Initializes a bank of direct page offsets +SetDPAddrs + lda #$0800 + sta $F000,y + lda #$0700 + sta $E000,y + lda #$0600 + sta $D000,y + lda #$0500 + sta $C000,y + lda #$0400 + sta $B000,y + lda #$0300 + sta $A000,y + lda #$0200 + sta $9000,y + lda #$0100 + sta: $8000,y + + lda #$0800 + sta $7000,y + lda #$0700 + sta $6000,y + lda #$0600 + sta $5000,y + lda #$0500 + sta $4000,y + lda #$0400 + sta $3000,y + lda #$0300 + sta $2000,y + lda #$0200 + sta $1000,y + lda #$0100 + sta: $0000,y + rts + +; SetAbsAddrs +; +; A = absolute address (largest) +; Y = offset +; X = number of lines +; +; Stores a value and decrements by $1000 for each line +SetAbsAddrs sec + jmp (:tbl,x) +:tbl da :bottom-00,:bottom-03,:bottom-09,:bottom-15 + da :bottom-21,:bottom-27,:bottom-33,:bottom-39 + da :bottom-45,:bottom-51,:bottom-57,:bottom-63 + da :bottom-69,:bottom-75,:bottom-81,:bottom-87 + da :bottom-93 +:top sta $F000,y + sbc #$1000 + sta $E000,y + sbc #$1000 + sta $D000,y + sbc #$1000 + sta $C000,y + sbc #$1000 + sta $B000,y + sbc #$1000 + sta $A000,y + sbc #$1000 + sta $9000,y + sbc #$1000 + sta $8000,y + sbc #$1000 + sta $7000,y + sbc #$1000 + sta $6000,y + sbc #$1000 + sta $5000,y + sbc #$1000 + sta $4000,y + sbc #$1000 + sta $3000,y + sbc #$1000 + sta $2000,y + sbc #$1000 + sta $1000,y + sbc #$1000 + sta: $0000,y +:bottom rts + +; Fill up a full bank with blitter templates. Currently we can fit 16 lines per bank, so need +; a total of 13 banks to hold the 208 lines for full-screen support +; +; A = high word of bank table +; Y = index * 4 of the bank to initialize +BuildBank + +:bankArray equ tmp0 +:target equ tmp2 +:nextBank equ tmp4 + + stx :bankArray + sta :bankArray+2 + + stz :target + iny + iny + lda [:bankArray],y + sta :target+2 + + iny ; move to the next item + iny + iny ; middle byte + cpy #4*13 ; if greater than the array length, wrap back to zero + bcc :ok + ldy #1 +:ok lda [:bankArray],y ; Get the middle and high bytes of the address + sta :nextBank + +:next + jsr :BuildLine2 + lda :target + clc + adc #$1000 + sta :target + bcc :next + + phb + pei :target+1 + plb + plb + +; Change the patched value to one of DP_ENTRY, TWO_LYR_ENTRY or ONE_LYR_ENTRY based on the capabilities +; that the engine needs. + + lda #$F000+{DP_ENTRY} ; Set the address from each line to the next + ldy #CODE_EXIT+1 + ldx #15*2 + jsr SetAbsAddrs + + ldy #DP_ADDR + jsr SetDPAddrs + + ldy #$F000+CODE_EXIT ; Patch the last line with a JML to go to the next bank + lda #{$005C+{DP_ENTRY}*256} + sta [:target],y + ldy #$F000+CODE_EXIT+2 + lda :nextBank + sta [:target],y + + ldy #$8000+CODE_EXIT ; Patch one line per bank to enable interrupts + lda #{$004C+{ENABLE_INT}*256} + sta [:target],y + + plb + rts + +; This is the relocation subroutine, it is responsible for copying the template to a +; memory location and patching up the necessary instructions. +; +; X = low word of address (must be a multiple of $1000) +; A = high word of address (bank) +:BuildLine + stx :target + sta :target+2 + +:BuildLine2 + lda #CODE_LEN ; round up to an even number of bytes + inc + and #$FFFE + beq :nocopy + dec + dec + tay +:loop lda base,y + sta [:target],y + + dey + dey + bpl :loop + +:nocopy lda #0 ; copy is complete, now patch up the addresses + sep #$20 + + ldx #0 + lda :target+2 ; patch in the bank for the absolute long addressing mode +:dobank ldy BankPatches,x + sta [:target],y + inx + inx + cpx #BankPatchNum + bcc :dobank + + ldx #0 +:dopage ldy PagePatches,x ; patch the page addresses by adding the page offset to each + lda [:target],y + clc + adc :target+1 + sta [:target],y + inx + inx + cpx #PagePatchNum + bcc :dopage + +:out + rep #$20 + rts \ No newline at end of file From d107365d796ed78b6ce7d4385c0bf19d43473e51 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 25 Apr 2022 11:32:06 -0500 Subject: [PATCH 08/82] Break up large source code files to help with dependency ordering --- macros/GTE.Macs.s | 5 +- src/CoreImpl.s | 9 +- src/Graphics.s | 133 +++++++++- src/Memory.s | 1 + src/blitter/BG0.s | 2 +- src/blitter/BG1.s | 231 ------------------ src/blitter/Blitter.s | 2 +- src/blitter/Tables.s | 33 +-- src/blitter/Template.s | 533 +---------------------------------------- 9 files changed, 160 insertions(+), 789 deletions(-) diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s index 67f4a3d..b60d55b 100644 --- a/macros/GTE.Macs.s +++ b/macros/GTE.Macs.s @@ -23,4 +23,7 @@ _GTEStatus MAC <<< _GTEReadControl MAC UserTool $900+GTEToolNum - <<< \ No newline at end of file + <<< +_GTESetScreenMode MAC + UserTool $A00+GTEToolNum + <<< diff --git a/src/CoreImpl.s b/src/CoreImpl.s index 7fae846..81c7a89 100644 --- a/src/CoreImpl.s +++ b/src/CoreImpl.s @@ -87,16 +87,17 @@ Overlay EXT ; Assumes the direct page is set and EngineMode and UserId has been initialized _CoreStartUp - jsr IntStartUp ; Enable certain interrupts + jsr IntStartUp ; Enable certain interrupts - jsr InitMemory ; Allocate and initialize memory for the engine - jsr EngineReset ; All of the resources are allocated, put the engine in a known state + jsr InitMemory ; Allocate and initialize memory for the engine +; jsr EngineReset ; All of the resources are allocated, put the engine in a known state ; jsr InitGraphics ; Initialize all of the graphics-related data ; jsr InitSprites ; Initialize the sprite subsystem ; jsr InitTiles ; Initialize the tile subsystem - jsr InitTimers ; Initialize the timer subsystem +; jsr InitTimers ; Initialize the timer subsystem + rts _CoreShutDown jsr IntShutDown diff --git a/src/Graphics.s b/src/Graphics.s index 1ef1656..8e9f175 100644 --- a/src/Graphics.s +++ b/src/Graphics.s @@ -11,12 +11,17 @@ InitGraphics lda #0 jsr _SetPalette - jsr _InitBG0 ; Initialize the background layers - jsr _InitBG1 + jsr _InitBG0 ; Initialize the background layer + lda EngineMode + bit #ENGINE_MODE_TWO_LAYER + beq :no_bg1 + + jsr _InitBG1 lda #0 jsr _ClearBG1Buffer +:no_bg1 rts DefaultPalette dw $0000,$007F,$0090,$0FF0 @@ -24,7 +29,6 @@ DefaultPalette dw $0000,$007F,$0090,$0FF0 dw $0fa9,$0ff0,$00e0,$04DF dw $0d00,$078f,$0ccc,$0FFF - ; Allow the user to dynamically select one of the pre-configured screen sizes, or pass ; in a specific width and height. The screen is automatically centered. If this is ; not desired, then SetScreenRect should be used directly @@ -224,9 +228,132 @@ _WaitForVBL rep #$20 rts +; Set the physical location of the virtual screen on the physical screen. The +; screen size must by a multiple of 8 +; +; A = XXYY where XX is the left edge [0, 159] and YY is the top edge [0, 199] +; X = width (in bytes) +; Y = height (in lines) +; +; This subroutine stores the screen positions in the direct page space and fills +; in the double-length ScreenAddrR table that holds the address of the right edge +; of the playfield. This table is used to set addresses in the code banks when the +; virtual origin is changed. +; +; We are not concerned about the raw performance of this function because it should +; usually only be executed once during app initialization. It doesn't get called +; with any significant frequency. +SetScreenRect sty ScreenHeight ; Save the screen height and width + stx ScreenWidth + tax ; Temp save of the accumulator + and #$00FF + sta ScreenY0 + clc + adc ScreenHeight + sta ScreenY1 + txa ; Restore the accumulator + xba + and #$00FF + sta ScreenX0 + clc + adc ScreenWidth + sta ScreenX1 + lda ScreenHeight ; Divide the height in scanlines by 8 to get the number tiles + lsr + lsr + lsr + sta ScreenTileHeight + lda ScreenWidth ; Divide width in bytes by 4 to get the number of tiles + lsr + lsr + sta ScreenTileWidth + lda ScreenY0 ; Calculate the address of the first byte + asl ; of the right side of the playfield + tax + lda ScreenAddr,x ; This is the address for the left edge of the physical screen + clc + adc ScreenX1 + dec + pha ; Save for second loop + + ldx #0 + ldy ScreenHeight + jsr :loop + pla ; Reset the address and continue filling in the + ldy ScreenHeight ; second half of the table +:loop clc + sta RTable,x + adc #160 + inx + inx + dey + bne :loop + +; Calculate the screen locations for each tile corner + + lda ScreenY0 ; Calculate the address of the first byte + asl ; of the right side of the playfield + tax + lda ScreenAddr,x ; This is the address for the left edge of the physical screen + clc + adc ScreenX0 + + ldx #0 + ldy #0 +:tsloop + stal TileStore+TS_SCREEN_ADDR,x + + clc + adc #4 ; Go to the next tile + + iny + cpy #41 ; If we've done 41 columns, move to the next line + bcc :nohop + ldy #0 + clc + adc #{8*160}-{4*41} +:nohop + + inx + inx + cpx #TILE_STORE_SIZE-2 + bcc :tsloop + + rts + +; Clear the SHR screen and then infill the defined field +FillScreen lda #0 + jsr _ClearToColor + + ldy ScreenY0 +:yloop + tya + asl a + tax + lda ScreenAddr,x + clc + adc ScreenX0 + tax + phy + + lda ScreenWidth + lsr + tay + lda #$FFFF +:xloop stal $E10000,x ; X is the absolute address + inx + inx + dey + bne :xloop + + ply + iny + cpy ScreenY1 + bcc :yloop + rts diff --git a/src/Memory.s b/src/Memory.s index b74790a..8572897 100644 --- a/src/Memory.s +++ b/src/Memory.s @@ -121,6 +121,7 @@ InitMemory lda EngineMode brl :bloop :exit1 + ldx #0 ldy #0 :bloop2 diff --git a/src/blitter/BG0.s b/src/blitter/BG0.s index 31da819..081f7d8 100644 --- a/src/blitter/BG0.s +++ b/src/blitter/BG0.s @@ -1,4 +1,4 @@ -; Support routinges for the primary background +; Support routines for the primary background _InitBG0 lda #DIRTY_BIT_BG0_X+DIRTY_BIT_BG0_Y tsb DirtyBits diff --git a/src/blitter/BG1.s b/src/blitter/BG1.s index c8718fe..d6a078f 100644 --- a/src/blitter/BG1.s +++ b/src/blitter/BG1.s @@ -4,7 +4,6 @@ _InitBG1 jsr _ApplyBG1XPos rts - ; Copy a binary image data file into BG1. Assumes the file is the correct size (328 x 208) ; ; A=low word of picture address @@ -193,59 +192,6 @@ _ApplyBG1XPos pld rts -ANGLEBNK ext -ApplyBG1XPosAngle ENT - phb - phk - plb - jsr _ApplyBG1XPosAngle - plb - rtl - -_ApplyBG1XPosAngle -; phy - -; lda BG1StartX -; jsr Mod164 -; sta BG1StartXMod164 - -; lda #162 -; sec -; sbc StartXMod164 -; bpl *+6 -; clc -; adc #164 -; clc -; adc BG1StartXMod164 -; cmp #164 -; bcc *+5 -; sbc #164 - -; clc -; adc 1,s -; tay ; cache the value - -; pla ; pop the value - phd ; save the direct page because we are going to switch to the - lda BlitterDP ; blitter direct page space and fill in the addresses - tcd - - lda #^ANGLEBNK - sta $fe - sty $fc ; Store in the new direct page - ldy #162 - tyx -:loop - lda [$fc],y - sta 00,x ; store the value - dey - dey - dex - dex - bpl :loop - pld - rts - _ClearBG1Buffer phb pha @@ -266,88 +212,6 @@ _ClearBG1Buffer plb rts -ApplyBG1YPosAngle ENT - phb - phk - plb - jsr _ApplyBG1YPosAngle - plb - rtl - -_ApplyBG1YPosAngle -:virt_line equ tmp0 -:lines_left equ tmp1 -:draw_count equ tmp2 -:ytbl_idx equ tmp3 -:angle_tbl equ tmp4 - - sty :angle_tbl - - lda BG1StartY - jsr Mod208 - sta BG1StartYMod208 - sta :ytbl_idx ; Start copying from the first entry in the table - - lda StartYMod208 ; This is the base line of the virtual screen - sta :virt_line ; Keep track of it - - lda ScreenHeight - sta :lines_left - -:loop - lda :virt_line - asl - tax - ldal BTableLow,x ; Get the address of the first code field line - tay - - sep #$20 - ldal BTableHigh,x - pha ; push the bank on the stack - plb - rep #$20 - - lda :virt_line - and #$000F - eor #$FFFF - inc - clc - adc #16 - min :lines_left - - sta :draw_count ; Do this many lines - asl - tax - - lda :ytbl_idx ; Read from this location (duplicate every 4 lines) - lsr - lsr - asl - clc - adc :angle_tbl - sec - sbc #ANGLEBNK - jsr CopyAngleYTableToBG1Addr ; or CopyBG1YTableToBG1Addr2 - - lda :virt_line ; advance to the virtual line after the segment we just - clc ; filled in - adc :draw_count - sta :virt_line - - lda :ytbl_idx ; advance the index into the YTable - adc :draw_count - sta :ytbl_idx - - lda :lines_left ; subtract the number of lines we just completed - sec - sbc :draw_count - sta :lines_left - - jne :loop - - phk - plb - rts ; Everytime either BG1 or BG0 Y-position changes, we have to update the Y-register ; value in all of the code fields (within the visible screen) @@ -494,97 +358,6 @@ CopyBG1YTableToBG1Addr sta: BG1_ADDR+$0000,y :none rts -; Unrolled copy routine to move y_angle entries into BG1_ADDR position with an additional -; shift. This has to be split into two -; -; A = index into the array (x2) -; Y = starting line * $1000 -; X = number of lines (x2) -CopyAngleYTableToBG1Addr - phx - phb - - phk ; restore access to this bank - plb - jsr SaveBG1AngleValues - - plb - plx ; x is used directly in this routine - jsr ApplyBG1OffsetValues - rts - -SaveBG1AngleValues - jmp (:tbl,x) -:tbl da :none - da :do01,:do02,:do03,:do04 - da :do05,:do06,:do07,:do08 - da :do09,:do10,:do11,:do12 - da :do13,:do14,:do15,:do16 -:do15 tax - bra :x15 -:do14 tax - bra :x14 -:do13 tax - bra :x13 -:do12 tax - bra :x12 -:do11 tax - bra :x11 -:do10 tax - bra :x10 -:do09 tax - bra :x09 -:do08 tax - bra :x08 -:do16 tax - ldal ANGLEBNK+06,x - sta BG1YCache+30 -:x15 ldal ANGLEBNK+06,x - sta BG1YCache+28 -:x14 ldal ANGLEBNK+06,x - sta BG1YCache+26 -:x13 ldal ANGLEBNK+06,x - sta BG1YCache+24 -:x12 ldal ANGLEBNK+04,x - sta BG1YCache+22 -:x11 ldal ANGLEBNK+04,x - sta BG1YCache+20 -:x10 ldal ANGLEBNK+04,x - sta BG1YCache+18 -:x09 ldal ANGLEBNK+04,x - sta BG1YCache+16 -:x08 ldal ANGLEBNK+02,x - sta BG1YCache+14 -:x07 ldal ANGLEBNK+02,x - sta BG1YCache+12 -:x06 ldal ANGLEBNK+02,x - sta BG1YCache+10 -:x05 ldal ANGLEBNK+02,x - sta BG1YCache+08 -:x04 ldal ANGLEBNK+00,x - sta BG1YCache+06 -:x03 ldal ANGLEBNK+00,x - sta BG1YCache+04 -:x02 ldal ANGLEBNK+00,x - sta BG1YCache+02 -:x01 ldal ANGLEBNK+00,x - sta BG1YCache+00 -:none rts -:do07 tax - bra :x07 -:do06 tax - bra :x06 -:do05 tax - bra :x05 -:do04 tax - bra :x04 -:do03 tax - bra :x03 -:do02 tax - bra :x02 -:do01 tax - bra :x01 - ; Unrolled copy routine to move BG1YTable entries into BG1_ADDR position with an additional ; shift. This has to be split into two ; @@ -738,7 +511,3 @@ ApplyBG1OffsetValues :none rts BG1YCache ds 32 - - - - diff --git a/src/blitter/Blitter.s b/src/blitter/Blitter.s index 54c3316..7afe2bf 100644 --- a/src/blitter/Blitter.s +++ b/src/blitter/Blitter.s @@ -59,7 +59,7 @@ _BltRange ; beq :primary ; lda BG1AltBank ; bra :alt -:primary lda BG1DataBank +:primary lda BG1DataBank ; This is $00 if the TWO_LAYER bit of EngineMode is not set :alt pha plb diff --git a/src/blitter/Tables.s b/src/blitter/Tables.s index bbe0a3d..b01a931 100644 --- a/src/blitter/Tables.s +++ b/src/blitter/Tables.s @@ -20,25 +20,25 @@ ; Remember, because the data is pushed on to the stack, the last instruction, which is ; in the highest memory location, pushed data that apepars on the left edge of the screen. -;PER_TILE_SIZE equ 3 -;]step equ 0 +PER_TILE_SIZE equ 3 +]step equ 0 -; dw CODE_TOP ; There is a spot where we load Col2CodeOffet-2,x -;Col2CodeOffset lup 82 -; dw CODE_TOP+{{81-]step}*PER_TILE_SIZE} -;]step equ ]step+1 -; --^ -; dw CODE_TOP+{81*PER_TILE_SIZE} + dw CODE_TOP ; There is a spot where we load Col2CodeOffet-2,x +Col2CodeOffset lup 82 + dw CODE_TOP+{{81-]step}*PER_TILE_SIZE} +]step equ ]step+1 + --^ + dw CODE_TOP+{81*PER_TILE_SIZE} ; A parallel table to Col2CodeOffset that holds the offset to the exception handler address for each column -;SNIPPET_SIZE equ 32 -;]step equ 0 -; dw SNIPPET_BASE -;JTableOffset lup 82 -; dw SNIPPET_BASE+{{81-]step}*SNIPPET_SIZE} -;]step equ ]step+1 -; --^ -; dw SNIPPET_BASE+{81*SNIPPET_SIZE} +SNIPPET_SIZE equ 32 +]step equ 0 + dw SNIPPET_BASE +JTableOffset lup 82 + dw SNIPPET_BASE+{{81-]step}*SNIPPET_SIZE} +]step equ ]step+1 + --^ + dw SNIPPET_BASE+{81*SNIPPET_SIZE} ; Table of BRA instructions that are used to exit the code field. Separate tables for ; even and odd aligned cases. @@ -275,6 +275,7 @@ RTable ds 400 ; Array of addresses for the banks that hold the blitter. BlitBuff ENT + dw $5a5a ds 4*13 ; The blitter table (BTable) is a double-length table that holds the full 4-byte address of each diff --git a/src/blitter/Template.s b/src/blitter/Template.s index 651d62e..332b534 100644 --- a/src/blitter/Template.s +++ b/src/blitter/Template.s @@ -42,7 +42,7 @@ PagePatches da {long_0-base+2} ]index equ 0 lup 82 ; All the snippet addresses. The two JMP - da {snippets-base+{]index*32}+31} ; instructino are at the end of each of + da {snippets-base+{]index*32}+31} ; instructions are at the end of each of da {snippets-base+{]index*32}+28} ; the 32-byte buffers ]index equ ]index+1 --^ @@ -58,537 +58,6 @@ BankPatches da {long_0-base+3} da {long_6-base+3} BankPatchNum equ *-BankPatches -; Set the physical location of the virtual screen on the physical screen. The -; screen size must by a multiple of 8 -; -; A = XXYY where XX is the left edge [0, 159] and YY is the top edge [0, 199] -; X = width (in bytes) -; Y = height (in lines) -; -; This subroutine stores the screen positions in the direct page space and fills -; in the double-length ScreenAddrR table that holds the address of the right edge -; of the playfield. This table is used to set addresses in the code banks when the -; virtual origin is changed. -; -; We are not concerned about the raw performance of this function because it should -; usually only be executed once during app initialization. It doesn't get called -; with any significant frequency. - -SetScreenRect sty ScreenHeight ; Save the screen height and width - stx ScreenWidth - - tax ; Temp save of the accumulator - and #$00FF - sta ScreenY0 - clc - adc ScreenHeight - sta ScreenY1 - - txa ; Restore the accumulator - xba - and #$00FF - sta ScreenX0 - clc - adc ScreenWidth - sta ScreenX1 - - lda ScreenHeight ; Divide the height in scanlines by 8 to get the number tiles - lsr - lsr - lsr - sta ScreenTileHeight - - lda ScreenWidth ; Divide width in bytes by 4 to get the number of tiles - lsr - lsr - sta ScreenTileWidth - - lda ScreenY0 ; Calculate the address of the first byte - asl ; of the right side of the playfield - tax - lda ScreenAddr,x ; This is the address for the left edge of the physical screen - clc - adc ScreenX1 - dec - pha ; Save for second loop - - ldx #0 - ldy ScreenHeight - jsr :loop - pla ; Reset the address and continue filling in the - ldy ScreenHeight ; second half of the table -:loop clc - sta RTable,x - adc #160 - inx - inx - dey - bne :loop - -; Calculate the screen locations for each tile corner - - lda ScreenY0 ; Calculate the address of the first byte - asl ; of the right side of the playfield - tax - lda ScreenAddr,x ; This is the address for the left edge of the physical screen - clc - adc ScreenX0 - - ldx #0 - ldy #0 -:tsloop - stal TileStore+TS_SCREEN_ADDR,x - - clc - adc #4 ; Go to the next tile - - iny - cpy #41 ; If we've done 41 columns, move to the next line - bcc :nohop - ldy #0 - clc - adc #{8*160}-{4*41} -:nohop - - inx - inx - cpx #TILE_STORE_SIZE-2 - bcc :tsloop - - rts - -; Generalized routine that calculates the on-screen address of the tiles and takes the -; StartX and StartY values into consideration. This routine really exists to support -; the dirty tile rendering mode and the tiles *must* be aligned with the playfield. -; That is, StartX % 4 == 0 and StartY % 8 == 0. If these conditions are not met, then -; screen will not render correctly. -_RecalcTileScreenAddrs -NextColPtr equ tmp0 -RowAddrPtr equ tmp1 -OnScreenAddr equ tmp2 -Counter equ tmp3 - - jsr _OriginToTileStore ; Get the (col,row) of the tile in the upper-left corner of the playfield - -; Manually add the offsets to the NextCol and TileStoreYTable array address and put in a direct page -; location so we can free up the registers. - - clc - txa - adc #NextCol - sta NextColPtr - - tya - adc #TileStoreYTable - sta RowAddrPtr - -; Calculate the on-screen address of the upper-left corner of the playfiled - - lda ScreenY0 ; Calculate the address of the first byte - asl ; of the right side of the playfield - tax - lda ScreenAddr,x ; This is the address for the left edge of the physical screen - clc - adc ScreenX0 - sta OnScreenAddr - -; Now, loop through the tile store - - lda #MAX_TILES - sta Counter - ldy #0 -:tsloop - 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 - - lda OnScreenAddr - stal TileStore+TS_SCREEN_ADDR,x - - clc - adc #4 ; Go to the next tile - - iny - iny - cpy #2*41 ; If we've done 41 columns, move to the next line - bcc :nohop - - inc RowAddrPtr ; Advance the row address (with wrap-around) - inc RowAddrPtr - ldy #0 ; Reset the column counter - clc - adc #{8*160}-{4*41} -:nohop - sta OnScreenAddr ; Save the updated on-screen address - dec Counter - bne :tsloop - - rts - -; Clear the SHR screen and then infill the defined field -FillScreen lda #0 - jsr _ClearToColor - - ldy ScreenY0 -:yloop - tya - asl a - tax - lda ScreenAddr,x - clc - adc ScreenX0 - tax - phy - - lda ScreenWidth - lsr - tay - lda #$FFFF -:xloop stal $E10000,x ; X is the absolute address - inx - inx - dey - bne :xloop - - ply - iny - cpy ScreenY1 - bcc :yloop - rts - -; Special subroutine to divide the accumulator by 164 and return remainder in the Accumulator -; -; 164 = $A4 = 1010_0100 -Mod164 cmp #%1010010000000000 - bcc *+5 - sbc #%1010010000000000 - - cmp #%0101001000000000 - bcc *+5 - sbc #%0101001000000000 - - cmp #%0010100100000000 - bcc *+5 - sbc #%0010100100000000 - - cmp #%0001010010000000 - bcc *+5 - sbc #%0001010010000000 - - cmp #%0000101001000000 - bcc *+5 - sbc #%0000101001000000 - - cmp #%0000010100100000 - bcc *+5 - sbc #%0000010100100000 - - cmp #%0000001010010000 - bcc *+5 - sbc #%0000001010010000 - - cmp #%0000000101001000 - bcc *+5 - sbc #%0000000101001000 - - cmp #%0000000010100100 - bcc *+5 - sbc #%0000000010100100 - rts - -; Special subroutine to divide the accumulator by 208 and return remainder in the Accumulator -; -; 208 = $D0 = 1101_0000 -; -; There are probably faster hacks to divide a 16-bit unsigned value by 208 -; https://www.drdobbs.com/parallel/optimizing-integer-division-by-a-constan/184408499 -; https://embeddedgurus.com/stack-overflow/2009/06/division-of-integers-by-constants/ - -Mod208 cmp #%1101000000000000 - bcc *+5 - sbc #%1101000000000000 - - cmp #%0110100000000000 - bcc *+5 - sbc #%0110100000000000 - - cmp #%0011010000000000 - bcc *+5 - sbc #%0011010000000000 - - cmp #%0001101000000000 - bcc *+5 - sbc #%0001101000000000 - - cmp #%0000110100000000 - bcc *+5 - sbc #%0000110100000000 - - cmp #%0000011010000000 - bcc *+5 - sbc #%0000011010000000 - - cmp #%0000001101000000 - bcc *+5 - sbc #%0000001101000000 - - cmp #%0000000110100000 - bcc *+5 - sbc #%0000000110100000 - - cmp #%0000000011010000 - bcc *+5 - sbc #%0000000011010000 - rts - -; Patch an 8-bit or 16-bit valueS into the bank. These are a set up unrolled loops to -; quickly patch in a constanct value, or a value from an array into a given set of -; templates. -; -; Because we have structured everything as parallel code blocks, most updates to the blitter -; reduce to storing a constant value and have an amortized cost of just a single store. -; -; The utility of these routines is that they also handle setting just a range of lines -; within a single bank. -; -; X = number of lines * 2, 0 to 32 -; Y = starting line * $1000 -; A = value -; -; Set M to 0 or 1 -SetConst ; Need a blank line here, otherwise the :tbl local variable resolveds backwards - jmp (:tbl,x) -:tbl da :bottom-00,:bottom-03,:bottom-06,:bottom-09 - da :bottom-12,:bottom-15,:bottom-18,:bottom-21 - da :bottom-24,:bottom-27,:bottom-30,:bottom-33 - da :bottom-36,:bottom-39,:bottom-42,:bottom-45 - da :bottom-48 -:top sta $F000,y - sta $E000,y - sta $D000,y - sta $C000,y - sta $B000,y - sta $A000,y - sta $9000,y - sta $8000,y - sta $7000,y - sta $6000,y - sta $5000,y - sta $4000,y - sta $3000,y - sta $2000,y - sta $1000,y - sta: $0000,y -:bottom rts - -; SetDPAddrs -; -; A = absolute address (largest) -; Y = offset -; -; Initializes a bank of direct page offsets -SetDPAddrs - lda #$0800 - sta $F000,y - lda #$0700 - sta $E000,y - lda #$0600 - sta $D000,y - lda #$0500 - sta $C000,y - lda #$0400 - sta $B000,y - lda #$0300 - sta $A000,y - lda #$0200 - sta $9000,y - lda #$0100 - sta: $8000,y - - lda #$0800 - sta $7000,y - lda #$0700 - sta $6000,y - lda #$0600 - sta $5000,y - lda #$0500 - sta $4000,y - lda #$0400 - sta $3000,y - lda #$0300 - sta $2000,y - lda #$0200 - sta $1000,y - lda #$0100 - sta: $0000,y - rts - -; SetAbsAddrs -; -; A = absolute address (largest) -; Y = offset -; X = number of lines -; -; Stores a value and decrements by $1000 for each line -SetAbsAddrs sec - jmp (:tbl,x) -:tbl da :bottom-00,:bottom-03,:bottom-09,:bottom-15 - da :bottom-21,:bottom-27,:bottom-33,:bottom-39 - da :bottom-45,:bottom-51,:bottom-57,:bottom-63 - da :bottom-69,:bottom-75,:bottom-81,:bottom-87 - da :bottom-93 -:top sta $F000,y - sbc #$1000 - sta $E000,y - sbc #$1000 - sta $D000,y - sbc #$1000 - sta $C000,y - sbc #$1000 - sta $B000,y - sbc #$1000 - sta $A000,y - sbc #$1000 - sta $9000,y - sbc #$1000 - sta $8000,y - sbc #$1000 - sta $7000,y - sbc #$1000 - sta $6000,y - sbc #$1000 - sta $5000,y - sbc #$1000 - sta $4000,y - sbc #$1000 - sta $3000,y - sbc #$1000 - sta $2000,y - sbc #$1000 - sta $1000,y - sbc #$1000 - sta: $0000,y -:bottom rts - -; Fill up a full bank with blitter templates. Currently we can fit 16 lines per bank, so need -; a total of 13 banks to hold the 208 lines for full-screen support -; -; A = high word of bank table -; Y = index * 4 of the bank to initialize -BuildBank - -:bankArray equ tmp0 -:target equ tmp2 -:nextBank equ tmp4 - - stx :bankArray - sta :bankArray+2 - - stz :target - iny - iny - lda [:bankArray],y - sta :target+2 - - iny ; move to the next item - iny - iny ; middle byte - cpy #4*13 ; if greater than the array length, wrap back to zero - bcc :ok - ldy #1 -:ok lda [:bankArray],y ; Get the middle and high bytes of the address - sta :nextBank - -:next - jsr :BuildLine2 - lda :target - clc - adc #$1000 - sta :target - bcc :next - - phb - pei :target+1 - plb - plb - -; Change the patched value to one of DP_ENTRY, TWO_LYR_ENTRY or ONE_LYR_ENTRY based on the capabilities -; that the engine needs. - - lda #$F000+{DP_ENTRY} ; Set the address from each line to the next - ldy #CODE_EXIT+1 - ldx #15*2 - jsr SetAbsAddrs - - ldy #DP_ADDR - jsr SetDPAddrs - - ldy #$F000+CODE_EXIT ; Patch the last line with a JML to go to the next bank - lda #{$005C+{DP_ENTRY}*256} - sta [:target],y - ldy #$F000+CODE_EXIT+2 - lda :nextBank - sta [:target],y - - ldy #$8000+CODE_EXIT ; Patch one line per bank to enable interrupts - lda #{$004C+{ENABLE_INT}*256} - sta [:target],y - - plb - rts - -; This is the relocation subroutine, it is responsible for copying the template to a -; memory location and patching up the necessary instructions. -; -; X = low word of address (must be a multiple of $1000) -; A = high word of address (bank) -:BuildLine - stx :target - sta :target+2 - -:BuildLine2 - lda #CODE_LEN ; round up to an even number of bytes - inc - and #$FFFE - beq :nocopy - dec - dec - tay -:loop lda base,y - sta [:target],y - - dey - dey - bpl :loop - -:nocopy lda #0 ; copy is complete, now patch up the addresses - sep #$20 - - ldx #0 - lda :target+2 ; patch in the bank for the absolute long addressing mode -:dobank ldy BankPatches,x - sta [:target],y - inx - inx - cpx #BankPatchNum - bcc :dobank - - ldx #0 -:dopage ldy PagePatches,x ; patch the page addresses by adding the page offset to each - lda [:target],y - clc - adc :target+1 - sta [:target],y - inx - inx - cpx #PagePatchNum - bcc :dopage - -:out - rep #$20 - rts - ; Start of the template code. This code is replicated 16 times per bank and spans ; 13 banks for a total of 208 lines, which is what is required to render 26 tiles ; to cover the full screen vertical scrolling. From 5745482ef6dabc07a44d501b7fc5b428bec61575 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 25 Apr 2022 11:32:25 -0500 Subject: [PATCH 09/82] Fix bank setting on toolset entry --- src/Tool.s | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Tool.s b/src/Tool.s index da77a71..0a499c9 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -46,8 +46,8 @@ _CallTable adrl _TSReserved-1 adrl _TSReserved-1 - adrl _TSReadControl-1 - adrl _TSSetScreenMode-1 + adrl _TSReadControl-1 ; Function 9 + adrl _TSSetScreenMode-1 ; Function 10 _CTEnd ; Do nothing when the tool set is installed @@ -82,7 +82,11 @@ zpToUse = userId+4 lda capFlags+2,s ; Get the engine capability bits sta EngineMode + phb + phk + plb jsr _CoreStartUp ; Initialize the library + plb ; SetWAP(userOrSystem, tsNum, waptPtr) @@ -106,7 +110,11 @@ _TSShutDown phd tcd ; Set the direct page for the toolset + phb + phk + plb jsr _CoreShutDown ; Shut down the library + plb pea $8000 pei ToolNum @@ -163,7 +171,7 @@ width equ FirstParam+2 tay lda width,s tax -; jsr _SetScreenMode ; Not implemented yet +; jsr _SetScreenMode _TSExit #0;#4 @@ -177,11 +185,15 @@ output equ FirstParam _TSExit #0;#0 -; Insert the core GTE functions +; Insert the GTE code + put Math.s put CoreImpl.s put Memory.s put Timer.s -; put Graphics.s -; put blitter/Template.s + put Graphics.s + put blitter/BG0.s + put blitter/BG1.s + put blitter/Template.s put blitter/Tables.s + put blitter/Blitter.s From e5da3991cd09cd55bd4b6860a6554bc28f440320 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 25 Apr 2022 16:34:54 -0500 Subject: [PATCH 10/82] Start to split the tiles into high-level and low-level code --- src/Tiles.s | 124 ++++++++++++++++++++++ src/blitter/Tiles.s | 246 ++++++++++++-------------------------------- 2 files changed, 189 insertions(+), 181 deletions(-) create mode 100644 src/Tiles.s diff --git a/src/Tiles.s b/src/Tiles.s new file mode 100644 index 0000000..37aaf3a --- /dev/null +++ b/src/Tiles.s @@ -0,0 +1,124 @@ +; Basic tile functions + + +; 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 +; incremental calculations, but it is handy for other utility functions +; +; A = tile descriptor +; +; The address is the TileID * 128 + (HFLIP * 64) +GetTileAddr ENT + jsr _GetTileAddr + rtl +_GetTileAddr + asl ; Multiply by 2 + bit #2*TILE_HFLIP_BIT ; Check if the horizontal flip bit is set + beq :no_flip + inc ; Set the LSB +:no_flip asl ; x4 + asl ; x8 + asl ; x16 + asl ; x32 + asl ; x64 + asl ; x128 + rts + +; Ignore the horizontal flip bit +_GetBaseTileAddr + asl ; Multiply by 2 + asl ; x4 + asl ; x8 + asl ; x16 + asl ; x32 + asl ; x64 + asl ; x128 + rts + + +; Initialize the tile storage data structures. This takes care of populating the tile records with the +; appropriate constant values. +InitTiles +:col equ tmp0 +:row equ tmp1 +:vbuff equ tmp2 + +; Fill in the TileStoreYTable. This is just a table of offsets into the Tile Store for each row. There +; are 26 rows with a stride of 41 + ldy #0 + lda #0 +:yloop + sta TileStoreYTable,y + clc + adc #41*2 + iny + iny + cpy #26*2 + bcc :yloop + +; Next, initialize the Tile Store itself + + ldx #TILE_STORE_SIZE-2 + lda #25 + sta :row + lda #40 + sta :col + lda #$8000 + sta :vbuff + +:loop + +; The first set of values in the Tile Store are changed during each frame based on the actions +; that are happening + + lda #0 + stal TileStore+TS_TILE_ID,x ; clear the tile store with the special zero tile + stal TileStore+TS_TILE_ADDR,x + stal TileStore+TS_SPRITE_FLAG,x ; no sprites are set at the beginning + stal TileStore+TS_DIRTY,x ; none of the tiles are dirty + +; 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 + + lda :vbuff ; array of sprite vbuff addresses per tile + stal TileStore+TS_VBUFF_ARRAY_ADDR,x + clc + adc #32 + sta :vbuff + +; 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 + + lda :row ; Set the long address of where this tile + asl ; exists in the code fields + tay + lda BRowTableHigh,y + stal TileStore+TS_CODE_ADDR_HIGH,x ; High word of the tile address (just the bank) + lda BRowTableLow,y + stal TileStore+TS_BASE_ADDR,x ; May not be needed later if we can figure out the right constant... + + lda :col ; Set the offset values based on the column + asl ; of this tile + asl + stal TileStore+TS_WORD_OFFSET,x ; This is the offset from 0 to 82, used in LDA (dp),y instruction + + tay + lda Col2CodeOffset+2,y + clc + adcl TileStore+TS_BASE_ADDR,x + stal TileStore+TS_CODE_ADDR_LOW,x ; Low word of the tile address in the code field + + dec :col + bpl :hop + dec :row + lda #40 + sta :col +:hop + + dex + dex + bpl :loop + rts diff --git a/src/blitter/Tiles.s b/src/blitter/Tiles.s index 0a9f66e..e0a25a5 100644 --- a/src/blitter/Tiles.s +++ b/src/blitter/Tiles.s @@ -41,40 +41,13 @@ TILE_CTRL_MASK equ $FE00 TILE_PROC_MASK equ $F800 ; Select tile proc for rendering -; 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 -; incremental calculations, but it is handy for other utility functions -; -; A = tile descriptor -; -; The address is the TileID * 128 + (HFLIP * 64) -GetTileAddr ENT - jsr _GetTileAddr - rtl -_GetTileAddr - asl ; Multiply by 2 - bit #2*TILE_HFLIP_BIT ; Check if the horizontal flip bit is set - beq :no_flip - inc ; Set the LSB -:no_flip asl ; x4 - asl ; x8 - asl ; x16 - asl ; x32 - asl ; x64 - asl ; x128 - rts +; Use some temporary space for the spriteIdx array (maximum of 4 entries) + +stkSave equ tmp9 +screenAddr equ tmp10 +tileAddr equ tmp11 +spriteIdx equ tmp12 -; Ignore the horizontal flip bit -_GetBaseTileAddr - asl ; Multiply by 2 - asl ; x4 - asl ; x8 - asl ; x16 - asl ; x32 - asl ; x64 - asl ; x128 - rts - ; On entry ; ; B is set to the correct BG1 data bank @@ -115,7 +88,7 @@ _RenderTile2 bne do_dirty_sprite ; Handle the non-sprite tile blit - +CopyNoSprites sep #$20 lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line pha ; and put on the stack for later @@ -182,75 +155,75 @@ dirty_sprite_dispatch ; because that code draws directly to the graphics screen, and this code draws ; to a temporary budder that has a different stride. - ldy TileStore+TS_VBUFF_ARRAY_ADDR,x ; base address of the VBUFF sprite address array for this tile +; ldy TileStore+TS_VBUFF_ARRAY_ADDR,x ; base address of the VBUFF sprite address array for this tile +; +; lsr +; bcc :loop_0_bit_1 +; dobit $0000;sprite_ptr0;:loop_1_bit_1;CopyOneSprite - lsr - bcc :loop_0_bit_1 - dobit $0000;sprite_ptr0;:loop_1_bit_1;CopyOneSprite +;:loop_0_bit_1 lsr +; bcc :loop_0_bit_2 +; dobit $0002;sprite_ptr0;:loop_1_bit_2;CopyOneSprite -:loop_0_bit_1 lsr - bcc :loop_0_bit_2 - dobit $0002;sprite_ptr0;:loop_1_bit_2;CopyOneSprite +;:loop_0_bit_2 lsr +; bcc :loop_0_bit_3 +; dobit $0004;sprite_ptr0;:loop_1_bit_3;CopyOneSprite -:loop_0_bit_2 lsr - bcc :loop_0_bit_3 - dobit $0004;sprite_ptr0;:loop_1_bit_3;CopyOneSprite +;:loop_0_bit_3 lsr +; bcc :loop_0_bit_4 +; dobit $0006;sprite_ptr0;:loop_1_bit_4;CopyOneSprite -:loop_0_bit_3 lsr - bcc :loop_0_bit_4 - dobit $0006;sprite_ptr0;:loop_1_bit_4;CopyOneSprite +;:loop_0_bit_4 lsr +; bcc :loop_0_bit_5 +; dobit $0008;sprite_ptr0;:loop_1_bit_5;CopyOneSprite -:loop_0_bit_4 lsr - bcc :loop_0_bit_5 - dobit $0008;sprite_ptr0;:loop_1_bit_5;CopyOneSprite +;:loop_0_bit_5 lsr +; bcc :loop_0_bit_6 +; dobit $000A;sprite_ptr0;:loop_1_bit_6;CopyOneSprite -:loop_0_bit_5 lsr - bcc :loop_0_bit_6 - dobit $000A;sprite_ptr0;:loop_1_bit_6;CopyOneSprite +;:loop_0_bit_6 lsr +; bcc :loop_0_bit_7 +; dobit $000C;sprite_ptr0;:loop_1_bit_7;CopyOneSprite -:loop_0_bit_6 lsr - bcc :loop_0_bit_7 - dobit $000C;sprite_ptr0;:loop_1_bit_7;CopyOneSprite +;:loop_0_bit_7 lsr +; bcc :loop_0_bit_8 +; dobit $000E;sprite_ptr0;:loop_1_bit_8;CopyOneSprite -:loop_0_bit_7 lsr - bcc :loop_0_bit_8 - dobit $000E;sprite_ptr0;:loop_1_bit_8;CopyOneSprite +;:loop_0_bit_8 lsr +; bcc :loop_0_bit_9 +; dobit $0010;sprite_ptr0;:loop_1_bit_9;CopyOneSprite -:loop_0_bit_8 lsr - bcc :loop_0_bit_9 - dobit $0010;sprite_ptr0;:loop_1_bit_9;CopyOneSprite +;:loop_0_bit_9 lsr +; bcc :loop_0_bit_10 +; ldx: $0012,y +; stx spriteIdx +; cmp #0 +; jne :loop_1_bit_10 +; jmp CopyOneSprite -:loop_0_bit_9 lsr - bcc :loop_0_bit_10 - ldx: $0012,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_10 - jmp CopyOneSprite +;:loop_0_bit_10 lsr +; bcc :loop_0_bit_11 +; dobit $0014;sprite_ptr0;:loop_1_bit_11;CopyOneSprite -:loop_0_bit_10 lsr - bcc :loop_0_bit_11 - dobit $0014;sprite_ptr0;:loop_1_bit_11;CopyOneSprite +;:loop_0_bit_11 lsr +; bcc :loop_0_bit_12 +; dobit $0016;sprite_ptr0;:loop_1_bit_12;CopyOneSprite -:loop_0_bit_11 lsr - bcc :loop_0_bit_12 - dobit $0016;sprite_ptr0;:loop_1_bit_12;CopyOneSprite +;:loop_0_bit_12 lsr +; bcc :loop_0_bit_13 +; dobit $0018;sprite_ptr0;:loop_1_bit_13;CopyOneSprite -:loop_0_bit_12 lsr - bcc :loop_0_bit_13 - dobit $0018;sprite_ptr0;:loop_1_bit_13;CopyOneSprite +;:loop_0_bit_13 lsr +; bcc :loop_0_bit_14 +; dobit $001A;sprite_ptr0;:loop_1_bit_14;CopyOneSprite -:loop_0_bit_13 lsr - bcc :loop_0_bit_14 - dobit $001A;sprite_ptr0;:loop_1_bit_14;CopyOneSprite +;:loop_0_bit_14 lsr +; bcc :loop_0_bit_15 +; dobit $001C;sprite_ptr0;:loop_1_bit_15;CopyOneSprite -:loop_0_bit_14 lsr - bcc :loop_0_bit_15 - dobit $001C;sprite_ptr0;:loop_1_bit_15;CopyOneSprite - -:loop_0_bit_15 ldx: $001E,y - stx spriteIdx - jmp CopyOneSprite +;:loop_0_bit_15 ldx: $001E,y +; stx spriteIdx +; jmp CopyOneSprite ; We can optimize later, for now just copy the sprite data and mask into its own ; direct page buffer and combine with the tile data later @@ -388,12 +361,10 @@ CopyOneSprite ]line equ 0 lup 8 - ldal tiledata,x - and [spriteIdx] - ora (spriteIdx) - sta tmp_sprite_data+{]line*4} - - +; ldal tiledata,x +; and [spriteIdx] +; ora (spriteIdx) +; sta tmp_sprite_data+{]line*4} ldal spritedata+{]line*SPRITE_PLANE_SPAN},x sta tmp_sprite_data+{]line*4} @@ -790,93 +761,6 @@ _CopyBG1Tile 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. -InitTiles -:col equ tmp0 -:row equ tmp1 -:vbuff equ tmp2 - -; Fill in the TileStoreYTable. This is just a table of offsets into the Tile Store for each row. There -; are 26 rows with a stride of 41 - ldy #0 - lda #0 -:yloop - sta TileStoreYTable,y - clc - adc #41*2 - iny - iny - cpy #26*2 - bcc :yloop - -; Next, initialize the Tile Store itself - - ldx #TILE_STORE_SIZE-2 - lda #25 - sta :row - lda #40 - sta :col - lda #$8000 - sta :vbuff - -:loop - -; The first set of values in the Tile Store are changed during each frame based on the actions -; that are happening - - lda #0 - stal TileStore+TS_TILE_ID,x ; clear the tile store with the special zero tile - stal TileStore+TS_TILE_ADDR,x - stal TileStore+TS_SPRITE_FLAG,x ; no sprites are set at the beginning - stal TileStore+TS_DIRTY,x ; none of the tiles are dirty - - 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 - - lda :vbuff ; array of sprite vbuff addresses per tile - stal TileStore+TS_VBUFF_ARRAY_ADDR,x - clc - adc #32 - sta :vbuff - -; 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 - - lda :row ; Set the long address of where this tile - asl ; exists in the code fields - tay - lda BRowTableHigh,y - stal TileStore+TS_CODE_ADDR_HIGH,x ; High word of the tile address (just the bank) - lda BRowTableLow,y - stal TileStore+TS_BASE_ADDR,x ; May not be needed later if we can figure out the right constant... - - lda :col ; Set the offset values based on the column - asl ; of this tile - asl - stal TileStore+TS_WORD_OFFSET,x ; This is the offset from 0 to 82, used in LDA (dp),y instruction - - tay - lda Col2CodeOffset+2,y - clc - adcl TileStore+TS_BASE_ADDR,x - stal TileStore+TS_CODE_ADDR_LOW,x ; Low word of the tile address in the code field - - dec :col - bpl :hop - dec :row - lda #40 - sta :col -:hop - - dex - dex - bpl :loop - rts - _ClearDirtyTiles bra :hop :loop From 1522922abece0bd4d4133d19b21e2cd21947a4f5 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 25 Apr 2022 16:35:47 -0500 Subject: [PATCH 11/82] Complete initialization sequence. --- src/CoreImpl.s | 16 ++++++++-------- src/Defs.s | 36 ++++++++++-------------------------- src/Render.s | 4 +--- src/Sprite.s | 15 ++++++--------- src/Tool.s | 5 ++++- 5 files changed, 29 insertions(+), 47 deletions(-) diff --git a/src/CoreImpl.s b/src/CoreImpl.s index 81c7a89..46ce903 100644 --- a/src/CoreImpl.s +++ b/src/CoreImpl.s @@ -87,16 +87,16 @@ Overlay EXT ; Assumes the direct page is set and EngineMode and UserId has been initialized _CoreStartUp - jsr IntStartUp ; Enable certain interrupts + jsr IntStartUp ; Enable certain interrupts - jsr InitMemory ; Allocate and initialize memory for the engine -; jsr EngineReset ; All of the resources are allocated, put the engine in a known state + jsr InitMemory ; Allocate and initialize memory for the engine + jsr EngineReset ; All of the resources are allocated, put the engine in a known state -; jsr InitGraphics ; Initialize all of the graphics-related data -; jsr InitSprites ; Initialize the sprite subsystem -; jsr InitTiles ; Initialize the tile subsystem + jsr InitGraphics ; Initialize all of the graphics-related data +; jsr InitSprites ; Initialize the sprite subsystem + jsr InitTiles ; Initialize the tile subsystem -; jsr InitTimers ; Initialize the timer subsystem + jsr InitTimers ; Initialize the timer subsystem rts _CoreShutDown @@ -243,7 +243,7 @@ EngineReset ldx #BlitBuff lda #^BlitBuff ldy tmp14 -; jsr BuildBank + jsr BuildBank lda tmp14 clc diff --git a/src/Defs.s b/src/Defs.s index f59e71f..ca080a6 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -199,7 +199,11 @@ VBUFF_SPRITE_STEP equ VBUFF_TILE_ROW_BYTES*3 ; Allocate space fo 16 VBUFF_SPRITE_START equ {8*VBUFF_TILE_ROW_BYTES}+4 ; Start at an offset so $0000 can be used as an empty value VBUFF_SLOT_COUNT equ 48 ; Have space for this many stamps +; This is 13 blocks wide +SPRITE_PLANE_SPAN equ VBUFF_STRIDE_BYTES + ; Tile storage parameters +TILE_DATA_SPAN equ 4 TILE_STORE_WIDTH equ 41 TILE_STORE_HEIGHT equ 26 MAX_TILES equ {26*41} ; Number of tiles in the code field (41 columns * 26 rows) @@ -218,29 +222,9 @@ TS_VBUFF_ARRAY_ADDR equ TILE_STORE_SIZE*9 ; const value to an aligned 32 TS_BASE_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function TS_DIRTY_TILE_DISP equ TILE_STORE_SIZE*11 ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function -; 16 consecutive entries to provide directly addressable space for holding the VBUFF address for the -; sprites that may be rendered at a given tile. Given a tile store offset, X, the way to address the -; address for the Y'th sprite is -; -; lda TileStore+TS_VBUFF_0+{Y*TILE_STORE_SIZE},x -; -; Moving to the next tile can be done with a constant. -; -; lda TileStore+TS_VBUFF_0+{Y*TILE_STORE_SIZE}+{41*row}+{2*col},x - -TS_VBUFF_0 equ TILE_STORE_SIZE*12 -TS_VBUFF_1 equ TILE_STORE_SIZE*13 -TS_VBUFF_2 equ TILE_STORE_SIZE*14 -TS_VBUFF_3 equ TILE_STORE_SIZE*15 -TS_VBUFF_4 equ TILE_STORE_SIZE*16 -TS_VBUFF_5 equ TILE_STORE_SIZE*17 -TS_VBUFF_6 equ TILE_STORE_SIZE*18 -TS_VBUFF_7 equ TILE_STORE_SIZE*19 -TS_VBUFF_8 equ TILE_STORE_SIZE*20 -TS_VBUFF_9 equ TILE_STORE_SIZE*21 -TS_VBUFF_10 equ TILE_STORE_SIZE*22 -TS_VBUFF_11 equ TILE_STORE_SIZE*23 -TS_VBUFF_12 equ TILE_STORE_SIZE*22 -TS_VBUFF_13 equ TILE_STORE_SIZE*23 -TS_VBUFF_14 equ TILE_STORE_SIZE*24 -TS_VBUFF_15 equ TILE_STORE_SIZE*25 +; Hold values for up to 4 sprites per tile +TS_VBUFF_ADDR_0 equ TILE_STORE_SIZE*12 +TS_VBUFF_ADDR_1 equ TILE_STORE_SIZE*13 +TS_VBUFF_ADDR_2 equ TILE_STORE_SIZE*14 +TS_VBUFF_ADDR_3 equ TILE_STORE_SIZE*15 +TS_VBUFF_ADDR_COUNT equ TILE_STORE_SIZE*16 ; replace usage of TS_VBUFF_ARRAY_ADDR with this later diff --git a/src/Render.s b/src/Render.s index 9e5e63f..4f52d89 100644 --- a/src/Render.s +++ b/src/Render.s @@ -669,7 +669,7 @@ dirty_sprite stx spriteIdx+6 jmp BlitFourSprites -DirtyTileProcs dw _TBDirtyTile_00,_TBDirtyTile_0H,_TBDirtyTile_V0,_TBDirtyTile_VH +DirtyTileProcs dw _TBDirtyTile_00,_TBDirtyTile_0H,_TBDirtyTile_V0,_TBDirtyTile_VH ;DirtyTileSpriteProcs dw _TBDirtySpriteTile_00,_TBDirtySpriteTile_0H,_TBDirtySpriteTile_V0,_TBDirtySpriteTile_VH ; Blit tiles directly to the screen. @@ -699,8 +699,6 @@ _TBDirtyTile_VH --^ rts -TILE_DATA_SPAN equ 4 - ; 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 diff --git a/src/Sprite.s b/src/Sprite.s index b659ed8..7419d26 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -76,12 +76,12 @@ InitSprites ; Return: ; A = vbuff address to be assigned to Sprite[VBUFF_ADDR] CreateSpriteStamp ENT - phb - phk - plb - jsr _CreateSpriteStamp - plb - rtl + phb + phk + plb + jsr _CreateSpriteStamp + plb + rtl _CreateSpriteStamp pha ; Save the descriptor @@ -752,9 +752,6 @@ _CacheSpriteBanks rts -; This is 13 blocks wide -SPRITE_PLANE_SPAN equ VBUFF_STRIDE_BYTES - ; A = x coordinate ; Y = y coordinate ;GetSpriteVBuffAddr ENT diff --git a/src/Tool.s b/src/Tool.s index 0a499c9..6a66d5e 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -171,7 +171,7 @@ width equ FirstParam+2 tay lda width,s tax -; jsr _SetScreenMode + jsr _SetScreenMode _TSExit #0;#4 @@ -192,8 +192,11 @@ output equ FirstParam put Memory.s put Timer.s put Graphics.s + put Tiles.s put blitter/BG0.s put blitter/BG1.s put blitter/Template.s + put blitter/TemplateUtils.s put blitter/Tables.s put blitter/Blitter.s +; put blitter/Tiles.s From 4d6f11a9ba49bea6db94d78ae3f0c6e2650a404f Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 25 Apr 2022 17:11:48 -0500 Subject: [PATCH 12/82] Initialize the graphics screen via the toolset --- demos/tool/App.Main.s | 34 ++++++++++++++++++++++++++++++++-- demos/tool/package.json | 4 +++- src/Tool.s | 31 +++++++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 40a3311..32e0f8f 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -11,6 +11,9 @@ mx %00 +ScreenX equ 0 +ScreenY equ 2 + ; Typical init phk plb @@ -20,6 +23,23 @@ jsr GTEStartUp ; Load and install the GTE User Tool +; Initialize the graphics screen to a 256x160 playfield + + pea #256 + pea #160 + _GTESetScreenMode + +; Load a tileset in from an uncompressed $C1 picture. The top-left 256x128 rectangle is used +; to populate the 512 tiles. + + +; Manually fill in the 41x26 tiles of the TileStore with a test pattern. + + +; Set the origin of the screen + stz ScreenX + stz ScreenY + ; Very simple actions :loop pha ; space for result, with pattern @@ -27,9 +47,19 @@ pla and #$00FF cmp #'q' - bne :loop + beq :exit -; Shut down eveything +; pei ScreenX +; pei ScreenY +; _GTESetBG0Origin + +; _GTERender + + inc ScreenX ; Just keep incrementing, it's OK + bra :loop + +; Shut down everything +:exit _GTEShutDown _QuitGS qtRec qtRec adrl $0000 diff --git a/demos/tool/package.json b/demos/tool/package.json index 0c8f1e9..cbf2360 100644 --- a/demos/tool/package.json +++ b/demos/tool/package.json @@ -13,7 +13,9 @@ "scripts": { "test": "npm run build && build-image.bat %npm_package_config_cadius% && %npm_package_config_gsport%", "debug": "%npm_package_config_crossrunner% GTEToolDemo -Source MAINSEG_Output.txt -Debug -CompatibilityLayer", - "build": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.s" + "build": "npm run build:tool && npm run build:sys16", + "build:sys16": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.s", + "build:tool": "%npm_package_config_merlin32% -V %npm_package_config_macros% ../../src/Master.s" }, "repository": { "type": "git", diff --git a/src/Tool.s b/src/Tool.s index 6a66d5e..0bd14fe 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -46,8 +46,11 @@ _CallTable adrl _TSReserved-1 adrl _TSReserved-1 - adrl _TSReadControl-1 ; Function 9 - adrl _TSSetScreenMode-1 ; Function 10 + adrl _TSReadControl-1 + adrl _TSSetScreenMode-1 + adrl _TSSetTile-1 + adrl _TSSetBG0Origin-1 + adrl _TSRender-1 _CTEnd ; Do nothing when the tool set is installed @@ -175,6 +178,7 @@ width equ FirstParam+2 _TSExit #0;#4 +; ReadControl() _TSReadControl output equ FirstParam @@ -183,8 +187,31 @@ output equ FirstParam jsr _ReadControl sta output,s + _TSExit #0;#0 + +; SetTile(xTile, yTile, tileId) +_TSSetTile +tileId equ FirstParam +yTile equ FirstParam+2 +xTile equ FirstParam+4 + + _TSEntry + _TSExit #0;#6 + +; SetBG0Origin(x, y) +_TSSetBG0Origin +yPos equ FirstParam +xPos equ FirstParam+2 + + _TSEntry + _TSExit #0;#4 + +; Render() +_TSRender + _TSEntry _TSExit #0;#0 + ; Insert the GTE code put Math.s From 7050b3613a222aa0719806e555d4d17261318365 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 27 Apr 2022 00:05:38 -0500 Subject: [PATCH 13/82] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 96f0717..00b4dfa 100644 --- a/README.md +++ b/README.md @@ -210,4 +210,5 @@ GTE provides the following capabilities * [Adaptive Tile Refresh](https://en.wikipedia.org/wiki/Adaptive_tile_refresh) * [A Guide to the Graphics of the Sega Mega Drive / Genesis](https://rasterscroll.com/mdgraphics/) * [Jon Burton / Traveller's Tales / Coding Secrets](https://ttjontt.wixsite.com/gamehut/coding-secrets) -* [Lou's Pseudo 3d Page](http://www.extentofthejam.com/pseudo/) \ No newline at end of file +* [Lou's Pseudo 3d Page](http://www.extentofthejam.com/pseudo/) +* [A Great Old-Timey Game-Programming Hack](https://blog.moertel.com/posts/2013-12-14-great-old-timey-game-programming-hack.html) From 7f6e5d1b1fc7270e42153e4eb8baeca172fc633d Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Fri, 29 Apr 2022 12:38:04 -0500 Subject: [PATCH 14/82] Work checkpoint --- demos/tool/App.Main.s | 8 +- demos/tool/build-image.bat | 2 +- macros/GTE.Macs.s | 11 ++- src/Defs.s | 6 +- src/Master.s | 2 +- src/Render.s | 86 +------------------ src/Tiles.s | 151 +++++++++++++++++++++++++++++++-- src/Tool.s | 16 ++++ src/_FileInformation.txt | 3 +- src/blitter/Horz.s | 27 ------ src/blitter/Tiles.s | 131 ---------------------------- src/blitter/Tiles00000.s | 52 +----------- src/blitter/Tiles10000.s | 113 ------------------------ src/blitter/TilesBG1.s | 25 ++++++ src/blitter/Vert.s | 22 ----- src/sprites/DirtySpriteProcs.s | 92 ++++++++++++++++++++ src/sprites/SpriteProcs.s | 150 ++++++++++++++++++++++++++++++++ src/tiles/DirtyTileProcs.s | 43 ++++++++++ src/tiles/FastRenderer.s | 151 +++++++++++++++++++++++++++++++++ src/tiles/TileProcs.s | 93 ++++++++++++++++++++ 20 files changed, 742 insertions(+), 442 deletions(-) create mode 100644 src/sprites/DirtySpriteProcs.s create mode 100644 src/sprites/SpriteProcs.s create mode 100644 src/tiles/DirtyTileProcs.s create mode 100644 src/tiles/FastRenderer.s create mode 100644 src/tiles/TileProcs.s diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 32e0f8f..db6741b 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -49,9 +49,9 @@ ScreenY equ 2 cmp #'q' beq :exit -; pei ScreenX -; pei ScreenY -; _GTESetBG0Origin + pei ScreenX + pei ScreenY + _GTESetBG0Origin ; _GTERender @@ -119,4 +119,4 @@ GTEStartUp MasterId ds 2 UserId ds 2 -ToolPath str '1/GTETool' \ No newline at end of file +ToolPath str '1/Tool160' \ No newline at end of file diff --git a/demos/tool/build-image.bat b/demos/tool/build-image.bat index 5546b72..462afd6 100644 --- a/demos/tool/build-image.bat +++ b/demos/tool/build-image.bat @@ -14,6 +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% .\GTEToolDemo -%CADIUS% ADDFILE %IMAGE% %FOLDER% ..\..\src\GTETool +%CADIUS% ADDFILE %IMAGE% %FOLDER% ..\..\src\Tool160 REM Copy in the image assets diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s index b60d55b..a89cafa 100644 --- a/macros/GTE.Macs.s +++ b/macros/GTE.Macs.s @@ -26,4 +26,13 @@ _GTEReadControl MAC <<< _GTESetScreenMode MAC UserTool $A00+GTEToolNum - <<< + <<< +_GTESetTile MAC + UserTool $B00+GTEToolNum + <<< +_GTESetBG0Origin MAC + UserTool $C00+GTEToolNum + <<< +_GTERender MAC + UserTool $D00+GTEToolNum + <<< diff --git a/src/Defs.s b/src/Defs.s index ca080a6..468528b 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -182,6 +182,8 @@ 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 ; Sprite constants SPRITE_HIDE equ $2000 @@ -218,7 +220,9 @@ TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5 TS_WORD_OFFSET equ TILE_STORE_SIZE*6 ; const value, word offset value for this tile if LDA (dp),y instructions re used TS_BASE_ADDR equ TILE_STORE_SIZE*7 ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000. TS_SCREEN_ADDR equ TILE_STORE_SIZE*8 ; cached value of on-screen location of tile. Used for DirtyRender. -TS_VBUFF_ARRAY_ADDR equ TILE_STORE_SIZE*9 ; const value to an aligned 32-byte array starting at $8000 in TileStore bank +;TS_VBUFF_ARRAY_ADDR equ TILE_STORE_SIZE*9 ; const value to an aligned 32-byte array starting at $8000 in TileStore bank + +TS_BASE_TILE_COPY equ TILE_STORE_SIZE*9 ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering TS_BASE_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function TS_DIRTY_TILE_DISP equ TILE_STORE_SIZE*11 ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function diff --git a/src/Master.s b/src/Master.s index 175f3a7..9c9a012 100644 --- a/src/Master.s +++ b/src/Master.s @@ -1,7 +1,7 @@ ; IIgs Generic Tile Engine User Toolset TYP $BA ; Tool set file - DSK GTETool + DSK Tool160 XPL ; Main toolbox interface and code diff --git a/src/Render.s b/src/Render.s index 4f52d89..d78d13b 100644 --- a/src/Render.s +++ b/src/Render.s @@ -10,15 +10,7 @@ ; ; Everything is composited into the tiles in the playfield and then the screen is rendered in ; a single pass. - -Render ENT - phb - phk - plb - jsr _Render - plb - rtl - +; ; TODO -- actually check the dirty bits and be selective on what gets updated. For example, if ; only the Y position changes, then we should only need to set new values on the ; virtual lines that were brought on screen. If the X position only changes by one @@ -36,13 +28,11 @@ _Render jsr _ApplyBG0XPosPre jsr _ApplyBG1XPosPre - nop 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 - nop jsr _ApplyTiles ; This function actually draws the new tiles into the code field jsr _ApplyBG0XPos ; Patch the code field instructions with exit BRA opcode @@ -108,58 +98,17 @@ _Render ; the sprite subsystem + tile attributes for single-screen games which should be able to run ; close to 60 fps. ; -; Because we are register starved, there is a lot of inline code to quickly fetch the information -; needed to render sprites appropriately. If there was a way to efficiently maintain an ordered -; and compact array of per-tile VBUFF addresses, rather than the current sparse array, then -; the sprite handling code could be significantly streamlined. A note for anyone attempting -; this optimization: -; -; The _MarkDirtyTiles simply stores a sprite's per-tile VBUFF address and marks the tile -; as being occupied by the sprite with just 4 instructions -; -; sta (vbuff_array_ptr),y -; lda TileStore+TS_SPRITE_FLAG,x -; ora SpriteBit,y -; sta TileStore+TS_SPRITE_FLAG,x -; -; Then, we have an unrolled loop that does repeated tests of -; -; lsr -; bcc *+ -; lda vbuff_array_ptr,y -; sta spriteVBuffArr -; -; The only gain to be had is if the sprites that are marked are in the high bits and there are no low-index -; sprites. Skipping over N bits of the SPRITE_FLAG takes only 5*N cycles. So, on average, we might waste -; 40 cycles looking for the proper bit. -; -; Any improvement to the existing code would need to be able to maintain a data structure and get the final -; values into the spriteVBuffArr for a total cost of under 75 cycles per tile. - -RenderDirty ENT - phb - phk - plb - jsr _RenderDirty - plb - rtl - ; 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 - lda #2 ; blue - jsr _SetBorderColor jsr _RecalcTileScreenAddrs ; them to make sure sprites draw at the correct screen address :norecalc - lda #3 ; purple - jsr _SetBorderColor - jsr _RenderSprites - lda #4 ; dk. green - jsr _SetBorderColor + jsr _RenderSprites jsr _ApplyDirtyTiles + lda #1 sta LastRender rts @@ -669,35 +618,6 @@ dirty_sprite stx spriteIdx+6 jmp BlitFourSprites -DirtyTileProcs dw _TBDirtyTile_00,_TBDirtyTile_0H,_TBDirtyTile_V0,_TBDirtyTile_VH -;DirtyTileSpriteProcs dw _TBDirtySpriteTile_00,_TBDirtySpriteTile_0H,_TBDirtySpriteTile_V0,_TBDirtySpriteTile_VH - -; Blit tiles directly to the screen. -_TBDirtyTile_00 -_TBDirtyTile_0H -]line equ 0 - lup 8 - ldal tiledata+{]line*4},x - sta: $0000+{]line*160},y - ldal tiledata+{]line*4}+2,x - sta: $0002+{]line*160},y -]line equ ]line+1 - --^ - rts - -_TBDirtyTile_V0 -_TBDirtyTile_VH -]src equ 7 -]dest equ 0 - lup 8 - ldal tiledata+{]src*4},x - sta: $0000+{]dest*160},y - ldal tiledata+{]src*4}+2,x - sta: $0002+{]dest*160},y -]src equ ]src-1 -]dest equ ]dest+1 - --^ - rts ; 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 diff --git a/src/Tiles.s b/src/Tiles.s index 37aaf3a..c50718b 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -8,9 +8,6 @@ ; A = tile descriptor ; ; The address is the TileID * 128 + (HFLIP * 64) -GetTileAddr ENT - jsr _GetTileAddr - rtl _GetTileAddr asl ; Multiply by 2 bit #2*TILE_HFLIP_BIT ; Check if the horizontal flip bit is set @@ -36,6 +33,29 @@ _GetBaseTileAddr 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 + ; Initialize the tile storage data structures. This takes care of populating the tile records with the ; appropriate constant values. InitTiles @@ -83,11 +103,13 @@ InitTiles ; lda TileProcs ; Same for non-dirty, non-sprite base case ; stal TileStore+TS_BASE_TILE_DISP,x - lda :vbuff ; array of sprite vbuff addresses per tile - stal TileStore+TS_VBUFF_ARRAY_ADDR,x - clc - adc #32 - sta :vbuff +; *** DEPRECATED *** +; lda :vbuff ; array of sprite vbuff addresses per tile +; stal TileStore+TS_VBUFF_ARRAY_ADDR,x +; clc +; adc #32 +; sta :vbuff +; *** ********** *** ; 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 @@ -122,3 +144,116 @@ InitTiles dex bpl :loop 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) +; +; Registers are not preserved +_SetTile + pha + jsr _GetTileStoreOffset0 ; Get the address of the X,Y tile position + tax + pla + + cmpl TileStore+TS_TILE_ID,x ; Only set to dirty if the value changed + beq :nochange + + stal TileStore+TS_TILE_ID,x ; Value is different, store it. + jsr _GetTileAddr + stal TileStore+TS_TILE_ADDR,x ; Committed to drawing this tile, so get the address of the tile in the tiledata bank for later + +; Set the standard renderer procs for this tile. +; +; 1. The dirty render proc is always set the same. +; 2. If BG1 and DYN_TILES are disabled, then the TS_BASE_TILE_DISP is selected from the Fast Renderers, otherwise +; it is selected from the full tile rendering functions. +; 3. The copy process is selected based on the flip bits +; +; When a tile overlaps the sprite, it is the responsibility of the Render function to compose the appropriate +; functionality. Sometimes it is simple, but in cases of the sprites overlapping Dynamic Tiles and other cases +; it can be more involved. + + ldal TileStore+TS_TILE_ID,x + and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value + xba + tay +; lda DirtyTileProcs,y +; stal TileStore+TS_DIRTY_TILE_DISP,x + +; lda CopyTileProcs,y +; stal TileStore+TS_DIRTY_TILE_COPY,x + + lda EngineMode + bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER + beq :fast + + ldal TileStore+TS_TILE_ID,x ; Get the non-sprite dispatch address + and #TILE_CTRL_MASK + xba + tay +; lda TileProcs,y +; stal TileStore+TS_BASE_TILE_DISP,x + bra :out + +:fast +; lda FastTileProcs,y +; stal TileStore+TS_BASE_TILE_DISP,x +:out + +; txa ; Add this tile to the list of dirty tiles to refresh +; jmp _PushDirtyTileX ; on the next call to _ApplyTiles + +:nochange rts + + +; SetBG0XPos +; +; Set the virtual horizontal position of the primary background layer. In addition to +; updating the direct page state locations, this routine needs to preserve the original +; value as well. This is a bit subtle, because if this routine is called multiple times +; with different values, we need to make sure the *original* value is preserved and not +; continuously overwrite it. +; +; We assume that there is a clean code field in this routine +SetBG0XPos ENT + jsr _SetBG0XPos + rtl + +_SetBG0XPos + cmp StartX + beq :out ; Easy, if nothing changed, then nothing changes + + ldx StartX ; Load the old value (but don't save it yet) + sta StartX ; Save the new position + + lda #DIRTY_BIT_BG0_X + tsb DirtyBits ; Check if the value is already dirty, if so exit + bne :out ; without overwriting the original value + + stx OldStartX ; First change, so preserve the value +:out rts + + +; SetBG0YPos +; +; Set the virtual position of the primary background layer. +SetBG0YPos ENT + jsr _SetBG0YPos + rtl + +_SetBG0YPos + cmp StartY + beq :out ; Easy, if nothing changed, then nothing changes + + ldx StartY ; Load the old value (but don't save it yet) + sta StartY ; Save the new position + + lda #DIRTY_BIT_BG0_Y + tsb DirtyBits ; Check if the value is already dirty, if so exit + bne :out ; without overwriting the original value + + stx OldStartY ; First change, so preserve the value +:out rts diff --git a/src/Tool.s b/src/Tool.s index 0bd14fe..87e5a92 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -196,6 +196,14 @@ yTile equ FirstParam+2 xTile equ FirstParam+4 _TSEntry + + lda xTile,s ; Valid range [0, 40] (41 columns) + tax + lda yTile,s ; Valid range [0, 25] (26 rows) + tay + lda tileId + jsr _SetTile + _TSExit #0;#6 ; SetBG0Origin(x, y) @@ -204,11 +212,18 @@ yPos equ FirstParam xPos equ FirstParam+2 _TSEntry + + lda xPos,s + jsr _SetBG0XPos + lda yPos,s + jsr _SetBG0YPos + _TSExit #0;#4 ; Render() _TSRender _TSEntry +; jsr _Render _TSExit #0;#0 @@ -220,6 +235,7 @@ _TSRender put Timer.s put Graphics.s put Tiles.s +; put Render.s put blitter/BG0.s put blitter/BG1.s put blitter/Template.s diff --git a/src/_FileInformation.txt b/src/_FileInformation.txt index 54452a1..8b4a06a 100644 --- a/src/_FileInformation.txt +++ b/src/_FileInformation.txt @@ -1,2 +1 @@ -Tool=Type(00),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) -GTETool=Type(BA),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) +Tool160=Type(BA),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) diff --git a/src/blitter/Horz.s b/src/blitter/Horz.s index a069313..9eaf5d8 100644 --- a/src/blitter/Horz.s +++ b/src/blitter/Horz.s @@ -3,33 +3,6 @@ ; when the virtual X-position of the play field changes. -; SetBG0XPos -; -; Set the virtual horizontal position of the primary background layer. In addition to -; updating the direct page state locations, this routine needs to preserve the original -; value as well. This is a bit subtle, because if this routine is called multiple times -; with different values, we need to make sure the *original* value is preserved and not -; continuously overwrite it. -; -; We assume that there is a clean code field in this routine -SetBG0XPos ENT - jsr _SetBG0XPos - rtl - -_SetBG0XPos - cmp StartX - beq :out ; Easy, if nothing changed, then nothing changes - - ldx StartX ; Load the old value (but don't save it yet) - sta StartX ; Save the new position - - lda #DIRTY_BIT_BG0_X - tsb DirtyBits ; Check if the value is already dirty, if so exit - bne :out ; without overwriting the original value - - stx OldStartX ; First change, so preserve the value -:out rts - ; Simple function that restores the saved opcode that are stashed in _applyBG0Xpos. It is ; very important that opcodes are restored before new ones are inserted, because there is ; only one, fixed storage location and old values will be overwritten if operations are not diff --git a/src/blitter/Tiles.s b/src/blitter/Tiles.s index e0a25a5..c175403 100644 --- a/src/blitter/Tiles.s +++ b/src/blitter/Tiles.s @@ -38,9 +38,6 @@ ; 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_CTRL_MASK equ $FE00 -TILE_PROC_MASK equ $F800 ; Select tile proc for rendering - ; Use some temporary space for the spriteIdx array (maximum of 4 entries) stkSave equ tmp9 @@ -48,29 +45,7 @@ screenAddr equ tmp10 tileAddr equ tmp11 spriteIdx equ tmp12 -; On entry -; -; B is set to the correct BG1 data bank -; A is set to the the tile descriptor -; Y is set to the top-left address of the tile in the BG1 data bank -; -; tmp0/tmp1 is reserved -_RenderTileBG1 - pha ; Save the tile descriptor - and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; Only horizontal and vertical flips are supported for BG1 - xba - tax - ldal :actions,x - stal :tiledisp+1 - - pla - and #TILE_ID_MASK ; Mask out the ID and save just that - _Mul128 ; multiplied by 128 - tax -:tiledisp jmp $0000 - -:actions dw _TBSolidBG1_00,_TBSolidBG1_0H,_TBSolidBG1_V0,_TBSolidBG1_VH ; 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. @@ -317,41 +292,6 @@ CopyTwoSprites --^ ; jmp FinishTile -; Copy a single piece of sprite data into a temporary direct page . X = spriteIdx -; -; X register is the offset of the underlying tile data -; Y register is the line offset into the sprite data and mask buffers -; There is a pointer for each sprite on the direct page that can be used -; to access both the data and mask components of a sprite -; The Data Bank reigster points to the sprite data -; -; ldal tiledata,x -; and [spriteIdx],y -; ora (spriteIdx),y -; sta tmp_sprite_data -; -; For multiple sprites, we can chain together the and/ora instructions to stack the sprites -; -; ldal tiledata,x -; and [spriteIdx],y -; ora (spriteIdx),y -; and [spriteIdx+4],y -; ora (spriteIdx+4),y -; and [spriteIdx+8],y -; ora (spriteIdx+8),y -; sta tmp_sprite_data -; -; When the sprites need to be drawn on top of the background, then change the order of operations -; -; lda (spriteIdx),y -; and [spriteIdx+4],y -; ora (spriteIdx+4),y -; and [spriteIdx+8],y -; ora (spriteIdx+8),y -; sta tmp_sprite_data -; andl tiledata+32,x -; oral tiledata,x -; CopyOneSprite clc lda TileStore+TS_VBUFF_ADDR_0,y @@ -770,77 +710,6 @@ _ClearDirtyTiles bne :loop 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 ENT - phb - phk - plb - jsr _GetTileStoreOffset - plb - rtl - - -_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) -; -; Registers are not preserved -_SetTile - pha - jsr _GetTileStoreOffset0 ; Get the address of the X,Y tile position - tax - pla - - cmpl TileStore+TS_TILE_ID,x ; Only set to dirty if the value changed - beq :nochange - - stal TileStore+TS_TILE_ID,x ; Value is different, store it. - jsr _GetTileAddr - stal TileStore+TS_TILE_ADDR,x ; Committed to drawing this tile, so get the address of the tile in the tiledata bank for later - - ldal TileStore+TS_TILE_ID,x - and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value - xba - tay - lda DirtyTileProcs,y - stal TileStore+TS_DIRTY_TILE_DISP,x - - ldal TileStore+TS_TILE_ID,x ; Get the non-sprite dispatch address - and #TILE_CTRL_MASK - xba - tay - lda TileProcs,y - stal TileStore+TS_BASE_TILE_DISP,x - -; txa ; Add this tile to the list of dirty tiles to refresh - jmp _PushDirtyTileX ; on the next call to _ApplyTiles - -:nochange rts - ; Append a new dirty tile record ; diff --git a/src/blitter/Tiles00000.s b/src/blitter/Tiles00000.s index 709b54d..854691d 100644 --- a/src/blitter/Tiles00000.s +++ b/src/blitter/Tiles00000.s @@ -36,6 +36,8 @@ _TBSolidTile_VH ; ; This does not increase the FPS by 37% because only a small number of tiles are drawn each frame, but it ; has an impact and can significantly help out when sprites trigger more dirty tile updates than normal. +_TBCopyDataFast + tax _TBCopyData ]line equ 0 lup 8 @@ -47,17 +49,8 @@ _TBCopyData --^ 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 - +_TBCopyDataVFast + tax _TBCopyDataV ]src equ 7 ]dest equ 0 @@ -71,40 +64,3 @@ _TBCopyDataV --^ 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 diff --git a/src/blitter/Tiles10000.s b/src/blitter/Tiles10000.s index 8e761d3..edc5655 100644 --- a/src/blitter/Tiles10000.s +++ b/src/blitter/Tiles10000.s @@ -87,119 +87,6 @@ _TBApplySpriteDataTwo --^ 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 tile mask data into the direct page compositing buffer. -_TBCopyTileMaskToCBuff -]line equ 0 - lup 8 - ldal tiledata+{]line*4}+32,x - sta blttmp+{]line*4}+32 - - ldal tiledata+{]line*4}+32+2,x - sta blttmp+{]line*4}+32+2 -]line equ ]line+1 - --^ - rts - -;_TBCopyTileMaskToCBuffH -;]line equ 0 -; lup 8 -; ldal tiledata+{]line*4}+32+64,x -; sta blttmp+{]line*4}+32 -; -; ldal tiledata+{]line*4}+32+64+2,x -; sta blttmp+{]line*4}+32+2 -;]line equ ]line+1 -; --^ -; rts - -_TBCopyTileMaskToCBuffV -]src equ 7 -]dest equ 0 - lup 8 - ldal tiledata+{]src*4}+32,x - sta blttmp+{]dest*4}+32 - - ldal tiledata+{]src*4}+32+2,x - sta blttmp+{]dest*4}+32+2 -]src equ ]src-1 -]dest equ ]dest+1 - --^ - rts - -;_TBCopyTileMaskToCBuffVH -;]src equ 7 -;]dest equ 0 -; lup 8 -; ldal tiledata+{]src*4}+32+64,x -; sta blttmp+{]dest*4}+32 -; -; ldal tiledata+{]src*4}+32+64+2,x -; sta blttmp+{]dest*4}+32+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 diff --git a/src/blitter/TilesBG1.s b/src/blitter/TilesBG1.s index 5345830..9825e11 100644 --- a/src/blitter/TilesBG1.s +++ b/src/blitter/TilesBG1.s @@ -1,4 +1,26 @@ + +; On entry +; +; B is set to the correct BG1 data bank +; A is set to the the tile descriptor +; Y is set to the top-left address of the tile in the BG1 data bank +; +; tmp0/tmp1 is reserved +_RenderTileBG1 + pha ; Save the tile descriptor + + and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; Only horizontal and vertical flips are supported for BG1 + xba + tax + + pla + and #TILE_ID_MASK ; Mask out the ID and save just that + _Mul128 ; multiplied by 128 + jmp (:actions,x) +:actions dw _TBSolidBG1_00,_TBSolidBG1_0H,_TBSolidBG1_V0,_TBSolidBG1_VH + _TBSolidBG1_00 + tax ]line equ 0 lup 8 ldal tiledata+{]line*4},x @@ -10,6 +32,7 @@ _TBSolidBG1_00 rts _TBSolidBG1_0H + tax ]line equ 0 lup 8 ldal tiledata+{]line*4}+64,x @@ -21,6 +44,7 @@ _TBSolidBG1_0H rts _TBSolidBG1_V0 + tax ]src equ 7 ]dest equ 0 lup 8 @@ -34,6 +58,7 @@ _TBSolidBG1_V0 rts _TBSolidBG1_VH + tax ]src equ 7 ]dest equ 0 lup 8 diff --git a/src/blitter/Vert.s b/src/blitter/Vert.s index 14e4535..c7b7bd6 100644 --- a/src/blitter/Vert.s +++ b/src/blitter/Vert.s @@ -2,28 +2,6 @@ ; of these routines are to adjust tables and patch in new values into the code field ; when the virtual Y-position of the play field changes. - -; SetBG0YPos -; -; Set the virtual position of the primary background layer. -SetBG0YPos ENT - jsr _SetBG0YPos - rtl - -_SetBG0YPos - cmp StartY - beq :out ; Easy, if nothing changed, then nothing changes - - ldx StartY ; Load the old value (but don't save it yet) - sta StartY ; Save the new position - - lda #DIRTY_BIT_BG0_Y - tsb DirtyBits ; Check if the value is already dirty, if so exit - bne :out ; without overwriting the original value - - stx OldStartY ; First change, so preserve the value -:out rts - ; Based on the current value of StartY in the direct page. Set up the dispatch ; information so that the BltRange driver will render the correct code field ; lines in the correct order diff --git a/src/sprites/DirtySpriteProcs.s b/src/sprites/DirtySpriteProcs.s new file mode 100644 index 0000000..65c68e1 --- /dev/null +++ b/src/sprites/DirtySpriteProcs.s @@ -0,0 +1,92 @@ +; Functions to handle rendering sprites into 8x8 tile buffers for dirty tile rendering. Because we +; are rendering directly to the graphics screen instead of the code field, we can map the direct +; page into Bank 01 and use that to avoid writing the merge sprite and tile data to an intermediate +; buffer. + +;DirtyTileSpriteProcs dw _TBDirtySpriteTile_00,_TBDirtySpriteTile_0H,_TBDirtySpriteTile_V0,_TBDirtySpriteTile_VH + +; Optimization Note: The single-sprite blitter seems like it could be made faster by taking advantage of +; the fact that only a single set of sprite data needs to be read, but the extra overhead +; of using the direct page and setting up and restoring registers wipes out the 2 cycle +; per word advantage. +; +; A = screen address +; X = address of sprite data +; Y = address of tile data +; B = tile data bank + +_OneDirtySprite_00 +_OneDirtySprite_0H + + phd + sei + clc + tcd + _R0W1 + + _ODS_Line 0,0,$0 + _ODS_Line 1,1,$A0 + tdc + adc #320 + tcd + _ODS_Line 2,2,$0 + _ODS_Line 3,3,$A0 + tdc + adc #320 + tcd + _ODS_Line 4,4,$0 + _ODS_Line 5,5,$A0 + tdc + adc #320 + tcd + _ODS_Line 6,6,$0 + _ODS_Line 7,7,$A0 + + _R0W0 + cli + pld + rts + + +_OneDirtySprite_V0 +_OneDirtySprite_VH + phd + sei + clc + tcd + _R0W1 + + _ODS_Line 0,7,$0 + _ODS_Line 1,6,$A0 + tdc + adc #320 + tcd + _ODS_Line 2,5,$0 + _ODS_Line 3,4,$A0 + tdc + adc #320 + tcd + _ODS_Line 4,3,$0 + _ODS_Line 5,2,$A0 + tdc + adc #320 + tcd + _ODS_Line 6,1,$0 + _ODS_Line 7,0,$A0 + + _R0W0 + cli + pld + rts + + +; Build up from here +_FourDirtySprites + lda TileStore+TS_VBUFF_ADDR_0,y + sta spriteIdx + lda TileStore+TS_VBUFF_ADDR_1,y + sta spriteIdx+4 + lda TileStore+TS_VBUFF_ADDR_2,y + sta spriteIdx+8 + lda TileStore+TS_VBUFF_ADDR_3,y + sta spriteIdx+12 \ No newline at end of file diff --git a/src/sprites/SpriteProcs.s b/src/sprites/SpriteProcs.s new file mode 100644 index 0000000..10d0fc5 --- /dev/null +++ b/src/sprites/SpriteProcs.s @@ -0,0 +1,150 @@ +; Functions to handle rendering sprite information into buffers for updates to the +; code field. Due to lack of parallel structure, the sprites are combined with the +; tile data and then written to a single direct page buffer. The data is read from +; this buffer and then applied to the code field + +; Merge a single block of sprite data with a tile +_OneSprite_00 +_OneSprite_H0 + ldx TileStore+TS_VBUFF_ADDR_0,y + lda TileStore+TS_TILE_ADDR,y + tay + +]line equ 0 + lup 8 + lda tiledata+{]line*TILE_DATA_SPAN},y + andl spritemask+{]line*SPRITE_PLANE_SPAN},x + oral spritedata+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_data+{]line*4} + + lda tiledata+{]line*TILE_DATA_SPAN}+2,y + andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x + oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_data+{]line*4}+2 +]line equ ]line+1 + --^ + +_OneSprite_V0 +_OneSprite_VH + ldx TileStore+TS_VBUFF_ADDR_0,y + lda TileStore+TS_TILE_ADDR,y + tay + +]line equ 7 +]dest equ 0 + lup 8 + lda tiledata+{]line*TILE_DATA_SPAN},y + andl spritemask+{]dest*SPRITE_PLANE_SPAN},x + oral spritedata+{]dest*SPRITE_PLANE_SPAN},x + sta tmp_sprite_data+{]dest*4} + + lda tiledata+{]line*TILE_DATA_SPAN}+2,y + andl spritemask+{]dest*SPRITE_PLANE_SPAN}+2,x + oral spritedata+{]dest*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_data+{]dest*4}+2 +]line equ ]line-1 +]dest equ ]dest+1 + --^ + rts + + +; Merge two blocks of sprite data. This is more involved because we need to use the +; direct page pointers to stack the sprite information +_TwoSprite_00 +_TwoSprite_H0 + lda TileStore+TS_VBUFF_ADDR_0,y + sta sprite_0 + lda TileStore+TS_VBUFF_ADDR_1,y + sta sprite_1 + ldx TileStore+TS_TILE_ADDR,y + +; line 0 + lda tiledata+{0*TILE_DATA_SPAN},x + and [sprite_1] + ora (sprite_1) + and [sprite_0] + ora (sprite_0) + sta tmp_sprite_data+{0*4} + + ldy #{0*SPRITE_PLANE_SPAN}+2 + lda tiledata+{0*TILE_DATA_SPAN}+2,x + and [sprite_1],y + ora (sprite_1),y + and [sprite_0],y + ora (sprite_0),y + sta tmp_sprite_data+{0*4}+2 + +; line 1 + ldy #{1*SPRITE_PLANE_SPAN} + lda tiledata+{1*TILE_DATA_SPAN},x + and [sprite_1],y + ora (sprite_1),y + and [sprite_0],y + ora (sprite_0),y + sta tmp_sprite_data+{1*4} + + ldy #{1*SPRITE_PLANE_SPAN}+2 + lda tiledata+{1*TILE_DATA_SPAN}+2,x + and [sprite_1],y + ora (sprite_1),y + and [sprite_0],y + ora (sprite_0),y + sta tmp_sprite_data+{1*4}+2 + + rts + + +; Merge three blocks of sprite data. This is more involved because we need to use the +; direct page pointers to stack the sprite information +_ThreeSprite_00 +_ThreeSprite_H0 + lda TileStore+TS_VBUFF_ADDR_0,y + sta sprite_0 + lda TileStore+TS_VBUFF_ADDR_1,y + sta sprite_1 + lda TileStore+TS_VBUFF_ADDR_2,y + sta sprite_2 + ldx TileStore+TS_TILE_ADDR,y + +; line 0 + lda tiledata+{0*TILE_DATA_SPAN},x + and [sprite_2] + ora (sprite_2) + and [sprite_1] + ora (sprite_1) + and [sprite_0] + ora (sprite_0) + sta tmp_sprite_data+{0*4} + + ldy #{0*SPRITE_PLANE_SPAN}+2 + lda tiledata+{0*TILE_DATA_SPAN}+2,x + and [sprite_2],y + ora (sprite_2),y + and [sprite_1],y + ora (sprite_1),y + and [sprite_0],y + ora (sprite_0),y + sta tmp_sprite_data+{0*4}+2 + +; line 1 + ldy #{1*SPRITE_PLANE_SPAN} + lda tiledata+{1*TILE_DATA_SPAN},x + and [sprite_2],y + ora (sprite_2),y + and [sprite_1],y + ora (sprite_1),y + and [sprite_0],y + ora (sprite_0),y + sta tmp_sprite_data+{1*4} + + ldy #{1*SPRITE_PLANE_SPAN}+2 + lda tiledata+{1*TILE_DATA_SPAN}+2,x + and [sprite_2],y + ora (sprite_2),y + and [sprite_1],y + ora (sprite_1),y + and [sprite_0],y + ora (sprite_0),y + sta tmp_sprite_data+{1*4}+2 + + rts \ No newline at end of file diff --git a/src/tiles/DirtyTileProcs.s b/src/tiles/DirtyTileProcs.s new file mode 100644 index 0000000..f3ebe56 --- /dev/null +++ b/src/tiles/DirtyTileProcs.s @@ -0,0 +1,43 @@ +; A collection of tile blitters used in the dirty renderer. These renderers copy data directly +; to the graphics screen. Also, because the dirty render assumes that the screen is not moving, +; there is no support for two layer tiles. + +; Address table of the rendering functions +DirtyTileProcs dw _TBDirtyTile_00,_TBDirtyTile_0H,_TBDirtyTile_V0,_TBDirtyTile_VH + +; Normal and horizontally flipped tiles. The horizontal variant is selected by choosing +; and appropriate value for the X register, so these can share the same code. +; +; B = Bank 01 +; X = address of tile data +; Y = screen address +_TBDirtyTile_00 +_TBDirtyTile_0H +]line equ 0 + lup 8 + ldal tiledata+{]line*4},x + sta: $0000+{]line*160},y + ldal tiledata+{]line*4}+2,x + sta: $0002+{]line*160},y +]line equ ]line+1 + --^ + rts + +; Vertically flipped tile renderers +; +; B = Bank 01 +; X = address of tile data +; Y = screen address +_TBDirtyTile_V0 +_TBDirtyTile_VH +]line equ 7 +]dest equ 0 + lup 8 + ldal tiledata+{]line*4},x + sta: $0000+{]dest*160},y + ldal tiledata+{]line*4}+2,x + sta: $0002+{]dest*160},y +]line equ ]line-1 +]dest equ ]dest+1 + --^ + rts \ No newline at end of file diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s new file mode 100644 index 0000000..e405c0b --- /dev/null +++ b/src/tiles/FastRenderer.s @@ -0,0 +1,151 @@ +; If the engine mode has the second background layer disabled, we take advantage of that to +; be more efficient in our rendering. Basically, without the second layer, there is no need +; to use the tile mask information. +; +; If there are no sprites, then we copy the tile data into the code field as fast as possible. +; If there are sprites, then the sprite data is flattened and stored into a direct page buffer +; and then copied into the code field +_RenderTileFast + ldx TileStore+TS_VBUFF_ADDR_COUNT,y ; How many sprites are on this tile? + beq NoSpritesFast ; This is faster if there are no sprites + + lda TileStore+TS_TILE_ID,y ; Check if the tile has + jmp (fast_dispatch,x) +fast_dispatch + da NoSpritesFast + da OneSpriteFast + da TwoSpritesFast + da ThreeSpritesFast + da FourSpritesFast + +NoSpritesFast + tyx + lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line + pha ; and put on the stack for later. Has addl bank in high byte. + ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field + lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) + plb ; set the code field bank + jmp (TileStore+TS_BASE_TILE_DISP,x) ; go to the tile copy routine (just basics) + +; The TS_BASE_TILE_DISP routines will come from this table when ENGINE_MODE_TWO_LAYER and +; ENGINE_MODE_DYN_TILES are both off. +FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast + +; Where there are sprites involved, the first step is to call a routine to copy the +; tile data into a temporary buffer. Then the sprite data is merged and placed into +; the code field. +OneSpriteFast + tyx + lda TileStore+TS_TILE_ADDR,y + per :-1 + jmp (TileStore+TS_BASE_TILE_COPY,x) ; Copy the tile data to the temporary buffer +: + ldx TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data + lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line + pha ; and put on the stack for later. + lda TileStore+TS_CODE_ADDR_LOW,y + tay + plb ; set the code field bank + +]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 + --^ + rts + +TwoSpritesFast + tyx + lda TileStore+TS_TILE_ADDR,y + per :-1 + jmp (TileStore+TS_BASE_TILE_COPY,x) ; Copy the tile data to the temporary buffer +: + lda TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data + sta spritedata_0 + sta spritemask_0 + lda TileStore+TS_VBUFF_ADDR_1,y ; address of the sprite data + sta spritedata_1 + sta spritemask_1 + + lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line + pha ; and put on the stack for later. + lda TileStore+TS_CODE_ADDR_LOW,y + tay + plb ; set the code field bank + +]line equ 0 + lup 8 + ldy #{]line*SPRITE_PLANE_SPAN} + lda blttmp+{]line*4} + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0004+{]line*$1000},x + + ldy #{]line*SPRITE_PLANE_SPAN}+2 + lda blttmp+{]line*4}+2 + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0001+{]line*$1000},x +]line equ ]line+1 + --^ + rts + +ThreeSpritesFast +FourSpritesFast + tyx + lda TileStore+TS_TILE_ADDR,y + per :-1 + jmp (TileStore+TS_BASE_TILE_COPY,x) ; Copy the tile data to the temporary buffer +: + lda TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data + sta spritedata_0 + sta spritemask_0 + lda TileStore+TS_VBUFF_ADDR_1,y + sta spritedata_1 + sta spritemask_1 + lda TileStore+TS_VBUFF_ADDR_2,y + sta spritedata_2 + sta spritemask_2 + + lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line + pha ; and put on the stack for later. + lda TileStore+TS_CODE_ADDR_LOW,y + tay + plb ; set the code field bank + +]line equ 0 + lup 8 + ldy #{]line*SPRITE_PLANE_SPAN} + lda blttmp+{]line*4} + andl [spritemask_2],y + oral [spritedata_2],y + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0004+{]line*$1000},x + + ldy #{]line*SPRITE_PLANE_SPAN}+2 + lda blttmp+{]line*4}+2 + andl [spritemask_2],y + oral [spritedata_2],y + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0001+{]line*$1000},x +]line equ ]line+1 + --^ + rts \ No newline at end of file diff --git a/src/tiles/TileProcs.s b/src/tiles/TileProcs.s new file mode 100644 index 0000000..a990d1c --- /dev/null +++ b/src/tiles/TileProcs.s @@ -0,0 +1,93 @@ +; A simple helper function that fills in all of the opcodes of a tile with the PEA opcode. This is +; a separate functino because we can often just update the tile data if we know the opcodes are already +; set. When we have to fill the opcodes, this function is used +_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 + + +; 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 +_TBCopyTileDataAndMaskToCBuff + jsr _TBCopyTileDataToCBuff + jmp _TBCopyTileMaskToCBuff + +_TBCopyTileDataAndMaskToCBuffV + jsr _TBCopyTileDataToCBuffV + jmp _TBCopyTileMaskToCBuffV + +_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 + +_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 + +; Copy tile mask data into the direct page compositing buffer. +_TBCopyTileMaskToCBuff +]line equ 0 + lup 8 + ldal tiledata+{]line*4}+32,x + sta blttmp+{]line*4}+32 + + ldal tiledata+{]line*4}+32+2,x + sta blttmp+{]line*4}+32+2 +]line equ ]line+1 + --^ + rts + +_TBCopyTileMaskToCBuffV +]src equ 7 +]dest equ 0 + lup 8 + ldal tiledata+{]src*4}+32,x + sta blttmp+{]dest*4}+32 + + ldal tiledata+{]src*4}+32+2,x + sta blttmp+{]dest*4}+32+2 +]src equ ]src-1 +]dest equ ]dest+1 + --^ + rts + From 01e92a7b62fdae853b06ba718094b76f08782add Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 18 May 2022 00:34:25 -0500 Subject: [PATCH 15/82] Update; tool is compiling while referencing new rederer pipeline --- demos/tool/Zelda.TileSet.s | 13688 +++++++++++++++++++++++++++ demos/zelda/.gitignore | 1 + macros/GTE.Macs.s | 3 + package.json | 4 +- src/Render.s | 1053 +-- src/Tiles.s | 29 +- src/Tool.s | 23 +- src/{tiles => blitter}/TileProcs.s | 23 +- src/blitter/Tiles.s | 143 +- src/tiles/DirtyTileQueue.s | 64 + src/tiles/FastRenderer.s | 90 +- 11 files changed, 14183 insertions(+), 938 deletions(-) create mode 100644 demos/tool/Zelda.TileSet.s create mode 100644 demos/zelda/.gitignore rename src/{tiles => blitter}/TileProcs.s (79%) create mode 100644 src/tiles/DirtyTileQueue.s diff --git a/demos/tool/Zelda.TileSet.s b/demos/tool/Zelda.TileSet.s new file mode 100644 index 0000000..6f7dcc3 --- /dev/null +++ b/demos/tool/Zelda.TileSet.s @@ -0,0 +1,13688 @@ +; startIndex = 0 +; Palette: +; $0F0F,$08C1,$0C41,$0F93,$0777,$0FDA,$00A0,$0000,$0D20,$0FFF,$023E +; Converting to BG0 format... +TSZelda ENT + +; Reserved space (tile 0 is special... + ds 128 +; Tile ID 1 +; From image coordinates 0, 0 + hex 00000111 + hex 00001111 + hex 00301222 + hex 00302222 + hex 00332313 + hex 00332323 + hex 00033333 + hex 00011332 + + hex fffff000 + hex ffff0000 + hex ff0f0000 + hex ff0f0000 + hex ff000000 + hex ff000000 + hex fff00000 + hex fff00000 + + hex 11100000 + hex 11110000 + hex 22210300 + hex 22220300 + hex 31323300 + hex 32323300 + hex 33333000 + hex 23311000 + + hex 000fffff + hex 0000ffff + hex 0000f0ff + hex 0000f0ff + hex 000000ff + hex 000000ff + hex 00000fff + hex 00000fff + +; Tile ID 2 +; From image coordinates 8, 0 + hex 11100000 + hex 11110000 + hex 22210300 + hex 22220300 + hex 31323300 + hex 32323300 + hex 33333200 + hex 23311200 + + hex 000fffff + hex 0000ffff + hex 0000f0ff + hex 0000f0ff + hex 000000ff + hex 000000ff + hex 000000ff + hex 000000ff + + hex 00000111 + hex 00001111 + hex 00301222 + hex 00302222 + hex 00332313 + hex 00332323 + hex 00233333 + hex 00211332 + + hex fffff000 + hex ffff0000 + hex ff0f0000 + hex ff0f0000 + hex ff000000 + hex ff000000 + hex ff000000 + hex ff000000 + +; Tile ID 3 +; From image coordinates 16, 0 + hex 00000111 + hex 00001111 + hex 00301222 + hex 00302222 + hex 00332313 + hex 00332323 + hex 00033333 + hex 00001332 + + hex fffff000 + hex ffff0000 + hex ff0f0000 + hex ff0f0000 + hex ff000000 + hex ff000000 + hex fff00000 + hex ffff0000 + + hex 11100000 + hex 11110000 + hex 22210300 + hex 22220300 + hex 31323300 + hex 32323300 + hex 33333000 + hex 23310000 + + hex 000fffff + hex 0000ffff + hex 0000f0ff + hex 0000f0ff + hex 000000ff + hex 000000ff + hex 00000fff + hex 0000ffff + +; Tile ID 4 +; From image coordinates 24, 0 + hex 11100000 + hex 11110000 + hex 22210300 + hex 22220300 + hex 31323300 + hex 32323300 + hex 33333200 + hex 23312200 + + hex 000fffff + hex 0000ffff + hex 0000f0ff + hex 0000f0ff + hex 000000ff + hex 000000ff + hex 000000ff + hex 000000ff + + hex 00000111 + hex 00001111 + hex 00301222 + hex 00302222 + hex 00332313 + hex 00332323 + hex 00233333 + hex 00221332 + + hex fffff000 + hex ffff0000 + hex ff0f0000 + hex ff0f0000 + hex ff000000 + hex ff000000 + hex ff000000 + hex ff000000 + +; Tile ID 5 +; From image coordinates 32, 0 + hex 00000111 + hex 00011111 + hex 01113112 + hex 11113322 + hex 10113332 + hex 00122332 + hex 00022233 + hex 00001111 + + hex fffff000 + hex fff00000 + hex f0000000 + hex 00000000 + hex 0f000000 + hex ff000000 + hex fff00000 + hex ffff0000 + + hex 11100000 + hex 11111000 + hex 21131110 + hex 22331111 + hex 23331101 + hex 23322100 + hex 33222000 + hex 11110000 + + hex 000fffff + hex 00000fff + hex 0000000f + hex 00000000 + hex 000000f0 + hex 000000ff + hex 00000fff + hex 0000ffff + +; Tile ID 6 +; From image coordinates 40, 0 + hex 10000000 + hex 22220000 + hex 22222000 + hex 22220000 + hex 33130020 + hex 33233320 + hex 33330020 + hex 33330020 + + hex 0fffffff + hex 0000ffff + hex 00000fff + hex 0000ffff + hex 0000ff0f + hex 0000000f + hex 0000ff0f + hex 0000ff0f + + hex 00000001 + hex 00002222 + hex 00022222 + hex 00002222 + hex 02003133 + hex 02333233 + hex 02003333 + hex 02003333 + + hex fffffff0 + hex ffff0000 + hex fff00000 + hex ffff0000 + hex f0ff0000 + hex f0000000 + hex f0ff0000 + hex f0ff0000 + +; Tile ID 7 +; From image coordinates 48, 0 + hex 00000000 + hex 00000111 + hex 00011111 + hex 01113112 + hex 11113322 + hex 10113332 + hex 00122332 + hex 00022233 + + hex ffffffff + hex fffff000 + hex fff00000 + hex f0000000 + hex 00000000 + hex 0f000000 + hex ff000000 + hex fff00000 + + hex 00000000 + hex 11100000 + hex 11111000 + hex 21131110 + hex 22331111 + hex 23331101 + hex 23322100 + hex 33222000 + + hex ffffffff + hex 000fffff + hex 00000fff + hex 0000000f + hex 00000000 + hex 000000f0 + hex 000000ff + hex 00000fff + +; Tile ID 8 +; From image coordinates 56, 0 + hex 00000000 + hex 10000000 + hex 22220000 + hex 22222000 + hex 22220000 + hex 33130000 + hex 33233300 + hex 33330200 + + hex ffffffff + hex 0fffffff + hex 0000ffff + hex 00000fff + hex 0000ffff + hex 0000ffff + hex 000000ff + hex 0000f0ff + + hex 00000000 + hex 00000001 + hex 00002222 + hex 00022222 + hex 00002222 + hex 00003133 + hex 00333233 + hex 00203333 + + hex ffffffff + hex fffffff0 + hex ffff0000 + hex fff00000 + hex ffff0000 + hex ffff0000 + hex ff000000 + hex ff0f0000 + +; Tile ID 9 +; From image coordinates 64, 0 + hex 00000111 + hex 00001111 + hex 00301111 + hex 00311111 + hex 00321111 + hex 00332211 + hex 00032221 + hex 00021222 + + hex fffff000 + hex ffff0000 + hex ff0f0000 + hex ff000000 + hex ff000000 + hex ff000000 + hex fff00000 + hex fff00000 + + hex 11100000 + hex 11110000 + hex 11110300 + hex 11111300 + hex 11112300 + hex 11223300 + hex 12223000 + hex 22212000 + + hex 000fffff + hex 0000ffff + hex 0000f0ff + hex 000000ff + hex 000000ff + hex 000000ff + hex 00000fff + hex 00000fff + +; Tile ID 10 +; From image coordinates 72, 0 + hex 11100000 + hex 11110000 + hex 11110300 + hex 11111300 + hex 11112300 + hex 11223300 + hex 12223000 + hex 22212000 + + hex 000fffff + hex 0000ffff + hex 0000f0ff + hex 000000ff + hex 000000ff + hex 000000ff + hex 00000fff + hex 00000fff + + hex 00000111 + hex 00001111 + hex 00301111 + hex 00311111 + hex 00321111 + hex 00332211 + hex 00032221 + hex 00021222 + + hex fffff000 + hex ffff0000 + hex ff0f0000 + hex ff000000 + hex ff000000 + hex ff000000 + hex fff00000 + hex fff00000 + +; Tile ID 11 +; From image coordinates 80, 0 + hex 00000111 + hex 00001111 + hex 00301111 + hex 00311111 + hex 00321111 + hex 00332211 + hex 00032221 + hex 00021222 + + hex fffff000 + hex ffff0000 + hex ff0f0000 + hex ff000000 + hex ff000000 + hex ff000000 + hex fff00000 + hex fff00000 + + hex 11100000 + hex 11110000 + hex 11110300 + hex 11111300 + hex 11112300 + hex 11223300 + hex 12223000 + hex 22212000 + + hex 000fffff + hex 0000ffff + hex 0000f0ff + hex 000000ff + hex 000000ff + hex 000000ff + hex 00000fff + hex 00000fff + +; Tile ID 12 +; From image coordinates 88, 0 + hex 11100000 + hex 11110000 + hex 11110300 + hex 11111300 + hex 11112300 + hex 11223300 + hex 12223000 + hex 22212000 + + hex 000fffff + hex 0000ffff + hex 0000f0ff + hex 000000ff + hex 000000ff + hex 000000ff + hex 00000fff + hex 00000fff + + hex 00000111 + hex 00001111 + hex 00301111 + hex 00311111 + hex 00321111 + hex 00332211 + hex 00032221 + hex 00021222 + + hex fffff000 + hex ffff0000 + hex ff0f0000 + hex ff000000 + hex ff000000 + hex ff000000 + hex fff00000 + hex fff00000 + +; Tile ID 13 +; From image coordinates 96, 0 + hex 00022011 + hex 00232111 + hex 02231122 + hex 22331222 + hex 22332313 + hex 32232323 + hex 03221332 + hex 00321132 + + hex fff00f00 + hex ff000000 + hex f0000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex f0000000 + hex ff000000 + + hex 11022000 + hex 11123200 + hex 22113220 + hex 22213322 + hex 31323322 + hex 32323223 + hex 23312230 + hex 23112300 + + hex 00f00fff + hex 000000ff + hex 0000000f + hex 00000000 + hex 00000000 + hex 00000000 + hex 0000000f + hex 000000ff + +; Tile ID 14 +; From image coordinates 104, 0 + hex 11100000 + hex 11110000 + hex 22111000 + hex 22211030 + hex 31221330 + hex 32321300 + hex 23333000 + hex 23322000 + + hex 000fffff + hex 0000ffff + hex 00000fff + hex 00000f0f + hex 0000000f + hex 000000ff + hex 00000fff + hex 00000fff + + hex 00000111 + hex 00001111 + hex 00011122 + hex 03011222 + hex 03312213 + hex 00312323 + hex 00033332 + hex 00022332 + + hex fffff000 + hex ffff0000 + hex fff00000 + hex f0f00000 + hex f0000000 + hex ff000000 + hex fff00000 + hex fff00000 + +; Tile ID 15 +; From image coordinates 112, 0 + hex 00000000 + hex 00000011 + hex 00001111 + hex 00011311 + hex 00011332 + hex 00111333 + hex 01112233 + hex 01002223 + + hex ffffffff + hex ffffff00 + hex ffff0000 + hex fff00000 + hex fff00000 + hex ff000000 + hex f0000000 + hex f0ff0000 + + hex 00000000 + hex 11000000 + hex 11110000 + hex 11311000 + hex 23311000 + hex 33311100 + hex 33221110 + hex 32220010 + + hex ffffffff + hex 00ffffff + hex 0000ffff + hex 00000fff + hex 00000fff + hex 000000ff + hex 0000000f + hex 0000ff0f + +; Tile ID 16 +; From image coordinates 120, 0 + hex 00000000 + hex 11000000 + hex 12222000 + hex 22222200 + hex 22222000 + hex 23313000 + hex 23323330 + hex 33333000 + + hex ffffffff + hex 00ffffff + hex 00000fff + hex 000000ff + hex 00000fff + hex 00000fff + hex 0000000f + hex 00000fff + + hex 00000000 + hex 00000011 + hex 00022221 + hex 00222222 + hex 00022222 + hex 00031332 + hex 03332332 + hex 00033333 + + hex ffffffff + hex ffffff00 + hex fff00000 + hex ff000000 + hex fff00000 + hex fff00000 + hex f0000000 + hex fff00000 + +; Tile ID 17 +; From image coordinates 128, 0 + hex 00021111 + hex 03011111 + hex 03211111 + hex 03311111 + hex 02321111 + hex 02322111 + hex 02212212 + hex 02211222 + + hex fff00000 + hex f0f00000 + hex f0000000 + hex f0000000 + hex f0000000 + hex f0000000 + hex f0000000 + hex f0000000 + + hex 11112000 + hex 11111030 + hex 11111230 + hex 11111330 + hex 11112320 + hex 11122320 + hex 21221220 + hex 22211220 + + hex 00000fff + hex 00000f0f + hex 0000000f + hex 0000000f + hex 0000000f + hex 0000000f + hex 0000000f + hex 0000000f + +; Tile ID 18 +; From image coordinates 136, 0 + hex 10000000 + hex 11000000 + hex 11100000 + hex 11103000 + hex 11233000 + hex 22230003 + hex 22130032 + hex 21222322 + + hex 0fffffff + hex 00ffffff + hex 000fffff + hex 000f0fff + hex 00000fff + hex 0000fff0 + hex 0000ff00 + hex 00000000 + + hex 00000001 + hex 00000011 + hex 00000111 + hex 00030111 + hex 00033211 + hex 30003222 + hex 23003122 + hex 22322212 + + hex fffffff0 + hex ffffff00 + hex fffff000 + hex fff0f000 + hex fff00000 + hex 0fff0000 + hex 00ff0000 + hex 00000000 + +; Tile ID 19 +; From image coordinates 144, 0 + hex 00222000 + hex 00222000 + hex 00222000 + hex 00222000 + hex 00222000 + hex 00222000 + hex 00222000 + hex 00222000 + + hex ff000fff + hex ff000fff + hex ff000fff + hex ff000fff + hex ff000fff + hex ff000fff + hex ff000fff + hex ff000fff + + hex 00022200 + hex 00022200 + hex 00022200 + hex 00022200 + hex 00022200 + hex 00022200 + hex 00022200 + hex 00022200 + + hex fff000ff + hex fff000ff + hex fff000ff + hex fff000ff + hex fff000ff + hex fff000ff + hex fff000ff + hex fff000ff + +; Tile ID 20 +; From image coordinates 152, 0 + hex 00222000 + hex 00222000 + hex 00222000 + hex 00222000 + hex 00222000 + hex 00222000 + hex 00020000 + hex 00000000 + + hex ff000fff + hex ff000fff + hex ff000fff + hex ff000fff + hex ff000fff + hex ff000fff + hex fff0ffff + hex ffffffff + + hex 00022200 + hex 00022200 + hex 00022200 + hex 00022200 + hex 00022200 + hex 00022200 + hex 00002000 + hex 00000000 + + hex fff000ff + hex fff000ff + hex fff000ff + hex fff000ff + hex fff000ff + hex fff000ff + hex ffff0fff + hex ffffffff + +; Tile ID 21 +; From image coordinates 160, 0 + hex 33100000 + hex 00100000 + hex 33122222 + hex 33122222 + hex 13122222 + hex 00100000 + hex 01100000 + hex 00000000 + + hex 000fffff + hex ff0fffff + hex 00000000 + hex 00000000 + hex 00000000 + hex ff0fffff + hex f00fffff + hex ffffffff + + hex 00000133 + hex 00000100 + hex 22222133 + hex 22222133 + hex 22222131 + hex 00000100 + hex 00000110 + hex 00000000 + + hex fffff000 + hex fffff0ff + hex 00000000 + hex 00000000 + hex 00000000 + hex fffff0ff + hex fffff00f + hex ffffffff + +; Tile ID 22 +; From image coordinates 168, 0 + hex 00000000 + hex 00000000 + hex 22222000 + hex 22222200 + hex 22222000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex 00000fff + hex 000000ff + hex 00000fff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00022222 + hex 00222222 + hex 00022222 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex fff00000 + hex ff000000 + hex fff00000 + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 23 +; From image coordinates 176, 0 + hex 33000000 + hex 42000000 + hex 22222222 + hex 32222222 + hex 32222222 + hex 42000000 + hex 42000000 + hex 42000000 + + hex 00ffffff + hex 00ffffff + hex 00000000 + hex 00000000 + hex 00000000 + hex 00ffffff + hex 00ffffff + hex 00ffffff + + hex 00000033 + hex 00000024 + hex 22222222 + hex 22222223 + hex 22222223 + hex 00000024 + hex 00000024 + hex 00000024 + + hex ffffff00 + hex ffffff00 + hex 00000000 + hex 00000000 + hex 00000000 + hex ffffff00 + hex ffffff00 + hex ffffff00 + +; Tile ID 24 +; From image coordinates 184, 0 + hex 00000000 + hex 00000000 + hex 22000000 + hex 22200000 + hex 22000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex 00ffffff + hex 000fffff + hex 00ffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000022 + hex 00000222 + hex 00000022 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffff00 + hex fffff000 + hex ffffff00 + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 25 +; From image coordinates 192, 0 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 26 +; From image coordinates 200, 0 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 27 +; From image coordinates 208, 0 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 28 +; From image coordinates 216, 0 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 29 +; From image coordinates 224, 0 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 30 +; From image coordinates 232, 0 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 31 +; From image coordinates 240, 0 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 32 +; From image coordinates 248, 0 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 33 +; From image coordinates 0, 8 + hex 02222233 + hex 22322221 + hex 23332232 + hex 22322231 + hex 22322232 + hex 22222231 + hex 03333320 + hex 00002220 + + hex f0000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex f000000f + hex ffff000f + + hex 33222220 + hex 12222322 + hex 23223332 + hex 13222322 + hex 23222322 + hex 13222222 + hex 02333330 + hex 02220000 + + hex 0000000f + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex f000000f + hex f000ffff + +; Tile ID 34 +; From image coordinates 8, 8 + hex 33112220 + hex 11113220 + hex 21133320 + hex 22223330 + hex 21111300 + hex 11110000 + hex 02220000 + hex 00000000 + + hex 0000000f + hex 0000000f + hex 0000000f + hex 0000000f + hex 000000ff + hex 0000ffff + hex f000ffff + hex ffffffff + + hex 02221133 + hex 02231111 + hex 02333112 + hex 03332222 + hex 00311112 + hex 00001111 + hex 00002220 + hex 00000000 + + hex f0000000 + hex f0000000 + hex f0000000 + hex f0000000 + hex ff000000 + hex ffff0000 + hex ffff000f + hex ffffffff + +; Tile ID 35 +; From image coordinates 16, 8 + hex 00222223 + hex 02232222 + hex 02333223 + hex 02232223 + hex 02232223 + hex 02222223 + hex 00333330 + hex 00000000 + + hex ff000000 + hex f0000000 + hex f0000000 + hex f0000000 + hex f0000000 + hex f0000000 + hex ff00000f + hex ffffffff + + hex 32222200 + hex 22223220 + hex 32233320 + hex 32223220 + hex 32223220 + hex 32222220 + hex 03333300 + hex 00000000 + + hex 000000ff + hex 0000000f + hex 0000000f + hex 0000000f + hex 0000000f + hex 0000000f + hex f00000ff + hex ffffffff + +; Tile ID 36 +; From image coordinates 24, 8 + hex 33111300 + hex 11111300 + hex 22112000 + hex 12221000 + hex 22111000 + hex 11120000 + hex 02220000 + hex 02220000 + + hex 000000ff + hex 000000ff + hex 00000fff + hex 00000fff + hex 00000fff + hex 0000ffff + hex f000ffff + hex f000ffff + + hex 00311133 + hex 00311111 + hex 00021122 + hex 00012221 + hex 00011122 + hex 00002111 + hex 00002220 + hex 00002220 + + hex ff000000 + hex ff000000 + hex fff00000 + hex fff00000 + hex fff00000 + hex ffff0000 + hex ffff000f + hex ffff000f + +; Tile ID 37 +; From image coordinates 32, 8 + hex 00211111 + hex 02221333 + hex 02222333 + hex 02222331 + hex 00122112 + hex 01111111 + hex 00002222 + hex 00002222 + + hex ff000000 + hex f0000000 + hex f0000000 + hex f0000000 + hex ff000000 + hex f0000000 + hex ffff0000 + hex ffff0000 + + hex 11111200 + hex 33312220 + hex 33322220 + hex 13322220 + hex 21122100 + hex 11111110 + hex 22220000 + hex 22220000 + + hex 000000ff + hex 0000000f + hex 0000000f + hex 0000000f + hex 000000ff + hex 0000000f + hex 0000ffff + hex 0000ffff + +; Tile ID 38 +; From image coordinates 40, 8 + hex 11222320 + hex 11122320 + hex 11122020 + hex 11200020 + hex 22200020 + hex 11100000 + hex 00000000 + hex 20000000 + + hex 0000000f + hex 0000000f + hex 00000f0f + hex 000fff0f + hex 000fff0f + hex 000fffff + hex ffffffff + hex 0fffffff + + hex 02322211 + hex 02322111 + hex 02022111 + hex 02000211 + hex 02000222 + hex 00000111 + hex 00000000 + hex 00000002 + + hex f0000000 + hex f0000000 + hex f0f00000 + hex f0fff000 + hex f0fff000 + hex fffff000 + hex ffffffff + hex fffffff0 + +; Tile ID 39 +; From image coordinates 48, 8 + hex 00001111 + hex 00122113 + hex 00222223 + hex 01222223 + hex 01122221 + hex 22111112 + hex 22211111 + hex 02220000 + + hex ffff0000 + hex ff000000 + hex ff000000 + hex f0000000 + hex f0000000 + hex 00000000 + hex 00000000 + hex f000ffff + + hex 11110000 + hex 31122100 + hex 32222200 + hex 32222210 + hex 12222110 + hex 21111122 + hex 11111222 + hex 00002220 + + hex 0000ffff + hex 000000ff + hex 000000ff + hex 0000000f + hex 0000000f + hex 00000000 + hex 00000000 + hex ffff000f + +; Tile ID 40 +; From image coordinates 56, 8 + hex 33330200 + hex 33223200 + hex 33123200 + hex 31120200 + hex 11200200 + hex 22210200 + hex 11122000 + hex 02220000 + + hex 0000f0ff + hex 000000ff + hex 000000ff + hex 0000f0ff + hex 000ff0ff + hex 0000f0ff + hex 00000fff + hex f000ffff + + hex 00203333 + hex 00232233 + hex 00232133 + hex 00202113 + hex 00200211 + hex 00201222 + hex 00022111 + hex 00002220 + + hex ff0f0000 + hex ff000000 + hex ff000000 + hex ff0f0000 + hex ff0ff000 + hex ff0f0000 + hex fff00000 + hex ffff000f + +; Tile ID 41 +; From image coordinates 64, 8 + hex 00221111 + hex 00221111 + hex 00022111 + hex 00011222 + hex 00011111 + hex 00022211 + hex 00022220 + hex 00002200 + + hex ff000000 + hex ff000000 + hex fff00000 + hex fff00000 + hex fff00000 + hex fff00000 + hex fff0000f + hex ffff00ff + + hex 11112200 + hex 11112200 + hex 11122000 + hex 22211000 + hex 11111000 + hex 11222000 + hex 02222000 + hex 00220000 + + hex 000000ff + hex 000000ff + hex 00000fff + hex 00000fff + hex 00000fff + hex 00000fff + hex f0000fff + hex ff00ffff + +; Tile ID 42 +; From image coordinates 72, 8 + hex 11122000 + hex 11122300 + hex 11122300 + hex 22213300 + hex 11111000 + hex 11120000 + hex 02200000 + hex 00000000 + + hex 00000fff + hex 000000ff + hex 000000ff + hex 000000ff + hex 00000fff + hex 0000ffff + hex f00fffff + hex ffffffff + + hex 00022111 + hex 00322111 + hex 00322111 + hex 00331222 + hex 00011111 + hex 00002111 + hex 00000220 + hex 00000000 + + hex fff00000 + hex ff000000 + hex ff000000 + hex ff000000 + hex fff00000 + hex ffff0000 + hex fffff00f + hex ffffffff + +; Tile ID 43 +; From image coordinates 80, 8 + hex 00022111 + hex 00322111 + hex 00322111 + hex 00331222 + hex 00011111 + hex 00002111 + hex 00000220 + hex 00000000 + + hex fff00000 + hex ff000000 + hex ff000000 + hex ff000000 + hex fff00000 + hex ffff0000 + hex fffff00f + hex ffffffff + + hex 11122000 + hex 11122300 + hex 11122300 + hex 22213300 + hex 11111000 + hex 11120000 + hex 02200000 + hex 00000000 + + hex 00000fff + hex 000000ff + hex 000000ff + hex 000000ff + hex 00000fff + hex 0000ffff + hex f00fffff + hex ffffffff + +; Tile ID 44 +; From image coordinates 88, 8 + hex 11112200 + hex 11112200 + hex 11122000 + hex 22211000 + hex 11111000 + hex 11222000 + hex 02222000 + hex 00220000 + + hex 000000ff + hex 000000ff + hex 00000fff + hex 00000fff + hex 00000fff + hex 00000fff + hex f0000fff + hex ff00ffff + + hex 00221111 + hex 00221111 + hex 00022111 + hex 00011222 + hex 00011111 + hex 00022211 + hex 00022220 + hex 00002200 + + hex ff000000 + hex ff000000 + hex fff00000 + hex fff00000 + hex fff00000 + hex fff00000 + hex fff0000f + hex ffff00ff + +; Tile ID 45 +; From image coordinates 96, 8 + hex 00031113 + hex 00002111 + hex 00001212 + hex 00001122 + hex 00022112 + hex 00222011 + hex 00000000 + hex 00000000 + + hex fff00000 + hex ffff0000 + hex ffff0000 + hex ffff0000 + hex fff00000 + hex ff000f00 + hex ffffffff + hex ffffffff + + hex 31113000 + hex 11120000 + hex 21210000 + hex 22110000 + hex 21122000 + hex 11022200 + hex 00000000 + hex 00000000 + + hex 00000fff + hex 0000ffff + hex 0000ffff + hex 0000ffff + hex 00000fff + hex 00f000ff + hex ffffffff + hex ffffffff + +; Tile ID 46 +; From image coordinates 104, 8 + hex 33222200 + hex 11222200 + hex 22222100 + hex 12221100 + hex 23321120 + hex 33311222 + hex 33302222 + hex 00000000 + + hex 000000ff + hex 000000ff + hex 000000ff + hex 000000ff + hex 0000000f + hex 00000000 + hex 000f0000 + hex ffffffff + + hex 00222233 + hex 00222211 + hex 00122222 + hex 00112221 + hex 02112332 + hex 22211333 + hex 22220333 + hex 00000000 + + hex ff000000 + hex ff000000 + hex ff000000 + hex ff000000 + hex f0000000 + hex 00000000 + hex 0000f000 + hex ffffffff + +; Tile ID 47 +; From image coordinates 112, 8 + hex 00001111 + hex 00011222 + hex 00012222 + hex 00112222 + hex 01111222 + hex 22111111 + hex 22211111 + hex 02220000 + + hex ffff0000 + hex fff00000 + hex fff00000 + hex ff000000 + hex f0000000 + hex 00000000 + hex 00000000 + hex f000ffff + + hex 11110000 + hex 22211000 + hex 22221000 + hex 22221100 + hex 22211110 + hex 11111122 + hex 11111222 + hex 00002220 + + hex 0000ffff + hex 00000fff + hex 00000fff + hex 000000ff + hex 0000000f + hex 00000000 + hex 00000000 + hex ffff000f + +; Tile ID 48 +; From image coordinates 120, 8 + hex 13333300 + hex 22233300 + hex 22233000 + hex 22210000 + hex 11122000 + hex 22222000 + hex 11112200 + hex 00022220 + + hex 000000ff + hex 000000ff + hex 00000fff + hex 0000ffff + hex 00000fff + hex 00000fff + hex 000000ff + hex fff0000f + + hex 00333331 + hex 00333222 + hex 00033222 + hex 00001222 + hex 00022111 + hex 00022222 + hex 00221111 + hex 02222000 + + hex ff000000 + hex ff000000 + hex fff00000 + hex ffff0000 + hex fff00000 + hex fff00000 + hex ff000000 + hex f0000fff + +; Tile ID 49 +; From image coordinates 128, 8 + hex 00211111 + hex 00211111 + hex 00121111 + hex 02112222 + hex 22111111 + hex 22200001 + hex 00000000 + hex 00000000 + + hex ff000000 + hex ff000000 + hex ff000000 + hex f0000000 + hex 00000000 + hex 000ffff0 + hex ffffffff + hex ffffffff + + hex 11111200 + hex 11111200 + hex 11112100 + hex 22221120 + hex 11111122 + hex 10000222 + hex 00000000 + hex 00000000 + + hex 000000ff + hex 000000ff + hex 000000ff + hex 0000000f + hex 00000000 + hex 0ffff000 + hex ffffffff + hex ffffffff + +; Tile ID 50 +; From image coordinates 136, 8 + hex 11223222 + hex 11232222 + hex 11322220 + hex 22232200 + hex 11113000 + hex 11122000 + hex 00222200 + hex 00222200 + + hex 00000000 + hex 00000000 + hex 0000000f + hex 000000ff + hex 00000fff + hex 00000fff + hex ff0000ff + hex ff0000ff + + hex 22232211 + hex 22223211 + hex 02222311 + hex 00223222 + hex 00031111 + hex 00022111 + hex 00222200 + hex 00222200 + + hex 00000000 + hex 00000000 + hex f0000000 + hex ff000000 + hex fff00000 + hex fff00000 + hex ff0000ff + hex ff0000ff + +; Tile ID 51 +; From image coordinates 144, 8 + hex 00222000 + hex 00222000 + hex 00020000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ff000fff + hex ff000fff + hex fff0ffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00022200 + hex 00022200 + hex 00002000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex fff000ff + hex fff000ff + hex ffff0fff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 52 +; From image coordinates 152, 8 + hex 33200000 + hex 00200000 + hex 00200000 + hex 23222200 + hex 23222220 + hex 22222200 + hex 00200000 + hex 00200000 + + hex 000fffff + hex ff0fffff + hex ff0fffff + hex 000000ff + hex 0000000f + hex 000000ff + hex ff0fffff + hex ff0fffff + + hex 00000233 + hex 00000200 + hex 00000200 + hex 00222232 + hex 02222232 + hex 00222222 + hex 00000200 + hex 00000200 + + hex fffff000 + hex fffff0ff + hex fffff0ff + hex ff000000 + hex f0000000 + hex ff000000 + hex fffff0ff + hex fffff0ff + +; Tile ID 53 +; From image coordinates 160, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 54 +; From image coordinates 168, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 55 +; From image coordinates 176, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 56 +; From image coordinates 184, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 57 +; From image coordinates 192, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 58 +; From image coordinates 200, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 59 +; From image coordinates 208, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 60 +; From image coordinates 216, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 61 +; From image coordinates 224, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 62 +; From image coordinates 232, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 63 +; From image coordinates 240, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 64 +; From image coordinates 248, 8 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 65 +; From image coordinates 0, 16 + hex 55555555 + hex 55556567 + hex 55656666 + hex 55667666 + hex 56666666 + hex 56666676 + hex 66666666 + hex 66766666 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 55555555 + hex 76565555 + hex 66665655 + hex 66676655 + hex 66666665 + hex 67666665 + hex 66666666 + hex 66666766 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 66 +; From image coordinates 8, 16 + hex 55555555 + hex 77555555 + hex 67777555 + hex 66776755 + hex 66677755 + hex 66767775 + hex 66777755 + hex 66677775 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 55555555 + hex 55555577 + hex 55577776 + hex 55767766 + hex 55777666 + hex 57776766 + hex 55777766 + hex 57777666 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 67 +; From image coordinates 16, 16 + hex 55667555 + hex 56666755 + hex 56566776 + hex 65666756 + hex 65666756 + hex 65667566 + hex 66667566 + hex 66667566 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 55576655 + hex 55766665 + hex 67766565 + hex 65766656 + hex 65766656 + hex 66576656 + hex 66576666 + hex 66576666 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 68 +; From image coordinates 24, 16 + hex 55555555 + hex 55555555 + hex 66775555 + hex 66667555 + hex 66667755 + hex 66666755 + hex 76666775 + hex 76677775 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 55555555 + hex 55555555 + hex 55557766 + hex 55576666 + hex 55776666 + hex 55766666 + hex 57766667 + hex 57777667 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 69 +; From image coordinates 32, 16 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555577 + hex 55555567 + hex 55555667 + hex 55555667 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 77555555 + hex 76555555 + hex 76655555 + hex 76655555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 70 +; From image coordinates 40, 16 + hex 55667755 + hex 56666775 + hex 66666675 + hex 56666677 + hex 56666677 + hex 56666777 + hex 56666677 + hex 76666675 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 55776655 + hex 57766665 + hex 57666666 + hex 77666665 + hex 77666665 + hex 77766665 + hex 77666665 + hex 57666667 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 71 +; From image coordinates 48, 16 + hex 55555555 + hex 55567555 + hex 55666755 + hex 56666675 + hex 76666675 + hex 56666667 + hex 56666667 + hex 56666677 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 55555555 + hex 55576555 + hex 55766655 + hex 57666665 + hex 57666667 + hex 76666665 + hex 76666665 + hex 77666665 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 72 +; From image coordinates 56, 16 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 65555555 + hex 66777555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555556 + hex 55577766 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 73 +; From image coordinates 64, 16 + hex 66665666 + hex 66665666 + hex 66675666 + hex 66675666 + hex 66675666 + hex 66675666 + hex 76775666 + hex 57775666 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 66656666 + hex 66656666 + hex 66657666 + hex 66657666 + hex 66657666 + hex 66657666 + hex 66657767 + hex 66657775 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 74 +; From image coordinates 72, 16 + hex 77756677 + hex 66756667 + hex 66756667 + hex 66756666 + hex 66756666 + hex 66756666 + hex 66775666 + hex 66777566 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 77665777 + hex 76665766 + hex 76665766 + hex 66665766 + hex 66665766 + hex 66665766 + hex 66657766 + hex 66577766 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 75 +; From image coordinates 80, 16 + hex 66675775 + hex 66667756 + hex 66667756 + hex 56667756 + hex 56667566 + hex 66667566 + hex 66667566 + hex 66667566 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 57757666 + hex 65776666 + hex 65776666 + hex 65776665 + hex 66576665 + hex 66576666 + hex 66576666 + hex 66576666 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 76 +; From image coordinates 88, 16 + hex 66775766 + hex 66667775 + hex 66667767 + hex 66666767 + hex 66666767 + hex 66666667 + hex 66655775 + hex 66566675 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 66757766 + hex 57776666 + hex 76776666 + hex 76766666 + hex 76766666 + hex 76666666 + hex 57755666 + hex 57666566 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 77 +; From image coordinates 96, 16 + hex 56666667 + hex 56666666 + hex 56776666 + hex 55667666 + hex 56667666 + hex 56667666 + hex 56667666 + hex 56667666 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 76666665 + hex 66666665 + hex 66667765 + hex 66676655 + hex 66676665 + hex 66676665 + hex 66676665 + hex 66676665 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 78 +; From image coordinates 104, 16 + hex 77566777 + hex 77566677 + hex 77566677 + hex 77566675 + hex 77566675 + hex 77567755 + hex 77775555 + hex 77555555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 77766577 + hex 77666577 + hex 77666577 + hex 57666577 + hex 57666577 + hex 55776577 + hex 55557777 + hex 55555577 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 79 +; From image coordinates 112, 16 + hex 00000883 + hex 00088388 + hex 88888888 + hex 08888838 + hex 00883888 + hex 88889988 + hex 08899899 + hex 00899889 + + hex fffff000 + hex fff00000 + hex 00000000 + hex f0000000 + hex ff000000 + hex 00000000 + hex f0000000 + hex ff000000 + + hex 38800000 + hex 88388000 + hex 88888888 + hex 83888880 + hex 88838800 + hex 88998888 + hex 99899880 + hex 98899800 + + hex 000fffff + hex 00000fff + hex 00000000 + hex 0000000f + hex 000000ff + hex 00000000 + hex 0000000f + hex 000000ff + +; Tile ID 80 +; From image coordinates 120, 16 + hex 38800000 + hex 88388000 + hex 88888888 + hex 83888880 + hex 88838800 + hex 88998888 + hex 99899880 + hex 98899800 + + hex 000fffff + hex 00000fff + hex 00000000 + hex 0000000f + hex 000000ff + hex 00000000 + hex 0000000f + hex 000000ff + + hex 00000883 + hex 00088388 + hex 88888888 + hex 08888838 + hex 00883888 + hex 88889988 + hex 08899899 + hex 00899889 + + hex fffff000 + hex fff00000 + hex 00000000 + hex f0000000 + hex ff000000 + hex 00000000 + hex f0000000 + hex ff000000 + +; Tile ID 81 +; From image coordinates 128, 16 + hex 00000883 + hex 88088388 + hex 08838888 + hex 00888838 + hex 88883888 + hex 08889988 + hex 00899899 + hex 08899889 + + hex fffff000 + hex 00f00000 + hex f0000000 + hex ff000000 + hex 00000000 + hex f0000000 + hex ff000000 + hex f0000000 + + hex 38800000 + hex 88388088 + hex 88883880 + hex 83888800 + hex 88838888 + hex 88998880 + hex 99899800 + hex 98899880 + + hex 000fffff + hex 00000f00 + hex 0000000f + hex 000000ff + hex 00000000 + hex 0000000f + hex 000000ff + hex 0000000f + +; Tile ID 82 +; From image coordinates 136, 16 + hex 38800000 + hex 88388088 + hex 88883880 + hex 83888800 + hex 88838888 + hex 88998880 + hex 99899800 + hex 98899880 + + hex 000fffff + hex 00000f00 + hex 0000000f + hex 000000ff + hex 00000000 + hex 0000000f + hex 000000ff + hex 0000000f + + hex 00000883 + hex 88088388 + hex 08838888 + hex 00888838 + hex 88883888 + hex 08889988 + hex 00899899 + hex 08899889 + + hex fffff000 + hex 00f00000 + hex f0000000 + hex ff000000 + hex 00000000 + hex f0000000 + hex ff000000 + hex f0000000 + +; Tile ID 83 +; From image coordinates 144, 16 + hex 00000000 + hex 00000088 + hex 00000008 + hex 00008888 + hex 80008899 + hex 80000898 + hex 88888988 + hex 88888999 + + hex ffffffff + hex ffffff00 + hex fffffff0 + hex ffff0000 + hex 0fff0000 + hex 0ffff000 + hex 00000000 + hex 00000000 + + hex 00000000 + hex 88000000 + hex 80000000 + hex 88880000 + hex 99880008 + hex 89800008 + hex 88988888 + hex 99988888 + + hex ffffffff + hex 00ffffff + hex 0fffffff + hex 0000ffff + hex 0000fff0 + hex 000ffff0 + hex 00000000 + hex 00000000 + +; Tile ID 84 +; From image coordinates 152, 16 + hex 00800800 + hex 08808800 + hex 88888800 + hex 99888880 + hex 99938880 + hex 88988838 + hex 89883888 + hex 99888883 + + hex ff0ff0ff + hex f00f00ff + hex 000000ff + hex 0000000f + hex 0000000f + hex 00000000 + hex 00000000 + hex 00000000 + + hex 00800800 + hex 00880880 + hex 00888888 + hex 08888899 + hex 08883999 + hex 83888988 + hex 88838898 + hex 38888899 + + hex ff0ff0ff + hex ff00f00f + hex ff000000 + hex f0000000 + hex f0000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 85 +; From image coordinates 160, 16 + hex 00000008 + hex 00000008 + hex 00008008 + hex 00008888 + hex 00800899 + hex 00880898 + hex 00888988 + hex 00888999 + + hex fffffff0 + hex fffffff0 + hex ffff0ff0 + hex ffff0000 + hex ff0ff000 + hex ff00f000 + hex ff000000 + hex ff000000 + + hex 80000000 + hex 80000000 + hex 80080000 + hex 88880000 + hex 99800800 + hex 89808800 + hex 88988800 + hex 99988800 + + hex 0fffffff + hex 0fffffff + hex 0ff0ffff + hex 0000ffff + hex 000ff0ff + hex 000f00ff + hex 000000ff + hex 000000ff + +; Tile ID 86 +; From image coordinates 168, 16 + hex 00080080 + hex 80880880 + hex 88888800 + hex 99888880 + hex 99938880 + hex 88988838 + hex 89883888 + hex 99888883 + + hex fff0ff0f + hex 0f00f00f + hex 000000ff + hex 0000000f + hex 0000000f + hex 00000000 + hex 00000000 + hex 00000000 + + hex 08008000 + hex 08808808 + hex 00888888 + hex 08888899 + hex 08883999 + hex 83888988 + hex 88838898 + hex 38888899 + + hex f0ff0fff + hex f00f00f0 + hex ff000000 + hex f0000000 + hex f0000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 87 +; From image coordinates 176, 16 + hex 00000000 + hex 00000000 + hex 00332000 + hex 02233200 + hex 23233220 + hex 23332220 + hex 23233232 + hex 33323232 + + hex ffffffff + hex ffffffff + hex ff000fff + hex f00000ff + hex 0000000f + hex 0000000f + hex 00000000 + hex 00000000 + + hex 00000000 + hex 00000000 + hex 00023300 + hex 00233220 + hex 02233232 + hex 02223332 + hex 23233232 + hex 23232333 + + hex ffffffff + hex ffffffff + hex fff000ff + hex ff00000f + hex f0000000 + hex f0000000 + hex 00000000 + hex 00000000 + +; Tile ID 88 +; From image coordinates 184, 16 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 89 +; From image coordinates 192, 16 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 90 +; From image coordinates 200, 16 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 91 +; From image coordinates 208, 16 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 92 +; From image coordinates 216, 16 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 93 +; From image coordinates 224, 16 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 94 +; From image coordinates 232, 16 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 95 +; From image coordinates 240, 16 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 96 +; From image coordinates 248, 16 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 97 +; From image coordinates 0, 24 + hex 56666666 + hex 66667666 + hex 66666666 + hex 66766666 + hex 56666667 + hex 55566a6a + hex 55557aaa + hex 55577777 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 66666665 + hex 66676666 + hex 66666666 + hex 66666766 + hex 76666665 + hex a6a66555 + hex aaa75555 + hex 77777555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 98 +; From image coordinates 8, 24 + hex 76677675 + hex 66677775 + hex 67777755 + hex 66767755 + hex 77777555 + hex 77655555 + hex aa777777 + hex 77777755 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 57677667 + hex 57777666 + hex 55777776 + hex 55776766 + hex 55577777 + hex 55555677 + hex 777777aa + hex 55777777 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 99 +; From image coordinates 16, 24 + hex 56667566 + hex 56667566 + hex 56667566 + hex 66667666 + hex 66667666 + hex 66666666 + hex 56666676 + hex 77777777 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 66576665 + hex 66576665 + hex 66576665 + hex 66676666 + hex 66676666 + hex 66666666 + hex 67666665 + hex 77777777 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 100 +; From image coordinates 24, 24 + hex 76676775 + hex 77676775 + hex 77676775 + hex 67677775 + hex 66677675 + hex 67776655 + hex 66777777 + hex 77777755 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 57767667 + hex 57767677 + hex 57767677 + hex 57777676 + hex 57677666 + hex 55667776 + hex 77777766 + hex 55777777 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 101 +; From image coordinates 32, 24 + hex 55577667 + hex 55667667 + hex 56666667 + hex 56667667 + hex 56667677 + hex 56667677 + hex 66666677 + hex 66666677 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 76677555 + hex 76676655 + hex 76666665 + hex 76676665 + hex 77676665 + hex 77676665 + hex 77666666 + hex 77666666 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 102 +; From image coordinates 40, 24 + hex 66666665 + hex 66666665 + hex 66666675 + hex 56666677 + hex 76666677 + hex 66666677 + hex 76666775 + hex 76776675 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 56666666 + hex 56666666 + hex 57666666 + hex 77666665 + hex 77666667 + hex 77666666 + hex 57766667 + hex 57667767 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 103 +; From image coordinates 48, 24 + hex 66666675 + hex 66666775 + hex 66666775 + hex 56666677 + hex 76666667 + hex 55776667 + hex 66667677 + hex 66677667 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 57666666 + hex 57766666 + hex 57766666 + hex 77666665 + hex 76666667 + hex 76667755 + hex 77676666 + hex 76677666 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 104 +; From image coordinates 56, 24 + hex 67666755 + hex 66666755 + hex 66666675 + hex 56666675 + hex 56666675 + hex 56666775 + hex 57766775 + hex 57776755 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 55766676 + hex 55766666 + hex 57666666 + hex 57666665 + hex 57666665 + hex 57766665 + hex 57766775 + hex 55767775 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 105 +; From image coordinates 64, 24 + hex 55577666 + hex 55557676 + hex 55555777 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 66677555 + hex 67675555 + hex 77755555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 106 +; From image coordinates 72, 24 + hex 66777566 + hex 67777566 + hex 77777566 + hex 77777566 + hex 55557566 + hex 55555766 + hex 55555766 + hex 55555577 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 66577766 + hex 66577776 + hex 66577777 + hex 66577777 + hex 66575555 + hex 66755555 + hex 66755555 + hex 77555555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 107 +; From image coordinates 80, 24 + hex 66677566 + hex 66677566 + hex 56777576 + hex 56755577 + hex 67566666 + hex 67566667 + hex 65666666 + hex 75656657 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 66577666 + hex 66577666 + hex 67577765 + hex 77555765 + hex 66666576 + hex 76666576 + hex 66666656 + hex 75665657 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 108 +; From image coordinates 88, 24 + hex 66566667 + hex 66566667 + hex 67566677 + hex 67566675 + hex 77566675 + hex 77566675 + hex 77766777 + hex 57755775 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 76666566 + hex 76666566 + hex 77666576 + hex 57666576 + hex 57666577 + hex 57666577 + hex 77766777 + hex 57755775 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 109 +; From image coordinates 96, 24 + hex 56667667 + hex 56667667 + hex 75667667 + hex 75667667 + hex 75667677 + hex 75677775 + hex 77555555 + hex 55555555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 76676665 + hex 76676665 + hex 76676657 + hex 76676657 + hex 77676657 + hex 57777657 + hex 55555577 + hex 55555555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 110 +; From image coordinates 104, 24 + hex 75555555 + hex 75555555 + hex 75555555 + hex 75555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 55555557 + hex 55555557 + hex 55555557 + hex 55555557 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 111 +; From image coordinates 112, 24 + hex 08889889 + hex 08089989 + hex 00088899 + hex 00088088 + hex 00000088 + hex 00000088 + hex 00000088 + hex 00008888 + + hex f0000000 + hex f0f00000 + hex fff00000 + hex fff00f00 + hex ffffff00 + hex ffffff00 + hex ffffff00 + hex ffff0000 + + hex 98898880 + hex 98998080 + hex 99888000 + hex 88088000 + hex 88000000 + hex 88000000 + hex 88000000 + hex 88880000 + + hex 0000000f + hex 00000f0f + hex 00000fff + hex 00f00fff + hex 00ffffff + hex 00ffffff + hex 00ffffff + hex 0000ffff + +; Tile ID 112 +; From image coordinates 120, 24 + hex 98898880 + hex 98998080 + hex 99888000 + hex 88088000 + hex 88000000 + hex 88000000 + hex 88000000 + hex 88880000 + + hex 0000000f + hex 00000f0f + hex 00000fff + hex 00f00fff + hex 00ffffff + hex 00ffffff + hex 00ffffff + hex 0000ffff + + hex 08889889 + hex 08089989 + hex 00088899 + hex 00088088 + hex 00000088 + hex 00000088 + hex 00000088 + hex 00008888 + + hex f0000000 + hex f0f00000 + hex fff00000 + hex fff00f00 + hex ffffff00 + hex ffffff00 + hex ffffff00 + hex ffff0000 + +; Tile ID 113 +; From image coordinates 128, 24 + hex 88889889 + hex 00089989 + hex 00088899 + hex 00880088 + hex 00000888 + hex 00008888 + hex 00000000 + hex 00000000 + + hex 00000000 + hex fff00000 + hex fff00000 + hex ff00ff00 + hex fffff000 + hex ffff0000 + hex ffffffff + hex ffffffff + + hex 98898888 + hex 98998000 + hex 99888000 + hex 88008800 + hex 88800000 + hex 88880000 + hex 00000000 + hex 00000000 + + hex 00000000 + hex 00000fff + hex 00000fff + hex 00ff00ff + hex 000fffff + hex 0000ffff + hex ffffffff + hex ffffffff + +; Tile ID 114 +; From image coordinates 136, 24 + hex 98898888 + hex 98998000 + hex 99888000 + hex 88008800 + hex 88800000 + hex 88880000 + hex 00000000 + hex 00000000 + + hex 00000000 + hex 00000fff + hex 00000fff + hex 00ff00ff + hex 000fffff + hex 0000ffff + hex ffffffff + hex ffffffff + + hex 88889889 + hex 00089989 + hex 00088899 + hex 00880088 + hex 00000888 + hex 00008888 + hex 00000000 + hex 00000000 + + hex 00000000 + hex fff00000 + hex fff00000 + hex ff00ff00 + hex fffff000 + hex ffff0000 + hex ffffffff + hex ffffffff + +; Tile ID 115 +; From image coordinates 144, 24 + hex 88888999 + hex 88888988 + hex 80000898 + hex 80008899 + hex 00008888 + hex 00000008 + hex 00000088 + hex 00000000 + + hex 00000000 + hex 00000000 + hex 0ffff000 + hex 0fff0000 + hex ffff0000 + hex fffffff0 + hex ffffff00 + hex ffffffff + + hex 99988888 + hex 88988888 + hex 89800008 + hex 99880008 + hex 88880000 + hex 80000000 + hex 88000000 + hex 00000000 + + hex 00000000 + hex 00000000 + hex 000ffff0 + hex 0000fff0 + hex 0000ffff + hex 0fffffff + hex 00ffffff + hex ffffffff + +; Tile ID 116 +; From image coordinates 152, 24 + hex 99888883 + hex 89883888 + hex 88988838 + hex 99938880 + hex 99888880 + hex 88888800 + hex 08808800 + hex 00800800 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 0000000f + hex 0000000f + hex 000000ff + hex f00f00ff + hex ff0ff0ff + + hex 38888899 + hex 88838898 + hex 83888988 + hex 08883999 + hex 08888899 + hex 00888888 + hex 00880880 + hex 00800800 + + hex 00000000 + hex 00000000 + hex 00000000 + hex f0000000 + hex f0000000 + hex ff000000 + hex ff00f00f + hex ff0ff0ff + +; Tile ID 117 +; From image coordinates 160, 24 + hex 00888999 + hex 00888988 + hex 00880898 + hex 00800899 + hex 00008888 + hex 00008008 + hex 00000008 + hex 00000008 + + hex ff000000 + hex ff000000 + hex ff00f000 + hex ff0ff000 + hex ffff0000 + hex ffff0ff0 + hex fffffff0 + hex fffffff0 + + hex 99988800 + hex 88988800 + hex 89808800 + hex 99800800 + hex 88880000 + hex 80080000 + hex 80000000 + hex 80000000 + + hex 000000ff + hex 000000ff + hex 000f00ff + hex 000ff0ff + hex 0000ffff + hex 0ff0ffff + hex 0fffffff + hex 0fffffff + +; Tile ID 118 +; From image coordinates 168, 24 + hex 99888883 + hex 89883888 + hex 88988838 + hex 99938880 + hex 99888880 + hex 88888800 + hex 80880880 + hex 00080080 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 0000000f + hex 0000000f + hex 000000ff + hex 0f00f00f + hex fff0ff0f + + hex 38888899 + hex 88838898 + hex 83888988 + hex 08883999 + hex 08888899 + hex 00888888 + hex 08808808 + hex 08008000 + + hex 00000000 + hex 00000000 + hex 00000000 + hex f0000000 + hex f0000000 + hex ff000000 + hex f00f00f0 + hex f0ff0fff + +; Tile ID 119 +; From image coordinates 176, 24 + hex 33332222 + hex 32332220 + hex 02222220 + hex 00022000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 00000000 + hex 0000000f + hex f000000f + hex fff00fff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 22223333 + hex 02223323 + hex 02222220 + hex 00022000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 00000000 + hex f0000000 + hex f000000f + hex fff00fff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 120 +; From image coordinates 184, 24 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 121 +; From image coordinates 192, 24 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 122 +; From image coordinates 200, 24 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 123 +; From image coordinates 208, 24 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 124 +; From image coordinates 216, 24 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 125 +; From image coordinates 224, 24 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 126 +; From image coordinates 232, 24 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 127 +; From image coordinates 240, 24 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 128 +; From image coordinates 248, 24 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 129 +; From image coordinates 0, 32 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + hex 55555555 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 130 +; From image coordinates 8, 32 + hex 77777777 + hex 77777777 + hex 77777777 + hex 77777777 + hex 77777777 + hex 77777777 + hex 77777777 + hex 77777777 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 77777777 + hex 77777777 + hex 77777777 + hex 77777777 + hex 77777777 + hex 77777777 + hex 77777777 + hex 77777777 + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +; Tile ID 131 +; From image coordinates 16, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 132 +; From image coordinates 24, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 133 +; From image coordinates 32, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 134 +; From image coordinates 40, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 135 +; From image coordinates 48, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 136 +; From image coordinates 56, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 137 +; From image coordinates 64, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 138 +; From image coordinates 72, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 139 +; From image coordinates 80, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 140 +; From image coordinates 88, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 141 +; From image coordinates 96, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 142 +; From image coordinates 104, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 143 +; From image coordinates 112, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 144 +; From image coordinates 120, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 145 +; From image coordinates 128, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 146 +; From image coordinates 136, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 147 +; From image coordinates 144, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 148 +; From image coordinates 152, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 149 +; From image coordinates 160, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 150 +; From image coordinates 168, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 151 +; From image coordinates 176, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 152 +; From image coordinates 184, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 153 +; From image coordinates 192, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 154 +; From image coordinates 200, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 155 +; From image coordinates 208, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 156 +; From image coordinates 216, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 157 +; From image coordinates 224, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 158 +; From image coordinates 232, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 159 +; From image coordinates 240, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 160 +; From image coordinates 248, 32 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 161 +; From image coordinates 0, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 162 +; From image coordinates 8, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 163 +; From image coordinates 16, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 164 +; From image coordinates 24, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 165 +; From image coordinates 32, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 166 +; From image coordinates 40, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 167 +; From image coordinates 48, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 168 +; From image coordinates 56, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 169 +; From image coordinates 64, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 170 +; From image coordinates 72, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 171 +; From image coordinates 80, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 172 +; From image coordinates 88, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 173 +; From image coordinates 96, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 174 +; From image coordinates 104, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 175 +; From image coordinates 112, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 176 +; From image coordinates 120, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 177 +; From image coordinates 128, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 178 +; From image coordinates 136, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 179 +; From image coordinates 144, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 180 +; From image coordinates 152, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 181 +; From image coordinates 160, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 182 +; From image coordinates 168, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 183 +; From image coordinates 176, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 184 +; From image coordinates 184, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 185 +; From image coordinates 192, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 186 +; From image coordinates 200, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 187 +; From image coordinates 208, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 188 +; From image coordinates 216, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 189 +; From image coordinates 224, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 190 +; From image coordinates 232, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 191 +; From image coordinates 240, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 192 +; From image coordinates 248, 40 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 193 +; From image coordinates 0, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 194 +; From image coordinates 8, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 195 +; From image coordinates 16, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 196 +; From image coordinates 24, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 197 +; From image coordinates 32, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 198 +; From image coordinates 40, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 199 +; From image coordinates 48, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 200 +; From image coordinates 56, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 201 +; From image coordinates 64, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 202 +; From image coordinates 72, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 203 +; From image coordinates 80, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 204 +; From image coordinates 88, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 205 +; From image coordinates 96, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 206 +; From image coordinates 104, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 207 +; From image coordinates 112, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 208 +; From image coordinates 120, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 209 +; From image coordinates 128, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 210 +; From image coordinates 136, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 211 +; From image coordinates 144, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 212 +; From image coordinates 152, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 213 +; From image coordinates 160, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 214 +; From image coordinates 168, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 215 +; From image coordinates 176, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 216 +; From image coordinates 184, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 217 +; From image coordinates 192, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 218 +; From image coordinates 200, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 219 +; From image coordinates 208, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 220 +; From image coordinates 216, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 221 +; From image coordinates 224, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 222 +; From image coordinates 232, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 223 +; From image coordinates 240, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 224 +; From image coordinates 248, 48 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 225 +; From image coordinates 0, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 226 +; From image coordinates 8, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 227 +; From image coordinates 16, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 228 +; From image coordinates 24, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 229 +; From image coordinates 32, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 230 +; From image coordinates 40, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 231 +; From image coordinates 48, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 232 +; From image coordinates 56, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 233 +; From image coordinates 64, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 234 +; From image coordinates 72, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 235 +; From image coordinates 80, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 236 +; From image coordinates 88, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 237 +; From image coordinates 96, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 238 +; From image coordinates 104, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 239 +; From image coordinates 112, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 240 +; From image coordinates 120, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 241 +; From image coordinates 128, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 242 +; From image coordinates 136, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 243 +; From image coordinates 144, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 244 +; From image coordinates 152, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 245 +; From image coordinates 160, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 246 +; From image coordinates 168, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 247 +; From image coordinates 176, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 248 +; From image coordinates 184, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 249 +; From image coordinates 192, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 250 +; From image coordinates 200, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 251 +; From image coordinates 208, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 252 +; From image coordinates 216, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 253 +; From image coordinates 224, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 254 +; From image coordinates 232, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 255 +; From image coordinates 240, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 256 +; From image coordinates 248, 56 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 257 +; From image coordinates 0, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 258 +; From image coordinates 8, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 259 +; From image coordinates 16, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 260 +; From image coordinates 24, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 261 +; From image coordinates 32, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 262 +; From image coordinates 40, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 263 +; From image coordinates 48, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 264 +; From image coordinates 56, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 265 +; From image coordinates 64, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 266 +; From image coordinates 72, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 267 +; From image coordinates 80, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 268 +; From image coordinates 88, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 269 +; From image coordinates 96, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 270 +; From image coordinates 104, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 271 +; From image coordinates 112, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 272 +; From image coordinates 120, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 273 +; From image coordinates 128, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 274 +; From image coordinates 136, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 275 +; From image coordinates 144, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 276 +; From image coordinates 152, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 277 +; From image coordinates 160, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 278 +; From image coordinates 168, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 279 +; From image coordinates 176, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 280 +; From image coordinates 184, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 281 +; From image coordinates 192, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 282 +; From image coordinates 200, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 283 +; From image coordinates 208, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 284 +; From image coordinates 216, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 285 +; From image coordinates 224, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 286 +; From image coordinates 232, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 287 +; From image coordinates 240, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 288 +; From image coordinates 248, 64 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 289 +; From image coordinates 0, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 290 +; From image coordinates 8, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 291 +; From image coordinates 16, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 292 +; From image coordinates 24, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 293 +; From image coordinates 32, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 294 +; From image coordinates 40, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 295 +; From image coordinates 48, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 296 +; From image coordinates 56, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 297 +; From image coordinates 64, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 298 +; From image coordinates 72, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 299 +; From image coordinates 80, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 300 +; From image coordinates 88, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 301 +; From image coordinates 96, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 302 +; From image coordinates 104, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 303 +; From image coordinates 112, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 304 +; From image coordinates 120, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 305 +; From image coordinates 128, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 306 +; From image coordinates 136, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 307 +; From image coordinates 144, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 308 +; From image coordinates 152, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 309 +; From image coordinates 160, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 310 +; From image coordinates 168, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 311 +; From image coordinates 176, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 312 +; From image coordinates 184, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 313 +; From image coordinates 192, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 314 +; From image coordinates 200, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 315 +; From image coordinates 208, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 316 +; From image coordinates 216, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 317 +; From image coordinates 224, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 318 +; From image coordinates 232, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 319 +; From image coordinates 240, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 320 +; From image coordinates 248, 72 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 321 +; From image coordinates 0, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 322 +; From image coordinates 8, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 323 +; From image coordinates 16, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 324 +; From image coordinates 24, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 325 +; From image coordinates 32, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 326 +; From image coordinates 40, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 327 +; From image coordinates 48, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 328 +; From image coordinates 56, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 329 +; From image coordinates 64, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 330 +; From image coordinates 72, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 331 +; From image coordinates 80, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 332 +; From image coordinates 88, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 333 +; From image coordinates 96, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 334 +; From image coordinates 104, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 335 +; From image coordinates 112, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 336 +; From image coordinates 120, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 337 +; From image coordinates 128, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 338 +; From image coordinates 136, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 339 +; From image coordinates 144, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 340 +; From image coordinates 152, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 341 +; From image coordinates 160, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 342 +; From image coordinates 168, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 343 +; From image coordinates 176, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 344 +; From image coordinates 184, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 345 +; From image coordinates 192, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 346 +; From image coordinates 200, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 347 +; From image coordinates 208, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 348 +; From image coordinates 216, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 349 +; From image coordinates 224, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 350 +; From image coordinates 232, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 351 +; From image coordinates 240, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 352 +; From image coordinates 248, 80 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 353 +; From image coordinates 0, 88 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 354 +; From image coordinates 8, 88 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 355 +; From image coordinates 16, 88 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 356 +; From image coordinates 24, 88 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 357 +; From image coordinates 32, 88 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 358 +; From image coordinates 40, 88 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 359 +; From image coordinates 48, 88 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + +; Tile ID 360 +; From image coordinates 56, 88 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + hex ffffffff + diff --git a/demos/zelda/.gitignore b/demos/zelda/.gitignore new file mode 100644 index 0000000..49eb481 --- /dev/null +++ b/demos/zelda/.gitignore @@ -0,0 +1 @@ +GTEZelda \ No newline at end of file diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s index a89cafa..abc3758 100644 --- a/macros/GTE.Macs.s +++ b/macros/GTE.Macs.s @@ -36,3 +36,6 @@ _GTESetBG0Origin MAC _GTERender MAC UserTool $D00+GTEToolNum <<< +_GTELoadTileSet MAC + UserTool $E00+GTEToolNum + <<< diff --git a/package.json b/package.json index 6391504..c79ebe3 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ }, "scripts": { "test": "npm run build && build-image.bat %npm_package_config_cadius% && %npm_package_config_gsport%", - "build": "%npm_package_config_merlin32% -V %npm_package_config_macros% ./src/Master.s" + "build": "%npm_package_config_merlin32% -V %npm_package_config_macros% ./src/Master.s", + "build:debug": "%npm_package_config_merlin32% -V %npm_package_config_macros% ./src/Debug.s", + "debug": "%npm_package_config_crossrunner% ./src/Debug160 -Source ./src/Debug160_S02__Output.txt -Debug -CompatibilityLayer" }, "repository": { "type": "git", diff --git a/src/Render.s b/src/Render.s index d78d13b..cba02a2 100644 --- a/src/Render.s +++ b/src/Render.s @@ -28,12 +28,12 @@ _Render jsr _ApplyBG0XPosPre jsr _ApplyBG1XPosPre - jsr _RenderSprites ; Once the BG0 X and Y positions are committed, update sprite data +; 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 _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 _ApplyTiles ; This function actually draws the new tiles into the code field + jsr _ApplyTilesFast ; This function actually draws the new tiles into the code field jsr _ApplyBG0XPos ; Patch the code field instructions with exit BRA opcode jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position @@ -92,6 +92,73 @@ _Render stz LastRender ; Mark that a full render was just performed rts +; The _ApplyTilesFast is the same as _ApplyTiles, but we use the _RenderTileFast subroutine +_ApplyTilesFast + tdc + clc + adc #$100 ; move to the next page + tcd + + ldy DirtyTileCount + beq :out + +:loop +; Retrieve the offset of the next dirty Tile Store items in the X-register + + jsr _PopDirtyTile2 + +; Call the generic dispatch with the Tile Store record pointer at by the X-register. + + phb + jsr _RenderTileFast + plb + +; Loop again until the list of dirty tiles is empty + + ldy DirtyTileCount + bne :loop + +:out + tdc ; Move back to the original direct page + sec + sbc #$100 + tcd + 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 @@ -104,10 +171,11 @@ _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 +; jsr _ClearSpritesFromCodeField ; Restore the tiles to their non-sprite versions :norecalc - jsr _RenderSprites - jsr _ApplyDirtyTiles +; jsr _RenderSprites +; jsr _ApplyDirtyTiles lda #1 sta LastRender @@ -135,31 +203,31 @@ _ApplyDirtyTiles ; Only render solid tiles and sprites _RenderDirtyTile - ldal TileStore+TS_SPRITE_FLAG,x ; This is a bitfield of all the sprites that intersect this tile, only care if non-zero or not - bne dirty_sprite + ldx TileStore+TS_VBUFF_ADDR_COUNT,y ; How many sprites are on this tile? + beq NoSpritesDirty ; This is faster if there are no sprites + + lda TileStore+TS_TILE_ID,y ; Check if the tile has + jmp (dirty_dispatch,x) +dirty_dispatch + da NoSpritesDirty + da OneSpriteDirty + da TwoSpritesDirty + da ThreeSpritesDirty + da FourSpritesDirty ; 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. - - pei TileStoreBankAndBank01 ; Special value that has the TileStore bank in LSB and $01 in MSB - plb - - lda TileStore+TS_DIRTY_TILE_DISP,x ; load and patch in the appropriate subroutine - stal :tiledisp+1 - - ldy TileStore+TS_SCREEN_ADDR,x ; Get the on-screen address of this tile - lda TileStore+TS_TILE_ADDR,y ; load the address of this tile's data (pre-calculated) - tax - - plb ; set the bank - +; ; B is set to Bank 01 -; 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 SHR screen -; X is set to the address of the tile data - -:tiledisp jmp $0000 ; render the tile +; A is set to the address of the tile data +NoSpritesDirty + tyx + 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) + plb ; set the code field bank + jmp (TileStore+TS_DIRTY_TILE_DISP,x) ; go to the tile copy routine (just basics) ; Use some temporary space for the spriteIdx array (maximum of 4 entries) @@ -168,750 +236,303 @@ screenAddr equ tmp10 tileAddr equ tmp11 spriteIdx equ tmp12 -; Handler for the sprite path -dirty_sprite - pei TileStoreBankAndTileDataBank ; Special value that has the TileStore bank in LSB and TileData bank in MSB - plb - -; Cache a couple of values into the direct page, but preserve the Accumulator - - ldy TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) - sty tileAddr - ldy TileStore+TS_SCREEN_ADDR,x ; Get the on-screen address of this tile - sty screenAddr - -; Now do all of the deferred work of actually drawing the sprites. We put considerable effort into -; figuring out if there is only one sprite or more than one since we optimize the former case as it -; is very common and can be done significantly faster. -; -; This is a big, unrolled chunk of code that packs the VBUFF addresses for the sprite positions marked -; in the bitfield into the spriteIdx array and then jumps to an optimized rendering function based on -; the number of sprites on the tile. -; -; After each set bit is identified, we check to see if that was the last one and immediately exit. Since -; a maximum of 4 sprites are processed per tile, this only results in (at most) 4 extra branch instructions. - - ldy TileStore+TS_VBUFF_ARRAY_ADDR,x ; base address of the VBUFF sprite address array for this tile - - lsr - bcc :loop_0_bit_1 - ldx: $0000,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_1 - jmp BlitOneSprite - -:loop_0_bit_1 lsr - bcc :loop_0_bit_2 - ldx: $0002,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_2 - jmp BlitOneSprite - -:loop_0_bit_2 lsr - bcc :loop_0_bit_3 - ldx: $0004,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_3 - jmp BlitOneSprite - -:loop_0_bit_3 lsr - bcc :loop_0_bit_4 - ldx: $0006,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_4 - jmp BlitOneSprite - -:loop_0_bit_4 lsr - bcc :loop_0_bit_5 - ldx: $0008,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_5 - jmp BlitOneSprite - -:loop_0_bit_5 lsr - bcc :loop_0_bit_6 - ldx: $000A,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_6 - jmp BlitOneSprite - -:loop_0_bit_6 lsr - bcc :loop_0_bit_7 - ldx: $000C,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_7 - jmp BlitOneSprite - -:loop_0_bit_7 lsr - bcc :loop_0_bit_8 - ldx: $000E,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_8 - jmp BlitOneSprite - -:loop_0_bit_8 lsr - bcc :loop_0_bit_9 - ldx: $0010,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_9 - jmp BlitOneSprite - -:loop_0_bit_9 lsr - bcc :loop_0_bit_10 - ldx: $0012,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_10 - jmp BlitOneSprite - -:loop_0_bit_10 lsr - bcc :loop_0_bit_11 - ldx: $0014,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_11 - jmp BlitOneSprite - -:loop_0_bit_11 lsr - bcc :loop_0_bit_12 - ldx: $0016,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_12 - jmp BlitOneSprite - -:loop_0_bit_12 lsr - bcc :loop_0_bit_13 - ldx: $0018,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_13 - jmp BlitOneSprite - -:loop_0_bit_13 lsr - bcc :loop_0_bit_14 - ldx: $001A,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_14 - jmp BlitOneSprite - -:loop_0_bit_14 lsr - bcc :loop_0_bit_15 - ldx: $001C,y - stx spriteIdx - cmp #0 - jne :loop_1_bit_15 - jmp BlitOneSprite - -; If we get to bit 15, then it *must* be a bit that is set -:loop_0_bit_15 ldx: $001E,y - stx spriteIdx - jmp BlitOneSprite - -:loop_1_bit_1 lsr - bcc :loop_1_bit_2 - ldx: $0002,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_2 - jmp BlitTwoSprites - -:loop_1_bit_2 lsr - bcc :loop_1_bit_3 - ldx: $0004,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_3 - jmp BlitTwoSprites - -:loop_1_bit_3 lsr - bcc :loop_1_bit_4 - ldx: $0006,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_4 - jmp BlitTwoSprites - -:loop_1_bit_4 lsr - bcc :loop_1_bit_5 - ldx: $0008,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_5 - jmp BlitTwoSprites - -:loop_1_bit_5 lsr - bcc :loop_1_bit_6 - ldx: $000A,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_6 - jmp BlitTwoSprites - -:loop_1_bit_6 lsr - bcc :loop_1_bit_7 - ldx: $000C,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_7 - jmp BlitTwoSprites - -:loop_1_bit_7 lsr - bcc :loop_1_bit_8 - ldx: $000E,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_8 - jmp BlitTwoSprites - -:loop_1_bit_8 lsr - bcc :loop_1_bit_9 - ldx: $0010,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_9 - jmp BlitTwoSprites - -:loop_1_bit_9 lsr - bcc :loop_1_bit_10 - ldx: $0012,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_10 - jmp BlitTwoSprites - -:loop_1_bit_10 lsr - bcc :loop_1_bit_11 - ldx: $0014,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_11 - jmp BlitTwoSprites - -:loop_1_bit_11 lsr - bcc :loop_1_bit_12 - ldx: $0016,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_12 - jmp BlitTwoSprites - -:loop_1_bit_12 lsr - bcc :loop_1_bit_13 - ldx: $0018,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_13 - jmp BlitTwoSprites - -:loop_1_bit_13 lsr - bcc :loop_1_bit_14 - ldx: $001A,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_14 - jmp BlitTwoSprites - -:loop_1_bit_14 lsr - bcc :loop_1_bit_15 - ldx: $001C,y - stx spriteIdx+2 - cmp #0 - jne :loop_2_bit_15 - jmp BlitTwoSprites - -:loop_1_bit_15 ldx: $001E,y - stx spriteIdx+2 - jmp BlitTwoSprites - -:loop_2_bit_2 lsr - bcc :loop_2_bit_3 - ldx: $0004,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_3 - jmp BlitThreeSprites - -:loop_2_bit_3 lsr - bcc :loop_2_bit_4 - ldx: $0006,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_4 - jmp BlitThreeSprites - -:loop_2_bit_4 lsr - bcc :loop_2_bit_5 - ldx: $0008,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_5 - jmp BlitThreeSprites - -:loop_2_bit_5 lsr - bcc :loop_2_bit_6 - ldx: $000A,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_6 - jmp BlitThreeSprites - -:loop_2_bit_6 lsr - bcc :loop_2_bit_7 - ldx: $000C,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_7 - jmp BlitThreeSprites - -:loop_2_bit_7 lsr - bcc :loop_2_bit_8 - ldx: $000E,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_8 - jmp BlitThreeSprites - -:loop_2_bit_8 lsr - bcc :loop_2_bit_9 - ldx: $0010,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_9 - jmp BlitThreeSprites - -:loop_2_bit_9 lsr - bcc :loop_2_bit_10 - ldx: $0012,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_10 - jmp BlitThreeSprites - -:loop_2_bit_10 lsr - bcc :loop_2_bit_11 - ldx: $0014,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_11 - jmp BlitThreeSprites - -:loop_2_bit_11 lsr - bcc :loop_2_bit_12 - ldx: $0016,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_12 - jmp BlitThreeSprites - -:loop_2_bit_12 lsr - bcc :loop_2_bit_13 - ldx: $0018,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_13 - jmp BlitThreeSprites - -:loop_2_bit_13 lsr - bcc :loop_2_bit_14 - ldx: $001A,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_14 - jmp BlitThreeSprites - -:loop_2_bit_14 lsr - bcc :loop_2_bit_15 - ldx: $001C,y - stx spriteIdx+4 - cmp #0 - jne :loop_3_bit_15 - jmp BlitThreeSprites - -:loop_2_bit_15 ldx: $001E,y - stx spriteIdx+4 - jmp BlitThreeSprites - -:loop_3_bit_3 lsr - bcc :loop_3_bit_4 - ldx $0006,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_4 lsr - bcc :loop_3_bit_5 - ldx $0008,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_5 lsr - bcc :loop_3_bit_6 - ldx $000A,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_6 lsr - bcc :loop_3_bit_7 - ldx $000C,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_7 lsr - bcc :loop_3_bit_8 - ldx $000E,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_8 lsr - bcc :loop_3_bit_9 - ldx $0010,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_9 lsr - bcc :loop_3_bit_10 - ldx $0012,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_10 lsr - bcc :loop_3_bit_11 - ldx $0014,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_11 lsr - bcc :loop_3_bit_12 - ldx $0016,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_12 lsr - bcc :loop_3_bit_13 - ldx $0018,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_13 lsr - bcc :loop_3_bit_14 - ldx $001A,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_14 lsr - bcc :loop_3_bit_15 - ldx $001C,y - stx spriteIdx+6 - jmp BlitFourSprites - -:loop_3_bit_15 ldx $001E,y - stx spriteIdx+6 - jmp BlitFourSprites - - ; 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. -BlitFourSprites -BlitThreeSprites -BlitTwoSprites - plb - tsc - sta stkSave ; Save the stack on the direct page +FourSpritesDirty +ThreeSpritesDirty +TwoSpritesDirty + + sta tileAddr + sty screenAddr + + plb + tsc + sta stkSave ; Save the stack on the direct page - sei - clc + sei + clc - ldy tileAddr - lda screenAddr ; Saved in direct page locations - tcs + ldy tileAddr + lda screenAddr ; Saved in direct page locations + tcs - _R0W1 + _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},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+{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},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 + 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 + 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},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+{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},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 + 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 + 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},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+{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},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 + 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 + 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},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+{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},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 + 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 + _R0W0 - lda stkSave - tcs - cli - rts + 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 -BlitOneSprite - ldy tileAddr ; load the address of this tile's data - lda screenAddr ; Get the on-screen address of this tile +OneSpriteDirty + ldy tileAddr ; load the address of this tile's data + lda screenAddr ; Get the on-screen address of this tile - plb + plb - phd - sei - clc - tcd + phd + sei + clc + tcd - _R0W1 + _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},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+{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},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 + 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 + 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},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+{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},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 + 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 + 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},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+{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},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 + 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 + 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},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+{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},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 + 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 + _R0W0 + cli + pld + rts diff --git a/src/Tiles.s b/src/Tiles.s index c50718b..9fe7896 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -1,5 +1,22 @@ ; Basic tile functions +; Copy tileset data from a pointer in memory to the tiledata back +; X = high word +; A = low word +_LoadTileSet + sta tmp0 + stx tmp2 + ldy #0 + tyx +:loop lda [tmp0],y + stal tiledata,x + dex + dex + dey + dey + bne :loop + rts + ; 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 @@ -10,8 +27,8 @@ ; The address is the TileID * 128 + (HFLIP * 64) _GetTileAddr asl ; Multiply by 2 - bit #2*TILE_HFLIP_BIT ; Check if the horizontal flip bit is set - beq :no_flip + bit #2*TILE_HFLIP_BIT ; Check if the horizontal flip bit is set + beq :no_flip inc ; Set the LSB :no_flip asl ; x4 asl ; x8 @@ -199,12 +216,12 @@ _SetTile bra :out :fast -; lda FastTileProcs,y -; stal TileStore+TS_BASE_TILE_DISP,x + lda FastTileProcs,y + stal TileStore+TS_BASE_TILE_DISP,x :out -; txa ; Add this tile to the list of dirty tiles to refresh -; jmp _PushDirtyTileX ; on the next call to _ApplyTiles + txa ; Add this tile to the list of dirty tiles to refresh + jmp _PushDirtyTileX ; on the next call to _ApplyTiles :nochange rts diff --git a/src/Tool.s b/src/Tool.s index 87e5a92..1c3204f 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -3,7 +3,6 @@ ; Ref: Toolbox Reference, Volume 2, Appendix A ; Ref: IIgs Tech Note #73 -; use Load.Macs.s use Mem.Macs.s use Misc.Macs.s use Util.Macs @@ -51,6 +50,7 @@ _CallTable adrl _TSSetTile-1 adrl _TSSetBG0Origin-1 adrl _TSRender-1 + adrl _TSLoadTileSet-1 _CTEnd ; Do nothing when the tool set is installed @@ -223,9 +223,20 @@ xPos equ FirstParam+2 ; Render() _TSRender _TSEntry -; jsr _Render + jsr _Render _TSExit #0;#0 +; LoadTileSet(Pointer) +_TSLoadTileSet +TSPtr equ FirstParam + + _TSEntry + + lda TSPtr+2,s + tax + lda TSPtr,s + jsr _LoadTileSet + _TSExit #0;#4 ; Insert the GTE code @@ -235,11 +246,17 @@ _TSRender put Timer.s put Graphics.s put Tiles.s -; put Render.s + put Render.s + put tiles/DirtyTileQueue.s + put tiles/FastRenderer.s + put blitter/Horz.s + put blitter/Vert.s put blitter/BG0.s put blitter/BG1.s put blitter/Template.s put blitter/TemplateUtils.s put blitter/Tables.s put blitter/Blitter.s + put blitter/TileProcs.s + put blitter/Tiles00000.s ; put blitter/Tiles.s diff --git a/src/tiles/TileProcs.s b/src/blitter/TileProcs.s similarity index 79% rename from src/tiles/TileProcs.s rename to src/blitter/TileProcs.s index a990d1c..5fe0b29 100644 --- a/src/tiles/TileProcs.s +++ b/src/blitter/TileProcs.s @@ -4,26 +4,15 @@ _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 +]line equ 0 + lup 8 + sta: $0000+{]line*$1000},y + sta: $0003+{]line*$1000},y +]line equ ]line+1 + --^ rep #$20 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. diff --git a/src/blitter/Tiles.s b/src/blitter/Tiles.s index c175403..c10c524 100644 --- a/src/blitter/Tiles.s +++ b/src/blitter/Tiles.s @@ -37,16 +37,7 @@ ; ; 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. - -; Use some temporary space for the spriteIdx array (maximum of 4 entries) - -stkSave equ tmp9 -screenAddr equ tmp10 -tileAddr equ tmp11 -spriteIdx equ tmp12 - - - +; ; 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. ; @@ -128,7 +119,7 @@ dirty_sprite_dispatch ; This is very similar to the code in the dirty tile renderer, but we can't reuse ; because that code draws directly to the graphics screen, and this code draws -; to a temporary budder that has a different stride. +; to a temporary buffer that has a different stride. ; ldy TileStore+TS_VBUFF_ARRAY_ADDR,x ; base address of the VBUFF sprite address array for this tile ; @@ -692,138 +683,10 @@ _CopyBG1Tile ; TileStore+TS_SPRITE_ADDR_15 ; TileStore+TS_SPRITE_ADDR_16 - -; TileStore+ -;TileStore ENT -; ds TILE_STORE_SIZE*11 - -; A list of dirty tiles that need to be updated in a given frame -DirtyTileCount ds 2 -DirtyTiles ds TILE_STORE_SIZE ; At most this many tiles can possibly be update at once - -_ClearDirtyTiles - bra :hop -:loop - jsr _PopDirtyTile -:hop - lda DirtyTileCount - bne :loop - rts - - -; Append a new dirty tile record -; -; A = result of _GetTileStoreOffset for X, Y -; -; The main purpose 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 ENT - phb - phk - plb - jsr _PushDirtyTile - plb - rtl - -; alternate version that is very slightly slower, but preserves the y-register -_PushDirtyTile - tax - -; alternate entry point if the x-register is already set -_PushDirtyTileX - ldal TileStore+TS_DIRTY,x - bne :occupied2 - - inc ; any non-zero value will work - stal TileStore+TS_DIRTY,x ; and is 1 cycle faster than loading a constant value - - txa - ldx DirtyTileCount ; 4 - sta DirtyTiles,x ; 6 - inx ; 2 - inx ; 2 - stx DirtyTileCount ; 4 = 18 - rts -:occupied2 - txa ; Make sure TileStore offset is returned in the accumulator - 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 ENT - phb - phk - plb - jsr _PopDirtyTile - plb - rtl - -_PopDirtyTile - ldy DirtyTileCount - bne _PopDirtyTile2 - rts - -_PopDirtyTile2 ; alternate entry point - dey - dey - 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 - rts - -; Run through the dirty tile list and render them into the code field -ApplyTiles ENT - phb - phk - plb - jsr _ApplyTiles - plb - rtl - -; 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 - ; To make processing the tile faster, we do them in chunks of eight. This allows the loop to be ; unrolled, which means we don't have to keep track of the register value and makes it faster to ; clear the dirty tile flag after being processed. - +; _ApplyTilesUnrolled tdc ; Move to the dedicated direct page for tile rendering clc adc #$100 diff --git a/src/tiles/DirtyTileQueue.s b/src/tiles/DirtyTileQueue.s new file mode 100644 index 0000000..5f078f2 --- /dev/null +++ b/src/tiles/DirtyTileQueue.s @@ -0,0 +1,64 @@ + +; A list of dirty tiles that need to be updated in a given frame +DirtyTileCount ds 2 +DirtyTiles ds TILE_STORE_SIZE ; At most this many tiles can possibly be update at once + +_ClearDirtyTiles + bra :hop +:loop + jsr _PopDirtyTile +:hop + lda DirtyTileCount + bne :loop + rts + + +; Append a new dirty tile record +; +; A = result of _GetTileStoreOffset for X, Y +; +; The main purpose 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 + tax + +; alternate entry point if the x-register is already set +_PushDirtyTileX + ldal TileStore+TS_DIRTY,x + bne :occupied2 + + inc ; any non-zero value will work + stal TileStore+TS_DIRTY,x ; and is 1 cycle faster than loading a constant value + + txa + ldx DirtyTileCount ; 4 + sta DirtyTiles,x ; 6 + inx ; 2 + inx ; 2 + stx DirtyTileCount ; 4 = 18 + rts +:occupied2 + txa ; Make sure TileStore offset is returned in the accumulator + 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 + ldy DirtyTileCount + bne _PopDirtyTile2 + rts + +_PopDirtyTile2 ; alternate entry point + dey + dey + 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 + rts diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index e405c0b..35f97a2 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -8,9 +8,7 @@ _RenderTileFast ldx TileStore+TS_VBUFF_ADDR_COUNT,y ; How many sprites are on this tile? beq NoSpritesFast ; This is faster if there are no sprites - - lda TileStore+TS_TILE_ID,y ; Check if the tile has - jmp (fast_dispatch,x) + jmp (fast_dispatch,x) ; Dispatch to the other routines fast_dispatch da NoSpritesFast da OneSpriteFast @@ -31,6 +29,16 @@ NoSpritesFast ; ENGINE_MODE_DYN_TILES are both off. FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast +; Pointers to sprite data and masks +spritedata_0 equ tmp0 +spritedata_1 equ tmp2 +spritedata_2 equ tmp4 +spritedata_3 equ tmp6 +spritemask_0 equ tmp8 +spritemask_1 equ tmp10 +spritemask_2 equ tmp12 +spritemask_3 equ tmp14 + ; Where there are sprites involved, the first step is to call a routine to copy the ; tile data into a temporary buffer. Then the sprite data is merged and placed into ; the code field. @@ -47,19 +55,15 @@ OneSpriteFast tay plb ; set the code field bank -]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 + OneSpriteToCodeField 0 + OneSpriteToCodeField 1 + OneSpriteToCodeField 2 + OneSpriteToCodeField 3 + OneSpriteToCodeField 4 + OneSpriteToCodeField 5 + OneSpriteToCodeField 6 + OneSpriteToCodeField 7 - 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 - --^ rts TwoSpritesFast @@ -81,25 +85,15 @@ TwoSpritesFast tay plb ; set the code field bank -]line equ 0 - lup 8 - ldy #{]line*SPRITE_PLANE_SPAN} - lda blttmp+{]line*4} - andl [spritemask_1],y - oral [spritedata_1],y - andl [spritemask_0],y - oral [spritedata_0],y - sta: $0004+{]line*$1000},x + TwoSpritesToCodeField 0 + TwoSpritesToCodeField 1 + TwoSpritesToCodeField 2 + TwoSpritesToCodeField 3 + TwoSpritesToCodeField 4 + TwoSpritesToCodeField 5 + TwoSpritesToCodeField 6 + TwoSpritesToCodeField 7 - ldy #{]line*SPRITE_PLANE_SPAN}+2 - lda blttmp+{]line*4}+2 - andl [spritemask_1],y - oral [spritedata_1],y - andl [spritemask_0],y - oral [spritedata_0],y - sta: $0001+{]line*$1000},x -]line equ ]line+1 - --^ rts ThreeSpritesFast @@ -125,27 +119,13 @@ FourSpritesFast tay plb ; set the code field bank -]line equ 0 - lup 8 - ldy #{]line*SPRITE_PLANE_SPAN} - lda blttmp+{]line*4} - andl [spritemask_2],y - oral [spritedata_2],y - andl [spritemask_1],y - oral [spritedata_1],y - andl [spritemask_0],y - oral [spritedata_0],y - sta: $0004+{]line*$1000},x + ThreeSpritesToCodeField 0 + ThreeSpritesToCodeField 1 + ThreeSpritesToCodeField 2 + ThreeSpritesToCodeField 3 + ThreeSpritesToCodeField 4 + ThreeSpritesToCodeField 5 + ThreeSpritesToCodeField 6 + ThreeSpritesToCodeField 7 - ldy #{]line*SPRITE_PLANE_SPAN}+2 - lda blttmp+{]line*4}+2 - andl [spritemask_2],y - oral [spritedata_2],y - andl [spritemask_1],y - oral [spritedata_1],y - andl [spritemask_0],y - oral [spritedata_0],y - sta: $0001+{]line*$1000},x -]line equ ]line+1 - --^ rts \ No newline at end of file From 755ac3fbfd586cb8b092cc4eab46f8239dae25c8 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 18 May 2022 21:00:06 -0500 Subject: [PATCH 16/82] Move most data storage to separate bank; fix many banking bugs --- demos/tool/App.Main.s | 37 +++- demos/tool/App.s | 5 + macros/CORE.MACS.S | 54 +++++ src/CoreImpl.s | 14 +- src/Defs.s | 68 ++++--- src/GTE.s | 18 -- src/Graphics.s | 34 +--- src/Memory.s | 26 +-- src/Render.s | 25 +-- src/Script.s | 15 +- src/Sprite.s | 139 ++----------- src/Tiles.s | 5 +- src/Timer.s | 55 +----- src/Tool.s | 21 +- src/blitter/BG0.s | 16 -- src/blitter/BG1.s | 25 +-- src/blitter/Blitter.s | 6 +- src/blitter/Horz.s | 7 +- src/blitter/PEISlammer.s | 12 +- src/blitter/Rotation.s | 25 +-- src/blitter/SCB.s | 10 +- src/blitter/Tables.s | 16 -- src/blitter/TemplateUtils.s | 8 +- src/blitter/Tiles.s | 16 -- src/blitter/Tiles00000.s | 16 ++ src/blitter/Vert.s | 2 +- src/static/TileStore.s | 380 +++++++++++++++++++++++++++++++++++- src/static/TileStoreDefs.s | 116 +++++++++++ src/tiles/DirtyTileQueue.s | 112 ++++++++++- src/tiles/FastRenderer.s | 28 +-- 30 files changed, 850 insertions(+), 461 deletions(-) create mode 100644 src/static/TileStoreDefs.s diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index db6741b..903dba7 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -11,6 +11,8 @@ mx %00 +TSZelda EXT ; tileset buffer + ScreenX equ 0 ScreenY equ 2 @@ -29,12 +31,41 @@ ScreenY equ 2 pea #160 _GTESetScreenMode -; Load a tileset in from an uncompressed $C1 picture. The top-left 256x128 rectangle is used -; to populate the 512 tiles. +; Load a tileset + pea #^TSZelda + pea #TSZelda + _GTELoadTileSet ; Manually fill in the 41x26 tiles of the TileStore with a test pattern. + ldx #0 + ldy #0 + +:loop + phx + phy + + phx + phy + pei 0 + _GTESetTile + + lda 0 + inc + and #$001F + sta 0 + + ply + plx + inx + cpx #41 + bcc :loop + + ldx #0 + iny + cpy #26 + bcc :loop ; Set the origin of the screen stz ScreenX @@ -53,7 +84,7 @@ ScreenY equ 2 pei ScreenY _GTESetBG0Origin -; _GTERender + _GTERender inc ScreenX ; Just keep incrementing, it's OK bra :loop diff --git a/demos/tool/App.s b/demos/tool/App.s index 3bd4b35..097e4d1 100644 --- a/demos/tool/App.s +++ b/demos/tool/App.s @@ -8,3 +8,8 @@ ASM App.Main.s SNA Main + +; Segment #2 -- Tileset + + ASM Zelda.TileSet.s + SNA TSET diff --git a/macros/CORE.MACS.S b/macros/CORE.MACS.S index bb29051..29fe055 100644 --- a/macros/CORE.MACS.S +++ b/macros/CORE.MACS.S @@ -325,3 +325,57 @@ transparent sta: ]3+1,y next eom + +; Large code blocks that can be used in sprite blitters +; ]1: line number +OneSpriteToCodeField mac + lda blttmp+{]1*4} + andl spritemask+{]1*SPRITE_PLANE_SPAN},x + oral spritedata+{]1*SPRITE_PLANE_SPAN},x + sta: $0004+{]1*$1000},y + + lda blttmp+{]1*4}+2 + andl spritemask+{]1*SPRITE_PLANE_SPAN}+2,x + oral spritedata+{]1*SPRITE_PLANE_SPAN}+2,x + sta: $0001+{]1*$1000},y + eom + +TwoSpritesToCodeField mac + ldy #{]1*SPRITE_PLANE_SPAN} + lda blttmp+{]1*4} + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0004+{]1*$1000},x + + ldy #{]1*SPRITE_PLANE_SPAN}+2 + lda blttmp+{]1*4}+2 + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0001+{]1*$1000},x + eom + +ThreeSpritesToCodeField mac + ldy #{]1*SPRITE_PLANE_SPAN} + lda blttmp+{]1*4} + andl [spritemask_2],y + oral [spritedata_2],y + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0004+{]1*$1000},x + + ldy #{]1*SPRITE_PLANE_SPAN}+2 + lda blttmp+{]1*4}+2 + andl [spritemask_2],y + oral [spritedata_2],y + andl [spritemask_1],y + oral [spritedata_1],y + andl [spritemask_0],y + oral [spritedata_0],y + sta: $0001+{]1*$1000},x + eom diff --git a/src/CoreImpl.s b/src/CoreImpl.s index 46ce903..04931a4 100644 --- a/src/CoreImpl.s +++ b/src/CoreImpl.s @@ -150,8 +150,8 @@ IntShutDown OneSecHandler mx %11 phb pha - phk - plb + + jsr _SetDataBank rep #$20 inc OneSecondCounter @@ -167,10 +167,7 @@ OneSecHandler mx %11 rtl mx %00 -OneSecondCounter ENT - dw 0 -OldOneSecVec ds 4 - +; This is OK, it's referenced by a long address VBLTASK hex 00000000 dw 0 hex 5AA5 @@ -270,11 +267,6 @@ ClearKbdStrobe sep #$20 rts ; Read the keyboard and paddle controls and return in a game-controller-like format -LastKey db 0 -ReadControl ENT - jsr _ReadControl - rtl - _ReadControl pea $0000 ; low byte = key code, high byte = %------AB diff --git a/src/Defs.s b/src/Defs.s index 468528b..6c5070c 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -92,7 +92,10 @@ TileStoreBankAndTileDataBank equ 108 TileStoreBankDoubled equ 110 UserId equ 112 ; Memory manager user Id to use ToolNum equ 114 ; Tool number assigned to us -Next equ 116 +LastKey equ 116 +LastTick equ 118 + +Next equ 120 activeSpriteList equ 128 ; 32 bytes for the active sprite list (can persist across frames) ; tiletmp equ 178 ; 16 bytes of temp storage for the tile renderers @@ -141,8 +144,9 @@ _OP_CACHE equ 156 ; Cache of a relevant operand / oeprato _TILE_ID equ 158 ; Copy of the tile descriptor ; Define free space the the application to use -FREE_SPACE_DP2 equ 160 - +; FREE_SPACE_DP2 equ 160 +DP2_DIRTY_TILE_COUNT equ 160 ; Local copy of dirty tile count to avoid banking +DP2_DIRTY_TILE_CALLBACK equ 162 ; End direct page values ; EngineMode definitions @@ -197,6 +201,7 @@ SPRITE_HFLIP equ $0200 ; Stamp storage parameters VBUFF_STRIDE_BYTES equ 12*4 ; Each line has 4 slots of 16 pixels + 8 buffer pixels VBUFF_TILE_ROW_BYTES equ 8*VBUFF_STRIDE_BYTES ; Each row is comprised of 8 lines +VBUFF_TILE_COL_BYTES equ 4 VBUFF_SPRITE_STEP equ VBUFF_TILE_ROW_BYTES*3 ; Allocate space fo 16 rows + 8 rows of buffer VBUFF_SPRITE_START equ {8*VBUFF_TILE_ROW_BYTES}+4 ; Start at an offset so $0000 can be used as an empty value VBUFF_SLOT_COUNT equ 48 ; Have space for this many stamps @@ -204,31 +209,32 @@ VBUFF_SLOT_COUNT equ 48 ; Have space for this m ; This is 13 blocks wide SPRITE_PLANE_SPAN equ VBUFF_STRIDE_BYTES -; Tile storage parameters -TILE_DATA_SPAN equ 4 -TILE_STORE_WIDTH equ 41 -TILE_STORE_HEIGHT equ 26 -MAX_TILES equ {26*41} ; Number of tiles in the code field (41 columns * 26 rows) -TILE_STORE_SIZE equ {MAX_TILES*2} ; The tile store contains a tile descriptor in each slot - -TS_TILE_ID equ TILE_STORE_SIZE*0 ; tile descriptor for this location -TS_DIRTY equ TILE_STORE_SIZE*1 ; Flag. Used to prevent a tile from being queued multiple times per frame -TS_SPRITE_FLAG equ TILE_STORE_SIZE*2 ; Bitfield of all sprites that intersect this tile. 0 if no sprites. -TS_TILE_ADDR equ TILE_STORE_SIZE*3 ; cached value, the address of the tiledata for this tile -TS_CODE_ADDR_LOW equ TILE_STORE_SIZE*4 ; const value, address of this tile in the code fields -TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5 -TS_WORD_OFFSET equ TILE_STORE_SIZE*6 ; const value, word offset value for this tile if LDA (dp),y instructions re used -TS_BASE_ADDR equ TILE_STORE_SIZE*7 ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000. -TS_SCREEN_ADDR equ TILE_STORE_SIZE*8 ; cached value of on-screen location of tile. Used for DirtyRender. -;TS_VBUFF_ARRAY_ADDR equ TILE_STORE_SIZE*9 ; const value to an aligned 32-byte array starting at $8000 in TileStore bank - -TS_BASE_TILE_COPY equ TILE_STORE_SIZE*9 ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering -TS_BASE_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function -TS_DIRTY_TILE_DISP equ TILE_STORE_SIZE*11 ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function - -; Hold values for up to 4 sprites per tile -TS_VBUFF_ADDR_0 equ TILE_STORE_SIZE*12 -TS_VBUFF_ADDR_1 equ TILE_STORE_SIZE*13 -TS_VBUFF_ADDR_2 equ TILE_STORE_SIZE*14 -TS_VBUFF_ADDR_3 equ TILE_STORE_SIZE*15 -TS_VBUFF_ADDR_COUNT equ TILE_STORE_SIZE*16 ; replace usage of TS_VBUFF_ARRAY_ADDR with this later +; External references to data bank +TileStore EXT +DirtyTileCount EXT +DirtyTiles EXT +_Sprites EXT +TileStore EXT +TileStoreLookupYTable EXT +TileStoreLookup EXT +Col2CodeOffset EXT +JTableOffset EXT +CodeFieldEvenBRA EXT +CodeFieldOddBRA EXT +ScreenAddr EXT +TileStoreYTable EXT +NextCol EXT +RTable EXT +BlitBuff EXT +BTableHigh EXT +BTableLow EXT +BRowTableHigh EXT +BRowTableLow EXT +BG1YTable EXT +BG1YOffsetTable EXT +OldOneSecVec EXT +OneSecondCounter EXT +Timers EXT +DefaultPalette EXT +ScreenModeWidth EXT +ScreenModeHeight EXT diff --git a/src/GTE.s b/src/GTE.s index 803c929..dd30f36 100644 --- a/src/GTE.s +++ b/src/GTE.s @@ -73,21 +73,3 @@ AllocBank EXT ScreenAddr EXT OneSecondCounter EXT BlitBuff EXT - -;; Helper function to load the GTE User Toolset -;GTEInstall -; php -; ~InitialLoad userId;localToolPath;#0 - -; pea $8000 ; User tool -; pea $00A5 ; Tool 165 -; PushLong toolPtr -; _SetTSPtr - -; plp -; rtl - -; Look for the tool set in the System Tools folder and then next to the application -;sysToolPath strl '*:System:Tools:ToolGTE' -;localToolPath strl '9:ToolGTE' -;toolPtr adrl 0 \ No newline at end of file diff --git a/src/Graphics.s b/src/Graphics.s index 8e9f175..25166f2 100644 --- a/src/Graphics.s +++ b/src/Graphics.s @@ -24,11 +24,6 @@ InitGraphics :no_bg1 rts -DefaultPalette dw $0000,$007F,$0090,$0FF0 - dw $000F,$0080,$0f70,$0FFF - dw $0fa9,$0ff0,$00e0,$04DF - dw $0d00,$078f,$0ccc,$0FFF - ; Allow the user to dynamically select one of the pre-configured screen sizes, or pass ; in a specific width and height. The screen is automatically centered. If this is ; not desired, then SetScreenRect should be used directly @@ -47,18 +42,6 @@ DefaultPalette dw $0000,$007F,$0090,$0FF0 ; ; X = mode number OR width in pixels (must be multiple of 2) ; Y = height in pixels (if X > 8) - -ScreenModeWidth dw 320,272,256,256,280,256,240,288,160,288,160,320 -ScreenModeHeight dw 200,192,200,176,160,160,160,128,144,192,102,1 - -SetScreenMode ENT - phb - phk - plb - jsr _SetScreenMode - plb - rtl - _SetScreenMode cpx #11 bcs :direct ; if x > 10, then assume X and Y are the dimensions @@ -113,10 +96,6 @@ _GetBorderColor lda #0000 rts ; Set the border color to the accumulator value. -SetBorderColor ENT - jsr _SetBorderColor - rtl - _SetBorderColor sep #$20 ; ACC = $X_Y, REG = $W_Z eorl BORDER_REG ; ACC = $(X^Y)_(Y^Z) and #$0F ; ACC = $0_(Y^Z) @@ -135,17 +114,6 @@ _ClearToColor rts ; Set a palette values -; A = high word of palette data pointer, X = low word of palette data pointer, Y = palette number -SetPalette ENT - phb ; save old data bank - pha ; push 16-bit value - plb ; pop 8-bit bank register - tya - jsr _SetPalette - plb ; pop the other half of the 16-bit push off - plb ; restore the original data bank - rtl - ; A = palette number, X = palette address _SetPalette and #$000F ; palette values are 0 - 15 and each palette is 32 bytes @@ -307,7 +275,7 @@ SetScreenRect sty ScreenHeight ; Save the screen height and ldx #0 ldy #0 :tsloop - stal TileStore+TS_SCREEN_ADDR,x + sta TileStore+TS_SCREEN_ADDR,x clc adc #4 ; Go to the next tile diff --git a/src/Memory.s b/src/Memory.s index 8572897..441e2ae 100644 --- a/src/Memory.s +++ b/src/Memory.s @@ -28,9 +28,9 @@ InitMemory lda EngineMode _NewHandle ; returns LONG Handle on stack plx ; base address of the new handle pla ; high address 00XX of the new handle (bank) - _Deref - stx Buff00 - sta Buff00+2 +; _Deref +; stx Buff00 +; sta Buff00+2 :no_bnk0_buff PushLong #0 ; space for result @@ -41,9 +41,9 @@ InitMemory lda EngineMode _NewHandle ; returns LONG Handle on stack plx ; base address of the new handle pla ; high address 00XX of the new handle (bank) - _Deref - stx Buff01 - sta Buff01+2 +; _Deref +; stx Buff01 +; sta Buff01+2 PushLong #0 ; space for result @@ -153,8 +153,8 @@ InitMemory lda EngineMode :exit rts -Buff00 ds 4 -Buff01 ds 4 +;Buff00 ds 4 +;Buff01 ds 4 ; Bank allocator (for one full, fixed bank of memory. Can be immediately deferenced) @@ -172,14 +172,6 @@ AllocOneBank PushLong #0 rts ; Variation that returns the pointer in the X/A registers (X = low, A = high) -AllocBank ENT - phb - phk - plb - jsr AllocOneBank2 - plb - rtl - AllocOneBank2 PushLong #0 PushLong #$10000 PushWord UserId @@ -190,5 +182,3 @@ AllocOneBank2 PushLong #0 pla ; high address 00XX of the new handle (bank) _Deref rts - - diff --git a/src/Render.s b/src/Render.s index cba02a2..cb95762 100644 --- a/src/Render.s +++ b/src/Render.s @@ -21,12 +21,12 @@ ; used in all of the other loops _Render jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen - jsr _ApplyBG1YPos +; jsr _ApplyBG1YPos ; _ApplyBG0Xpos need to be split because we have to set the offsets, then draw in any updated tiles, and ; finally patch out the code field. Right now, the BRA operand is getting overwritten by tile data. jsr _ApplyBG0XPosPre - jsr _ApplyBG1XPosPre +; jsr _ApplyBG1XPosPre ; jsr _RenderSprites ; Once the BG0 X and Y positions are committed, update sprite data @@ -36,7 +36,7 @@ _Render jsr _ApplyTilesFast ; This function actually draws the new tiles into the code field jsr _ApplyBG0XPos ; Patch the code field instructions with exit BRA opcode - jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position +; jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position ; The code fields are locked in now and ready to be rendered @@ -99,26 +99,13 @@ _ApplyTilesFast adc #$100 ; move to the next page tcd - ldy DirtyTileCount - beq :out - -:loop -; Retrieve the offset of the next dirty Tile Store items in the X-register + lda DirtyTileCount ; Cache the dirty tile count + sta DP2_DIRTY_TILE_COUNT jsr _PopDirtyTile2 -; Call the generic dispatch with the Tile Store record pointer at by the X-register. + stz DirtyTileCount - phb - jsr _RenderTileFast - plb - -; Loop again until the list of dirty tiles is empty - - ldy DirtyTileCount - bne :loop - -:out tdc ; Move back to the original direct page sec sbc #$100 diff --git a/src/Script.s b/src/Script.s index bbeb01d..2771c8b 100644 --- a/src/Script.s +++ b/src/Script.s @@ -35,8 +35,7 @@ ; timer's user data section. StartScript ENT phb - phk - plb + jsr _SetDataBank phx ; Save the script array address pha @@ -69,14 +68,6 @@ ARG1 equ 2 ARG2 equ 4 ARG3 equ 6 -DoScriptSeq ENT - phb - phk - plb - jsl _DoScriptSeq ; Yes, this is a special JSL, because _DoScriptSeq is a time callback - plb - rtl - _DoScriptSeq phx ; save the timer index; will need to update user data at the end phb ; save the current data bank @@ -180,9 +171,9 @@ _SetDTile _UserCallback lda: ARG1,y - sta :dispatch+1 + stal :dispatch+1 lda: ARG1+1,y - sta :dispatch+2 + stal :dispatch+2 lda: ARG3,y :dispatch jsl $000000 brl _dss_cmd_rtn diff --git a/src/Sprite.s b/src/Sprite.s index 7419d26..506ed77 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -27,24 +27,6 @@ InitSprites ; dex ; bpl :loop3 -; Initialize the VBUFF address offsets in the data and mask banks for each sprite -; -; The internal grid 12 tiles wide where each sprite has a 2x2 interior square with a -; tile-size buffer all around. We pre-render each sprite with all four vert/horz flips -; -; Eventually we should be able to have a separate rendering path for vertically flipped -; sprites and will be able to double the capacity of the stamp buffer - - ldx #0 - lda #VBUFF_SPRITE_START - clc -:loop4 sta VBuffAddrTable,x - adc #VBUFF_SPRITE_STEP - inx - inx - cpx #VBUFF_SLOT_COUNT*2 - bcc :loop4 - ; Precalculate some bank values jsr _CacheSpriteBanks rts @@ -68,38 +50,25 @@ InitSprites ; the stamp every time. So this allows users to create stamps in advance and then ; assign them to the sprites as needed. ; -; Currently, we support a maximum of 48 stamps. +; Note that the user had full freedom to create a stamp at any VBUFF address, however, +; without leaving a buffer around each stamp, graphical corruption will occur. It is +; recommended that the defines for VBUFF_SPRITE_START, VBUFF_TILE_ROW_BYTES and +; VBUFF_TILE_COL_BYTES to calculate tile-aligned corner locations to lay out the +; sprite stamps in VBUFF memory. ; ; Input: ; A = sprite descriptor -; X = stamp slot -; Return: -; A = vbuff address to be assigned to Sprite[VBUFF_ADDR] -CreateSpriteStamp ENT - phb - phk - plb - jsr _CreateSpriteStamp - plb - rtl - +; Y = vbuff address +; +; The Sprite[VBUFF_ADDR] property must be set to the vbuff address passed into this function +; to bind the sprite stamp to the sprite record. _CreateSpriteStamp pha ; Save the descriptor jsr _GetBaseTileAddr ; Get the address of the tile data - pha - txa - asl - tax - ldy VBuffAddrTable,x ; Load the address of the stamp slot - - plx ; Pop the tile address + tax ; Tile data address pla ; Pop the sprite ID - phy ; VBUFF_ADDR value - jsr _DrawSpriteStamp ; Render the sprite data and create a stamp - - pla ; Pop the VBUFF_ADDR and return - rts + jmp _DrawSpriteStamp ; Render the sprite data and create a stamp ; Add a new sprite to the rendering pipeline ; @@ -123,14 +92,6 @@ _CreateSpriteStamp ; A = tileId + flags ; Y = High Byte = x-pos, Low Byte = y-pos ; X = Sprite Slot (0 - 15) -AddSprite ENT - phb - phk - plb - jsr _AddSprite - plb - rtl - _AddSprite pha txa @@ -888,14 +849,6 @@ _PrecalcAllSpriteInfo ; picked up in the next AddSprite. ; ; A = Sprite ID -RemoveSprite ENT - phb - phk - plb - jsr _RemoveSprite - plb - rtl - _RemoveSprite cmp #MAX_SPRITES bcc :ok @@ -917,14 +870,6 @@ _RemoveSprite ; A = Sprite ID ; X = New Sprite Flags ; Y = New Sprite Stamp Address -UpdateSprite ENT - phb - phk - plb - jsr _UpdateSprite - plb - rtl - _UpdateSprite cmp #MAX_SPRITES bcc :ok @@ -961,14 +906,6 @@ _UpdateSprite ; A = sprite ID ; X = x position ; Y = y position -MoveSprite ENT - phb - phk - plb - jsr _MoveSprite - plb - rtl - _MoveSprite cmp #MAX_SPRITES bcc :ok @@ -999,57 +936,3 @@ _MoveSprite sta _Sprites+SPRITE_STATUS,x jmp _PrecalcAllSpriteInfo ; Can be specialized to only update (x,y) values - -; Sprite data structures. We cache quite a few pieces of information about the sprite -; to make calculations faster, so this is hidden from the caller. -; -; -; Number of "off-screen" lines above logical (0,0) -; NUM_BUFF_LINES equ 24 - -MAX_SPRITES equ 16 -SPRITE_REC_SIZE equ 52 - -; Mark each sprite as ADDED, UPDATED, MOVED, REMOVED depending on the actions applied to it -; on this frame. Quick note, the same Sprite ID cannot be removed and added in the same frame. -; A REMOVED sprite if removed from the sprite list during the Render call, so it's ID is not -; available to the AddSprite function until the next frame. - -SPRITE_STATUS_EMPTY equ $0000 ; If the status value is zero, this sprite slot is available -SPRITE_STATUS_OCCUPIED equ $8000 ; Set the MSB to flag it as occupied -SPRITE_STATUS_ADDED equ $0001 ; Sprite was just added (new sprite) -SPRITE_STATUS_MOVED equ $0002 ; Sprite's position was changed -SPRITE_STATUS_UPDATED equ $0004 ; Sprite's non-position attributes were changed -SPRITE_STATUS_REMOVED equ $0008 ; Sprite has been removed. - -SPRITE_STATUS equ {MAX_SPRITES*0} -; TILE_DATA_OFFSET equ {MAX_SPRITES*2} -VBUFF_ADDR equ {MAX_SPRITES*4} ; Base address of the sprite's stamp in the data/mask banks -SPRITE_ID equ {MAX_SPRITES*6} -SPRITE_X equ {MAX_SPRITES*8} -SPRITE_Y equ {MAX_SPRITES*10} -; TILE_STORE_ADDR_1 equ {MAX_SPRITES*12} -TS_LOOKUP_INDEX equ {MAX_SPRITES*12} ; The index into the TileStoreLookup table corresponding to the top-left corner of the sprite -; TILE_STORE_ADDR_2 equ {MAX_SPRITES*14} -TS_COVERAGE_SIZE equ {MAX_SPRITES*14} ; Index into the lookup table of how many TileStore tiles are covered by this sprite -;TILE_STORE_ADDR_3 equ {MAX_SPRITES*16} -TS_VBUFF_BASE_ADDR equ {MAX_SPRITES*16} ; Fixed address of the TS_VBUFF_X memory locations -;TILE_STORE_ADDR_4 equ {MAX_SPRITES*18} -;TILE_STORE_ADDR_5 equ {MAX_SPRITES*20} -;TILE_STORE_ADDR_6 equ {MAX_SPRITES*22} -;TILE_STORE_ADDR_7 equ {MAX_SPRITES*24} -;TILE_STORE_ADDR_8 equ {MAX_SPRITES*26} -;TILE_STORE_ADDR_9 equ {MAX_SPRITES*28} -;TILE_STORE_ADDR_10 equ {MAX_SPRITES*30} -SPRITE_DISP equ {MAX_SPRITES*32} ; cached address of the specific stamp based on flags -SPRITE_CLIP_LEFT equ {MAX_SPRITES*34} -SPRITE_CLIP_RIGHT equ {MAX_SPRITES*36} -SPRITE_CLIP_TOP equ {MAX_SPRITES*38} -SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*40} -IS_OFF_SCREEN equ {MAX_SPRITES*42} -SPRITE_WIDTH equ {MAX_SPRITES*44} -SPRITE_HEIGHT equ {MAX_SPRITES*46} -SPRITE_CLIP_WIDTH equ {MAX_SPRITES*48} -SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*50} - -_Sprites ds SPRITE_REC_SIZE*MAX_SPRITES diff --git a/src/Tiles.s b/src/Tiles.s index 9fe7896..5758bb2 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -134,8 +134,11 @@ InitTiles lda :row ; Set the long address of where this tile asl ; exists in the code fields tay - lda BRowTableHigh,y + lda #>TileStore ; get middle 16 bits: "00 -->BBHH<-- LL" + and #$FF00 ; merge with code field bank + ora BRowTableHigh,y stal TileStore+TS_CODE_ADDR_HIGH,x ; High word of the tile address (just the bank) + lda BRowTableLow,y stal TileStore+TS_BASE_ADDR,x ; May not be needed later if we can figure out the right constant... diff --git a/src/Timer.s b/src/Timer.s index fbb2b53..a1ca960 100644 --- a/src/Timer.s +++ b/src/Timer.s @@ -1,37 +1,5 @@ -; Timer implementation -; -; The engire provides four timer slot that can be used by one-shot or -; recurring timers. Each timer is given an initial tick count, a -; reset tick count (0 = one-shot), and an action to perform. -; -; The timers handle overflow, so if a recurring timer has a tick count of 3 -; and 7 VBL ticks have passed, then the timer will be fired twice and -; a tick count of 2 will be set. -; -; As such, the timers are appropriate to drive physical and other game -; behaviors at a frame-independent rate. -; -; A collection of 4 timers that are triggered when their countdown -; goes below zero. Each timer takes up 16 bytes -; -; A timer can fire multiple times during a singular evaluation. For example, if the -; timer delay is set to 1 and 3 VBL ticks happen, then the timer delta is -2, will fire, -; have the delay added and get -1, fire again, increment to zero, first again and then -; finally reset to 1. -; -; +0 counter decremented by the number of ticks since last run -; +2 reset copied into counter when triggered. 0 turns off the timer. -; +4 addr long address of timer routine -; +8 user 8 bytes of user data space for timer state -MAX_TIMERS equ 4 -TIMER_REC_SIZE equ 16 + mx %00 -lastTick ds 2 -Timers ds TIMER_REC_SIZE*MAX_TIMERS - -GetVBLTicks ENT - jsr _GetVBLTicks - rtl _GetVBLTicks PushLong #0 _GetTick @@ -42,7 +10,7 @@ _GetVBLTicks ; Initialize the timers InitTimers jsr _GetVBLTicks - sta lastTick + sta LastTick lda #0 ldx #{TIMER_REC_SIZE*MAX_TIMERS}-2 @@ -71,8 +39,7 @@ AddTimer ENT pha phy - phk - plb + jsr _SetDataBank ldx #0 :loop lda Timers,x ; If the counter is zero, timer is free @@ -125,8 +92,7 @@ AddTimer ENT ; A = Timer ID RemoveTimer ENT phb - phk - plb + jsr _SetDataBank cmp #{TIMER_REC_SIZE*{MAX_TIMERS-1}}+1 bcs :exit @@ -143,17 +109,16 @@ RemoveTimer ENT ; Execute the timer functions DoTimers ENT phb - phk - plb + jsr _SetDataBank jsr _GetVBLTicks - cmp lastTick ; Throttle to 60 fps + cmp LastTick ; Throttle to 60 fps beq :exit tax ; Calculate the increment sec - sbc lastTick - stx lastTick + sbc LastTick + stx LastTick ; We don't want times to fire excessively. If the timer has nt been evaluated for over ; one second, then just skip processing and wait for the next call. @@ -186,9 +151,9 @@ _DoTimers phx ; Save our index lda Timers+4,x ; execute the timer callback - sta :dispatch+1 + stal :dispatch+1 lda Timers+5,x - sta :dispatch+2 + stal :dispatch+2 :dispatch jsl $000000 plx diff --git a/src/Tool.s b/src/Tool.s index 1c3204f..e08e555 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -10,6 +10,7 @@ use Core.MACS.s use Defs.s + use static/TileStoreDefs.s ToStrip equ $E10184 @@ -18,8 +19,7 @@ _TSEntry mac phd phb tcd - phk ; Default to setting the data back to the current bank. - plb + jsr _SetDataBank <<< _TSExit mac @@ -53,6 +53,14 @@ _CallTable adrl _TSLoadTileSet-1 _CTEnd +; Helper function to set the data back to the toolset default +_SetDataBank sep #$20 + lda #^TileStore + pha + plb + rep #$20 + rts + ; Do nothing when the tool set is installed _TSBootInit lda #0 @@ -86,8 +94,7 @@ zpToUse = userId+4 sta EngineMode phb - phk - plb + jsr _SetDataBank jsr _CoreStartUp ; Initialize the library plb @@ -114,8 +121,7 @@ _TSShutDown tcd ; Set the direct page for the toolset phb - phk - plb + jsr _SetDataBank jsr _CoreShutDown ; Shut down the library plb @@ -201,7 +207,7 @@ xTile equ FirstParam+4 tax lda yTile,s ; Valid range [0, 25] (26 rows) tay - lda tileId + lda tileId,s jsr _SetTile _TSExit #0;#6 @@ -255,7 +261,6 @@ TSPtr equ FirstParam put blitter/BG1.s put blitter/Template.s put blitter/TemplateUtils.s - put blitter/Tables.s put blitter/Blitter.s put blitter/TileProcs.s put blitter/Tiles00000.s diff --git a/src/blitter/BG0.s b/src/blitter/BG0.s index 081f7d8..32519af 100644 --- a/src/blitter/BG0.s +++ b/src/blitter/BG0.s @@ -8,14 +8,6 @@ _InitBG0 ; ; A=low word of picture address ; X=high word of pixture address -CopyBinToField ENT - phb - phk - plb - jsr _CopyBinToField - plb - rtl - _CopyBinToField :srcptr equ tmp0 :line_cnt equ tmp2 @@ -222,14 +214,6 @@ _CopyBinToField ; X=high workd of pixture address ; ; Picture must be within one bank -CopyPicToField ENT - phb - phk - plb - jsr _CopyPicToField - plb - rtl - _CopyPicToField :srcptr equ tmp0 :line_cnt equ tmp2 diff --git a/src/blitter/BG1.s b/src/blitter/BG1.s index d6a078f..71639fa 100644 --- a/src/blitter/BG1.s +++ b/src/blitter/BG1.s @@ -9,14 +9,6 @@ _InitBG1 ; A=low word of picture address ; X=high word of pixture address ; Y=high word of BG1 bank -CopyBinToBG1 ENT - phb - phk - plb - jsr _CopyBinToBG1 - plb - rtl - _CopyBinToBG1 :src_width equ tmp6 :src_height equ tmp7 @@ -39,14 +31,6 @@ _CopyBinToBG1 ; A=low word of picture address ; X=high word of pixture address ; Y=high word of BG1 bank -CopyPicToBG1 ENT - phb - phk - plb - jsr _CopyPicToBG1 - plb - rtl - _CopyPicToBG1 :src_width equ tmp6 :src_height equ tmp7 @@ -221,6 +205,8 @@ _ApplyBG1YPos :draw_count equ tmp2 :ytbl_idx equ tmp3 + phb ; Save the bank + lda BG1StartY jsr Mod208 sta BG1StartYMod208 @@ -277,7 +263,6 @@ _ApplyBG1YPos jne :loop - phk plb rts @@ -369,16 +354,14 @@ CopyBG1YTableToBG1Addr2 phx phb - phk ; restore access to this bank - plb + jsr _SetDataBank ; restore access to this bank ldy BG1OffsetIndex ; Get the offset and save the values jsr SaveBG1OffsetValues plb plx ; x is used directly in this routine ply - jsr ApplyBG1OffsetValues - rts + jmp ApplyBG1OffsetValues SaveBG1OffsetValues jmp (:tbl,x) diff --git a/src/blitter/Blitter.s b/src/blitter/Blitter.s index 7afe2bf..d490e3c 100644 --- a/src/blitter/Blitter.s +++ b/src/blitter/Blitter.s @@ -33,10 +33,10 @@ _BltRange sep #$20 ; 8-bit Acc lda BTableHigh,x ; patch in the bank - sta blt_entry+3 + stal blt_entry+3 lda BTableLow+1,x ; patch in the page - sta blt_entry+2 + stal blt_entry+2 ; The way we patch the exit code is subtle, but very fast. The CODE_EXIT offset points to ; an JMP/JML instruction that transitions to the next line after all of the code has been @@ -74,6 +74,7 @@ _BltRange tsc ; save the stack pointer stal stk_save+1 + bra blt_return blt_entry jml $000000 ; Jump into the blitter code $XX/YY00 blt_return _R0W0 @@ -88,5 +89,6 @@ stk_save lda #0000 ; load the stack sta [:exit_ptr],y rep #$20 +blt_out plb ; restore the bank rts diff --git a/src/blitter/Horz.s b/src/blitter/Horz.s index 9eaf5d8..dbe428b 100644 --- a/src/blitter/Horz.s +++ b/src/blitter/Horz.s @@ -21,6 +21,8 @@ _RestoreBG0Opcodes :draw_count_x2 equ tmp3 :exit_offset equ tmp4 + phb ; Save data bank + asl sta :virt_line_x2 ; Keep track of it @@ -75,7 +77,6 @@ _RestoreBG0Opcodes stz LastPatchOffset ; Clear the value once completed :out - phk plb rts @@ -279,6 +280,7 @@ _ApplyBG0XPos ; 2. Writes the BRA instruction to exit the code field ; 3. Writes the JMP entry point to enter the code field + phb ; Save the existing bank :loop lda :virt_line asl ; This will clear the carry bit @@ -353,7 +355,7 @@ _ApplyBG0XPos ldx :draw_count_x2 ldy :base_address ; Y-register is preserved, this can be removed pei :exit_address - jmp :SaveHighOperand ; Only used once, so "inline" it + jmp :SaveHighOperand ; Only used once, so "inline" it :save_high_op_rtn :not_odd @@ -374,7 +376,6 @@ _ApplyBG0XPos jne :loop - phk plb rts diff --git a/src/blitter/PEISlammer.s b/src/blitter/PEISlammer.s index 7eedffd..888335d 100644 --- a/src/blitter/PEISlammer.s +++ b/src/blitter/PEISlammer.s @@ -15,13 +15,13 @@ _PEISlam lda ScreenWidth dec - sta :screen_width_1 ; save the width-1 outside of the direct page + stal :screen_width_1 ; save the width-1 outside of the direct page lda #:pei_end ; patch the PEI entry address and #$FFFE ; should always be even, but.... sec sbc ScreenWidth - sta :inner+1 + stal :inner+1 phx tya @@ -43,7 +43,7 @@ _PEISlam tcd ; screen address to the direct page register tsc - sta :stk_save ; save the stack pointer to restore later + stal :stk_save ; save the stack pointer to restore later clc ; clear before the loop -- nothing in the loop affect the carry bit brl :outer ; hop into the entry point. @@ -57,7 +57,7 @@ _PEISlam tdc ; Move to the next line adc #160 tcd - adc :screen_width_1 + adcl :screen_width_1 tcs dey ; decrement the total counter, if zero then we're done @@ -79,7 +79,7 @@ _PEISlam :restore tsx ; save the current stack _R0W0 ; restore the execution environment and - lda :stk_save ; give a few cycles to catch some interrupts + ldal :stk_save ; give a few cycles to catch some interrupts tcs cli ; fall through here -- saves a BRA instruction @@ -92,7 +92,7 @@ _PEISlam :exit _R0W0 - lda :stk_save + ldal :stk_save tcs cli diff --git a/src/blitter/Rotation.s b/src/blitter/Rotation.s index e91cc9a..fed6a55 100644 --- a/src/blitter/Rotation.s +++ b/src/blitter/Rotation.s @@ -10,14 +10,6 @@ ; of the BG1 data buffer provides the "fill value". ANGLEBNK ext -ApplyBG1XPosAngle ENT - phb - phk - plb - jsr _ApplyBG1XPosAngle - plb - rtl - _ApplyBG1XPosAngle ; phy @@ -62,14 +54,6 @@ _ApplyBG1XPosAngle pld rts -ApplyBG1YPosAngle ENT - phb - phk - plb - jsr _ApplyBG1YPosAngle - plb - rtl - _ApplyBG1YPosAngle :virt_line equ tmp0 :lines_left equ tmp1 @@ -90,6 +74,7 @@ _ApplyBG1YPosAngle lda ScreenHeight sta :lines_left + phb :loop lda :virt_line asl @@ -141,7 +126,6 @@ _ApplyBG1YPosAngle jne :loop - phk plb rts @@ -155,14 +139,13 @@ CopyAngleYTableToBG1Addr phx phb - phk ; restore access to this bank - plb + + jsr _SetDataBank ; restore access to this bank jsr SaveBG1AngleValues plb plx ; x is used directly in this routine - jsr ApplyBG1OffsetValues - rts + jmp ApplyBG1OffsetValues SaveBG1AngleValues jmp (:tbl,x) diff --git a/src/blitter/SCB.s b/src/blitter/SCB.s index f60f3c9..f43e02c 100644 --- a/src/blitter/SCB.s +++ b/src/blitter/SCB.s @@ -9,15 +9,7 @@ ; on the SHR screen or the current value of StartY ; ; This could be made faster by forcing a SCB array to be copied into PEAs ahead of time, but this -; is a bit more flexible -BltSCB ENT - phb - phk - plb - jsr _BltSCB - plb - rtl - +; is a bit more flexible _BltSCBOut rts _BltSCB diff --git a/src/blitter/Tables.s b/src/blitter/Tables.s index b01a931..91a28e1 100644 --- a/src/blitter/Tables.s +++ b/src/blitter/Tables.s @@ -253,19 +253,6 @@ NextCol ]step = ]step+2 --^ -; A double-sized table of lookup values. This is basically the cross-product of TileStoreYTable and -; NextCol. If is double-width and double-height so that, if we know a tile's address position -; of (X + 41*Y), then any relative tile store address can be looked up by adding a constant value. -; -; 50 rows by 80 columns + 2 extra rows and columns -TS_LOOKUP_WIDTH equ 80 -TS_LOOKUP_HEIGHT equ 50 -TS_LOOKUP_SPAN equ {TS_LOOKUP_WIDTH+2} -TS_LOOKUP_ROWS equ {TS_LOOKUP_HEIGHT+2} - -TileStoreLookupYTable ds {TS_LOOKUP_HEIGHT*2} -TileStoreLookup ds {TS_LOOKUP_SPAN*TS_LOOKUP_ROWS*2} - ; This is a double-length table that holds the right-edge adresses of the playfield on the physical ; screen. At most, it needs to hold 200 addresses for a full height playfield. It is double-length ; so that code can pick any offset and copy values without needing to check for a wrap-around. If the @@ -305,6 +292,3 @@ BG1YTable lup 208 BG1YOffsetTable lup 26 dw 1,1,1,2,2,2,2,2,1,1,1,0,0,0,0,0 --^ - -; Table of base VBUFF addresses for each sprite stamp slot -VBuffAddrTable ds 2*VBUFF_SLOT_COUNT \ No newline at end of file diff --git a/src/blitter/TemplateUtils.s b/src/blitter/TemplateUtils.s index 121f3af..56b60bf 100644 --- a/src/blitter/TemplateUtils.s +++ b/src/blitter/TemplateUtils.s @@ -49,7 +49,7 @@ Counter equ tmp3 tax ; NOTE: Try to rework to use new TileStore2DLookup array lda OnScreenAddr - stal TileStore+TS_SCREEN_ADDR,x + sta TileStore+TS_SCREEN_ADDR,x clc adc #4 ; Go to the next tile @@ -278,6 +278,10 @@ BuildBank sta :target+2 :BuildLine2 + phb ; save bank and reset to the code bank because + phk ; the template is part of this bank + plb + lda #CODE_LEN ; round up to an even number of bytes inc and #$FFFE @@ -315,6 +319,6 @@ BuildBank cpx #PagePatchNum bcc :dopage -:out rep #$20 + plb rts \ No newline at end of file diff --git a/src/blitter/Tiles.s b/src/blitter/Tiles.s index c10c524..7a6f197 100644 --- a/src/blitter/Tiles.s +++ b/src/blitter/Tiles.s @@ -561,14 +561,6 @@ CopyTileMToDyn ; A = Tile ID (0 - 511) ; X = Tile column (0 - 40) ; Y = Tile row (0 - 25) -CopyBG0Tile ENT - phb - phk - plb - jsr _CopyBG0Tile - plb - rtl - _CopyBG0Tile phb ; save the current bank phx ; save the original x-value @@ -614,14 +606,6 @@ _CopyBG0Tile ; A = Tile ID (0 - 511) ; X = Tile column (0 - 40) ; Y = Tile row (0 - 25) -CopyBG1Tile - phb - phk - plb - jsr _CopyBG1Tile - plb - rtl - _CopyBG1Tile phb ; save the current bank phx ; save the original x-value diff --git a/src/blitter/Tiles00000.s b/src/blitter/Tiles00000.s index 854691d..7fa74bd 100644 --- a/src/blitter/Tiles00000.s +++ b/src/blitter/Tiles00000.s @@ -36,8 +36,24 @@ _TBSolidTile_VH ; ; This does not increase the FPS by 37% because only a small number of tiles are drawn each frame, but it ; has an impact and can significantly help out when sprites trigger more dirty tile updates than normal. + + +; This is called via a JMP (abs,x) with an extra byte on the stack that holds the bank +; register value. This must be restored prior to returning _TBCopyDataFast tax +]line equ 0 + lup 8 + ldal tiledata+{]line*4},x + sta: $0004+{]line*$1000},y + ldal tiledata+{]line*4}+2,x + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + plb + rts + + _TBCopyData ]line equ 0 lup 8 diff --git a/src/blitter/Vert.s b/src/blitter/Vert.s index c7b7bd6..0679cd3 100644 --- a/src/blitter/Vert.s +++ b/src/blitter/Vert.s @@ -45,6 +45,7 @@ _ApplyBG0YPos ; and ~2,500 per secord. This is ~1% of our total CPU budget and is *just* enough cycles to be ; interesting.... Another 8 cycles could be removed by doing all calculatinos pre-multiplied by 2 ; to avoid several 'asl' instructions + phb :loop lda :virt_line asl @@ -91,7 +92,6 @@ _ApplyBG0YPos jne :loop - phk plb rts diff --git a/src/static/TileStore.s b/src/static/TileStore.s index 7182fb3..7f65abb 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -1,4 +1,378 @@ ; Bank of memory that holds the core sprite and tile store data structures -TileStore ENT -; ds 65535 - ds 65536 + + put ../Defs.s + put TileStoreDefs.s + put ../blitter/Template.s + +;------------------------------------------------------------------------------------- +; +; Buffer space + + ds 256 + +;------------------------------------------------------------------------------------- + +TileStore ENT + ds {TILE_STORE_SIZE*17} + +;------------------------------------------------------------------------------------- +; +; A list of dirty tiles that need to be updated in a given frame + + ds \,$00 ; pad to the next page boundary +DirtyTileCount ENT + ds 2 +DirtyTiles ENT + ds TILE_STORE_SIZE ; At most this many tiles can possibly be update at once + +;------------------------------------------------------------------------------------- +; + + ds \,$00 ; pad to the next page boundary +_Sprites ENT + ds SPRITE_REC_SIZE*MAX_SPRITES + +;------------------------------------------------------------------------------------- +; +; A double-sized table of lookup values. It is double-width and double-height so that, +; if we know a tile's address position of (X + 41*Y), then any relative tile store address +; can be looked up by adding a constant value. + ds \,$00 ; pad to the next page boundary +TileStoreLookupYTable ENT + ds {TS_LOOKUP_HEIGHT*2} +TileStoreLookup ENT + ds {TS_LOOKUP_SPAN*TS_LOOKUP_ROWS*2} + +;------------------------------------------------------------------------------------- +; +; Other data tables + +; Col2CodeOffset +; +; Takes a column number (0 - 81) and returns the offset into the blitter code +; template. +; +; This is used for rendering tile data into the code field. For example, is we assume that +; we are filling in the operands for a bunch of PEA values, we could do this +; +; ldy tileColumn*2 +; lda #DATA +; ldx Col2CodeOffset,y +; sta $0001,x +; +; The table values are pre-reversed so that loop can go in logical order 0, 2, 4, ... +; and the resulting offsets will map to the code instructions in right-to-left order. +; +; Remember, because the data is pushed on to the stack, the last instruction, which is +; in the highest memory location, pushed data that apepars on the left edge of the screen. + +]step equ 0 + dw CODE_TOP ; There is a spot where we load Col2CodeOffet-2,x +Col2CodeOffset ENT + lup 82 + dw CODE_TOP+{{81-]step}*PER_TILE_SIZE} +]step equ ]step+1 + --^ + dw CODE_TOP+{81*PER_TILE_SIZE} + +; A parallel table to Col2CodeOffset that holds the offset to the exception handler address for each column +]step equ 0 + dw SNIPPET_BASE +JTableOffset ENT + lup 82 + dw SNIPPET_BASE+{{81-]step}*SNIPPET_SIZE} +]step equ ]step+1 + --^ + dw SNIPPET_BASE+{81*SNIPPET_SIZE} + +; Table of BRA instructions that are used to exit the code field. Separate tables for +; even and odd aligned cases. +; +; The even exit point is closest to the code field. The odd exit point is 3 bytes further +; +; These tables are reversed to be parallel with the JTableOffset and Col2CodeOffset tables above. The +; physical word index that each instruction is intended to be placed at is in the comment. +CodeFieldEvenBRA ENT + bra *+6 ; 81 -- need to skip over the JMP loop that passed control back + bra *+9 ; 80 + bra *+12 ; 79 + bra *+15 ; 78 + bra *+18 ; 77 + bra *+21 ; 76 + bra *+24 ; 75 + bra *+27 ; 74 + bra *+30 ; 73 + bra *+33 ; 72 + bra *+36 ; 71 + bra *+39 ; 70 + bra *+42 ; 69 + bra *+45 ; 68 + bra *+48 ; 67 + bra *+51 ; 66 + bra *+54 ; 65 + bra *+57 ; 64 + bra *+60 ; 63 + bra *+63 ; 62 + bra *+66 ; 61 + bra *+69 ; 60 + bra *+72 ; 59 + bra *+75 ; 58 + bra *+78 ; 57 + bra *+81 ; 56 + bra *+84 ; 55 + bra *+87 ; 54 + bra *+90 ; 53 + bra *+93 ; 52 + bra *+96 ; 51 + bra *+99 ; 50 + bra *+102 ; 49 + bra *+105 ; 48 + bra *+108 ; 47 + bra *+111 ; 46 + bra *+114 ; 45 + bra *+117 ; 44 + bra *+120 ; 43 + bra *+123 ; 42 + bra *+126 ; 41 + bra *-123 ; 40 + bra *-120 ; 39 + bra *-117 ; 38 + bra *-114 ; 37 + bra *-111 ; 36 + bra *-108 ; 35 + bra *-105 ; 34 + bra *-102 ; 33 + bra *-99 ; 32 + bra *-96 ; 31 + bra *-93 ; 30 + bra *-90 ; 29 + bra *-87 ; 28 + bra *-84 ; 27 + bra *-81 ; 26 + bra *-78 ; 25 + bra *-75 ; 24 + bra *-72 ; 23 + bra *-69 ; 22 + bra *-66 ; 21 + bra *-63 ; 20 + bra *-60 ; 19 + bra *-57 ; 18 + bra *-54 ; 17 + bra *-51 ; 16 + bra *-48 ; 15 + bra *-45 ; 14 + bra *-42 ; 13 + bra *-39 ; 12 + bra *-36 ; 11 + bra *-33 ; 10 + bra *-30 ; 9 + bra *-27 ; 8 + bra *-24 ; 7 + bra *-21 ; 6 + bra *-18 ; 5 + bra *-15 ; 4 + bra *-12 ; 3 + bra *-9 ; 2 + bra *-6 ; 1 + bra *-3 ; 0 + +CodeFieldOddBRA ENT + bra *+9 ; 81 -- need to skip over two JMP instructions + bra *+12 ; 80 + bra *+15 ; 79 + bra *+18 ; 78 + bra *+21 ; 77 + bra *+24 ; 76 + bra *+27 ; 75 + bra *+30 ; 74 + bra *+33 ; 73 + bra *+36 ; 72 + bra *+39 ; 71 + bra *+42 ; 70 + bra *+45 ; 69 + bra *+48 ; 68 + bra *+51 ; 67 + bra *+54 ; 66 + bra *+57 ; 65 + bra *+60 ; 64 + bra *+63 ; 64 + bra *+66 ; 62 + bra *+69 ; 61 + bra *+72 ; 60 + bra *+75 ; 59 + bra *+78 ; 58 + bra *+81 ; 57 + bra *+84 ; 56 + bra *+87 ; 55 + bra *+90 ; 54 + bra *+93 ; 53 + bra *+96 ; 52 + bra *+99 ; 51 + bra *+102 ; 50 + bra *+105 ; 49 + bra *+108 ; 48 + bra *+111 ; 47 + bra *+114 ; 46 + bra *+117 ; 45 + bra *+120 ; 44 + bra *+123 ; 43 + bra *+126 ; 42 + bra *+129 ; 41 + bra *-126 ; 40 + bra *-123 ; 39 + bra *-120 ; 38 + bra *-117 ; 37 + bra *-114 ; 36 + bra *-111 ; 35 + bra *-108 ; 34 + bra *-105 ; 33 + bra *-102 ; 32 + bra *-99 ; 31 + bra *-96 ; 30 + bra *-93 ; 29 + bra *-90 ; 28 + bra *-87 ; 27 + bra *-84 ; 26 + bra *-81 ; 25 + bra *-78 ; 24 + bra *-75 ; 23 + bra *-72 ; 22 + bra *-69 ; 21 + bra *-66 ; 20 + bra *-63 ; 19 + bra *-60 ; 18 + bra *-57 ; 17 + bra *-54 ; 16 + bra *-51 ; 15 + bra *-48 ; 14 + bra *-45 ; 13 + bra *-42 ; 12 + bra *-39 ; 11 + bra *-36 ; 10 + bra *-33 ; 9 + bra *-30 ; 8 + bra *-27 ; 7 + bra *-24 ; 6 + bra *-21 ; 5 + bra *-18 ; 4 + bra *-15 ; 3 + bra *-12 ; 2 + bra *-9 ; 1 + bra *-6 ; 0 -- branch back 6 to skip the JMP even path + +]step equ $2000 +ScreenAddr ENT + lup 200 + dw ]step +]step = ]step+160 + --^ + +; Table of offsets into each row of a Tile Store table. We currently have two tables defined; one +; that is the backing store for the tiles rendered into the code field, and another that holds +; backlink information on the sprite entries that overlap various tiles. +; +; This table is double-length to support accessing off the end modulo its legth +TileStoreYTable ENT +]step equ 0 + lup 26 + dw ]step +]step = ]step+{41*2} + --^ +]step equ 0 + lup 26 + dw ]step +]step = ]step+{41*2} + --^ + +; Create a table to look up the "next" column with modulo wraparound. Basically a[i] = i +; and the table is double-length. Use constant offsets to pick an amount to advance +NextCol ENT +]step equ 0 + lup 41 + dw ]step +]step = ]step+2 + --^ +]step equ 0 + lup 41 + dw ]step +]step = ]step+2 + --^ + +; This is a double-length table that holds the right-edge adresses of the playfield on the physical +; screen. At most, it needs to hold 200 addresses for a full height playfield. It is double-length +; so that code can pick any offset and copy values without needing to check for a wrap-around. If the +; playfield is less than 200 lines tall, then any values after 2 * PLAYFIELD_HEIGHT are undefined. +RTable ENT + ds 400 + ds 400 + +; Array of addresses for the banks that hold the blitter. +BlitBuff ENT + dw $5a5a + ds 4*13 + +; The blitter table (BTable) is a double-length table that holds the full 4-byte address of each +; line of the blit fields. We decompose arrays of pointers into separate high and low words so +; that everything can use the same indexing offsets +BTableHigh ENT + ds 208*2*2 +BTableLow ENT + ds 208*2*2 + +; A shorter table that just holds the blitter row addresses +BRowTableHigh ENT + ds 26*2*2 +BRowTableLow ENT + ds 26*2*2 + +; A double-length table of addresses for the BG1 bank. The BG1 buffer is 208 rows of 256 bytes each and +; the first row starts $1800 bytes in to center the buffer in the bank +]step equ $1800 +BG1YTable ENT + lup 208 + dw ]step +]step = ]step+256 + --^ +]step equ 256 + lup 208 + dw ]step +]step = ]step+256 + --^ + +; Repeat +BG1YOffsetTable ENT + lup 26 + dw 1,1,1,2,2,2,2,2,1,1,1,0,0,0,0,0 + --^ + +; Other Toolset variables +OneSecondCounter ENT + dw 0 +OldOneSecVec ENT + ds 4 +Timers ENT + ds TIMER_REC_SIZE*MAX_TIMERS +DefaultPalette ENT + dw $0000,$007F,$0090,$0FF0 + dw $000F,$0080,$0f70,$0FFF + dw $0fa9,$0ff0,$00e0,$04DF + dw $0d00,$078f,$0ccc,$0FFF + +; 0. Full Screen : 40 x 25 320 x 200 (32,000 bytes (100.0%)) +; 1. Sword of Sodan : 34 x 24 272 x 192 (26,112 bytes ( 81.6%)) +; 2. ~NES : 32 x 25 256 x 200 (25,600 bytes ( 80.0%)) +; 3. Task Force : 32 x 22 256 x 176 (22,528 bytes ( 70.4%)) +; 4. Defender of the World : 35 x 20 280 x 160 (22,400 bytes ( 70.0%)) +; 5. Rastan : 32 x 20 256 x 160 (20,480 bytes ( 64.0%)) +; 6. Game Boy Advanced : 30 x 20 240 x 160 (19,200 bytes ( 60.0%)) +; 7. Ancient Land of Y's : 36 x 16 288 x 128 (18,432 bytes ( 57.6%)) +; 8. Game Boy Color : 20 x 18 160 x 144 (11,520 bytes ( 36.0%)) +; 9. Agony (Amiga) : 36 x 24 288 x 192 (27,648 bytes ( 86.4%)) +; 10. Atari Lynx : 20 x 13 160 x 102 (8,160 bytes ( 25.5%)) +ScreenModeWidth ENT + dw 320,272,256,256,280,256,240,288,160,288,160,320 +ScreenModeHeight ENT + dw 200,192,200,176,160,160,160,128,144,192,102,1 + +blt_return +stk_save \ No newline at end of file diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s new file mode 100644 index 0000000..debdd63 --- /dev/null +++ b/src/static/TileStoreDefs.s @@ -0,0 +1,116 @@ +; Tile storage parameters +TILE_DATA_SPAN equ 4 +TILE_STORE_WIDTH equ 41 +TILE_STORE_HEIGHT equ 26 +MAX_TILES equ {26*41} ; Number of tiles in the code field (41 columns * 26 rows) +TILE_STORE_SIZE equ {MAX_TILES*2} ; The tile store contains a tile descriptor in each slot + +TS_TILE_ID equ TILE_STORE_SIZE*0 ; tile descriptor for this location +TS_DIRTY equ TILE_STORE_SIZE*1 ; Flag. Used to prevent a tile from being queued multiple times per frame +TS_SPRITE_FLAG equ TILE_STORE_SIZE*2 ; Bitfield of all sprites that intersect this tile. 0 if no sprites. +TS_TILE_ADDR equ TILE_STORE_SIZE*3 ; cached value, the address of the tiledata for this tile +TS_CODE_ADDR_LOW equ TILE_STORE_SIZE*4 ; const value, address of this tile in the code fields +TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5 +TS_WORD_OFFSET equ TILE_STORE_SIZE*6 ; const value, word offset value for this tile if LDA (dp),y instructions re used +TS_BASE_ADDR equ TILE_STORE_SIZE*7 ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000. +TS_SCREEN_ADDR equ TILE_STORE_SIZE*8 ; cached value of on-screen location of tile. Used for DirtyRender. +;TS_VBUFF_ARRAY_ADDR equ TILE_STORE_SIZE*9 ; const value to an aligned 32-byte array starting at $8000 in TileStore bank + +TS_BASE_TILE_COPY equ TILE_STORE_SIZE*9 ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering +TS_BASE_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function +TS_DIRTY_TILE_DISP equ TILE_STORE_SIZE*11 ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function + +; Hold values for up to 4 sprites per tile +TS_VBUFF_ADDR_0 equ TILE_STORE_SIZE*12 +TS_VBUFF_ADDR_1 equ TILE_STORE_SIZE*13 +TS_VBUFF_ADDR_2 equ TILE_STORE_SIZE*14 +TS_VBUFF_ADDR_3 equ TILE_STORE_SIZE*15 +TS_VBUFF_ADDR_COUNT equ TILE_STORE_SIZE*16 ; replace usage of TS_VBUFF_ARRAY_ADDR with this later + +; Sprite data structures. We cache quite a few pieces of information about the sprite +; to make calculations faster, so this is hidden from the caller. + +MAX_SPRITES equ 16 +SPRITE_REC_SIZE equ 52 + +; Mark each sprite as ADDED, UPDATED, MOVED, REMOVED depending on the actions applied to it +; on this frame. Quick note, the same Sprite ID cannot be removed and added in the same frame. +; A REMOVED sprite if removed from the sprite list during the Render call, so it's ID is not +; available to the AddSprite function until the next frame. + +SPRITE_STATUS_EMPTY equ $0000 ; If the status value is zero, this sprite slot is available +SPRITE_STATUS_OCCUPIED equ $8000 ; Set the MSB to flag it as occupied +SPRITE_STATUS_ADDED equ $0001 ; Sprite was just added (new sprite) +SPRITE_STATUS_MOVED equ $0002 ; Sprite's position was changed +SPRITE_STATUS_UPDATED equ $0004 ; Sprite's non-position attributes were changed +SPRITE_STATUS_REMOVED equ $0008 ; Sprite has been removed. + +SPRITE_STATUS equ {MAX_SPRITES*0} +; TILE_DATA_OFFSET equ {MAX_SPRITES*2} +VBUFF_ADDR equ {MAX_SPRITES*4} ; Base address of the sprite's stamp in the data/mask banks +SPRITE_ID equ {MAX_SPRITES*6} +SPRITE_X equ {MAX_SPRITES*8} +SPRITE_Y equ {MAX_SPRITES*10} +; TILE_STORE_ADDR_1 equ {MAX_SPRITES*12} +TS_LOOKUP_INDEX equ {MAX_SPRITES*12} ; The index into the TileStoreLookup table corresponding to the top-left corner of the sprite +; TILE_STORE_ADDR_2 equ {MAX_SPRITES*14} +TS_COVERAGE_SIZE equ {MAX_SPRITES*14} ; Index into the lookup table of how many TileStore tiles are covered by this sprite +;TILE_STORE_ADDR_3 equ {MAX_SPRITES*16} +TS_VBUFF_BASE_ADDR equ {MAX_SPRITES*16} ; Fixed address of the TS_VBUFF_X memory locations +;TILE_STORE_ADDR_4 equ {MAX_SPRITES*18} +;TILE_STORE_ADDR_5 equ {MAX_SPRITES*20} +;TILE_STORE_ADDR_6 equ {MAX_SPRITES*22} +;TILE_STORE_ADDR_7 equ {MAX_SPRITES*24} +;TILE_STORE_ADDR_8 equ {MAX_SPRITES*26} +;TILE_STORE_ADDR_9 equ {MAX_SPRITES*28} +;TILE_STORE_ADDR_10 equ {MAX_SPRITES*30} +SPRITE_DISP equ {MAX_SPRITES*32} ; cached address of the specific stamp based on flags +SPRITE_CLIP_LEFT equ {MAX_SPRITES*34} +SPRITE_CLIP_RIGHT equ {MAX_SPRITES*36} +SPRITE_CLIP_TOP equ {MAX_SPRITES*38} +SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*40} +IS_OFF_SCREEN equ {MAX_SPRITES*42} +SPRITE_WIDTH equ {MAX_SPRITES*44} +SPRITE_HEIGHT equ {MAX_SPRITES*46} +SPRITE_CLIP_WIDTH equ {MAX_SPRITES*48} +SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*50} + +; 50 rows by 80 columns + 2 extra rows and columns +TS_LOOKUP_WIDTH equ 80 +TS_LOOKUP_HEIGHT equ 50 +TS_LOOKUP_SPAN equ {TS_LOOKUP_WIDTH+2} +TS_LOOKUP_ROWS equ {TS_LOOKUP_HEIGHT+2} + +; Blitter template constancts +PER_TILE_SIZE equ 3 +SNIPPET_SIZE equ 32 + +;---------------------------------------------------------------------- +; +; Timer implementation +; +; The engire provides four timer slot that can be used by one-shot or +; recurring timers. Each timer is given an initial tick count, a +; reset tick count (0 = one-shot), and an action to perform. +; +; The timers handle overflow, so if a recurring timer has a tick count of 3 +; and 7 VBL ticks have passed, then the timer will be fired twice and +; a tick count of 2 will be set. +; +; As such, the timers are appropriate to drive physical and other game +; behaviors at a frame-independent rate. +; +; A collection of 4 timers that are triggered when their countdown +; goes below zero. Each timer takes up 16 bytes +; +; A timer can fire multiple times during a singular evaluation. For example, if the +; timer delay is set to 1 and 3 VBL ticks happen, then the timer delta is -2, will fire, +; have the delay added and get -1, fire again, increment to zero, first again and then +; finally reset to 1. +; +; +0 counter decremented by the number of ticks since last run +; +2 reset copied into counter when triggered. 0 turns off the timer. +; +4 addr long address of timer routine +; +8 user 8 bytes of user data space for timer state +MAX_TIMERS equ 4 +TIMER_REC_SIZE equ 16 diff --git a/src/tiles/DirtyTileQueue.s b/src/tiles/DirtyTileQueue.s index 5f078f2..7e4fcca 100644 --- a/src/tiles/DirtyTileQueue.s +++ b/src/tiles/DirtyTileQueue.s @@ -1,8 +1,3 @@ - -; A list of dirty tiles that need to be updated in a given frame -DirtyTileCount ds 2 -DirtyTiles ds TILE_STORE_SIZE ; At most this many tiles can possibly be update at once - _ClearDirtyTiles bra :hop :loop @@ -62,3 +57,110 @@ _PopDirtyTile2 ; alternate entry point lda #$FFFF stal TileStore+TS_DIRTY,x ; clear the occupied backlink rts + +; An optimized subroutine that runs through the dirty tile list and executes a callback function +; for each dirty tile. This is an unrolled loop, so we avoid the need to track a register and +; decrement on each iteration. +; +; Also, if we are handling less that 8 dirty tiles, we use a code path that does not +; need to use an index register +; +; Bank = Tile Store +; D = Page 2 +_PopDirtyTilesFast + ldx DP2_DIRTY_TILE_COUNT ; This is pre-multiplied by 2 + bne pdtf_not_empty ; If there are no items, exit +at_exit rts +pdtf_not_empty + cpx #16 ; If there are >= 8 elements, then + bcs full_chunk ; do a full chunk + + stz DP2_DIRTY_TILE_COUNT ; Otherwise, this pass will handle them all + jmp (at_table,x) +at_table da at_exit,at_one,at_two,at_three + da at_four,at_five,at_six,at_seven + +full_chunk txa + sbc #16 ; carry set from branch + sta DP2_DIRTY_TILE_COUNT ; fall through + tay ; use the Y-register for the index + +; Because all of the registers get used in the subroutine, we +; push the values from the DirtyTiles array onto the stack and then pop off +; the values as we go + + ldx DirtyTiles+14,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+12,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+10,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+8,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+6,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+4,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+2,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+0,y + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + jmp _PopDirtyTilesFast + +; These routines just handle between 1 and 7 dirty tiles +at_seven + ldx DirtyTiles+12 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_six + ldx DirtyTiles+10 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_five + ldx DirtyTiles+8 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_four + ldx DirtyTiles+6 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_three + ldx DirtyTiles+4 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_two + ldx DirtyTiles+2 + stz TileStore+TS_DIRTY,x + jsr _RenderTileFast + +at_one + ldx DirtyTiles+0 + stz TileStore+TS_DIRTY,x + jmp _RenderTileFast diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index 35f97a2..b285ecc 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -6,20 +6,12 @@ ; If there are sprites, then the sprite data is flattened and stored into a direct page buffer ; and then copied into the code field _RenderTileFast - ldx TileStore+TS_VBUFF_ADDR_COUNT,y ; How many sprites are on this tile? - beq NoSpritesFast ; This is faster if there are no sprites - jmp (fast_dispatch,x) ; Dispatch to the other routines -fast_dispatch - da NoSpritesFast - da OneSpriteFast - da TwoSpritesFast - da ThreeSpritesFast - da FourSpritesFast +; lda TileStore+TS_VBUFF_ADDR_COUNT,x ; How many sprites are on this tile? +; bne SpriteDispatch ; This is faster if there are no sprites -NoSpritesFast - tyx +NoSpriteFast lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line - pha ; and put on the stack for later. Has addl bank in high byte. + pha ; and put on the stack for later. Has TileStore bank in high byte. ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) plb ; set the code field bank @@ -27,7 +19,17 @@ NoSpritesFast ; The TS_BASE_TILE_DISP routines will come from this table when ENGINE_MODE_TWO_LAYER and ; ENGINE_MODE_DYN_TILES are both off. -FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast +FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast +; dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast + +SpriteDispatch + tax + jmp (:,x) ; Dispatch to the other routines +: da NoSpriteFast ; Placeholder + da OneSpriteFast + da TwoSpritesFast + da ThreeSpritesFast + da FourSpritesFast ; Pointers to sprite data and masks spritedata_0 equ tmp0 From fd07ead8ed7406c4064037ddf0060be75f0834a8 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 18 May 2022 21:39:39 -0500 Subject: [PATCH 17/82] Get tile data to display from code field --- src/Render.s | 2 +- src/Tiles.s | 36 +++++++++++++++++------------------- src/blitter/Blitter.s | 1 - src/tiles/DirtyTileQueue.s | 19 +++++++++++++++++++ src/tiles/FastRenderer.s | 6 ++++-- 5 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/Render.s b/src/Render.s index cb95762..d86fff1 100644 --- a/src/Render.s +++ b/src/Render.s @@ -102,7 +102,7 @@ _ApplyTilesFast lda DirtyTileCount ; Cache the dirty tile count sta DP2_DIRTY_TILE_COUNT - jsr _PopDirtyTile2 + jsr _PopDirtyTilesFast stz DirtyTileCount diff --git a/src/Tiles.s b/src/Tiles.s index 5758bb2..125150e 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -175,15 +175,15 @@ InitTiles _SetTile pha jsr _GetTileStoreOffset0 ; Get the address of the X,Y tile position - tax + tay pla - cmpl TileStore+TS_TILE_ID,x ; Only set to dirty if the value changed + cmp TileStore+TS_TILE_ID,y ; Only set to dirty if the value changed beq :nochange - stal TileStore+TS_TILE_ID,x ; Value is different, store it. + sta TileStore+TS_TILE_ID,y ; Value is different, store it. jsr _GetTileAddr - stal TileStore+TS_TILE_ADDR,x ; Committed to drawing this tile, so get the address of the tile in the tiledata bank for later + sta TileStore+TS_TILE_ADDR,y ; Committed to drawing this tile, so get the address of the tile in the tiledata bank for later ; Set the standard renderer procs for this tile. ; @@ -196,35 +196,33 @@ _SetTile ; functionality. Sometimes it is simple, but in cases of the sprites overlapping Dynamic Tiles and other cases ; it can be more involved. - ldal TileStore+TS_TILE_ID,x + lda TileStore+TS_TILE_ID,y and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value xba - tay -; lda DirtyTileProcs,y -; stal TileStore+TS_DIRTY_TILE_DISP,x + tax +; ldal DirtyTileProcs,x +; sta TileStore+TS_DIRTY_TILE_DISP,y -; lda CopyTileProcs,y -; stal TileStore+TS_DIRTY_TILE_COPY,x +; ldal CopyTileProcs,x +; sta TileStore+TS_DIRTY_TILE_COPY,y lda EngineMode bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER beq :fast - ldal TileStore+TS_TILE_ID,x ; Get the non-sprite dispatch address + lda TileStore+TS_TILE_ID,y ; Get the non-sprite dispatch address and #TILE_CTRL_MASK xba - tay -; lda TileProcs,y -; stal TileStore+TS_BASE_TILE_DISP,x + tax +; ldal TileProcs,x +; sta TileStore+TS_BASE_TILE_DISP,y bra :out :fast - lda FastTileProcs,y - stal TileStore+TS_BASE_TILE_DISP,x + ldal FastTileProcs,x + sta TileStore+TS_BASE_TILE_DISP,y :out - - txa ; Add this tile to the list of dirty tiles to refresh - jmp _PushDirtyTileX ; on the next call to _ApplyTiles + jmp _PushDirtyTileY ; on the next call to _ApplyTiles :nochange rts diff --git a/src/blitter/Blitter.s b/src/blitter/Blitter.s index d490e3c..4b149bc 100644 --- a/src/blitter/Blitter.s +++ b/src/blitter/Blitter.s @@ -74,7 +74,6 @@ _BltRange tsc ; save the stack pointer stal stk_save+1 - bra blt_return blt_entry jml $000000 ; Jump into the blitter code $XX/YY00 blt_return _R0W0 diff --git a/src/tiles/DirtyTileQueue.s b/src/tiles/DirtyTileQueue.s index 7e4fcca..df9bdb2 100644 --- a/src/tiles/DirtyTileQueue.s +++ b/src/tiles/DirtyTileQueue.s @@ -38,6 +38,24 @@ _PushDirtyTileX txa ; Make sure TileStore offset is returned in the accumulator rts +; alternate entry point if the Y-register is already set +_PushDirtyTileY + lda TileStore+TS_DIRTY,y + bne :occupied2 + + inc ; any non-zero value will work + sta TileStore+TS_DIRTY,y ; and is 1 cycle faster than loading a constant value + + tya + ldy DirtyTileCount ; 4 + sta DirtyTiles,y ; 6 + iny ; 2 + iny ; 2 + sty DirtyTileCount ; 4 = 18 + rts +:occupied2 + tya ; Make sure TileStore offset is returned in the accumulator + 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 @@ -68,6 +86,7 @@ _PopDirtyTile2 ; alternate entry point ; Bank = Tile Store ; D = Page 2 _PopDirtyTilesFast +; brk $EE ldx DP2_DIRTY_TILE_COUNT ; This is pre-multiplied by 2 bne pdtf_not_empty ; If there are no items, exit at_exit rts diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index b285ecc..67d2518 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -13,9 +13,11 @@ NoSpriteFast lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line pha ; and put on the stack for later. Has 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) + lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) + lda TileStore+TS_BASE_TILE_DISP,x ; go to the tile copy routine (just basics) + stal nsf_patch+1 plb ; set the code field bank - jmp (TileStore+TS_BASE_TILE_DISP,x) ; go to the tile copy routine (just basics) +nsf_patch jmp $0000 ; The TS_BASE_TILE_DISP routines will come from this table when ENGINE_MODE_TWO_LAYER and ; ENGINE_MODE_DYN_TILES are both off. From fc4833006111864ec208df1946a4a6ce8bc4cef9 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 18 May 2022 22:49:14 -0500 Subject: [PATCH 18/82] Fix bug in tile rendering and tile sheet copying --- src/Tiles.s | 2 +- src/Tool.s | 1 + src/tiles/DirtyTileQueue.s | 1 - src/tiles/FastRenderer.s | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Tiles.s b/src/Tiles.s index 125150e..aa0637c 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -5,7 +5,7 @@ ; A = low word _LoadTileSet sta tmp0 - stx tmp2 + stx tmp1 ldy #0 tyx :loop lda [tmp0],y diff --git a/src/Tool.s b/src/Tool.s index e08e555..86b28ea 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -242,6 +242,7 @@ TSPtr equ FirstParam tax lda TSPtr,s jsr _LoadTileSet + _TSExit #0;#4 ; Insert the GTE code diff --git a/src/tiles/DirtyTileQueue.s b/src/tiles/DirtyTileQueue.s index df9bdb2..b3cb0d2 100644 --- a/src/tiles/DirtyTileQueue.s +++ b/src/tiles/DirtyTileQueue.s @@ -86,7 +86,6 @@ _PopDirtyTile2 ; alternate entry point ; Bank = Tile Store ; D = Page 2 _PopDirtyTilesFast -; brk $EE ldx DP2_DIRTY_TILE_COUNT ; This is pre-multiplied by 2 bne pdtf_not_empty ; If there are no items, exit at_exit rts diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index 67d2518..780d319 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -13,9 +13,9 @@ NoSpriteFast lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line pha ; and put on the stack for later. Has 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) lda TileStore+TS_BASE_TILE_DISP,x ; go to the tile copy routine (just basics) stal nsf_patch+1 + lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) plb ; set the code field bank nsf_patch jmp $0000 From e52ef4f7cfd47d1d66ceaade9b310b59fcef89b9 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Thu, 19 May 2022 23:39:19 -0500 Subject: [PATCH 19/82] Add message output for debugging --- demos/tool/App.Main.s | 75 ++++- demos/tool/App.Msg.s | 105 +++++++ demos/tool/font.s | 668 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 837 insertions(+), 11 deletions(-) create mode 100644 demos/tool/App.Msg.s create mode 100644 demos/tool/font.s diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 903dba7..068c9ab 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -9,6 +9,8 @@ use EDS.GSOS.Macs use GTE.Macs + use ../../src/Defs.s + mx %00 TSZelda EXT ; tileset buffer @@ -20,7 +22,7 @@ ScreenY equ 2 phk plb - sta UserId ; GS/OS passes the memory manager user ID for the aoplication into the program + sta MyUserId ; GS/OS passes the memory manager user ID for the aoplication into the program _MTStartUp ; GTE requires the miscellaneous toolset to be running jsr GTEStartUp ; Load and install the GTE User Tool @@ -68,11 +70,18 @@ ScreenY equ 2 bcc :loop ; Set the origin of the screen - stz ScreenX - stz ScreenY + + lda #3 + sta ScreenX + lda #10 + sta ScreenY + + pea #3 + pea #10 + _GTESetBG0Origin ; Very simple actions -:loop +:evt_loop pha ; space for result, with pattern _GTEReadControl pla @@ -80,14 +89,56 @@ ScreenY equ 2 cmp #'q' beq :exit + cmp #$15 ; left = $08, right = $15, up = $0B, down = $0A + bne :8 + inc ScreenX + bra :next + +:8 cmp #$08 + bne :9 + dec ScreenX + bra :next + +:9 cmp #$0B + bne :10 + inc ScreenY + bra :next + +:10 cmp #$0A + bne :next + dec ScreenY + +:next pei ScreenX pei ScreenY _GTESetBG0Origin _GTERender - inc ScreenX ; Just keep incrementing, it's OK - bra :loop +; Debug stuff + ldx #$100 + lda StartX,x + ldx #0 + jsr DrawWord + + ldx #$100 + lda StartY,x + ldx #160*8 + jsr DrawWord + + lda ScreenX + ldx #160*16 + jsr DrawWord + + lda ScreenY + ldx #160*24 + jsr DrawWord + + tdc + ldx #160*32 + jsr DrawWord + + brl :evt_loop ; Shut down everything :exit @@ -108,7 +159,7 @@ GTEStartUp pea $0000 pea $0000 ; result space - lda UserId + lda MyUserId pha pea #^ToolPath @@ -139,7 +190,7 @@ GTEStartUp adc #$0100 pha pea $0000 ; No extra capabilities - lda UserId ; Pass the userId for memory allocation + lda MyUserId ; Pass the userId for memory allocation pha _GTEStartUp bcc :ok3 @@ -148,6 +199,8 @@ GTEStartUp :ok3 rts -MasterId ds 2 -UserId ds 2 -ToolPath str '1/Tool160' \ No newline at end of file +MyUserId ds 2 +ToolPath str '1/Tool160' + + PUT App.Msg.s + PUT font.s diff --git a/demos/tool/App.Msg.s b/demos/tool/App.Msg.s new file mode 100644 index 0000000..4afc4dd --- /dev/null +++ b/demos/tool/App.Msg.s @@ -0,0 +1,105 @@ +HexToChar dfb '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' + +; Convert a byte (Acc) into a string and store at (Y) +ByteToString and #$00FF + sep #$20 + pha + lsr + lsr + lsr + lsr + and #$0F + tax + ldal HexToChar,x + sta: $0000,y + + pla + and #$0F + tax + ldal HexToChar,x + sta: $0001,y + + rep #$20 + rts + +; Convert a word (Acc) into a hexadecimal string and store at (Y) +WordToString pha + bra Addr2ToString + +; Pass in Acc = High, X = low +Addr3ToString phx + jsr ByteToString + iny + iny + lda 1,s +Addr2ToString xba + jsr ByteToString + iny + iny + pla + jsr ByteToString + rts + +; A=Value +; X=Screen offset +DrawWord phx ; Save register value + phy + ldy #WordBuff+1 + jsr WordToString + ply + plx + lda #WordBuff + jsr DrawString + rts + +WordBuff str '0000' +Addr3Buff str '000000' ; str adds leading length byte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/tool/font.s b/demos/tool/font.s new file mode 100644 index 0000000..8c71e10 --- /dev/null +++ b/demos/tool/font.s @@ -0,0 +1,668 @@ +**************************************** +* FONT ENGINE (v3?) * +* * +* Dagen Brock * +* 2013-07-20 * +**************************************** +* A= ptr to string preceded by length * +* X= screen location * +**************************************** +; each char: +; draw char at loc +; update loc +; see if length hit - no? back to draw char + rel + mx %00 +]F_Length ds 2 ;length of string (only one byte currently used) +]F_CharIdx ds 2 ;index of current character +]F_CurrentPos ds 2 ;current top left char position +]F_StrPtr equ $01 ;pointer to string (including length byte) / DP +]F_StrClr equ $03 + +DrawString + pha ; local variable space + pha + tsc + phd + tcd + +; sta ]F_StrPtr ; (done in pha init above) store at dp 0 ($00) for indirect loads + stx ]F_CurrentPos + sty ]F_StrClr + stz ]F_CharIdx + lda (]F_StrPtr) + and #$00ff ;strip off first char (len is only one byte) + sta ]F_Length ;get our length byte + +NextChar lda ]F_CharIdx + cmp ]F_Length + bne :notDone + ldy ]F_StrClr ;restore the color pattern + pld + pla + pla + rts ;DONE! Return to caller + +:notDone inc ]F_CharIdx + ldy ]F_CharIdx + lda (]F_StrPtr),y ;get next char! + and #$00FF ;mask high byte + sec + sbc #' ' ;our table starts with space ' ' + asl ;*2 + tay + ldx ]F_CurrentPos + jsr :drawChar + inc ]F_CurrentPos ;compare to addition time (?) + inc ]F_CurrentPos + inc ]F_CurrentPos + inc ]F_CurrentPos ;update screen pos (2 words=8 pixels) + bra NextChar + +;x = TopLeft screen pos +;y = char table offset +:drawChar lda FontTable,y ;get real address of char data + sec + sbc #FontData ;pivot offset - now a is offset of fontdata + tay ;so we'll index with that + + lda FontData,y + and ]F_StrClr + stal $E12000,x + + lda FontData+2,y + and ]F_StrClr + stal $E12000+2,x + + lda FontData+4,y + and ]F_StrClr + stal $E12000+160,x + + lda FontData+6,y + and ]F_StrClr + stal $E12000+160+2,x + + lda FontData+8,y + and ]F_StrClr + stal {$E12000+160*2},x + + lda FontData+10,y + and ]F_StrClr + stal {$E12000+160*2+2},x + + lda FontData+12,y + and ]F_StrClr + stal {$E12000+160*3},x + + lda FontData+14,y + and ]F_StrClr + stal {$E12000+160*3+2},x + + lda FontData+16,y + and ]F_StrClr + stal {$E12000+160*4},x + + lda FontData+18,y + and ]F_StrClr + stal {$E12000+160*4+2},x + + lda FontData+20,y + and ]F_StrClr + stal {$E12000+160*5},x + + lda FontData+22,y + and ]F_StrClr + stal {$E12000+160*5+2},x + rts + +FontTable dw s_Space + dw s_Exclaim + dw s_Quote + dw s_Number + dw s_Dollar + dw s_Percent + dw s_Amper + dw s_Single + dw s_OpenParen + dw s_CloseParen + dw s_Asterix + dw s_Plus + dw s_Comma + dw s_Minus + dw s_Period + dw s_Slash + dw s_N0 + dw s_N1 + dw s_N2 + dw s_N3 + dw s_N4 + dw s_N5 + dw s_N6 + dw s_N7 + dw s_N8 + dw s_N9 + dw s_Colon + dw s_Semi + dw s_LAngle + dw s_Equal + dw s_RAngle + dw s_Question + dw s_At + dw s_A + dw s_B + dw s_C + dw s_D + dw s_E + dw s_F + dw s_G + dw s_H + dw s_I + dw s_J + dw s_K + dw s_L + dw s_M + dw s_N + dw s_O + dw s_P + dw s_Q + dw s_R + dw s_S + dw s_T + dw s_U + dw s_V + dw s_W + dw s_X + dw s_Y + dw s_Z + dw s_LBracket + dw s_BackSlash + dw s_RBracket + dw s_Carot + dw s_UnderLine + +FontData = * +s_Space hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +s_Exclaim hex 000FF000 + hex 000FF000 + hex 000FF000 + hex 000FF000 + hex 00000000 + hex 000FF000 + +s_Quote hex 0FF00FF0 + hex 00F000F0 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +s_Number hex 00000000 + hex 00F00F00 + hex 0FFFFFF0 + hex 00F00F00 + hex 0FFFFFF0 + hex 00F00F00 + +s_Dollar hex 000F0F00 + hex 00FFFFF0 + hex 0F0F0F00 + hex 00FFFF00 + hex 000F0FF0 + hex 0FFFFF00 + +s_Percent hex 0FF000F0 + hex 00000F00 + hex 0000F000 + hex 000F0000 + hex 00F00000 + hex 0F000FF0 + +s_Amper hex 000FF000 + hex 00F00F00 + hex 0F00F000 + hex 00F000F0 + hex 0F0FFF00 + hex 00F0F000 + +s_Single hex 000FF000 + hex 0000F000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + +s_OpenParen hex 000FF000 + hex 00FF0000 + hex 0FF00000 + hex 0FF00000 + hex 00FF0000 + hex 000FF000 + +s_CloseParen hex 000FF000 + hex 0000FF00 + hex 00000FF0 + hex 00000FF0 + hex 0000FF00 + hex 000FF000 + + +s_Asterix hex 00000000 + hex 00F0F0F0 + hex 000FFF00 + hex 00FFFFF0 + hex 000FFF00 + hex 00F0F0F0 + +s_Plus hex 000F0000 + hex 000F0000 + hex 0FFFFF00 + hex 000F0000 + hex 000F0000 + hex 00000000 + +s_Comma hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 0000FF00 + hex 0000F000 + +s_Minus hex 00000000 + hex 00000000 + hex 0FFFFF00 + hex 00000000 + hex 00000000 + hex 00000000 + + +s_Period hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 0000FF00 + hex 0000FF00 + +s_Slash hex 000000F0 + hex 00000F00 + hex 0000F000 + hex 000F0000 + hex 00F00000 + hex 0F000000 + +s_N0 hex 00FFFF00 + hex 0F000FF0 + hex 0F00F0F0 + hex 0F0F00F0 + hex 0FF000F0 + hex 00FFFF00 + +s_N1 hex 000F0000 + hex 00FF0000 + hex 000F0000 + hex 000F0000 + hex 000F0000 + hex 00FFF000 + +s_N2 hex 00FFFF00 + hex 0F0000F0 + hex 00000F00 + hex 000FF000 + hex 00F00000 + hex 0FFFFFF0 + +s_N3 hex 00FFFF00 + hex 000000F0 + hex 000FFF00 + hex 000000F0 + hex 000000F0 + hex 00FFFF00 + +s_N4 hex 0000FF00 + hex 000F0F00 + hex 00F00F00 + hex 0FFFFFF0 + hex 00000F00 + hex 00000F00 + +s_N5 hex 0FFFFFF0 + hex 0F000000 + hex 0FFFFF00 + hex 000000F0 + hex 0F0000F0 + hex 00FFFF00 + +s_N6 hex 000FFF00 + hex 00F00000 + hex 0F000000 + hex 0FFFFF00 + hex 0F0000F0 + hex 00FFFFF0 + +s_N7 hex 0FFFFFF0 + hex 000000F0 + hex 00000F00 + hex 0000F000 + hex 000F0000 + hex 000F0000 + +s_N8 hex 00FFFF00 + hex 0F0000F0 + hex 00FFFF00 + hex 0F0000F0 + hex 0F0000F0 + hex 00FFFF00 + +s_N9 hex 00FFFF00 + hex 0F0000F0 + hex 00FFFF00 + hex 0000F000 + hex 000F0000 + hex 00F00000 + +s_Colon hex 000FF000 + hex 000FF000 + hex 00000000 + hex 000FF000 + hex 000FF000 + hex 00000000 + +s_Semi hex 00000000 + hex 000FF000 + hex 000FF000 + hex 00000000 + hex 000FF000 + hex 000F0000 + +s_LAngle hex 0000F000 + hex 000F0000 + hex 00F00000 + hex 000F0000 + hex 0000F000 + hex 00000000 + +s_Equal hex 00000000 + hex 00000000 + hex 0FFFFF00 + hex 00000000 + hex 0FFFFF00 + hex 00000000 + +s_RAngle hex 0000F000 + hex 00000F00 + hex 000000F0 + hex 00000F00 + hex 0000F000 + hex 00000000 + +s_Question hex 00FFF000 + hex 0F000F00 + hex 00000F00 + hex 000FF000 + hex 00000000 + hex 000FF000 + +s_At hex 00FFFF00 + hex 0F0000F0 + hex 0F00F0F0 + hex 0FFFF0F0 + hex 000000F0 + hex 0FFFFF00 + +s_A hex 000FF000 + hex 00F00F00 + hex 0F0000F0 + hex 0FFFFFF0 + hex 0F0000F0 + hex 0F0000F0 + +s_B hex 0FFFFF00 + hex 0F0000F0 + hex 0FFFFF00 + hex 0F0000F0 + hex 0F0000F0 + hex 0FFFFF00 + +s_C hex 00FFFFF0 + hex 0F000000 + hex 0F000000 + hex 0F000000 + hex 0F000000 + hex 00FFFFF0 + +s_D hex 0FFFFF00 + hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + hex 0FFFFF00 + +s_E hex 0FFFFFF0 + hex 0F000000 + hex 0FFFF000 + hex 0F000000 + hex 0F000000 + hex 0FFFFFF0 + +s_F hex 0FFFFFF0 + hex 0F000000 + hex 0FFFF000 + hex 0F000000 + hex 0F000000 + hex 0F000000 + +s_G hex 00FFFFF0 + hex 0F000000 + hex 0F000000 + hex 0F00FFF0 + hex 0F0000F0 + hex 00FFFF00 + +s_H hex 0F0000F0 + hex 0F0000F0 + hex 0FFFFFF0 + hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + +s_I hex 0FFFFF00 + hex 000F0000 + hex 000F0000 + hex 000F0000 + hex 000F0000 + hex 0FFFFF00 + +s_J hex 000000F0 + hex 000000F0 + hex 000000F0 + hex 0F0000F0 + hex 0F0000F0 + hex 00FFFF00 + +s_K hex 0F000F00 + hex 0F00F000 + hex 0FFF0000 + hex 0F00F000 + hex 0F000F00 + hex 0F000F00 + +s_L hex 0F000000 + hex 0F000000 + hex 0F000000 + hex 0F000000 + hex 0F000000 + hex 0FFFFFF0 + +s_M hex 0F0000F0 + hex 0FF00FF0 + hex 0F0FF0F0 + hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + +s_N hex 0F0000F0 + hex 0FF000F0 + hex 0F0F00F0 + hex 0F00F0F0 + hex 0F000FF0 + hex 0F0000F0 + +s_O hex 00FFFF00 + hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + hex 00FFFF00 + +s_P hex 0FFFFF00 + hex 0F0000F0 + hex 0FFFFF00 + hex 0F000000 + hex 0F000000 + hex 0F000000 + +s_Q hex 00FFFF00 + hex 0F0000F0 + hex 0F0000F0 + hex 0F00F0F0 + hex 0F000FF0 + hex 00FFFFF0 + +s_R hex 0FFFFF00 + hex 0F0000F0 + hex 0FFFFF00 + hex 0F000F00 + hex 0F0000F0 + hex 0F0000F0 + +s_S hex 00FFFFF0 + hex 0F000000 + hex 00FFFF00 + hex 000000F0 + hex 000000F0 + hex 0FFFFF00 + +s_T hex 0FFFFF00 + hex 000F0000 + hex 000F0000 + hex 000F0000 + hex 000F0000 + hex 000F0000 + +s_U hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + hex 00FFFF00 + +s_V hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + hex 00F00F00 + hex 000FF000 + +s_W hex 0F0000F0 + hex 0F0000F0 + hex 0F0000F0 + hex 0F0FF0F0 + hex 0FF00FF0 + hex 0F0000F0 + +s_X hex 0F0000F0 + hex 00F00F00 + hex 000FF000 + hex 000FF000 + hex 00F00F00 + hex 0F0000F0 + +s_Y hex F00000F0 + hex 0F000F00 + hex 00F0F000 + hex 000F0000 + hex 000F0000 + hex 000F0000 + +s_Z hex 0FFFFFF0 + hex 00000F00 + hex 0000F000 + hex 000F0000 + hex 00F00000 + hex 0FFFFFF0 + +s_LBracket hex 000FFF00 + hex 000F0000 + hex 000F0000 + hex 000F0000 + hex 000F0000 + hex 000FFF00 + +s_BackSlash hex 0F000000 + hex 00F00000 + hex 000F0000 + hex 0000F000 + hex 00000F00 + hex 000000F0 + +s_RBracket hex 00FFF000 + hex 0000F000 + hex 0000F000 + hex 0000F000 + hex 0000F000 + hex 00FFF000 + +s_Carot hex 0000F000 + hex 000F0F00 + hex 00F000F0 + hex 00000000 + hex 00000000 + hex 00000000 + +s_UnderLine hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex FFFFFFF0 + +s_Template hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex 00000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 9ac9ea623ebe302bd95ed60c73f6daaf61107736 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Thu, 19 May 2022 23:40:45 -0500 Subject: [PATCH 20/82] More banking fixes --- src/CoreImpl.s | 7 +- src/Memory.s | 2 +- src/Tiles.s | 26 ++-- src/blitter/Horz.s | 1 - src/blitter/Tables.s | 294 ------------------------------------- src/tiles/DirtyTileQueue.s | 4 +- 6 files changed, 15 insertions(+), 319 deletions(-) delete mode 100644 src/blitter/Tables.s diff --git a/src/CoreImpl.s b/src/CoreImpl.s index 04931a4..a8696a7 100644 --- a/src/CoreImpl.s +++ b/src/CoreImpl.s @@ -296,9 +296,9 @@ _ReadControl ora 1,s sta 1,s - cmpl LastKey + cmp LastKey beq :KbdDown - stal LastKey + sta LastKey lda #PAD_KEY_DOWN ; set the keydown flag ora 2,s @@ -306,8 +306,7 @@ _ReadControl bra :KbdDown :KbdNotDwn - lda #0 - stal LastKey + stz LastKey :KbdDown rep #$20 pla diff --git a/src/Memory.s b/src/Memory.s index 441e2ae..6a05700 100644 --- a/src/Memory.s +++ b/src/Memory.s @@ -167,7 +167,7 @@ AllocOneBank PushLong #0 plx ; base address of the new handle pla ; high address 00XX of the new handle (bank) xba ; swap accumulator bytes to XX00 - sta :bank+2 ; store as bank for next op (overwrite $XX00) + stal :bank+2 ; store as bank for next op (overwrite $XX00) :bank ldal $000001,X ; recover the bank address in A=XX/00 rts diff --git a/src/Tiles.s b/src/Tiles.s index aa0637c..85b1e82 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -109,10 +109,10 @@ InitTiles ; that are happening lda #0 - stal TileStore+TS_TILE_ID,x ; clear the tile store with the special zero tile - stal TileStore+TS_TILE_ADDR,x - stal TileStore+TS_SPRITE_FLAG,x ; no sprites are set at the beginning - stal TileStore+TS_DIRTY,x ; none of the tiles are dirty + sta TileStore+TS_TILE_ID,x ; clear the tile store with the special zero tile + sta TileStore+TS_TILE_ADDR,x + sta TileStore+TS_SPRITE_FLAG,x ; no sprites are set at the beginning + sta TileStore+TS_DIRTY,x ; none of the tiles are dirty ; lda DirtyTileProcs ; Fill in with the first dispatch address ; stal TileStore+TS_DIRTY_TILE_DISP,x @@ -137,21 +137,21 @@ InitTiles lda #>TileStore ; get middle 16 bits: "00 -->BBHH<-- LL" and #$FF00 ; merge with code field bank ora BRowTableHigh,y - stal TileStore+TS_CODE_ADDR_HIGH,x ; High word of the tile address (just the bank) + sta TileStore+TS_CODE_ADDR_HIGH,x ; High word of the tile address (just the bank) lda BRowTableLow,y - stal TileStore+TS_BASE_ADDR,x ; May not be needed later if we can figure out the right constant... + 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 asl - stal TileStore+TS_WORD_OFFSET,x ; This is the offset from 0 to 82, used in LDA (dp),y instruction + sta TileStore+TS_WORD_OFFSET,x ; This is the offset from 0 to 82, used in LDA (dp),y instruction tay lda Col2CodeOffset+2,y clc - adcl TileStore+TS_BASE_ADDR,x - stal TileStore+TS_CODE_ADDR_LOW,x ; Low word of the tile address in the code field + adc TileStore+TS_BASE_ADDR,x + sta TileStore+TS_CODE_ADDR_LOW,x ; Low word of the tile address in the code field dec :col bpl :hop @@ -236,10 +236,6 @@ _SetTile ; continuously overwrite it. ; ; We assume that there is a clean code field in this routine -SetBG0XPos ENT - jsr _SetBG0XPos - rtl - _SetBG0XPos cmp StartX beq :out ; Easy, if nothing changed, then nothing changes @@ -258,10 +254,6 @@ _SetBG0XPos ; SetBG0YPos ; ; Set the virtual position of the primary background layer. -SetBG0YPos ENT - jsr _SetBG0YPos - rtl - _SetBG0YPos cmp StartY beq :out ; Easy, if nothing changed, then nothing changes diff --git a/src/blitter/Horz.s b/src/blitter/Horz.s index dbe428b..eb7aa5d 100644 --- a/src/blitter/Horz.s +++ b/src/blitter/Horz.s @@ -32,7 +32,6 @@ _RestoreBG0Opcodes lda LastPatchOffset ; If zero, there are no saved opcodes sta :exit_offset - beq :loop :loop ldx :virt_line_x2 diff --git a/src/blitter/Tables.s b/src/blitter/Tables.s deleted file mode 100644 index 91a28e1..0000000 --- a/src/blitter/Tables.s +++ /dev/null @@ -1,294 +0,0 @@ -; Collection of data tables -; - -; Col2CodeOffset -; -; Takes a column number (0 - 81) and returns the offset into the blitter code -; template. -; -; This is used for rendering tile data into the code field. For example, is we assume that -; we are filling in the operands for a bunch of PEA values, we could do this -; -; ldy tileColumn*2 -; lda #DATA -; ldx Col2CodeOffset,y -; sta $0001,x -; -; The table values are pre-reversed so that loop can go in logical order 0, 2, 4, ... -; and the resulting offsets will map to the code instructions in right-to-left order. -; -; Remember, because the data is pushed on to the stack, the last instruction, which is -; in the highest memory location, pushed data that apepars on the left edge of the screen. - -PER_TILE_SIZE equ 3 -]step equ 0 - - dw CODE_TOP ; There is a spot where we load Col2CodeOffet-2,x -Col2CodeOffset lup 82 - dw CODE_TOP+{{81-]step}*PER_TILE_SIZE} -]step equ ]step+1 - --^ - dw CODE_TOP+{81*PER_TILE_SIZE} - -; A parallel table to Col2CodeOffset that holds the offset to the exception handler address for each column -SNIPPET_SIZE equ 32 -]step equ 0 - dw SNIPPET_BASE -JTableOffset lup 82 - dw SNIPPET_BASE+{{81-]step}*SNIPPET_SIZE} -]step equ ]step+1 - --^ - dw SNIPPET_BASE+{81*SNIPPET_SIZE} - -; Table of BRA instructions that are used to exit the code field. Separate tables for -; even and odd aligned cases. -; -; The even exit point is closest to the code field. The odd exit point is 3 bytes further -; -; These tables are reversed to be parallel with the JTableOffset and Col2CodeOffset tables above. The -; physical word index that each instruction is intended to be placed at is in the comment. -CodeFieldEvenBRA - bra *+6 ; 81 -- need to skip over the JMP loop that passed control back - bra *+9 ; 80 - bra *+12 ; 79 - bra *+15 ; 78 - bra *+18 ; 77 - bra *+21 ; 76 - bra *+24 ; 75 - bra *+27 ; 74 - bra *+30 ; 73 - bra *+33 ; 72 - bra *+36 ; 71 - bra *+39 ; 70 - bra *+42 ; 69 - bra *+45 ; 68 - bra *+48 ; 67 - bra *+51 ; 66 - bra *+54 ; 65 - bra *+57 ; 64 - bra *+60 ; 63 - bra *+63 ; 62 - bra *+66 ; 61 - bra *+69 ; 60 - bra *+72 ; 59 - bra *+75 ; 58 - bra *+78 ; 57 - bra *+81 ; 56 - bra *+84 ; 55 - bra *+87 ; 54 - bra *+90 ; 53 - bra *+93 ; 52 - bra *+96 ; 51 - bra *+99 ; 50 - bra *+102 ; 49 - bra *+105 ; 48 - bra *+108 ; 47 - bra *+111 ; 46 - bra *+114 ; 45 - bra *+117 ; 44 - bra *+120 ; 43 - bra *+123 ; 42 - bra *+126 ; 41 - bra *-123 ; 40 - bra *-120 ; 39 - bra *-117 ; 38 - bra *-114 ; 37 - bra *-111 ; 36 - bra *-108 ; 35 - bra *-105 ; 34 - bra *-102 ; 33 - bra *-99 ; 32 - bra *-96 ; 31 - bra *-93 ; 30 - bra *-90 ; 29 - bra *-87 ; 28 - bra *-84 ; 27 - bra *-81 ; 26 - bra *-78 ; 25 - bra *-75 ; 24 - bra *-72 ; 23 - bra *-69 ; 22 - bra *-66 ; 21 - bra *-63 ; 20 - bra *-60 ; 19 - bra *-57 ; 18 - bra *-54 ; 17 - bra *-51 ; 16 - bra *-48 ; 15 - bra *-45 ; 14 - bra *-42 ; 13 - bra *-39 ; 12 - bra *-36 ; 11 - bra *-33 ; 10 - bra *-30 ; 9 - bra *-27 ; 8 - bra *-24 ; 7 - bra *-21 ; 6 - bra *-18 ; 5 - bra *-15 ; 4 - bra *-12 ; 3 - bra *-9 ; 2 - bra *-6 ; 1 - bra *-3 ; 0 - -CodeFieldOddBRA - bra *+9 ; 81 -- need to skip over two JMP instructions - bra *+12 ; 80 - bra *+15 ; 79 - bra *+18 ; 78 - bra *+21 ; 77 - bra *+24 ; 76 - bra *+27 ; 75 - bra *+30 ; 74 - bra *+33 ; 73 - bra *+36 ; 72 - bra *+39 ; 71 - bra *+42 ; 70 - bra *+45 ; 69 - bra *+48 ; 68 - bra *+51 ; 67 - bra *+54 ; 66 - bra *+57 ; 65 - bra *+60 ; 64 - bra *+63 ; 64 - bra *+66 ; 62 - bra *+69 ; 61 - bra *+72 ; 60 - bra *+75 ; 59 - bra *+78 ; 58 - bra *+81 ; 57 - bra *+84 ; 56 - bra *+87 ; 55 - bra *+90 ; 54 - bra *+93 ; 53 - bra *+96 ; 52 - bra *+99 ; 51 - bra *+102 ; 50 - bra *+105 ; 49 - bra *+108 ; 48 - bra *+111 ; 47 - bra *+114 ; 46 - bra *+117 ; 45 - bra *+120 ; 44 - bra *+123 ; 43 - bra *+126 ; 42 - bra *+129 ; 41 - bra *-126 ; 40 - bra *-123 ; 39 - bra *-120 ; 38 - bra *-117 ; 37 - bra *-114 ; 36 - bra *-111 ; 35 - bra *-108 ; 34 - bra *-105 ; 33 - bra *-102 ; 32 - bra *-99 ; 31 - bra *-96 ; 30 - bra *-93 ; 29 - bra *-90 ; 28 - bra *-87 ; 27 - bra *-84 ; 26 - bra *-81 ; 25 - bra *-78 ; 24 - bra *-75 ; 23 - bra *-72 ; 22 - bra *-69 ; 21 - bra *-66 ; 20 - bra *-63 ; 19 - bra *-60 ; 18 - bra *-57 ; 17 - bra *-54 ; 16 - bra *-51 ; 15 - bra *-48 ; 14 - bra *-45 ; 13 - bra *-42 ; 12 - bra *-39 ; 11 - bra *-36 ; 10 - bra *-33 ; 9 - bra *-30 ; 8 - bra *-27 ; 7 - bra *-24 ; 6 - bra *-21 ; 5 - bra *-18 ; 4 - bra *-15 ; 3 - bra *-12 ; 2 - bra *-9 ; 1 - bra *-6 ; 0 -- branch back 6 to skip the JMP even path - -]step equ $2000 -ScreenAddr ENT - lup 200 - dw ]step -]step = ]step+160 - --^ - -; Table of offsets into each row of a Tile Store table. We currently have two tables defined; one -; that is the backing store for the tiles rendered into the code field, and another that holds -; backlink information on the sprite entries that overlap various tiles. -; -; This table is double-length to support accessing off the end modulo its legth -TileStoreYTable ENT -]step equ 0 - lup 26 - dw ]step -]step = ]step+{41*2} - --^ -]step equ 0 - lup 26 - dw ]step -]step = ]step+{41*2} - --^ - -; Create a table to look up the "next" column with modulo wraparound. Basically a[i] = i -; and the table is double-length. Use constant offsets to pick an amount to advance -NextCol -]step equ 0 - lup 41 - dw ]step -]step = ]step+2 - --^ -]step equ 0 - lup 41 - dw ]step -]step = ]step+2 - --^ - -; This is a double-length table that holds the right-edge adresses of the playfield on the physical -; screen. At most, it needs to hold 200 addresses for a full height playfield. It is double-length -; so that code can pick any offset and copy values without needing to check for a wrap-around. If the -; playfield is less than 200 lines tall, then any values after 2 * PLAYFIELD_HEIGHT are undefined. -RTable ds 400 - ds 400 - -; Array of addresses for the banks that hold the blitter. -BlitBuff ENT - dw $5a5a - ds 4*13 - -; The blitter table (BTable) is a double-length table that holds the full 4-byte address of each -; line of the blit fields. We decompose arrays of pointers into separate high and low words so -; that everything can use the same indexing offsets -BTableHigh 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 center the buffer in the bank -]step equ $1800 -BG1YTable lup 208 - dw ]step -]step = ]step+256 - --^ -]step equ 256 - lup 208 - dw ]step -]step = ]step+256 - --^ - -; Repeat -BG1YOffsetTable lup 26 - dw 1,1,1,2,2,2,2,2,1,1,1,0,0,0,0,0 - --^ diff --git a/src/tiles/DirtyTileQueue.s b/src/tiles/DirtyTileQueue.s index b3cb0d2..3e58b58 100644 --- a/src/tiles/DirtyTileQueue.s +++ b/src/tiles/DirtyTileQueue.s @@ -21,11 +21,11 @@ _PushDirtyTile ; alternate entry point if the x-register is already set _PushDirtyTileX - ldal TileStore+TS_DIRTY,x + lda TileStore+TS_DIRTY,x bne :occupied2 inc ; any non-zero value will work - stal TileStore+TS_DIRTY,x ; and is 1 cycle faster than loading a constant value + sta TileStore+TS_DIRTY,x ; and is 1 cycle faster than loading a constant value txa ldx DirtyTileCount ; 4 From 8582ecb3208f9e58b6df65f0913912662132c235 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sun, 22 May 2022 21:57:52 -0500 Subject: [PATCH 21/82] Fix weird search/replace bug --- src/blitter/Blitter.s | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/blitter/Blitter.s b/src/blitter/Blitter.s index 4b149bc..0c5b4ad 100644 --- a/src/blitter/Blitter.s +++ b/src/blitter/Blitter.s @@ -18,7 +18,7 @@ _BltRange dey tya ; Get the address of the line that we want to return from - adc StartY ; and create a pointer to it + adc StartYMod208 ; and create a pointer to it asl tay lda BTableLow,y @@ -27,7 +27,7 @@ _BltRange sta :exit_ptr+2 txa ; get the first line (0 - 199) - adc StartY ; add in the virtual offset (0, 207) -- max value of 406 + adc StartYMod208 ; add in the virtual offset (0, 207) -- max value of 406 asl tax ; this is the offset into the blitter table @@ -88,6 +88,5 @@ stk_save lda #0000 ; load the stack sta [:exit_ptr],y rep #$20 -blt_out plb ; restore the bank rts From d27b10b6fd0e35fd98c9f4ffbda6eadd0097ee7e Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sun, 22 May 2022 23:52:54 -0500 Subject: [PATCH 22/82] Fix nasty RTable initialization bug --- src/Graphics.s | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Graphics.s b/src/Graphics.s index 25166f2..c4d7d3d 100644 --- a/src/Graphics.s +++ b/src/Graphics.s @@ -244,7 +244,7 @@ SetScreenRect sty ScreenHeight ; Save the screen height and lda ScreenY0 ; Calculate the address of the first byte asl ; of the right side of the playfield tax - lda ScreenAddr,x ; This is the address for the left edge of the physical screen + lda ScreenAddr,x ; This is the address for the edge of the physical screen clc adc ScreenX1 dec @@ -252,9 +252,6 @@ SetScreenRect sty ScreenHeight ; Save the screen height and ldx #0 ldy ScreenHeight - jsr :loop - pla ; Reset the address and continue filling in the - ldy ScreenHeight ; second half of the table :loop clc sta RTable,x adc #160 @@ -263,6 +260,16 @@ SetScreenRect sty ScreenHeight ; Save the screen height and dey bne :loop + ldy ScreenHeight + pla ; Reset the address and continue filling in the +:loop2 clc + sta RTable,x + adc #160 + inx + inx + dey + bne :loop2 + ; Calculate the screen locations for each tile corner lda ScreenY0 ; Calculate the address of the first byte From 9da3616c1d7649a4c28c5390eb955523f334b126 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sun, 22 May 2022 23:54:47 -0500 Subject: [PATCH 23/82] Start working to bring sprites rendering back into the pipeline --- macros/GTE.Macs.s | 39 +++++++++++++++++++--------- src/Defs.s | 2 ++ src/Memory.s | 6 ++--- src/Render.s | 2 +- src/Sprite.s | 5 ++-- src/Tool.s | 59 ++++++++++++++++++++++++++++++++++++++++++ src/blitter/Blitter.s | 5 ++++ src/blitter/SCB.s | 4 +-- src/blitter/Vert.s | 9 ++++--- src/static/TileStore.s | 7 ++++- 10 files changed, 112 insertions(+), 26 deletions(-) diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s index abc3758..2932fcf 100644 --- a/macros/GTE.Macs.s +++ b/macros/GTE.Macs.s @@ -4,38 +4,53 @@ GTEToolNum equ $A0 _GTEBootInit MAC - UserTool $100+GTEToolNum + UserTool $0100+GTEToolNum <<< _GTEStartUp MAC - UserTool $200+GTEToolNum + UserTool $0200+GTEToolNum <<< _GTEShutDown MAC - UserTool $300+GTEToolNum + UserTool $0300+GTEToolNum <<< _GTEVersion MAC - UserTool $400+GTEToolNum + UserTool $0400+GTEToolNum <<< _GTEReset MAC - UserTool $500+GTEToolNum + UserTool $0500+GTEToolNum <<< _GTEStatus MAC - UserTool $600+GTEToolNum + UserTool $0600+GTEToolNum <<< _GTEReadControl MAC - UserTool $900+GTEToolNum + UserTool $0900+GTEToolNum <<< _GTESetScreenMode MAC - UserTool $A00+GTEToolNum + UserTool $0A00+GTEToolNum <<< _GTESetTile MAC - UserTool $B00+GTEToolNum + UserTool $0B00+GTEToolNum <<< _GTESetBG0Origin MAC - UserTool $C00+GTEToolNum + UserTool $0C00+GTEToolNum <<< _GTERender MAC - UserTool $D00+GTEToolNum + UserTool $0D00+GTEToolNum <<< _GTELoadTileSet MAC - UserTool $E00+GTEToolNum + UserTool $0E00+GTEToolNum <<< +_GTECreateSpriteStamp MAC + UserTool $0F00+GTEToolNum + <<< +_GTEAddSprite MAC + UserTool $1000+GTEToolNum + <<< +_GTEMoveSprite MAC + UserTool $1100+GTEToolNum + <<< +_GTEUpdateSprite MAC + UserTool $1200+GTEToolNum + <<< +_GTERemoveSprite MAC + UserTool $1300+GTEToolNum + <<< \ No newline at end of file diff --git a/src/Defs.s b/src/Defs.s index 6c5070c..31823ef 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -238,3 +238,5 @@ Timers EXT DefaultPalette EXT ScreenModeWidth EXT ScreenModeHeight EXT +_SpriteBits EXT +_SpriteBitsNot EXT diff --git a/src/Memory.s b/src/Memory.s index 6a05700..c4c449d 100644 --- a/src/Memory.s +++ b/src/Memory.s @@ -97,6 +97,7 @@ InitMemory lda EngineMode sta BTableHigh+]step+{208*2},x ; 16 lines per bank ]step equ ]step+2 --^ + lda BlitBuff,y sta BTableLow,x sta BTableLow+{208*2},x @@ -110,10 +111,12 @@ InitMemory lda EngineMode --^ txa + clc adc #16*2 ; move to the next chunk of BTableHigh and BTableLow tax tya + clc adc #4 ; move to the next bank address tay cmp #4*13 @@ -153,9 +156,6 @@ InitMemory lda EngineMode :exit rts -;Buff00 ds 4 -;Buff01 ds 4 - ; Bank allocator (for one full, fixed bank of memory. Can be immediately deferenced) AllocOneBank PushLong #0 diff --git a/src/Render.s b/src/Render.s index d86fff1..6adb306 100644 --- a/src/Render.s +++ b/src/Render.s @@ -74,7 +74,7 @@ _Render ; ldy ScreenHeight ; jsr _BltSCB - lda StartY ; Restore the fields back to their original state + lda StartYMod208 ; Restore the fields back to their original state ldx ScreenHeight jsr _RestoreBG0Opcodes diff --git a/src/Sprite.s b/src/Sprite.s index 506ed77..f23c77b 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -117,7 +117,6 @@ _AddSprite sta _Sprites+SPRITE_X,x ; X coordinate jsr _PrecalcAllSpriteInfo ; Cache sprite property values (simple stuff) -; jsr _DrawSpriteSheet ; Render the sprite into internal space ; Mark the dirty bit to indicate that the active sprite list needs to be rebuilt in the next ; render call @@ -535,7 +534,7 @@ _DoPhase2 ; can be drawn to the screen. This routine is also responsible for setting the specific ; VBUFF address for each sprite's tile sheet position - jmp _MarkDirtySprite +; jmp _MarkDirtySprite :out rts @@ -867,7 +866,7 @@ _RemoveSprite ; Update the sprite's flags. We do not allow the size of a sprite to be changed. That requires ; the sprite to be removed and re-added. ; -; A = Sprite ID +; A = Sprite slot ; X = New Sprite Flags ; Y = New Sprite Stamp Address _UpdateSprite diff --git a/src/Tool.s b/src/Tool.s index 86b28ea..e528672 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -51,6 +51,7 @@ _CallTable adrl _TSSetBG0Origin-1 adrl _TSRender-1 adrl _TSLoadTileSet-1 + adrl _TSCreateSpriteStamp-1 _CTEnd ; Helper function to set the data back to the toolset default @@ -222,6 +223,8 @@ xPos equ FirstParam+2 lda xPos,s jsr _SetBG0XPos lda yPos,s + bpl *+5 + lda #0 jsr _SetBG0YPos _TSExit #0;#4 @@ -245,6 +248,60 @@ TSPtr equ FirstParam _TSExit #0;#4 +; CreateSpriteStamp(spriteId: Word, vbuffAddr: Word) +_TSCreateSpriteStamp +:vbuff equ FirstParam +:spriteId equ FirstParam+2 + + _TSEntry + + lda :vbuff,s + tay + lda :spriteId,s + jsr _CreateSpriteStamp + + _TSExit #0;#4 + +_TSAddSprite +:spriteSlot equ FirstParam+0 +:spriteY equ FirstParam+2 +:spriteX equ FirstParam+4 +:spriteId equ FirstParam+6 + + _TSEntry + + lda :spriteY,s + and #$00FF + xba + sta :spriteY,s + lda :spriteX,s + and #$00FF + ora :spriteY,s + tay + + lda :spriteSlot,s + tax + + lda :spriteId,s + jsr _AddSprite + + _TSExit #0;#8 + +_TSUpdateSprite +:vbuff equ FirstParam+0 +:spriteFlags equ FirstParam+2 +:spriteSlot equ FirstParam+4 + _TSEntry + + lda :spriteFlags,s + tax + lda :vbuff,s + tay + lda :spriteSlot,s + jsr _UpdateSprite + + _TSExit #0;#6 + ; Insert the GTE code put Math.s @@ -253,6 +310,8 @@ TSPtr equ FirstParam put Timer.s put Graphics.s put Tiles.s + put Sprite.s + put SpriteRender.s put Render.s put tiles/DirtyTileQueue.s put tiles/FastRenderer.s diff --git a/src/blitter/Blitter.s b/src/blitter/Blitter.s index 0c5b4ad..44d5f36 100644 --- a/src/blitter/Blitter.s +++ b/src/blitter/Blitter.s @@ -51,6 +51,11 @@ _BltRange lda #FULL_RETURN ; this is the offset of the return code sta [:exit_ptr],y ; patch out the low byte of the JMP/JML +; lda StartYMod208 +; cmp #63 +; bne *+4 +; brk $40 + ; Now we need to set up the Bank, Stack Pointer and Direct Page registers for calling into ; the code field diff --git a/src/blitter/SCB.s b/src/blitter/SCB.s index f43e02c..361018a 100644 --- a/src/blitter/SCB.s +++ b/src/blitter/SCB.s @@ -28,10 +28,10 @@ _BltSCB lda SCBArrayPtr+2 bpl :bind_to_bg0 - lda BG1StartY + lda BG1StartYMod208 bra :bind_to_bg1 :bind_to_bg0 - lda StartY + lda StartYMod208 :bind_to_bg1 clc adc SCBArrayPtr diff --git a/src/blitter/Vert.s b/src/blitter/Vert.s index 0679cd3..042cce3 100644 --- a/src/blitter/Vert.s +++ b/src/blitter/Vert.s @@ -91,8 +91,9 @@ _ApplyBG0YPos sta :lines_left jne :loop - plb + +:out rts ; Unrolled copy routine to move RTable intries into STK_ADDR position. @@ -145,7 +146,7 @@ CopyRTableToStkAddr :x14 ldal RTable+26,x sta STK_ADDR+$D000,y :x13 ldal RTable+24,x - sta: STK_ADDR+$C000,y + sta STK_ADDR+$C000,y :x12 ldal RTable+22,x sta STK_ADDR+$B000,y :x11 ldal RTable+20,x @@ -153,7 +154,7 @@ CopyRTableToStkAddr :x10 ldal RTable+18,x sta STK_ADDR+$9000,y :x09 ldal RTable+16,x - sta: STK_ADDR+$8000,y + sta STK_ADDR+$8000,y :x08 ldal RTable+14,x sta STK_ADDR+$7000,y :x07 ldal RTable+12,x @@ -161,7 +162,7 @@ CopyRTableToStkAddr :x06 ldal RTable+10,x sta STK_ADDR+$5000,y :x05 ldal RTable+08,x - sta: STK_ADDR+$4000,y + sta STK_ADDR+$4000,y :x04 ldal RTable+06,x sta STK_ADDR+$3000,y :x03 ldal RTable+04,x diff --git a/src/static/TileStore.s b/src/static/TileStore.s index 7f65abb..ec7ff57 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -308,7 +308,6 @@ RTable ENT ; Array of addresses for the banks that hold the blitter. BlitBuff ENT - dw $5a5a ds 4*13 ; The blitter table (BTable) is a double-length table that holds the full 4-byte address of each @@ -374,5 +373,11 @@ ScreenModeWidth ENT ScreenModeHeight ENT dw 200,192,200,176,160,160,160,128,144,192,102,1 +; Convert sprite index to a bit position +_SpriteBits ENT + dw $0001,$0002,$0004,$0008,$0010,$0020,$0040,$0080,$0100,$0200,$0400,$0800,$1000,$2000,$4000,$8000 +_SpriteBitsNot ENT + dw $FFFE,$FFFD,$FFFB,$FFF7,$FFEF,$FFDF,$FFBF,$FF7F,$FEFF,$FDFF,$FBFF,$F7FF,$EFFF,$DFFF,$BFFF,$7FFF + blt_return stk_save \ No newline at end of file From 5577105be8f63c9baf72c8540df3199f6aa7c52e Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 23 May 2022 15:18:34 -0500 Subject: [PATCH 24/82] Complete unrolled loop for clearing sprites from the tile store --- src/Defs.s | 3 +- src/Render.s | 2 +- src/Sprite.s | 341 +++++++++++++++++++------------------------------- src/Sprite2.s | 4 - src/Tool.s | 2 - 5 files changed, 135 insertions(+), 217 deletions(-) diff --git a/src/Defs.s b/src/Defs.s index 31823ef..77479c7 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -94,8 +94,9 @@ UserId equ 112 ; Memory manager user Id to use ToolNum equ 114 ; Tool number assigned to us LastKey equ 116 LastTick equ 118 +ForceSpriteFlag equ 120 -Next equ 120 +Next equ 122 activeSpriteList equ 128 ; 32 bytes for the active sprite list (can persist across frames) ; tiletmp equ 178 ; 16 bytes of temp storage for the tile renderers diff --git a/src/Render.s b/src/Render.s index 6adb306..fe62c45 100644 --- a/src/Render.s +++ b/src/Render.s @@ -28,7 +28,7 @@ _Render jsr _ApplyBG0XPosPre ; jsr _ApplyBG1XPosPre -; jsr _RenderSprites ; Once the BG0 X and Y positions are committed, update sprite data + 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 diff --git a/src/Sprite.s b/src/Sprite.s index f23c77b..d031a9b 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -132,11 +132,39 @@ _AddSprite rts +; Macro to make the unrolled loop more concise +; +; The macro +; +; 1. Load the tile store address from a fixed offset +; 2. Clears the sprite bit from the TS_SPRITE_FLAG location +; 3. Checks if the tile is dirty and marks it +; 4. If the tile was dirty, save the tile store address to be added to the DirtyTiles list later +TSClearSprite mac + ldy TileStoreLookup+]1,x + + lda TileStore+TS_SPRITE_FLAG,y + and tmp0 + sta TileStore+TS_SPRITE_FLAG,y + + lda TileStore+TS_DIRTY,y + bne next + + inc + sta TileStore+TS_DIRTY,y + phy +next + <<< + ; Alternate implementation that uses the TS_COVERAGE_SIZE and TS_LOOKUP_INDEX properties to ; load the old values directly from the TileStoreLookup table, rather than caching them. ; This is more efficient, because the work in MarkDirtySprite is independent of the ; sprite size and, by inlining the _PushDirtyTile logic, we can save a fair amount of overhead -_ClearSpriteFromTileStore2 +_ClearSpriteFromTileStore + tsc + sta tmp1 ; We use the stack as a counter + lda _SpriteBitsNot,y ; Cache this value in a direct page location + sta tmp0 ldx _Sprites+TS_COVERAGE_SIZE,y jmp (csfts_tbl,x) csfts_tbl dw csfts_1x1,csfts_1x2,csfts_1x3,csfts_out @@ -144,216 +172,112 @@ csfts_tbl dw csfts_1x1,csfts_1x2,csfts_1x3,csfts_out dw csfts_3x1,csfts_3x2,csfts_3x3,csfts_out dw csfts_out,csfts_out,csfts_out,csfts_out -; Just a single value to clear and add to the dirty tile list +csfts_3x3 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 2 + TSClearSprite 4 + TSClearSprite 1*{TS_LOOKUP_SPAN*2} + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+4 + TSClearSprite 2*{TS_LOOKUP_SPAN*2} + TSClearSprite 2*{TS_LOOKUP_SPAN*2}+2 + TSClearSprite 2*{TS_LOOKUP_SPAN*2}+4 + jmp csfts_finish + +csfts_3x2 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 2 + TSClearSprite 1*{TS_LOOKUP_SPAN*2} + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 + TSClearSprite 2*{TS_LOOKUP_SPAN*2} + TSClearSprite 2*{TS_LOOKUP_SPAN*2}+2 + jmp csfts_finish + +csfts_3x1 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 1*{TS_LOOKUP_SPAN*2} + TSClearSprite 2*{TS_LOOKUP_SPAN*2} + jmp csfts_finish + +csfts_2x3 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 2 + TSClearSprite 4 + TSClearSprite 1*{TS_LOOKUP_SPAN*2} + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+4 + jmp csfts_finish + +csfts_2x2 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 2 + TSClearSprite 1*{TS_LOOKUP_SPAN*2} + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 + jmp csfts_finish + +csfts_2x1 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 1*{TS_LOOKUP_SPAN*2} + jmp csfts_finish + +csfts_1x3 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 2 + TSClearSprite 4 + jmp csfts_finish + +csfts_1x2 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 2 + jmp csfts_finish + csfts_1x1 ldx _Sprites+TS_LOOKUP_INDEX,y - lda TileStoreLookup,x - tax + TSClearSprite 0 - lda TileStore+TS_SPRITE_FLAG,x - and _SpriteBitsNot,y - sta TileStore+TS_SPRITE_FLAG,x +; Second phase; put all the dirty tiles on the DirtyTiles list +csfts_finish + tsc + eor #$FFFF + sec + adc tmp1 ; Looks weird, but calculates (tmp1 - acc) - lda TileStore+TS_DIRTY,x - bne csfts_1x1_out + tax ; This is 2 * N where N is the number of dirty tiles + ldy DirtyTileCount ; Grab a copy of the old index (for addressing) - inc ; any non-zero value will work - sta TileStore+TS_DIRTY,x ; and is 1 cycle faster than loading a constant value + clc + adc DirtyTileCount ; Add the new items to the list + sta DirtyTileCount - txa - ldx DirtyTileCount - sta DirtyTiles,x - inx - inx - stx DirtyTileCount -csfts_1x2 -csfts_1x3 -csfts_2x1 -csfts_2x3 -csfts_3x1 -csfts_3x2 -csfts_3x3 -csfts_1x1_out - rts + jmp (dtloop,x) +dtloop dw csfts_out, dtloop1, dtloop2, dtloop3 + dw dtloop4, dtloop5, dtloop6, dtloop7 + dw dtloop8, dtloop9, dtloop10, dtloop11 -; This is a more interesting case where the ability to batch things up starts to produce some -; efficiency gains -csfts_2x2 ldx _Sprites+TS_LOOKUP_INDEX,y ; Get the address of the old top-left corner - tay - ldx TileStoreLookup,y - - lda TileStore+TS_SPRITE_FLAG,x - and _SpriteBits - sta TileStore+TS_SPRITE_FLAG,x - - lda TileStore+TS_DIRTY,x - beq *+3 - phx - - - ldx TileStoreLookup+2,y - - lda TileStore+TS_SPRITE_FLAG,x - and _SpriteBits - sta TileStore+TS_SPRITE_FLAG,x - - lda TileStore+TS_DIRTY,x - beq *+3 - phx - - - ldx TileStoreLookup+TS_LOOKUP_SPAN,y - - lda TileStore+TS_SPRITE_FLAG,x - and _SpriteBits - sta TileStore+TS_SPRITE_FLAG,x - - lda TileStore+TS_DIRTY,x - beq *+3 - phx - - - ldx TileStoreLookup+TS_LOOKUP_SPAN+2,y - - lda TileStore+TS_SPRITE_FLAG,x - and _SpriteBits - sta TileStore+TS_SPRITE_FLAG,x - - ldy DirtyTileCount - - lda TileStore+TS_DIRTY,x - beq skip_2x2 - - txa - sta DirtyTiles,y - sta TileStore+TS_DIRTY,x - -skip_2x2 - pla - beq :done1 - sta DirtyTiles+2,x - tay - sta TileStore+TS_DIRTY,y - - pla - beq :done2 - sta DirtyTiles+4,x - tay - sta TileStore+TS_DIRTY,y - - pla - beq :done3 - sta DirtyTiles+6,x - tay - sta TileStore+TS_DIRTY,y - -; Maximum number of dirty tiles reached. Just fall through. - - pla - txa - adc #8 - sta DirtyTileCount - rts -:done3 - txa - adc #6 - sta DirtyTileCount - rts -:done2 - txa - adc #4 - sta DirtyTileCount - rts -:done1 - inx - inx - stx DirtyTileCount - - rts - - - - lda _SpriteBitsNot,y ; Cache the bit value for this sprite - - ldy TileStoreLookup,x ; Get the tile store offset - - - and TileStore+TS_SPRITE_FLAG,y - sta TileStore+TS_SPRITE_FLAG,y +dtloop11 pla + sta DirtyTiles+20,y +dtloop10 pla + sta DirtyTiles+18,y +dtloop9 pla + sta DirtyTiles+16,y +dtloop8 pla + sta DirtyTiles+14,y +dtloop7 pla + sta DirtyTiles+12,y +dtloop6 pla + sta DirtyTiles+10,y +dtloop5 pla + sta DirtyTiles+8,y +dtloop4 pla + sta DirtyTiles+6,y +dtloop3 pla + sta DirtyTiles+4,y +dtloop2 pla + sta DirtyTiles+2,y +dtloop1 pla + sta DirtyTiles+0,y csfts_out rts -; Run through the list of tile store offsets that this sprite was last drawn into and mark -; those tiles as dirty. The largest number of tiles that a sprite could possibly cover is 20 -; (an unaligned 4x3 sprite), covering a 5x4 area of play field tiles. -; -; Y register = sprite record index -_CSFTS_Out rts -_ClearSpriteFromTileStore -; ldx _Sprites+TILE_STORE_ADDR_1,y -; beq _CSFTS_Out -; ldal TileStore+TS_SPRITE_FLAG,x ; Clear the bit in the bit field. This seems wasteful, but -; and _SpriteBitsNot,y ; there is no indexed form of TSB/TRB and caching the value in -; stal TileStore+TS_SPRITE_FLAG,x ; a direct page location, only saves 1 or 2 cycles per and costs 10. -; jsr _PushDirtyTileX - -; ldx _Sprites+TILE_STORE_ADDR_2,y -; beq _CSFTS_Out -; ldal TileStore+TS_SPRITE_FLAG,x -; and _SpriteBitsNot,y -; stal TileStore+TS_SPRITE_FLAG,x -; jsr _PushDirtyTileX - -; ldx _Sprites+TILE_STORE_ADDR_3,y -; beq _CSFTS_Out -; ldal TileStore+TS_SPRITE_FLAG,x -; and _SpriteBitsNot,y -; stal TileStore+TS_SPRITE_FLAG,x -; jsr _PushDirtyTileX - -; ldx _Sprites+TILE_STORE_ADDR_4,y -; beq _CSFTS_Out -; ldal TileStore+TS_SPRITE_FLAG,x -; and _SpriteBitsNot,y -; stal TileStore+TS_SPRITE_FLAG,x -; jsr _PushDirtyTileX - -; ldx _Sprites+TILE_STORE_ADDR_5,y -; beq :out -; ldal TileStore+TS_SPRITE_FLAG,x -; and _SpriteBitsNot,y -; stal TileStore+TS_SPRITE_FLAG,x -; jsr _PushDirtyTileX - -; ldx _Sprites+TILE_STORE_ADDR_6,y -; beq :out -; ldal TileStore+TS_SPRITE_FLAG,x -; and _SpriteBitsNot,y -; stal TileStore+TS_SPRITE_FLAG,x -; jsr _PushDirtyTileX - -; ldx _Sprites+TILE_STORE_ADDR_7,y -; beq :out -; ldal TileStore+TS_SPRITE_FLAG,x -; and _SpriteBitsNot,y -; stal TileStore+TS_SPRITE_FLAG,x -; jsr _PushDirtyTileX - -; ldx _Sprites+TILE_STORE_ADDR_8,y -; beq :out -; ldal TileStore+TS_SPRITE_FLAG,x -; and _SpriteBitsNot,y -; stal TileStore+TS_SPRITE_FLAG,x -; jsr _PushDirtyTileX - -; ldx _Sprites+TILE_STORE_ADDR_9,y -; beq :out -; ldal TileStore+TS_SPRITE_FLAG,x -; and _SpriteBitsNot,y -; stal TileStore+TS_SPRITE_FLAG,x -; jmp _PushDirtyTileX - -:out rts - ; This function looks at the sprite list and renders the sprite plane data into the appropriate ; tiles in the code field. There are a few phases to this routine. The assumption is that ; any sprite that needs to be re-drawn has been marked as DIRTY or DAMAGED. @@ -437,7 +361,7 @@ phase1 dw :phase1_0 ; tile store locations to the dirty tile list. _DoPhase1 lda _Sprites+SPRITE_STATUS,y - ora forceSpriteFlag + ora ForceSpriteFlag bit #SPRITE_STATUS_MOVED+SPRITE_STATUS_REMOVED beq :no_clear jsr _ClearSpriteFromTileStore @@ -520,7 +444,7 @@ phase2 dw :phase2_0 _DoPhase2 lda _Sprites+SPRITE_STATUS,y beq :out ; If phase 1 marked us as empty, do nothing - ora forceSpriteFlag + ora ForceSpriteFlag and #SPRITE_STATUS_ADDED+SPRITE_STATUS_MOVED+SPRITE_STATUS_UPDATED beq :out @@ -580,7 +504,6 @@ RebuildSpriteArray stx ActiveSpriteCount rts -forceSpriteFlag ds 2 _RenderSprites ; Check to see if any sprites have been added or removed. If so, then we regenerate the active @@ -617,7 +540,7 @@ _RenderSprites ; occupies and old locations that it no longer covers. It's possible that just testing ; for equality would be the easiest win to know when we can skip everything. - stz forceSpriteFlag + stz ForceSpriteFlag lda StartX cmp OldStartX bne :force_update @@ -628,7 +551,7 @@ _RenderSprites :force_update lda #SPRITE_STATUS_MOVED - sta forceSpriteFlag + sta ForceSpriteFlag :no_change ; Dispatch to the first phase of rendering the sprites. By pre-building the list, we know exactly @@ -636,12 +559,12 @@ _RenderSprites ; of an iterating variable ldx ActiveSpriteCount - jmp (phase1,x) +; jmp (phase1,x) phase1_rtn ; Dispatch to the second phase of rendering the sprites. ldx ActiveSpriteCount - jmp (phase2,x) +; jmp (phase2,x) phase2_rtn rts diff --git a/src/Sprite2.s b/src/Sprite2.s index 404ade4..d150879 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -744,7 +744,3 @@ skip ;_SpriteWidth dw 4,8,4,8 ;_SpriteWidthMinus1 dw 3,7,3,7 ;_SpriteCols dw 1,2,1,2 - -; Convert sprite index to a bit position -_SpriteBits dw $0001,$0002,$0004,$0008,$0010,$0020,$0040,$0080,$0100,$0200,$0400,$0800,$1000,$2000,$4000,$8000 -_SpriteBitsNot dw $FFFE,$FFFD,$FFFB,$FFF7,$FFEF,$FFDF,$FFBF,$FF7F,$FEFF,$FDFF,$FBFF,$F7FF,$EFFF,$DFFF,$BFFF,$7FFF diff --git a/src/Tool.s b/src/Tool.s index e528672..711b6a8 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -223,8 +223,6 @@ xPos equ FirstParam+2 lda xPos,s jsr _SetBG0XPos lda yPos,s - bpl *+5 - lda #0 jsr _SetBG0YPos _TSExit #0;#4 From 7909113a976d0ad2e1a5a18f10b44ab727570d46 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Thu, 26 May 2022 19:36:40 -0500 Subject: [PATCH 25/82] Checkpoint --- demos/tool/App.Main.s | 29 +- src/Defs.s | 6 +- src/Render.s | 8 + src/Sprite.s | 508 +++++++++++----------------- src/Sprite2.s | 668 ++++++------------------------------- src/Tool.s | 1 + src/static/TileStore.s | 8 +- src/static/TileStoreDefs.s | 48 +-- 8 files changed, 363 insertions(+), 913 deletions(-) diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 068c9ab..8c3e922 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -39,6 +39,26 @@ ScreenY equ 2 pea #TSZelda _GTELoadTileSet +; Create stamps for the sprites we are going to use +HERO_SPRITE_1 equ SPRITE_16X16+1 +HERO_SLOT equ 0 + + pea HERO_SPRITE_1 ; sprinte id + pea VBUFF_SPRITE_START ; vbuff address + _GTECreateSpriteStamp + +; Create sprites + pea HERO_SPRITE_1 ; sprite id + pea #10 ; screen x-position (<256) + pea #8 ; screen y-position (<256) + pea HERO_SLOT ; sprite slot (0 - 15) + _GTEAddSprite + + pea HERO_SLOT ; update the sprite in this slot + pea $0000 ; with these flags (h/v flip) + pea VBUFF_SPRITE_START ; and use this stamp + _GTEUpdateSprite + ; Manually fill in the 41x26 tiles of the TileStore with a test pattern. ldx #0 @@ -71,15 +91,10 @@ ScreenY equ 2 ; Set the origin of the screen - lda #3 - sta ScreenX - lda #10 + stz ScreenX + lda #63 sta ScreenY - pea #3 - pea #10 - _GTESetBG0Origin - ; Very simple actions :evt_loop pha ; space for result, with pattern diff --git a/src/Defs.s b/src/Defs.s index 77479c7..bd81382 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -82,7 +82,7 @@ BG1TileMapPtr equ 86 SCBArrayPtr equ 90 ; Used for palette binding SpriteBanks equ 94 ; Bank bytes for the sprite data and sprite mask -LastRender equ 96 ; Record which reder function was last executed +LastRender equ 96 ; Record which render function was last executed ; gap SpriteMap equ 100 ; Bitmap of open sprite slots. ActiveSpriteCount equ 102 @@ -96,7 +96,8 @@ LastKey equ 116 LastTick equ 118 ForceSpriteFlag equ 120 -Next equ 122 +VBuffArrayPtr equ 122 +SpriteRemovedFlag equ 126 ; Indicate if any sprites were removed this frame activeSpriteList equ 128 ; 32 bytes for the active sprite list (can persist across frames) ; tiletmp equ 178 ; 16 bytes of temp storage for the tile renderers @@ -241,3 +242,4 @@ ScreenModeWidth EXT ScreenModeHeight EXT _SpriteBits EXT _SpriteBitsNot EXT +VBuffArrayAddr EXT diff --git a/src/Render.s b/src/Render.s index fe62c45..46d2437 100644 --- a/src/Render.s +++ b/src/Render.s @@ -20,6 +20,8 @@ ; 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 + stz SpriteRemovedFlag ; If we remove a sprite, then we need to flag a rebuild for the next frame + jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen ; jsr _ApplyBG1YPos @@ -90,6 +92,12 @@ _Render stz DirtyBits stz LastRender ; Mark that a full render was just performed + + lda SpriteRemovedFlag ; If any sprite was removed, set the rebuild flag + beq :no_removal + lda #DIRTY_BIT_SPRITE_ARRAY + sta DirtyBits +:no_removal rts ; The _ApplyTilesFast is the same as _ApplyTiles, but we use the _RenderTileFast subroutine diff --git a/src/Sprite.s b/src/Sprite.s index d031a9b..aea622b 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -31,6 +31,178 @@ InitSprites jsr _CacheSpriteBanks rts + +; _RenderSprites +; +; The function is responsible for updating all of the rendering information based on any changes +; that occured to the sprites on this frame. Sprite handling is one of the most expensive and +; complicated pieces of the rendering pipeline, so these functions are aggressively simplified and +; optimized. +; +; The sprite rendering pipeline is: +; +; 0. Check if any new sprites have been added by testing the DIRTY_BIT_SPRITE_ARRAY. If so, then +; the activeSpriteList (a 32-byte array on the direct page) is rebuilt from the SpriteBits bitmap +; word. +; +; Next, the activeSpriteList is scanned for changes to specific sprites. If the screen has been +; scrolled, then every sprite is considered to have the SPRITE_STATUS_MOVED flag set. +; +; 1. If a sprite is marked as (SPRITE_STATUS_MOVED or SPRITE_STATUS_UPDATED or SPRITE_STATUS_ADDED) and not SPRITE_STATUS_REMOVED +; A. Calculate the TS_COVERAGE_SIZE, TS_LOOKUP_INDEX, and TS_VBUFF_BASE for the sprite +; B. For each tile the sprite overlaps with: +; i. Set its bit in the TileStore's TS_SPRITE_FLAG +; ii. Add the tile to the DirtyTile list +; iii. Set the VBUFF address for the sprite block +; C. If the sprite is not marked as SPRITE_STATUS_ADDED +; i. For each old tile the sprite overlaps with +; a. If it is not marked in the DirtyTile list +; * Clear its bit from the TileStore's TS_SPRITE_FLAG +; * Add the tile to the DirtyTile list +; +; 2. If a sprite is marked as SPRITE_STATUS_REMOVED, then +; A. Clear its bit from the SpriteBits bitmap +; B. For each tile the sprite overlaps with: +; i. Clear its bit from the TileStore's TS_SPRITE_FLAG +; ii. Add the tile to the DirtyTile list +; C. Clear the SPRITE_STATUS flags (work complete) +; +; 3. For each tile on the Dirty Tile list +; A. Place the sprite VBUFF addresses in TS_VBUFF_ADDR_0 through TS_VBUFF_ADDR_3 and set TS_VBUFF_ADDR_COUNT +; +; It is important that this work is done *prior* to any tile map updates so that we can interate over the +; DirtyTile list and *know* that it only contains tiles that are impacted by sprite changes. +_RenderSprites + +; Check to see if any sprites have been added or removed. If so, then we regenerate the active +; sprite list. Since adding and removing sprites is rare, this is a worthwhile tradeoff, because +; there are several places where we want to iterate over the all of the sprites, and having a list +; and not have to constantly load and test the SPRITE_STATUS just to skip unused slots can help +; streamline the code. + + lda #DIRTY_BIT_SPRITE_ARRAY + trb DirtyBits ; clears the flag, if it was set + beq :no_rebuild + jsr RebuildSpriteArray + +:no_rebuild + +; First step is to look at the StartX and StartY values. If the screen has scrolled, then it has +; the same effect as moving all of the sprites. +; +; OPTIMIZATION NOTE: Should check that the sprite actually changes position. If the screen scrolls +; by +X, but the sprite moves by -X (so it's relative position is unchanged), then +; it does NOT need to be marked as dirty. + + stz ForceSpriteFlag + lda StartX + cmp OldStartX + bne :force_update + + lda StartY + cmp OldStartY + beq :no_change + +:force_update + lda #SPRITE_STATUS_MOVED + sta ForceSpriteFlag +:no_change + +; Dispatch to the update process for sprites. By pre-building the list, we know exactly +; how many sprite to process and they are in a contiguous array. So we don't have to keep +; track of an iteration variable + + ldx ActiveSpriteCount + jmp (phase1,x) + +; Implement the logic for updating sprite and tile rendering information. Each iteration of the +; ActiveSpriteCount will call this routine with the Y-register set to the sprite index +tmpY equ tmp15 +tmpA equ tmp14 +_DoPhase1 + lda _Sprites+SPRITE_STATUS,y + ora ForceSpriteFlag + sta tmpA + sty tmpY + +; First step, if a sprite is being removed, then we just have to clear its old tile information +; and mark the tiles it overlapped as dirty. + + bit #SPRITE_STATUS_REMOVED + beq :no_clear + + lda _SpriteBits,y ; Clear from the sprite bitmap + sta SpriteRemovedFlag ; Stick a non-zero value here + trb SpriteMap + + jmp _ClearSpriteFromTileStore ; Clear the tile flags, add to the dirty tile list and done + +; Need to calculate new VBUFF information. The could be reuqired for UPDATED, ADDED or MOVED +; sprites, so we do it unconditionally. +:no_clear + jsr _CalcDirtySprite + +; If the sprite is marked as ADDED, then it does not need to have its old tile locations cleared + lda tmpA + bit #SPRITE_STATUS_ADDED + bne :no_move + jsr _ClearSpriteFromTileStore + ldy tmpY + +; Anything else (MOVED, UPDATED, ADDED) will need to have the VBUFF information updated and the +; current tiles marked for update +:no_move + jmp _MarkDirtySpriteTiles + +; Once all of the sprite values have been calculated, we need to scan the dirty tile list and +; collapse the sprite information down to no more than 4 vbuff references per tile. We used to +; do this on the fly in the renderer, but that required differentiating between tile with and +; without sprites in the core rendering function. My lifting this up, we simplify the core code +; and possible open up some optimization opportunities. +_SetTileStoreVBuffAddrs + + + +; Dispatch table. It's unintersting, so it's tucked out of the way +phase1 dw :phase1_0 + dw :phase1_1,:phase1_2,:phase1_3,:phase1_4 + dw :phase1_5,:phase1_6,:phase1_7,:phase1_8 + dw :phase1_9,:phase1_10,:phase1_11,:phase1_12 + dw :phase1_13,:phase1_14,:phase1_15,:phase1_16 +:phase1_16 ldy activeSpriteList+30 + jsr _DoPhase1 +:phase1_15 ldy activeSpriteList+28 + jsr _DoPhase1 +:phase1_14 ldy activeSpriteList+26 + jsr _DoPhase1 +:phase1_13 ldy activeSpriteList+24 + jsr _DoPhase1 +:phase1_12 ldy activeSpriteList+22 + jsr _DoPhase1 +:phase1_11 ldy activeSpriteList+20 + jsr _DoPhase1 +:phase1_10 ldy activeSpriteList+18 + jsr _DoPhase1 +:phase1_9 ldy activeSpriteList+16 + jsr _DoPhase1 +:phase1_8 ldy activeSpriteList+14 + jsr _DoPhase1 +:phase1_7 ldy activeSpriteList+12 + jsr _DoPhase1 +:phase1_6 ldy activeSpriteList+10 + jsr _DoPhase1 +:phase1_5 ldy activeSpriteList+8 + jsr _DoPhase1 +:phase1_4 ldy activeSpriteList+6 + jsr _DoPhase1 +:phase1_3 ldy activeSpriteList+4 + jsr _DoPhase1 +:phase1_2 ldy activeSpriteList+2 + jsr _DoPhase1 +:phase1_1 ldy activeSpriteList + jsr _DoPhase1 +:phase1_0 jmp _SetTileStoreVBuffAddrs + ; Utility function to calculate the difference in tile positions between a sprite's current ; position and it's previous position. This gets interesting because the number of tiles ; that a sprite covers can change based on the relative alignemen of the sprite with the @@ -134,8 +306,6 @@ _AddSprite ; Macro to make the unrolled loop more concise ; -; The macro -; ; 1. Load the tile store address from a fixed offset ; 2. Clears the sprite bit from the TS_SPRITE_FLAG location ; 3. Checks if the tile is dirty and marks it @@ -149,10 +319,15 @@ TSClearSprite mac lda TileStore+TS_DIRTY,y bne next - inc sta TileStore+TS_DIRTY,y - phy + + tya + ldy DirtyTileCount + sta DirtyTiles,y + iny + iny + sty DirtyTileCount next <<< @@ -161,8 +336,6 @@ next ; This is more efficient, because the work in MarkDirtySprite is independent of the ; sprite size and, by inlining the _PushDirtyTile logic, we can save a fair amount of overhead _ClearSpriteFromTileStore - tsc - sta tmp1 ; We use the stack as a counter lda _SpriteBitsNot,y ; Cache this value in a direct page location sta tmp0 ldx _Sprites+TS_COVERAGE_SIZE,y @@ -172,6 +345,8 @@ csfts_tbl dw csfts_1x1,csfts_1x2,csfts_1x3,csfts_out dw csfts_3x1,csfts_3x2,csfts_3x3,csfts_out dw csfts_out,csfts_out,csfts_out,csfts_out +csfts_out rts + csfts_3x3 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 @@ -182,7 +357,7 @@ csfts_3x3 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 2*{TS_LOOKUP_SPAN*2} TSClearSprite 2*{TS_LOOKUP_SPAN*2}+2 TSClearSprite 2*{TS_LOOKUP_SPAN*2}+4 - jmp csfts_finish + rts csfts_3x2 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 @@ -191,13 +366,13 @@ csfts_3x2 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 TSClearSprite 2*{TS_LOOKUP_SPAN*2} TSClearSprite 2*{TS_LOOKUP_SPAN*2}+2 - jmp csfts_finish + rts csfts_3x1 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 1*{TS_LOOKUP_SPAN*2} TSClearSprite 2*{TS_LOOKUP_SPAN*2} - jmp csfts_finish + rts csfts_2x3 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 @@ -206,260 +381,33 @@ csfts_2x3 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 1*{TS_LOOKUP_SPAN*2} TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+4 - jmp csfts_finish + rts csfts_2x2 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 TSClearSprite 1*{TS_LOOKUP_SPAN*2} TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 - jmp csfts_finish + rts csfts_2x1 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 1*{TS_LOOKUP_SPAN*2} - jmp csfts_finish + rts csfts_1x3 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 TSClearSprite 4 - jmp csfts_finish + rts csfts_1x2 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 - jmp csfts_finish + rts csfts_1x1 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 - -; Second phase; put all the dirty tiles on the DirtyTiles list -csfts_finish - tsc - eor #$FFFF - sec - adc tmp1 ; Looks weird, but calculates (tmp1 - acc) - - tax ; This is 2 * N where N is the number of dirty tiles - ldy DirtyTileCount ; Grab a copy of the old index (for addressing) - - clc - adc DirtyTileCount ; Add the new items to the list - sta DirtyTileCount - - jmp (dtloop,x) -dtloop dw csfts_out, dtloop1, dtloop2, dtloop3 - dw dtloop4, dtloop5, dtloop6, dtloop7 - dw dtloop8, dtloop9, dtloop10, dtloop11 - -dtloop11 pla - sta DirtyTiles+20,y -dtloop10 pla - sta DirtyTiles+18,y -dtloop9 pla - sta DirtyTiles+16,y -dtloop8 pla - sta DirtyTiles+14,y -dtloop7 pla - sta DirtyTiles+12,y -dtloop6 pla - sta DirtyTiles+10,y -dtloop5 pla - sta DirtyTiles+8,y -dtloop4 pla - sta DirtyTiles+6,y -dtloop3 pla - sta DirtyTiles+4,y -dtloop2 pla - sta DirtyTiles+2,y -dtloop1 pla - sta DirtyTiles+0,y - -csfts_out rts - -; This function looks at the sprite list and renders the sprite plane data into the appropriate -; tiles in the code field. There are a few phases to this routine. The assumption is that -; any sprite that needs to be re-drawn has been marked as DIRTY or DAMAGED. -; -; A DIRTY sprite is one that has moved, so it needs to be erased/redrawn in the sprite -; buffer AND the tiles it covers marked for refresh. A DAMAGED sprite shared one or more -; tiles with a DIRTY sprite, so it needs to be redraw in the sprite buffer (but not erased!) -; and its tile do NOT need to be marked for refresh. -; -; In the first phase, we run through the list of dirty sprites and erase them from their -; OLD_VBUFF_ADDR. This clears the sprite plane buffers. We also iterate through the -; TILE_STORE_ADDR_X array and mark all of the tile store location that this sprite had occupied -; as dirty, as well as removing this sprite from the TS_SPRITE_FLAG bitfield. -; -; A final aspect is that any of the sprites indicated in the TS_SPRITE_FLAG are marked to be -; drawn in the next phase (since a portion of their content may have been erased if they overlap) -; -; In the second phase, the sprite is re-drawn into the sprite plane buffers and the appropriate -; Tile Store locations are marked as dirty. It is important to recognize that the sprites themselves -; can be marked dirty, and the underlying tiles in the tile store are independently marked dirty. - -phase1 dw :phase1_0 - dw :phase1_1,:phase1_2,:phase1_3,:phase1_4 - dw :phase1_5,:phase1_6,:phase1_7,:phase1_8 - dw :phase1_9,:phase1_10,:phase1_11,:phase1_12 - dw :phase1_13,:phase1_14,:phase1_15,:phase1_16 - -:phase1_16 - ldy activeSpriteList+30 - jsr _DoPhase1 -:phase1_15 - ldy activeSpriteList+28 - jsr _DoPhase1 -:phase1_14 - ldy activeSpriteList+26 - jsr _DoPhase1 -:phase1_13 - ldy activeSpriteList+24 - jsr _DoPhase1 -:phase1_12 - ldy activeSpriteList+22 - jsr _DoPhase1 -:phase1_11 - ldy activeSpriteList+20 - jsr _DoPhase1 -:phase1_10 - ldy activeSpriteList+18 - jsr _DoPhase1 -:phase1_9 - ldy activeSpriteList+16 - jsr _DoPhase1 -:phase1_8 - ldy activeSpriteList+14 - jsr _DoPhase1 -:phase1_7 - ldy activeSpriteList+12 - jsr _DoPhase1 -:phase1_6 - ldy activeSpriteList+10 - jsr _DoPhase1 -:phase1_5 - ldy activeSpriteList+8 - jsr _DoPhase1 -:phase1_4 - ldy activeSpriteList+6 - jsr _DoPhase1 -:phase1_3 - ldy activeSpriteList+4 - jsr _DoPhase1 -:phase1_2 - ldy activeSpriteList+2 - jsr _DoPhase1 -:phase1_1 - ldy activeSpriteList - jsr _DoPhase1 -:phase1_0 - jmp phase1_rtn - -; If this sprite has been MOVED or REMOVED, then clear its bit from the TS_SPRITE_FLAG in -; all of the tile store locations that it occupied on the previous frame and add those -; tile store locations to the dirty tile list. -_DoPhase1 - lda _Sprites+SPRITE_STATUS,y - ora ForceSpriteFlag - bit #SPRITE_STATUS_MOVED+SPRITE_STATUS_REMOVED - beq :no_clear - jsr _ClearSpriteFromTileStore -:no_clear - -; Check to see if sprite was REMOVED If so, clear the sprite slot status - - lda _Sprites+SPRITE_STATUS,y - bit #SPRITE_STATUS_REMOVED - beq :out - - lda #SPRITE_STATUS_EMPTY ; Mark as empty (zero value) - sta _Sprites+SPRITE_STATUS,y - - lda _SpriteBits,y ; Clear from the sprite bitmap - trb SpriteMap - -:out - rts - -; Second phase takes care of drawing the sprites and marking the tiles that will need to be merged -; with pixel data from the sprite plane -phase2 dw :phase2_0 - dw :phase2_1,:phase2_2,:phase2_3,:phase2_4 - dw :phase2_5,:phase2_6,:phase2_7,:phase2_8 - dw :phase2_9,:phase2_10,:phase2_11,:phase2_12 - dw :phase2_13,:phase2_14,:phase2_15,:phase2_16 - -:phase2_16 - ldy activeSpriteList+30 - jsr _DoPhase2 -:phase2_15 - ldy activeSpriteList+28 - jsr _DoPhase2 -:phase2_14 - ldy activeSpriteList+26 - jsr _DoPhase2 -:phase2_13 - ldy activeSpriteList+24 - jsr _DoPhase2 -:phase2_12 - ldy activeSpriteList+22 - jsr _DoPhase2 -:phase2_11 - ldy activeSpriteList+20 - jsr _DoPhase2 -:phase2_10 - ldy activeSpriteList+18 - jsr _DoPhase2 -:phase2_9 - ldy activeSpriteList+16 - jsr _DoPhase2 -:phase2_8 - ldy activeSpriteList+14 - jsr _DoPhase2 -:phase2_7 - ldy activeSpriteList+12 - jsr _DoPhase2 -:phase2_6 - ldy activeSpriteList+10 - jsr _DoPhase2 -:phase2_5 - ldy activeSpriteList+8 - jsr _DoPhase2 -:phase2_4 - ldy activeSpriteList+6 - jsr _DoPhase2 -:phase2_3 - ldy activeSpriteList+4 - jsr _DoPhase2 -:phase2_2 - ldy activeSpriteList+2 - jsr _DoPhase2 -:phase2_1 - ldy activeSpriteList - jsr _DoPhase2 -:phase2_0 - jmp phase2_rtn - -_DoPhase2 - lda _Sprites+SPRITE_STATUS,y - beq :out ; If phase 1 marked us as empty, do nothing - ora ForceSpriteFlag - and #SPRITE_STATUS_ADDED+SPRITE_STATUS_MOVED+SPRITE_STATUS_UPDATED - beq :out - -; Last thing to do, so go ahead and clear the flags - - lda #SPRITE_STATUS_OCCUPIED - sta _Sprites+SPRITE_STATUS,y - -; Mark the appropriate tiles as dirty and as occupied by a sprite so that the ApplyTiles -; subroutine will combine the sprite data with the tile data into the code field where it -; can be drawn to the screen. This routine is also responsible for setting the specific -; VBUFF address for each sprite's tile sheet position - -; jmp _MarkDirtySprite -:out rts ; Use the blttmp space to build the active sprite list. Since the sprite tiles are not drawn until later, @@ -504,71 +452,6 @@ RebuildSpriteArray stx ActiveSpriteCount rts -_RenderSprites - -; Check to see if any sprites have been added or removed. If so, then we regenerate the active -; sprite list. Since adding and removing sprites is rare, this is a worthwhile tradeoff, because -; there are several places where we want to iterate over the all of the sprites, and having a list -; and not have to constantly load and test the SPRITE_STATUS just to skip unused slots can help -; streamline the code. - - lda #DIRTY_BIT_SPRITE_ARRAY - trb DirtyBits ; clears the flag, if it was set - beq :no_rebuild - jsr RebuildSpriteArray - -:no_rebuild - -; First step is to look at the StartX and StartY values. If the screen has scrolled, then it has -; the same effect as moving all of the sprites. -; -; OPTIMIZATION NOTE: Should check that the sprite actually changes position. If the screen scrolls -; by +X, but the sprite moves by -X (so it's relative position is unchanged), then -; it does NOT need to be marked as dirty. -; -; OPTIMIZATION NOTE: At this point, a decent chunk of per-tile time is spent cupdating the sprite flgas -; for a given TileStore entry. When a sprite needs to be redrawn (such as when the -; screen scrolls), the code marks every tile the sprite was on as no longer occupied -; and then marks the occupied tiles. While simple, this is very redundent when the -; screen in scrolling slowly since it is very likely that the same sprite covers the -; exact same tiles. Each pair of markings requires 35 cycles, so a basic 16x16 sprite -; could save >300 cycles per frame. With 4 or 5 sprites on screen, the saving passes -; our 1% threshold for useful optimizations. -; -; Since we cache the tile location and effective sprite coverage, we need a fast -; way to compare the old and new positions and get a list of the new tiles the sprite -; occupies and old locations that it no longer covers. It's possible that just testing -; for equality would be the easiest win to know when we can skip everything. - - stz ForceSpriteFlag - lda StartX - cmp OldStartX - bne :force_update - - lda StartY - cmp OldStartY - beq :no_change - -:force_update - lda #SPRITE_STATUS_MOVED - sta ForceSpriteFlag -:no_change - -; Dispatch to the first phase of rendering the sprites. By pre-building the list, we know exactly -; how many sprite to process and they are in a contiguous array. So we on't have to keep track -; of an iterating variable - - ldx ActiveSpriteCount -; jmp (phase1,x) -phase1_rtn - -; Dispatch to the second phase of rendering the sprites. - ldx ActiveSpriteCount -; jmp (phase2,x) -phase2_rtn - - rts - ; _GetTileAt ; ; Given a relative playfield coordinate [0, ScreenWidth), [0, ScreenHeight) return the @@ -679,13 +562,14 @@ _PrecalcAllSpriteInfo ; and #$3E00 xba and #$0006 - tay - lda _Sprites+VBUFF_ADDR,x - clc - adc _stamp_step,y - sta _Sprites+SPRITE_DISP,x -; Set the + txy ; swap X/Y for this... + tax + lda _Sprites+VBUFF_ADDR,y + clc + adcl _stamp_step,x + sta _Sprites+SPRITE_DISP,y + tyx ; Set the sprite's width and height lda #4 diff --git a/src/Sprite2.s b/src/Sprite2.s index d150879..13996e9 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -38,15 +38,28 @@ VBuffOrigin equ tmp11 ; ... ; ; For the Y-coordinate, we just use "mod 8" instead of "mod 4" +; +; When this subroutine is completed, the following values will be calculated +; +; _Sprites+TS_COVERAGE_SIZE : The number of horizontal and vertical playfield tiles covered by the sprite +; _Sprites+TS_LOOKUP_INDEX : TileStore index of the upper-left corner of the sprite +; _Sprites+TS_VBUFF_BASE : Address of the top-left corner of the sprite in the VBUFF sprite stamp memory +; mdsOut2 lda #6 ; Pick a value for a 0x0 tile sprite sta _Sprites+TS_COVERAGE_SIZE,y ; zero the list of tile store addresses rts -_MarkDirtySprite +_CalcDirtySprite lda _Sprites+IS_OFF_SCREEN,y ; Check if the sprite is visible in the playfield bne mdsOut2 +; Copy the current values into the old value slots + lda _Sprites+TS_COVERAGE_SIZE,y + sta _Sprites+OLD_TS_COVERAGE_SIZE,y + lda _Sprites+TS_LOOKUP_INDEX,y + sta _Sprites+OLD_TS_LOOKUP_INDEX,y + ; Add the first visible row of the sprite to the Y-scroll offset to find the first line in the ; code field that needs to be drawn. The range of values is 0 to 199+207 = [0, 406] @@ -77,10 +90,10 @@ _MarkDirtySprite and #$0018 sta AreaIndex - txa + txa ; Get the verical offset in the VBUFF memory asl tax - lda :vbuff_mul,x + ldal :vbuff_mul,x sta tmp0 ; Add the horizontal position to the horizontal offset to find the first column in the @@ -91,12 +104,10 @@ _MarkDirtySprite adc StartXMod164 tax and #$FFFC - lsr -; sta ColLeft ; Even numbers from [0, 160] (80 elements) + lsr ; Even numbers from [0, 160] (80 elements) adc RowTop sta _Sprites+TS_LOOKUP_INDEX,y ; This is the index into the TileStoreLookup table - ; Calculate the final address of the sprite data in the stamp buffer. We have to move earlier ; in the buffer based on the horizontal offset and move up for each vertical offset. @@ -111,14 +122,7 @@ _MarkDirtySprite eor #$FFFF ; A = -X - 1 sec ; C = 1 adc _Sprites+SPRITE_DISP,y ; A = SPRITE_DISP + (-X - 1) + 1 = SPRITE_DISP - X - - sta VBuffOrigin ; this is the final (adjusted) origin for this sprite - -; Load the base address of the appropriate TS_VBUFF_? offset for this sprite index and -; store it as an indirect address. - - lda _Sprites+TS_VBUFF_BASE_ADDR,y - sta tmp0 + sta _Sprites+TS_VBUFF_BASE,y ; We know the starting corner of the TileStore. Now, we need to figure out now many tiles ; the sprite covers. This is a function of the sprite's width and height and the specific @@ -136,101 +140,26 @@ _MarkDirtySprite ; Then, when we need to erase we can just lookup the values in the TileStoreLookup table. sta _Sprites+TS_COVERAGE_SIZE,y - tax -; lda TileStoreBaseIndex -; sta _Sprites+TS_LOOKUP_INDEX,y - -; Jump to the appropriate marking routine - - jmp (:mark,x) - mdsOut rts -;_MarkDirtySprite -; -; lda #0 -; sta _Sprites+TILE_STORE_ADDR_1,y ; Clear this sprite's dirty tile list in case of an early exit -; lda _SpriteBits,y ; Cache its bit flag to mark in the tile slots -; sta SpriteBit -; lda _Sprites+IS_OFF_SCREEN,y ; Check if the sprite is visible in the playfield -; bne mdsOut -; At this point we know that we have to update the tiles that overlap the sprite's rectangle defined -; by (Top, Left), (Bottom, Right). First, calculate the row and column in the TileStore that -; encloses the top-left on-screen corner of the sprite +; NOTE: The VBuffArrayAddr lookup 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 VBuffArrayAddr,y ; Get the base address for the TileStore VBuff array for this sprite + sta VBuffArrayPtr -; clc -; lda _Sprites+SPRITE_CLIP_TOP,y -; adc StartYMod208 ; Adjust for the scroll offset -; tax ; cache -; cmp #208 ; check if we went too far positive -; bcc *+5 -; sbc #208 -; lsr -; lsr ; This is the row in the Tile Store for top-left corner of the sprite -; and #$FFFE ; Store the value pre-multiplied by 2 for indexing in the :mark_R_C routines -; sta RowTop + lda _Sprites+TS_VBUFF_BASE,y ; This is the final upper-left cornder for this frame + sta VBuffOrigin -; Next, calculate how many tiles are covered by the sprite. This uses the table at the top of this function, but -; the idea is that for every increment of StartX or StartY, that can shift the sprite into the next tile, up to -; a maximum of mod 4 / mod 8. So the effective width of a sprite is (((StartX + Clip_Left) mod 4) + Clip_Width) / 4 + lda _SpriteBits,y + sta SpriteBit -; txa -; and #$0007 -; sta tmp0 ; save to adjust sprite origin + clc + ldx _Sprites+TS_COVERAGE_SIZE,y + jmp (:mark,x) -; lda _Sprites+SPRITE_CLIP_HEIGHT,y ; Nominal value between 0 and 16+7 = 23 = 10111 -; dec -; clc -; adc tmp0 -; and #$0018 -; sta AreaIndex - -; Repeat to get the same information for the columns - -; clc -; lda _Sprites+SPRITE_CLIP_LEFT,y -; adc StartXMod164 -; tax -; cmp #164 -; bcc *+5 -; sbc #164 -; lsr -; and #$FFFE ; Same pre-multiply by 2 for later -; sta ColLeft - -; txa -; and #$0003 -; sta tmp1 ; save to adjust sprite origin; - -; lda _Sprites+SPRITE_CLIP_WIDTH,y ; max width = 8 = 0x08 -; dec -; clc -; adc tmp1 -; lsr ; max value = 4 = 0x04 -; and #$0006 -; ora AreaIndex -; sta AreaIndex - -; Calculate the modified origin address for the sprite. We need to look at the sprite flip bits -; to determine which of the four sprite stamps is the correct one to use. Then, offset that origin -; based on the (x, y) and (startx, starty) positions. - -; lda _Sprites+SPRITE_DISP,y ; Get the sprite's base display address -; sec -; sbc tmp1 ; Subtract the horizontal within-tile displacement -; asl tmp0 -; ldx tmp0 -; sec -; sbc :vbuff_mul,x -; sta VBuffOrigin -; lda #^TileStore -; sta tmp1 - -; Dispatch to cover the tiles - -; ldx AreaIndex -; jmp (:mark,x) :mark dw :mark1x1,:mark1x2,:mark1x3,mdsOut dw :mark2x1,:mark2x2,:mark2x3,mdsOut dw :mark3x1,:mark3x2,:mark3x3,mdsOut @@ -238,509 +167,110 @@ mdsOut rts :vbuff_mul dw 0,52,104,156,208,260,312,364 -; Dispatch to the calculated sizing - -; Begin a list of subroutines to cover all of the valid sprite size combinations. This is all unrolled code, -; mainly to be able to do an unrolled fill of the TILE_STORE_ADDR_X values. Thus, it's important that the clipping -; function does its job properly since it allows us to save a lot of time here. +; Pair of macros to make the unrolled loop more concise ; -; These functions are a trade off of being composable versus fast. Having to pay for multiple JSR/RTS invocations -; in the hot sprite path isn't great, but we're at a point of diminishing returns. +; 1. Load the tile store address from a fixed offset +; 2. Set the sprite bit from the TS_SPRITE_FLAG location +; 3. Checks if the tile is dirty and marks it +; 4. If the tile was dirty, save the tile store address to be added to the DirtyTiles list later +; 5. Sets the VBUFF address for the current sprite slot ; -; There *might* be some speed gained by pushing a list of :mark_R_C addressed onto the stack in the clipping routing -; and dispatching that way, but probably not... +; The second macro is the same as the first, but the VBUFF calculation is moved up so that the value +; from the previous step can be reused and save a load every other step. +TSSetSprite mac + ldy TileStoreLookup+]1,x -:mark1x1_v2 - - tax ; Get the TileStoreBaseIndex - - ldy TileStoreLookup,x ; Get the offset into the TileStore for this tile - - lda SpriteBit ; Mark this tile as having this sprite + lda SpriteBit ora TileStore+TS_SPRITE_FLAG,y sta TileStore+TS_SPRITE_FLAG,y lda VBuffOrigin - sta (tmp0),y ; Fill in the slot for this sprite on this tile + adc ]2 + sta [tmp0],y ; This is *very* carefully constructed.... - lda TileStore+TS_DIRTY,y ; If this tile is not yet marked dirty, mark it - bne exit1x1 + lda TileStore+TS_DIRTY,y + bne next - ldx DirtyTileCount - tya - sta DirtyTiles,x + inc sta TileStore+TS_DIRTY,y - inx - inx - stx DirtyTileCount - -exit1x1 - rts - -:mark2x2_v2 - -; Put the TileStoreBaseIndex into the X-register - - tax - -; Push a sentinel value of the stack that we use to inline all of the dirty tile array updates faster -; and the end of this routine. - - pea #$0000 - -; Now, move through each of the TileStore locations and set the necessary fields. We have to do the -; following -; -; 1. Set the marker bit in the TS_SPRITE_FLAG so the renderer knows which vbuff addresses to load -; 2. Set the address of the sprite stamp graphics that are used. This can change every frame. -; 3. Mark the tile as dirty and put it on the list if it was marked dirty for the first time. - - ldy TileStoreLookup,x ; Get the offset into the TileStore for this tile - - lda SpriteBit ; Mark this tile as having this sprite - ora TileStore+TS_SPRITE_FLAG,y - sta TileStore+TS_SPRITE_FLAG,y - - lda TileStore+TS_DIRTY,y ; If this tile is not yet marked dirty, queue it up - bne *+3 - phy - - lda VBuffOrigin - sta (tmp0),y ; Fill in the slot for this sprite on this tile - -; Move to the next tile - - ldy TileStoreLookup+2,x - - adc #4 ; Weave in the VBuffOrigin values to save a load every - sta (tmp0),y ; other iteration - - lda SpriteBit - ora TileStore+TS_SPRITE_FLAG,y - sta TileStore+TS_SPRITE_FLAG,y - - lda TileStore+TS_DIRTY,y - bne *+3 - phy - -; Third tile - - ldy TileStoreLookup+TS_LOOKUP_SPAN,x - - lda SpriteBit - ora TileStore+TS_SPRITE_FLAG,y - sta TileStore+TS_SPRITE_FLAG,y - - lda TileStore+TS_DIRTY,y - bne *+3 - phy - - lda VBuffOrigin - adc #SPRITE_PLANE_SPAN - sta (tmp0),y - -; Fourth tile - - ldy TileStoreLookup+TS_LOOKUP_SPAN+2,x - - adc #4+SPRITE_PLANE_SPAN - sta (tmp0),y - - lda SpriteBit - ora TileStore+TS_SPRITE_FLAG,y - sta TileStore+TS_SPRITE_FLAG,y - -; Lift this above the last TS_DIRTY check - - ldx DirtyTileCount - -; Check the TS_DIRTY flag for this tile. We handle it immediately, if needed - - lda TileStore+TS_DIRTY,y - bne skip - -; Now, update the Dirty Tile array tya - sta DirtyTiles,x - sta TileStore+TS_DIRTY,y - -skip - pla - beq :done1 - sta DirtyTiles+2,x - tay - sta TileStore+TS_DIRTY,y - - pla - beq :done2 - sta DirtyTiles+4,x - tay - sta TileStore+TS_DIRTY,y - - pla - beq :done3 - sta DirtyTiles+6,x - tay - sta TileStore+TS_DIRTY,y - -; Maximum number of dirty tiles reached. Just fall through. - - pla - txa - adc #8 - sta DirtyTileCount - rts -:done3 - txa - adc #6 - sta DirtyTileCount - rts -:done2 - txa - adc #4 - sta DirtyTileCount - rts -:done1 - inx - inx - stx DirtyTileCount - - rts + ldy DirtyTileCount + sta DirtyTiles,y + iny + iny + sty DirtyTileCount +next + <<< :mark1x1 - jsr :mark_0_0 -; sta _Sprites+TILE_STORE_ADDR_1,y -; lda #0 -; sta _Sprites+TILE_STORE_ADDR_2,y + ldx _Sprites+TS_LOOKUP_INDEX,y + TSSetSprite 0*{TS_LOOKUP_SPAN*2};#0 rts -; NOTE: If we rework the _PushDirtyTile to use the Y register instead of the X register, we can -; optimize all of these :mark routines as -; -; :mark1x1 -; jsr :mark_0_0 -; sty _Sprites+TILE_STORE_ADDR_1,x -; stz _Sprites+TILE_STORE_ADDR_2,y -; rts - :mark1x2 - jsr :mark_0_0 -; sta _Sprites+TILE_STORE_ADDR_1,y - jsr :mark_0_1 -; sta _Sprites+TILE_STORE_ADDR_2,y -; lda #0 -; sta _Sprites+TILE_STORE_ADDR_3,y + ldx _Sprites+TS_LOOKUP_INDEX,y + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} rts :mark1x3 - jsr :mark_0_0 -; sta _Sprites+TILE_STORE_ADDR_1,y - jsr :mark_0_1 -; sta _Sprites+TILE_STORE_ADDR_2,y - jsr :mark_0_2 -; sta _Sprites+TILE_STORE_ADDR_3,y -; lda #0 -; sta _Sprites+TILE_STORE_ADDR_4,y + ldx _Sprites+TS_LOOKUP_INDEX,y + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+4;#{0*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} rts :mark2x1 - jsr :mark_0_0 -; sta _Sprites+TILE_STORE_ADDR_1,y - jsr :mark_1_0 -; sta _Sprites+TILE_STORE_ADDR_2,y -; lda #0 -; sta _Sprites+TILE_STORE_ADDR_3,y + ldx _Sprites+TS_LOOKUP_INDEX,y + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} rts :mark2x2 - jsr :mark_0_0 -; sta _Sprites+TILE_STORE_ADDR_1,y - jsr :mark_0_1 -; sta _Sprites+TILE_STORE_ADDR_2,y - jsr :mark_1_0 -; sta _Sprites+TILE_STORE_ADDR_3,y - jsr :mark_1_1 -; sta _Sprites+TILE_STORE_ADDR_4,y -; lda #0 -; sta _Sprites+TILE_STORE_ADDR_5,y + ldx _Sprites+TS_LOOKUP_INDEX,y + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2;#{1*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} rts :mark2x3 - jsr :mark_0_0 -; sta _Sprites+TILE_STORE_ADDR_1,y - jsr :mark_0_1 -; sta _Sprites+TILE_STORE_ADDR_2,y - jsr :mark_0_2 -; sta _Sprites+TILE_STORE_ADDR_3,y - jsr :mark_1_0 -; sta _Sprites+TILE_STORE_ADDR_4,y - jsr :mark_1_1 -; sta _Sprites+TILE_STORE_ADDR_5,y - jsr :mark_1_2 -; sta _Sprites+TILE_STORE_ADDR_6,y -; lda #0 -; sta _Sprites+TILE_STORE_ADDR_7,y + ldx _Sprites+TS_LOOKUP_INDEX,y + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+4;#{0*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2;#{1*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+4;#{1*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} rts :mark3x1 - jsr :mark_0_0 -; sta _Sprites+TILE_STORE_ADDR_1,y - jsr :mark_1_0 -; sta _Sprites+TILE_STORE_ADDR_2,y - jsr :mark_2_0 -; sta _Sprites+TILE_STORE_ADDR_3,y -; lda #0 -; sta _Sprites+TILE_STORE_ADDR_4,y + ldx _Sprites+TS_LOOKUP_INDEX,y + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+0;#{2*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} rts :mark3x2 - jsr :mark_0_0 -; sta _Sprites+TILE_STORE_ADDR_1,y - jsr :mark_1_0 -; sta _Sprites+TILE_STORE_ADDR_2,y - jsr :mark_2_0 -; sta _Sprites+TILE_STORE_ADDR_3,y - jsr :mark_0_1 -; sta _Sprites+TILE_STORE_ADDR_4,y - jsr :mark_1_1 -; sta _Sprites+TILE_STORE_ADDR_5,y - jsr :mark_2_1 -; sta _Sprites+TILE_STORE_ADDR_6,y -; lda #0 -; sta _Sprites+TILE_STORE_ADDR_7,y + ldx _Sprites+TS_LOOKUP_INDEX,y + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2;#{1*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+0;#{2*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+2;#{2*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} rts :mark3x3 - jsr :mark_0_0 -; sta _Sprites+TILE_STORE_ADDR_1,y - jsr :mark_1_0 -; sta _Sprites+TILE_STORE_ADDR_2,y - jsr :mark_2_0 -; sta _Sprites+TILE_STORE_ADDR_3,y - jsr :mark_0_1 -; sta _Sprites+TILE_STORE_ADDR_4,y - jsr :mark_1_1 -; sta _Sprites+TILE_STORE_ADDR_5,y - jsr :mark_2_1 -; sta _Sprites+TILE_STORE_ADDR_6,y - jsr :mark_0_2 -; sta _Sprites+TILE_STORE_ADDR_7,y - jsr :mark_1_2 -; sta _Sprites+TILE_STORE_ADDR_8,y - jsr :mark_2_2 -; sta _Sprites+TILE_STORE_ADDR_9,y -; lda #0 -; sta _Sprites+TILE_STORE_ADDR_10,y + ldx _Sprites+TS_LOOKUP_INDEX,y + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+4;#{0*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2;#{1*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+4;#{1*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+0;#{2*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+2;#{2*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+4;#{2*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} rts - -; Begin List of subroutines to mark each tile offset -:mark_0_0 - ldx RowTop - lda ColLeft - clc - adc TileStoreYTable,x ; Fixed offset to the next row - tax - - ldal TileStore+TS_VBUFF_ARRAY_ADDR,x - sta tmp0 - - lda VBuffOrigin - sta [tmp0],y - -; lda VBuffOrigin ; This is an interesting case. The mapping between the tile store -; adc #{0*4}+{0*256} ; and the sprite buffers changes as the StartX, StartY values change -; stal TileStore+TS_SPRITE_ADDR,x ; but don't depend on any sprite information. However, by setting the - ; value only for the tiles that get added to the dirty tile list, we - ; can avoid recalculating over 1,000 values whenever the screen scrolls - ; (which is common) and just limit it to the number of tiles covered by - ; the sprites. If the screen is not scrolling and the sprites are not - ; moving and they are being dirtied, then we may do more work, but the - ; odds are in our favor to just take care of it here. - - ; lda TileStore+TS_SPRITE_FLAG,x - lda SpriteBit - oral TileStore+TS_SPRITE_FLAG,x - stal TileStore+TS_SPRITE_FLAG,x - - jmp _PushDirtyTileX ; Needs X = tile store offset; destroys A,X. Returns X in A - -:mark_1_0 - lda ColLeft - ldx RowTop - clc - adc TileStoreYTable+2,x - tax - - ldal TileStore+TS_VBUFF_ARRAY_ADDR,x - sta tmp0 - - lda VBuffOrigin - adc #{0*4}+{1*8*SPRITE_PLANE_SPAN} - sta [tmp0],y - - lda SpriteBit - oral TileStore+TS_SPRITE_FLAG,x - stal TileStore+TS_SPRITE_FLAG,x - - jmp _PushDirtyTileX - -:mark_2_0 - lda ColLeft - ldx RowTop - clc - adc TileStoreYTable+4,x - tax - - ldal TileStore+TS_VBUFF_ARRAY_ADDR,x - sta tmp0 - - lda VBuffOrigin - adc #{0*4}+{2*8*SPRITE_PLANE_SPAN} - sta [tmp0],y - - lda SpriteBit - oral TileStore+TS_SPRITE_FLAG,x - stal TileStore+TS_SPRITE_FLAG,x - - jmp _PushDirtyTileX - -:mark_0_1 - ldx ColLeft - lda NextCol+2,x - ldx RowTop - clc - adc TileStoreYTable,x - tax - - ldal TileStore+TS_VBUFF_ARRAY_ADDR,x - sta tmp0 - - lda VBuffOrigin - adc #{1*4}+{0*8*SPRITE_PLANE_SPAN} - sta [tmp0],y - - lda SpriteBit - oral TileStore+TS_SPRITE_FLAG,x - stal TileStore+TS_SPRITE_FLAG,x - - jmp _PushDirtyTileX - -:mark_1_1 - ldx ColLeft - lda NextCol+2,x - ldx RowTop - clc - adc TileStoreYTable+2,x - tax - - ldal TileStore+TS_VBUFF_ARRAY_ADDR,x - sta tmp0 - - lda VBuffOrigin - adc #{1*4}+{1*8*SPRITE_PLANE_SPAN} - sta [tmp0],y - - lda SpriteBit - oral TileStore+TS_SPRITE_FLAG,x - stal TileStore+TS_SPRITE_FLAG,x - - jmp _PushDirtyTileX - -:mark_2_1 - ldx ColLeft - lda NextCol+2,x - ldx RowTop - clc - adc TileStoreYTable+4,x - tax - - ldal TileStore+TS_VBUFF_ARRAY_ADDR,x - sta tmp0 - - lda VBuffOrigin - adc #{1*4}+{2*8*SPRITE_PLANE_SPAN} - sta [tmp0],y - - lda SpriteBit - oral TileStore+TS_SPRITE_FLAG,x - stal TileStore+TS_SPRITE_FLAG,x - - jmp _PushDirtyTileX - -:mark_0_2 - ldx ColLeft - lda NextCol+4,x - ldx RowTop - clc - adc TileStoreYTable,x - tax - - ldal TileStore+TS_VBUFF_ARRAY_ADDR,x - sta tmp0 - - lda VBuffOrigin - adc #{2*4}+{0*8*SPRITE_PLANE_SPAN} - sta [tmp0],y - - lda SpriteBit - oral TileStore+TS_SPRITE_FLAG,x - stal TileStore+TS_SPRITE_FLAG,x - - jmp _PushDirtyTileX - -:mark_1_2 - ldx ColLeft - lda NextCol+4,x - ldx RowTop - clc - adc TileStoreYTable+2,x - tax - - ldal TileStore+TS_VBUFF_ARRAY_ADDR,x - sta tmp0 - - lda VBuffOrigin - adc #{2*4}+{1*8*SPRITE_PLANE_SPAN} - sta [tmp0],y - - lda SpriteBit - oral TileStore+TS_SPRITE_FLAG,x - stal TileStore+TS_SPRITE_FLAG,x - - jmp _PushDirtyTileX - -:mark_2_2 - ldx ColLeft - lda NextCol+4,x - ldx RowTop - clc - adc TileStoreYTable+4,x - tax - - ldal TileStore+TS_VBUFF_ARRAY_ADDR,x - sta tmp0 - - lda VBuffOrigin - adc #{2*4}+{2*8*SPRITE_PLANE_SPAN} - sta [tmp0],y - - lda SpriteBit - oral TileStore+TS_SPRITE_FLAG,x - stal TileStore+TS_SPRITE_FLAG,x - - jmp _PushDirtyTileX - -; End list of subroutines to mark dirty tiles - -; Range-check and clamp the vertical part of the sprite. When this routine returns we will have valid -; values for the tile-top and row-top. Also, the accumulator will return the number of rows to render, -; a value of zero means that all of the sprite's rows are off-screen. -; -; This subroutine takes are of calculating the extra tile for unaligned accesses, too. -;_SpriteHeight dw 8,8,16,16 -;_SpriteHeightMinus1 dw 7,7,15,15 -;_SpriteRows dw 1,1,2,2 -;_SpriteWidth dw 4,8,4,8 -;_SpriteWidthMinus1 dw 3,7,3,7 -;_SpriteCols dw 1,2,1,2 diff --git a/src/Tool.s b/src/Tool.s index 711b6a8..b3be07a 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -309,6 +309,7 @@ _TSUpdateSprite put Graphics.s put Tiles.s put Sprite.s + put Sprite2.s put SpriteRender.s put Render.s put tiles/DirtyTileQueue.s diff --git a/src/static/TileStore.s b/src/static/TileStore.s index ec7ff57..b586ed6 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -368,11 +368,15 @@ DefaultPalette ENT ; 8. Game Boy Color : 20 x 18 160 x 144 (11,520 bytes ( 36.0%)) ; 9. Agony (Amiga) : 36 x 24 288 x 192 (27,648 bytes ( 86.4%)) ; 10. Atari Lynx : 20 x 13 160 x 102 (8,160 bytes ( 25.5%)) -ScreenModeWidth ENT +ScreenModeWidth ENT dw 320,272,256,256,280,256,240,288,160,288,160,320 -ScreenModeHeight ENT +ScreenModeHeight ENT dw 200,192,200,176,160,160,160,128,144,192,102,1 +; List of addresses of the VBuff arrays for each Tile Store entry, indexed by sprite index +VBuffArrayAddr ENT + ds MAX_SPRITES*2 + ; Convert sprite index to a bit position _SpriteBits ENT dw $0001,$0002,$0004,$0008,$0010,$0020,$0040,$0080,$0100,$0200,$0400,$0800,$1000,$2000,$4000,$8000 diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index debdd63..b855619 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -45,18 +45,34 @@ SPRITE_STATUS_MOVED equ $0002 ; Sprite's position was changed SPRITE_STATUS_UPDATED equ $0004 ; Sprite's non-position attributes were changed SPRITE_STATUS_REMOVED equ $0008 ; Sprite has been removed. -SPRITE_STATUS equ {MAX_SPRITES*0} -; TILE_DATA_OFFSET equ {MAX_SPRITES*2} -VBUFF_ADDR equ {MAX_SPRITES*4} ; Base address of the sprite's stamp in the data/mask banks -SPRITE_ID equ {MAX_SPRITES*6} -SPRITE_X equ {MAX_SPRITES*8} -SPRITE_Y equ {MAX_SPRITES*10} -; TILE_STORE_ADDR_1 equ {MAX_SPRITES*12} -TS_LOOKUP_INDEX equ {MAX_SPRITES*12} ; The index into the TileStoreLookup table corresponding to the top-left corner of the sprite -; TILE_STORE_ADDR_2 equ {MAX_SPRITES*14} -TS_COVERAGE_SIZE equ {MAX_SPRITES*14} ; Index into the lookup table of how many TileStore tiles are covered by this sprite +; These values are set by the user +SPRITE_STATUS equ {MAX_SPRITES*0} +SPRITE_ID equ {MAX_SPRITES*2} +SPRITE_X equ {MAX_SPRITES*4} +SPRITE_Y equ {MAX_SPRITES*6} + +; These values are cached / calculated during the rendering process +VBUFF_ADDR equ {MAX_SPRITES*8} ; Base address of the sprite's stamp in the data/mask banks +TS_LOOKUP_INDEX equ {MAX_SPRITES*10} ; The index from the TileStoreLookup table that corresponds to the top-left corner of the sprite +TS_COVERAGE_SIZE equ {MAX_SPRITES*12} ; Representation of how many TileStore tiles (NxM) are covered by this sprite +OLD_TS_LOOKUP_INDEX equ {MAX_SPRITES*14} ; Copy of the values to support diffing +OLD_TS_COVERAGE_SIZE equ {MAX_SPRITES*16} +SPRITE_DISP equ {MAX_SPRITES*18} ; Cached address of the specific stamp based on sprite flags +SPRITE_CLIP_LEFT equ {MAX_SPRITES*20} +SPRITE_CLIP_RIGHT equ {MAX_SPRITES*22} +SPRITE_CLIP_TOP equ {MAX_SPRITES*24} +SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*26} +IS_OFF_SCREEN equ {MAX_SPRITES*28} +SPRITE_WIDTH equ {MAX_SPRITES*30} +SPRITE_HEIGHT equ {MAX_SPRITES*32} +SPRITE_CLIP_WIDTH equ {MAX_SPRITES*34} +SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*36} +TS_VBUFF_BASE equ {MAX_SPRITES*38} ; Finalized VBUFF address based on the sprite position and tile offsets +;TILE_DATA_OFFSET equ {MAX_SPRITES*2} +;TILE_STORE_ADDR_1 equ {MAX_SPRITES*12} +;TILE_STORE_ADDR_2 equ {MAX_SPRITES*14} ;TILE_STORE_ADDR_3 equ {MAX_SPRITES*16} -TS_VBUFF_BASE_ADDR equ {MAX_SPRITES*16} ; Fixed address of the TS_VBUFF_X memory locations +;TS_VBUFF_BASE_ADDR equ {MAX_SPRITES*16} ; Fixed address of the TS_VBUFF_X memory locations ;TILE_STORE_ADDR_4 equ {MAX_SPRITES*18} ;TILE_STORE_ADDR_5 equ {MAX_SPRITES*20} ;TILE_STORE_ADDR_6 equ {MAX_SPRITES*22} @@ -64,16 +80,6 @@ TS_VBUFF_BASE_ADDR equ {MAX_SPRITES*16} ; Fixed address of the TS_VBUFF_X ;TILE_STORE_ADDR_8 equ {MAX_SPRITES*26} ;TILE_STORE_ADDR_9 equ {MAX_SPRITES*28} ;TILE_STORE_ADDR_10 equ {MAX_SPRITES*30} -SPRITE_DISP equ {MAX_SPRITES*32} ; cached address of the specific stamp based on flags -SPRITE_CLIP_LEFT equ {MAX_SPRITES*34} -SPRITE_CLIP_RIGHT equ {MAX_SPRITES*36} -SPRITE_CLIP_TOP equ {MAX_SPRITES*38} -SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*40} -IS_OFF_SCREEN equ {MAX_SPRITES*42} -SPRITE_WIDTH equ {MAX_SPRITES*44} -SPRITE_HEIGHT equ {MAX_SPRITES*46} -SPRITE_CLIP_WIDTH equ {MAX_SPRITES*48} -SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*50} ; 50 rows by 80 columns + 2 extra rows and columns TS_LOOKUP_WIDTH equ 80 From 78d7dafe140196195ba1fe8ca41c54f7b64388d4 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 31 May 2022 08:43:26 -0500 Subject: [PATCH 26/82] Another checkpoint; converging on the working implementation --- package.json | 1 + src/CoreImpl.s | 2 +- src/Defs.s | 8 +- src/Render.s | 33 +++--- src/Sprite.s | 49 ++++---- src/Sprite2.s | 149 ++++++++++++++++++++----- src/Tiles.s | 216 ++++++++++++++++++++++++++++++++++-- src/Tool.s | 41 ++++++- src/blitter/TemplateUtils.s | 2 +- src/blitter/TileProcs.s | 12 ++ src/blitter/Tiles.s | 88 +-------------- src/blitter/Tiles10000.s | 1 - src/static/TileStore.s | 21 ++-- src/static/TileStoreDefs.s | 12 +- src/tiles/FastRenderer.s | 159 +++++++++++++++----------- 15 files changed, 535 insertions(+), 259 deletions(-) diff --git a/package.json b/package.json index c79ebe3..d5c1d91 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "crossrunner": "C:\\Programs\\Crossrunner\\Crossrunner.exe" }, "scripts": { + "archive": "%npm_package_config_cadius% EXTRACTFILE ", "test": "npm run build && build-image.bat %npm_package_config_cadius% && %npm_package_config_gsport%", "build": "%npm_package_config_merlin32% -V %npm_package_config_macros% ./src/Master.s", "build:debug": "%npm_package_config_merlin32% -V %npm_package_config_macros% ./src/Debug.s", diff --git a/src/CoreImpl.s b/src/CoreImpl.s index a8696a7..ee31bea 100644 --- a/src/CoreImpl.s +++ b/src/CoreImpl.s @@ -93,7 +93,7 @@ _CoreStartUp jsr EngineReset ; All of the resources are allocated, put the engine in a known state jsr InitGraphics ; Initialize all of the graphics-related data -; jsr InitSprites ; Initialize the sprite subsystem + jsr InitSprites ; Initialize the sprite subsystem jsr InitTiles ; Initialize the tile subsystem jsr InitTimers ; Initialize the timer subsystem diff --git a/src/Defs.s b/src/Defs.s index bd81382..a74298d 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -96,7 +96,7 @@ LastKey equ 116 LastTick equ 118 ForceSpriteFlag equ 120 -VBuffArrayPtr equ 122 +;VBuffArrayPtr equ 122 SpriteRemovedFlag equ 126 ; Indicate if any sprites were removed this frame activeSpriteList equ 128 ; 32 bytes for the active sprite list (can persist across frames) @@ -147,8 +147,10 @@ _TILE_ID equ 158 ; Copy of the tile descriptor ; Define free space the the application to use ; FREE_SPACE_DP2 equ 160 -DP2_DIRTY_TILE_COUNT equ 160 ; Local copy of dirty tile count to avoid banking +DP2_DIRTY_TILE_COUNT equ 160 ; Local copy of dirty tile count to avoid banking DP2_DIRTY_TILE_CALLBACK equ 162 + +SPRITE_VBUFF_PTR equ 224 ; 32 bytes of adjusted pointers to VBuffArray addresses ; End direct page values ; EngineMode definitions @@ -242,4 +244,4 @@ ScreenModeWidth EXT ScreenModeHeight EXT _SpriteBits EXT _SpriteBitsNot EXT -VBuffArrayAddr EXT +VBuffArray EXT diff --git a/src/Render.s b/src/Render.s index 46d2437..7a7bba6 100644 --- a/src/Render.s +++ b/src/Render.s @@ -102,22 +102,22 @@ _Render ; The _ApplyTilesFast is the same as _ApplyTiles, but we use the _RenderTileFast subroutine _ApplyTilesFast + ldx DirtyTileCount + tdc clc adc #$100 ; move to the next page tcd - lda DirtyTileCount ; Cache the dirty tile count - sta DP2_DIRTY_TILE_COUNT - + stx DP2_DIRTY_TILE_COUNT ; Cache the dirty tile count jsr _PopDirtyTilesFast - stz DirtyTileCount - tdc ; Move back to the original direct page sec sbc #$100 tcd + + stz DirtyTileCount ; Reset the dirty tile count rts ; The _ApplyTiles function is responsible for rendering all of the dirty tiles into the code @@ -198,17 +198,10 @@ _ApplyDirtyTiles ; Only render solid tiles and sprites _RenderDirtyTile - ldx TileStore+TS_VBUFF_ADDR_COUNT,y ; How many sprites are on this tile? + lda TileStore+TS_SPRITE_FLAG,y beq NoSpritesDirty ; This is faster if there are no sprites - lda TileStore+TS_TILE_ID,y ; Check if the tile has - jmp (dirty_dispatch,x) -dirty_dispatch - da NoSpritesDirty - da OneSpriteDirty - da TwoSpritesDirty - da ThreeSpritesDirty - da FourSpritesDirty +; 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 @@ -218,12 +211,12 @@ dirty_dispatch ; 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 - tyx - 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) + 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 - jmp (TileStore+TS_DIRTY_TILE_DISP,x) ; go to the tile copy routine (just basics) - +:nsd jmp $0000 ; Use some temporary space for the spriteIdx array (maximum of 4 entries) stkSave equ tmp9 @@ -240,7 +233,7 @@ ThreeSpritesDirty TwoSpritesDirty sta tileAddr - sty screenAddr + stx screenAddr plb tsc diff --git a/src/Sprite.s b/src/Sprite.s index aea622b..e8d685d 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -19,19 +19,32 @@ InitSprites cpx #$FFFE bne :loop2 -; Clear values in the sprite array +; Set the VBuff array addresses for each sprite, since they're static -; ldx #{MAX_SPRITES-1}*2 -;:loop3 stz _Sprites+TILE_STORE_ADDR_1,x -; dex -; dex -; bpl :loop3 + ldx #0 + lda #VBuffArray +:loop3 sta _Sprites+VBUFF_ARRAY_ADDR,x + clc + adc #4*2 ; skip ahead 4 tiles + inx + inx + cpx #8*2 + bcc :loop3 + +; Now do the second set of sprites + lda #VBuffArray+{3*{TILE_STORE_WIDTH*2}} +:loop4 sta _Sprites+VBUFF_ARRAY_ADDR,x + clc + adc #4*2 ; skip ahead 4 tiles + inx + inx + cpx #8*2 + bcc :loop4 ; Precalculate some bank values jsr _CacheSpriteBanks rts - ; _RenderSprites ; ; The function is responsible for updating all of the rendering information based on any changes @@ -59,7 +72,7 @@ InitSprites ; a. If it is not marked in the DirtyTile list ; * Clear its bit from the TileStore's TS_SPRITE_FLAG ; * Add the tile to the DirtyTile list -; +;t ; 2. If a sprite is marked as SPRITE_STATUS_REMOVED, then ; A. Clear its bit from the SpriteBits bitmap ; B. For each tile the sprite overlaps with: @@ -134,6 +147,8 @@ _DoPhase1 lda _SpriteBits,y ; Clear from the sprite bitmap sta SpriteRemovedFlag ; Stick a non-zero value here 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 jmp _ClearSpriteFromTileStore ; Clear the tile flags, add to the dirty tile list and done @@ -154,15 +169,6 @@ _DoPhase1 :no_move jmp _MarkDirtySpriteTiles -; Once all of the sprite values have been calculated, we need to scan the dirty tile list and -; collapse the sprite information down to no more than 4 vbuff references per tile. We used to -; do this on the fly in the renderer, but that required differentiating between tile with and -; without sprites in the core rendering function. My lifting this up, we simplify the core code -; and possible open up some optimization opportunities. -_SetTileStoreVBuffAddrs - - - ; Dispatch table. It's unintersting, so it's tucked out of the way phase1 dw :phase1_0 dw :phase1_1,:phase1_2,:phase1_3,:phase1_4 @@ -200,8 +206,8 @@ phase1 dw :phase1_0 :phase1_2 ldy activeSpriteList+2 jsr _DoPhase1 :phase1_1 ldy activeSpriteList - jsr _DoPhase1 -:phase1_0 jmp _SetTileStoreVBuffAddrs + jmp _DoPhase1 +:phase1_0 rts ; Utility function to calculate the difference in tile positions between a sprite's current ; position and it's previous position. This gets interesting because the number of tiles @@ -299,9 +305,6 @@ _AddSprite lda _SpriteBits,x ; Get the bit flag for this sprite slot tsb SpriteMap ; Mark it in the sprite map bit field -; txa ; And return the sprite ID -; clc ; Mark that the sprite was successfully added - rts ; Macro to make the unrolled loop more concise @@ -314,7 +317,7 @@ TSClearSprite mac ldy TileStoreLookup+]1,x lda TileStore+TS_SPRITE_FLAG,y - and tmp0 + and tmp0 sta TileStore+TS_SPRITE_FLAG,y lda TileStore+TS_DIRTY,y diff --git a/src/Sprite2.s b/src/Sprite2.s index 13996e9..4776a6f 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -66,14 +66,11 @@ _CalcDirtySprite clc lda _Sprites+SPRITE_CLIP_TOP,y adc StartYMod208 ; Adjust for the scroll offset - pha ; Cache + tax ; Cache and #$FFF8 ; mask first to ensure LSR will clear the carry lsr lsr - tax - lda TileStoreLookupYTable,x ; Even numbers from [0, 100] (50 elements) - sta RowTop - pla + sta RowTop ; Even numbers from [0, 100] (50 elements) ; Get the position of the top edge within the tile and then add it to the sprite's height ; to calculate the number of tiles that are overlapped. We use the actual width and height @@ -90,7 +87,7 @@ _CalcDirtySprite and #$0018 sta AreaIndex - txa ; Get the verical offset in the VBUFF memory + txa ; Get the vertical offset in the VBUFF memory asl tax ldal :vbuff_mul,x @@ -108,13 +105,19 @@ _CalcDirtySprite adc RowTop sta _Sprites+TS_LOOKUP_INDEX,y ; This is the index into the TileStoreLookup table +; Create an offset value for loading the calculated VBUFF addresses within the core renderer + + eor #$FFFF + sec + adc _Sprites+VBUFF_ARRAY_ADDR,y + sta tmp1 ; Spill this value to direct page temp space + ; Calculate the final address of the sprite data in the stamp buffer. We have to move earlier ; in the buffer based on the horizontal offset and move up for each vertical offset. txa and #$0003 tax - adc tmp0 ; add to the vertical offset ; Subtract this value from the SPRITE_DISP address @@ -134,12 +137,17 @@ _CalcDirtySprite and #$000C lsr ; max value = 4 = 0x04 ora AreaIndex ; merge into the area index + sta _Sprites+TS_COVERAGE_SIZE,y ; Save this value as a key to the coverage size of the sprite -; No need to copy the TileStore addresses into the Sprite's TILE_STORE_ADDR values. Just -; hold a copy of the corner offset into the lookup table and the sprite's size in tiles. -; Then, when we need to erase we can just lookup the values in the TileStoreLookup table. +; Last task. Since we don't need to use the X-register to cache values; load the direct page 2 +; offset for the SPRITE_VBUFF_PTR and save it + + tya + ora #$100 + tax + lda tmp1 + sta SPRITE_VBUFF_PTR,x - sta _Sprites+TS_COVERAGE_SIZE,y mdsOut rts @@ -147,12 +155,6 @@ mdsOut rts ; 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 VBuffArrayAddr,y ; Get the base address for the TileStore VBuff array for this sprite - sta VBuffArrayPtr - - lda _Sprites+TS_VBUFF_BASE,y ; This is the final upper-left cornder for this frame - sta VBuffOrigin - lda _SpriteBits,y sta SpriteBit @@ -184,10 +186,6 @@ TSSetSprite mac ora TileStore+TS_SPRITE_FLAG,y sta TileStore+TS_SPRITE_FLAG,y - lda VBuffOrigin - adc ]2 - sta [tmp0],y ; This is *very* carefully constructed.... - lda TileStore+TS_DIRTY,y bne next @@ -203,31 +201,68 @@ TSSetSprite mac next <<< +ROW equ TILE_STORE_WIDTH*2 +COL equ TILE_DATA_SPAN + :mark1x1 + ldx _Sprites+VBUFF_ARRAY_ADDR,y ; get the address of this sprite's vbuff values + lda _Sprites+TS_VBUFF_BASE,y ; get the starting vbuff address + sta: {0*ROW}+{0*COL},x ; Put in the vbuff address + ldx _Sprites+TS_LOOKUP_INDEX,y - TSSetSprite 0*{TS_LOOKUP_SPAN*2};#0 + TSSetSprite 0*{TS_LOOKUP_SPAN*2} rts :mark1x2 + ldx _Sprites+VBUFF_ARRAY_ADDR,y + lda _Sprites+TS_VBUFF_BASE,y + sta: {0*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {0*ROW}+{1*COL},x + ldx _Sprites+TS_LOOKUP_INDEX,y - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 rts :mark1x3 + ldx _Sprites+VBUFF_ARRAY_ADDR,y + lda _Sprites+TS_VBUFF_BASE,y + sta: {0*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {0*ROW}+{1*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {0*ROW}+{2*COL},x + ldx _Sprites+TS_LOOKUP_INDEX,y - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+4;#{0*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+4 rts :mark2x1 + ldx _Sprites+VBUFF_ARRAY_ADDR,y + lda _Sprites+TS_VBUFF_BASE,y + sta: {0*ROW}+{0*COL},x + adc #VBUFF_TILE_ROW_BYTES + sta: {1*ROW}+{0*COL},x + ldx _Sprites+TS_LOOKUP_INDEX,y - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0 rts :mark2x2 + ldx _Sprites+VBUFF_ARRAY_ADDR,y + lda _Sprites+TS_VBUFF_BASE,y + sta: {0*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {0*ROW}+{1*COL},x + adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES + sta: {1*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {1*ROW}+{1*COL},x + ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} @@ -236,6 +271,20 @@ next rts :mark2x3 + ldx _Sprites+VBUFF_ARRAY_ADDR,y + lda _Sprites+TS_VBUFF_BASE,y + sta: {0*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {0*ROW}+{1*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {0*ROW}+{2*COL},x + adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} + sta: {1*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {1*ROW}+{1*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {1*ROW}+{2*COL},x + ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} @@ -246,6 +295,14 @@ next rts :mark3x1 + ldx _Sprites+VBUFF_ARRAY_ADDR,y + lda _Sprites+TS_VBUFF_BASE,y + sta: {0*ROW}+{0*COL},x + adc #VBUFF_TILE_ROW_BYTES + sta: {1*ROW}+{0*COL},x + adc #VBUFF_TILE_ROW_BYTES + sta: {2*ROW}+{0*COL},x + ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} @@ -253,6 +310,20 @@ next rts :mark3x2 + ldx _Sprites+VBUFF_ARRAY_ADDR,y + lda _Sprites+TS_VBUFF_BASE,y + sta: {0*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {0*ROW}+{1*COL},x + adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES + sta: {1*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {1*ROW}+{1*COL},x + adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES + sta: {2*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {2*ROW}+{1*COL},x + ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} @@ -263,6 +334,26 @@ next rts :mark3x3 + ldx _Sprites+VBUFF_ARRAY_ADDR,y + lda _Sprites+TS_VBUFF_BASE,y + sta: {0*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {0*ROW}+{1*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {0*ROW}+{2*COL},x + adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} + sta: {1*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {1*ROW}+{1*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {1*ROW}+{2*COL},x + adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} + sta: {2*ROW}+{0*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {2*ROW}+{1*COL},x + adc #VBUFF_TILE_COL_BYTES + sta: {2*ROW}+{2*COL},x + ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} diff --git a/src/Tiles.s b/src/Tiles.s index 85b1e82..616d767 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -120,14 +120,6 @@ InitTiles ; lda TileProcs ; Same for non-dirty, non-sprite base case ; stal TileStore+TS_BASE_TILE_DISP,x -; *** DEPRECATED *** -; lda :vbuff ; array of sprite vbuff addresses per tile -; stal TileStore+TS_VBUFF_ARRAY_ADDR,x -; clc -; adc #32 -; sta :vbuff -; *** ********** *** - ; 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 @@ -267,3 +259,211 @@ _SetBG0YPos stx OldStartY ; First change, so preserve the value :out rts + +; Macro helper for the bit test tree +; dobit bit_position,dest;next;exit +dobit mac + lsr + bcc next_bit + beq last_bit + tax + lda (SPRITE_VBUFF_PTR+{]2*2}),y + sta sprite_ptr0+{]2*4} + txa + jmp ]3 +last_bit lda (SPRITE_VBUFF_PTR+{]2*2}),y + sta sprite_ptr0+{]2*4} + jmp ]4 +next_bit + <<< + +; Specialization for the first sprite which can just return the vbuff address +; in a register if there is only one sprite intersecting the tile +dobit1 mac + lsr + bcc next_bit + beq last_bit + tax + lda (SPRITE_VBUFF_PTR+{]2*2}),y + sta sprite_ptr0+{]2*4} + txa + jmp ]3 +last_bit lda (SPRITE_VBUFF_PTR+{]2*2}),y + jmp ]4 +next_bit + <<< + +; Optimization discussion. In the Sprite2.s file, we calculate the VBUFF address for each tile overlapped +; by a sprite: +; +; 4 lda VBuffOrigin +; 3 adc ]2 +; 7 sta [tmp0],y +; +; and then in this macro it is loaded again and copied to the direct page. If a sprite is never drawn, this is +; wasted work (which is not too ofter since >4 sprites would need to be overlapping), but still. +; +; 6 ldy: {]1*TILE_STORE_SIZE},x +; 4 sty sprite_ptr0+{]2*4} +; +; Since we know *exactly* which sprite is being accessed, the _Sprites+TS_VBUFF_BASE,y value can be loaded without +; an index +; +; 5 lda _Sprites+TS_VBUFF_BASE+{]1*2} +; 6 adc {]1*TILE_STORE_SIZE},x +; 4 sta sprite_ptr0+{]2*4} +; 2 tya +; +; = a savings of at least (24 - 17) = 7 cycles per tile and more if the sprite is skipped. +; +; The problem is that this still required storing a value for the sprite in the tile store. What is ideal is +; if there is a way to know implicitly which relative tile offset we are on for a given sprite and use +; that to calculate the offset... +; +; What do we know +; X = current tile +; Sprite+TS_LOOKUP_INDEX +; +; txa +; sbc _Sprites+TS_LOOKUP_INDEX+{]1*2} +; tay +; lda _Sprites+TS_VBUFF_BASE+{]1*2} +; adc DisplacementTable,y +; sta sprite_ptr0+{]2*4} +; +; Have the sprite select a table base which holds the offset values, pre-adjusted for the TS_LOOKUP_INDEX. The table +; values are fixed. Yes!! This is the solution!! It will only need 288 bytes of total space +; +; Best implementation will pass the Tile Store index in Y instead of X +; +; 5 lda _Sprites+VBUFF_TABLE+{]1*2} +; 6 sta self_mod +; 6 lda $0000,x +; 4 sta sprite_ptr0+{]2*4} +; 2 tya +; +; or +; +; 5 lda _Sprites+VBUFF_TABLE+{]1*2} +; 4 sta tmp0 +; 7 lda (tmp0),y +; 4 sta sprite_ptr0+{]2*4} +; 2 txa +; +; Even better, if the VBUFF_TABLE (only 32 bytes) was already stored in the second direct page +; +; 7 lda (VBUFF_TABLE+{]1*2}),y +; 5 adc _Sprites+VBUFF_TABLE+{]1*2} +; 4 sta sprite_ptr0+{]2*4} +; 2 txa +; +; Final saving compared to current implementation is (24 - 18) = 6 cycles per tile and we eliminate +; the need to pre-calculate +; + +; If we find a last bit (4th in this case) and will exit +stpbit mac + lsr + bcc next_bit + lda (SPRITE_VBUFF_PTR+{]2*2}),y + sta sprite_ptr0+{]2*4} + jmp ]3 +next_bit + <<< + +; Last bit test which *must* be set +endbit mac + lda (SPRITE_VBUFF_PTR+{]2*2}),y + sta sprite_ptr0+{]2*4} + jmp ]3 + <<< + +; OPTIMIZATION: +; +; bit #$00FF ; Optimization to skip the first 8 bits if they are all zeros +; bne norm_entry +; xba +; jmp skip_entry +; +; Placed at the entry point + +; This is a complex, but fast subroutine that is called from the core tile rendering code. It +; Takes a bitmap of sprites in the Accumulator and then extracts the VBuff addresses for the +; target TileStore entry and places them in specific direct page locations. +; +; Inputs: +; A = sprite bitmap (assumed to be non-zero) +; Y = tile store index +; D = second work page +; B = vbuff array bank +; Output: +; X = +; +; ]1 address of single sprite process +; ]2 address of two sprite process +; ]3 address of three sprite process +; ]4 address of four sprite process + +SpriteBitsToVBuffAddrs mac + dobit1 0;0;b_1_1;]1 + dobit1 1;0;b_2_1;]1 + dobit1 2;0;b_3_1;]1 + dobit1 3;0;b_4_1;]1 + dobit1 4;0;b_5_1;]1 + dobit1 5;0;b_6_1;]1 + dobit1 6;0;b_7_1;]1 + dobit1 7;0;b_8_1;]1 + dobit1 8;0;b_9_1;]1 + dobit1 9;0;b_10_1;]1 + dobit1 10;0;b_11_1;]1 + dobit1 11;0;b_12_1;]1 + dobit1 12;0;b_13_1;]1 + dobit1 13;0;b_14_1;]1 + dobit1 14;0;b_15_1;]1 + endbit 15;0;]1 + +b_1_1 dobit 1;1;b_2_2;]2 +b_2_1 dobit 2;1;b_3_2;]2 +b_3_1 dobit 3;1;b_4_2;]2 +b_4_1 dobit 4;1;b_5_2;]2 +b_5_1 dobit 5;1;b_6_2;]2 +b_6_1 dobit 6;1;b_7_2;]2 +b_7_1 dobit 7;1;b_8_2;]2 +b_8_1 dobit 8;1;b_9_2;]2 +b_9_1 dobit 9;1;b_10_2;]2 +b_10_1 dobit 10;1;b_11_2;]2 +b_11_1 dobit 11;1;b_12_2;]2 +b_12_1 dobit 12;1;b_13_2;]2 +b_13_1 dobit 13;1;b_14_2;]2 +b_14_1 dobit 14;1;b_15_2;]2 +b_15_1 endbit 15;1;]2 + +b_2_2 dobit 2;2;b_3_3;]3 +b_3_2 dobit 3;2;b_4_3;]3 +b_4_2 dobit 4;2;b_5_3;]3 +b_5_2 dobit 5;2;b_6_3;]3 +b_6_2 dobit 6;2;b_7_3;]3 +b_7_2 dobit 7;2;b_8_3;]3 +b_8_2 dobit 8;2;b_9_3;]3 +b_9_2 dobit 9;2;b_10_3;]3 +b_10_2 dobit 10;2;b_11_3;]3 +b_11_2 dobit 11;2;b_12_3;]3 +b_12_2 dobit 12;2;b_13_3;]3 +b_13_2 dobit 13;2;b_14_3;]3 +b_14_2 dobit 14;2;b_15_3;]3 +b_15_2 endbit 15;2;]3 + +b_3_3 stpbit 3;3;]4 +b_4_3 stpbit 4;3;]4 +b_5_3 stpbit 5;3;]4 +b_6_3 stpbit 6;3;]4 +b_7_3 stpbit 7;3;]4 +b_8_3 stpbit 8;3;]4 +b_9_3 stpbit 9;3;]4 +b_10_3 stpbit 10;3;]4 +b_11_3 stpbit 11;3;]4 +b_12_3 stpbit 12;3;]4 +b_13_3 stpbit 13;3;]4 +b_14_3 stpbit 14;3;]4 +b_15_3 endbit 15;3;]4 + <<< \ No newline at end of file diff --git a/src/Tool.s b/src/Tool.s index b3be07a..1f4315f 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -52,8 +52,23 @@ _CallTable adrl _TSRender-1 adrl _TSLoadTileSet-1 adrl _TSCreateSpriteStamp-1 + adrl _TSAddSprite-1 + adrl _TSMoveSprite-1 + adrl _TSUpdateSprite-1 + adrl _TSRemoveSprite-1 _CTEnd - +_GTEAddSprite MAC + UserTool $1000+GTEToolNum + <<< +_GTEMoveSprite MAC + UserTool $1100+GTEToolNum + <<< +_GTEUpdateSprite MAC + UserTool $1200+GTEToolNum + <<< +_GTERemoveSprite MAC + UserTool $1300+GTEToolNum + <<< ; Helper function to set the data back to the toolset default _SetDataBank sep #$20 lda #^TileStore @@ -285,6 +300,21 @@ _TSAddSprite _TSExit #0;#8 +_TSMoveSprite +:spriteY equ FirstParam+0 +:spriteX equ FirstParam+2 +:spriteSlot equ FirstParam+4 + _TSEntry + + lda :spriteX,s + tax + lda :spriteY,s + tay + lda :spriteSlot,s + jsr _MoveSprite + + _TSExit #0;#6 + _TSUpdateSprite :vbuff equ FirstParam+0 :spriteFlags equ FirstParam+2 @@ -300,6 +330,15 @@ _TSUpdateSprite _TSExit #0;#6 +_TSRemoveSprite +:spriteSlot equ FirstParam+0 + _TSEntry + + lda :spriteSlot,s + jsr _UpdateSprite + + _TSExit #0;#2 + ; Insert the GTE code put Math.s diff --git a/src/blitter/TemplateUtils.s b/src/blitter/TemplateUtils.s index 56b60bf..88852bb 100644 --- a/src/blitter/TemplateUtils.s +++ b/src/blitter/TemplateUtils.s @@ -73,7 +73,7 @@ Counter equ tmp3 ; Patch an 8-bit or 16-bit valueS into the bank. These are a set up unrolled loops to -; quickly patch in a constanct value, or a value from an array into a given set of +; quickly patch in a constant value, or a value from an array into a given set of ; templates. ; ; Because we have structured everything as parallel code blocks, most updates to the blitter diff --git a/src/blitter/TileProcs.s b/src/blitter/TileProcs.s index 5fe0b29..f40b634 100644 --- a/src/blitter/TileProcs.s +++ b/src/blitter/TileProcs.s @@ -27,6 +27,18 @@ _TBCopyTileDataAndMaskToCBuffV jsr _TBCopyTileDataToCBuffV jmp _TBCopyTileMaskToCBuffV +_CopyTileDataToDP2 +]line equ 0 + lup 8 + ldal tiledata+{]line*4},x + sta tmp_tile_data+{]line*4} + + ldal tiledata+{]line*4}+2,x + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + rts + _TBCopyTileDataToCBuff ]line equ 0 lup 8 diff --git a/src/blitter/Tiles.s b/src/blitter/Tiles.s index 7a6f197..b1779b0 100644 --- a/src/blitter/Tiles.s +++ b/src/blitter/Tiles.s @@ -84,18 +84,6 @@ CopyNoSprites :tiledisp jmp $0000 ; render the tile -; Let's make a macro helper for the bit test tree -; dobit src_offset,dest,next_target,end_target -dobit MAC - beq last_bit - ldx: ]1,y - stx ]2 - jmp ]3 -last_bit ldx: ]1,y - stx ]2 - jmp ]4 - EOM - ; The sprite code is just responsible for quickly copying all of the sprite data ; into the direct page temp area. @@ -117,83 +105,9 @@ dirty_sprite_dispatch da CopyThreeSprites da CopyFourSprites ; MAX, don't bother with more than 4 sprites per tile -; This is very similar to the code in the dirty tile renderer, but we can't reuse -; because that code draws directly to the graphics screen, and this code draws -; to a temporary buffer that has a different stride. - -; ldy TileStore+TS_VBUFF_ARRAY_ADDR,x ; base address of the VBUFF sprite address array for this tile -; -; lsr -; bcc :loop_0_bit_1 -; dobit $0000;sprite_ptr0;:loop_1_bit_1;CopyOneSprite - -;:loop_0_bit_1 lsr -; bcc :loop_0_bit_2 -; dobit $0002;sprite_ptr0;:loop_1_bit_2;CopyOneSprite - -;:loop_0_bit_2 lsr -; bcc :loop_0_bit_3 -; dobit $0004;sprite_ptr0;:loop_1_bit_3;CopyOneSprite - -;:loop_0_bit_3 lsr -; bcc :loop_0_bit_4 -; dobit $0006;sprite_ptr0;:loop_1_bit_4;CopyOneSprite - -;:loop_0_bit_4 lsr -; bcc :loop_0_bit_5 -; dobit $0008;sprite_ptr0;:loop_1_bit_5;CopyOneSprite - -;:loop_0_bit_5 lsr -; bcc :loop_0_bit_6 -; dobit $000A;sprite_ptr0;:loop_1_bit_6;CopyOneSprite - -;:loop_0_bit_6 lsr -; bcc :loop_0_bit_7 -; dobit $000C;sprite_ptr0;:loop_1_bit_7;CopyOneSprite - -;:loop_0_bit_7 lsr -; bcc :loop_0_bit_8 -; dobit $000E;sprite_ptr0;:loop_1_bit_8;CopyOneSprite - -;:loop_0_bit_8 lsr -; bcc :loop_0_bit_9 -; dobit $0010;sprite_ptr0;:loop_1_bit_9;CopyOneSprite - -;:loop_0_bit_9 lsr -; bcc :loop_0_bit_10 -; ldx: $0012,y -; stx spriteIdx -; cmp #0 -; jne :loop_1_bit_10 -; jmp CopyOneSprite - -;:loop_0_bit_10 lsr -; bcc :loop_0_bit_11 -; dobit $0014;sprite_ptr0;:loop_1_bit_11;CopyOneSprite - -;:loop_0_bit_11 lsr -; bcc :loop_0_bit_12 -; dobit $0016;sprite_ptr0;:loop_1_bit_12;CopyOneSprite - -;:loop_0_bit_12 lsr -; bcc :loop_0_bit_13 -; dobit $0018;sprite_ptr0;:loop_1_bit_13;CopyOneSprite - -;:loop_0_bit_13 lsr -; bcc :loop_0_bit_14 -; dobit $001A;sprite_ptr0;:loop_1_bit_14;CopyOneSprite - -;:loop_0_bit_14 lsr -; bcc :loop_0_bit_15 -; dobit $001C;sprite_ptr0;:loop_1_bit_15;CopyOneSprite - -;:loop_0_bit_15 ldx: $001E,y -; stx spriteIdx -; jmp CopyOneSprite - ; We can optimize later, for now just copy the sprite data and mask into its own ; direct page buffer and combine with the tile data later - +; ; We set up direct page pointers to the mask bank and use the bank register for the ; data. CopyFourSprites diff --git a/src/blitter/Tiles10000.s b/src/blitter/Tiles10000.s index edc5655..d56861a 100644 --- a/src/blitter/Tiles10000.s +++ b/src/blitter/Tiles10000.s @@ -30,7 +30,6 @@ _TBFastSpriteTile_VH ; 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} diff --git a/src/static/TileStore.s b/src/static/TileStore.s index b586ed6..f03cf02 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -2,18 +2,15 @@ put ../Defs.s put TileStoreDefs.s - put ../blitter/Template.s ;------------------------------------------------------------------------------------- ; -; Buffer space - - ds 256 + put ../blitter/Template.s ;------------------------------------------------------------------------------------- TileStore ENT - ds {TILE_STORE_SIZE*17} + ds {TILE_STORE_SIZE*TILE_STORE_NUM} ;------------------------------------------------------------------------------------- ; @@ -23,7 +20,7 @@ TileStore ENT DirtyTileCount ENT ds 2 DirtyTiles ENT - ds TILE_STORE_SIZE ; At most this many tiles can possibly be update at once + ds TILE_STORE_SIZE ; At most this many tiles can possibly be updated at once ;------------------------------------------------------------------------------------- ; @@ -373,9 +370,15 @@ ScreenModeWidth ENT ScreenModeHeight ENT dw 200,192,200,176,160,160,160,128,144,192,102,1 -; List of addresses of the VBuff arrays for each Tile Store entry, indexed by sprite index -VBuffArrayAddr ENT - ds MAX_SPRITES*2 +; VBuff arrays for each sprite. We need at least a 3x3 block for each sprite and the shape of the +; array must match the TileStore structure. The TileStore is 41 blocks wide. To keep things simple +; we allocate 8 sprites in the first row and 8 more sprites in the 4th row. So we need to allocate a +; total of 6 rows of TileStore space +; +; It is *critical* that this array be placed in a memory location that is greated than the largest +; TileStore offset. +VBuffArray ENT + ds 6*{TILE_STORE_WIDTH*2} ; Convert sprite index to a bit position _SpriteBits ENT diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index b855619..2ab791a 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -14,24 +14,17 @@ TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5 TS_WORD_OFFSET equ TILE_STORE_SIZE*6 ; const value, word offset value for this tile if LDA (dp),y instructions re used TS_BASE_ADDR equ TILE_STORE_SIZE*7 ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000. TS_SCREEN_ADDR equ TILE_STORE_SIZE*8 ; cached value of on-screen location of tile. Used for DirtyRender. -;TS_VBUFF_ARRAY_ADDR equ TILE_STORE_SIZE*9 ; const value to an aligned 32-byte array starting at $8000 in TileStore bank - TS_BASE_TILE_COPY equ TILE_STORE_SIZE*9 ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering TS_BASE_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function TS_DIRTY_TILE_DISP equ TILE_STORE_SIZE*11 ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function -; Hold values for up to 4 sprites per tile -TS_VBUFF_ADDR_0 equ TILE_STORE_SIZE*12 -TS_VBUFF_ADDR_1 equ TILE_STORE_SIZE*13 -TS_VBUFF_ADDR_2 equ TILE_STORE_SIZE*14 -TS_VBUFF_ADDR_3 equ TILE_STORE_SIZE*15 -TS_VBUFF_ADDR_COUNT equ TILE_STORE_SIZE*16 ; replace usage of TS_VBUFF_ARRAY_ADDR with this later +TILE_STORE_NUM equ 12 ; Need this many parallel arrays ; Sprite data structures. We cache quite a few pieces of information about the sprite ; to make calculations faster, so this is hidden from the caller. MAX_SPRITES equ 16 -SPRITE_REC_SIZE equ 52 +SPRITE_REC_SIZE equ 42 ; Mark each sprite as ADDED, UPDATED, MOVED, REMOVED depending on the actions applied to it ; on this frame. Quick note, the same Sprite ID cannot be removed and added in the same frame. @@ -68,6 +61,7 @@ SPRITE_HEIGHT equ {MAX_SPRITES*32} SPRITE_CLIP_WIDTH equ {MAX_SPRITES*34} SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*36} TS_VBUFF_BASE equ {MAX_SPRITES*38} ; Finalized VBUFF address based on the sprite position and tile offsets +VBUFF_ARRAY_ADDR equ {MAX_SPRITES*40} ; Fixed address where this sprite's VBUFF addresses are stores. The array is the same shape as TileStore, but much smaller ;TILE_DATA_OFFSET equ {MAX_SPRITES*2} ;TILE_STORE_ADDR_1 equ {MAX_SPRITES*12} ;TILE_STORE_ADDR_2 equ {MAX_SPRITES*14} diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index 780d319..298c100 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -6,8 +6,8 @@ ; If there are sprites, then the sprite data is flattened and stored into a direct page buffer ; and then copied into the code field _RenderTileFast -; lda TileStore+TS_VBUFF_ADDR_COUNT,x ; How many sprites are on this tile? -; bne SpriteDispatch ; This is faster if there are no sprites + lda TileStore+TS_SPRITE_FLAG,x ; any sprites on this line? + bne SpriteDispatch NoSpriteFast lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line @@ -15,7 +15,7 @@ NoSpriteFast ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field lda TileStore+TS_BASE_TILE_DISP,x ; go to the tile copy routine (just basics) stal nsf_patch+1 - lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) + lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) plb ; set the code field bank nsf_patch jmp $0000 @@ -24,35 +24,60 @@ nsf_patch jmp $0000 FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast ; dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast -SpriteDispatch - tax - jmp (:,x) ; Dispatch to the other routines -: da NoSpriteFast ; Placeholder - da OneSpriteFast - da TwoSpritesFast - da ThreeSpritesFast - da FourSpritesFast +; NOTE: Inlining the dispatch would eliminate a JSR,RTS,LDX, and JMP (abs,x) because the exit code +; could jump directly to the target address. Net savings of 20 cycles per tile. For a 16x16 +; sprite with a 3x3 block coverage this is 180 cycles per frame per block... This would also +; preserve a register +; +; For comparison, a fast one sprite copy takes 22 cycles per word, so this would save +; about 1/2 block of render time per tile. +; +; Need to determine if the sprite or tile data is on top, as that will decide whether the +; sprite or tile data is copied into the temporary buffer first. Also, if TWO_LAYER is set +; then the mask information must be copied as well....This is the last decision point. -; Pointers to sprite data and masks -spritedata_0 equ tmp0 -spritedata_1 equ tmp2 -spritedata_2 equ tmp4 -spritedata_3 equ tmp6 -spritemask_0 equ tmp8 -spritemask_1 equ tmp10 -spritemask_2 equ tmp12 -spritemask_3 equ tmp14 +SpriteDispatch + txy + SpriteBitsToVBuffAddrs OneSpriteFast;OneSpriteFast;OneSpriteFast;OneSpriteFast + sta sprite_ptr0 + ldx TileStore+TS_TILE_ADDR,y + jsr _CopyTileDataToDP2 ; preserves Y + lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line + pha ; and put on the stack for later. Has TileStore bank in high byte. + ldx sprite_ptr0 ; address of sprite vbuff info + lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field + tay +; jmp _TBApplySpriteData2 + +_TBApplySpriteData2 +]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 + --^ + rts ; Where there are sprites involved, the first step is to call a routine to copy the ; tile data into a temporary buffer. Then the sprite data is merged and placed into ; the code field. +; +; A = vbuff address +; Y = tile store address OneSpriteFast - tyx - lda TileStore+TS_TILE_ADDR,y - per :-1 - jmp (TileStore+TS_BASE_TILE_COPY,x) ; Copy the tile data to the temporary buffer -: - ldx TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data + tax ; address of the sprite data + lda TileStore+TS_BASE_TILE_COPY,y ; copy routine (handles flips and other behaviors) + stal osf_copy+1 +osf_copy jsr $0000 + +; ldx TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line pha ; and put on the stack for later. lda TileStore+TS_CODE_ADDR_LOW,y @@ -71,17 +96,17 @@ OneSpriteFast rts TwoSpritesFast - tyx - lda TileStore+TS_TILE_ADDR,y - per :-1 - jmp (TileStore+TS_BASE_TILE_COPY,x) ; Copy the tile data to the temporary buffer -: - lda TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data - sta spritedata_0 - sta spritemask_0 - lda TileStore+TS_VBUFF_ADDR_1,y ; address of the sprite data - sta spritedata_1 - sta spritemask_1 +; tyx +; lda TileStore+TS_TILE_ADDR,y +; per :-1 +; jmp (TileStore+TS_BASE_TILE_COPY,x) ; Copy the tile data to the temporary buffer +;: +; lda TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data +; sta spritedata_0 +; sta spritemask_0 +; lda TileStore+TS_VBUFF_ADDR_1,y ; address of the sprite data +; sta spritedata_1 +; sta spritemask_1 lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line pha ; and put on the stack for later. @@ -89,33 +114,33 @@ TwoSpritesFast tay plb ; set the code field bank - TwoSpritesToCodeField 0 - TwoSpritesToCodeField 1 - TwoSpritesToCodeField 2 - TwoSpritesToCodeField 3 - TwoSpritesToCodeField 4 - TwoSpritesToCodeField 5 - TwoSpritesToCodeField 6 - TwoSpritesToCodeField 7 +; TwoSpritesToCodeField 0 +; TwoSpritesToCodeField 1 +; TwoSpritesToCodeField 2 +; TwoSpritesToCodeField 3 +; TwoSpritesToCodeField 4 +; TwoSpritesToCodeField 5 +; TwoSpritesToCodeField 6 +; TwoSpritesToCodeField 7 rts ThreeSpritesFast FourSpritesFast - tyx - lda TileStore+TS_TILE_ADDR,y - per :-1 - jmp (TileStore+TS_BASE_TILE_COPY,x) ; Copy the tile data to the temporary buffer -: - lda TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data - sta spritedata_0 - sta spritemask_0 - lda TileStore+TS_VBUFF_ADDR_1,y - sta spritedata_1 - sta spritemask_1 - lda TileStore+TS_VBUFF_ADDR_2,y - sta spritedata_2 - sta spritemask_2 +; tyx +; lda TileStore+TS_TILE_ADDR,y +; per :-1 +; jmp (TileStore+TS_BASE_TILE_COPY,x) ; Copy the tile data to the temporary buffer +;: +; lda TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data +; sta spritedata_0 +; sta spritemask_0 +; lda TileStore+TS_VBUFF_ADDR_1,y +; sta spritedata_1 +; sta spritemask_1 +; lda TileStore+TS_VBUFF_ADDR_2,y +; sta spritedata_2 +; sta spritemask_2 lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line pha ; and put on the stack for later. @@ -123,13 +148,13 @@ FourSpritesFast tay plb ; set the code field bank - ThreeSpritesToCodeField 0 - ThreeSpritesToCodeField 1 - ThreeSpritesToCodeField 2 - ThreeSpritesToCodeField 3 - ThreeSpritesToCodeField 4 - ThreeSpritesToCodeField 5 - ThreeSpritesToCodeField 6 - ThreeSpritesToCodeField 7 +; ThreeSpritesToCodeField 0 +; ThreeSpritesToCodeField 1 +; ThreeSpritesToCodeField 2 +; ThreeSpritesToCodeField 3 +; ThreeSpritesToCodeField 4 +; ThreeSpritesToCodeField 5 +; ThreeSpritesToCodeField 6 +; ThreeSpritesToCodeField 7 rts \ No newline at end of file From 10d6dc5931d14f5803693d446bd23b5fe777df37 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 31 May 2022 09:14:25 -0500 Subject: [PATCH 27/82] Fix crashing when trying to render sprite; sprite not visible but have end-to-end code execution --- src/Sprite.s | 1 + src/Tool.s | 2 +- src/tiles/FastRenderer.s | 58 +++++++++++++++++++++++----------------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/Sprite.s b/src/Sprite.s index e8d685d..2eda837 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -143,6 +143,7 @@ _DoPhase1 bit #SPRITE_STATUS_REMOVED beq :no_clear + brk $02 lda _SpriteBits,y ; Clear from the sprite bitmap sta SpriteRemovedFlag ; Stick a non-zero value here diff --git a/src/Tool.s b/src/Tool.s index 1f4315f..ec94670 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -335,7 +335,7 @@ _TSRemoveSprite _TSEntry lda :spriteSlot,s - jsr _UpdateSprite + jsr _RemoveSprite _TSExit #0;#2 diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index 298c100..72f61d0 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -39,31 +39,6 @@ FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFa SpriteDispatch txy SpriteBitsToVBuffAddrs OneSpriteFast;OneSpriteFast;OneSpriteFast;OneSpriteFast - sta sprite_ptr0 - ldx TileStore+TS_TILE_ADDR,y - jsr _CopyTileDataToDP2 ; preserves Y - lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line - pha ; and put on the stack for later. Has TileStore bank in high byte. - ldx sprite_ptr0 ; address of sprite vbuff info - lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field - tay -; jmp _TBApplySpriteData2 - -_TBApplySpriteData2 -]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 - --^ - rts ; Where there are sprites involved, the first step is to call a routine to copy the ; tile data into a temporary buffer. Then the sprite data is merged and placed into @@ -72,6 +47,39 @@ _TBApplySpriteData2 ; A = vbuff address ; Y = tile store address OneSpriteFast + sta sprite_ptr0 + ldx TileStore+TS_TILE_ADDR,y + jsr _CopyTileDataToDP2 ; preserves Y + lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line + pha ; and put on the stack for later. Has TileStore bank in high byte. + ldx sprite_ptr0 ; address of sprite vbuff info + lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field + tay + plb + +; jmp _TBApplySpriteData2 + +_TBApplySpriteData2 +]line equ 0 + lup 8 + lda blttmp+{]line*4} + andl spritemask+{]line*SPRITE_PLANE_SPAN},x + oral spritedata+{]line*SPRITE_PLANE_SPAN},x +; lda #$FFFF + 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 +; lda #$FFFF + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + plb + rts + + +OneSpriteFastX tax ; address of the sprite data lda TileStore+TS_BASE_TILE_COPY,y ; copy routine (handles flips and other behaviors) stal osf_copy+1 From bba8929db762816c5ee0ddfa7f1740caf25b29ad Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 31 May 2022 13:01:09 -0500 Subject: [PATCH 28/82] PRoperly initialize Lookup tables --- src/static/TileStore.s | 31 ++++++++++++++++++++++++++++--- src/static/TileStoreDefs.s | 11 ++++++----- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/static/TileStore.s b/src/static/TileStore.s index f03cf02..0ac2d33 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -36,10 +36,35 @@ _Sprites ENT ; can be looked up by adding a constant value. ds \,$00 ; pad to the next page boundary TileStoreLookupYTable ENT - ds {TS_LOOKUP_HEIGHT*2} -TileStoreLookup ENT - ds {TS_LOOKUP_SPAN*TS_LOOKUP_ROWS*2} +]line equ 0 + lup TS_LOOKUP_HEIGHT + dw ]line +]line equ ]line+{2*TS_LOOKUP_SPAN} + --^ +; Width of tile store is 41 elements +TileStoreData mac + dw ]1+0,]1+2,]1+4,]1+6,]1+8,]1+10,]1+12,]1+14 + dw ]1+16,]1+18,]1+20,]1+22,]1+24,]1+26,]1+28,]1+30 + dw ]1+32,]1+34,]1+36,]1+38,]1+40,]1+42,]1+44,]1+46 + dw ]1+48,]1+50,]1+52,]1+54,]1+56,]1+58,]1+60,]1+62 + dw ]1+64,]1+66,]1+68,]1+70,]1+72,]1+74,]1+76,]1+78 + dw ]1+80 + <<< +; Create a lookup table with two runs of offsets, plus an overlap area on the end (41+41+1 = 83 = TS_LOOKUP_SPAN) +TileStoreLookup ENT +]row equ 0 + lup TILE_STORE_HEIGHT + TileStoreData ]row*2*TILE_STORE_WIDTH + TileStoreData ]row*2*TILE_STORE_WIDTH + dw ]row*2*TILE_STORE_WIDTH +]row equ ]row+1 + --^ + +; ds {TS_LOOKUP_SPAN*TS_LOOKUP_ROWS*2} +; +;TILE_STORE_WIDTH equ 41 +;TILE_STORE_HEIGHT equ 26 ;------------------------------------------------------------------------------------- ; ; Other data tables diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index 2ab791a..e08bc61 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -75,11 +75,12 @@ VBUFF_ARRAY_ADDR equ {MAX_SPRITES*40} ; Fixed address where this spri ;TILE_STORE_ADDR_9 equ {MAX_SPRITES*28} ;TILE_STORE_ADDR_10 equ {MAX_SPRITES*30} -; 50 rows by 80 columns + 2 extra rows and columns -TS_LOOKUP_WIDTH equ 80 -TS_LOOKUP_HEIGHT equ 50 -TS_LOOKUP_SPAN equ {TS_LOOKUP_WIDTH+2} -TS_LOOKUP_ROWS equ {TS_LOOKUP_HEIGHT+2} +; 51 rows by 81 columns + 2 extra rows and columns for sprite sizes +TS_LOOKUP_WIDTH equ 81 +TS_LOOKUP_HEIGHT equ 51 +TS_LOOKUP_BORDER equ 2 +TS_LOOKUP_SPAN equ {TS_LOOKUP_WIDTH+TS_LOOKUP_BORDER} +TS_LOOKUP_ROWS equ {TS_LOOKUP_HEIGHT+TS_LOOKUP_BORDER} ; Blitter template constancts PER_TILE_SIZE equ 3 From 98adfe5daa4cc8f53108d331560bcf8857593a7d Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 31 May 2022 16:31:44 -0500 Subject: [PATCH 29/82] Continuing to tweak and debug --- src/Sprite.s | 14 ++- src/Sprite2.s | 72 +++++++------ src/SpriteV1.s | 226 +++++++++++++++++++++++++++++++++++++++ src/tiles/FastRenderer.s | 7 +- 4 files changed, 276 insertions(+), 43 deletions(-) create mode 100644 src/SpriteV1.s diff --git a/src/Sprite.s b/src/Sprite.s index 2eda837..f18a4a3 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -38,7 +38,7 @@ InitSprites adc #4*2 ; skip ahead 4 tiles inx inx - cpx #8*2 + cpx #16*2 bcc :loop4 ; Precalculate some bank values @@ -153,7 +153,7 @@ _DoPhase1 jmp _ClearSpriteFromTileStore ; Clear the tile flags, add to the dirty tile list and done -; Need to calculate new VBUFF information. The could be reuqired for UPDATED, ADDED or MOVED +; Need to calculate new VBUFF information. The could be required for UPDATED, ADDED or MOVED ; sprites, so we do it unconditionally. :no_clear jsr _CalcDirtySprite @@ -648,6 +648,7 @@ _PrecalcAllSpriteInfo sbc _Sprites+SPRITE_CLIP_TOP,x inc sta _Sprites+SPRITE_CLIP_HEIGHT,x + rts :offscreen @@ -691,14 +692,19 @@ _UpdateSprite tax pla - cmp _Sprites+SPRITE_ID,x ; If the flags changed, need to redraw the sprite - bne :sprite_flag_change ; on the next frame +; Do some work to see if only the H or V bits have changed. If so, merge them into the +; SPRITE_ID + eor _Sprites+SPRITE_ID,x ; If either bit has changed, this will be non-zero + and #SPRITE_VFLIP+SPRITE_HFLIP + bne :sprite_flag_change + tya cmp _Sprites+VBUFF_ADDR,x ; Did the stamp change? bne :sprite_stamp_change rts ; Nothing changed, so just return :sprite_flag_change + eor _Sprites+SPRITE_ID,x ; put the new bits into the value. ---HV--- ^ SPRITE_ID & 00011000 ^ SPRITE_ID = SSSHVSSS sta _Sprites+SPRITE_ID,x ; Keep a copy of the full descriptor tya :sprite_stamp_change diff --git a/src/Sprite2.s b/src/Sprite2.s index 4776a6f..ab43684 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -61,15 +61,18 @@ _CalcDirtySprite sta _Sprites+OLD_TS_LOOKUP_INDEX,y ; Add the first visible row of the sprite to the Y-scroll offset to find the first line in the -; code field that needs to be drawn. The range of values is 0 to 199+207 = [0, 406] +; code field that needs to be drawn. The range of values is 0 to 199+207 = [0, 406]. This +; value is dividede by 8, so the range of lookup values is [0, 50], so 51 possible values. clc lda _Sprites+SPRITE_CLIP_TOP,y adc StartYMod208 ; Adjust for the scroll offset - tax ; Cache + pha ; Cache and #$FFF8 ; mask first to ensure LSR will clear the carry lsr lsr + tax + lda TileStoreLookupYTable,x sta RowTop ; Even numbers from [0, 100] (50 elements) ; Get the position of the top edge within the tile and then add it to the sprite's height @@ -78,7 +81,7 @@ _CalcDirtySprite ; that are intersected, rather than assuming an 8x8 sprite always takes up that amount of ; space. - txa + pla and #$0007 tax ; cache again. This is a bit faster than recalculating @@ -94,7 +97,8 @@ _CalcDirtySprite sta tmp0 ; Add the horizontal position to the horizontal offset to find the first column in the -; code field that needs to be drawn. The range of values is 0 to 159+163 = [0, 322] +; code field that needs to be drawn. The range of values is 0 to 159+163 = [0, 322]. +; This value is divided by 4, so 81 possible values clc lda _Sprites+SPRITE_CLIP_LEFT,y @@ -201,8 +205,8 @@ TSSetSprite mac next <<< -ROW equ TILE_STORE_WIDTH*2 -COL equ TILE_DATA_SPAN +ROW equ TILE_STORE_WIDTH*2 ; This many bytes to the next row in TileStore coordinates +COL equ 2 ; This many bytes for each element :mark1x1 ldx _Sprites+VBUFF_ARRAY_ADDR,y ; get the address of this sprite's vbuff values @@ -264,10 +268,10 @@ COL equ TILE_DATA_SPAN sta: {1*ROW}+{1*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2;#{1*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2 rts :mark2x3 @@ -286,12 +290,12 @@ COL equ TILE_DATA_SPAN sta: {1*ROW}+{2*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+4;#{0*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2;#{1*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+4;#{1*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+4 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+4 rts :mark3x1 @@ -304,9 +308,9 @@ COL equ TILE_DATA_SPAN sta: {2*ROW}+{0*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 2*{TS_LOOKUP_SPAN*2}+0;#{2*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+0 rts :mark3x2 @@ -325,12 +329,12 @@ COL equ TILE_DATA_SPAN sta: {2*ROW}+{1*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2;#{1*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} - TSSetSprite 2*{TS_LOOKUP_SPAN*2}+0;#{2*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 2*{TS_LOOKUP_SPAN*2}+2;#{2*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2 + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+2 rts :mark3x3 @@ -355,13 +359,13 @@ COL equ TILE_DATA_SPAN sta: {2*ROW}+{2*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0;#{0*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2;#{0*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} - TSSetSprite 0*{TS_LOOKUP_SPAN*2}+4;#{0*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0;#{1*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2;#{1*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} - TSSetSprite 1*{TS_LOOKUP_SPAN*2}+4;#{1*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} - TSSetSprite 2*{TS_LOOKUP_SPAN*2}+0;#{2*VBUFF_TILE_ROW_BYTES}+{0*VBUFF_TILE_COL_BYTES} - TSSetSprite 2*{TS_LOOKUP_SPAN*2}+2;#{2*VBUFF_TILE_ROW_BYTES}+{1*VBUFF_TILE_COL_BYTES} - TSSetSprite 2*{TS_LOOKUP_SPAN*2}+4;#{2*VBUFF_TILE_ROW_BYTES}+{2*VBUFF_TILE_COL_BYTES} + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+4 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+4 + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+2 + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+4 rts diff --git a/src/SpriteV1.s b/src/SpriteV1.s new file mode 100644 index 0000000..6d24dbb --- /dev/null +++ b/src/SpriteV1.s @@ -0,0 +1,226 @@ +; Old code the was in Version 1, but is not needed. May be adapted for Verions 2. + +; Y = _Sprites array offset +_EraseSpriteY + lda _Sprites+OLD_VBUFF_ADDR,y + beq :noerase + ldx _Sprites+SPRITE_DISP,y ; get the dispatch index for this sprite (32 values) + jmp (:do_erase,x) +:noerase rts +:do_erase dw _EraseTileSprite8x8,_EraseTileSprite8x8,_EraseTileSprite8x8,_EraseTileSprite8x8 + dw _EraseTileSprite8x16,_EraseTileSprite8x16,_EraseTileSprite8x16,_EraseTileSprite8x16 + dw _EraseTileSprite16x8,_EraseTileSprite16x8,_EraseTileSprite16x8,_EraseTileSprite16x8 + dw _EraseTileSprite16x16,_EraseTileSprite16x16,_EraseTileSprite16x16,_EraseTileSprite16x16 + dw _EraseTileSprite8x8,_EraseTileSprite8x8,_EraseTileSprite8x8,_EraseTileSprite8x8 + dw _EraseTileSprite8x16,_EraseTileSprite8x16,_EraseTileSprite8x16,_EraseTileSprite8x16 + dw _EraseTileSprite16x8,_EraseTileSprite16x8,_EraseTileSprite16x8,_EraseTileSprite16x8 + dw _EraseTileSprite16x16,_EraseTileSprite16x16,_EraseTileSprite16x16,_EraseTileSprite16x16 + +; A = bank address +_EraseTileSprite8x8 + tax + phb ; Save the bank to switch to the sprite plane + + pei SpriteBanks + plb ; pop the data bank (low byte) + +]line equ 0 + lup 8 + stz: {]line*SPRITE_PLANE_SPAN}+0,x + stz: {]line*SPRITE_PLANE_SPAN}+2,x +]line equ ]line+1 + --^ + + plb ; pop the mask bank (high byte) + lda #$FFFF +]line equ 0 + lup 8 + sta: {]line*SPRITE_PLANE_SPAN}+0,x + sta: {]line*SPRITE_PLANE_SPAN}+2,x +]line equ ]line+1 + --^ + + plb + rts + +_EraseTileSprite8x16 + tax + phb ; Save the bank to switch to the sprite plane + + pei SpriteBanks + plb ; pop the data bank (low byte) + +]line equ 0 + lup 16 + stz: {]line*SPRITE_PLANE_SPAN}+0,x + stz: {]line*SPRITE_PLANE_SPAN}+2,x +]line equ ]line+1 + --^ + + plb ; pop the mask bank (high byte) + lda #$FFFF +]line equ 0 + lup 16 + sta: {]line*SPRITE_PLANE_SPAN}+0,x + sta: {]line*SPRITE_PLANE_SPAN}+2,x +]line equ ]line+1 + --^ + + plb + rts + +_EraseTileSprite16x8 + tax + phb ; Save the bank to switch to the sprite plane + + pei SpriteBanks + plb ; pop the data bank (low byte) + +]line equ 0 + lup 8 + stz: {]line*SPRITE_PLANE_SPAN}+0,x + stz: {]line*SPRITE_PLANE_SPAN}+2,x + stz: {]line*SPRITE_PLANE_SPAN}+4,x + stz: {]line*SPRITE_PLANE_SPAN}+6,x +]line equ ]line+1 + --^ + + plb ; pop the mask bank (high byte) + lda #$FFFF +]line equ 0 + lup 8 + sta: {]line*SPRITE_PLANE_SPAN}+0,x + sta: {]line*SPRITE_PLANE_SPAN}+2,x + sta: {]line*SPRITE_PLANE_SPAN}+4,x + sta: {]line*SPRITE_PLANE_SPAN}+6,x +]line equ ]line+1 + --^ + + plb + rts + +_EraseTileSprite16x16 + tax + phb ; Save the bank to switch to the sprite plane + + pei SpriteBanks + plb ; pop the data bank (low byte) + +]line equ 0 + lup 16 + stz: {]line*SPRITE_PLANE_SPAN}+0,x + stz: {]line*SPRITE_PLANE_SPAN}+2,x + stz: {]line*SPRITE_PLANE_SPAN}+4,x + stz: {]line*SPRITE_PLANE_SPAN}+6,x +]line equ ]line+1 + --^ + + plb ; pop the mask bank (high byte) + + lda #$FFFF +]line equ 0 + lup 16 + sta: {]line*SPRITE_PLANE_SPAN}+0,x + sta: {]line*SPRITE_PLANE_SPAN}+2,x + sta: {]line*SPRITE_PLANE_SPAN}+4,x + sta: {]line*SPRITE_PLANE_SPAN}+6,x +]line equ ]line+1 + --^ + + plb + rts + + +; First, if there is only one sprite, then we can skip any overhead and do a single lda/and/ora/sta to put the +; sprite data on the screen. +; +; Second, if there are 4 or less, then we "stack" the sprite data using an unrolled loop that allows each +; sprite to just be a single and/ora pair and the final result is not written to any intermediate memory buffer. +; +; Third, if there are 5 or more sprites, then we assume that the sprites are "dense" and that there will be a +; non-trivial amount of overdraw. In this case we do a series of optimized copies of the sprite data *and* +; masks into a direct page buffer in *reverse order*. Once a mask value becomes zero, then nothing else can +; show through and that value can be skipped. Once all of the mask values are zero, then the render is terminated +; and the data buffer copied to the final destination. +; +; Note that these rendering algorithms impose a priority ordering on the sprites where lower sprite IDs are drawn +; underneath higher sprite IDs. +RenderActiveSpriteTiles + cmp #0 ; Is there only one active sprite? If so optimise + bne :many + + ldx vbuff ; load the address to the (adjusted) sprite tile + lda TileStore+TS_SCREEN_ADDR,y + tay + + lda tiledata+0,y + andl spritemask,x + oral spritedata,x + sta 00,s + + lda tiledata+2,y + andl spritemask+2,x + oral spritedata+2,x + sta 02,s + + ... + tsc + adc #320 + tcs + ... + + lda tiledata+{line*4},y + andl spritemask+{line*SPAN},x + oral spritedata+{line*SPAN},x + sta 160,s + + lda tiledata+{line*4}+2,y + andl spritemask+{line*SPAN}+2,x + oral spritedata+{line*SPAN}+2,x + sta 162,s + + rts + + +:many + lda TileStore+TS_SCREEN_ADDR,y + tcs + lda TileStore+TS_TILE_ADDR,y + tay + + ldx count + jmp (:arr,x) + lda tiledata+0,y + ldx vbuff + andl spritemask,x + oral spritedata,x + ldx vbuff+2 + andl spritemask,x + oral spritedata,x + ldx vbuff+4 + andl spritemask,x + oral spritedata,x + ... + sta 00,s + + ldx count + jmp (:arr,x) + lda tiledata+0,y + ldx vbuff + andl spritemask,x + oral spritedata,x + ldx vbuff+2 + andl spritemask,x + oral spritedata,x + ldx vbuff+4 + andl spritemask,x + oral spritedata,x + ... + sta 02,s + + sta 160,s + + sta 162,s + + tsc + adc #320 \ No newline at end of file diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index 72f61d0..f8654b7 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -58,20 +58,17 @@ OneSpriteFast plb ; jmp _TBApplySpriteData2 - _TBApplySpriteData2 ]line equ 0 lup 8 - lda blttmp+{]line*4} + lda tmp_tile_data+{]line*4} andl spritemask+{]line*SPRITE_PLANE_SPAN},x oral spritedata+{]line*SPRITE_PLANE_SPAN},x -; lda #$FFFF sta: $0004+{]line*$1000},y - lda blttmp+{]line*4}+2 + lda tmp_tile_data+{]line*4}+2 andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x -; lda #$FFFF sta: $0001+{]line*$1000},y ]line equ ]line+1 --^ From dec50bc6fc7ff7018d9bb418caabbf4643723abd Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 31 May 2022 22:53:33 -0500 Subject: [PATCH 30/82] Fix several bugs; biggest one in the SpriteStamp routine --- demos/tool/App.Main.s | 21 ++++++++++++----- src/Sprite.s | 3 +-- src/Sprite2.s | 14 +++++++----- src/SpriteRender.s | 46 ++++++++++++-------------------------- src/blitter/Tiles10001.s | 8 +++---- src/blitter/Tiles10011.s | 10 ++++----- src/blitter/Tiles11001.s | 2 +- src/blitter/Tiles11011.s | 4 ++-- src/tiles/DirtyTileQueue.s | 2 +- 9 files changed, 51 insertions(+), 59 deletions(-) diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 8c3e922..a63e935 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -43,14 +43,14 @@ ScreenY equ 2 HERO_SPRITE_1 equ SPRITE_16X16+1 HERO_SLOT equ 0 - pea HERO_SPRITE_1 ; sprinte id + pea HERO_SPRITE_1 ; sprint id pea VBUFF_SPRITE_START ; vbuff address _GTECreateSpriteStamp ; Create sprites pea HERO_SPRITE_1 ; sprite id - pea #10 ; screen x-position (<256) - pea #8 ; screen y-position (<256) + pea #0 ; screen x-position (<256) + pea #0 ; screen y-position (<256) pea HERO_SLOT ; sprite slot (0 - 15) _GTEAddSprite @@ -70,7 +70,10 @@ HERO_SLOT equ 0 phx phy - pei 0 + lda 0 + clc + adc #64 + pha _GTESetTile lda 0 @@ -90,10 +93,10 @@ HERO_SLOT equ 0 bcc :loop ; Set the origin of the screen +:skip stz ScreenX - lda #63 - sta ScreenY + stz ScreenY ; Very simple actions :evt_loop @@ -128,6 +131,12 @@ HERO_SLOT equ 0 pei ScreenY _GTESetBG0Origin +; Update the sprite each frame for testing +; pea HERO_SLOT +; pea $0000 +; pea VBUFF_SPRITE_START +; _GTEUpdateSprite + _GTERender ; Debug stuff diff --git a/src/Sprite.s b/src/Sprite.s index f18a4a3..63f7472 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -143,7 +143,6 @@ _DoPhase1 bit #SPRITE_STATUS_REMOVED beq :no_clear - brk $02 lda _SpriteBits,y ; Clear from the sprite bitmap sta SpriteRemovedFlag ; Stick a non-zero value here @@ -315,7 +314,7 @@ _AddSprite ; 3. Checks if the tile is dirty and marks it ; 4. If the tile was dirty, save the tile store address to be added to the DirtyTiles list later TSClearSprite mac - ldy TileStoreLookup+]1,x + ldy TileStoreLookup+{]1},x lda TileStore+TS_SPRITE_FLAG,y and tmp0 diff --git a/src/Sprite2.s b/src/Sprite2.s index ab43684..fc02f5d 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -5,16 +5,16 @@ ;Top equ tmp3 ;Bottom equ tmp4 -Origin equ tmp4 -TileTop equ tmp5 +;Origin equ tmp4 +;TileTop equ tmp5 RowTop equ tmp6 AreaIndex equ tmp7 -TileLeft equ tmp8 -ColLeft equ tmp9 +;TileLeft equ tmp8 +;ColLeft equ tmp9 SpriteBit equ tmp10 ; set the bit of the value that if the current sprite index -VBuffOrigin equ tmp11 +; VBuffOrigin equ tmp11 ; Marks a sprite as dirty. The work here is mapping from local screen coordinates to the ; tile store indices. The first step is to adjust the sprite coordinates based on the current @@ -122,6 +122,7 @@ _CalcDirtySprite txa and #$0003 tax + clc adc tmp0 ; add to the vertical offset ; Subtract this value from the SPRITE_DISP address @@ -136,6 +137,7 @@ _CalcDirtySprite ; location of the upper-left corner of the sprite within the corner tile. txa + clc adc _Sprites+SPRITE_CLIP_WIDTH,y ; max width = 8 = 0x08 dec and #$000C @@ -184,7 +186,7 @@ _MarkDirtySpriteTiles ; The second macro is the same as the first, but the VBUFF calculation is moved up so that the value ; from the previous step can be reused and save a load every other step. TSSetSprite mac - ldy TileStoreLookup+]1,x + ldy TileStoreLookup+{]1},x lda SpriteBit ora TileStore+TS_SPRITE_FLAG,y diff --git a/src/SpriteRender.s b/src/SpriteRender.s index 4eed2a3..4f85020 100644 --- a/src/SpriteRender.s +++ b/src/SpriteRender.s @@ -4,44 +4,24 @@ ; Y = VBUFF address ; X = Tile Data address ; A = Sprite Flags +DISP_VFLIP equ $0004 ; hard code these because they are internal values +DISP_HFLIP equ $0002 +DISP_MASK equ $0018 ; Preserve the size bits + _DrawSpriteStamp sty tmp1 stx tmp2 + xba and #DISP_MASK ; dispatch to all of the different orientations sta tmp3 - jmp _DSSCommon -; Function to render a sprite from a sprite definition into the internal data buffers -; -; X = sprite index -; _DrawSpriteSheet -DISP_VFLIP equ $0004 ; hard code these because they are internal values -DISP_HFLIP equ $0002 -DISP_MASK equ $0018 ; Isolate the size bits - -; phx -; -; lda _Sprites+VBUFF_ADDR,x -; sta tmp1 -; -; lda _Sprites+TILE_DATA_OFFSET,x -; sta tmp2 -; -; lda _Sprites+SPRITE_DISP,x -; and #DISP_MASK ; dispatch to all of the different orientations -; sta tmp3 -; -; jsr _DSSCommon -; -; plx -; rts - -_DSSCommon -; Set bank phb pea #^tiledata ; Set the bank to the tile data plb +; X = sprite ID +; Y = Tile Data +; A = VBUFF address ldx tmp3 ldy tmp2 lda tmp1 @@ -53,7 +33,7 @@ _DSSCommon ldy tmp2 lda tmp1 clc - adc #4*3 + adc #3*4 jsr _DrawSprite lda tmp3 @@ -62,7 +42,7 @@ _DSSCommon ldy tmp2 lda tmp1 clc - adc #4*6 + adc #6*4 jsr _DrawSprite lda tmp3 @@ -71,7 +51,7 @@ _DSSCommon ldy tmp2 lda tmp1 clc - adc #4*9 + adc #9*4 jsr _DrawSprite ; Restore bank @@ -81,7 +61,6 @@ _DSSCommon ; ; X = _Sprites array offset _DrawSprite -; ldx _Sprites+SPRITE_DISP,y ; use bits 9, 10, 11, 12 and 13 to dispatch jmp (draw_sprite,x) draw_sprite dw draw_8x8,draw_8x8h,draw_8x8v,draw_8x8hv @@ -183,6 +162,9 @@ draw_16x8hv ply jmp _DrawTile8x8V +; X = sprite ID +; Y = Tile Data +; A = VBUFF address draw_16x16 clc tax diff --git a/src/blitter/Tiles10001.s b/src/blitter/Tiles10001.s index 438921f..d834eca 100644 --- a/src/blitter/Tiles10001.s +++ b/src/blitter/Tiles10001.s @@ -70,13 +70,13 @@ CopyDynSpriteWord MAC ; ; If MASK == 0, then we can do a PEA. If MASK == $FFFF, then fall back to the simple Dynamic Tile ; code. - ldal spritemask+]1,x ; load the mask value + ldal spritemask+{]1},x ; load the mask value bne mixed ; a non-zero value may be mixed ; This is a solid word lda #$00F4 ; PEA instruction sta: ]2,y - ldal spritedata+]1,x ; load the sprite data + ldal spritedata+{]1},x ; load the sprite data sta: ]2+1,y ; PEA operand bra next @@ -95,12 +95,12 @@ mixed cmp #$FFFF ; All 1's in the mask is a fully transpare lda #$0029 ; AND #SPRITE_MASK sta: $0002,y - ldal spritemask+]1,x + ldal spritemask+{]1},x sta: $0003,y lda #$0009 ; ORA #SPRITE_DATA sta: $0005,y - ldal spritedata+]1,x + ldal spritedata+{]1},x sta: $0006,y lda #$0D80 ; branch to the prologue (BRA *+15) diff --git a/src/blitter/Tiles10011.s b/src/blitter/Tiles10011.s index 51a4e0c..9778560 100644 --- a/src/blitter/Tiles10011.s +++ b/src/blitter/Tiles10011.s @@ -82,13 +82,13 @@ CopyDynMaskedSpriteWord MAC ; If MASK == 0, then we can do a PEA. If MASK == $FFFF, then fall back to the simple Dynamic Tile ; code and eliminate the constanct AND/ORA instructions. - ldal spritemask+]1,x ; load the mask value + ldal spritemask+{]1},x ; load the mask value bne mixed ; a non-zero value may be mixed ; This is a solid word lda #$00F4 ; PEA instruction sta: ]2,y - ldal spritedata+]1,x ; load the sprite data + ldal spritedata+{]1},x ; load the sprite data sta: ]2+1,y ; PEA operand bra next @@ -99,7 +99,7 @@ mixed sta: ]2,y lda _JTBL_CACHE ; Get the offset to the exception handler for this column ora #{]2&$F000} ; adjust for the current row offset - sta: ]2+1,y + sta: {]2}+1,y tay ; This becomes the new address that we use to patch in lda _OP_CACHE @@ -111,14 +111,14 @@ mixed lda #$0029 ; AND #SPRITE_MASK sta: $0006,y - ldal spritemask+]1,x + ldal spritemask+{]1},x cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word beq transparent ; so we can use the Tile00011 method sta: $0007,y lda #$0009 ; ORA #SPRITE_DATA sta: $0009,y - ldal spritedata+]1,x + ldal spritedata+{]1},x sta: $000A,y lda #$0980 ; branch to the prologue (BRA *+11) diff --git a/src/blitter/Tiles11001.s b/src/blitter/Tiles11001.s index c33232d..eafc342 100644 --- a/src/blitter/Tiles11001.s +++ b/src/blitter/Tiles11001.s @@ -77,7 +77,7 @@ CopyDynPriSpriteWord MAC lda #$00A9 ; LDA #DATA sta: $0000,y - ldal spritedata+]1,x + ldal spritedata+{]1},x sta: $0001,y lda _OP_CACHE diff --git a/src/blitter/Tiles11011.s b/src/blitter/Tiles11011.s index d306cb3..2f6f9a3 100644 --- a/src/blitter/Tiles11011.s +++ b/src/blitter/Tiles11011.s @@ -90,14 +90,14 @@ CopyDynPrioMaskedSpriteWord MAC lda #$0029 ; AND #SPRITE_MASK sta: $0002,y - ldal spritemask+]1,x + ldal spritemask+{]1},x cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word beq transparent ; so we can use the Tile00011 method sta: $0003,y lda #$0009 ; ORA #SPRITE_DATA sta: $0005,y - ldal spritedata+]1,x + ldal spritedata+{]1},x sta: $0006,y lda _T_PTR diff --git a/src/tiles/DirtyTileQueue.s b/src/tiles/DirtyTileQueue.s index 3e58b58..7a2599d 100644 --- a/src/tiles/DirtyTileQueue.s +++ b/src/tiles/DirtyTileQueue.s @@ -93,7 +93,7 @@ pdtf_not_empty cpx #16 ; If there are >= 8 elements, then bcs full_chunk ; do a full chunk - stz DP2_DIRTY_TILE_COUNT ; Otherwise, this pass will handle them all +; stz DP2_DIRTY_TILE_COUNT ; Otherwise, this pass will handle them all jmp (at_table,x) at_table da at_exit,at_one,at_two,at_three da at_four,at_five,at_six,at_seven From cde517bcc9981c920aabf4b87cfa06321f05cde8 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 1 Jun 2022 07:50:19 -0500 Subject: [PATCH 31/82] Fix single-step vertical displacement issue --- src/Defs.s | 6 +++--- src/Sprite.s | 56 +++++++++++++++++++++++++++++++++------------------ src/Sprite2.s | 11 +++++++++- 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/Defs.s b/src/Defs.s index a74298d..38308c2 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -203,10 +203,10 @@ SPRITE_VFLIP equ $0400 SPRITE_HFLIP equ $0200 ; Stamp storage parameters -VBUFF_STRIDE_BYTES equ 12*4 ; Each line has 4 slots of 16 pixels + 8 buffer pixels -VBUFF_TILE_ROW_BYTES equ 8*VBUFF_STRIDE_BYTES ; Each row is comprised of 8 lines +VBUFF_STRIDE_BYTES equ {12*4} ; Each line has 4 slots of 16 pixels + 8 buffer pixels +VBUFF_TILE_ROW_BYTES equ {8*VBUFF_STRIDE_BYTES} ; Each row is comprised of 8 lines VBUFF_TILE_COL_BYTES equ 4 -VBUFF_SPRITE_STEP equ VBUFF_TILE_ROW_BYTES*3 ; Allocate space fo 16 rows + 8 rows of buffer +VBUFF_SPRITE_STEP equ {VBUFF_TILE_ROW_BYTES*3} ; Allocate space fo 16 rows + 8 rows of buffer VBUFF_SPRITE_START equ {8*VBUFF_TILE_ROW_BYTES}+4 ; Start at an offset so $0000 can be used as an empty value VBUFF_SLOT_COUNT equ 48 ; Have space for this many stamps diff --git a/src/Sprite.s b/src/Sprite.s index 63f7472..8d121be 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -150,6 +150,11 @@ _DoPhase1 lda #SPRITE_STATUS_EMPTY ; Mark as empty so no error if we try to Add a sprite here again sta _Sprites+SPRITE_STATUS,y + lda _Sprites+TS_COVERAGE_SIZE,y ; Manually copy current value to old + sta _Sprites+OLD_TS_COVERAGE_SIZE,y + lda _Sprites+TS_LOOKUP_INDEX,y + sta _Sprites+OLD_TS_LOOKUP_INDEX,y + 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 @@ -158,15 +163,26 @@ _DoPhase1 jsr _CalcDirtySprite ; If the sprite is marked as ADDED, then it does not need to have its old tile locations cleared + lda tmpA bit #SPRITE_STATUS_ADDED bne :no_move - jsr _ClearSpriteFromTileStore + +; 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 + +; jsr _ClearSpriteFromTileStore ldy tmpY ; Anything else (MOVED, UPDATED, ADDED) will need to have the VBUFF information updated and the ; current tiles marked for update :no_move + lda #SPRITE_STATUS_OCCUPIED ; Clear the dirty bits (ADDED, UPDATED, MOVED) + sta _Sprites+SPRITE_STATUS,y + jmp _MarkDirtySpriteTiles ; Dispatch table. It's unintersting, so it's tucked out of the way @@ -341,7 +357,7 @@ next _ClearSpriteFromTileStore lda _SpriteBitsNot,y ; Cache this value in a direct page location sta tmp0 - ldx _Sprites+TS_COVERAGE_SIZE,y + ldx _Sprites+OLD_TS_COVERAGE_SIZE,y jmp (csfts_tbl,x) csfts_tbl dw csfts_1x1,csfts_1x2,csfts_1x3,csfts_out dw csfts_2x1,csfts_2x2,csfts_2x3,csfts_out @@ -350,66 +366,66 @@ csfts_tbl dw csfts_1x1,csfts_1x2,csfts_1x3,csfts_out csfts_out rts -csfts_3x3 ldx _Sprites+TS_LOOKUP_INDEX,y +csfts_3x3 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 TSClearSprite 4 - TSClearSprite 1*{TS_LOOKUP_SPAN*2} + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+4 - TSClearSprite 2*{TS_LOOKUP_SPAN*2} + TSClearSprite 2*{TS_LOOKUP_SPAN*2}+0 TSClearSprite 2*{TS_LOOKUP_SPAN*2}+2 TSClearSprite 2*{TS_LOOKUP_SPAN*2}+4 rts -csfts_3x2 ldx _Sprites+TS_LOOKUP_INDEX,y +csfts_3x2 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 - TSClearSprite 1*{TS_LOOKUP_SPAN*2} + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 - TSClearSprite 2*{TS_LOOKUP_SPAN*2} + TSClearSprite 2*{TS_LOOKUP_SPAN*2}+0 TSClearSprite 2*{TS_LOOKUP_SPAN*2}+2 rts -csfts_3x1 ldx _Sprites+TS_LOOKUP_INDEX,y +csfts_3x1 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 0 - TSClearSprite 1*{TS_LOOKUP_SPAN*2} - TSClearSprite 2*{TS_LOOKUP_SPAN*2} + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 + TSClearSprite 2*{TS_LOOKUP_SPAN*2}+0 rts -csfts_2x3 ldx _Sprites+TS_LOOKUP_INDEX,y +csfts_2x3 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 TSClearSprite 4 - TSClearSprite 1*{TS_LOOKUP_SPAN*2} + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+4 rts -csfts_2x2 ldx _Sprites+TS_LOOKUP_INDEX,y +csfts_2x2 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 - TSClearSprite 1*{TS_LOOKUP_SPAN*2} + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 rts -csfts_2x1 ldx _Sprites+TS_LOOKUP_INDEX,y +csfts_2x1 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 0 - TSClearSprite 1*{TS_LOOKUP_SPAN*2} + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 rts -csfts_1x3 ldx _Sprites+TS_LOOKUP_INDEX,y +csfts_1x3 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 TSClearSprite 4 rts -csfts_1x2 ldx _Sprites+TS_LOOKUP_INDEX,y +csfts_1x2 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 rts -csfts_1x1 ldx _Sprites+TS_LOOKUP_INDEX,y +csfts_1x1 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 0 rts diff --git a/src/Sprite2.s b/src/Sprite2.s index fc02f5d..c68d189 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -55,6 +55,7 @@ _CalcDirtySprite bne mdsOut2 ; Copy the current values into the old value slots + lda _Sprites+TS_COVERAGE_SIZE,y sta _Sprites+OLD_TS_COVERAGE_SIZE,y lda _Sprites+TS_LOOKUP_INDEX,y @@ -173,7 +174,15 @@ _MarkDirtySpriteTiles dw :mark3x1,:mark3x2,:mark3x3,mdsOut dw mdsOut,mdsOut,mdsOut,mdsOut -:vbuff_mul dw 0,52,104,156,208,260,312,364 +:vbuff_mul + dw 0*VBUFF_STRIDE_BYTES + dw 1*VBUFF_STRIDE_BYTES + dw 2*VBUFF_STRIDE_BYTES + dw 3*VBUFF_STRIDE_BYTES + dw 4*VBUFF_STRIDE_BYTES + dw 5*VBUFF_STRIDE_BYTES + dw 6*VBUFF_STRIDE_BYTES + dw 7*VBUFF_STRIDE_BYTES ; Pair of macros to make the unrolled loop more concise ; From b022162036f752e75fe781d3c2a0686afcad6144 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 1 Jun 2022 09:19:28 -0500 Subject: [PATCH 32/82] Fix vertical offset bug; was using the TileStore lookup index, not the TileStore offset --- src/Sprite2.s | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/Sprite2.s b/src/Sprite2.s index c68d189..12445f5 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -16,6 +16,17 @@ AreaIndex equ tmp7 SpriteBit equ tmp10 ; set the bit of the value that if the current sprite index ; VBuffOrigin equ tmp11 +; Table of pre-multiplied vbuff strides +vbuff_mul + dw 0*VBUFF_STRIDE_BYTES + dw 1*VBUFF_STRIDE_BYTES + dw 2*VBUFF_STRIDE_BYTES + dw 3*VBUFF_STRIDE_BYTES + dw 4*VBUFF_STRIDE_BYTES + dw 5*VBUFF_STRIDE_BYTES + dw 6*VBUFF_STRIDE_BYTES + dw 7*VBUFF_STRIDE_BYTES + ; Marks a sprite as dirty. The work here is mapping from local screen coordinates to the ; tile store indices. The first step is to adjust the sprite coordinates based on the current ; code field offsets and then cache variations of this value needed in the rest of the subroutine @@ -94,7 +105,7 @@ _CalcDirtySprite txa ; Get the vertical offset in the VBUFF memory asl tax - ldal :vbuff_mul,x + ldal vbuff_mul,x sta tmp0 ; Add the horizontal position to the horizontal offset to find the first column in the @@ -104,23 +115,29 @@ _CalcDirtySprite clc lda _Sprites+SPRITE_CLIP_LEFT,y adc StartXMod164 - tax + pha and #$FFFC lsr ; Even numbers from [0, 160] (80 elements) adc RowTop sta _Sprites+TS_LOOKUP_INDEX,y ; This is the index into the TileStoreLookup table -; Create an offset value for loading the calculated VBUFF addresses within the core renderer +; Create an offset value for loading the calculated VBUFF addresses within the core renderer by +; subtracting the actual TileStore offset from the sprite's vbuff address array - eor #$FFFF + tax + lda _Sprites+VBUFF_ARRAY_ADDR,y sec - adc _Sprites+VBUFF_ARRAY_ADDR,y + sbc TileStoreLookup,x + +; eor #$FFFF +; sec +; adc _Sprites+VBUFF_ARRAY_ADDR,y sta tmp1 ; Spill this value to direct page temp space ; Calculate the final address of the sprite data in the stamp buffer. We have to move earlier ; in the buffer based on the horizontal offset and move up for each vertical offset. - txa + pla and #$0003 tax clc @@ -167,23 +184,13 @@ _MarkDirtySpriteTiles clc ldx _Sprites+TS_COVERAGE_SIZE,y - jmp (:mark,x) + jmp (mdsmark,x) -:mark dw :mark1x1,:mark1x2,:mark1x3,mdsOut +mdsmark dw :mark1x1,:mark1x2,:mark1x3,mdsOut dw :mark2x1,:mark2x2,:mark2x3,mdsOut dw :mark3x1,:mark3x2,:mark3x3,mdsOut dw mdsOut,mdsOut,mdsOut,mdsOut -:vbuff_mul - dw 0*VBUFF_STRIDE_BYTES - dw 1*VBUFF_STRIDE_BYTES - dw 2*VBUFF_STRIDE_BYTES - dw 3*VBUFF_STRIDE_BYTES - dw 4*VBUFF_STRIDE_BYTES - dw 5*VBUFF_STRIDE_BYTES - dw 6*VBUFF_STRIDE_BYTES - dw 7*VBUFF_STRIDE_BYTES - ; Pair of macros to make the unrolled loop more concise ; ; 1. Load the tile store address from a fixed offset From a938639c1be15ee948994b9b53c94b21c26cfeb4 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 1 Jun 2022 13:55:04 -0500 Subject: [PATCH 33/82] Fix initialization bug in TileStore --- demos/tool/App.Main.s | 86 ++++++++++++++++++++++++++------------ src/Sprite.s | 2 +- src/Tiles.s | 31 +++++++------- src/static/TileStoreDefs.s | 4 +- 4 files changed, 79 insertions(+), 44 deletions(-) diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index a63e935..f662cb1 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -39,6 +39,19 @@ ScreenY equ 2 pea #TSZelda _GTELoadTileSet +; Set the palette + ldx #11*2 +:ploop + lda palette,x + stal $E19E00,x + dex + dex + bpl :ploop + bra sprt + +palette dw $0000,$08C1,$0C41,$0F93,$0777,$0FDA,$00A0,$0000,$0D20,$0FFF,$023E +sprt + ; Create stamps for the sprites we are going to use HERO_SPRITE_1 equ SPRITE_16X16+1 HERO_SLOT equ 0 @@ -49,8 +62,8 @@ HERO_SLOT equ 0 ; Create sprites pea HERO_SPRITE_1 ; sprite id - pea #0 ; screen x-position (<256) - pea #0 ; screen y-position (<256) + pea #11 ; screen x-position (<256) + pea #23 ; screen y-position (<256) pea HERO_SLOT ; sprite slot (0 - 15) _GTEAddSprite @@ -59,44 +72,62 @@ HERO_SLOT equ 0 pea VBUFF_SPRITE_START ; and use this stamp _GTEUpdateSprite -; Manually fill in the 41x26 tiles of the TileStore with a test pattern. +; Manually fill in the 41x26 tiles of the TileStore with a test pattern of trees +; +; Tile 65 Tile 66 +; Tile 97 Tile 98 - ldx #0 - ldy #0 + stz 0 ; X + stz 2 ; Y -:loop - phx - phy +:tloop + ldx 0 + ldy 2 phx phy - lda 0 - clc - adc #64 - pha + pea #65 + + inx + phx + phy + pea #66 + + iny + phx + phy + pea #98 + + dex + phx + phy + pea #97 + + _GTESetTile + _GTESetTile + _GTESetTile _GTESetTile lda 0 inc - and #$001F + inc sta 0 + cmp #40 + bcc :tloop - ply - plx - inx - cpx #41 - bcc :loop + stz 0 + lda 2 + inc + inc + sta 2 + cmp #25 + bcc :tloop - ldx #0 - iny - cpy #26 - bcc :loop +; Set the screen coordinates -; Set the origin of the screen -:skip - - stz ScreenX - stz ScreenY + lda #8 + sta ScreenX + sta ScreenY ; Very simple actions :evt_loop @@ -138,6 +169,7 @@ HERO_SLOT equ 0 ; _GTEUpdateSprite _GTERender + brl :evt_loop ; Debug stuff ldx #$100 diff --git a/src/Sprite.s b/src/Sprite.s index 8d121be..628585e 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -174,7 +174,7 @@ _DoPhase1 bit #SPRITE_STATUS_MOVED beq :no_move -; jsr _ClearSpriteFromTileStore + jsr _ClearSpriteFromTileStore ldy tmpY ; Anything else (MOVED, UPDATED, ADDED) will need to have the VBUFF information updated and the diff --git a/src/Tiles.s b/src/Tiles.s index 616d767..ef293d8 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -80,20 +80,7 @@ InitTiles :row equ tmp1 :vbuff equ tmp2 -; Fill in the TileStoreYTable. This is just a table of offsets into the Tile Store for each row. There -; are 26 rows with a stride of 41 - ldy #0 - lda #0 -:yloop - sta TileStoreYTable,y - clc - adc #41*2 - iny - iny - cpy #26*2 - bcc :yloop - -; Next, initialize the Tile Store itself +; Initialize the Tile Store ldx #TILE_STORE_SIZE-2 lda #25 @@ -105,7 +92,7 @@ InitTiles :loop -; The first set of values in the Tile Store are changed during each frame based on the actions +; The first set of values in the Tile Store that are changed during each frame based on the actions ; that are happening lda #0 @@ -114,12 +101,26 @@ InitTiles sta TileStore+TS_SPRITE_FLAG,x ; no sprites are set at the beginning sta TileStore+TS_DIRTY,x ; none of the tiles are dirty +; Set the default tile rendering functions + + lda EngineMode + bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER + beq :fast +; ldal TileProcs +; sta TileStore+TS_BASE_TILE_DISP,x + bra :out +:fast + ldal FastTileProcs + sta TileStore+TS_BASE_TILE_DISP,x +: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 diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index e08bc61..beb278c 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -14,6 +14,8 @@ TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5 TS_WORD_OFFSET equ TILE_STORE_SIZE*6 ; const value, word offset value for this tile if LDA (dp),y instructions re used TS_BASE_ADDR equ TILE_STORE_SIZE*7 ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000. TS_SCREEN_ADDR equ TILE_STORE_SIZE*8 ; cached value of on-screen location of tile. Used for DirtyRender. + +; TODO: Move these arrays into the K bank to support direct dispatch via jmp (abs,x) TS_BASE_TILE_COPY equ TILE_STORE_SIZE*9 ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering TS_BASE_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function TS_DIRTY_TILE_DISP equ TILE_STORE_SIZE*11 ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function @@ -43,9 +45,9 @@ SPRITE_STATUS equ {MAX_SPRITES*0} SPRITE_ID equ {MAX_SPRITES*2} SPRITE_X equ {MAX_SPRITES*4} SPRITE_Y equ {MAX_SPRITES*6} +VBUFF_ADDR equ {MAX_SPRITES*8} ; Base address of the sprite's stamp in the data/mask banks ; These values are cached / calculated during the rendering process -VBUFF_ADDR equ {MAX_SPRITES*8} ; Base address of the sprite's stamp in the data/mask banks TS_LOOKUP_INDEX equ {MAX_SPRITES*10} ; The index from the TileStoreLookup table that corresponds to the top-left corner of the sprite TS_COVERAGE_SIZE equ {MAX_SPRITES*12} ; Representation of how many TileStore tiles (NxM) are covered by this sprite OLD_TS_LOOKUP_INDEX equ {MAX_SPRITES*14} ; Copy of the values to support diffing From 24ebb1f3afd0d32ee3db72ed809c27ada34b183b Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 1 Jun 2022 21:12:54 -0500 Subject: [PATCH 34/82] Fix Tile Store lookup initialization bug --- src/static/TileStore.s | 20 ++++++++++++++++---- src/static/TileStoreDefs.s | 3 +++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/static/TileStore.s b/src/static/TileStore.s index 0ac2d33..8309773 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -53,6 +53,8 @@ TileStoreData mac <<< ; Create a lookup table with two runs of offsets, plus an overlap area on the end (41+41+1 = 83 = TS_LOOKUP_SPAN) TileStoreLookup ENT + +; First copy ]row equ 0 lup TILE_STORE_HEIGHT TileStoreData ]row*2*TILE_STORE_WIDTH @@ -61,10 +63,20 @@ TileStoreLookup ENT ]row equ ]row+1 --^ -; ds {TS_LOOKUP_SPAN*TS_LOOKUP_ROWS*2} -; -;TILE_STORE_WIDTH equ 41 -;TILE_STORE_HEIGHT equ 26 +; Second copy +]row equ 0 + lup TILE_STORE_HEIGHT + TileStoreData ]row*2*TILE_STORE_WIDTH + TileStoreData ]row*2*TILE_STORE_WIDTH + dw ]row*2*TILE_STORE_WIDTH +]row equ ]row+1 + --^ + +; Last row + TileStoreData 0*2*TILE_STORE_WIDTH + TileStoreData 0*2*TILE_STORE_WIDTH + dw 0*2*TILE_STORE_WIDTH + ;------------------------------------------------------------------------------------- ; ; Other data tables diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index beb278c..d6ccdc2 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -78,6 +78,9 @@ VBUFF_ARRAY_ADDR equ {MAX_SPRITES*40} ; Fixed address where this spri ;TILE_STORE_ADDR_10 equ {MAX_SPRITES*30} ; 51 rows by 81 columns + 2 extra rows and columns for sprite sizes +; +; 53 rows = TILE_STORE_HEIGHT + TILE_STORE_HEIGHT + 1 +; 83 cols = TILE_STORE_WIDTH + TILE_STORE_WIDTH + 1 TS_LOOKUP_WIDTH equ 81 TS_LOOKUP_HEIGHT equ 51 TS_LOOKUP_BORDER equ 2 From 83ec527b06d2b8fb921d51f54251d9eaf5e30099 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 1 Jun 2022 22:24:45 -0500 Subject: [PATCH 35/82] Make the test demo more sophisticated; multiple sprites --- demos/tool/App.Main.s | 170 +++++++++++++++++++++++++++++++++--------- macros/GTE.Macs.s | 3 + src/CoreImpl.s | 5 +- src/Tool.s | 16 +++- 4 files changed, 153 insertions(+), 41 deletions(-) diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index f662cb1..91f4cf7 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -15,8 +15,12 @@ TSZelda EXT ; tileset buffer +MAX_SPRITES equ 1 + ScreenX equ 0 ScreenY equ 2 +Tmp0 equ 4 +Tmp1 equ 6 ; Typical init phk @@ -53,30 +57,43 @@ palette dw $0000,$08C1,$0C41,$0F93,$0777,$0FDA,$00A0,$0000,$0D20,$0FFF,$023E sprt ; Create stamps for the sprites we are going to use -HERO_SPRITE_1 equ SPRITE_16X16+1 -HERO_SLOT equ 0 +HERO_SPRITE equ SPRITE_16X16+1 - pea HERO_SPRITE_1 ; sprint id + pea HERO_SPRITE ; sprint id pea VBUFF_SPRITE_START ; vbuff address _GTECreateSpriteStamp ; Create sprites - pea HERO_SPRITE_1 ; sprite id - pea #11 ; screen x-position (<256) - pea #23 ; screen y-position (<256) - pea HERO_SLOT ; sprite slot (0 - 15) + stz Tmp0 + stz Tmp1 + + ldx Tmp0 +:sloop + pea HERO_SPRITE ; sprite id + lda PlayerX,x + pha + lda PlayerY,x + pha + pei Tmp1 _GTEAddSprite - pea HERO_SLOT ; update the sprite in this slot + pei Tmp1 ; update the sprite in this slot pea $0000 ; with these flags (h/v flip) pea VBUFF_SPRITE_START ; and use this stamp _GTEUpdateSprite + inc Tmp1 + ldx Tmp0 + inx + inx + stx Tmp0 + cpx #MAX_SPRITES*2 + bcc :sloop + ; Manually fill in the 41x26 tiles of the TileStore with a test pattern of trees ; ; Tile 65 Tile 66 ; Tile 97 Tile 98 - stz 0 ; X stz 2 ; Y @@ -123,6 +140,10 @@ HERO_SLOT equ 0 cmp #25 bcc :tloop +; Initialize the frame counter + + stz FrameCount + ; Set the screen coordinates lda #8 @@ -136,8 +157,29 @@ HERO_SLOT equ 0 pla and #$00FF cmp #'q' - beq :exit - + bne :3 + brl :exit +:3 + cmp #'s' + bne :4 + inc PlayerY + brl :next +:4 + cmp #'w' + bne :5 + dec PlayerY + brl :next +:5 + cmp #'d' + bne :6 + inc PlayerX + brl :next +:6 + cmp #'a' + bne :7 + dec PlayerX + brl :next +:7 cmp #$15 ; left = $08, right = $15, up = $0B, down = $0A bne :8 inc ScreenX @@ -146,53 +188,101 @@ HERO_SLOT equ 0 :8 cmp #$08 bne :9 dec ScreenX - bra :next + brl :next :9 cmp #$0B bne :10 inc ScreenY - bra :next + brl :next :10 cmp #$0A bne :next dec ScreenY :next + inc ScreenX + pei ScreenX pei ScreenY _GTESetBG0Origin -; Update the sprite each frame for testing -; pea HERO_SLOT -; pea $0000 -; pea VBUFF_SPRITE_START -; _GTEUpdateSprite + stz Tmp0 + stz Tmp1 + + ldx Tmp0 +loopX + lda PlayerX,x + clc + adc PlayerU,x + sta PlayerX,x + bpl is_posx + lda PlayerU,x + eor #$FFFF + inc + sta PlayerU,x + bra do_y +is_posx cmp #128 + bcc do_y + lda PlayerU,x + eor #$FFFF + inc + sta PlayerU,x + +do_y + lda PlayerY,x + clc + adc PlayerV,x + sta PlayerY,x + bpl is_posy + lda PlayerV,x + eor #$FFFF + inc + sta PlayerV,x + bra do_z +is_posy cmp #160 + bcc do_z + lda PlayerV,x + eor #$FFFF + inc + sta PlayerV,x +do_z + pei Tmp1 + lda PlayerX,x + pha + lda PlayerY,x + pha + _GTEMoveSprite + + inc Tmp1 + ldx Tmp0 + inx + inx + stx Tmp0 + cpx #MAX_SPRITES*2 + bcc loopX _GTERender - brl :evt_loop + inc FrameCount ; Debug stuff - ldx #$100 - lda StartX,x + pha + _GTEGetSeconds + pla + cmp LastSecond + beq :no_fps + sta LastSecond + + lda FrameCount ldx #0 + ldy #$FFFF jsr DrawWord - ldx #$100 - lda StartY,x - ldx #160*8 - jsr DrawWord + stz FrameCount +:no_fps - lda ScreenX - ldx #160*16 - jsr DrawWord - - lda ScreenY - ldx #160*24 - jsr DrawWord - - tdc - ldx #160*32 - jsr DrawWord +; tdc +; ldx #160*32 +; jsr DrawWord brl :evt_loop @@ -203,6 +293,12 @@ HERO_SLOT equ 0 qtRec adrl $0000 da $00 +; Array of sprite positions and velocities +PlayerX dw 0,14,29,34,45,67,81,83,92,101,39,22,7,74,111,9 +PlayerY dw 7,24,13,56,35,72,23,8,93,123,134,87,143,14,46,65 +PlayerU dw 1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4 +PlayerV dw 1,1,1,1,2,2,2,4,3,3,3,3,4,4,4,4 + ; Load the GTE User Tool and install it GTEStartUp pea $0000 @@ -257,6 +353,8 @@ GTEStartUp MyUserId ds 2 ToolPath str '1/Tool160' +FrameCount ds 2 +LastSecond dw 0 PUT App.Msg.s PUT font.s diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s index 2932fcf..68bc12b 100644 --- a/macros/GTE.Macs.s +++ b/macros/GTE.Macs.s @@ -53,4 +53,7 @@ _GTEUpdateSprite MAC <<< _GTERemoveSprite MAC UserTool $1300+GTEToolNum + <<< +_GTEGetSeconds MAC + UserTool $1400+GTEToolNum <<< \ No newline at end of file diff --git a/src/CoreImpl.s b/src/CoreImpl.s index ee31bea..a514c03 100644 --- a/src/CoreImpl.s +++ b/src/CoreImpl.s @@ -1,5 +1,5 @@ ; 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 space provided by the main program segment @@ -267,8 +267,7 @@ ClearKbdStrobe sep #$20 rts ; Read the keyboard and paddle controls and return in a game-controller-like format -_ReadControl - pea $0000 ; low byte = key code, high byte = %------AB +_ReadControl pea $0000 ; low byte = key code, high byte = %------AB sep #$20 ldal OPTION_KEY_REG ; 'B' button diff --git a/src/Tool.s b/src/Tool.s index ec94670..4e6f48e 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -56,6 +56,8 @@ _CallTable adrl _TSMoveSprite-1 adrl _TSUpdateSprite-1 adrl _TSRemoveSprite-1 + + adrl _TSGetSeconds-1 _CTEnd _GTEAddSprite MAC UserTool $1000+GTEToolNum @@ -202,12 +204,12 @@ width equ FirstParam+2 ; ReadControl() _TSReadControl -output equ FirstParam +:output equ FirstParam _TSEntry jsr _ReadControl - sta output,s + sta :output,s _TSExit #0;#0 @@ -339,6 +341,16 @@ _TSRemoveSprite _TSExit #0;#2 +_TSGetSeconds +:output equ FirstParam + + _TSEntry + + ldal OneSecondCounter + sta :output,s + + _TSExit #0;#0 + ; Insert the GTE code put Math.s From 4f557230c628572d7c9079d82f08fe20a082c681 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Thu, 2 Jun 2022 12:28:49 -0500 Subject: [PATCH 36/82] Remove some dead code and streamline some addressing. Fix bug in dispatch macro --- src/Defs.s | 1 + src/Sprite.s | 14 +++++--------- src/Sprite2.s | 4 ---- src/Tiles.s | 15 ++++++++------- src/static/TileStore.s | 4 ++++ 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/Defs.s b/src/Defs.s index 38308c2..8ddd45a 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -245,3 +245,4 @@ ScreenModeHeight EXT _SpriteBits EXT _SpriteBitsNot EXT VBuffArray EXT +_stamp_step EXT diff --git a/src/Sprite.s b/src/Sprite.s index 628585e..ddcb55e 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -575,20 +575,17 @@ _CacheSpriteBanks ; it's tile information, or changing its position. ; ; X = sprite index -_stamp_step dw 0,12,24,36 _PrecalcAllSpriteInfo lda _Sprites+SPRITE_ID,x ; and #$3E00 xba and #$0006 - txy ; swap X/Y for this... - tax - lda _Sprites+VBUFF_ADDR,y + tay + lda _Sprites+VBUFF_ADDR,x clc - adcl _stamp_step,x + adc _stamp_step,y sta _Sprites+SPRITE_DISP,y - tyx ; Set the sprite's width and height lda #4 @@ -604,7 +601,7 @@ _PrecalcAllSpriteInfo :width_4 lda _Sprites+SPRITE_ID,x - bit #$0800 ; width select + bit #$0800 ; height select beq :height_8 lda #16 sta _Sprites+SPRITE_HEIGHT,x @@ -734,7 +731,7 @@ _UpdateSprite ; Move a sprite to a new location. If the tile ID of the sprite needs to be changed, then ; a full remove/add cycle needs to happen ; -; A = sprite ID +; A = sprite slot ; X = x position ; Y = y position _MoveSprite @@ -750,7 +747,6 @@ _MoveSprite cmp _Sprites+SPRITE_X,x bne :changed1 - sta _Sprites+SPRITE_X,x ; Update the X coordinate tya cmp _Sprites+SPRITE_Y,x bne :changed2 diff --git a/src/Sprite2.s b/src/Sprite2.s index 12445f5..d7830c2 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -128,10 +128,6 @@ _CalcDirtySprite lda _Sprites+VBUFF_ARRAY_ADDR,y sec sbc TileStoreLookup,x - -; eor #$FFFF -; sec -; adc _Sprites+VBUFF_ARRAY_ADDR,y sta tmp1 ; Spill this value to direct page temp space ; Calculate the final address of the sprite data in the stamp buffer. We have to move earlier diff --git a/src/Tiles.s b/src/Tiles.s index ef293d8..8601c2e 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -268,11 +268,11 @@ dobit mac bcc next_bit beq last_bit tax - lda (SPRITE_VBUFF_PTR+{]2*2}),y + lda (SPRITE_VBUFF_PTR+{]1*2}),y sta sprite_ptr0+{]2*4} txa jmp ]3 -last_bit lda (SPRITE_VBUFF_PTR+{]2*2}),y +last_bit lda (SPRITE_VBUFF_PTR+{]1*2}),y sta sprite_ptr0+{]2*4} jmp ]4 next_bit @@ -280,16 +280,17 @@ next_bit ; Specialization for the first sprite which can just return the vbuff address ; in a register if there is only one sprite intersecting the tile +; dobit bit_position,dest;next;exit dobit1 mac lsr bcc next_bit beq last_bit tax - lda (SPRITE_VBUFF_PTR+{]2*2}),y + lda (SPRITE_VBUFF_PTR+{]1*2}),y sta sprite_ptr0+{]2*4} txa jmp ]3 -last_bit lda (SPRITE_VBUFF_PTR+{]2*2}),y +last_bit lda (SPRITE_VBUFF_PTR+{]1*2}),y jmp ]4 next_bit <<< @@ -366,7 +367,7 @@ next_bit stpbit mac lsr bcc next_bit - lda (SPRITE_VBUFF_PTR+{]2*2}),y + lda (SPRITE_VBUFF_PTR+{]1*2}),y sta sprite_ptr0+{]2*4} jmp ]3 next_bit @@ -374,7 +375,7 @@ next_bit ; Last bit test which *must* be set endbit mac - lda (SPRITE_VBUFF_PTR+{]2*2}),y + lda (SPRITE_VBUFF_PTR+{]1*2}),y sta sprite_ptr0+{]2*4} jmp ]3 <<< @@ -421,7 +422,7 @@ SpriteBitsToVBuffAddrs mac dobit1 12;0;b_13_1;]1 dobit1 13;0;b_14_1;]1 dobit1 14;0;b_15_1;]1 - endbit 15;0;]1 + endbit 15;0;]1 b_1_1 dobit 1;1;b_2_2;]2 b_2_1 dobit 2;1;b_3_2;]2 diff --git a/src/static/TileStore.s b/src/static/TileStore.s index 8309773..0284cd2 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -423,5 +423,9 @@ _SpriteBits ENT _SpriteBitsNot ENT dw $FFFE,$FFFD,$FFFB,$FFF7,$FFEF,$FFDF,$FFBF,$FF7F,$FEFF,$FDFF,$FBFF,$F7FF,$EFFF,$DFFF,$BFFF,$7FFF +; Steps to the different sprite stamps +_stamp_step ENT + dw 0,12,24,36 + blt_return stk_save \ No newline at end of file From e6251f05a354cdd711451d673d9370a3f17ac954 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Thu, 2 Jun 2022 13:06:26 -0500 Subject: [PATCH 37/82] Clean up constants and fix bad addressing mode --- src/Defs.s | 8 ++++---- src/Sprite.s | 6 +++--- src/Sprite2.s | 10 +++++----- src/static/TileStore.s | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Defs.s b/src/Defs.s index 8ddd45a..2fbd829 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -95,9 +95,9 @@ ToolNum equ 114 ; Tool number assigned to us LastKey equ 116 LastTick equ 118 ForceSpriteFlag equ 120 +SpriteRemovedFlag equ 122 ; Indicate if any sprites were removed this frame + -;VBuffArrayPtr equ 122 -SpriteRemovedFlag equ 126 ; Indicate if any sprites were removed this frame activeSpriteList equ 128 ; 32 bytes for the active sprite list (can persist across frames) ; tiletmp equ 178 ; 16 bytes of temp storage for the tile renderers @@ -206,8 +206,8 @@ SPRITE_HFLIP equ $0200 VBUFF_STRIDE_BYTES equ {12*4} ; Each line has 4 slots of 16 pixels + 8 buffer pixels VBUFF_TILE_ROW_BYTES equ {8*VBUFF_STRIDE_BYTES} ; Each row is comprised of 8 lines VBUFF_TILE_COL_BYTES equ 4 -VBUFF_SPRITE_STEP equ {VBUFF_TILE_ROW_BYTES*3} ; Allocate space fo 16 rows + 8 rows of buffer -VBUFF_SPRITE_START equ {8*VBUFF_TILE_ROW_BYTES}+4 ; Start at an offset so $0000 can be used as an empty value +VBUFF_SPRITE_STEP equ {VBUFF_TILE_ROW_BYTES*3} ; Allocate space for 16 rows + 8 rows of buffer +VBUFF_SPRITE_START equ {VBUFF_TILE_ROW_BYTES+4} ; Start at an offset so $0000 can be used as an empty value VBUFF_SLOT_COUNT equ 48 ; Have space for this many stamps ; This is 13 blocks wide diff --git a/src/Sprite.s b/src/Sprite.s index ddcb55e..1a94ca2 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -42,8 +42,8 @@ InitSprites bcc :loop4 ; Precalculate some bank values - jsr _CacheSpriteBanks - rts + jsr _CacheSpriteBanks + rts ; _RenderSprites ; @@ -585,7 +585,7 @@ _PrecalcAllSpriteInfo lda _Sprites+VBUFF_ADDR,x clc adc _stamp_step,y - sta _Sprites+SPRITE_DISP,y + sta _Sprites+SPRITE_DISP,x ; Set the sprite's width and height lda #4 diff --git a/src/Sprite2.s b/src/Sprite2.s index d7830c2..70db3ce 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -171,9 +171,9 @@ _CalcDirtySprite mdsOut rts -; NOTE: The VBuffArrayAddr lookup 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 +; 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 @@ -224,8 +224,8 @@ COL equ 2 ; This many bytes for each e :mark1x1 ldx _Sprites+VBUFF_ARRAY_ADDR,y ; get the address of this sprite's vbuff values - lda _Sprites+TS_VBUFF_BASE,y ; get the starting vbuff address - sta: {0*ROW}+{0*COL},x ; Put in the vbuff address + lda _Sprites+TS_VBUFF_BASE,y ; get the starting vbuff address + sta: {0*ROW}+{0*COL},x ; Put in the vbuff address ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2} diff --git a/src/static/TileStore.s b/src/static/TileStore.s index 0284cd2..b5f07f4 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -412,7 +412,7 @@ ScreenModeHeight ENT ; we allocate 8 sprites in the first row and 8 more sprites in the 4th row. So we need to allocate a ; total of 6 rows of TileStore space ; -; It is *critical* that this array be placed in a memory location that is greated than the largest +; It is *critical* that this array be placed in a memory location that is greater than the largest ; TileStore offset. VBuffArray ENT ds 6*{TILE_STORE_WIDTH*2} From 783e813dc222daf2e86135f6eeadb68ac4cfef3d Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Thu, 2 Jun 2022 13:59:15 -0500 Subject: [PATCH 38/82] Implement overlapping rendering for two sprites --- src/Sprite.s | 8 ++++ src/tiles/FastRenderer.s | 97 +++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 51 deletions(-) diff --git a/src/Sprite.s b/src/Sprite.s index 1a94ca2..bae0041 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -41,6 +41,14 @@ InitSprites cpx #16*2 bcc :loop4 +; Initialize the Page 2 pointers + ldx #$100 + lda #^spritemask + sta sprite_ptr0+2,x + sta sprite_ptr1+2,x + sta sprite_ptr2+2,x + sta sprite_ptr3+2,x + ; Precalculate some bank values jsr _CacheSpriteBanks rts diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index f8654b7..5e4d430 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -38,7 +38,7 @@ FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFa SpriteDispatch txy - SpriteBitsToVBuffAddrs OneSpriteFast;OneSpriteFast;OneSpriteFast;OneSpriteFast + SpriteBitsToVBuffAddrs OneSpriteFast;TwoSpritesFast;TwoSpritesFast;TwoSpritesFast ; Where there are sprites involved, the first step is to call a routine to copy the ; tile data into a temporary buffer. Then the sprite data is merged and placed into @@ -57,7 +57,6 @@ OneSpriteFast tay plb -; jmp _TBApplySpriteData2 _TBApplySpriteData2 ]line equ 0 lup 8 @@ -75,60 +74,56 @@ _TBApplySpriteData2 plb rts - -OneSpriteFastX - tax ; address of the sprite data - lda TileStore+TS_BASE_TILE_COPY,y ; copy routine (handles flips and other behaviors) - stal osf_copy+1 -osf_copy jsr $0000 - -; ldx TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data - lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line - pha ; and put on the stack for later. - lda TileStore+TS_CODE_ADDR_LOW,y - tay - plb ; set the code field bank - - OneSpriteToCodeField 0 - OneSpriteToCodeField 1 - OneSpriteToCodeField 2 - OneSpriteToCodeField 3 - OneSpriteToCodeField 4 - OneSpriteToCodeField 5 - OneSpriteToCodeField 6 - OneSpriteToCodeField 7 - - rts +TwoSpriteLine mac +; and [sprite_ptr1],y + db $37,sprite_ptr1 + ora (sprite_ptr1),y +; and [sprite_ptr0],y + db $37,sprite_ptr0 + ora (sprite_ptr0),y + <<< TwoSpritesFast -; tyx -; lda TileStore+TS_TILE_ADDR,y -; per :-1 -; jmp (TileStore+TS_BASE_TILE_COPY,x) ; Copy the tile data to the temporary buffer -;: -; lda TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data -; sta spritedata_0 -; sta spritemask_0 -; lda TileStore+TS_VBUFF_ADDR_1,y ; address of the sprite data -; sta spritedata_1 -; sta spritemask_1 - + ldx TileStore+TS_TILE_ADDR,y lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line - pha ; and put on the stack for later. - lda TileStore+TS_CODE_ADDR_LOW,y - tay - plb ; set the code field bank + pha ; and put on the stack for later. Has TileStore bank in high byte. + lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field + pha ; Need to pop it later.... -; TwoSpritesToCodeField 0 -; TwoSpritesToCodeField 1 -; TwoSpritesToCodeField 2 -; TwoSpritesToCodeField 3 -; TwoSpritesToCodeField 4 -; TwoSpritesToCodeField 5 -; TwoSpritesToCodeField 6 -; TwoSpritesToCodeField 7 + sep #$20 ; set the sprite data bank + lda #^spritedata + pha + plb + rep #$20 - rts +]line equ 0 + lup 8 + ldy #{]line*SPRITE_PLANE_SPAN} + ldal tiledata+{]line*4},x + TwoSpriteLine + sta tmp_tile_data+{]line*4} + + ldy #{]line*SPRITE_PLANE_SPAN}+2 + ldal tiledata+{]line*4}+2,x + TwoSpriteLine + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + + ply ; Pop off CODE_ADDR_LOW + plb ; Set the CODE_ADDR_HIGH bank + +_TBApplySpriteData3 +]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 ; Reset to the bank in the top byte of CODE_ADDR_HIGH + rts ThreeSpritesFast FourSpritesFast From 36d57f7c2d95020c7d0d0a8b462267db1df6d79c Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Thu, 2 Jun 2022 23:02:06 -0500 Subject: [PATCH 39/82] Fix signed calculations for negative sprite X and Y coordinates --- src/Sprite2.s | 82 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/src/Sprite2.s b/src/Sprite2.s index 70db3ce..f706321 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -56,6 +56,9 @@ vbuff_mul ; _Sprites+TS_LOOKUP_INDEX : TileStore index of the upper-left corner of the sprite ; _Sprites+TS_VBUFF_BASE : Address of the top-left corner of the sprite in the VBUFF sprite stamp memory ; +; The clipped sprite coordinates are used to calculate the tiles that are visible, but the actual +; sprite coordinates (including handling negative values) are used to calculate the VBUFF offset +; values. mdsOut2 lda #6 ; Pick a value for a 0x0 tile sprite sta _Sprites+TS_COVERAGE_SIZE,y ; zero the list of tile store addresses @@ -95,19 +98,11 @@ _CalcDirtySprite pla and #$0007 - tax ; cache again. This is a bit faster than recalculating - adc _Sprites+SPRITE_CLIP_HEIGHT,y ; Nominal value between 0 and 16+7 = 23 = 10111 dec and #$0018 sta AreaIndex - txa ; Get the vertical offset in the VBUFF memory - asl - tax - ldal vbuff_mul,x - sta tmp0 - ; Add the horizontal position to the horizontal offset to find the first column in the ; code field that needs to be drawn. The range of values is 0 to 159+163 = [0, 322]. ; This value is divided by 4, so 81 possible values @@ -121,21 +116,52 @@ _CalcDirtySprite adc RowTop sta _Sprites+TS_LOOKUP_INDEX,y ; This is the index into the TileStoreLookup table -; Create an offset value for loading the calculated VBUFF addresses within the core renderer by -; subtracting the actual TileStore offset from the sprite's vbuff address array - tax - lda _Sprites+VBUFF_ARRAY_ADDR,y - sec - sbc TileStoreLookup,x - sta tmp1 ; Spill this value to direct page temp space - -; Calculate the final address of the sprite data in the stamp buffer. We have to move earlier -; in the buffer based on the horizontal offset and move up for each vertical offset. +; Calculate the final amount of visible tiles that need to be refreshed and use that to +; set the coverage size index. pla and #$0003 - tax + adc _Sprites+SPRITE_CLIP_WIDTH,y ; max width = 8 = 0x08 + dec + and #$000C + lsr ; max value = 4 = 0x04 + ora AreaIndex ; merge into the area index + sta _Sprites+TS_COVERAGE_SIZE,y ; Save this value as a key to the coverage size of the sprite + + +; Calculate the VBUFF offset based on the actual (signed) sprite position + + clc ; Carry should still be clear here.... + lda StartYMod208 + and #$0007 + adc _Sprites+SPRITE_Y,y + bmi :neg_y + and #$0007 +:neg_y + asl ; Multiply by 48. Would be nice to use a + asl ; table lookup, but the values can be negative + asl ; so do the calculation + asl + sta tmp0 + asl + clc + adc tmp0 + sta tmp0 + +; Calculate the final address of the sprite data in the stamp buffer. We have to move earlier +; in the buffer based on the horizontal offset and move up for each vertical offset. +; +; For a negative value we need to adjust the vbuff by the number of off-screen tiles plus +; the alignment adjustment. + + clc + lda StartXMod164 + and #$0003 + adc _Sprites+SPRITE_X,y + bmi :neg_x + and #$0003 +:neg_x clc adc tmp0 ; add to the vertical offset @@ -146,18 +172,14 @@ _CalcDirtySprite adc _Sprites+SPRITE_DISP,y ; A = SPRITE_DISP + (-X - 1) + 1 = SPRITE_DISP - X sta _Sprites+TS_VBUFF_BASE,y -; We know the starting corner of the TileStore. Now, we need to figure out now many tiles -; the sprite covers. This is a function of the sprite's width and height and the specific -; location of the upper-left corner of the sprite within the corner tile. +; Create an offset value for loading the calculated VBUFF addresses within the core renderer by +; subtracting the actual TileStore offset from the sprite's vbuff address array - txa - clc - adc _Sprites+SPRITE_CLIP_WIDTH,y ; max width = 8 = 0x08 - dec - and #$000C - lsr ; max value = 4 = 0x04 - ora AreaIndex ; merge into the area index - sta _Sprites+TS_COVERAGE_SIZE,y ; Save this value as a key to the coverage size of the sprite + ldx _Sprites+TS_LOOKUP_INDEX,y + lda _Sprites+VBUFF_ARRAY_ADDR,y + sec + sbc TileStoreLookup,x + sta tmp1 ; Spill this value to direct page temp space ; Last task. Since we don't need to use the X-register to cache values; load the direct page 2 ; offset for the SPRITE_VBUFF_PTR and save it From b6202ca44c21984a1b70d8c13f4085abfe9f6f57 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 6 Jun 2022 11:23:00 -0500 Subject: [PATCH 40/82] Implement framework for adjusting VBuff tables to handle boundary transitions --- src/Defs.s | 2 + src/Sprite.s | 128 ++++++++++++++++++++++---- src/Sprite2.s | 181 ++++++++++++++++++++----------------- src/Tiles.s | 4 + src/static/TileStore.s | 82 +++++++++++++++-- src/static/TileStoreDefs.s | 10 +- 6 files changed, 292 insertions(+), 115 deletions(-) diff --git a/src/Defs.s b/src/Defs.s index 2fbd829..cf0a499 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -246,3 +246,5 @@ _SpriteBits EXT _SpriteBitsNot EXT VBuffArray EXT _stamp_step EXT +VBuffVertTableSelect EXT +VBuffHorzTableSelect EXT diff --git a/src/Sprite.s b/src/Sprite.s index bae0041..b524484 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -19,27 +19,72 @@ InitSprites cpx #$FFFE bne :loop2 -; Set the VBuff array addresses for each sprite, since they're static +; Initialize the VBuff offset values for the different cases - ldx #0 - lda #VBuffArray -:loop3 sta _Sprites+VBUFF_ARRAY_ADDR,x - clc - adc #4*2 ; skip ahead 4 tiles - inx - inx - cpx #8*2 - bcc :loop3 +TILE_STORE_SPAN equ {TILE_STORE_WIDTH*2} +LAST_ROW equ {TILE_STORE_SPAN*{TILE_STORE_HEIGHT-1}} +NEXT_TO_LAST_ROW equ {TILE_STORE_SPAN*{TILE_STORE_HEIGHT-2}} +LAST_COL equ {TILE_STORE_WIDTH-1}*2 +NEXT_TO_LAST_COL equ {TILE_STORE_WIDTH-2}*2 + + lda #0 ; Normal row, Normal column + ldx #0 + jsr _SetVBuffValues + + lda #8 + ldx #LAST_COL ; Normal row, Last column + jsr _SetVBuffValues + + lda #16 + ldx #NEXT_TO_LAST_COL ; Normal row, Next-to-Last column + jsr _SetVBuffValues + + lda #24 ; Last row, normal column + ldx #LAST_ROW + jsr _SetVBuffValues + + lda #32 + ldx #LAST_ROW+LAST_COL ; Last row, Last column + jsr _SetVBuffValues + + lda #40 + ldx #LAST_ROW+NEXT_TO_LAST_COL ; Last row, Next-to-Last column + jsr _SetVBuffValues + + lda #48 ; Next-to-Last row, normal column + ldx #NEXT_TO_LAST_ROW + jsr _SetVBuffValues + + lda #56 + ldx #NEXT_TO_LAST_ROW+LAST_COL ; Next-to-Last row, Last column + jsr _SetVBuffValues + + lda #64 + ldx #NEXT_TO_LAST_ROW+NEXT_TO_LAST_COL ; Next-to-Last row, Next-to-Last column + jsr _SetVBuffValues + +; Set the VBuff array addresses for each sprite, since they're static +; +; NOTE: Can remove later +; ldx #0 +; lda #VBuffArray +;:loop3 sta _Sprites+VBUFF_ARRAY_ADDR,x +; clc +; adc #4*2 ; skip ahead 4 tiles +; inx +; inx +; cpx #8*2 +; bcc :loop3 ; Now do the second set of sprites - lda #VBuffArray+{3*{TILE_STORE_WIDTH*2}} -:loop4 sta _Sprites+VBUFF_ARRAY_ADDR,x - clc - adc #4*2 ; skip ahead 4 tiles - inx - inx - cpx #16*2 - bcc :loop4 +; lda #VBuffArray+{3*{TILE_STORE_WIDTH*2}} +;:loop4 sta _Sprites+VBUFF_ARRAY_ADDR,x +; clc +; adc #4*2 ; skip ahead 4 tiles +; inx +; inx +; cpx #16*2 +; bcc :loop4 ; Initialize the Page 2 pointers ldx #$100 @@ -53,6 +98,53 @@ InitSprites jsr _CacheSpriteBanks rts +; Call with X-register set to TileStore tile and Acc set to the VBuff slot offset +_SetVBuffValues +COL_BYTES equ 4 ; VBUFF_TILE_COL_BYTES +ROW_BYTES equ 384 ; VBUFF_TILE_ROW_BYTES + + clc + adc #VBuffArray + sec + sbc TileStoreLookup,x + sta tmp0 + + ldy TileStoreLookup,x + lda #{0*COL_BYTES}+{0*ROW_BYTES} + sta (tmp0),y + + ldy TileStoreLookup+2,x + lda #{1*COL_BYTES}+{0*ROW_BYTES} + sta (tmp0),y + + ldy TileStoreLookup+4,x + lda #{2*COL_BYTES}+{0*ROW_BYTES} + sta (tmp0),y + + ldy TileStoreLookup+1*{TS_LOOKUP_SPAN*2},x + lda #{0*COL_BYTES}+{1*ROW_BYTES} + sta (tmp0),y + + ldy TileStoreLookup+1*{TS_LOOKUP_SPAN*2}+2,x + lda #{1*COL_BYTES}+{1*ROW_BYTES} + sta (tmp0),y + + ldy TileStoreLookup+1*{TS_LOOKUP_SPAN*2}+4,x + lda #{2*COL_BYTES}+{1*ROW_BYTES} + sta (tmp0),y + + ldy TileStoreLookup+2*{TS_LOOKUP_SPAN*2},x + lda #{0*COL_BYTES}+{2*ROW_BYTES} + sta (tmp0),y + + ldy TileStoreLookup+2*{TS_LOOKUP_SPAN*2}+2,x + lda #{1*COL_BYTES}+{2*ROW_BYTES} + sta (tmp0),y + + ldy TileStoreLookup+2*{TS_LOOKUP_SPAN*2}+4,x + lda #{2*COL_BYTES}+{2*ROW_BYTES} + sta (tmp0),y + rts ; _RenderSprites ; ; The function is responsible for updating all of the rendering information based on any changes diff --git a/src/Sprite2.s b/src/Sprite2.s index f706321..11bf3c7 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -88,7 +88,7 @@ _CalcDirtySprite lsr tax lda TileStoreLookupYTable,x - sta RowTop ; Even numbers from [0, 100] (50 elements) + sta RowTop ; Even numbers from [0, 100] (51 elements) ; Get the position of the top edge within the tile and then add it to the sprite's height ; to calculate the number of tiles that are overlapped. We use the actual width and height @@ -112,7 +112,12 @@ _CalcDirtySprite adc StartXMod164 pha and #$FFFC - lsr ; Even numbers from [0, 160] (80 elements) + lsr ; Even numbers from [0, 160] (81 elements) +; cmp #TILE_STORE_WIDTH*2 +; bcc :x_in_range +; sbc #TILE_STORE_WIDTH*2 +;:x_in_range + sta tmp3 adc RowTop sta _Sprites+TS_LOOKUP_INDEX,y ; This is the index into the TileStoreLookup table @@ -174,9 +179,17 @@ _CalcDirtySprite ; Create an offset value for loading the calculated VBUFF addresses within the core renderer by ; subtracting the actual TileStore offset from the sprite's vbuff address array +; +; The X-register still has the TileStoreLookupYTable index, which we re-use to get a VBuff +; array selector for the vertical location + lda VBuffVertTableSelect,x ; A bunch of 12, 24 or 36 values + clc + ldx tmp3 + adc VBuffHorzTableSelect,x ; A bunch of 0, 4 or 8 values + clc + adc #VBuffArray ldx _Sprites+TS_LOOKUP_INDEX,y - lda _Sprites+VBUFF_ARRAY_ADDR,y sec sbc TileStoreLookup,x sta tmp1 ; Spill this value to direct page temp space @@ -245,20 +258,20 @@ ROW equ TILE_STORE_WIDTH*2 ; This many bytes to the nex COL equ 2 ; This many bytes for each element :mark1x1 - ldx _Sprites+VBUFF_ARRAY_ADDR,y ; get the address of this sprite's vbuff values - lda _Sprites+TS_VBUFF_BASE,y ; get the starting vbuff address - sta: {0*ROW}+{0*COL},x ; Put in the vbuff address +; ldx _Sprites+VBUFF_ARRAY_ADDR,y ; get the address of this sprite's vbuff values +; lda _Sprites+TS_VBUFF_BASE,y ; get the starting vbuff address +; sta: {0*ROW}+{0*COL},x ; Put in the vbuff address ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2} rts :mark1x2 - ldx _Sprites+VBUFF_ARRAY_ADDR,y - lda _Sprites+TS_VBUFF_BASE,y - sta: {0*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {0*ROW}+{1*COL},x +; ldx _Sprites+VBUFF_ARRAY_ADDR,y +; lda _Sprites+TS_VBUFF_BASE,y +; sta: {0*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {0*ROW}+{1*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 @@ -266,13 +279,13 @@ COL equ 2 ; This many bytes for each e rts :mark1x3 - ldx _Sprites+VBUFF_ARRAY_ADDR,y - lda _Sprites+TS_VBUFF_BASE,y - sta: {0*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {0*ROW}+{1*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {0*ROW}+{2*COL},x +; ldx _Sprites+VBUFF_ARRAY_ADDR,y +; lda _Sprites+TS_VBUFF_BASE,y +; sta: {0*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {0*ROW}+{1*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {0*ROW}+{2*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 @@ -281,11 +294,11 @@ COL equ 2 ; This many bytes for each e rts :mark2x1 - ldx _Sprites+VBUFF_ARRAY_ADDR,y - lda _Sprites+TS_VBUFF_BASE,y - sta: {0*ROW}+{0*COL},x - adc #VBUFF_TILE_ROW_BYTES - sta: {1*ROW}+{0*COL},x +; ldx _Sprites+VBUFF_ARRAY_ADDR,y +; lda _Sprites+TS_VBUFF_BASE,y +; sta: {0*ROW}+{0*COL},x +; adc #VBUFF_TILE_ROW_BYTES +; sta: {1*ROW}+{0*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 @@ -293,15 +306,15 @@ COL equ 2 ; This many bytes for each e rts :mark2x2 - ldx _Sprites+VBUFF_ARRAY_ADDR,y - lda _Sprites+TS_VBUFF_BASE,y - sta: {0*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {0*ROW}+{1*COL},x - adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES - sta: {1*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {1*ROW}+{1*COL},x +; ldx _Sprites+VBUFF_ARRAY_ADDR,y +; lda _Sprites+TS_VBUFF_BASE,y +; sta: {0*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {0*ROW}+{1*COL},x +; adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES +; sta: {1*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {1*ROW}+{1*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 @@ -311,19 +324,19 @@ COL equ 2 ; This many bytes for each e rts :mark2x3 - ldx _Sprites+VBUFF_ARRAY_ADDR,y - lda _Sprites+TS_VBUFF_BASE,y - sta: {0*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {0*ROW}+{1*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {0*ROW}+{2*COL},x - adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} - sta: {1*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {1*ROW}+{1*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {1*ROW}+{2*COL},x +; ldx _Sprites+VBUFF_ARRAY_ADDR,y +; lda _Sprites+TS_VBUFF_BASE,y +; sta: {0*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {0*ROW}+{1*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {0*ROW}+{2*COL},x +; adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} +; sta: {1*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {1*ROW}+{1*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {1*ROW}+{2*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 @@ -335,13 +348,13 @@ COL equ 2 ; This many bytes for each e rts :mark3x1 - ldx _Sprites+VBUFF_ARRAY_ADDR,y - lda _Sprites+TS_VBUFF_BASE,y - sta: {0*ROW}+{0*COL},x - adc #VBUFF_TILE_ROW_BYTES - sta: {1*ROW}+{0*COL},x - adc #VBUFF_TILE_ROW_BYTES - sta: {2*ROW}+{0*COL},x +; ldx _Sprites+VBUFF_ARRAY_ADDR,y +; lda _Sprites+TS_VBUFF_BASE,y +; sta: {0*ROW}+{0*COL},x +; adc #VBUFF_TILE_ROW_BYTES +; sta: {1*ROW}+{0*COL},x +; adc #VBUFF_TILE_ROW_BYTES +; sta: {2*ROW}+{0*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 @@ -350,19 +363,19 @@ COL equ 2 ; This many bytes for each e rts :mark3x2 - ldx _Sprites+VBUFF_ARRAY_ADDR,y - lda _Sprites+TS_VBUFF_BASE,y - sta: {0*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {0*ROW}+{1*COL},x - adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES - sta: {1*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {1*ROW}+{1*COL},x - adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES - sta: {2*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {2*ROW}+{1*COL},x +; ldx _Sprites+VBUFF_ARRAY_ADDR,y +; lda _Sprites+TS_VBUFF_BASE,y +; sta: {0*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {0*ROW}+{1*COL},x +; adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES +; sta: {1*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {1*ROW}+{1*COL},x +; adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES +; sta: {2*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {2*ROW}+{1*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 @@ -374,25 +387,25 @@ COL equ 2 ; This many bytes for each e rts :mark3x3 - ldx _Sprites+VBUFF_ARRAY_ADDR,y - lda _Sprites+TS_VBUFF_BASE,y - sta: {0*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {0*ROW}+{1*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {0*ROW}+{2*COL},x - adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} - sta: {1*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {1*ROW}+{1*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {1*ROW}+{2*COL},x - adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} - sta: {2*ROW}+{0*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {2*ROW}+{1*COL},x - adc #VBUFF_TILE_COL_BYTES - sta: {2*ROW}+{2*COL},x +; ldx _Sprites+VBUFF_ARRAY_ADDR,y +; lda _Sprites+TS_VBUFF_BASE,y +; sta: {0*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {0*ROW}+{1*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {0*ROW}+{2*COL},x +; adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} +; sta: {1*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {1*ROW}+{1*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {1*ROW}+{2*COL},x +; adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} +; sta: {2*ROW}+{0*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {2*ROW}+{1*COL},x +; adc #VBUFF_TILE_COL_BYTES +; sta: {2*ROW}+{2*COL},x ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 diff --git a/src/Tiles.s b/src/Tiles.s index 8601c2e..85680cb 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -287,10 +287,14 @@ dobit1 mac beq last_bit tax lda (SPRITE_VBUFF_PTR+{]1*2}),y + clc + adc _Sprites+TS_VBUFF_BASE+{]1*2} sta sprite_ptr0+{]2*4} txa jmp ]3 last_bit lda (SPRITE_VBUFF_PTR+{]1*2}),y + clc ; pre-adjust these later + adc _Sprites+TS_VBUFF_BASE+{]1*2} jmp ]4 next_bit <<< diff --git a/src/static/TileStore.s b/src/static/TileStore.s index b5f07f4..56558ba 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -59,7 +59,7 @@ TileStoreLookup ENT lup TILE_STORE_HEIGHT TileStoreData ]row*2*TILE_STORE_WIDTH TileStoreData ]row*2*TILE_STORE_WIDTH - dw ]row*2*TILE_STORE_WIDTH + dw ]row*2*TILE_STORE_WIDTH,]row*2*TILE_STORE_WIDTH+2 ]row equ ]row+1 --^ @@ -68,14 +68,17 @@ TileStoreLookup ENT lup TILE_STORE_HEIGHT TileStoreData ]row*2*TILE_STORE_WIDTH TileStoreData ]row*2*TILE_STORE_WIDTH - dw ]row*2*TILE_STORE_WIDTH + dw ]row*2*TILE_STORE_WIDTH,]row*2*TILE_STORE_WIDTH+2 ]row equ ]row+1 --^ -; Last row +; Last two rows TileStoreData 0*2*TILE_STORE_WIDTH TileStoreData 0*2*TILE_STORE_WIDTH - dw 0*2*TILE_STORE_WIDTH + dw 0*2*TILE_STORE_WIDTH,0*2*TILE_STORE_WIDTH+2 + TileStoreData 1*2*TILE_STORE_WIDTH + TileStoreData 1*2*TILE_STORE_WIDTH + dw 1*2*TILE_STORE_WIDTH,1*2*TILE_STORE_WIDTH+2 ;------------------------------------------------------------------------------------- ; @@ -408,14 +411,73 @@ ScreenModeHeight ENT dw 200,192,200,176,160,160,160,128,144,192,102,1 ; VBuff arrays for each sprite. We need at least a 3x3 block for each sprite and the shape of the -; array must match the TileStore structure. The TileStore is 41 blocks wide. To keep things simple -; we allocate 8 sprites in the first row and 8 more sprites in the 4th row. So we need to allocate a -; total of 6 rows of TileStore space +; array must match the TileStore structure. The TileStore is 41 blocks wide. ; ; It is *critical* that this array be placed in a memory location that is greater than the largest -; TileStore offset. -VBuffArray ENT - ds 6*{TILE_STORE_WIDTH*2} +; TileStore offset because the engine maintaines a per-sprite pointer equal to the VBuff array +; address minut the TileStore offset for the top-left corner of that sprite. This allows all of +; the sprites to share the same table, but the result of the subtraction has to be positive. +; +; Each block of data contains fixed offsets for the relative position of vbuff addresses. There +; are multiple copies of the array to handle cases where a sprite needs to transition across the +; boundary. +; +; For example. If a sprite is drawn in the last column, but is two blocks wide, the TileIndex +; value for the first column is $52 and the second column is $00. Since the pointer to the +; VBuffArray is pre-adjusted by the first column's size, the first offset value will be read +; from (VBuffArray - $52)[$52] = VBuffArray[0], which is correct. However, the second column will be +; read from (VBuffArray - $52)[$00] which is one row off from the correct value's location. +; +; The wrapping also need to account for vertical wrapping. Consider a 16x16 sprite with its top-left +; conder inside the physical tile that is the bottom-right-most tile in the Tile Store. So, the +; lookup index for this tile is (26*41*2)-2 = 2130. When using the lookup table, each step to the +; right or down will cause wrap-around. So the lookup addresses look like this +; +; +------+------+ +------+------+ +; | $852 | $800 | | $000 | $004 | +; +------+------+ --> +------+------+ +; | $052 | $000 | | $030 | $034 | +; +------+------+ +------+------+ +; +; We need to maintain 9 different lookup table variations, which is equal to the number of tile +; in the largest sprite (3x3 tiles = 9 different border cases) + +;COL_BYTES equ 4 ; VBUFF_TILE_COL_BYTES +;ROW_BYTES equ 384 ; VBUFF_TILE_ROW_BYTES + +; Define the offset values +;___NA_NA___ equ 0 +;ROW_0_COL_0 equ {{0*COL_BYTES}+{0*ROW_BYTES}} +;ROW_0_COL_1 equ {{1*COL_BYTES}+{0*ROW_BYTES}} +;ROW_0_COL_2 equ {{2*COL_BYTES}+{0*ROW_BYTES}} +;ROW_1_COL_0 equ {{0*COL_BYTES}+{1*ROW_BYTES}} +;ROW_1_COL_1 equ {{1*COL_BYTES}+{1*ROW_BYTES}} +;ROW_1_COL_2 equ {{2*COL_BYTES}+{1*ROW_BYTES}} +;ROW_2_COL_0 equ {{0*COL_BYTES}+{2*ROW_BYTES}} +;ROW_2_COL_1 equ {{1*COL_BYTES}+{2*ROW_BYTES}} +;ROW_2_COL_2 equ {{2*COL_BYTES}+{2*ROW_BYTES}} + +; Allocate an amount of space equal to a TileStore block because we could have vertical wrap around. +; The rest of the values are in just the first few rows following this block +; +; The first block of 4 values is the "normal" case, (X in [0, N-3], Y in [0, M-3]), so no wrap around is needed +; The second block is (X = N-1, Y in [0, M-3]) +; The third block is (X = N-2, Y in [0, M-3]) +; The fourth block is (X in [0, N-3], Y = M-1) +; The fifth block is (X = N-1, Y = M-1) +; The sixth block is (X = N-2, Y = M-1) +; The seventh block is (X in [0, N-3], Y = M-2) +; The eighth block is (X = N-1, Y = M-2) +; The ninth block is (X = N-2, Y = M-2) + +VBuffVertTableSelect ENT + ds 51*2 +VBuffHorzTableSelect ENT + ds 81*2 + +VBuffStart ds TILE_STORE_SIZE +VBuffArray ENT + ds {TILE_STORE_WIDTH*2}*3 ; Convert sprite index to a bit position _SpriteBits ENT diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index d6ccdc2..904f2b7 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -77,12 +77,16 @@ VBUFF_ARRAY_ADDR equ {MAX_SPRITES*40} ; Fixed address where this spri ;TILE_STORE_ADDR_9 equ {MAX_SPRITES*28} ;TILE_STORE_ADDR_10 equ {MAX_SPRITES*30} -; 51 rows by 81 columns + 2 extra rows and columns for sprite sizes +; 52 rows by 82 columns + 2 extra rows and columns for sprite sizes ; ; 53 rows = TILE_STORE_HEIGHT + TILE_STORE_HEIGHT + 1 ; 83 cols = TILE_STORE_WIDTH + TILE_STORE_WIDTH + 1 -TS_LOOKUP_WIDTH equ 81 -TS_LOOKUP_HEIGHT equ 51 +; +; TILE_STORE_WIDTH equ 41 +; TILE_STORE_HEIGHT equ 26 + +TS_LOOKUP_WIDTH equ 82 +TS_LOOKUP_HEIGHT equ 52 TS_LOOKUP_BORDER equ 2 TS_LOOKUP_SPAN equ {TS_LOOKUP_WIDTH+TS_LOOKUP_BORDER} TS_LOOKUP_ROWS equ {TS_LOOKUP_HEIGHT+TS_LOOKUP_BORDER} From 2c409b02a75cab3f128044fd0ccaf0e9f3781672 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 6 Jun 2022 20:10:15 -0500 Subject: [PATCH 41/82] Fix lookup tables to fix boundary rendering --- src/Sprite.s | 27 ++++++++++++++------------- src/Sprite2.s | 6 +----- src/static/TileStore.s | 18 +++++++++++++++--- src/static/TileStoreDefs.s | 26 +++++++++++++------------- 4 files changed, 43 insertions(+), 34 deletions(-) diff --git a/src/Sprite.s b/src/Sprite.s index b524484..9f4afaa 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -19,13 +19,14 @@ InitSprites cpx #$FFFE bne :loop2 -; Initialize the VBuff offset values for the different cases +; Initialize the VBuff offset values for the different cases. These are locations in +; the TileStoreLookup table, which has different dimensions than the underlying TileStore +; array -TILE_STORE_SPAN equ {TILE_STORE_WIDTH*2} -LAST_ROW equ {TILE_STORE_SPAN*{TILE_STORE_HEIGHT-1}} -NEXT_TO_LAST_ROW equ {TILE_STORE_SPAN*{TILE_STORE_HEIGHT-2}} -LAST_COL equ {TILE_STORE_WIDTH-1}*2 -NEXT_TO_LAST_COL equ {TILE_STORE_WIDTH-2}*2 +LAST_ROW equ {2*TS_LOOKUP_SPAN*{TILE_STORE_HEIGHT-1}} +NEXT_TO_LAST_ROW equ {2*TS_LOOKUP_SPAN*{TILE_STORE_HEIGHT-2}} +LAST_COL equ {{TILE_STORE_WIDTH-1}*2} +NEXT_TO_LAST_COL equ {{TILE_STORE_WIDTH-2}*2} lda #0 ; Normal row, Normal column ldx #0 @@ -98,7 +99,7 @@ NEXT_TO_LAST_COL equ {TILE_STORE_WIDTH-2}*2 jsr _CacheSpriteBanks rts -; Call with X-register set to TileStore tile and Acc set to the VBuff slot offset +; Call with X-register set to TileStore tile and A set to the VBuff slot offset _SetVBuffValues COL_BYTES equ 4 ; VBUFF_TILE_COL_BYTES ROW_BYTES equ 384 ; VBUFF_TILE_ROW_BYTES @@ -121,27 +122,27 @@ ROW_BYTES equ 384 ; VBUFF_TILE_ROW_BYTES lda #{2*COL_BYTES}+{0*ROW_BYTES} sta (tmp0),y - ldy TileStoreLookup+1*{TS_LOOKUP_SPAN*2},x + ldy TileStoreLookup+{1*{TS_LOOKUP_SPAN*2}},x lda #{0*COL_BYTES}+{1*ROW_BYTES} sta (tmp0),y - ldy TileStoreLookup+1*{TS_LOOKUP_SPAN*2}+2,x + ldy TileStoreLookup+{1*{TS_LOOKUP_SPAN*2}}+2,x lda #{1*COL_BYTES}+{1*ROW_BYTES} sta (tmp0),y - ldy TileStoreLookup+1*{TS_LOOKUP_SPAN*2}+4,x + ldy TileStoreLookup+{1*{TS_LOOKUP_SPAN*2}}+4,x lda #{2*COL_BYTES}+{1*ROW_BYTES} sta (tmp0),y - ldy TileStoreLookup+2*{TS_LOOKUP_SPAN*2},x + ldy TileStoreLookup+{2*{TS_LOOKUP_SPAN*2}},x lda #{0*COL_BYTES}+{2*ROW_BYTES} sta (tmp0),y - ldy TileStoreLookup+2*{TS_LOOKUP_SPAN*2}+2,x + ldy TileStoreLookup+{2*{TS_LOOKUP_SPAN*2}}+2,x lda #{1*COL_BYTES}+{2*ROW_BYTES} sta (tmp0),y - ldy TileStoreLookup+2*{TS_LOOKUP_SPAN*2}+4,x + ldy TileStoreLookup+{2*{TS_LOOKUP_SPAN*2}}+4,x lda #{2*COL_BYTES}+{2*ROW_BYTES} sta (tmp0),y rts diff --git a/src/Sprite2.s b/src/Sprite2.s index 11bf3c7..0500bad 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -113,10 +113,6 @@ _CalcDirtySprite pha and #$FFFC lsr ; Even numbers from [0, 160] (81 elements) -; cmp #TILE_STORE_WIDTH*2 -; bcc :x_in_range -; sbc #TILE_STORE_WIDTH*2 -;:x_in_range sta tmp3 adc RowTop sta _Sprites+TS_LOOKUP_INDEX,y ; This is the index into the TileStoreLookup table @@ -183,7 +179,7 @@ _CalcDirtySprite ; The X-register still has the TileStoreLookupYTable index, which we re-use to get a VBuff ; array selector for the vertical location - lda VBuffVertTableSelect,x ; A bunch of 12, 24 or 36 values + lda VBuffVertTableSelect,x ; A bunch of 0, 12 or 24 values clc ldx tmp3 adc VBuffHorzTableSelect,x ; A bunch of 0, 4 or 8 values diff --git a/src/static/TileStore.s b/src/static/TileStore.s index 56558ba..f88cb96 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -470,10 +470,22 @@ ScreenModeHeight ENT ; The eighth block is (X = N-1, Y = M-2) ; The ninth block is (X = N-2, Y = M-2) -VBuffVertTableSelect ENT - ds 51*2 +VBuffVertTableSelect ENT ; 51 entries + dw 0,0,0,0,0,0,0,0,0,0 + dw 0,0,0,0,0,0,0,0,0,0 + dw 0,0,0,0,48,24 + dw 0,0,0,0,0,0,0,0,0,0 + dw 0,0,0,0,0,0,0,0,0,0 + dw 0,0,0,0,48,24 VBuffHorzTableSelect ENT - ds 81*2 + dw 0,0,0,0,0,0,0,0,0,0 + dw 0,0,0,0,0,0,0,0,0,0 + dw 0,0,0,0,0,0,0,0,0,0 + dw 0,0,0,0,0,0,0,0,0,16,8 + dw 0,0,0,0,0,0,0,0,0,0 + dw 0,0,0,0,0,0,0,0,0,0 + dw 0,0,0,0,0,0,0,0,0,0 + dw 0,0,0,0,0,0,0,0,0,16,8 VBuffStart ds TILE_STORE_SIZE VBuffArray ENT diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index 904f2b7..a4ec69f 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -5,20 +5,20 @@ TILE_STORE_HEIGHT equ 26 MAX_TILES equ {26*41} ; Number of tiles in the code field (41 columns * 26 rows) TILE_STORE_SIZE equ {MAX_TILES*2} ; The tile store contains a tile descriptor in each slot -TS_TILE_ID equ TILE_STORE_SIZE*0 ; tile descriptor for this location -TS_DIRTY equ TILE_STORE_SIZE*1 ; Flag. Used to prevent a tile from being queued multiple times per frame -TS_SPRITE_FLAG equ TILE_STORE_SIZE*2 ; Bitfield of all sprites that intersect this tile. 0 if no sprites. -TS_TILE_ADDR equ TILE_STORE_SIZE*3 ; cached value, the address of the tiledata for this tile -TS_CODE_ADDR_LOW equ TILE_STORE_SIZE*4 ; const value, address of this tile in the code fields -TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5 -TS_WORD_OFFSET equ TILE_STORE_SIZE*6 ; const value, word offset value for this tile if LDA (dp),y instructions re used -TS_BASE_ADDR equ TILE_STORE_SIZE*7 ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000. -TS_SCREEN_ADDR equ TILE_STORE_SIZE*8 ; cached value of on-screen location of tile. Used for DirtyRender. +TS_TILE_ID equ {TILE_STORE_SIZE*0} ; tile descriptor for this location +TS_DIRTY equ {TILE_STORE_SIZE*1} ; Flag. Used to prevent a tile from being queued multiple times per frame +TS_SPRITE_FLAG equ {TILE_STORE_SIZE*2} ; Bitfield of all sprites that intersect this tile. 0 if no sprites. +TS_TILE_ADDR equ {TILE_STORE_SIZE*3} ; cached value, the address of the tiledata for this tile +TS_CODE_ADDR_LOW equ {TILE_STORE_SIZE*4} ; const value, address of this tile in the code fields +TS_CODE_ADDR_HIGH equ {TILE_STORE_SIZE*5} +TS_WORD_OFFSET equ {TILE_STORE_SIZE*6} ; const value, word offset value for this tile if LDA (dp),y instructions re used +TS_BASE_ADDR equ {TILE_STORE_SIZE*7} ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000. +TS_SCREEN_ADDR equ {TILE_STORE_SIZE*8} ; cached value of on-screen location of tile. Used for DirtyRender. ; TODO: Move these arrays into the K bank to support direct dispatch via jmp (abs,x) -TS_BASE_TILE_COPY equ TILE_STORE_SIZE*9 ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering -TS_BASE_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function -TS_DIRTY_TILE_DISP equ TILE_STORE_SIZE*11 ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function +TS_BASE_TILE_COPY equ {TILE_STORE_SIZE*9} ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering +TS_BASE_TILE_DISP equ {TILE_STORE_SIZE*10} ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function +TS_DIRTY_TILE_DISP equ {TILE_STORE_SIZE*11} ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function TILE_STORE_NUM equ 12 ; Need this many parallel arrays @@ -91,7 +91,7 @@ TS_LOOKUP_BORDER equ 2 TS_LOOKUP_SPAN equ {TS_LOOKUP_WIDTH+TS_LOOKUP_BORDER} TS_LOOKUP_ROWS equ {TS_LOOKUP_HEIGHT+TS_LOOKUP_BORDER} -; Blitter template constancts +; Blitter template constants PER_TILE_SIZE equ 3 SNIPPET_SIZE equ 32 From 3591c494bffd31647f062286c2bc6c8c03cf6f46 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 7 Jun 2022 10:05:40 -0500 Subject: [PATCH 42/82] Add three-sprite rendering function --- src/tiles/FastRenderer.s | 57 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index 5e4d430..aed1eae 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -38,7 +38,7 @@ FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFa SpriteDispatch txy - SpriteBitsToVBuffAddrs OneSpriteFast;TwoSpritesFast;TwoSpritesFast;TwoSpritesFast + SpriteBitsToVBuffAddrs OneSpriteFast;TwoSpritesFast;ThreeSpritesFast;ThreeSpritesFast ; Where there are sprites involved, the first step is to call a routine to copy the ; tile data into a temporary buffer. Then the sprite data is merged and placed into @@ -113,7 +113,6 @@ TwoSpritesFast ply ; Pop off CODE_ADDR_LOW plb ; Set the CODE_ADDR_HIGH bank -_TBApplySpriteData3 ]line equ 0 lup 8 lda tmp_tile_data+{]line*4} @@ -125,7 +124,61 @@ _TBApplySpriteData3 plb ; Reset to the bank in the top byte of CODE_ADDR_HIGH rts +ThreeSpriteLine mac +; 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 + <<< + ThreeSpritesFast + ldx TileStore+TS_TILE_ADDR,y + lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line + pha ; and put on the stack for later. Has TileStore bank in high byte. + lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field + pha ; Need to pop it later.... + + sep #$20 ; set the sprite data bank + lda #^spritedata + pha + plb + rep #$20 + +]line equ 0 + lup 8 + ldy #{]line*SPRITE_PLANE_SPAN} + ldal tiledata+{]line*4},x + ThreeSpriteLine + sta tmp_tile_data+{]line*4} + + ldy #{]line*SPRITE_PLANE_SPAN}+2 + ldal tiledata+{]line*4}+2,x + ThreeSpriteLine + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + + ply ; Pop off CODE_ADDR_LOW + plb ; Set the CODE_ADDR_HIGH bank + +]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 ; Reset to the bank in the top byte of CODE_ADDR_HIGH + rts + + + FourSpritesFast ; tyx ; lda TileStore+TS_TILE_ADDR,y From 1de4c0bc7fc61d1df4d0c67ebf1b91fc40ad5f30 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 7 Jun 2022 10:05:52 -0500 Subject: [PATCH 43/82] Make sure all macros add the vbuff offsets --- src/Tiles.s | 76 ++++++----------------------------------------------- 1 file changed, 8 insertions(+), 68 deletions(-) diff --git a/src/Tiles.s b/src/Tiles.s index 85680cb..d423e49 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -269,10 +269,14 @@ dobit mac beq last_bit tax lda (SPRITE_VBUFF_PTR+{]1*2}),y + clc + adc _Sprites+TS_VBUFF_BASE+{]1*2} sta sprite_ptr0+{]2*4} txa jmp ]3 last_bit lda (SPRITE_VBUFF_PTR+{]1*2}),y + clc ; pre-adjust these later + adc _Sprites+TS_VBUFF_BASE+{]1*2} sta sprite_ptr0+{]2*4} jmp ]4 next_bit @@ -299,79 +303,13 @@ last_bit lda (SPRITE_VBUFF_PTR+{]1*2}),y next_bit <<< -; Optimization discussion. In the Sprite2.s file, we calculate the VBUFF address for each tile overlapped -; by a sprite: -; -; 4 lda VBuffOrigin -; 3 adc ]2 -; 7 sta [tmp0],y -; -; and then in this macro it is loaded again and copied to the direct page. If a sprite is never drawn, this is -; wasted work (which is not too ofter since >4 sprites would need to be overlapping), but still. -; -; 6 ldy: {]1*TILE_STORE_SIZE},x -; 4 sty sprite_ptr0+{]2*4} -; -; Since we know *exactly* which sprite is being accessed, the _Sprites+TS_VBUFF_BASE,y value can be loaded without -; an index -; -; 5 lda _Sprites+TS_VBUFF_BASE+{]1*2} -; 6 adc {]1*TILE_STORE_SIZE},x -; 4 sta sprite_ptr0+{]2*4} -; 2 tya -; -; = a savings of at least (24 - 17) = 7 cycles per tile and more if the sprite is skipped. -; -; The problem is that this still required storing a value for the sprite in the tile store. What is ideal is -; if there is a way to know implicitly which relative tile offset we are on for a given sprite and use -; that to calculate the offset... -; -; What do we know -; X = current tile -; Sprite+TS_LOOKUP_INDEX -; -; txa -; sbc _Sprites+TS_LOOKUP_INDEX+{]1*2} -; tay -; lda _Sprites+TS_VBUFF_BASE+{]1*2} -; adc DisplacementTable,y -; sta sprite_ptr0+{]2*4} -; -; Have the sprite select a table base which holds the offset values, pre-adjusted for the TS_LOOKUP_INDEX. The table -; values are fixed. Yes!! This is the solution!! It will only need 288 bytes of total space -; -; Best implementation will pass the Tile Store index in Y instead of X -; -; 5 lda _Sprites+VBUFF_TABLE+{]1*2} -; 6 sta self_mod -; 6 lda $0000,x -; 4 sta sprite_ptr0+{]2*4} -; 2 tya -; -; or -; -; 5 lda _Sprites+VBUFF_TABLE+{]1*2} -; 4 sta tmp0 -; 7 lda (tmp0),y -; 4 sta sprite_ptr0+{]2*4} -; 2 txa -; -; Even better, if the VBUFF_TABLE (only 32 bytes) was already stored in the second direct page -; -; 7 lda (VBUFF_TABLE+{]1*2}),y -; 5 adc _Sprites+VBUFF_TABLE+{]1*2} -; 4 sta sprite_ptr0+{]2*4} -; 2 txa -; -; Final saving compared to current implementation is (24 - 18) = 6 cycles per tile and we eliminate -; the need to pre-calculate -; - ; If we find a last bit (4th in this case) and will exit stpbit mac lsr bcc next_bit lda (SPRITE_VBUFF_PTR+{]1*2}),y + clc ; pre-adjust these later + adc _Sprites+TS_VBUFF_BASE+{]1*2} sta sprite_ptr0+{]2*4} jmp ]3 next_bit @@ -380,6 +318,8 @@ next_bit ; Last bit test which *must* be set endbit mac lda (SPRITE_VBUFF_PTR+{]1*2}),y + clc ; pre-adjust these later + adc _Sprites+TS_VBUFF_BASE+{]1*2} sta sprite_ptr0+{]2*4} jmp ]3 <<< From 388470c133290276c9ea559bdf0dfea3afb381e6 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 8 Jun 2022 14:25:52 -0500 Subject: [PATCH 44/82] Fix left-edge sprite rendering glitch; was an issue with not erasing sprites when they transition to IS_OFFSCREEN status --- src/Sprite.s | 95 ++++----------------- src/Sprite2.s | 163 ++++++++++--------------------------- src/static/TileStoreDefs.s | 38 +++------ src/tiles/FastRenderer.s | 79 +++++++++++------- 4 files changed, 124 insertions(+), 251 deletions(-) diff --git a/src/Sprite.s b/src/Sprite.s index 9f4afaa..7a2b76c 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -64,29 +64,6 @@ NEXT_TO_LAST_COL equ {{TILE_STORE_WIDTH-2}*2} ldx #NEXT_TO_LAST_ROW+NEXT_TO_LAST_COL ; Next-to-Last row, Next-to-Last column jsr _SetVBuffValues -; Set the VBuff array addresses for each sprite, since they're static -; -; NOTE: Can remove later -; ldx #0 -; lda #VBuffArray -;:loop3 sta _Sprites+VBUFF_ARRAY_ADDR,x -; clc -; adc #4*2 ; skip ahead 4 tiles -; inx -; inx -; cpx #8*2 -; bcc :loop3 - -; Now do the second set of sprites -; lda #VBuffArray+{3*{TILE_STORE_WIDTH*2}} -;:loop4 sta _Sprites+VBUFF_ARRAY_ADDR,x -; clc -; adc #4*2 ; skip ahead 4 tiles -; inx -; inx -; cpx #16*2 -; bcc :loop4 - ; Initialize the Page 2 pointers ldx #$100 lda #^spritemask @@ -231,13 +208,9 @@ _RenderSprites ; Implement the logic for updating sprite and tile rendering information. Each iteration of the ; ActiveSpriteCount will call this routine with the Y-register set to the sprite index -tmpY equ tmp15 -tmpA equ tmp14 _DoPhase1 lda _Sprites+SPRITE_STATUS,y ora ForceSpriteFlag - sta tmpA - sty tmpY ; First step, if a sprite is being removed, then we just have to clear its old tile information ; and mark the tiles it overlapped as dirty. @@ -251,21 +224,15 @@ _DoPhase1 lda #SPRITE_STATUS_EMPTY ; Mark as empty so no error if we try to Add a sprite here again sta _Sprites+SPRITE_STATUS,y - lda _Sprites+TS_COVERAGE_SIZE,y ; Manually copy current value to old - sta _Sprites+OLD_TS_COVERAGE_SIZE,y - lda _Sprites+TS_LOOKUP_INDEX,y - sta _Sprites+OLD_TS_LOOKUP_INDEX,y - 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. +; sprites, so we do it unconditionally, but we do need to mark the current sprite for erasure if +; needed :no_clear - jsr _CalcDirtySprite ; If the sprite is marked as ADDED, then it does not need to have its old tile locations cleared - lda tmpA bit #SPRITE_STATUS_ADDED bne :no_move @@ -275,12 +242,15 @@ _DoPhase1 bit #SPRITE_STATUS_MOVED beq :no_move + phy jsr _ClearSpriteFromTileStore - ldy tmpY + ply ; Anything else (MOVED, UPDATED, ADDED) will need to have the VBUFF information updated and the ; current tiles marked for update :no_move + jsr _CalcDirtySprite ; This function preserves Y + lda #SPRITE_STATUS_OCCUPIED ; Clear the dirty bits (ADDED, UPDATED, MOVED) sta _Sprites+SPRITE_STATUS,y @@ -384,7 +354,7 @@ _CreateSpriteStamp ; the vertical tiles are taken from tileId + 32. This is why tile sheets should be saved ; with a width of 256 pixels. ; -; A = tileId + flags +; A = vbuffAddress ; Y = High Byte = x-pos, Low Byte = y-pos ; X = Sprite Slot (0 - 15) _AddSprite @@ -458,7 +428,7 @@ next _ClearSpriteFromTileStore lda _SpriteBitsNot,y ; Cache this value in a direct page location sta tmp0 - ldx _Sprites+OLD_TS_COVERAGE_SIZE,y + ldx _Sprites+TS_COVERAGE_SIZE,y jmp (csfts_tbl,x) csfts_tbl dw csfts_1x1,csfts_1x2,csfts_1x3,csfts_out dw csfts_2x1,csfts_2x2,csfts_2x3,csfts_out @@ -467,7 +437,7 @@ csfts_tbl dw csfts_1x1,csfts_1x2,csfts_1x3,csfts_out csfts_out rts -csfts_3x3 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y +csfts_3x3 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 TSClearSprite 4 @@ -479,7 +449,7 @@ csfts_3x3 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 2*{TS_LOOKUP_SPAN*2}+4 rts -csfts_3x2 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y +csfts_3x2 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 @@ -488,13 +458,13 @@ csfts_3x2 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 2*{TS_LOOKUP_SPAN*2}+2 rts -csfts_3x1 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y +csfts_3x1 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 TSClearSprite 2*{TS_LOOKUP_SPAN*2}+0 rts -csfts_2x3 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y +csfts_2x3 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 TSClearSprite 4 @@ -503,30 +473,30 @@ csfts_2x3 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y TSClearSprite 1*{TS_LOOKUP_SPAN*2}+4 rts -csfts_2x2 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y +csfts_2x2 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 rts -csfts_2x1 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y +csfts_2x1 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 rts -csfts_1x3 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y +csfts_1x3 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 TSClearSprite 4 rts -csfts_1x2 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y +csfts_1x2 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 TSClearSprite 2 rts -csfts_1x1 ldx _Sprites+OLD_TS_LOOKUP_INDEX,y +csfts_1x1 ldx _Sprites+TS_LOOKUP_INDEX,y TSClearSprite 0 rts @@ -638,37 +608,6 @@ _CacheSpriteBanks rts -; A = x coordinate -; Y = y coordinate -;GetSpriteVBuffAddr ENT -; jsr _GetSpriteVBuffAddr -; rtl - -; A = x coordinate -; Y = y coordinate -;_GetSpriteVBuffAddr -; pha -; 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 1,s -; pla -; rts - -; Version that uses temporary space (tmp15) -;_GetSpriteVBuffAddrTmp -; sta tmp15 -; 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 tmp15 -; rts - ; Precalculate some cached values for a sprite. These are *only* to make other part of code, ; specifically the draw/erase routines more efficient. ; diff --git a/src/Sprite2.s b/src/Sprite2.s index 0500bad..4d131e0 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -1,20 +1,9 @@ ; Scratch space to lay out idealized _MakeDirtySprite ; On input, X register = Sprite Array Index -;Left equ tmp1 -;Right equ tmp2 -;Top equ tmp3 -;Bottom equ tmp4 -;Origin equ tmp4 -;TileTop equ tmp5 RowTop equ tmp6 AreaIndex equ tmp7 - -;TileLeft equ tmp8 -;ColLeft equ tmp9 - -SpriteBit equ tmp10 ; set the bit of the value that if the current sprite index -; VBuffOrigin equ tmp11 +SpriteBit equ tmp8 ; set the bit of the value that if the current sprite index ; Table of pre-multiplied vbuff strides vbuff_mul @@ -48,7 +37,7 @@ vbuff_mul ; 8 10 2 8 ; ... ; -; For the Y-coordinate, we just use "mod 8" instead of "mod 4" +; For the Y-coordinate, we use "mod 8" instead of "mod 4" ; ; When this subroutine is completed, the following values will be calculated ; @@ -68,12 +57,14 @@ _CalcDirtySprite lda _Sprites+IS_OFF_SCREEN,y ; Check if the sprite is visible in the playfield bne mdsOut2 -; Copy the current values into the old value slots - - lda _Sprites+TS_COVERAGE_SIZE,y - sta _Sprites+OLD_TS_COVERAGE_SIZE,y - lda _Sprites+TS_LOOKUP_INDEX,y - sta _Sprites+OLD_TS_LOOKUP_INDEX,y +; Part 1: Calculate the visible tiles that the sprite covers. If the sprite is partially +; off-screen, then the visible tiles may be different than the set of tiles +; covered by the sprite. In particular, the upper-left corner tile which defines +; relative offset values will change. +; +; So, we do some calculations with the CLIPPED values and some with the actual +; sprite values. There is an optimization opportunity here to share calculations +; when the x or y position of the sprite is positive. ; Add the first visible row of the sprite to the Y-scroll offset to find the first line in the ; code field that needs to be drawn. The range of values is 0 to 199+207 = [0, 406]. This @@ -113,7 +104,6 @@ _CalcDirtySprite pha and #$FFFC lsr ; Even numbers from [0, 160] (81 elements) - sta tmp3 adc RowTop sta _Sprites+TS_LOOKUP_INDEX,y ; This is the index into the TileStoreLookup table @@ -131,15 +121,39 @@ _CalcDirtySprite sta _Sprites+TS_COVERAGE_SIZE,y ; Save this value as a key to the coverage size of the sprite -; Calculate the VBUFF offset based on the actual (signed) sprite position +; Part 2: Redo some calculation with the actual (signed) sprite positions that take into +; account negative coordinates to set the VBuff offset values. + + clc + lda _Sprites+SPRITE_Y,y + adc StartYMod208 + bpl :y_ok + clc + adc #208 ; Wrap the actual coordinat around +:y_ok and #$FFF8 ; mask first to ensure LSR will clear the carry + lsr + lsr + tax ; Tile store lookup index + + lda _Sprites+SPRITE_X,y + adc StartXMod164 + bpl :x_ok + clc + adc #164 +:x_ok and #$FFFC + lsr ; Even numbers from [0, 160] (81 elements) + sta tmp3 + adc TileStoreLookupYTable,x + pha ; will be PLX later clc ; Carry should still be clear here.... lda StartYMod208 - and #$0007 adc _Sprites+SPRITE_Y,y - bmi :neg_y + bpl :pos_y + clc + adc #208 +:pos_y and #$0007 -:neg_y asl ; Multiply by 48. Would be nice to use a asl ; table lookup, but the values can be negative asl ; so do the calculation @@ -158,11 +172,12 @@ _CalcDirtySprite clc lda StartXMod164 - and #$0003 adc _Sprites+SPRITE_X,y - bmi :neg_x + bpl :pos_x + clc + adc #164 +:pos_x and #$0003 -:neg_x clc adc tmp0 ; add to the vertical offset @@ -185,7 +200,8 @@ _CalcDirtySprite adc VBuffHorzTableSelect,x ; A bunch of 0, 4 or 8 values clc adc #VBuffArray - ldx _Sprites+TS_LOOKUP_INDEX,y + plx +; ldx _Sprites+TS_LOOKUP_INDEX,y sec sbc TileStoreLookup,x sta tmp1 ; Spill this value to direct page temp space @@ -193,6 +209,7 @@ _CalcDirtySprite ; Last task. Since we don't need to use the X-register to cache values; load the direct page 2 ; offset for the SPRITE_VBUFF_PTR and save it +tmp_out tya ora #$100 tax @@ -254,35 +271,17 @@ ROW equ TILE_STORE_WIDTH*2 ; This many bytes to the nex COL equ 2 ; This many bytes for each element :mark1x1 -; ldx _Sprites+VBUFF_ARRAY_ADDR,y ; get the address of this sprite's vbuff values -; lda _Sprites+TS_VBUFF_BASE,y ; get the starting vbuff address -; sta: {0*ROW}+{0*COL},x ; Put in the vbuff address - ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2} rts :mark1x2 -; ldx _Sprites+VBUFF_ARRAY_ADDR,y -; lda _Sprites+TS_VBUFF_BASE,y -; sta: {0*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {0*ROW}+{1*COL},x - ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 rts :mark1x3 -; ldx _Sprites+VBUFF_ARRAY_ADDR,y -; lda _Sprites+TS_VBUFF_BASE,y -; sta: {0*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {0*ROW}+{1*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {0*ROW}+{2*COL},x - ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 @@ -290,28 +289,12 @@ COL equ 2 ; This many bytes for each e rts :mark2x1 -; ldx _Sprites+VBUFF_ARRAY_ADDR,y -; lda _Sprites+TS_VBUFF_BASE,y -; sta: {0*ROW}+{0*COL},x -; adc #VBUFF_TILE_ROW_BYTES -; sta: {1*ROW}+{0*COL},x - ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0 rts :mark2x2 -; ldx _Sprites+VBUFF_ARRAY_ADDR,y -; lda _Sprites+TS_VBUFF_BASE,y -; sta: {0*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {0*ROW}+{1*COL},x -; adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES -; sta: {1*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {1*ROW}+{1*COL},x - ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 @@ -320,20 +303,6 @@ COL equ 2 ; This many bytes for each e rts :mark2x3 -; ldx _Sprites+VBUFF_ARRAY_ADDR,y -; lda _Sprites+TS_VBUFF_BASE,y -; sta: {0*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {0*ROW}+{1*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {0*ROW}+{2*COL},x -; adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} -; sta: {1*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {1*ROW}+{1*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {1*ROW}+{2*COL},x - ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 @@ -344,14 +313,6 @@ COL equ 2 ; This many bytes for each e rts :mark3x1 -; ldx _Sprites+VBUFF_ARRAY_ADDR,y -; lda _Sprites+TS_VBUFF_BASE,y -; sta: {0*ROW}+{0*COL},x -; adc #VBUFF_TILE_ROW_BYTES -; sta: {1*ROW}+{0*COL},x -; adc #VBUFF_TILE_ROW_BYTES -; sta: {2*ROW}+{0*COL},x - ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0 @@ -359,20 +320,6 @@ COL equ 2 ; This many bytes for each e rts :mark3x2 -; ldx _Sprites+VBUFF_ARRAY_ADDR,y -; lda _Sprites+TS_VBUFF_BASE,y -; sta: {0*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {0*ROW}+{1*COL},x -; adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES -; sta: {1*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {1*ROW}+{1*COL},x -; adc #VBUFF_TILE_ROW_BYTES-VBUFF_TILE_COL_BYTES -; sta: {2*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {2*ROW}+{1*COL},x - ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 @@ -383,26 +330,6 @@ COL equ 2 ; This many bytes for each e rts :mark3x3 -; ldx _Sprites+VBUFF_ARRAY_ADDR,y -; lda _Sprites+TS_VBUFF_BASE,y -; sta: {0*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {0*ROW}+{1*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {0*ROW}+{2*COL},x -; adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} -; sta: {1*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {1*ROW}+{1*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {1*ROW}+{2*COL},x -; adc #VBUFF_TILE_ROW_BYTES-{2*VBUFF_TILE_COL_BYTES} -; sta: {2*ROW}+{0*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {2*ROW}+{1*COL},x -; adc #VBUFF_TILE_COL_BYTES -; sta: {2*ROW}+{2*COL},x - ldx _Sprites+TS_LOOKUP_INDEX,y TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0 TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index a4ec69f..1e5ada2 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -50,32 +50,18 @@ VBUFF_ADDR equ {MAX_SPRITES*8} ; Base address of the sprite's ; These values are cached / calculated during the rendering process TS_LOOKUP_INDEX equ {MAX_SPRITES*10} ; The index from the TileStoreLookup table that corresponds to the top-left corner of the sprite TS_COVERAGE_SIZE equ {MAX_SPRITES*12} ; Representation of how many TileStore tiles (NxM) are covered by this sprite -OLD_TS_LOOKUP_INDEX equ {MAX_SPRITES*14} ; Copy of the values to support diffing -OLD_TS_COVERAGE_SIZE equ {MAX_SPRITES*16} -SPRITE_DISP equ {MAX_SPRITES*18} ; Cached address of the specific stamp based on sprite flags -SPRITE_CLIP_LEFT equ {MAX_SPRITES*20} -SPRITE_CLIP_RIGHT equ {MAX_SPRITES*22} -SPRITE_CLIP_TOP equ {MAX_SPRITES*24} -SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*26} -IS_OFF_SCREEN equ {MAX_SPRITES*28} -SPRITE_WIDTH equ {MAX_SPRITES*30} -SPRITE_HEIGHT equ {MAX_SPRITES*32} -SPRITE_CLIP_WIDTH equ {MAX_SPRITES*34} -SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*36} -TS_VBUFF_BASE equ {MAX_SPRITES*38} ; Finalized VBUFF address based on the sprite position and tile offsets -VBUFF_ARRAY_ADDR equ {MAX_SPRITES*40} ; Fixed address where this sprite's VBUFF addresses are stores. The array is the same shape as TileStore, but much smaller -;TILE_DATA_OFFSET equ {MAX_SPRITES*2} -;TILE_STORE_ADDR_1 equ {MAX_SPRITES*12} -;TILE_STORE_ADDR_2 equ {MAX_SPRITES*14} -;TILE_STORE_ADDR_3 equ {MAX_SPRITES*16} -;TS_VBUFF_BASE_ADDR equ {MAX_SPRITES*16} ; Fixed address of the TS_VBUFF_X memory locations -;TILE_STORE_ADDR_4 equ {MAX_SPRITES*18} -;TILE_STORE_ADDR_5 equ {MAX_SPRITES*20} -;TILE_STORE_ADDR_6 equ {MAX_SPRITES*22} -;TILE_STORE_ADDR_7 equ {MAX_SPRITES*24} -;TILE_STORE_ADDR_8 equ {MAX_SPRITES*26} -;TILE_STORE_ADDR_9 equ {MAX_SPRITES*28} -;TILE_STORE_ADDR_10 equ {MAX_SPRITES*30} +SPRITE_DISP equ {MAX_SPRITES*14} ; Cached address of the specific stamp based on sprite flags +SPRITE_CLIP_LEFT equ {MAX_SPRITES*16} +SPRITE_CLIP_RIGHT equ {MAX_SPRITES*18} +SPRITE_CLIP_TOP equ {MAX_SPRITES*20} +SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*22} +IS_OFF_SCREEN equ {MAX_SPRITES*24} +SPRITE_WIDTH equ {MAX_SPRITES*26} +SPRITE_HEIGHT equ {MAX_SPRITES*28} +SPRITE_CLIP_WIDTH equ {MAX_SPRITES*30} +SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*32} +TS_VBUFF_BASE equ {MAX_SPRITES*34} ; Finalized VBUFF address based on the sprite position and tile offsets +VBUFF_ARRAY_ADDR equ {MAX_SPRITES*36} ; Fixed address where this sprite's VBUFF addresses are stores. The array is the same shape as TileStore, but much smaller ; 52 rows by 82 columns + 2 extra rows and columns for sprite sizes ; diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index aed1eae..00b46af 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -38,7 +38,7 @@ FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFa SpriteDispatch txy - SpriteBitsToVBuffAddrs OneSpriteFast;TwoSpritesFast;ThreeSpritesFast;ThreeSpritesFast + SpriteBitsToVBuffAddrs OneSpriteFast;TwoSpritesFast;ThreeSpritesFast;FourSpritesFast ; Where there are sprites involved, the first step is to call a routine to copy the ; tile data into a temporary buffer. Then the sprite data is merged and placed into @@ -177,37 +177,58 @@ ThreeSpritesFast plb ; Reset to the bank in the top byte of CODE_ADDR_HIGH rts - +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 -; lda TileStore+TS_TILE_ADDR,y -; per :-1 -; jmp (TileStore+TS_BASE_TILE_COPY,x) ; Copy the tile data to the temporary buffer -;: -; lda TileStore+TS_VBUFF_ADDR_0,y ; address of the sprite data -; sta spritedata_0 -; sta spritemask_0 -; lda TileStore+TS_VBUFF_ADDR_1,y -; sta spritedata_1 -; sta spritemask_1 -; lda TileStore+TS_VBUFF_ADDR_2,y -; sta spritedata_2 -; sta spritemask_2 - + ldx TileStore+TS_TILE_ADDR,y lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line - pha ; and put on the stack for later. - lda TileStore+TS_CODE_ADDR_LOW,y - tay - plb ; set the code field bank + pha ; and put on the stack for later. Has TileStore bank in high byte. + lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field + pha ; Need to pop it later.... -; ThreeSpritesToCodeField 0 -; ThreeSpritesToCodeField 1 -; ThreeSpritesToCodeField 2 -; ThreeSpritesToCodeField 3 -; ThreeSpritesToCodeField 4 -; ThreeSpritesToCodeField 5 -; ThreeSpritesToCodeField 6 -; ThreeSpritesToCodeField 7 + sep #$20 ; set the sprite data bank + lda #^spritedata + pha + plb + rep #$20 +]line equ 0 + lup 8 + ldy #{]line*SPRITE_PLANE_SPAN} + ldal tiledata+{]line*4},x + FourSpriteLine + sta tmp_tile_data+{]line*4} + + ldy #{]line*SPRITE_PLANE_SPAN}+2 + ldal tiledata+{]line*4}+2,x + FourSpriteLine + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + + ply ; Pop off CODE_ADDR_LOW + plb ; Set the CODE_ADDR_HIGH bank + +]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 ; Reset to the bank in the top byte of CODE_ADDR_HIGH rts \ No newline at end of file From 227643d7df5d8224647fe19ea2a9b09fbf72ff91 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 8 Jun 2022 17:17:28 -0500 Subject: [PATCH 45/82] Fix H+V sprite stamp renderer --- src/SpriteRender.s | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/SpriteRender.s b/src/SpriteRender.s index 4f85020..71d5ced 100644 --- a/src/SpriteRender.s +++ b/src/SpriteRender.s @@ -261,7 +261,7 @@ draw_16x16hv tax tya pha - adc #128+{128*32} ; Bottom-right source to top-left + adc #{128*{32+1}}+64 ; Bottom-right source to top-left tay jsr _DrawTile8x8V @@ -269,7 +269,7 @@ draw_16x16hv adc #4 tax lda 1,s - adc #{128*32} + adc #{128*32}+64 tay jsr _DrawTile8x8V @@ -277,14 +277,16 @@ draw_16x16hv adc #{8*SPRITE_PLANE_SPAN}-4 tax lda 1,s - adc #128 + adc #128+64 tay jsr _DrawTile8x8V txa adc #4 tax - ply + pla + adc #64 + tay jmp _DrawTile8x8V From 3c41e97b0fe717d07e5f925ab0a518d7f5a5bca4 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 8 Jun 2022 17:34:23 -0500 Subject: [PATCH 46/82] Move dispatch table into K bank and save 9 cycles per tile in dispatch --- src/Core.s | 1 - src/Tiles.s | 11 ++++++++--- src/tiles/FastRenderer.s | 17 ++++++----------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Core.s b/src/Core.s index 619f4ac..f102699 100644 --- a/src/Core.s +++ b/src/Core.s @@ -46,7 +46,6 @@ ToolStartUp rts MasterId ds 2 -;UserId ds 2 ; Fatal error handler invoked by the _Err macro PgmDeath tax diff --git a/src/Tiles.s b/src/Tiles.s index d423e49..8a24c61 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -111,7 +111,7 @@ InitTiles bra :out :fast ldal FastTileProcs - sta TileStore+TS_BASE_TILE_DISP,x + stal K_TS_BASE_TILE_DISP,x :out ; lda DirtyTileProcs ; Fill in with the first dispatch address @@ -213,7 +213,9 @@ _SetTile :fast ldal FastTileProcs,x - sta TileStore+TS_BASE_TILE_DISP,y + tyx + stal K_TS_BASE_TILE_DISP,x + :out jmp _PushDirtyTileY ; on the next call to _ApplyTiles @@ -412,4 +414,7 @@ b_12_3 stpbit 12;3;]4 b_13_3 stpbit 13;3;]4 b_14_3 stpbit 14;3;]4 b_15_3 endbit 15;3;]4 - <<< \ No newline at end of file + <<< + +; Store some tables in the K bank that will be used exclusively for jmp (abs,x) dispatch +K_TS_BASE_TILE_DISP ds TILE_STORE_SIZE \ No newline at end of file diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index 00b46af..4363625 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -13,25 +13,20 @@ NoSpriteFast lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line pha ; and put on the stack for later. Has TileStore bank in high byte. ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field - lda TileStore+TS_BASE_TILE_DISP,x ; go to the tile copy routine (just basics) - stal nsf_patch+1 +; lda TileStore+TS_BASE_TILE_DISP,x ; go to the tile copy routine (just basics) +; stal nsf_patch+1 lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) plb ; set the code field bank -nsf_patch jmp $0000 + jmp (K_TS_BASE_TILE_DISP,x) + +;nsf_patch jmp $0000 ; The TS_BASE_TILE_DISP routines will come from this table when ENGINE_MODE_TWO_LAYER and ; ENGINE_MODE_DYN_TILES are both off. FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast + ; dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast -; NOTE: Inlining the dispatch would eliminate a JSR,RTS,LDX, and JMP (abs,x) because the exit code -; could jump directly to the target address. Net savings of 20 cycles per tile. For a 16x16 -; sprite with a 3x3 block coverage this is 180 cycles per frame per block... This would also -; preserve a register -; -; For comparison, a fast one sprite copy takes 22 cycles per word, so this would save -; about 1/2 block of render time per tile. -; ; Need to determine if the sprite or tile data is on top, as that will decide whether the ; sprite or tile data is copied into the temporary buffer first. Also, if TWO_LAYER is set ; then the mask information must be copied as well....This is the last decision point. From e5938cb3c77f79e385e5373db7ed395078f8def0 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 8 Jun 2022 23:34:15 -0500 Subject: [PATCH 47/82] Change the entry points based on the engine mode --- src/blitter/TemplateUtils.s | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/blitter/TemplateUtils.s b/src/blitter/TemplateUtils.s index 88852bb..9f29eed 100644 --- a/src/blitter/TemplateUtils.s +++ b/src/blitter/TemplateUtils.s @@ -211,6 +211,7 @@ BuildBank :bankArray equ tmp0 :target equ tmp2 :nextBank equ tmp4 +:entryOffset equ tmp6 stx :bankArray sta :bankArray+2 @@ -246,7 +247,16 @@ BuildBank ; Change the patched value to one of DP_ENTRY, TWO_LYR_ENTRY or ONE_LYR_ENTRY based on the capabilities ; that the engine needs. - lda #$F000+{DP_ENTRY} ; Set the address from each line to the next + lda #DP_ENTRY + sta :entryOffset + lda EngineMode + bne :not_simple + lda #ONE_LYR_ENTRY + sta :entryOffset +:not_simple + + lda #$F000 ; Set the address from each line to the next + ora :entryOffset ldy #CODE_EXIT+1 ldx #15*2 jsr SetAbsAddrs @@ -255,7 +265,9 @@ BuildBank jsr SetDPAddrs ldy #$F000+CODE_EXIT ; Patch the last line with a JML to go to the next bank - lda #{$005C+{DP_ENTRY}*256} + lda :entryOffset + xba + ora #$005C sta [:target],y ldy #$F000+CODE_EXIT+2 lda :nextBank From 3f9ba7e81b4be3fc55e790c5411d2505e84588bc Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Thu, 9 Jun 2022 07:36:40 -0500 Subject: [PATCH 48/82] Local cleanup --- .gitignore | 5 ++++- _FileInformation.txt | 2 ++ package.json | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 _FileInformation.txt diff --git a/.gitignore b/.gitignore index 3c46de8..3fd0032 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ node_modules emu +.vscode *_Output.txt -src/GTETestApp \ No newline at end of file +src/GTETestApp +*.2mg +Tool160.SHK \ No newline at end of file diff --git a/_FileInformation.txt b/_FileInformation.txt new file mode 100644 index 0000000..74bd862 --- /dev/null +++ b/_FileInformation.txt @@ -0,0 +1,2 @@ +GTETool.SHK=Type(E0),AuxType(8002),VersionCreate(00),MinVersion(87),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) +Tool160.SHK=Type(E0),AuxType(8002),VersionCreate(00),MinVersion(B8),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) diff --git a/package.json b/package.json index d5c1d91..711ed14 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "crossrunner": "C:\\Programs\\Crossrunner\\Crossrunner.exe" }, "scripts": { - "archive": "%npm_package_config_cadius% EXTRACTFILE ", + "archive": "%npm_package_config_cadius% EXTRACTFILE ./emu/Target.2mg /GTEDev/Tool160.SHK .", "test": "npm run build && build-image.bat %npm_package_config_cadius% && %npm_package_config_gsport%", "build": "%npm_package_config_merlin32% -V %npm_package_config_macros% ./src/Master.s", "build:debug": "%npm_package_config_merlin32% -V %npm_package_config_macros% ./src/Debug.s", From 364af7bc57d095e4ec05d88b361d47031899063c Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Thu, 9 Jun 2022 07:41:03 -0500 Subject: [PATCH 49/82] Misc updates of local work --- .../assets/fatdog-rpg.tiled-session | 20 +- demos/fatdog-rpg/assets/world-map.tmx | 2 +- demos/fatdog/.gitignore | 1 + demos/pacman/_FileInformation.txt | 1 + demos/shell/.gitignore | 1 + demos/shell/assets/assets.tiled-session | 133 +++++++++-- demos/shell/assets/fatdog-1.png | Bin 260 -> 248 bytes demos/shell/assets/fatdog-2.png | Bin 255 -> 243 bytes demos/smb/.gitignore | 1 + demos/smb/_FileInformation.txt | 1 + demos/smb/package.json | 28 +++ demos/sprites/_FileInformation.txt | 2 +- demos/sprites/assets/tiled/world_1-1.tmx | 12 +- demos/tool/.gitignore | 1 + demos/tool/App.Main.s | 123 ++++++++++- demos/tool/GTEToolDemo | Bin 243 -> 0 bytes demos/tool/Zelda.TileSet.s | 206 +++++++++--------- 17 files changed, 391 insertions(+), 141 deletions(-) create mode 100644 demos/fatdog/.gitignore create mode 100644 demos/pacman/_FileInformation.txt create mode 100644 demos/shell/.gitignore create mode 100644 demos/smb/.gitignore create mode 100644 demos/smb/_FileInformation.txt create mode 100644 demos/smb/package.json create mode 100644 demos/tool/.gitignore delete mode 100644 demos/tool/GTEToolDemo diff --git a/demos/fatdog-rpg/assets/fatdog-rpg.tiled-session b/demos/fatdog-rpg/assets/fatdog-rpg.tiled-session index 67101b1..e84888f 100644 --- a/demos/fatdog-rpg/assets/fatdog-rpg.tiled-session +++ b/demos/fatdog-rpg/assets/fatdog-rpg.tiled-session @@ -3,10 +3,24 @@ "height": 4300, "width": 2 }, - "activeFile": "", + "activeFile": "world-map.tmx", "expandedProjectPaths": [ + "." ], + "file.lastUsedOpenFilter": "All Files (*)", "fileStates": { + "world-map.tmx": { + "scale": 4, + "selectedLayer": 1, + "viewCenter": { + "x": 143.375, + "y": 120.75 + } + }, + "world-tiles.tsx": { + "scaleInDock": 4, + "scaleInEditor": 1 + } }, "last.imagePath": "C:/checkout/iigs-game-engine/demos/fatdog-rpg/assets", "map.height": 128, @@ -15,9 +29,13 @@ "map.tileWidth": 8, "map.width": 128, "openFiles": [ + "world-map.tmx", + "world-tiles.tsx" ], "project": "fatdog-rpg.tiled-project", "recentFiles": [ + "world-tiles.tsx", + "world-map.tmx" ], "tileset.lastUsedFormat": "tsx", "tileset.tileSize": { diff --git a/demos/fatdog-rpg/assets/world-map.tmx b/demos/fatdog-rpg/assets/world-map.tmx index 351e306..d6b7420 100644 --- a/demos/fatdog-rpg/assets/world-map.tmx +++ b/demos/fatdog-rpg/assets/world-map.tmx @@ -1,7 +1,7 @@ - + 1,1,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, diff --git a/demos/fatdog/.gitignore b/demos/fatdog/.gitignore new file mode 100644 index 0000000..bf24d3e --- /dev/null +++ b/demos/fatdog/.gitignore @@ -0,0 +1 @@ +GTEShooter \ No newline at end of file diff --git a/demos/pacman/_FileInformation.txt b/demos/pacman/_FileInformation.txt new file mode 100644 index 0000000..c37c6cc --- /dev/null +++ b/demos/pacman/_FileInformation.txt @@ -0,0 +1 @@ +GTEPacMan=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) \ No newline at end of file diff --git a/demos/shell/.gitignore b/demos/shell/.gitignore new file mode 100644 index 0000000..56bf228 --- /dev/null +++ b/demos/shell/.gitignore @@ -0,0 +1 @@ +GTETestApp \ No newline at end of file diff --git a/demos/shell/assets/assets.tiled-session b/demos/shell/assets/assets.tiled-session index d7755b1..869dee2 100644 --- a/demos/shell/assets/assets.tiled-session +++ b/demos/shell/assets/assets.tiled-session @@ -3,7 +3,7 @@ "height": 4300, "width": 2 }, - "activeFile": "tiled/world_1-1.tmx", + "activeFile": "C:/checkout/iigs-game-engine/demos/zelda/assets/overworld.tmx", "expandedProjectPaths": [ "tiled" ], @@ -26,46 +26,145 @@ "scaleInDock": 2, "scaleInEditor": 4 }, + "C:/checkout/iigs-game-engine/demos/sprites/assets/tiled/Overworld.tsx": { + "scaleInDock": 2, + "scaleInEditor": 4 + }, + "C:/checkout/iigs-game-engine/demos/sprites/assets/tiled/world_1-1.tmx": { + "scale": 2, + "selectedLayer": 0, + "viewCenter": { + "x": 412, + "y": 309 + } + }, + "C:/checkout/iigs-game-engine/demos/zelda/assets/Zelda.tsx": { + "scaleInDock": 4, + "scaleInEditor": 1 + }, + "C:/checkout/iigs-game-engine/demos/zelda/assets/overworld.tmx": { + "scale": 2, + "selectedLayer": 0, + "viewCenter": { + "x": 222.75, + "y": 192.5 + } + }, + "C:/checkout/tiled/examples/desert.tmx": { + "scale": 0.5462499999999999, + "selectedLayer": 0, + "viewCenter": { + "x": 716.704805491991, + "y": 793.5926773455379 + } + }, + "C:/checkout/tiled/examples/desert.tsx": { + "scaleInDock": 1 + }, + "C:/checkout/tiled/examples/hexagonal-mini.tmx": { + "scale": 3.0067010309278346, + "selectedLayer": 0, + "viewCenter": { + "x": 159.14452254414542, + "y": 116.90553745928342 + } + }, + "C:/checkout/tiled/examples/hexagonal-mini.tmx#hex mini": { + "scaleInDock": 1 + }, + "C:/checkout/tiled/examples/isometric_grass_and_water.tmx": { + "scale": 0.4643125, + "selectedLayer": 0, + "viewCenter": { + "x": 1039.1708170682462, + "y": 573.9668865257775 + } + }, + "C:/checkout/tiled/examples/isometric_grass_and_water.tmx#isometric_grass_and_water": { + "scaleInDock": 1 + }, + "C:/checkout/tiled/examples/perspective_walls.tmx": { + "scale": 0.8369268292682926, + "selectedLayer": 0, + "viewCenter": { + "x": 497.65401876784983, + "y": 479.7313050067028 + } + }, + "C:/checkout/tiled/examples/perspective_walls.tsx": { + "scaleInDock": 1 + }, + "C:/checkout/tiled/examples/rpg/beach_tileset.tsx": { + "scaleInDock": 1 + }, + "C:/checkout/tiled/examples/rpg/island.tmx": { + "scale": 1.0267780172413792, + "selectedLayer": 0, + "viewCenter": { + "x": 464.0730440258173, + "y": 376.42021304507534 + } + }, + "C:/checkout/tiled/examples/sewers.tmx": { + "scale": 0.7148749999999999, + "selectedLayer": 0, + "viewCenter": { + "x": 600.8043364224515, + "y": 600.8043364224516 + } + }, + "C:/checkout/tiled/examples/sewers.tmx#sewer_tileset": { + "scaleInDock": 1 + }, "tiled/Overworld.tsx": { "scaleInDock": 2, - "scaleInEditor": 1 + "scaleInEditor": 8 }, "tiled/world_1-1.tmx": { "scale": 2, "selectedLayer": 1, "viewCenter": { - "x": 210.5, - "y": 107.75 + "x": 212.75, + "y": 211.25 } } }, - "last.exportedFilePath": "C:/checkout/iigs-game-engine/assets/tiled", - "last.imagePath": "C:/checkout/iigs-game-engine/assets/tilesets", - "map.height": 30, + "frame.defaultDuration": 256, + "last.exportedFilePath": "C:/checkout/iigs-game-engine/demos/zelda/assets", + "last.imagePath": "C:/checkout/iigs-game-engine/demos/zelda/assets", + "map.height": 44, "map.lastUsedExportFilter": "JSON map files (*.json)", "map.lastUsedFormat": "tmx", "map.renderOrder": null, "map.tileHeight": 8, "map.tileWidth": 8, - "map.width": 256, + "map.width": 64, "openFiles": [ - "tiled/world_1-1.tmx", - "tiled/Overworld.tsx" + "C:/checkout/iigs-game-engine/demos/zelda/assets/overworld.tmx", + "C:/checkout/iigs-game-engine/demos/zelda/assets/Zelda.tsx" ], "project": "assets.tiled-project", - "property.type": "int", + "property.type": "bool", "recentFiles": [ - "tiled/Overworld.tsx", + "C:/checkout/iigs-game-engine/demos/zelda/assets/Zelda.tsx", + "C:/checkout/iigs-game-engine/demos/zelda/assets/overworld.tmx", + "C:/checkout/iigs-game-engine/demos/sprites/assets/tiled/Overworld.tsx", + "C:/checkout/iigs-game-engine/demos/sprites/assets/tiled/world_1-1.tmx", "tiled/world_1-1.tmx", - "C:/Users/lscharen/SNES - Final Fantasy 6 - South Figaro Exterior.tsx", - "C:/Users/lscharen/Overworld.tmx", - "C:/Users/lscharen/EUm-lfFWkAEb5fJ.tsx" + "tiled/Overworld.tsx", + "C:/checkout/tiled/examples/desert.tmx", + "C:/checkout/tiled/examples/hexagonal-mini.tmx", + "C:/checkout/tiled/examples/isometric_grass_and_water.tmx", + "C:/checkout/tiled/examples/perspective_walls.tmx", + "C:/checkout/tiled/examples/sewers.tmx", + "C:/checkout/tiled/examples/rpg/island.tmx" ], + "resizeMap.removeObjects": true, "tileset.lastUsedFormat": "tsx", "tileset.tileSize": { "height": 8, "width": 8 }, - "tileset.transparentColor": "#6b8cff", - "tileset.useTransparentColor": false + "tileset.transparentColor": "#ff00ff", + "tileset.useTransparentColor": true } diff --git a/demos/shell/assets/fatdog-1.png b/demos/shell/assets/fatdog-1.png index 79398d71fd3dfb31cafece744f8396f134953a3d..598e30d056652ceb4f7b653665020d454d4188b3 100644 GIT binary patch delta 220 zcmZo+`oXB!8Q|y6%O%Cdz`(%k>ERLtq!oZzf`u7KX4XDppQxx-&+s3}`2YkA3_Y9o zUIB7A3p^r=85sBufiR<}hF1enu+-DVF+}5h>4Zk!0}320iOm1a8w7X#FL2G&HW3sS z)+?%;P|SPr+GERLtq@{sajDs0SvP}`+IZ;uq{{Md<=K~NhFih*v zs{?X43p^r=85sBugD~Uq{1quc!A4IP#}JL++6kO|4F()6-2eWcKeXxTgd@I7&iX8U z@zZV7nhq)VNW)c4P8XPXyaNi)v@c+Ei(Ep--PS!0?_KhGx}p8LD@DFXEzVh7E%;abZo|GwX*~U3 b=X_>5q|CU?Y+JA!&@~L6u6{1-oD!MO*y zYQ7aOZ9Sg4^Ip8Rn$w+C<%|`_C83VXj2-L^;;(tG@W^a_cW_oM(-Bt@2k+#FjGY%F z_$zkk9*wDF)ZIPhRUGe5C-z^QK2|sWJF_3&(-9m3%0>gTe~DWM4fU_f1f diff --git a/demos/smb/.gitignore b/demos/smb/.gitignore new file mode 100644 index 0000000..56bf228 --- /dev/null +++ b/demos/smb/.gitignore @@ -0,0 +1 @@ +GTETestApp \ No newline at end of file diff --git a/demos/smb/_FileInformation.txt b/demos/smb/_FileInformation.txt new file mode 100644 index 0000000..4fffcad --- /dev/null +++ b/demos/smb/_FileInformation.txt @@ -0,0 +1 @@ +GTETestApp=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) diff --git a/demos/smb/package.json b/demos/smb/package.json new file mode 100644 index 0000000..6a20491 --- /dev/null +++ b/demos/smb/package.json @@ -0,0 +1,28 @@ +{ + "name": "super-mario-bros-nes-demo", + "version": "1.0.0", + "description": "Wrapping the SMB 6502 ROM in GTE", + "main": "index.js", + "config": { + "merlin32": "C:\\Programs\\IIgsXDev\\bin\\Merlin32-1.1.10.exe", + "cadius": "C:\\Programs\\IIgsXDev\\bin\\Cadius.exe", + "gsport": "C:\\Programs\\gsport\\gsport_0.31\\GSPort.exe", + "macros": "C:\\Programs\\BrutalDeluxe\\Merlin32\\Library", + "crossrunner": "C:\\Programs\\Crossrunner\\Crossrunner.exe" + }, + "scripts": { + "build": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.s" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/lscharen/iigs-game-engine.git" + }, + "author": "Lucas Scharenbroich", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/lscharen/iigs-game-engine/issues" + }, + "homepage": "https://github.com/lscharen/iigs-game-engine#readme", + "devDependencies": { + } +} diff --git a/demos/sprites/_FileInformation.txt b/demos/sprites/_FileInformation.txt index 3d84606..38df996 100644 --- a/demos/sprites/_FileInformation.txt +++ b/demos/sprites/_FileInformation.txt @@ -1 +1 @@ -GTETestSprites=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) +GTEZelda=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) diff --git a/demos/sprites/assets/tiled/world_1-1.tmx b/demos/sprites/assets/tiled/world_1-1.tmx index c9ef6bc..65bbbfd 100644 --- a/demos/sprites/assets/tiled/world_1-1.tmx +++ b/demos/sprites/assets/tiled/world_1-1.tmx @@ -14,24 +14,24 @@ 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,185,186,186,187,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,153,154,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,155,156,157,158,33,33,33,33,33,33,33,33,33,33,33,33,153,154,153,154,153,154,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,153,154,153,154,153,154,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,59,60,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,185,186,186,187,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,185,186,186,186,186,186,186,187,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,185,186,186,186,186,186,186,187,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,55,56,10,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, -33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,155,156,157,158,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,155,156,157,156,157,156,157,158,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,155,156,157,156,157,156,157,158,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,55,10,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, -33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,137,138,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,155,156,157,158,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,155,156,157,156,157,156,157,158,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,155,156,157,156,157,156,157,158,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,55,10,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,169,170,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,159,160,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,33,33,33,33,33,33,188,188,188,188,188,188,159,160,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,159,160,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,188,188,188,188,188,188,33,33,33,33,33,33,33,33,188,188,159,160,159,160,188,188,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,191,192,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,33,33,33,33,33,33,26,26,26,26,26,26,191,192,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,191,192,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,26,26,26,26,26,26,33,33,33,33,33,33,33,33,26,26,191,192,191,192,26,26,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,5,6,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,7,8,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,5,6,5,6,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,7,8,7,8,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,31,32,31,32,31,32,33,33,33,33, -33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,5,6,5,6,5,6,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,26,26,26,26,26,26,33,33,33,33, -33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,7,8,7,8,7,8,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,26,26,26,26,26,26,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,137,138,137,138,137,138,137,138,137,138,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,5,6,5,6,5,6,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,26,26,26,26,26,26,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,169,170,169,170,169,170,169,170,169,170,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,7,8,7,8,7,8,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,26,26,26,26,26,26,33,33,33,33, 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,159,160,33,33,33,33,33,33,188,188,159,160,188,188,159,160,188,188,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,11,12,13,14,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,11,12,13,14,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,188,188,159,160,188,188,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,188,188,33,33,33,33,33,33,33,33,33,33,188,188,188,188,33,33,33,33,33,33,33,33,159,160,33,33,33,33,159,160,33,33,33,33,159,160,33,33,33,33,33,33,33,33,33,33,188,188,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,188,188,188,188,33,33,33,33,33,33,33,33,33,33,33,33,5,6,33,33,33,33,5,6,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,33,33,33,33,5,6,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,188,188,188,188,159,160,188,188,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,5,6,5,6,5,6,5,6,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,26,64,26,26,64,26,33,33,33,33, 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,191,192,33,33,33,33,33,33,26,26,191,192,26,26,191,192,26,26,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,15,16,17,18,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,15,16,17,18,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,26,26,191,192,26,26,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,26,26,33,33,33,33,33,33,33,33,33,33,26,26,26,26,33,33,33,33,33,33,33,33,191,192,33,33,33,33,191,192,33,33,33,33,191,192,33,33,33,33,33,33,33,33,33,33,26,26,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,26,26,26,26,33,33,33,33,33,33,33,33,33,33,33,33,7,8,33,33,33,33,7,8,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,33,33,33,33,7,8,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,26,26,26,26,191,192,26,26,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,7,8,7,8,7,8,7,8,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,33,33,26,64,26,26,64,26,33,33,33,33, 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,11,12,13,14,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,33,33,33,33,5,6,5,6,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,5,6,33,33,33,33,5,6,5,6,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,5,6,5,6,5,6,5,6,5,6,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,9,10,33,33,33,33,33,33,31,32,24,25,24,25,24,25,31,32,33,33, 33,33,33,33,49,50,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,15,16,17,18,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,33,49,50,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,49,50,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,33,33,33,33,7,8,7,8,33,33,33,33,33,33,33,33,49,50,33,33,33,33,33,33,7,8,7,8,7,8,33,33,33,33,7,8,7,8,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,7,8,7,8,7,8,7,8,7,8,33,33,33,33,33,33,33,33,49,50,33,33,33,33,33,33,9,10,33,33,33,33,33,33,26,26,26,26,57,58,26,26,26,26,33,33, 33,33,33,48,21,54,51,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,11,12,13,14,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,48,21,54,51,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,48,21,54,51,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,5,6,33,33,33,33,5,6,5,6,5,6,33,33,33,33,33,48,21,54,51,33,33,33,5,6,5,6,5,6,5,6,33,33,33,33,5,6,5,6,5,6,33,33,33,33,33,33,33,33,33,33,11,12,13,14,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,11,12,13,14,33,33,5,6,5,6,5,6,5,6,5,6,5,6,5,6,5,6,33,33,33,33,33,33,33,48,21,54,51,33,33,33,33,33,9,10,33,33,33,33,33,33,26,26,26,26,64,64,26,26,26,26,33,33, 33,33,48,21,21,21,21,51,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,49,2147483697,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,15,16,17,18,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,48,21,21,21,21,51,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,33,33,33,33,33,33,33,33,33,49,50,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,48,21,21,21,21,51,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,49,50,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,7,8,33,33,33,33,7,8,7,8,7,8,33,33,33,33,48,21,21,21,21,51,33,33,7,8,7,8,7,8,7,8,33,33,33,33,7,8,7,8,7,8,33,33,33,33,33,33,49,50,33,33,15,16,17,18,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,15,16,17,18,33,33,7,8,7,8,7,8,7,8,7,8,7,8,7,8,7,8,33,33,33,33,33,33,48,21,21,21,21,51,33,33,33,33,9,10,33,33,33,33,33,33,26,26,26,26,64,64,26,26,26,26,33,33, -33,48,21,54,21,21,54,21,51,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,34,35,34,35,34,35,33,33,33,48,21,54,2147483696,33,33,87,86,87,86,87,86,87,86,33,34,35,33,33,87,86,87,86,19,20,21,22,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,33,34,35,34,35,33,33,33,33,19,20,21,22,33,48,21,54,21,21,54,21,51,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,34,35,34,35,34,35,33,33,33,48,21,54,51,33,33,33,33,33,33,33,33,33,33,33,34,35,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,34,35,34,35,33,33,33,33,33,33,33,33,33,48,21,54,21,21,54,21,51,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,34,35,34,35,34,35,33,33,33,48,21,54,51,33,33,33,33,33,33,33,33,33,33,33,34,35,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,5,6,5,6,34,35,34,35,5,6,5,6,5,6,5,6,33,48,21,54,21,21,54,21,5,6,5,6,5,6,5,6,5,6,33,33,33,33,5,6,5,6,5,6,5,6,33,33,33,48,21,54,51,33,19,20,21,22,33,33,33,33,0,33,34,35,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,5,6,5,6,5,6,5,6,5,6,5,6,5,6,5,6,5,6,33,33,33,33,33,48,21,54,21,21,54,21,51,33,33,33,5,6,33,33,33,33,33,33,26,26,26,26,64,64,26,26,26,26,33,33, -48,21,21,21,21,21,21,21,21,51,33,33,33,33,33,33,33,33,33,33,33,33,33,36,37,37,37,37,37,37,38,33,48,21,21,21,21,2147483696,33,119,118,119,118,119,118,119,118,36,37,37,38,33,119,118,119,118,19,20,21,22,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,36,37,37,37,37,38,33,33,33,19,20,21,22,48,21,21,21,21,21,21,21,21,51,33,33,33,33,33,33,33,33,19,20,21,22,33,36,37,37,37,37,37,37,38,33,48,21,21,21,21,51,33,33,33,33,33,33,33,33,33,36,37,37,38,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,36,37,37,37,37,38,33,33,33,33,33,33,33,48,21,21,21,21,21,21,21,21,51,33,33,33,33,33,33,33,33,33,33,33,33,33,36,37,37,37,37,37,37,38,33,48,21,21,21,21,51,33,33,33,33,33,33,33,33,33,36,37,37,38,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,7,8,7,8,37,37,37,37,7,8,7,8,7,8,7,8,48,21,21,21,21,21,21,21,7,8,7,8,7,8,7,8,7,8,33,33,33,33,7,8,7,8,7,8,7,8,38,33,48,21,21,21,21,51,19,20,21,22,33,33,33,33,0,36,37,37,38,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,7,8,7,8,7,8,7,8,7,8,7,8,7,8,7,8,7,8,33,33,33,33,48,21,21,21,21,21,21,21,21,51,33,33,7,8,33,33,33,33,33,33,26,26,26,26,64,64,26,26,26,26,38,33, +33,48,21,54,21,21,54,21,51,33,137,138,33,33,33,33,33,33,33,33,33,33,33,33,34,35,34,35,34,35,33,33,33,48,21,54,2147483696,33,33,87,86,87,86,87,86,87,86,33,34,35,33,33,87,86,87,86,19,20,21,22,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,33,34,35,34,35,33,33,33,33,19,20,21,22,33,48,21,54,21,21,54,21,51,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,34,35,34,35,34,35,33,33,33,48,21,54,51,33,33,33,33,33,33,33,33,33,33,33,34,35,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,34,35,34,35,33,33,33,33,33,33,33,33,33,48,21,54,21,21,54,21,51,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,34,35,34,35,34,35,33,33,33,48,21,54,51,33,33,33,33,33,33,33,33,33,33,33,34,35,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,5,6,5,6,5,6,5,6,34,35,34,35,5,6,5,6,5,6,5,6,33,48,21,54,21,21,54,21,5,6,5,6,5,6,5,6,5,6,33,33,33,33,5,6,5,6,5,6,5,6,33,33,33,48,21,54,51,33,19,20,21,22,33,33,33,33,0,33,34,35,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,5,6,5,6,5,6,5,6,5,6,5,6,5,6,5,6,5,6,33,33,33,33,33,48,21,54,21,21,54,21,51,33,33,33,5,6,33,33,33,33,33,33,26,26,26,26,64,64,26,26,26,26,33,33, +48,21,21,21,21,21,21,21,21,51,169,170,33,33,33,33,33,33,33,33,33,33,33,36,37,37,37,37,37,37,38,33,48,21,21,21,21,2147483696,33,119,118,119,118,119,118,119,118,36,37,37,38,33,119,118,119,118,19,20,21,22,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,33,33,33,36,37,37,37,37,38,33,33,33,19,20,21,22,48,21,21,21,21,21,21,21,21,51,33,33,33,33,33,33,33,33,19,20,21,22,33,36,37,37,37,37,37,37,38,33,48,21,21,21,21,51,33,33,33,33,33,33,33,33,33,36,37,37,38,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,36,37,37,37,37,38,33,33,33,33,33,33,33,48,21,21,21,21,21,21,21,21,51,33,33,33,33,33,33,33,33,33,33,33,33,33,36,37,37,37,37,37,37,38,33,48,21,21,21,21,51,33,33,33,33,33,33,33,33,33,36,37,37,38,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,7,8,7,8,7,8,7,8,37,37,37,37,7,8,7,8,7,8,7,8,48,21,21,21,21,21,21,21,7,8,7,8,7,8,7,8,7,8,33,33,33,33,7,8,7,8,7,8,7,8,38,33,48,21,21,21,21,51,19,20,21,22,33,33,33,33,0,36,37,37,38,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,20,21,22,7,8,7,8,7,8,7,8,7,8,7,8,7,8,7,8,7,8,33,33,33,33,48,21,21,21,21,21,21,21,21,51,33,33,7,8,33,33,33,33,33,33,26,26,26,26,64,64,26,26,26,26,38,33, 1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,33,33,33,33,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,33,33,33,33,33,33,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,33,33,33,33,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, 3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,33,33,33,33,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,33,33,33,33,33,33,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,33,33,33,33,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4, 1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,33,33,33,33,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,33,33,33,33,33,33,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,33,33,33,33,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, diff --git a/demos/tool/.gitignore b/demos/tool/.gitignore new file mode 100644 index 0000000..ef9cf26 --- /dev/null +++ b/demos/tool/.gitignore @@ -0,0 +1 @@ +GTEToolDemo \ No newline at end of file diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 91f4cf7..a8f2fb5 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -15,18 +15,21 @@ TSZelda EXT ; tileset buffer -MAX_SPRITES equ 1 +MAX_SPRITES equ 16 ScreenX equ 0 ScreenY equ 2 Tmp0 equ 4 Tmp1 equ 6 +KeyState equ 8 +Selected equ 10 +Flips equ 12 ; Typical init phk plb - sta MyUserId ; GS/OS passes the memory manager user ID for the aoplication into the program + 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 @@ -35,6 +38,8 @@ Tmp1 equ 6 pea #256 pea #160 +; pea #176 +; pea #144 _GTESetScreenMode ; Load a tileset @@ -110,6 +115,8 @@ HERO_SPRITE equ SPRITE_16X16+1 phy pea #66 + cpy #24 + beq :skip_last iny phx phy @@ -122,6 +129,7 @@ HERO_SPRITE equ SPRITE_16X16+1 _GTESetTile _GTESetTile +:skip_last _GTESetTile _GTESetTile @@ -146,10 +154,14 @@ HERO_SPRITE equ SPRITE_16X16+1 ; Set the screen coordinates - lda #8 + lda #128 sta ScreenX + lda #128 sta ScreenY + stz Selected + stz Flips + ; Very simple actions :evt_loop pha ; space for result, with pattern @@ -157,27 +169,95 @@ HERO_SPRITE equ SPRITE_16X16+1 pla and #$00FF cmp #'q' - bne :3 + bne :2 brl :exit +:2 +; cmp KeyState +; beq :evt_loop +; sta KeyState +; cmp #0 +; beq :evt_loop + +; cmp #' ' +; bne :evt_loop ; only advance one frame at a time +; brl :next + :3 + cmp #'1' + bcc :3a + cmp #'9' + bcs :3a + sec + sbc #'1' + asl + sta Selected + brl :next + +:3a + cmp #'r' + bne :3b + lda Flips + clc + adc #SPRITE_HFLIP + and #SPRITE_VFLIP+SPRITE_HFLIP + sta Flips + + pei Selected ; update the sprite in this slot + pei Flips ; with these flags (h/v flip) + pea VBUFF_SPRITE_START ; and use this stamp + _GTEUpdateSprite + +:3b + cmp #'x' + bne :3d + ldx Selected + lda PlayerX,x + clc + adc PlayerU,x + sta PlayerX,x + + lda PlayerY,x + clc + adc PlayerV,x + sta PlayerY,x + brl :next +:3d + cmp #'z' + bne :3e + ldx Selected + lda PlayerX,x + sec + sbc PlayerU,x + sta PlayerX,x + + lda PlayerY,x + sec + sbc PlayerV,x + sta PlayerY,x + brl :next +:3e cmp #'s' bne :4 - inc PlayerY + ldx Selected + inc PlayerY,x brl :next :4 cmp #'w' bne :5 - dec PlayerY + ldx Selected + dec PlayerY,x brl :next :5 cmp #'d' bne :6 - inc PlayerX + ldx Selected + inc PlayerX,x brl :next :6 cmp #'a' bne :7 - dec PlayerX + ldx Selected + dec PlayerX,x brl :next :7 cmp #$15 ; left = $08, right = $15, up = $0B, down = $0A @@ -200,12 +280,14 @@ HERO_SPRITE equ SPRITE_16X16+1 dec ScreenY :next - inc ScreenX +; inc ScreenX pei ScreenX pei ScreenY _GTESetBG0Origin +; brl no_animate + stz Tmp0 stz Tmp1 @@ -216,6 +298,8 @@ loopX adc PlayerU,x sta PlayerX,x bpl is_posx + cmp #-15 + bcs do_y lda PlayerU,x eor #$FFFF inc @@ -234,6 +318,8 @@ do_y adc PlayerV,x sta PlayerY,x bpl is_posy + cmp #-15 + bcs do_z lda PlayerV,x eor #$FFFF inc @@ -246,6 +332,19 @@ is_posy cmp #160 inc sta PlayerV,x do_z + inc Tmp1 + ldx Tmp0 + inx + inx + stx Tmp0 + cpx #MAX_SPRITES*2 + bcc loopX + +no_animate + stz Tmp0 + stz Tmp1 + ldx Tmp0 +loopY pei Tmp1 lda PlayerX,x pha @@ -259,7 +358,7 @@ do_z inx stx Tmp0 cpx #MAX_SPRITES*2 - bcc loopX + bcc loopY _GTERender inc FrameCount @@ -294,8 +393,8 @@ qtRec adrl $0000 da $00 ; Array of sprite positions and velocities -PlayerX dw 0,14,29,34,45,67,81,83,92,101,39,22,7,74,111,9 -PlayerY dw 7,24,13,56,35,72,23,8,93,123,134,87,143,14,46,65 +PlayerX dw 8,14,29,34,45,67,81,83,92,101,39,22,7,74,111,9 +PlayerY dw 72,24,13,56,35,72,23,8,93,123,134,87,143,14,46,65 PlayerU dw 1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4 PlayerV dw 1,1,1,1,2,2,2,4,3,3,3,3,4,4,4,4 diff --git a/demos/tool/GTEToolDemo b/demos/tool/GTEToolDemo deleted file mode 100644 index 466b7a7db08bbb37269ef4430eb4e29265038fc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 243 zcmey&009#~6c-B<0|O%ig8) Date: Thu, 9 Jun 2022 07:41:58 -0500 Subject: [PATCH 50/82] Remove unused memory and start working on priority renderer --- src/static/TileStoreDefs.s | 2 +- src/tiles/FastRenderer.s | 52 +++++++++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index 1e5ada2..c6a41c2 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -17,7 +17,7 @@ TS_SCREEN_ADDR equ {TILE_STORE_SIZE*8} ; cached value of on-screen ; TODO: Move these arrays into the K bank to support direct dispatch via jmp (abs,x) TS_BASE_TILE_COPY equ {TILE_STORE_SIZE*9} ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering -TS_BASE_TILE_DISP equ {TILE_STORE_SIZE*10} ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function +; TS_BASE_TILE_DISP equ {TILE_STORE_SIZE*10} ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function TS_DIRTY_TILE_DISP equ {TILE_STORE_SIZE*11} ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function TILE_STORE_NUM equ 12 ; Need this many parallel arrays diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index 4363625..14dcd90 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -13,13 +13,9 @@ NoSpriteFast lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line pha ; and put on the stack for later. Has TileStore bank in high byte. ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field -; lda TileStore+TS_BASE_TILE_DISP,x ; go to the tile copy routine (just basics) -; stal nsf_patch+1 lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated) plb ; set the code field bank - jmp (K_TS_BASE_TILE_DISP,x) - -;nsf_patch jmp $0000 + jmp (K_TS_BASE_TILE_DISP,x) ; go to the tile copy routine (just basics) ; The TS_BASE_TILE_DISP routines will come from this table when ENGINE_MODE_TWO_LAYER and ; ENGINE_MODE_DYN_TILES are both off. @@ -32,8 +28,51 @@ FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFa ; then the mask information must be copied as well....This is the last decision point. SpriteDispatch +; jmp (K_TS_SPRITE_TILE_DISP,x) + txy - SpriteBitsToVBuffAddrs OneSpriteFast;TwoSpritesFast;ThreeSpritesFast;FourSpritesFast + SpriteBitsToVBuffAddrs OneSpriteFastUnder;TwoSpritesFast;ThreeSpritesFast;FourSpritesFast + +; This handles sprite with the tile above +OneSpriteFastUnder + tax + jsr _CopySpriteDataToDP2 ; preserves Y + + ldx TileStore+TS_TILE_ADDR,y + lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line + pha ; and put on the stack for later. Has TileStore bank in high byte. + lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field + tay + plb + +]line equ 0 + lup 8 + lda tmp_tile_data+{]line*4} + andl tiledata+{]line*4}+32,x + oral tiledata+{]line*4},x + sta: $0004+{]line*$1000},y + + lda tmp_tile_data+{]line*4}+2 + andl tiledata+{]line*4}+32+2,x + oral tiledata+{]line*4}+2,x + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + plb + rts + + +_CopySpriteDataToDP2 +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta tmp_tile_data+{]line*4} + + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + rts ; Where there are sprites involved, the first step is to call a routine to copy the ; tile data into a temporary buffer. Then the sprite data is merged and placed into @@ -52,7 +91,6 @@ OneSpriteFast tay plb -_TBApplySpriteData2 ]line equ 0 lup 8 lda tmp_tile_data+{]line*4} From 7af4a216a0b8e2ac26bbd8789ec33e89088bc809 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sun, 12 Jun 2022 05:28:12 -0500 Subject: [PATCH 51/82] Add modular dispatch for copy routines; add initial support for tile priority bit --- src/Defs.s | 3 ++ src/Sprite.s | 4 +++ src/Tiles.s | 37 ++++++++++++++++++++---- src/blitter/TileProcs.s | 19 +++++++++++-- src/blitter/Tiles00000.s | 13 +++++++++ src/tiles/FastRenderer.s | 61 ++++++++++++++++++---------------------- 6 files changed, 96 insertions(+), 41 deletions(-) diff --git a/src/Defs.s b/src/Defs.s index cf0a499..06062f7 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -150,6 +150,9 @@ _TILE_ID equ 158 ; Copy of the tile descriptor DP2_DIRTY_TILE_COUNT equ 160 ; Local copy of dirty tile count to avoid banking DP2_DIRTY_TILE_CALLBACK equ 162 +; Some pre-defined bank values +DP2_TILEDATA_AND_TILESTORE_BANKS equ 164 + SPRITE_VBUFF_PTR equ 224 ; 32 bytes of adjusted pointers to VBuffArray addresses ; End direct page values diff --git a/src/Sprite.s b/src/Sprite.s index 7a2b76c..f8eb9d6 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -601,6 +601,10 @@ _CacheSpriteBanks ora #^TileStore sta TileStoreBankAndTileDataBank + xba + ldx #$100 + sta DP2_TILEDATA_AND_TILESTORE_BANKS,x ; put a reversed copy in the second direct page + lda #>TileStore and #$FF00 ora #^TileStore diff --git a/src/Tiles.s b/src/Tiles.s index 8a24c61..802e236 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -112,6 +112,10 @@ InitTiles :fast ldal FastTileProcs stal K_TS_BASE_TILE_DISP,x + ldal FastTileCopy + stal K_TS_COPY_TILE_DATA,x + ldal FastSpriteSub + stal K_TS_SPRITE_TILE_DISP,x :out ; lda DirtyTileProcs ; Fill in with the first dispatch address @@ -189,6 +193,10 @@ _SetTile ; functionality. Sometimes it is simple, but in cases of the sprites overlapping Dynamic Tiles and other cases ; it can be more involved. + lda EngineMode + bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER + beq :fast + lda TileStore+TS_TILE_ID,y and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value xba @@ -199,10 +207,6 @@ _SetTile ; ldal CopyTileProcs,x ; sta TileStore+TS_DIRTY_TILE_COPY,y - lda EngineMode - bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER - beq :fast - lda TileStore+TS_TILE_ID,y ; Get the non-sprite dispatch address and #TILE_CTRL_MASK xba @@ -212,10 +216,30 @@ _SetTile bra :out :fast + tyx + lda TileStore+TS_TILE_ID,y ; First, check if the sprites are over or under + bit #TILE_PRIORITY_BIT + beq :fast_over + ldal FastSpriteSub+2 + bra :fast_under +:fast_over ldal FastSpriteSub +:fast_under stal K_TS_SPRITE_TILE_DISP,x + + lda TileStore+TS_TILE_ID,y ; Now, set the draw and copy routines based on H/V bits + and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value + xba + tax + phx + ldal FastTileProcs,x tyx stal K_TS_BASE_TILE_DISP,x + plx + ldal FastTileCopy,x + tyx + stal K_TS_COPY_TILE_DATA,x + :out jmp _PushDirtyTileY ; on the next call to _ApplyTiles @@ -417,4 +441,7 @@ b_15_3 endbit 15;3;]4 <<< ; Store some tables in the K bank that will be used exclusively for jmp (abs,x) dispatch -K_TS_BASE_TILE_DISP ds TILE_STORE_SIZE \ No newline at end of file + +K_TS_BASE_TILE_DISP ds TILE_STORE_SIZE ; draw the tile without a sprite +K_TS_COPY_TILE_DATA ds TILE_STORE_SIZE ; copy the tile into temp storage (used when tile below sprite) +K_TS_SPRITE_TILE_DISP ds TILE_STORE_SIZE ; select the sprite routine for this tile \ No newline at end of file diff --git a/src/blitter/TileProcs.s b/src/blitter/TileProcs.s index f40b634..46cc548 100644 --- a/src/blitter/TileProcs.s +++ b/src/blitter/TileProcs.s @@ -30,15 +30,30 @@ _TBCopyTileDataAndMaskToCBuffV _CopyTileDataToDP2 ]line equ 0 lup 8 - ldal tiledata+{]line*4},x + lda tiledata+{]line*4},y sta tmp_tile_data+{]line*4} - ldal tiledata+{]line*4}+2,x + lda tiledata+{]line*4}+2,y sta tmp_tile_data+{]line*4}+2 ]line equ ]line+1 --^ rts +_CopyTileDataToDP2V +]src equ 7 +]dest equ 0 + lup 8 + lda tiledata+{]src*4},y + sta tmp_tile_data+{]dest*4} + + lda tiledata+{]src*4}+2,y + sta tmp_tile_data+{]dest*4}+2 +]src equ ]src-1 +]dest equ ]dest+1 + --^ + rts + + _TBCopyTileDataToCBuff ]line equ 0 lup 8 diff --git a/src/blitter/Tiles00000.s b/src/blitter/Tiles00000.s index 7fa74bd..0d3f6e8 100644 --- a/src/blitter/Tiles00000.s +++ b/src/blitter/Tiles00000.s @@ -67,6 +67,19 @@ _TBCopyData _TBCopyDataVFast tax +]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 + --^ + plb + rts + _TBCopyDataV ]src equ 7 ]dest equ 0 diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index 14dcd90..e6f2ce0 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -9,7 +9,6 @@ _RenderTileFast lda TileStore+TS_SPRITE_FLAG,x ; any sprites on this line? bne SpriteDispatch -NoSpriteFast lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line pha ; and put on the stack for later. Has TileStore bank in high byte. ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field @@ -19,19 +18,25 @@ NoSpriteFast ; The TS_BASE_TILE_DISP routines will come from this table when ENGINE_MODE_TWO_LAYER and ; ENGINE_MODE_DYN_TILES are both off. -FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast,_TBCopyDataFast - -; dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast +FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast +FastTileCopy dw _CopyTileDataToDP2,_CopyTileDataToDP2,_CopyTileDataToDP2V,_CopyTileDataToDP2V +FastSpriteSub dw FastSpriteOver,FastSpriteUnder ; Need to determine if the sprite or tile data is on top, as that will decide whether the ; sprite or tile data is copied into the temporary buffer first. Also, if TWO_LAYER is set ; then the mask information must be copied as well....This is the last decision point. SpriteDispatch -; jmp (K_TS_SPRITE_TILE_DISP,x) + jmp (K_TS_SPRITE_TILE_DISP,x) +FastSpriteOver txy - SpriteBitsToVBuffAddrs OneSpriteFastUnder;TwoSpritesFast;ThreeSpritesFast;FourSpritesFast + SpriteBitsToVBuffAddrs OneSpriteFast;TwoSpritesFast;ThreeSpritesFast;FourSpritesFast + +FastSpriteUnder + txy + SpriteBitsToVBuffAddrs OneSpriteFastUnder;OneSpriteFastUnder;OneSpriteFastUnder;OneSpriteFastUnder + ; This handles sprite with the tile above OneSpriteFastUnder @@ -82,13 +87,18 @@ _CopySpriteDataToDP2 ; Y = tile store address OneSpriteFast sta sprite_ptr0 - ldx TileStore+TS_TILE_ADDR,y - jsr _CopyTileDataToDP2 ; preserves Y - lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line + tyx + ldy TileStore+TS_TILE_ADDR,x + + pei DP2_TILEDATA_AND_TILESTORE_BANKS + plb + jsr (K_TS_COPY_TILE_DATA,x) + plb + + 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 ldx sprite_ptr0 ; address of sprite vbuff info - lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field - tay plb ]line equ 0 @@ -145,17 +155,7 @@ TwoSpritesFast ply ; Pop off CODE_ADDR_LOW plb ; Set the CODE_ADDR_HIGH bank - -]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 ; Reset to the bank in the top byte of CODE_ADDR_HIGH - rts + jmp _CopyDP2ToCodeField ThreeSpriteLine mac ; and [sprite_ptr2],y @@ -198,17 +198,7 @@ ThreeSpritesFast ply ; Pop off CODE_ADDR_LOW plb ; Set the CODE_ADDR_HIGH bank - -]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 ; Reset to the bank in the top byte of CODE_ADDR_HIGH - rts + jmp _CopyDP2ToCodeField FourSpriteLine mac ; and [sprite_ptr3],y @@ -255,6 +245,9 @@ FourSpritesFast ply ; Pop off CODE_ADDR_LOW plb ; Set the CODE_ADDR_HIGH bank +; Fall through + +_CopyDP2ToCodeField ]line equ 0 lup 8 lda tmp_tile_data+{]line*4} @@ -264,4 +257,4 @@ FourSpritesFast ]line equ ]line+1 --^ plb ; Reset to the bank in the top byte of CODE_ADDR_HIGH - rts \ No newline at end of file + rts From d3da96a834dc88892d1bc0e3e55351daf8118002 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 14 Jun 2022 08:12:33 -0500 Subject: [PATCH 52/82] Add in the special Tile 0 render routines --- src/Defs.s | 1 + src/Sprite.s | 7 ++ src/Tiles.s | 14 +++- src/blitter/TileProcs.s | 35 +++++++++ src/tiles/DirtyTileQueue.s | 2 +- src/tiles/FastRenderer.s | 147 +++++++++++++++++++------------------ 6 files changed, 131 insertions(+), 75 deletions(-) diff --git a/src/Defs.s b/src/Defs.s index 06062f7..3f2a9b7 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -152,6 +152,7 @@ DP2_DIRTY_TILE_CALLBACK equ 162 ; Some pre-defined bank values DP2_TILEDATA_AND_TILESTORE_BANKS equ 164 +DP2_SPRITEDATA_AND_TILESTORE_BANKS equ 166 SPRITE_VBUFF_PTR equ 224 ; 32 bytes of adjusted pointers to VBuffArray addresses ; End direct page values diff --git a/src/Sprite.s b/src/Sprite.s index f8eb9d6..6789c69 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -605,6 +605,13 @@ _CacheSpriteBanks ldx #$100 sta DP2_TILEDATA_AND_TILESTORE_BANKS,x ; put a reversed copy in the second direct page + lda #>spritedata + and #$FF00 + ora #^TileStore + xba + ldx #$100 + sta DP2_SPRITEDATA_AND_TILESTORE_BANKS,x ; put a reversed copy in the second direct page + lda #>TileStore and #$FF00 ora #^TileStore diff --git a/src/Tiles.s b/src/Tiles.s index 802e236..bbf329a 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -110,9 +110,11 @@ InitTiles ; sta TileStore+TS_BASE_TILE_DISP,x bra :out :fast - ldal FastTileProcs + lda #_TBConstTile0 ; Use the special tile 0 routines +; ldal FastTileProcs stal K_TS_BASE_TILE_DISP,x - ldal FastTileCopy + lda #_TBConstTileDataToDP2 +; ldal FastTileCopy stal K_TS_COPY_TILE_DATA,x ldal FastSpriteSub stal K_TS_SPRITE_TILE_DISP,x @@ -226,6 +228,8 @@ _SetTile :fast_under stal K_TS_SPRITE_TILE_DISP,x lda TileStore+TS_TILE_ID,y ; Now, set the draw and copy routines based on H/V bits + bit #TILE_ID_MASK ; unless it's the special Tile 0 + beq :fast_0 and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value xba tax @@ -239,6 +243,12 @@ _SetTile ldal FastTileCopy,x tyx stal K_TS_COPY_TILE_DATA,x + bra :out +:fast_0 + lda #_TBConstTile0 ; Use the special tile 0 routines + stal K_TS_BASE_TILE_DISP,x + lda #_TBConstTileDataToDP2 + stal K_TS_COPY_TILE_DATA,x :out jmp _PushDirtyTileY ; on the next call to _ApplyTiles diff --git a/src/blitter/TileProcs.s b/src/blitter/TileProcs.s index 46cc548..069a5ce 100644 --- a/src/blitter/TileProcs.s +++ b/src/blitter/TileProcs.s @@ -107,3 +107,38 @@ _TBCopyTileMaskToCBuffV --^ rts +; Tile 0 specializations +; _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 +_TBConstTile0 tax + lda #0 +_TBConstTileX sta: $0001,y + sta: $0004,y + sta $1001,y + sta $1004,y + sta $2001,y + sta $2004,y + sta $3001,y + sta $3004,y + sta $4001,y + sta $4004,y + sta $5001,y + sta $5004,y + sta $6001,y + sta $6004,y + sta $7001,y + sta $7004,y + plb + rts +; jmp _TBFillPEAOpcode + +_TBConstTileDataToDP2 +]line equ 0 + lup 8 + stz tmp_tile_data+{]line*4} + stz tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + rts diff --git a/src/tiles/DirtyTileQueue.s b/src/tiles/DirtyTileQueue.s index 7a2599d..1dfbe31 100644 --- a/src/tiles/DirtyTileQueue.s +++ b/src/tiles/DirtyTileQueue.s @@ -80,7 +80,7 @@ _PopDirtyTile2 ; alternate entry point ; for each dirty tile. This is an unrolled loop, so we avoid the need to track a register and ; decrement on each iteration. ; -; Also, if we are handling less that 8 dirty tiles, we use a code path that does not +; Also, if we are handling less than 8 dirty tiles, we use a code path that does not ; need to use an index register ; ; Bank = Tile Store diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index e6f2ce0..997f5c4 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -7,14 +7,15 @@ ; and then copied into the code field _RenderTileFast lda TileStore+TS_SPRITE_FLAG,x ; any sprites on this line? - bne SpriteDispatch + bne :sprites 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 ; set the code field bank - jmp (K_TS_BASE_TILE_DISP,x) ; go to the tile copy routine (just basics) + jmp (K_TS_BASE_TILE_DISP,x) ; go to the tile copy routine +:sprites jmp (K_TS_SPRITE_TILE_DISP,x) ; go to the sprite+tile routine ; The TS_BASE_TILE_DISP routines will come from this table when ENGINE_MODE_TWO_LAYER and ; ENGINE_MODE_DYN_TILES are both off. @@ -22,22 +23,25 @@ FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataV FastTileCopy dw _CopyTileDataToDP2,_CopyTileDataToDP2,_CopyTileDataToDP2V,_CopyTileDataToDP2V FastSpriteSub dw FastSpriteOver,FastSpriteUnder -; Need to determine if the sprite or tile data is on top, as that will decide whether the -; sprite or tile data is copied into the temporary buffer first. Also, if TWO_LAYER is set -; then the mask information must be copied as well....This is the last decision point. - -SpriteDispatch - jmp (K_TS_SPRITE_TILE_DISP,x) - +; Optimized routines to render sprites on top of the tile data and update the code field +; assuming that the opcode will never need to be reset, e.g. all of the instructions are +; PEA opcodes, so only the operands need to be set. +; +; Since the sprite is drawn on top of the tile, the first step is to copy the tile data +; into the direct page temporary space, then dispatch to the appropriate sprite rendering +; subroutine FastSpriteOver txy SpriteBitsToVBuffAddrs OneSpriteFast;TwoSpritesFast;ThreeSpritesFast;FourSpritesFast +; Optimized routines for drawing sprites underneath the tile. In this case, the sprite is drawn first, +; so we have to calculate the sprite dispatch subrotine to copy the sprite data into the direct +; page space and then merge it with the tile data at the end. FastSpriteUnder + rts txy SpriteBitsToVBuffAddrs OneSpriteFastUnder;OneSpriteFastUnder;OneSpriteFastUnder;OneSpriteFastUnder - ; This handles sprite with the tile above OneSpriteFastUnder tax @@ -88,17 +92,17 @@ _CopySpriteDataToDP2 OneSpriteFast sta sprite_ptr0 tyx - ldy TileStore+TS_TILE_ADDR,x - pei DP2_TILEDATA_AND_TILESTORE_BANKS - plb - jsr (K_TS_COPY_TILE_DATA,x) - plb + ldy TileStore+TS_TILE_ADDR,x ; load the tile address + pei DP2_TILEDATA_AND_TILESTORE_BANKS ; copy the tile. Setting the bank + plb ; saves 16 cycles and costs 14, so it's + jsr (K_TS_COPY_TILE_DATA,x) ; a small win, but we really do it to be + plb ; able to preserve the X register 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 - ldx sprite_ptr0 ; address of sprite vbuff info + ldx sprite_ptr0 plb ]line equ 0 @@ -115,7 +119,7 @@ OneSpriteFast ]line equ ]line+1 --^ plb - rts + rts TwoSpriteLine mac ; and [sprite_ptr1],y @@ -127,35 +131,51 @@ TwoSpriteLine mac <<< TwoSpritesFast - ldx TileStore+TS_TILE_ADDR,y - lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line - pha ; and put on the stack for later. Has TileStore bank in high byte. - lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field - pha ; Need to pop it later.... + tyx ; save for after compositing the sprites - sep #$20 ; set the sprite data bank - lda #^spritedata - pha + ldy TileStore+TS_TILE_ADDR,x + pei DP2_TILEDATA_AND_TILESTORE_BANKS plb - rep #$20 + 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} - ldal tiledata+{]line*4},x + lda tmp_tile_data+{]line*4} TwoSpriteLine sta tmp_tile_data+{]line*4} ldy #{]line*SPRITE_PLANE_SPAN}+2 - ldal tiledata+{]line*4}+2,x + lda tmp_tile_data+{]line*4}+2 TwoSpriteLine sta tmp_tile_data+{]line*4}+2 -]line equ ]line+1 +]line equ ]line+1 --^ - ply ; Pop off CODE_ADDR_LOW + plb ; restore access to data bank + +; Fall through +_CopyDP2ToCodeField + 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 plb ; Set the CODE_ADDR_HIGH bank - jmp _CopyDP2ToCodeField + +]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 ; Reset to the bank in the top byte of CODE_ADDR_HIGH + rts ThreeSpriteLine mac ; and [sprite_ptr2],y @@ -170,34 +190,32 @@ ThreeSpriteLine mac <<< ThreeSpritesFast - ldx TileStore+TS_TILE_ADDR,y - lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line - pha ; and put on the stack for later. Has TileStore bank in high byte. - lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field - pha ; Need to pop it later.... + tyx ; save for after compositing the sprites - sep #$20 ; set the sprite data bank - lda #^spritedata - pha + ldy TileStore+TS_TILE_ADDR,x + pei DP2_TILEDATA_AND_TILESTORE_BANKS plb - rep #$20 + 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} - ldal tiledata+{]line*4},x + lda tmp_tile_data+{]line*4} ThreeSpriteLine sta tmp_tile_data+{]line*4} ldy #{]line*SPRITE_PLANE_SPAN}+2 - ldal tiledata+{]line*4}+2,x + lda tmp_tile_data+{]line*4}+2 ThreeSpriteLine sta tmp_tile_data+{]line*4}+2 -]line equ ]line+1 +]line equ ]line+1 --^ - ply ; Pop off CODE_ADDR_LOW - plb ; Set the CODE_ADDR_HIGH bank + plb jmp _CopyDP2ToCodeField FourSpriteLine mac @@ -216,45 +234,30 @@ FourSpriteLine mac <<< FourSpritesFast - ldx TileStore+TS_TILE_ADDR,y - lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line - pha ; and put on the stack for later. Has TileStore bank in high byte. - lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field - pha ; Need to pop it later.... + tyx ; save for after compositing the sprites - sep #$20 ; set the sprite data bank - lda #^spritedata - pha + ldy TileStore+TS_TILE_ADDR,x + pei DP2_TILEDATA_AND_TILESTORE_BANKS plb - rep #$20 + 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} - ldal tiledata+{]line*4},x + lda tmp_tile_data+{]line*4} FourSpriteLine sta tmp_tile_data+{]line*4} ldy #{]line*SPRITE_PLANE_SPAN}+2 - ldal tiledata+{]line*4}+2,x + lda tmp_tile_data+{]line*4}+2 FourSpriteLine sta tmp_tile_data+{]line*4}+2 ]line equ ]line+1 --^ - ply ; Pop off CODE_ADDR_LOW - plb ; Set the CODE_ADDR_HIGH bank - -; Fall through - -_CopyDP2ToCodeField -]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 ; Reset to the bank in the top byte of CODE_ADDR_HIGH - rts + plb + jmp _CopyDP2ToCodeField From 12b05139c217f1bdc231e1ddddc36db9d29bc0aa Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 14 Jun 2022 14:21:51 -0500 Subject: [PATCH 53/82] Move to tables of dispatch tuples to set tile rendering information --- src/Tiles.s | 146 +++++++++++++++++++++++++++++---------- src/blitter/TileProcs.s | 27 -------- src/tiles/FastRenderer.s | 77 +++++++++++++++++---- 3 files changed, 172 insertions(+), 78 deletions(-) diff --git a/src/Tiles.s b/src/Tiles.s index bbf329a..917a6c0 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -110,14 +110,10 @@ InitTiles ; sta TileStore+TS_BASE_TILE_DISP,x bra :out :fast - lda #_TBConstTile0 ; Use the special tile 0 routines -; ldal FastTileProcs - stal K_TS_BASE_TILE_DISP,x - lda #_TBConstTileDataToDP2 -; ldal FastTileCopy - stal K_TS_COPY_TILE_DATA,x - ldal FastSpriteSub - stal K_TS_SPRITE_TILE_DISP,x + lda #0 ; Initialize with Tile 0 + ldy #FastOverZero + jsr _SetTileProcs + :out ; lda DirtyTileProcs ; Fill in with the first dispatch address @@ -197,8 +193,11 @@ _SetTile lda EngineMode bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER - beq :fast + bne :not_fast + brl _SetTileFast +:nochange rts +:not_fast lda TileStore+TS_TILE_ID,y and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value xba @@ -215,46 +214,116 @@ _SetTile tax ; ldal TileProcs,x ; sta TileStore+TS_BASE_TILE_DISP,y - bra :out + jmp _PushDirtyTileY ; on the next call to _ApplyTiles -:fast +; 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 tyx - lda TileStore+TS_TILE_ID,y ; First, check if the sprites are over or under + lda TileStore+TS_TILE_ID,x bit #TILE_PRIORITY_BIT beq :fast_over - ldal FastSpriteSub+2 - bra :fast_under -:fast_over ldal FastSpriteSub -:fast_under stal K_TS_SPRITE_TILE_DISP,x +:fast_under bit #TILE_ID_MASK + beq :fast_under_zero + ldy #FastUnderNonZero + jsr _SetTileProcs + jmp _PushDirtyTileX - lda TileStore+TS_TILE_ID,y ; Now, set the draw and copy routines based on H/V bits - bit #TILE_ID_MASK ; unless it's the special Tile 0 - beq :fast_0 - and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value - xba - tax - phx +:fast_under_zero ldy #FastUnderZero + jsr _SetTileProcs + jmp _PushDirtyTileX - ldal FastTileProcs,x - tyx - stal K_TS_BASE_TILE_DISP,x +:fast_over bit #TILE_ID_MASK + beq :fast_over_zero + ldy #FastOverNonZero + jsr _SetTileProcs + jmp _PushDirtyTileX - plx - ldal FastTileCopy,x - tyx - stal K_TS_COPY_TILE_DATA,x - bra :out -:fast_0 - lda #_TBConstTile0 ; Use the special tile 0 routines - stal K_TS_BASE_TILE_DISP,x - lda #_TBConstTileDataToDP2 - stal K_TS_COPY_TILE_DATA,x +:fast_over_zero ldy #FastOverZero + jsr _SetTileProcs + jmp _PushDirtyTileX :out jmp _PushDirtyTileY ; on the next call to _ApplyTiles -:nochange rts +; X = Tile Store offset +; Y = table address +; A = TILE_ID +; +; see TileProcTables in static/TileStore.s +bnkPtr equ blttmp +tblPtr equ blttmp+4 +stpTmp equ blttmp+8 +_SetTileProcs + and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value + xba + sta stpTmp ; save it +; Set a long pointer to this bank + phk + phk + pla + and #$00FF + stz bnkPtr ; pointer to this bank + sta bnkPtr+2 + + sty tblPtr ; pointer to the table + sta tblPtr+2 + +; Lookup the base tile procedure + + clc + ldy #0 + lda [tblPtr],y ; load address of the base tile proc array + adc stpTmp ; add the offset + tay + lda [bnkPtr],y ; load the actual value + stal K_TS_BASE_TILE_DISP,x ; store it in the dispatch table + +; Lookup the tile copy routine + + clc + ldy #2 + lda [tblPtr],y ; load address to the tile copy proc array + adc stpTmp + tay + lda [bnkPtr],y + stal K_TS_COPY_TILE_DATA,x + +; Finally, load in the last two addresses directly + + ldy #4 + lda [tblPtr],y + stal K_TS_SPRITE_TILE_DISP,x + + ldy #6 + lda [tblPtr],y + stal K_TS_ONE_SPRITE,x + rts + + +; TileProcTables +; +; Tables of tuples used to populate the K_TS_* dispatch arrays for different combinations. Easier to maintain +; than a bunch of conditional code. Each "table" address holds four pointers to routines to handle the four +; combinations of HFLIP and VFLIP bits. +; +; First address: A table of routines that render a tile when there is no sprite present +; Second address: A table of routines that copy a tile into the direct page workspace +; Third address: The general sprite routine; currently only used for Over/Under selection +; Fourth address: The specific sprite routine to use when only one sprite intersects the tile +FastOverNonZero dw FastTileProcs,FastTileCopy,FastSpriteOver,_OneSpriteFastOver +FastOverZero dw FastTileProcs0,FastTileCopy0,FastSpriteOver,_OneSpriteFastOver0 +FastUnderNonZero dw FastTileProcs,FastTileCopy,FastSpriteUnder,_OneSpriteFastUnder +FastUnderZero dw FastTileProcs0,FastTileCopy0,FastSpriteUnder,_OneSpriteFastUnder0 + +; The routines will come from this table when ENGINE_MODE_TWO_LAYER and ENGINE_MODE_DYN_TILES +; are both off. +FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast +FastTileCopy dw _CopyTileDataToDP2,_CopyTileDataToDP2,_CopyTileDataToDP2V,_CopyTileDataToDP2V + +FastTileProcs0 dw _TBConstTile0,_TBConstTile0,_TBConstTile0,_TBConstTile0 +FastTileCopy0 dw _TBConstTileDataToDP2,_TBConstTileDataToDP2,_TBConstTileDataToDP2,_TBConstTileDataToDP2 ; SetBG0XPos ; @@ -454,4 +523,5 @@ b_15_3 endbit 15;3;]4 K_TS_BASE_TILE_DISP ds TILE_STORE_SIZE ; draw the tile without a sprite K_TS_COPY_TILE_DATA ds TILE_STORE_SIZE ; copy the tile into temp storage (used when tile below sprite) -K_TS_SPRITE_TILE_DISP ds TILE_STORE_SIZE ; select the sprite routine for this tile \ No newline at end of file +K_TS_SPRITE_TILE_DISP ds TILE_STORE_SIZE ; select the sprite routine for this tile +K_TS_ONE_SPRITE ds TILE_STORE_SIZE ; specialized sprite routine when only one sprite covers the tile \ No newline at end of file diff --git a/src/blitter/TileProcs.s b/src/blitter/TileProcs.s index 069a5ce..a8a7d10 100644 --- a/src/blitter/TileProcs.s +++ b/src/blitter/TileProcs.s @@ -27,33 +27,6 @@ _TBCopyTileDataAndMaskToCBuffV jsr _TBCopyTileDataToCBuffV jmp _TBCopyTileMaskToCBuffV -_CopyTileDataToDP2 -]line equ 0 - lup 8 - lda tiledata+{]line*4},y - sta tmp_tile_data+{]line*4} - - lda tiledata+{]line*4}+2,y - sta tmp_tile_data+{]line*4}+2 -]line equ ]line+1 - --^ - rts - -_CopyTileDataToDP2V -]src equ 7 -]dest equ 0 - lup 8 - lda tiledata+{]src*4},y - sta tmp_tile_data+{]dest*4} - - lda tiledata+{]src*4}+2,y - sta tmp_tile_data+{]dest*4}+2 -]src equ ]src-1 -]dest equ ]dest+1 - --^ - rts - - _TBCopyTileDataToCBuff ]line equ 0 lup 8 diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index 997f5c4..aae3ffe 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -9,6 +9,8 @@ _RenderTileFast lda TileStore+TS_SPRITE_FLAG,x ; any sprites on this line? bne :sprites +_OneSpriteFastUnder0 +_RenderNoSprite 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 @@ -17,12 +19,6 @@ _RenderTileFast jmp (K_TS_BASE_TILE_DISP,x) ; go to the tile copy routine :sprites jmp (K_TS_SPRITE_TILE_DISP,x) ; go to the sprite+tile routine -; The TS_BASE_TILE_DISP routines will come from this table when ENGINE_MODE_TWO_LAYER and -; ENGINE_MODE_DYN_TILES are both off. -FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast -FastTileCopy dw _CopyTileDataToDP2,_CopyTileDataToDP2,_CopyTileDataToDP2V,_CopyTileDataToDP2V -FastSpriteSub dw FastSpriteOver,FastSpriteUnder - ; Optimized routines to render sprites on top of the tile data and update the code field ; assuming that the opcode will never need to be reset, e.g. all of the instructions are ; PEA opcodes, so only the operands need to be set. @@ -38,12 +34,16 @@ FastSpriteOver ; so we have to calculate the sprite dispatch subrotine to copy the sprite data into the direct ; page space and then merge it with the tile data at the end. FastSpriteUnder - rts txy SpriteBitsToVBuffAddrs OneSpriteFastUnder;OneSpriteFastUnder;OneSpriteFastUnder;OneSpriteFastUnder -; This handles sprite with the tile above +; This handles sprites with the tile above OneSpriteFastUnder + tyx + jmp (K_TS_ONE_SPRITE,x) + +; General copy +_OneSpriteFastUnder tax jsr _CopySpriteDataToDP2 ; preserves Y @@ -90,14 +90,33 @@ _CopySpriteDataToDP2 ; A = vbuff address ; Y = tile store address OneSpriteFast - sta sprite_ptr0 tyx + jmp (K_TS_ONE_SPRITE,x) +; Specialize when the tile is Tile 0 +_OneSpriteFastOver0 + ldy TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line + phy ; 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 + tax + plb + +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta: $0004+{]line*$1000},y + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + plb + rts + +; General copy +_OneSpriteFastOver + sta sprite_ptr0 ldy TileStore+TS_TILE_ADDR,x ; load the tile address - pei DP2_TILEDATA_AND_TILESTORE_BANKS ; copy the tile. Setting the bank - plb ; saves 16 cycles and costs 14, so it's - jsr (K_TS_COPY_TILE_DATA,x) ; a small win, but we really do it to be - plb ; able to preserve the X register + jsr (K_TS_COPY_TILE_DATA,x) ; This routine *must* preserve X register 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. @@ -261,3 +280,35 @@ FourSpritesFast plb jmp _CopyDP2ToCodeField + +_CopyTileDataToDP2 + pei DP2_TILEDATA_AND_TILESTORE_BANKS ; Setting the bank saves 16 cycles and costs 14, so it's a bit faster, + plb ; but we really do it to preserve the X register +]line equ 0 + lup 8 + lda tiledata+{]line*4},y + sta tmp_tile_data+{]line*4} + + lda tiledata+{]line*4}+2,y + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + plb + rts + +_CopyTileDataToDP2V + pei DP2_TILEDATA_AND_TILESTORE_BANKS ; Setting the bank saves 16 cycles and costs 14, so it's a bit faster, + plb ; but we really do it to preserve the X register +]src equ 7 +]dest equ 0 + lup 8 + lda tiledata+{]src*4},y + sta tmp_tile_data+{]dest*4} + + lda tiledata+{]src*4}+2,y + sta tmp_tile_data+{]dest*4}+2 +]src equ ]src-1 +]dest equ ]dest+1 + --^ + plb + rts \ No newline at end of file From 1f9c9b3f5bc0135caa9b7c5255e10b0bd6e14290 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 20 Jun 2022 15:55:09 -0500 Subject: [PATCH 54/82] Checkpoint of final tile proc reorg for generalize new infrastructure for different engine modes --- src/Defs.s | 13 +- src/Render.s | 4 +- src/Sprite.s | 5 + src/Tiles.s | 234 ++++++++++++++++----------- src/Tool.s | 6 +- src/blitter/TileProcs.s | 11 +- src/blitter/Tiles.s | 4 +- src/blitter/Tiles00000.s | 11 ++ src/blitter/Tiles00001.s | 42 +++++ src/blitter/Tiles10001.s | 94 ++++++++++- src/render/Fast.s | 148 +++++++++++++++++ src/render/README.txt | 83 ++++++++++ src/render/Render.s | 231 +++++++++++++++++++++++++++ src/render/Slow.s | 51 ++++++ src/render/Sprite1.s | 180 +++++++++++++++++++++ src/render/Sprite2.s | 102 ++++++++++++ src/render/Sprite3.s | 36 +++++ src/render/Sprite4.s | 0 src/static/TileStore.s | 10 +- src/static/TileStoreDefs.s | 7 +- src/tiles/DirtyTileQueue.s | 37 ++--- src/tiles/FastRenderer.s | 314 ------------------------------------- 22 files changed, 1172 insertions(+), 451 deletions(-) create mode 100644 src/render/Fast.s create mode 100644 src/render/README.txt create mode 100644 src/render/Render.s create mode 100644 src/render/Slow.s create mode 100644 src/render/Sprite1.s create mode 100644 src/render/Sprite2.s create mode 100644 src/render/Sprite3.s create mode 100644 src/render/Sprite4.s diff --git a/src/Defs.s b/src/Defs.s index 3f2a9b7..8f05c28 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -153,6 +153,7 @@ DP2_DIRTY_TILE_CALLBACK equ 162 ; Some pre-defined bank values DP2_TILEDATA_AND_TILESTORE_BANKS equ 164 DP2_SPRITEDATA_AND_TILESTORE_BANKS equ 166 +DP2_TILEDATA_AND_SPRITEDATA_BANKS equ 168 SPRITE_VBUFF_PTR equ 224 ; 32 bytes of adjusted pointers to VBuffArray addresses ; End direct page values @@ -186,16 +187,16 @@ PAD_BUTTON_A equ $02 PAD_KEY_DOWN equ $04 ; Tile constants -TILE_ID_MASK equ $01FF -TILE_SPRITE_BIT equ $8000 ; Set if this tile intersects an active sprite +; TILE_RESERVED_BIT equ $8000 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_FRINGE_BIT equ $2000 ; Unused +TILE_MASK_BIT equ $1000 ; Hint bit used in TWO_LAYER_MODE to optimize rendering +TILE_DYN_BIT equ $0800 ; Is this a Dynamic Tile? TILE_VFLIP_BIT equ $0400 TILE_HFLIP_BIT equ $0200 +TILE_ID_MASK equ $01FF TILE_CTRL_MASK equ $FE00 -TILE_PROC_MASK equ $F800 ; Select tile proc for rendering +; TILE_PROC_MASK equ $F800 ; Select tile proc for rendering ; Sprite constants SPRITE_HIDE equ $2000 diff --git a/src/Render.s b/src/Render.s index 7a7bba6..2c046dd 100644 --- a/src/Render.s +++ b/src/Render.s @@ -211,8 +211,8 @@ _RenderDirtyTile ; 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 +; 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 diff --git a/src/Sprite.s b/src/Sprite.s index 6789c69..6632a3b 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -605,6 +605,11 @@ _CacheSpriteBanks ldx #$100 sta DP2_TILEDATA_AND_TILESTORE_BANKS,x ; put a reversed copy in the second direct page + lda #>spritedata + and #$FF00 + ora #^tiledata + sta DP2_TILEDATA_AND_SPRITEDATA_BANKS,x + lda #>spritedata and #$FF00 ora #^TileStore diff --git a/src/Tiles.s b/src/Tiles.s index 917a6c0..0f9f7d9 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -79,6 +79,7 @@ InitTiles :col equ tmp0 :row equ tmp1 :vbuff equ tmp2 +:base equ tmp3 ; Initialize the Tile Store @@ -106,12 +107,18 @@ InitTiles lda EngineMode bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER beq :fast + bit #ENGINE_MODE_TWO_LAYER + beq :dyn ; ldal TileProcs ; sta TileStore+TS_BASE_TILE_DISP,x bra :out :fast lda #0 ; Initialize with Tile 0 - ldy #FastOverZero + ldy #FastOverZA + jsr _SetTileProcs + +:dyn lda #0 ; Initialize with Tile 0 + ldy #DynOverZA jsr _SetTileProcs :out @@ -135,7 +142,8 @@ InitTiles sta TileStore+TS_CODE_ADDR_HIGH,x ; High word of the tile address (just the bank) lda BRowTableLow,y - sta TileStore+TS_BASE_ADDR,x ; May not be needed later if we can figure out the right constant... + 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 @@ -145,7 +153,8 @@ InitTiles tay lda Col2CodeOffset+2,y clc - adc TileStore+TS_BASE_ADDR,x + 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 dec :col @@ -167,18 +176,25 @@ InitTiles ; Y = tile row [0, 25] (26 rows) ; ; Registers are not preserved -_SetTile - pha - jsr _GetTileStoreOffset0 ; Get the address of the X,Y tile position - tay - pla - - cmp TileStore+TS_TILE_ID,y ; Only set to dirty if the value changed - beq :nochange +oldTileId equ blttmp ; This location is used in _SetTileProcs, too +newTileId equ blttmp+2 +procIdx equ blttmp+4 - sta TileStore+TS_TILE_ID,y ; Value is different, store it. +_SetTile + sta newTileId + jsr _GetTileStoreOffset0 ; Get the address of the X,Y tile position + tax + + lda TileStore+TS_TILE_ID,x + cmp newTileId + bne :changed + rts + +:changed sta oldTileId + lda newTileId + sta TileStore+TS_TILE_ID,x ; Value is different, store it. jsr _GetTileAddr - sta TileStore+TS_TILE_ADDR,y ; Committed to drawing this tile, so get the address of the tile in the tiledata bank for later + sta TileStore+TS_TILE_ADDR,x ; Committed to drawing this tile, so get the address of the tile in the tiledata bank for later ; Set the standard renderer procs for this tile. ; @@ -191,55 +207,81 @@ _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 + lda newTileId + + clc + bit #TILE_PRIORITY_BIT + beq :low_priority + sec +:low_priority asl procIdx + + clc + bit #TILE_ID_MASK + bne :not_zero + sec +:not_zero asl procIdx + + clc + bit #TILE_VFLIP_BIT + beq :no_vflip + sec +:no_vflip asl procIdx + +; Multiple by 6 to get the correct table entry index + + asl procIdx + lda procIdx + asl + adc procIdx + tay + +; Now integrate with the engine mode indicator + lda EngineMode bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER bne :not_fast - brl _SetTileFast -:nochange rts + brl :setTileFast -:not_fast - lda TileStore+TS_TILE_ID,y +:not_fast bit #ENGINE_MODE_TWO_LAYER + bne :not_dyn + brl :setTileDyn + +:not_dyn + lda TileStore+TS_TILE_ID,x and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value xba - tax + tay ; ldal DirtyTileProcs,x ; sta TileStore+TS_DIRTY_TILE_DISP,y ; ldal CopyTileProcs,x ; sta TileStore+TS_DIRTY_TILE_COPY,y - lda TileStore+TS_TILE_ID,y ; Get the non-sprite dispatch address + lda TileStore+TS_TILE_ID,x ; Get the non-sprite dispatch address and #TILE_CTRL_MASK xba - tax -; ldal TileProcs,x + tay +; ldal TileProcs,y ; sta TileStore+TS_BASE_TILE_DISP,y - jmp _PushDirtyTileY ; on the next call to _ApplyTiles + jmp _PushDirtyTileX ; on the next call to _ApplyTiles ; 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 - tyx - lda TileStore+TS_TILE_ID,x - bit #TILE_PRIORITY_BIT - beq :fast_over -:fast_under bit #TILE_ID_MASK - beq :fast_under_zero - ldy #FastUnderNonZero +:setTileFast + lda #FastProcs + lda procIdx jsr _SetTileProcs jmp _PushDirtyTileX -:fast_under_zero ldy #FastUnderZero - jsr _SetTileProcs - jmp _PushDirtyTileX - -:fast_over bit #TILE_ID_MASK - beq :fast_over_zero - ldy #FastOverNonZero - jsr _SetTileProcs - jmp _PushDirtyTileX - -:fast_over_zero ldy #FastOverZero +; 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. If the old tile and the new tile +; are both Dynamic tiles or both Basic tiles, then we can use an optimized routine. Otherwise +; we must set the opcodes as well as the operands +:setTileDyn + lda #DynProcs + lda procIdx jsr _SetTileProcs jmp _PushDirtyTileX @@ -247,83 +289,84 @@ _SetTileFast jmp _PushDirtyTileY ; on the next call to _ApplyTiles ; X = Tile Store offset -; Y = table address -; A = TILE_ID +; Y = Engine Mode Base Table address +; A = Table proc index ; ; see TileProcTables in static/TileStore.s bnkPtr equ blttmp tblPtr equ blttmp+4 -stpTmp equ blttmp+8 _SetTileProcs - and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value - xba - sta stpTmp ; save it ; Set a long pointer to this bank + sty tblPtr + clc + adc tblPtr + sta tblPtr + phk phk pla and #$00FF - stz bnkPtr ; pointer to this bank - sta bnkPtr+2 - - sty tblPtr ; pointer to the table sta tblPtr+2 -; Lookup the base tile procedure +; Lookup the tile procedures - clc ldy #0 - lda [tblPtr],y ; load address of the base tile proc array - adc stpTmp ; add the offset - tay - lda [bnkPtr],y ; load the actual value - stal K_TS_BASE_TILE_DISP,x ; store it in the dispatch table + lda [tblPtr],y + stal K_TS_BASE_TILE_DISP,x -; Lookup the tile copy routine - - clc ldy #2 - lda [tblPtr],y ; load address to the tile copy proc array - adc stpTmp - tay - lda [bnkPtr],y - stal K_TS_COPY_TILE_DATA,x - -; Finally, load in the last two addresses directly - - ldy #4 lda [tblPtr],y stal K_TS_SPRITE_TILE_DISP,x - ldy #6 + ldy #4 lda [tblPtr],y stal K_TS_ONE_SPRITE,x rts - ; TileProcTables ; -; Tables of tuples used to populate the K_TS_* dispatch arrays for different combinations. Easier to maintain -; than a bunch of conditional code. Each "table" address holds four pointers to routines to handle the four -; combinations of HFLIP and VFLIP bits. +; Tables of tuples used to populate the K_TS_* dispatch arrays for different combinations. This is +; easier to maintain than a bunch of conditional code. Each etry hold three addresses. ; -; First address: A table of routines that render a tile when there is no sprite present -; Second address: A table of routines that copy a tile into the direct page workspace -; Third address: The general sprite routine; currently only used for Over/Under selection -; Fourth address: The specific sprite routine to use when only one sprite intersects the tile -FastOverNonZero dw FastTileProcs,FastTileCopy,FastSpriteOver,_OneSpriteFastOver -FastOverZero dw FastTileProcs0,FastTileCopy0,FastSpriteOver,_OneSpriteFastOver0 -FastUnderNonZero dw FastTileProcs,FastTileCopy,FastSpriteUnder,_OneSpriteFastUnder -FastUnderZero dw FastTileProcs0,FastTileCopy0,FastSpriteUnder,_OneSpriteFastUnder0 +; First address: Draw a tile directly into the code buffer (no sprites) +; Second address: Draw a tile merged with sprite data from the direct page +; Third address: Specialize routine to draw a tile merged with one sprite +; +; There are unique tuples of routines for all of the different combinations of tile properties +; and engine modes. This is an extesive number of combinations, but it simplified the development +; and maintainence of the rendering subroutines. Also, the difference subroutines can be written +; in any way and can make use of their on subroutines to reduce code size. +; +; Properties: +; +; [MODE] ENGINE_MODE: Fast, Dyn, TwoLayer +; [Z | N] Is Tile 0? : Yes, No +; [A | V] Is VFLIP? : Yes, No +; [Over | Under] Priority? : Yes, No +; +; So eight tuples per engine mode; 24 tuples total. Table name convention +; +; +FastProcs +FastOverZA dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 +FastOverZV dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 +FastOverNA dw _TBCopyDataFast,GenericOverAFast,_OneSpriteFastOverA +FastOverNV dw _TBCopyDataVFast,GenericOverVFast,_OneSpriteFastOverV +FastUnderZA dw _TBConstTile0,GenericUnderZero,GenericUnderZero +FastUnderZV dw _TBConstTile0,GenericUnderZero,GenericUnderZero +FastUnderNA dw _TBCopyDataFast,GenericUnderAFast,_OneSpriteFastUnderA +FastUnderNV dw _TBCopyDataVFast,GenericUnderVFast,_OneSpriteFastUnderV -; The routines will come from this table when ENGINE_MODE_TWO_LAYER and ENGINE_MODE_DYN_TILES -; are both off. -FastTileProcs dw _TBCopyDataFast,_TBCopyDataFast,_TBCopyDataVFast,_TBCopyDataVFast -FastTileCopy dw _CopyTileDataToDP2,_CopyTileDataToDP2,_CopyTileDataToDP2V,_CopyTileDataToDP2V - -FastTileProcs0 dw _TBConstTile0,_TBConstTile0,_TBConstTile0,_TBConstTile0 -FastTileCopy0 dw _TBConstTileDataToDP2,_TBConstTileDataToDP2,_TBConstTileDataToDP2,_TBConstTileDataToDP2 +DynProcs +DynOverZA +DynOverZV +DynOverNA +DynOverNV +DynUnderZA +DynUnderZV +DynUnderNA +DynUnderNV ; SetBG0XPos ; @@ -387,8 +430,7 @@ last_bit lda (SPRITE_VBUFF_PTR+{]1*2}),y next_bit <<< -; Specialization for the first sprite which can just return the vbuff address -; in a register if there is only one sprite intersecting the tile +; Specialization for the first sprite which can optimize its dispatch if its the only one ; dobit bit_position,dest;next;exit dobit1 mac lsr @@ -404,7 +446,8 @@ dobit1 mac last_bit lda (SPRITE_VBUFF_PTR+{]1*2}),y clc ; pre-adjust these later adc _Sprites+TS_VBUFF_BASE+{]1*2} - jmp ]4 + sta sprite_ptr0+{]2*4} + jmp (K_TS_ONE_SPRITE,x) next_bit <<< @@ -522,6 +565,7 @@ b_15_3 endbit 15;3;]4 ; Store some tables in the K bank that will be used exclusively for jmp (abs,x) dispatch K_TS_BASE_TILE_DISP ds TILE_STORE_SIZE ; draw the tile without a sprite -K_TS_COPY_TILE_DATA ds TILE_STORE_SIZE ; copy the tile into temp storage (used when tile below sprite) +K_TS_COPY_TILE_DATA ds TILE_STORE_SIZE ; copy/merge the tile into temp storage K_TS_SPRITE_TILE_DISP ds TILE_STORE_SIZE ; select the sprite routine for this tile -K_TS_ONE_SPRITE ds TILE_STORE_SIZE ; specialized sprite routine when only one sprite covers the tile \ No newline at end of file +K_TS_ONE_SPRITE ds TILE_STORE_SIZE ; specialized sprite routine when only one sprite covers the tile +K_TS_APPLY_TILE_DATA ds TILE_STORE_SIZE ; move tile from temp storage into code field \ No newline at end of file diff --git a/src/Tool.s b/src/Tool.s index 4e6f48e..8863e74 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -363,8 +363,12 @@ _TSGetSeconds put Sprite2.s put SpriteRender.s put Render.s + put render/Render.s + put render/Fast.s + put render/Sprite1.s + put render/Sprite2.s put tiles/DirtyTileQueue.s - put tiles/FastRenderer.s +; put tiles/FastRenderer.s put blitter/Horz.s put blitter/Vert.s put blitter/BG0.s diff --git a/src/blitter/TileProcs.s b/src/blitter/TileProcs.s index a8a7d10..ad50277 100644 --- a/src/blitter/TileProcs.s +++ b/src/blitter/TileProcs.s @@ -84,10 +84,11 @@ _TBCopyTileMaskToCBuffV ; _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 +; fill in solid colors, so there are no specialized horizontal or verical flipped variantsConstUnderZero _TBConstTile0 tax +_TBConstTileX lda #0 -_TBConstTileX sta: $0001,y + sta: $0001,y sta: $0004,y sta $1001,y sta $1004,y @@ -105,7 +106,11 @@ _TBConstTileX sta: $0001,y sta $7004,y plb rts -; jmp _TBFillPEAOpcode + +_TBConstTileSlow0 + tax + jsr _TBFillPEAOpcode + jmp _TBConstTileX _TBConstTileDataToDP2 ]line equ 0 diff --git a/src/blitter/Tiles.s b/src/blitter/Tiles.s index b1779b0..5a55228 100644 --- a/src/blitter/Tiles.s +++ b/src/blitter/Tiles.s @@ -59,8 +59,8 @@ CopyNoSprites lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line pha ; and put on the stack for later - lda TileStore+TS_BASE_ADDR+1,x ; load the base address of the code field ($0000 or $8000) - sta _BASE_ADDR+1 ; so we can get by just copying the high byte +; lda TileStore+TS_BASE_ADDR+1,x ; load the base address of the code field ($0000 or $8000) +; sta _BASE_ADDR+1 ; so we can get by just copying the high byte rep #$20 lda TileStore+TS_BASE_TILE_DISP,x ; Get the address of the renderer for this tile diff --git a/src/blitter/Tiles00000.s b/src/blitter/Tiles00000.s index 0d3f6e8..7921c2b 100644 --- a/src/blitter/Tiles00000.s +++ b/src/blitter/Tiles00000.s @@ -42,6 +42,7 @@ _TBSolidTile_VH ; register value. This must be restored prior to returning _TBCopyDataFast tax +_TBCopyDataFastX ]line equ 0 lup 8 ldal tiledata+{]line*4},x @@ -53,6 +54,10 @@ _TBCopyDataFast plb rts +_TBCopyDataSlow + tax + jsr _TBFillPEAOpcode + jmp _TBCopyDataFastX _TBCopyData ]line equ 0 @@ -67,6 +72,7 @@ _TBCopyData _TBCopyDataVFast tax +_TBCopyDataVFastX ]src equ 7 ]dest equ 0 lup 8 @@ -80,6 +86,11 @@ _TBCopyDataVFast plb rts +_TBCopyDataVSlow + tax + jsr _TBFillPEAOpcode + jmp _TBCopyDataVFastX + _TBCopyDataV ]src equ 7 ]dest equ 0 diff --git a/src/blitter/Tiles00001.s b/src/blitter/Tiles00001.s index 505abb2..f44aefa 100644 --- a/src/blitter/Tiles00001.s +++ b/src/blitter/Tiles00001.s @@ -12,6 +12,48 @@ _TBDynamicTile_00 jsr _TBDynamicData jmp _TBFillLdaDpOpcode +_TBDynamic + ldal TileStore+TS_TILE_ID,x + and #$007F + ora #$4800 + +]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 + --^ + + 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 + + plb + rts ; Primitive to render a dynamic tile ; ; LDA 00,x / PHA where the operand is fixed when the tile is rendered diff --git a/src/blitter/Tiles10001.s b/src/blitter/Tiles10001.s index d834eca..dd63ee4 100644 --- a/src/blitter/Tiles10001.s +++ b/src/blitter/Tiles10001.s @@ -3,6 +3,48 @@ ; 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 +_TBDynamicSpriteTile + sta _X_REG + ldal TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + ldal TileStore+TS_TILE_ID,x ; Get the original tile descriptor + and #$007F ; clamp to < (32 * 4) + ora #$B500 + xba + sta _OP_CACHE ; This is the 2-byte opcode for to load the data + + CopyDynWord 0;$0003 + CopyDynWord 4;$1003 + CopyDynWord 8;$2003 + CopyDynWord 12;$3003 + CopyDynWord 16;$4003 + CopyDynWord 20;$5003 + CopyDynWord 24;$6003 + CopyDynWord 28;$7003 + + clc + lda _JTBL_CACHE + adc #32 ; All the snippets are 32 bytes wide and, since we're + sta _JTBL_CACHE ; within one tile, the second column is consecutive + + lda _OP_CACHE + adc #$0200 + sta _OP_CACHE + + CopyDynWord 2;$0000 + CopyDynWord 6;$1000 + CopyDynWord 10;$2000 + CopyDynWord 14;$3000 + CopyDynWord 18;$4000 + CopyDynWord 22;$5000 + CopyDynWord 26;$6000 + CopyDynWord 30;$7000 + + plb + rts + + _TBDynamicSpriteTile_00 sty _Y_REG ; This is restored in the macro @@ -53,6 +95,56 @@ _TBDynamicSpriteTile_00 rts +; Create a masked render based on data in the direct page temporary buffer +; +; ]1 : sprite buffer offset +; ]2 : code field offset +CopyDynWord mac + lda tmp_sprite_mask+{]1} ; load the mask value + bne mixed ; a non-zero value may be mixed + +; This is a solid word + lda #$00F4 ; PEA instruction + sta: ]2,y + lda tmp_sprite_data+{]1} ; load the sprite data + sta: ]2+1,y ; PEA operand + bra next + +mixed cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word + beq transparent + + lda #$004C ; JMP to handler + sta: {]2},y + lda _JTBL_CACHE ; Get the offset to the exception handler for this column + ora #{]2&$F000} ; adjust for the current row offset + sta: {]2}+1,y + tax ; This becomes the new address that we use to patch in + + lda _OP_CACHE ; Get the LDA dp,x instruction for this column + sta: $0000,x + + lda #$0029 ; AND #SPRITE_MASK + sta: $0002,x + lda tmp_sprite_mask+{]1} + sta: $0003,x + + lda #$0009 ; ORA #SPRITE_DATA + sta: $0005,x + lda tmp_sprite_data+{]1} + sta: $0006,x + + lda #$0D80 ; branch to the prologue (BRA *+15) + sta: $0008,x + bra next + +; This is a transparent word, so just show the dynamic data +transparent + lda #$4800 ; Put the PHA in the third byte + sta: {]2}+1,y + lda _OP_CACHE ; Store the LDA dp,x instruction with operand + sta: {]2},y +next + <<< ; Masked renderer for a dynamic tile with sprite data overlaid. ; @@ -71,7 +163,7 @@ CopyDynSpriteWord MAC ; If MASK == 0, then we can do a PEA. If MASK == $FFFF, then fall back to the simple Dynamic Tile ; code. ldal spritemask+{]1},x ; load the mask value - bne mixed ; a non-zero value may be mixed + bne mixed ; a non-zero value may be mixed ; This is a solid word lda #$00F4 ; PEA instruction diff --git a/src/render/Fast.s b/src/render/Fast.s new file mode 100644 index 0000000..1b3c41c --- /dev/null +++ b/src/render/Fast.s @@ -0,0 +1,148 @@ +; Collection of render function used when the engine is in "FAST" mode. In this mode +; there are no dynamic tile or two layer tiles enabled, so all of the tiles are comprised +; of PEA opcodes. These functions take advantage of this as the fact that masks are +; not needed to improve rendering speed. +; +; The following functions are defined here +; +; GenericOverAFast : Places data from tmp_sprite_data on top of the TileStore's tile +; GenericUnderAFast : Places the TileStore's tile on top of tmp_sprite_data + +GenericOverAFast + 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 + tax + plb + +]line equ 0 + lup 8 + ldal tiledata+{]line*4},x + and tmp_sprite_mask+{]line*4} + ora tmp_sprite_data+{]line*4} + sta: $0004+{]line*$1000},y + + ldal tiledata+{]line*4}+2,x + and tmp_sprite_mask+{]line*4}+2 + ora tmp_sprite_data+{]line*4}+2 + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + + plb + rts + +GenericOverVFast + 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 + tax + plb + +]src equ 7 +]dest equ 0 + lup 8 + ldal tiledata+{]src*4},x + and tmp_sprite_mask+{]line*4} + ora tmp_sprite_data+{]line*4} + sta: $0004+{]line*$1000},y + + ldal tiledata+{]src*4}+2,x + and tmp_sprite_mask+{]line*4}+2 + ora tmp_sprite_data+{]line*4}+2 + sta: $0001+{]line*$1000},y +]src equ ]src-1 +]dest equ ]dest+1 + --^ + plb + rts + +GenericOverZero + 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 + plb + +]line equ 0 + lup 8 + lda tmp_sprite_data+{]line*4} + sta: $0004+{]line*$1000},y + + lda tmp_sprite_data+{]line*4}+2 + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + + plb + rts + +GenericUnderAFast + 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 + tax + plb + +]line equ 0 + lup 8 + lda tmp_sprite_data+{]line*4} + andl tiledata+{]line*4}+32,x + oral tiledata+{]line*4}+32,x + sta: $0004+{]line*$1000},y + + lda tmp_sprite_data+{]line*4}+2 + andl tiledata+{]line*4}+32+2,x + oral tiledata+{]line*4}+32+2,x + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + + plb + rts + +GenericUnderVFast + 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 + tax + plb + +]src equ 7 +]dest equ 0 + lup 8 + lda tmp_sprite_data+{]line*4} + andl tiledata+{]src*4}+32,x + oral tiledata+{]src*4}+32,x + sta: $0004+{]line*$1000},y + + lda tmp_sprite_data+{]line*4}+2 + andl tiledata+{]src*4}+32+2,x + oral tiledata+{]src*4}+32+2,x + sta: $0001+{]line*$1000},y +]src equ ]src-1 +]dest equ ]dest+1 + --^ + + plb + rts + +GenericUnderZero + 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 + plb + lda #0 + +]line equ 0 + lup 8 + sta: $0004+{]line*$1000},y + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + + plb + rts diff --git a/src/render/README.txt b/src/render/README.txt new file mode 100644 index 0000000..0dbffdf --- /dev/null +++ b/src/render/README.txt @@ -0,0 +1,83 @@ +This folder contains the rendering tuples for the different type of tile rendering modes +that are defined by both the engine mode and the specific tile attributes. There are +a *lot* or variants, so they are cataloged here. + +The top-level TileRender function in the main entry point that defined the overal tile render +flow as well as the register parameters and calling conventions for each of the modular +components. + +There are 5 pluggable functions that make up a rendering mode + +1. K_TS_BASE_TILE_DISP + + An address to a function that will render a tile into the code field. There are no + sprites to handle in this case. + + Arguments: + A: TileData/TileMask address + B: code field bank + Y: address of the tile in the code bank + X: TileStore offset + + Return: + None + + If additional TileStore properties are needed for the renderer, they can be read using the X + register. + +2. K_TS_SPRITE_TILE_DISP + + Selects the top-level handler for rendering a tile with a sprite. Currently, this is used to + select between rendering a sprite above the tile, or under the tile based on the value of the + TILE_PRIORITY_BIT. + + Arguments: + A: TileStore+TS_SPRITE_FLAG + X: TileStore offset + + Return: + Y: TileStore offset + sprite_ptrX dirct page values set to the sprite VBuff addresses + + The handler routine is responsible for examining the TS_SPRITE_FLAG value and dispatching + to an appropriate routine to handle the number of sprites intersecting the tile. + +3. K_TS_ONE_SPRITE + + A specialized routine when K_TS_SPRITE_TILE_DISP determines there is only one sprite to render + it MUST dispatch to this function. The K_TS_ONE_SPRITE routine MAY make use of the K_TS_COPY_TILE_DATA + and K_TS_APPLY_TILE_DATA functions, but is not required to do so. + +4. K_TS_COPY_TILE_DATA & K_TS_APPLY_TILE_DATA + + A pair of function that copye tile data (and possible mask information) into a temporary + direct page space and then render that workspace into the code field. + + These functions are used as building blocks by the generic Over/Under multi-sprite + rendering code. + + K_TS_COPY_TILE_DATA + Arguments: + B: Set to the TileData bank + Y: Set to the tile address + Return: + X: preserve the X register + + K_TS_APPLY_TILE_DATA + Arguments: + B: code field bank + Y: address of the tile in the code bank + Return: + None + + + +Generic Flow + + 1. Is there a sprite? + No -> Call K_TS_BASE_TILE_DISP to render a tile into the code field + + Yes -> Call K_TS_SPRITE_TILE_DISP + + Over : Copy tile data + mask to DP, Copy sprite data + mask to DP, render tile to code field + Under : Copy sprite data to DP, \ No newline at end of file diff --git a/src/render/Render.s b/src/render/Render.s new file mode 100644 index 0000000..d0a6cc5 --- /dev/null +++ b/src/render/Render.s @@ -0,0 +1,231 @@ +; 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_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 ; set the code field bank + jmp (K_TS_BASE_TILE_DISP,x) ; go to the tile copy routine + +; 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 + SpriteBitsToVBuffAddrs $0000;TwoSprites;ThreeSprites;FourSprites + +; Dispatch vectors for the two, three and four sprite functions. These just +; flatten the sprite data into the direct page workspace and then pass control +; to the configurable routine which is set in SetTile and knows what to do +; based on the tile properties (over/under, engine mode, etc.) +TwoSprites tyx + jsr CopyTwoSpritesDataAndMaskToDP + jmp (K_TS_SPRITE_TILE_DISP,x) + +ThreeSprites tyx + jsr CopyThreeSpritesDataAndMaskToDP + jmp (K_TS_SPRITE_TILE_DISP,x) + +FourSprites tyx + jsr CopyFourSpritesDataAndMaskToDP + jmp (K_TS_SPRITE_TILE_DISP,x) + +; Helper functions (and macros) + +; CopyTileToDP -- executes the K_TS_COPY_TILE_DATA routine. This may copy just data or data+mask +; information to the direct page +_CopyTileToDP mac + ldy TileStore+TS_TILE_ADDR,x ; load the tile address + pei DP2_TILEDATA_AND_TILESTORE_BANKS + plb ; set to the tiledata bank + jsr (K_TS_COPY_TILE_DATA,x) ; preserves X-reg + plb + <<< +CopyTileToDP + _CopyTileToDP + rts + +; CopyTileToDPSprite -- same as above, but returns with the Data BAnk set to the sprite data bank +_CopyTileToDPSprite mac + ldy TileStore+TS_TILE_ADDR,x ; load the tile address + pei DP2_TILEDATA_AND_SPRITEDATA_BANKS + plb ; set to the tiledata bank + jsr (K_TS_COPY_TILE_DATA,x) ; preserves X-reg + plb + <<< +CopyTileToDPSprite + _CopyTileToDPSprite + rts + +; Simple pair of routines that copies just the tile data to the direct page workspace. Data Bank +; must be set to the TileData bank in entry. +; +; Preserves the X-register +CopyTileDataToDP +]line equ 0 + lup 8 + lda tiledata+{]line*4},y + sta tmp_tile_data+{]line*4} + + lda tiledata+{]line*4}+2,y + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + rts + +CopyTileDataToDPV +]src equ 7 +]dest equ 0 + lup 8 + lda tiledata+{]src*4},y + sta tmp_tile_data+{]dest*4} + + lda tiledata+{]src*4}+2,y + sta tmp_tile_data+{]dest*4}+2 +]src equ ]src-1 +]dest equ ]dest+1 + --^ + rts + +; Copy both the tile and mask data to the driect page space +_CopyTileDataAndMaskToDP +]line equ 0 + lup 8 + lda tiledata+{]line*4},y + sta tmp_tile_data+{]line*4} + lda tiledata+{]line*4}+32,y + sta tmp_tile_mask+{]line*4} + + lda tiledata+{]line*4}+2,y + sta tmp_tile_data+{]line*4}+2 + lda tiledata+{]line*4}+32+2,y + sta tmp_tile_mask+{]line*4}+2 +]line equ ]line+1 + --^ + rts + +_CopyTileDataAndMaskToDPV +]src equ 7 +]dest equ 0 + lup 8 + lda tiledata+{]src*4},y + sta tmp_tile_data+{]dest*4} + lda tiledata+{]src*4}+32,y + sta tmp_tile_mask+{]dest*4} + + lda tiledata+{]src*4}+2,y + sta tmp_tile_data+{]dest*4}+2 + lda tiledata+{]src*4}+32+2,y + sta tmp_tile_mask+{]dest*4}+2 +]src equ ]src-1 +]dest equ ]dest+1 + --^ + rts + +; Given a populate tmp_sprite_data buffer to use as a base, merge it with a tile and write to the +; code field +MergeSpriteWithTileFast + ldx TileStore+TS_TILE_ADDR,y + lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line + pha ; and put on the stack for later. Has TileStore bank in high byte. + lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field + tay + plb + +]line equ 0 + lup 8 + lda tmp_sprite_data+{]line*4} + andl tiledata+{]line*4}+32,x + oral tiledata+{]line*4},x + sta: $0004+{]line*$1000},y + + lda tmp_sprite_data+{]line*4}+2 + andl tiledata+{]line*4}+32+2,x + oral tiledata+{]line*4}+2,x + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + plb + rts + +MergeSpriteWithTileSlow + ldx TileStore+TS_TILE_ADDR,y + lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line + pha ; and put on the stack for later. Has TileStore bank in high byte. + lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field + tay + plb + +]line equ 0 + lup 8 + lda tmp_sprite_data+{]line*4} + andl tiledata+{]line*4}+32,x + oral tiledata+{]line*4},x + sta: $0004+{]line*$1000},y + + lda tmp_sprite_data+{]line*4}+2 + andl tiledata+{]line*4}+32+2,x + oral tiledata+{]line*4}+2,x + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + jmp _FillPEAOpcode + + + + + +; 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) + + diff --git a/src/render/Slow.s b/src/render/Slow.s new file mode 100644 index 0000000..a6a7c8a --- /dev/null +++ b/src/render/Slow.s @@ -0,0 +1,51 @@ +; Identical routines to those in Fast.s, but also set the opcode. Used to render solid +; tiles when the engine mode has other capabilities turned on +; +; The following functions are defined here +; +; GenericOverSlow : Places data from tmp_sprite_data on top of the TileStore's tile +; GenericUnderSlow : Places the TileStore's tile on top of tmp_sprite_data + +GenericOverSlow + 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 + tax + +]line equ 0 + lup 8 + ldal tiledata+{]line*4},x + and tmp_sprite_mask+{]line*4} + ora tmp_sprite_data+{]line*4} + sta: $0004+{]line*$1000},y + + ldal tiledata+{]line*4}+2,x + and tmp_sprite_mask+{]line*4}+2 + ora tmp_sprite_data+{]line*4}+2 + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + jmp _FillPEAOpcode + +GenericUnderSlow + 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 + tax + +]line equ 0 + lup 8 + lda tmp_sprite_data+{]line*4} + andl tiledata+{]line*4}+32,x + oral tiledata+{]line*4}+32,x + sta: $0004+{]line*$1000},y + + lda tmp_sprite_data+{]line*4}+2 + andl tiledata+{]line*4}+32+2,x + oral tiledata+{]line*4}+32+2,x + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + jmp _FillPEAOpcode diff --git a/src/render/Sprite1.s b/src/render/Sprite1.s new file mode 100644 index 0000000..28d433a --- /dev/null +++ b/src/render/Sprite1.s @@ -0,0 +1,180 @@ +; Specialized routines that can be assigned to K_TS_ONE_SPRITE for rendering a single sprite into +; a tile. There are more variants of this function because having a single sprite in a tile is a very +; common scenario, so we put additional effort into optimizing this case. + +;------------------------------ +; Section: Above Tile Renderers + +; The simplest implementation. When drawing a sprite over Tile 0 in FAST mode, we can just copy the +; sprite data into the coe field directly. + +_OneSpriteFastOver0 + ldy TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line + phy ; 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 + tax ; VBuff address from SpriteBitsToVBuffAddrs macro + plb ; set to the code field bank + +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta: $0004+{]line*$1000},y + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + + plb ; Restore the TileStore bank + rts + +; Next implementation; drawing a sprite onto a regular tile. In this case we need to make +; use of the K_TS_COPY_TILE_DATA function because that takes care of copying the correct +; tile data into the direct page buffer. + +; The 1-sprite dispatch prserves the X-register, so it already points to the TileStore + +_OneSpriteFastOverV + jsr CopyTileDataToDPV + bra _OneSpriteFastOver + +_OneSpriteFastOverA + jsr CopyTileDataToDP + +_OneSpriteFastOver + 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 + ldx sprite_ptr0 + plb + +]line equ 0 + lup 8 + lda tmp_tile_data+{]line*4} + andl spritemask+{]line*SPRITE_PLANE_SPAN},x + oral spritedata+{]line*SPRITE_PLANE_SPAN},x + sta: $0004+{]line*$1000},y + + lda tmp_tile_data+{]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 + --^ + plb + rts + +; This is the "SLOW" variant that fills in the PEA opcode specialized for Tile 0. + +_OneSpriteSlowOver0 + ldy TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line + phy ; 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 + tax ; VBuff address from SpriteBitsToVBuffAddrs macro + plb ; set to the code field bank + +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta: $0004+{]line*$1000},y + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + + jmp _FillPEAOpcode + +; Slow variant for regular tile. + +_OneSpriteSlowOver + jsr CopyTileDataToDP + + 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 + ldx sprite_ptr0 + plb + +]line equ 0 + lup 8 + lda tmp_tile_data+{]line*4} + andl spritemask+{]line*SPRITE_PLANE_SPAN},x + oral spritedata+{]line*SPRITE_PLANE_SPAN},x + sta: $0004+{]line*$1000},y + + lda tmp_tile_data+{]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 + --^ + +; Fall through here to give the common case a small boost +_FillPEAOpcode + sep #$20 + lda #$F4 +]line equ 0 + lup 8 + sta: $0000+{]line*$1000},y + sta: $0003+{]line*$1000},y +]line equ ]line+1 + --^ + rep #$20 + + plb ; Restore the TileStore bank + rts + +;------------------------------ +; Section: Below Tile Renderers + +; Drawing under the zero tile is the same as not drawing a sprite fo both the fast and slow cases +_OneSpriteFastUnderA + jsr _CopyTileDataAndMaskToDP + bra _OneSpriteFastUnder + +_OneSpriteFastUnderV + jsr _CopyTileDataAndMaskToDPV + +_OneSpriteFastUnder + 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 + ldx sprite_ptr0 + plb + +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + ora tmp_tile_mask+{]line*4} + ora tmp_tile_data+{]line*4} + sta: $0004+{]line*$1000},y + + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + ora tmp_tile_mask+{]line*4}+2 + ora tmp_tile_data+{]line*4}+2 + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + + plb + rts + +_OneSpriteSlowUnder0 + 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 ; set the code field bank + jmp (K_TS_BASE_TILE_DISP,x) ; go to the tile copy routine + +;-------------------------------- +; Helper functions for one Sprite +CopyOneSpriteDataToDP +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_data+{]line*4} + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_data+{]line*4}+2 +]line equ ]line+1 + --^ + rts \ No newline at end of file diff --git a/src/render/Sprite2.s b/src/render/Sprite2.s new file mode 100644 index 0000000..6f296da --- /dev/null +++ b/src/render/Sprite2.s @@ -0,0 +1,102 @@ +; Specialize routines for handling two sprites. Like Sprite3.s and Sprite4.s there are four +; variants -- one to handle over / under sprite orders and one each for whether the mask needs +; to be used or not. +TwoSpriteLine mac + db $37,sprite_ptr1 ; and [sprite_ptr1],y + ora (sprite_ptr1),y + db $37,sprite_ptr0 ; and [sprite_ptr0],y + ora (sprite_ptr0),y + <<< + +TwoSpriteData mac + lda (sprite_ptr1),y + db $37,sprite_ptr0 ; and [sprite_ptr0],y + ora (sprite_ptr0),y + <<< + +TwoSpriteMask mac + db $B7,sprite_ptr1 ; lda [sprite_ptr1],y + db $37,sprite_ptr0 ; and [sprite_ptr0],y + <<< + +TwoSpritesOver + tyx ; save after compositing the sprites + phb ; save the current bank + jsr CopyTileToDPSprite ; copy necessary tile data to the direct page + +]line equ 0 + lup 8 + ldy #{]line*SPRITE_PLANE_SPAN} + lda tmp_tile_data+{]line*4} + TwoSpriteLine + sta tmp_tile_data+{]line*4} + + ldy #{]line*SPRITE_PLANE_SPAN}+2 + lda tmp_tile_data+{]line*4}+2 + TwoSpriteLine + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + + plb + jmp (K_TS_APPLY_TILE_DATA,x) + + +TwoSpritesUnderFast + tyx ; save after compositing the sprites + phb ; save the current bank + jsr CopyTwoSpritesDataToDP ; copy necessary sprite data to the direct page + jmp MergeSpriteWithTileFast + +]line equ 0 + lup 8 + ldy #{]line*SPRITE_PLANE_SPAN} + lda tmp_tile_data+{]line*4} + TwoSpriteLine + sta tmp_tile_data+{]line*4} + + ldy #{]line*SPRITE_PLANE_SPAN}+2 + lda tmp_tile_data+{]line*4}+2 + TwoSpriteLine + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + + plb + jmp (K_TS_APPLY_TILE_DATA,x) + +;--------------------------------- +; Helper functions for two Sprites +CopyTwoSpritesDataToDP +]line equ 0 + lup 8 + ldy #{]line*SPRITE_PLANE_SPAN} + TwoSpriteData + sta tmp_sprite_data+{]line*4} + + ldy #{]line*SPRITE_PLANE_SPAN}+2 + TwoSpriteData + sta tmp_sprite_data+{]line*4}+2 +]line equ ]line+1 + --^ + rts +CopyFourSpritesDataAndMaskToDP +CopyThreeSpritesDataAndMaskToDP +CopyTwoSpritesDataAndMaskToDP +]line equ 0 + lup 8 + ldy #{]line*SPRITE_PLANE_SPAN} + TwoSpriteData + sta tmp_sprite_data+{]line*4} + TwoSpriteMask + sta tmp_sprite_mask+{]line*4} + + ldy #{]line*SPRITE_PLANE_SPAN}+2 + TwoSpriteData + sta tmp_sprite_data+{]line*4}+2 + TwoSpriteMask + sta tmp_sprite_mask+{]line*4}+2 +]line equ ]line+1 + --^ + jmp (K_TS_SPRITE_TILE_DISP,x) + diff --git a/src/render/Sprite3.s b/src/render/Sprite3.s new file mode 100644 index 0000000..30bc408 --- /dev/null +++ b/src/render/Sprite3.s @@ -0,0 +1,36 @@ + +ThreeSpriteLine mac + db $37,sprite_ptr2 ; and [sprite_ptr2],y + ora (sprite_ptr2),y + db $37,sprite_ptr1 ; and [sprite_ptr1],y + ora (sprite_ptr1),y + db $37,sprite_ptr0 ; and [sprite_ptr0],y + ora (sprite_ptr0),y + <<< + +; Three sprites wiithout extra masking +ThreeSpritesFast + tyx ; save for after compositing the sprites + + ldy TileStore+TS_TILE_ADDR,x + pei DP2_TILEDATA_AND_SPRITEDATA_BANKS + plb ; set to the tiledata bank + jsr (K_TS_COPY_TILE_DATA,x) + plb ; set to the sprite data bank + +]line equ 0 + lup 8 + ldy #{]line*SPRITE_PLANE_SPAN} + lda tmp_tile_data+{]line*4} + ThreeSpriteLine + sta tmp_tile_data+{]line*4} + + ldy #{]line*SPRITE_PLANE_SPAN}+2 + lda tmp_tile_data+{]line*4}+2 + ThreeSpriteLine + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + + plb + jmp _CopyDP2ToCodeField diff --git a/src/render/Sprite4.s b/src/render/Sprite4.s new file mode 100644 index 0000000..e69de29 diff --git a/src/static/TileStore.s b/src/static/TileStore.s index f88cb96..7b13242 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -388,11 +388,13 @@ OldOneSecVec ENT ds 4 Timers ENT ds TIMER_REC_SIZE*MAX_TIMERS + +; From the IIgs ref DefaultPalette ENT - dw $0000,$007F,$0090,$0FF0 - dw $000F,$0080,$0f70,$0FFF - dw $0fa9,$0ff0,$00e0,$04DF - dw $0d00,$078f,$0ccc,$0FFF + dw $0000,$0777,$0841,$072C + dw $000F,$0080,$0F70,$0D00 + dw $0FA9,$0FF0,$00E0,$04DF + dw $0DAF,$078F,$0CCC,$0FFF ; 0. Full Screen : 40 x 25 320 x 200 (32,000 bytes (100.0%)) ; 1. Sword of Sodan : 34 x 24 272 x 192 (26,112 bytes ( 81.6%)) diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index c6a41c2..8a5d3d1 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -12,13 +12,14 @@ TS_TILE_ADDR equ {TILE_STORE_SIZE*3} ; cached value, the address TS_CODE_ADDR_LOW equ {TILE_STORE_SIZE*4} ; const value, address of this tile in the code fields TS_CODE_ADDR_HIGH equ {TILE_STORE_SIZE*5} TS_WORD_OFFSET equ {TILE_STORE_SIZE*6} ; const value, word offset value for this tile if LDA (dp),y instructions re used -TS_BASE_ADDR equ {TILE_STORE_SIZE*7} ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000. +;TS_BASE_ADDR equ {TILE_STORE_SIZE*7} ; const value, because there are two rows of tiles per bank, this is set to $0000 or $8000. +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. ; TODO: Move these arrays into the K bank to support direct dispatch via jmp (abs,x) -TS_BASE_TILE_COPY equ {TILE_STORE_SIZE*9} ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering +; TS_BASE_TILE_COPY equ {TILE_STORE_SIZE*9} ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering ; TS_BASE_TILE_DISP equ {TILE_STORE_SIZE*10} ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function -TS_DIRTY_TILE_DISP equ {TILE_STORE_SIZE*11} ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function +; TS_DIRTY_TILE_DISP equ {TILE_STORE_SIZE*11} ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function TILE_STORE_NUM equ 12 ; Need this many parallel arrays diff --git a/src/tiles/DirtyTileQueue.s b/src/tiles/DirtyTileQueue.s index 1dfbe31..459c00e 100644 --- a/src/tiles/DirtyTileQueue.s +++ b/src/tiles/DirtyTileQueue.s @@ -56,11 +56,9 @@ _PushDirtyTileY :occupied2 tya ; Make sure TileStore offset is returned in the accumulator 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. +; that the core rendering functions *only* use _PopDirtyTile to get a list of tiles to update. _PopDirtyTile ldy DirtyTileCount bne _PopDirtyTile2 @@ -93,7 +91,6 @@ pdtf_not_empty cpx #16 ; If there are >= 8 elements, then bcs full_chunk ; do a full chunk -; stz DP2_DIRTY_TILE_COUNT ; Otherwise, this pass will handle them all jmp (at_table,x) at_table da at_exit,at_one,at_two,at_three da at_four,at_five,at_six,at_seven @@ -109,76 +106,76 @@ full_chunk txa ldx DirtyTiles+14,y stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile ldy DP2_DIRTY_TILE_COUNT ldx DirtyTiles+12,y stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile ldy DP2_DIRTY_TILE_COUNT ldx DirtyTiles+10,y stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile ldy DP2_DIRTY_TILE_COUNT ldx DirtyTiles+8,y stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile ldy DP2_DIRTY_TILE_COUNT ldx DirtyTiles+6,y stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile ldy DP2_DIRTY_TILE_COUNT ldx DirtyTiles+4,y stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile ldy DP2_DIRTY_TILE_COUNT ldx DirtyTiles+2,y stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile ldy DP2_DIRTY_TILE_COUNT ldx DirtyTiles+0,y stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile jmp _PopDirtyTilesFast ; These routines just handle between 1 and 7 dirty tiles at_seven ldx DirtyTiles+12 stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile at_six ldx DirtyTiles+10 stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile at_five ldx DirtyTiles+8 stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile at_four ldx DirtyTiles+6 stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile at_three ldx DirtyTiles+4 stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile at_two ldx DirtyTiles+2 stz TileStore+TS_DIRTY,x - jsr _RenderTileFast + jsr _RenderTile at_one ldx DirtyTiles+0 stz TileStore+TS_DIRTY,x - jmp _RenderTileFast + jmp _RenderTile diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s index aae3ffe..e69de29 100644 --- a/src/tiles/FastRenderer.s +++ b/src/tiles/FastRenderer.s @@ -1,314 +0,0 @@ -; If the engine mode has the second background layer disabled, we take advantage of that to -; be more efficient in our rendering. Basically, without the second layer, there is no need -; to use the tile mask information. -; -; If there are no sprites, then we copy the tile data into the code field as fast as possible. -; If there are sprites, then the sprite data is flattened and stored into a direct page buffer -; and then copied into the code field -_RenderTileFast - lda TileStore+TS_SPRITE_FLAG,x ; any sprites on this line? - bne :sprites - -_OneSpriteFastUnder0 -_RenderNoSprite - 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 ; set the code field bank - jmp (K_TS_BASE_TILE_DISP,x) ; go to the tile copy routine -:sprites jmp (K_TS_SPRITE_TILE_DISP,x) ; go to the sprite+tile routine - -; Optimized routines to render sprites on top of the tile data and update the code field -; assuming that the opcode will never need to be reset, e.g. all of the instructions are -; PEA opcodes, so only the operands need to be set. -; -; Since the sprite is drawn on top of the tile, the first step is to copy the tile data -; into the direct page temporary space, then dispatch to the appropriate sprite rendering -; subroutine -FastSpriteOver - txy - SpriteBitsToVBuffAddrs OneSpriteFast;TwoSpritesFast;ThreeSpritesFast;FourSpritesFast - -; Optimized routines for drawing sprites underneath the tile. In this case, the sprite is drawn first, -; so we have to calculate the sprite dispatch subrotine to copy the sprite data into the direct -; page space and then merge it with the tile data at the end. -FastSpriteUnder - txy - SpriteBitsToVBuffAddrs OneSpriteFastUnder;OneSpriteFastUnder;OneSpriteFastUnder;OneSpriteFastUnder - -; This handles sprites with the tile above -OneSpriteFastUnder - tyx - jmp (K_TS_ONE_SPRITE,x) - -; General copy -_OneSpriteFastUnder - tax - jsr _CopySpriteDataToDP2 ; preserves Y - - ldx TileStore+TS_TILE_ADDR,y - lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line - pha ; and put on the stack for later. Has TileStore bank in high byte. - lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field - tay - plb - -]line equ 0 - lup 8 - lda tmp_tile_data+{]line*4} - andl tiledata+{]line*4}+32,x - oral tiledata+{]line*4},x - sta: $0004+{]line*$1000},y - - lda tmp_tile_data+{]line*4}+2 - andl tiledata+{]line*4}+32+2,x - oral tiledata+{]line*4}+2,x - sta: $0001+{]line*$1000},y -]line equ ]line+1 - --^ - plb - rts - - -_CopySpriteDataToDP2 -]line equ 0 - lup 8 - ldal spritedata+{]line*SPRITE_PLANE_SPAN},x - sta tmp_tile_data+{]line*4} - - ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_tile_data+{]line*4}+2 -]line equ ]line+1 - --^ - rts - -; Where there are sprites involved, the first step is to call a routine to copy the -; tile data into a temporary buffer. Then the sprite data is merged and placed into -; the code field. -; -; A = vbuff address -; Y = tile store address -OneSpriteFast - tyx - jmp (K_TS_ONE_SPRITE,x) - -; Specialize when the tile is Tile 0 -_OneSpriteFastOver0 - ldy TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line - phy ; 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 - tax - plb - -]line equ 0 - lup 8 - ldal spritedata+{]line*SPRITE_PLANE_SPAN},x - sta: $0004+{]line*$1000},y - ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x - sta: $0001+{]line*$1000},y -]line equ ]line+1 - --^ - plb - rts - -; General copy -_OneSpriteFastOver - sta sprite_ptr0 - ldy TileStore+TS_TILE_ADDR,x ; load the tile address - jsr (K_TS_COPY_TILE_DATA,x) ; This routine *must* preserve X register - - 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 - ldx sprite_ptr0 - plb - -]line equ 0 - lup 8 - lda tmp_tile_data+{]line*4} - andl spritemask+{]line*SPRITE_PLANE_SPAN},x - oral spritedata+{]line*SPRITE_PLANE_SPAN},x - sta: $0004+{]line*$1000},y - - lda tmp_tile_data+{]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 - --^ - plb - rts - -TwoSpriteLine mac -; and [sprite_ptr1],y - db $37,sprite_ptr1 - ora (sprite_ptr1),y -; and [sprite_ptr0],y - db $37,sprite_ptr0 - ora (sprite_ptr0),y - <<< - -TwoSpritesFast - 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} - TwoSpriteLine - sta tmp_tile_data+{]line*4} - - ldy #{]line*SPRITE_PLANE_SPAN}+2 - lda tmp_tile_data+{]line*4}+2 - TwoSpriteLine - sta tmp_tile_data+{]line*4}+2 -]line equ ]line+1 - --^ - - plb ; restore access to data bank - -; Fall through -_CopyDP2ToCodeField - 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 - plb ; Set the CODE_ADDR_HIGH bank - -]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 ; Reset to the bank in the top byte of CODE_ADDR_HIGH - rts - -ThreeSpriteLine mac -; 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 - <<< - -ThreeSpritesFast - 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} - ThreeSpriteLine - sta tmp_tile_data+{]line*4} - - ldy #{]line*SPRITE_PLANE_SPAN}+2 - lda tmp_tile_data+{]line*4}+2 - ThreeSpriteLine - sta tmp_tile_data+{]line*4}+2 -]line equ ]line+1 - --^ - - plb - jmp _CopyDP2ToCodeField - -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 _CopyDP2ToCodeField - -_CopyTileDataToDP2 - pei DP2_TILEDATA_AND_TILESTORE_BANKS ; Setting the bank saves 16 cycles and costs 14, so it's a bit faster, - plb ; but we really do it to preserve the X register -]line equ 0 - lup 8 - lda tiledata+{]line*4},y - sta tmp_tile_data+{]line*4} - - lda tiledata+{]line*4}+2,y - sta tmp_tile_data+{]line*4}+2 -]line equ ]line+1 - --^ - plb - rts - -_CopyTileDataToDP2V - pei DP2_TILEDATA_AND_TILESTORE_BANKS ; Setting the bank saves 16 cycles and costs 14, so it's a bit faster, - plb ; but we really do it to preserve the X register -]src equ 7 -]dest equ 0 - lup 8 - lda tiledata+{]src*4},y - sta tmp_tile_data+{]dest*4} - - lda tiledata+{]src*4}+2,y - sta tmp_tile_data+{]dest*4}+2 -]src equ ]src-1 -]dest equ ]dest+1 - --^ - plb - rts \ No newline at end of file From 4ea3033b5e40e2c3aa801b5b21e6ce80e5b0ddd2 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 20 Jun 2022 17:57:19 -0500 Subject: [PATCH 55/82] Get minimal tiles and sprites rendering without a crash --- src/Tiles.s | 85 ++++++++++++++++++++++------------------ src/Tool.s | 1 - src/blitter/TileProcs.s | 2 +- src/tiles/FastRenderer.s | 0 4 files changed, 48 insertions(+), 40 deletions(-) delete mode 100644 src/tiles/FastRenderer.s diff --git a/src/Tiles.s b/src/Tiles.s index 0f9f7d9..b7d0201 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -114,8 +114,9 @@ InitTiles bra :out :fast lda #0 ; Initialize with Tile 0 - ldy #FastOverZA + ldy #FastProcs jsr _SetTileProcs + bra :out :dyn lda #0 ; Initialize with Tile 0 ldy #DynOverZA @@ -209,25 +210,26 @@ _SetTile ; Calculate the base tile proc selector from the tile Id stz procIdx - lda newTileId - clc - bit #TILE_PRIORITY_BIT + lda #TILE_PRIORITY_BIT + bit newTileId beq :low_priority - sec -:low_priority asl procIdx + lda #4 + sta procIdx +:low_priority - clc - bit #TILE_ID_MASK + lda #TILE_ID_MASK + bit newTileId bne :not_zero - sec -:not_zero asl procIdx + lda #2 + tsb procIdx +:not_zero - clc - bit #TILE_VFLIP_BIT + lda #TILE_VFLIP_BIT beq :no_vflip - sec -:no_vflip asl procIdx + lda #1 + tsb procIdx +:no_vflip ; Multiple by 6 to get the correct table entry index @@ -235,7 +237,7 @@ _SetTile lda procIdx asl adc procIdx - tay + sta procIdx ; Now integrate with the engine mode indicator @@ -270,7 +272,7 @@ _SetTile ; 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 - lda #FastProcs + ldy #FastProcs lda procIdx jsr _SetTileProcs jmp _PushDirtyTileX @@ -280,21 +282,18 @@ _SetTile ; are both Dynamic tiles or both Basic tiles, then we can use an optimized routine. Otherwise ; we must set the opcodes as well as the operands :setTileDyn - lda #DynProcs + brk $55 + ldy #DynProcs lda procIdx jsr _SetTileProcs jmp _PushDirtyTileX -:out - jmp _PushDirtyTileY ; on the next call to _ApplyTiles - ; X = Tile Store offset ; Y = Engine Mode Base Table address ; A = Table proc index ; ; see TileProcTables in static/TileStore.s -bnkPtr equ blttmp -tblPtr equ blttmp+4 +tblPtr equ blttmp _SetTileProcs ; Set a long pointer to this bank @@ -447,6 +446,7 @@ last_bit lda (SPRITE_VBUFF_PTR+{]1*2}),y clc ; pre-adjust these later adc _Sprites+TS_VBUFF_BASE+{]1*2} sta sprite_ptr0+{]2*4} + tyx jmp (K_TS_ONE_SPRITE,x) next_bit <<< @@ -472,6 +472,15 @@ endbit mac jmp ]3 <<< +endbit1 mac + lda (SPRITE_VBUFF_PTR+{]1*2}),y + clc ; pre-adjust these later + adc _Sprites+TS_VBUFF_BASE+{]1*2} + sta sprite_ptr0+{]2*4} + tyx + jmp (K_TS_ONE_SPRITE,x) + <<< + ; OPTIMIZATION: ; ; bit #$00FF ; Optimization to skip the first 8 bits if they are all zeros @@ -499,22 +508,22 @@ endbit mac ; ]4 address of four sprite process SpriteBitsToVBuffAddrs mac - dobit1 0;0;b_1_1;]1 - dobit1 1;0;b_2_1;]1 - dobit1 2;0;b_3_1;]1 - dobit1 3;0;b_4_1;]1 - dobit1 4;0;b_5_1;]1 - dobit1 5;0;b_6_1;]1 - dobit1 6;0;b_7_1;]1 - dobit1 7;0;b_8_1;]1 - dobit1 8;0;b_9_1;]1 - dobit1 9;0;b_10_1;]1 - dobit1 10;0;b_11_1;]1 - dobit1 11;0;b_12_1;]1 - dobit1 12;0;b_13_1;]1 - dobit1 13;0;b_14_1;]1 - dobit1 14;0;b_15_1;]1 - endbit 15;0;]1 + dobit1 0;0;b_1_1 + dobit1 1;0;b_2_1 + dobit1 2;0;b_3_1 + dobit1 3;0;b_4_1 + dobit1 4;0;b_5_1 + dobit1 5;0;b_6_1 + dobit1 6;0;b_7_1 + dobit1 7;0;b_8_1 + dobit1 8;0;b_9_1 + dobit1 9;0;b_10_1 + dobit1 10;0;b_11_1 + dobit1 11;0;b_12_1 + dobit1 12;0;b_13_1 + dobit1 13;0;b_14_1 + dobit1 14;0;b_15_1 + endbit1 15;0 b_1_1 dobit 1;1;b_2_2;]2 b_2_1 dobit 2;1;b_3_2;]2 diff --git a/src/Tool.s b/src/Tool.s index 8863e74..e154f98 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -368,7 +368,6 @@ _TSGetSeconds put render/Sprite1.s put render/Sprite2.s put tiles/DirtyTileQueue.s -; put tiles/FastRenderer.s put blitter/Horz.s put blitter/Vert.s put blitter/BG0.s diff --git a/src/blitter/TileProcs.s b/src/blitter/TileProcs.s index ad50277..df71401 100644 --- a/src/blitter/TileProcs.s +++ b/src/blitter/TileProcs.s @@ -85,7 +85,7 @@ _TBCopyTileMaskToCBuffV ; ; 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 variantsConstUnderZero -_TBConstTile0 tax +_TBConstTile0 _TBConstTileX lda #0 sta: $0001,y diff --git a/src/tiles/FastRenderer.s b/src/tiles/FastRenderer.s deleted file mode 100644 index e69de29..0000000 From 553fd3c02aaac50744440f3cc4a56b83e8a2dfa9 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 21 Jun 2022 07:29:18 -0500 Subject: [PATCH 56/82] Tweaking updated sprite dispatch flow --- demos/tool/App.Main.s | 247 +++++++++++++++++++++++++++++++-------- demos/tool/package.json | 2 +- src/Tiles.s | 44 ++++--- src/blitter/Tiles00000.s | 1 + src/render/Fast.s | 42 +++++++ src/render/Render.s | 2 +- src/render/Sprite1.s | 6 +- 7 files changed, 273 insertions(+), 71 deletions(-) diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index a8f2fb5..124b444 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -38,8 +38,6 @@ Flips equ 12 pea #256 pea #160 -; pea #176 -; pea #144 _GTESetScreenMode ; Load a tileset @@ -95,58 +93,57 @@ HERO_SPRITE equ SPRITE_16X16+1 cpx #MAX_SPRITES*2 bcc :sloop -; Manually fill in the 41x26 tiles of the TileStore with a test pattern of trees -; -; Tile 65 Tile 66 -; Tile 97 Tile 98 - stz 0 ; X - stz 2 ; Y +; Manually fill in the 41x26 tiles of the TileStore with a test pattern of trees -:tloop - ldx 0 - ldy 2 + jsr _fillTileStore - phx - phy - pea #65 - - inx - phx - phy - pea #66 + ldx #0 + ldy #0 + jsr _drawTree - cpy #24 - beq :skip_last - iny - phx - phy - pea #98 + ldx #3 + ldy #0 + jsr _drawTreeH - dex - phx - phy - pea #97 + ldx #0 + ldy #3 + jsr _drawTreeV - _GTESetTile - _GTESetTile -:skip_last - _GTESetTile - _GTESetTile + ldx #3 + ldy #3 + jsr _drawTreeHV - lda 0 - inc - inc - sta 0 - cmp #40 - bcc :tloop + ldx #9 + ldy #0 + jsr _drawTree - stz 0 - lda 2 - inc - inc - sta 2 - cmp #25 - bcc :tloop + ldx #9 + ldy #3 + jsr _drawTree + + ldx #12 + ldy #0 + jsr _drawTree + + ldx #12 + ldy #3 + jsr _drawTree + + ldx #6 + ldy #0 + jsr _drawTreeFront + ldx #6 + ldy #3 + jsr _drawTreeFront + ldx #6 + ldy #6 + jsr _drawTreeFront + ldx #3 + ldy #6 + jsr _drawTreeFront + ldx #0 + ldy #6 + jsr _drawTreeFront ; Initialize the frame counter @@ -450,6 +447,162 @@ GTEStartUp :ok3 rts +_fillTileStore + stz Tmp0 +:oloop + stz Tmp1 +:iloop + pei Tmp1 + pei Tmp0 + pea #129 + _GTESetTile + + lda Tmp1 + inc + sta Tmp1 + cmp #41 + bcc :iloop + + lda Tmp0 + inc + sta Tmp0 + cmp #26 + bcc :oloop + rts + +; Tile 65 Tile 66 +; Tile 97 Tile 98 + +_drawTreeFront + phx + phy + pea #65+TILE_PRIORITY_BIT + + inx + phx + phy + pea #66+TILE_PRIORITY_BIT + + iny + phx + phy + pea #98+TILE_PRIORITY_BIT + + dex + phx + phy + pea #97+TILE_PRIORITY_BIT + + _GTESetTile + _GTESetTile + _GTESetTile + _GTESetTile + rts + +_drawTree + phx + phy + pea #65 + + inx + phx + phy + pea #66 + + iny + phx + phy + pea #98 + + dex + phx + phy + pea #97 + + _GTESetTile + _GTESetTile + _GTESetTile + _GTESetTile + rts + +_drawTreeH + phx + phy + pea #66+TILE_HFLIP_BIT + + inx + phx + phy + pea #65+TILE_HFLIP_BIT + + iny + phx + phy + pea #97+TILE_HFLIP_BIT + + dex + phx + phy + pea #98+TILE_HFLIP_BIT + + _GTESetTile + _GTESetTile + _GTESetTile + _GTESetTile + rts + +_drawTreeV + phx + phy + pea #97+TILE_VFLIP_BIT + + inx + phx + phy + pea #98+TILE_VFLIP_BIT + + iny + phx + phy + pea #66+TILE_VFLIP_BIT + + dex + phx + phy + pea #65+TILE_VFLIP_BIT + + _GTESetTile + _GTESetTile + _GTESetTile + _GTESetTile + rts + +_drawTreeHV + phx + phy + pea #98+TILE_VFLIP_BIT+TILE_HFLIP_BIT + + inx + phx + phy + pea #97+TILE_VFLIP_BIT+TILE_HFLIP_BIT + + iny + phx + phy + pea #65+TILE_VFLIP_BIT+TILE_HFLIP_BIT + + dex + phx + phy + pea #66+TILE_VFLIP_BIT+TILE_HFLIP_BIT + + _GTESetTile + _GTESetTile + _GTESetTile + _GTESetTile + rts + MyUserId ds 2 ToolPath str '1/Tool160' FrameCount ds 2 diff --git a/demos/tool/package.json b/demos/tool/package.json index cbf2360..70528d0 100644 --- a/demos/tool/package.json +++ b/demos/tool/package.json @@ -12,7 +12,7 @@ }, "scripts": { "test": "npm run build && build-image.bat %npm_package_config_cadius% && %npm_package_config_gsport%", - "debug": "%npm_package_config_crossrunner% GTEToolDemo -Source MAINSEG_Output.txt -Debug -CompatibilityLayer", + "debug": "%npm_package_config_crossrunner% GTEToolDemo -Source MAINSEG_Output.txt -Debug -CompatibilityLayer -Map App.s", "build": "npm run build:tool && npm run build:sys16", "build:sys16": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.s", "build:tool": "%npm_package_config_merlin32% -V %npm_package_config_macros% ../../src/Master.s" diff --git a/src/Tiles.s b/src/Tiles.s index b7d0201..bdbf5d0 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -217,28 +217,20 @@ _SetTile lda #4 sta procIdx :low_priority - lda #TILE_ID_MASK bit newTileId - bne :not_zero + beq :is_zero lda #2 tsb procIdx -:not_zero +:is_zero lda #TILE_VFLIP_BIT + bit newTileId beq :no_vflip lda #1 tsb procIdx :no_vflip -; Multiple by 6 to get the correct table entry index - - asl procIdx - lda procIdx - asl - adc procIdx - sta procIdx - ; Now integrate with the engine mode indicator lda EngineMode @@ -296,12 +288,22 @@ _SetTile tblPtr equ blttmp _SetTileProcs -; Set a long pointer to this bank - sty tblPtr - clc +; Multiple the proc index by 6 to get the correct table entry offset + + asl + sta tblPtr + asl adc tblPtr sta tblPtr +; Add this offset to the base table address + + tya + adc tblPtr + sta tblPtr + +; Set the pointer to this bank + phk phk pla @@ -350,12 +352,16 @@ _SetTileProcs FastProcs FastOverZA dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 FastOverZV dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 -FastOverNA dw _TBCopyDataFast,GenericOverAFast,_OneSpriteFastOverA -FastOverNV dw _TBCopyDataVFast,GenericOverVFast,_OneSpriteFastOverV +FastOverNA dw _TBCopyDataAFast,GenericOverZero,_OneSpriteFastOverA +FastOverNV dw _TBCopyDataVFast,GenericOverZero,_OneSpriteFastOverV +;FastOverNA dw _TBCopyDataAFast,GenericOverAFast,_OneSpriteFastOverA +;FastOverNV dw _TBCopyDataVFast,GenericOverVFast,_OneSpriteFastOverV FastUnderZA dw _TBConstTile0,GenericUnderZero,GenericUnderZero FastUnderZV dw _TBConstTile0,GenericUnderZero,GenericUnderZero -FastUnderNA dw _TBCopyDataFast,GenericUnderAFast,_OneSpriteFastUnderA -FastUnderNV dw _TBCopyDataVFast,GenericUnderVFast,_OneSpriteFastUnderV +FastUnderNA dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 +FastUnderNV dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 +;FastUnderNA dw _TBCopyDataFast,GenericUnderAFast,_OneSpriteFastUnderA +;FastUnderNV dw _TBCopyDataVFast,GenericUnderVFast,_OneSpriteFastUnderV DynProcs DynOverZA @@ -483,7 +489,7 @@ endbit1 mac ; OPTIMIZATION: ; -; bit #$00FF ; Optimization to skip the first 8 bits if they are all zeros +; bit #$00FF ; Skip the first 8 bits if they are all zeros ; bne norm_entry ; xba ; jmp skip_entry diff --git a/src/blitter/Tiles00000.s b/src/blitter/Tiles00000.s index 7921c2b..dce4fae 100644 --- a/src/blitter/Tiles00000.s +++ b/src/blitter/Tiles00000.s @@ -41,6 +41,7 @@ _TBSolidTile_VH ; This is called via a JMP (abs,x) with an extra byte on the stack that holds the bank ; register value. This must be restored prior to returning _TBCopyDataFast +_TBCopyDataAFast tax _TBCopyDataFastX ]line equ 0 diff --git a/src/render/Fast.s b/src/render/Fast.s index 1b3c41c..985da2d 100644 --- a/src/render/Fast.s +++ b/src/render/Fast.s @@ -146,3 +146,45 @@ GenericUnderZero plb rts + +; Simple pair of routines that copies just the tile data to the direct page workspace. Data Bank +; must be set to the TileData bank in entry. +; +; Preserves the X-register +FastCopyTileDataA + ldy TileStore+TS_TILE_ADDR,x ; load the tile address + pei DP2_TILEDATA_AND_TILESTORE_BANKS + plb ; set to the tiledata bank + +]line equ 0 + lup 8 + lda tiledata+{]line*4},y + sta tmp_tile_data+{]line*4} + + lda tiledata+{]line*4}+2,y + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + + plb + rts + +FastCopyTileDataV + ldy TileStore+TS_TILE_ADDR,x ; load the tile address + pei DP2_TILEDATA_AND_TILESTORE_BANKS + plb ; set to the tiledata bank + +]src equ 7 +]dest equ 0 + lup 8 + lda tiledata+{]src*4},y + sta tmp_tile_data+{]dest*4} + + lda tiledata+{]src*4}+2,y + sta tmp_tile_data+{]dest*4}+2 +]src equ ]src-1 +]dest equ ]dest+1 + --^ + + plb + rts diff --git a/src/render/Render.s b/src/render/Render.s index d0a6cc5..17722a7 100644 --- a/src/render/Render.s +++ b/src/render/Render.s @@ -65,7 +65,7 @@ CopyTileToDPSprite ; must be set to the TileData bank in entry. ; ; Preserves the X-register -CopyTileDataToDP +CopyTileDataToDPA ]line equ 0 lup 8 lda tiledata+{]line*4},y diff --git a/src/render/Sprite1.s b/src/render/Sprite1.s index 28d433a..6217cbf 100644 --- a/src/render/Sprite1.s +++ b/src/render/Sprite1.s @@ -34,11 +34,11 @@ _OneSpriteFastOver0 ; The 1-sprite dispatch prserves the X-register, so it already points to the TileStore _OneSpriteFastOverV - jsr CopyTileDataToDPV + jsr FastCopyTileDataV bra _OneSpriteFastOver _OneSpriteFastOverA - jsr CopyTileDataToDP + jsr FastCopyTileDataA _OneSpriteFastOver lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line @@ -86,7 +86,7 @@ _OneSpriteSlowOver0 ; Slow variant for regular tile. _OneSpriteSlowOver - jsr CopyTileDataToDP + jsr CopyTileDataToDPA 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. From 400851259da3719d6cfd726ed7d3173f7e4b38b7 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 21 Jun 2022 10:06:17 -0500 Subject: [PATCH 57/82] More minor fixes --- src/Tiles.s | 2 +- src/render/Fast.s | 46 ++++++++++++++++++++++++++++++++++++++++++++ src/render/Sprite1.s | 8 ++++---- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/Tiles.s b/src/Tiles.s index bdbf5d0..02e2f62 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -358,7 +358,7 @@ FastOverNV dw _TBCopyDataVFast,GenericOverZero,_OneSpriteFastOverV ;FastOverNV dw _TBCopyDataVFast,GenericOverVFast,_OneSpriteFastOverV FastUnderZA dw _TBConstTile0,GenericUnderZero,GenericUnderZero FastUnderZV dw _TBConstTile0,GenericUnderZero,GenericUnderZero -FastUnderNA dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 +FastUnderNA dw _TBConstTile0,GenericOverZero,_OneSpriteFastUnderA FastUnderNV dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 ;FastUnderNA dw _TBCopyDataFast,GenericUnderAFast,_OneSpriteFastUnderA ;FastUnderNV dw _TBCopyDataVFast,GenericUnderVFast,_OneSpriteFastUnderV diff --git a/src/render/Fast.s b/src/render/Fast.s index 985da2d..586e3ca 100644 --- a/src/render/Fast.s +++ b/src/render/Fast.s @@ -188,3 +188,49 @@ FastCopyTileDataV plb rts + +FastCopyTileDataAndMaskA + ldy TileStore+TS_TILE_ADDR,x ; load the tile address + pei DP2_TILEDATA_AND_TILESTORE_BANKS + plb ; set to the tiledata bank + +]line equ 0 + lup 8 + lda tiledata+{]line*4},y + sta tmp_tile_data+{]line*4} + lda tiledata+{]line*4}+32,y + sta tmp_tile_mask+{]line*4} + + lda tiledata+{]line*4}+2,y + sta tmp_tile_data+{]line*4}+2 + lda tiledata+{]line*4}+32+2,y + sta tmp_tile_mask+{]line*4}+2 +]line equ ]line+1 + --^ + + plb + rts + +FastCopyTileDataAndMaskV + ldy TileStore+TS_TILE_ADDR,x ; load the tile address + pei DP2_TILEDATA_AND_TILESTORE_BANKS + plb ; set to the tiledata bank + +]src equ 7 +]dest equ 0 + lup 8 + lda tiledata+{]src*4},y + sta tmp_tile_data+{]dest*4} + lda tiledata+{]src*4}+32,y + sta tmp_tile_mask+{]dest*4} + + lda tiledata+{]src*4}+2,y + sta tmp_tile_data+{]dest*4}+2 + lda tiledata+{]src*4}+32+2,y + sta tmp_tile_mask+{]dest*4}+2 +]src equ ]src-1 +]dest equ ]dest+1 + --^ + + plb + rts diff --git a/src/render/Sprite1.s b/src/render/Sprite1.s index 6217cbf..57350ff 100644 --- a/src/render/Sprite1.s +++ b/src/render/Sprite1.s @@ -128,11 +128,11 @@ _FillPEAOpcode ; Drawing under the zero tile is the same as not drawing a sprite fo both the fast and slow cases _OneSpriteFastUnderA - jsr _CopyTileDataAndMaskToDP + jsr FastCopyTileDataAndMaskA bra _OneSpriteFastUnder _OneSpriteFastUnderV - jsr _CopyTileDataAndMaskToDPV + jsr FastCopyTileDataAndMaskV _OneSpriteFastUnder lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line @@ -144,12 +144,12 @@ _OneSpriteFastUnder ]line equ 0 lup 8 ldal spritedata+{]line*SPRITE_PLANE_SPAN},x - ora tmp_tile_mask+{]line*4} + and tmp_tile_mask+{]line*4} ora tmp_tile_data+{]line*4} sta: $0004+{]line*$1000},y ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x - ora tmp_tile_mask+{]line*4}+2 + and tmp_tile_mask+{]line*4}+2 ora tmp_tile_data+{]line*4}+2 sta: $0001+{]line*$1000},y ]line equ ]line+1 From 2820d318a025edb66a754e7cbba10b940d3f401c Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 21 Jun 2022 11:23:06 -0500 Subject: [PATCH 58/82] Final cleanups for Fast mode --- src/Tiles.s | 10 ++++------ src/render/Fast.s | 8 ++++---- src/render/Render.s | 12 ++++++------ src/render/Sprite1.s | 7 ++----- src/render/Sprite2.s | 6 ++++++ 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/Tiles.s b/src/Tiles.s index 02e2f62..4731ab5 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -352,14 +352,12 @@ _SetTileProcs FastProcs FastOverZA dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 FastOverZV dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 -FastOverNA dw _TBCopyDataAFast,GenericOverZero,_OneSpriteFastOverA -FastOverNV dw _TBCopyDataVFast,GenericOverZero,_OneSpriteFastOverV -;FastOverNA dw _TBCopyDataAFast,GenericOverAFast,_OneSpriteFastOverA -;FastOverNV dw _TBCopyDataVFast,GenericOverVFast,_OneSpriteFastOverV +FastOverNA dw _TBCopyDataAFast,GenericOverAFast,_OneSpriteFastOverA +FastOverNV dw _TBCopyDataVFast,GenericOverVFast,_OneSpriteFastOverV FastUnderZA dw _TBConstTile0,GenericUnderZero,GenericUnderZero FastUnderZV dw _TBConstTile0,GenericUnderZero,GenericUnderZero -FastUnderNA dw _TBConstTile0,GenericOverZero,_OneSpriteFastUnderA -FastUnderNV dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 +FastUnderNA dw _TBCopyDataAFast,GenericUnderAFast,_OneSpriteFastUnderA +FastUnderNV dw _TBCopyDataVFast,GenericUnderVFast,_OneSpriteFastUnderV ;FastUnderNA dw _TBCopyDataFast,GenericUnderAFast,_OneSpriteFastUnderA ;FastUnderNV dw _TBCopyDataVFast,GenericUnderVFast,_OneSpriteFastUnderV diff --git a/src/render/Fast.s b/src/render/Fast.s index 586e3ca..b1f2fbb 100644 --- a/src/render/Fast.s +++ b/src/render/Fast.s @@ -90,12 +90,12 @@ GenericUnderAFast lup 8 lda tmp_sprite_data+{]line*4} andl tiledata+{]line*4}+32,x - oral tiledata+{]line*4}+32,x + oral tiledata+{]line*4},x sta: $0004+{]line*$1000},y lda tmp_sprite_data+{]line*4}+2 andl tiledata+{]line*4}+32+2,x - oral tiledata+{]line*4}+32+2,x + oral tiledata+{]line*4}+2,x sta: $0001+{]line*$1000},y ]line equ ]line+1 --^ @@ -116,12 +116,12 @@ GenericUnderVFast lup 8 lda tmp_sprite_data+{]line*4} andl tiledata+{]src*4}+32,x - oral tiledata+{]src*4}+32,x + oral tiledata+{]src*4},x sta: $0004+{]line*$1000},y lda tmp_sprite_data+{]line*4}+2 andl tiledata+{]src*4}+32+2,x - oral tiledata+{]src*4}+32+2,x + oral tiledata+{]src*4}+2,x sta: $0001+{]line*$1000},y ]src equ ]src-1 ]dest equ ]dest+1 diff --git a/src/render/Render.s b/src/render/Render.s index 17722a7..ed74ca2 100644 --- a/src/render/Render.s +++ b/src/render/Render.s @@ -22,17 +22,17 @@ _RenderTile ; flatten the sprite data into the direct page workspace and then pass control ; to the configurable routine which is set in SetTile and knows what to do ; based on the tile properties (over/under, engine mode, etc.) +; +; NOTE: Could pull the CopyXXXSprites function inline and same the 3 cycles for the JMP, +; - or - put the TYX into the macro and jump directly from there. TwoSprites tyx - jsr CopyTwoSpritesDataAndMaskToDP - jmp (K_TS_SPRITE_TILE_DISP,x) + jmp CopyTwoSpritesDataAndMaskToDP ThreeSprites tyx - jsr CopyThreeSpritesDataAndMaskToDP - jmp (K_TS_SPRITE_TILE_DISP,x) + jmp CopyThreeSpritesDataAndMaskToDP FourSprites tyx - jsr CopyFourSpritesDataAndMaskToDP - jmp (K_TS_SPRITE_TILE_DISP,x) + jmp CopyFourSpritesDataAndMaskToDP ; Helper functions (and macros) diff --git a/src/render/Sprite1.s b/src/render/Sprite1.s index 57350ff..cea6c33 100644 --- a/src/render/Sprite1.s +++ b/src/render/Sprite1.s @@ -27,11 +27,8 @@ _OneSpriteFastOver0 plb ; Restore the TileStore bank rts -; Next implementation; drawing a sprite onto a regular tile. In this case we need to make -; use of the K_TS_COPY_TILE_DATA function because that takes care of copying the correct -; tile data into the direct page buffer. - -; The 1-sprite dispatch prserves the X-register, so it already points to the TileStore +; Next implementation; drawing a sprite onto a regular tile. The 1-sprite dispatch preerves the +; X-register, so it already points to the TileStore _OneSpriteFastOverV jsr FastCopyTileDataV diff --git a/src/render/Sprite2.s b/src/render/Sprite2.s index 6f296da..04c3467 100644 --- a/src/render/Sprite2.s +++ b/src/render/Sprite2.s @@ -80,9 +80,13 @@ CopyTwoSpritesDataToDP ]line equ ]line+1 --^ rts + CopyFourSpritesDataAndMaskToDP CopyThreeSpritesDataAndMaskToDP CopyTwoSpritesDataAndMaskToDP + pei DP2_SPRITEDATA_AND_TILESTORE_BANKS + plb + ]line equ 0 lup 8 ldy #{]line*SPRITE_PLANE_SPAN} @@ -98,5 +102,7 @@ CopyTwoSpritesDataAndMaskToDP sta tmp_sprite_mask+{]line*4}+2 ]line equ ]line+1 --^ + + plb jmp (K_TS_SPRITE_TILE_DISP,x) From a91f39aab8670437ba105fc261a77dbda5fc9159 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 21 Jun 2022 13:33:17 -0500 Subject: [PATCH 59/82] Remove dead code and implement Slow tile renderers in terms of the Fast once to avoid code bloat --- src/Tiles.s | 60 ++++++++++++++-------- src/Tool.s | 1 + src/blitter/TileProcs.s | 3 +- src/blitter/Tiles00000.s | 46 +++++++++-------- src/render/Fast.s | 46 ++++++++++++----- src/render/Render.s | 74 --------------------------- src/render/Slow.s | 89 ++++++++++++++++++++++---------- src/render/Sprite1.s | 106 +++++++++++++++------------------------ src/render/Sprite2.s | 73 +-------------------------- src/render/Sprite3.s | 36 ------------- 10 files changed, 202 insertions(+), 332 deletions(-) diff --git a/src/Tiles.s b/src/Tiles.s index 4731ab5..565e1df 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -119,7 +119,7 @@ InitTiles bra :out :dyn lda #0 ; Initialize with Tile 0 - ldy #DynOverZA + ldy #FastProcs jsr _SetTileProcs :out @@ -274,8 +274,8 @@ _SetTile ; are both Dynamic tiles or both Basic tiles, then we can use an optimized routine. Otherwise ; we must set the opcodes as well as the operands :setTileDyn - brk $55 - ldy #DynProcs + +; ldy #DynProcs lda procIdx jsr _SetTileProcs jmp _PushDirtyTileX @@ -350,26 +350,42 @@ _SetTileProcs ; ; FastProcs -FastOverZA dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 -FastOverZV dw _TBConstTile0,GenericOverZero,_OneSpriteFastOver0 -FastOverNA dw _TBCopyDataAFast,GenericOverAFast,_OneSpriteFastOverA -FastOverNV dw _TBCopyDataVFast,GenericOverVFast,_OneSpriteFastOverV -FastUnderZA dw _TBConstTile0,GenericUnderZero,GenericUnderZero -FastUnderZV dw _TBConstTile0,GenericUnderZero,GenericUnderZero -FastUnderNA dw _TBCopyDataAFast,GenericUnderAFast,_OneSpriteFastUnderA -FastUnderNV dw _TBCopyDataVFast,GenericUnderVFast,_OneSpriteFastUnderV -;FastUnderNA dw _TBCopyDataFast,GenericUnderAFast,_OneSpriteFastUnderA -;FastUnderNV dw _TBCopyDataVFast,GenericUnderVFast,_OneSpriteFastUnderV +FastOverZA dw ConstTile0Fast,SpriteOver0Fast,OneSpriteFastOver0 +FastOverZV dw ConstTile0Fast,SpriteOver0Fast,OneSpriteFastOver0 +FastOverNA dw CopyTileAFast,SpriteOverAFast,OneSpriteFastOverA +FastOverNV dw CopyTileVFast,SpriteOverVFast,OneSpriteFastOverV +FastUnderZA dw ConstTile0Fast,SpriteUnder0Fast,SpriteUnder0Fast +FastUnderZV dw ConstTile0Fast,SpriteUnder0Fast,SpriteUnder0Fast +FastUnderNA dw CopyTileAFast,SpriteUnderAFast,OneSpriteFastUnderA +FastUnderNV dw CopyTileVFast,SpriteUnderVFast,OneSpriteFastUnderV + +; "Slow" procs. These are duplicates of the "Fast" functions, but also +; set the PEA opcode in all cases. +SlowProcs +SlowOverZA dw ConstTile0Slow,SpriteOver0Slow,OneSpriteSlowOver0 +SlowOverZV dw ConstTile0Slow,SpriteOver0Slow,OneSpriteSlowOver0 +SlowOverNA dw CopyTileASlow,SpriteOverASlow,OneSpriteSlowOverA +SlowOverNV dw CopyTileVSlow,SpriteOverVSlow,OneSpriteSlowOverV +SlowUnderZA dw ConstTile0Slow,SpriteUnder0Slow,SpriteUnder0Slow +SlowUnderZV dw ConstTile0Slow,SpriteUnder0Slow,SpriteUnder0Slow +SlowUnderNA dw CopyTileASlow,SpriteUnderASlow,OneSpriteSlowUnderA +SlowUnderNV dw CopyTileVSlow,SpriteUnderVSlow,OneSpriteSlowUnderV + +; "Dynamic" procs. These are the specialized routines for a dynamic tiles +; that does not need to worry about a second background. Because dynamic +; tiles don't support horizontal or vertical flipping, there are only two +; sets of procedures: one for Over and one for Under. +;DynOver dw _TBDynamicTile,DynamicOver,_OneSpriteDynamicOver +;DynUnder dw _TBDynamicTile,DynamicUnder,_OneSpriteDynamicUnder + +; "Two Layer" procs. These are the most complex procs. Generally, +; all of these methods are implemented by building up the data +; and mask into the direct page space and then calling a common +; function to create the complex code fragments in the code field. +; There is not a lot of opportuinity to optimize these routines. + + -DynProcs -DynOverZA -DynOverZV -DynOverNA -DynOverNV -DynUnderZA -DynUnderZV -DynUnderNA -DynUnderNV ; SetBG0XPos ; diff --git a/src/Tool.s b/src/Tool.s index e154f98..b92dc7c 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -365,6 +365,7 @@ _TSGetSeconds put Render.s put render/Render.s put render/Fast.s + put render/Slow.s put render/Sprite1.s put render/Sprite2.s put tiles/DirtyTileQueue.s diff --git a/src/blitter/TileProcs.s b/src/blitter/TileProcs.s index df71401..b5c77db 100644 --- a/src/blitter/TileProcs.s +++ b/src/blitter/TileProcs.s @@ -84,8 +84,7 @@ _TBCopyTileMaskToCBuffV ; _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 variantsConstUnderZero -_TBConstTile0 +; fill in solid colors, so there are no specialized horizontal or verical flipped variants _TBConstTileX lda #0 sta: $0001,y diff --git a/src/blitter/Tiles00000.s b/src/blitter/Tiles00000.s index dce4fae..19fc409 100644 --- a/src/blitter/Tiles00000.s +++ b/src/blitter/Tiles00000.s @@ -40,10 +40,9 @@ _TBSolidTile_VH ; This is called via a JMP (abs,x) with an extra byte on the stack that holds the bank ; register value. This must be restored prior to returning -_TBCopyDataFast -_TBCopyDataAFast +CopyTileAFast tax -_TBCopyDataFastX +_CopyTileAFast ]line equ 0 lup 8 ldal tiledata+{]line*4},x @@ -55,25 +54,14 @@ _TBCopyDataFastX plb rts -_TBCopyDataSlow +CopyTileASlow tax - jsr _TBFillPEAOpcode - jmp _TBCopyDataFastX + jsr FillPEAOpcode + jmp _CopyTileAFast -_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 - -_TBCopyDataVFast +CopyTileVFast tax -_TBCopyDataVFastX +_CopyTileVFast ]src equ 7 ]dest equ 0 lup 8 @@ -87,10 +75,24 @@ _TBCopyDataVFastX plb rts -_TBCopyDataVSlow +CopyTileVSlow tax - jsr _TBFillPEAOpcode - jmp _TBCopyDataVFastX + jsr FillPEAOpcode + jmp _CopyTileVFast + + + +; Old routines +_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 _TBCopyDataV ]src equ 7 diff --git a/src/render/Fast.s b/src/render/Fast.s index b1f2fbb..9391692 100644 --- a/src/render/Fast.s +++ b/src/render/Fast.s @@ -2,13 +2,29 @@ ; there are no dynamic tile or two layer tiles enabled, so all of the tiles are comprised ; of PEA opcodes. These functions take advantage of this as the fact that masks are ; not needed to improve rendering speed. -; -; The following functions are defined here -; -; GenericOverAFast : Places data from tmp_sprite_data on top of the TileStore's tile -; GenericUnderAFast : Places the TileStore's tile on top of tmp_sprite_data -GenericOverAFast +ConstTile0Fast + lda #0 + sta: $0001,y + sta: $0004,y + sta $1001,y + sta $1004,y + sta $2001,y + sta $2004,y + sta $3001,y + sta $3004,y + sta $4001,y + sta $4004,y + sta $5001,y + sta $5004,y + sta $6001,y + sta $6004,y + sta $7001,y + sta $7004,y + plb + rts + +SpriteOverAFast 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 @@ -16,6 +32,7 @@ GenericOverAFast tax plb +_SpriteOverAFast ; Alternate entry point for the "Slow" routines ]line equ 0 lup 8 ldal tiledata+{]line*4},x @@ -33,7 +50,7 @@ GenericOverAFast plb rts -GenericOverVFast +SpriteOverVFast 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 @@ -41,6 +58,7 @@ GenericOverVFast tax plb +_SpriteOverVFast ]src equ 7 ]dest equ 0 lup 8 @@ -59,12 +77,13 @@ GenericOverVFast plb rts -GenericOverZero +SpriteOver0Fast 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 plb +_SpriteOver0Fast ]line equ 0 lup 8 lda tmp_sprite_data+{]line*4} @@ -78,7 +97,7 @@ GenericOverZero plb rts -GenericUnderAFast +SpriteUnderAFast 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 @@ -86,6 +105,7 @@ GenericUnderAFast tax plb +_SpriteUnderAFast ]line equ 0 lup 8 lda tmp_sprite_data+{]line*4} @@ -103,7 +123,7 @@ GenericUnderAFast plb rts -GenericUnderVFast +SpriteUnderVFast 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 @@ -111,6 +131,7 @@ GenericUnderVFast tax plb +_SpriteUnderVFast ]src equ 7 ]dest equ 0 lup 8 @@ -130,13 +151,14 @@ GenericUnderVFast plb rts -GenericUnderZero +SpriteUnder0Fast 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 plb - lda #0 +_SpriteUnder0Fast + lda #0 ]line equ 0 lup 8 sta: $0004+{]line*$1000},y diff --git a/src/render/Render.s b/src/render/Render.s index ed74ca2..1d57fed 100644 --- a/src/render/Render.s +++ b/src/render/Render.s @@ -34,32 +34,6 @@ ThreeSprites tyx FourSprites tyx jmp CopyFourSpritesDataAndMaskToDP -; Helper functions (and macros) - -; CopyTileToDP -- executes the K_TS_COPY_TILE_DATA routine. This may copy just data or data+mask -; information to the direct page -_CopyTileToDP mac - ldy TileStore+TS_TILE_ADDR,x ; load the tile address - pei DP2_TILEDATA_AND_TILESTORE_BANKS - plb ; set to the tiledata bank - jsr (K_TS_COPY_TILE_DATA,x) ; preserves X-reg - plb - <<< -CopyTileToDP - _CopyTileToDP - rts - -; CopyTileToDPSprite -- same as above, but returns with the Data BAnk set to the sprite data bank -_CopyTileToDPSprite mac - ldy TileStore+TS_TILE_ADDR,x ; load the tile address - pei DP2_TILEDATA_AND_SPRITEDATA_BANKS - plb ; set to the tiledata bank - jsr (K_TS_COPY_TILE_DATA,x) ; preserves X-reg - plb - <<< -CopyTileToDPSprite - _CopyTileToDPSprite - rts ; Simple pair of routines that copies just the tile data to the direct page workspace. Data Bank ; must be set to the TileData bank in entry. @@ -77,54 +51,6 @@ CopyTileDataToDPA --^ rts -CopyTileDataToDPV -]src equ 7 -]dest equ 0 - lup 8 - lda tiledata+{]src*4},y - sta tmp_tile_data+{]dest*4} - - lda tiledata+{]src*4}+2,y - sta tmp_tile_data+{]dest*4}+2 -]src equ ]src-1 -]dest equ ]dest+1 - --^ - rts - -; Copy both the tile and mask data to the driect page space -_CopyTileDataAndMaskToDP -]line equ 0 - lup 8 - lda tiledata+{]line*4},y - sta tmp_tile_data+{]line*4} - lda tiledata+{]line*4}+32,y - sta tmp_tile_mask+{]line*4} - - lda tiledata+{]line*4}+2,y - sta tmp_tile_data+{]line*4}+2 - lda tiledata+{]line*4}+32+2,y - sta tmp_tile_mask+{]line*4}+2 -]line equ ]line+1 - --^ - rts - -_CopyTileDataAndMaskToDPV -]src equ 7 -]dest equ 0 - lup 8 - lda tiledata+{]src*4},y - sta tmp_tile_data+{]dest*4} - lda tiledata+{]src*4}+32,y - sta tmp_tile_mask+{]dest*4} - - lda tiledata+{]src*4}+2,y - sta tmp_tile_data+{]dest*4}+2 - lda tiledata+{]src*4}+32+2,y - sta tmp_tile_mask+{]dest*4}+2 -]src equ ]src-1 -]dest equ ]dest+1 - --^ - rts ; Given a populate tmp_sprite_data buffer to use as a base, merge it with a tile and write to the ; code field diff --git a/src/render/Slow.s b/src/render/Slow.s index a6a7c8a..4254fc7 100644 --- a/src/render/Slow.s +++ b/src/render/Slow.s @@ -6,46 +6,81 @@ ; GenericOverSlow : Places data from tmp_sprite_data on top of the TileStore's tile ; GenericUnderSlow : Places the TileStore's tile on top of tmp_sprite_data -GenericOverSlow +ConstTile0Slow + jsr FillPEAOpcode + jmp ConstTile0Fast + +SpriteOverASlow 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 tax + plb + jsr FillPEAOpcode + jmp _SpriteOverAFast -]line equ 0 - lup 8 - ldal tiledata+{]line*4},x - and tmp_sprite_mask+{]line*4} - ora tmp_sprite_data+{]line*4} - sta: $0004+{]line*$1000},y - - ldal tiledata+{]line*4}+2,x - and tmp_sprite_mask+{]line*4}+2 - ora tmp_sprite_data+{]line*4}+2 - sta: $0001+{]line*$1000},y -]line equ ]line+1 - --^ - jmp _FillPEAOpcode - -GenericUnderSlow +SpriteOverVSlow 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 tax + plb + jsr FillPEAOpcode + jmp _SpriteOverVFast +SpriteOver0Slow + 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 + plb + jsr FillPEAOpcode + jmp _SpriteOver0Fast + +SpriteUnderASlow + 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 + tax + plb + jsr FillPEAOpcode + jmp _SpriteUnderAFast + +SpriteUnderVSlow + 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 + tax + plb + jsr FillPEAOpcode + jmp _SpriteUnderVFast + +SpriteUnder0Slow + 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 + plb + jsr FillPEAOpcode + jmp _SpriteUnder0Fast + +; Helper function; no stack manipulation +FillPEAOpcode + sep #$20 + lda #$F4 ]line equ 0 lup 8 - lda tmp_sprite_data+{]line*4} - andl tiledata+{]line*4}+32,x - oral tiledata+{]line*4}+32,x - sta: $0004+{]line*$1000},y - - lda tmp_sprite_data+{]line*4}+2 - andl tiledata+{]line*4}+32+2,x - oral tiledata+{]line*4}+32+2,x - sta: $0001+{]line*$1000},y + sta: $0000+{]line*$1000},y + sta: $0003+{]line*$1000},y ]line equ ]line+1 --^ - jmp _FillPEAOpcode + rep #$20 + rts + +; This is a dtub; will be removed eventually +_FillPEAOpcode + jsr FillPEAOpcode + plb ; Restore the TileStore bank + rts \ No newline at end of file diff --git a/src/render/Sprite1.s b/src/render/Sprite1.s index cea6c33..57163c0 100644 --- a/src/render/Sprite1.s +++ b/src/render/Sprite1.s @@ -8,13 +8,14 @@ ; The simplest implementation. When drawing a sprite over Tile 0 in FAST mode, we can just copy the ; sprite data into the coe field directly. -_OneSpriteFastOver0 +OneSpriteFastOver0 ldy TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line phy ; 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 tax ; VBuff address from SpriteBitsToVBuffAddrs macro plb ; set to the code field bank +_OneSpriteFastOver0 ]line equ 0 lup 8 ldal spritedata+{]line*SPRITE_PLANE_SPAN},x @@ -30,11 +31,11 @@ _OneSpriteFastOver0 ; Next implementation; drawing a sprite onto a regular tile. The 1-sprite dispatch preerves the ; X-register, so it already points to the TileStore -_OneSpriteFastOverV +OneSpriteFastOverV jsr FastCopyTileDataV bra _OneSpriteFastOver -_OneSpriteFastOverA +OneSpriteFastOverA jsr FastCopyTileDataA _OneSpriteFastOver @@ -44,6 +45,8 @@ _OneSpriteFastOver ldx sprite_ptr0 plb +_OneSpriteFastOverA +_OneSpriteFastOverV ]line equ 0 lup 8 lda tmp_tile_data+{]line*4} @@ -62,73 +65,45 @@ _OneSpriteFastOver ; This is the "SLOW" variant that fills in the PEA opcode specialized for Tile 0. -_OneSpriteSlowOver0 +OneSpriteSlowOver0 ldy TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line phy ; 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 tax ; VBuff address from SpriteBitsToVBuffAddrs macro plb ; set to the code field bank - -]line equ 0 - lup 8 - ldal spritedata+{]line*SPRITE_PLANE_SPAN},x - sta: $0004+{]line*$1000},y - ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x - sta: $0001+{]line*$1000},y -]line equ ]line+1 - --^ - - jmp _FillPEAOpcode + jsr FillPEAOpcode + jmp _OneSpriteFastOver0 ; Slow variant for regular tile. - -_OneSpriteSlowOver - jsr CopyTileDataToDPA - +OneSpriteSlowOverV + jsr FastCopyTileDataV 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 ldx sprite_ptr0 plb + jsr FillPEAOpcode + jmp _OneSpriteFastOverV -]line equ 0 - lup 8 - lda tmp_tile_data+{]line*4} - andl spritemask+{]line*SPRITE_PLANE_SPAN},x - oral spritedata+{]line*SPRITE_PLANE_SPAN},x - sta: $0004+{]line*$1000},y - - lda tmp_tile_data+{]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 - --^ - -; Fall through here to give the common case a small boost -_FillPEAOpcode - sep #$20 - lda #$F4 -]line equ 0 - lup 8 - sta: $0000+{]line*$1000},y - sta: $0003+{]line*$1000},y -]line equ ]line+1 - --^ - rep #$20 - - plb ; Restore the TileStore bank - rts +OneSpriteSlowOverA + jsr FastCopyTileDataA + 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 + ldx sprite_ptr0 + plb + jsr FillPEAOpcode + jmp _OneSpriteFastOverA ;------------------------------ ; Section: Below Tile Renderers ; Drawing under the zero tile is the same as not drawing a sprite fo both the fast and slow cases -_OneSpriteFastUnderA +OneSpriteFastUnderA jsr FastCopyTileDataAndMaskA bra _OneSpriteFastUnder -_OneSpriteFastUnderV +OneSpriteFastUnderV jsr FastCopyTileDataAndMaskV _OneSpriteFastUnder @@ -138,6 +113,8 @@ _OneSpriteFastUnder ldx sprite_ptr0 plb +_OneSpriteFastUnderA +_OneSpriteFastUnderV ]line equ 0 lup 8 ldal spritedata+{]line*SPRITE_PLANE_SPAN},x @@ -155,23 +132,22 @@ _OneSpriteFastUnder plb rts -_OneSpriteSlowUnder0 +OneSpriteSlowUnderA + jsr FastCopyTileDataAndMaskA 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 ; set the code field bank - jmp (K_TS_BASE_TILE_DISP,x) ; go to the tile copy routine + ldx sprite_ptr0 + plb + jsr FillPEAOpcode + jmp _OneSpriteFastUnderA -;-------------------------------- -; Helper functions for one Sprite -CopyOneSpriteDataToDP -]line equ 0 - lup 8 - ldal spritedata+{]line*SPRITE_PLANE_SPAN},x - sta tmp_sprite_data+{]line*4} - ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_sprite_data+{]line*4}+2 -]line equ ]line+1 - --^ - rts \ No newline at end of file +OneSpriteSlowUnderV + jsr FastCopyTileDataAndMaskV + 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 + ldx sprite_ptr0 + plb + jsr FillPEAOpcode + jmp _OneSpriteFastUnderV diff --git a/src/render/Sprite2.s b/src/render/Sprite2.s index 04c3467..452beec 100644 --- a/src/render/Sprite2.s +++ b/src/render/Sprite2.s @@ -1,13 +1,4 @@ -; Specialize routines for handling two sprites. Like Sprite3.s and Sprite4.s there are four -; variants -- one to handle over / under sprite orders and one each for whether the mask needs -; to be used or not. -TwoSpriteLine mac - db $37,sprite_ptr1 ; and [sprite_ptr1],y - ora (sprite_ptr1),y - db $37,sprite_ptr0 ; and [sprite_ptr0],y - ora (sprite_ptr0),y - <<< - +; Specialize routines for handling two sprites. TwoSpriteData mac lda (sprite_ptr1),y db $37,sprite_ptr0 ; and [sprite_ptr0],y @@ -19,68 +10,6 @@ TwoSpriteMask mac db $37,sprite_ptr0 ; and [sprite_ptr0],y <<< -TwoSpritesOver - tyx ; save after compositing the sprites - phb ; save the current bank - jsr CopyTileToDPSprite ; copy necessary tile data to the direct page - -]line equ 0 - lup 8 - ldy #{]line*SPRITE_PLANE_SPAN} - lda tmp_tile_data+{]line*4} - TwoSpriteLine - sta tmp_tile_data+{]line*4} - - ldy #{]line*SPRITE_PLANE_SPAN}+2 - lda tmp_tile_data+{]line*4}+2 - TwoSpriteLine - sta tmp_tile_data+{]line*4}+2 -]line equ ]line+1 - --^ - - plb - jmp (K_TS_APPLY_TILE_DATA,x) - - -TwoSpritesUnderFast - tyx ; save after compositing the sprites - phb ; save the current bank - jsr CopyTwoSpritesDataToDP ; copy necessary sprite data to the direct page - jmp MergeSpriteWithTileFast - -]line equ 0 - lup 8 - ldy #{]line*SPRITE_PLANE_SPAN} - lda tmp_tile_data+{]line*4} - TwoSpriteLine - sta tmp_tile_data+{]line*4} - - ldy #{]line*SPRITE_PLANE_SPAN}+2 - lda tmp_tile_data+{]line*4}+2 - TwoSpriteLine - sta tmp_tile_data+{]line*4}+2 -]line equ ]line+1 - --^ - - plb - jmp (K_TS_APPLY_TILE_DATA,x) - -;--------------------------------- -; Helper functions for two Sprites -CopyTwoSpritesDataToDP -]line equ 0 - lup 8 - ldy #{]line*SPRITE_PLANE_SPAN} - TwoSpriteData - sta tmp_sprite_data+{]line*4} - - ldy #{]line*SPRITE_PLANE_SPAN}+2 - TwoSpriteData - sta tmp_sprite_data+{]line*4}+2 -]line equ ]line+1 - --^ - rts - CopyFourSpritesDataAndMaskToDP CopyThreeSpritesDataAndMaskToDP CopyTwoSpritesDataAndMaskToDP diff --git a/src/render/Sprite3.s b/src/render/Sprite3.s index 30bc408..e69de29 100644 --- a/src/render/Sprite3.s +++ b/src/render/Sprite3.s @@ -1,36 +0,0 @@ - -ThreeSpriteLine mac - db $37,sprite_ptr2 ; and [sprite_ptr2],y - ora (sprite_ptr2),y - db $37,sprite_ptr1 ; and [sprite_ptr1],y - ora (sprite_ptr1),y - db $37,sprite_ptr0 ; and [sprite_ptr0],y - ora (sprite_ptr0),y - <<< - -; Three sprites wiithout extra masking -ThreeSpritesFast - tyx ; save for after compositing the sprites - - ldy TileStore+TS_TILE_ADDR,x - pei DP2_TILEDATA_AND_SPRITEDATA_BANKS - plb ; set to the tiledata bank - jsr (K_TS_COPY_TILE_DATA,x) - plb ; set to the sprite data bank - -]line equ 0 - lup 8 - ldy #{]line*SPRITE_PLANE_SPAN} - lda tmp_tile_data+{]line*4} - ThreeSpriteLine - sta tmp_tile_data+{]line*4} - - ldy #{]line*SPRITE_PLANE_SPAN}+2 - lda tmp_tile_data+{]line*4}+2 - ThreeSpriteLine - sta tmp_tile_data+{]line*4}+2 -]line equ ]line+1 - --^ - - plb - jmp _CopyDP2ToCodeField From 76a9710114a3e69540d5d44e26a9d26490c8f9d4 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 21 Jun 2022 15:28:58 -0500 Subject: [PATCH 60/82] Add dynamic rendering functions --- src/Defs.s | 2 +- src/Tiles.s | 54 +++++---- src/Tool.s | 1 + src/blitter/Tiles10001.s | 2 +- src/render/Dynamic.s | 249 +++++++++++++++++++++++++++++++++++++++ src/render/Sprite1.s | 33 ++++++ 6 files changed, 315 insertions(+), 26 deletions(-) create mode 100644 src/render/Dynamic.s diff --git a/src/Defs.s b/src/Defs.s index 8f05c28..4eb1430 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -190,7 +190,7 @@ PAD_KEY_DOWN equ $04 ; TILE_RESERVED_BIT equ $8000 TILE_PRIORITY_BIT equ $4000 ; Put tile on top of sprite TILE_FRINGE_BIT equ $2000 ; Unused -TILE_MASK_BIT equ $1000 ; Hint bit used in TWO_LAYER_MODE to optimize rendering +TILE_SOLID_BIT equ $1000 ; Hint bit used in TWO_LAYER_MODE to optimize rendering TILE_DYN_BIT equ $0800 ; Is this a Dynamic Tile? TILE_VFLIP_BIT equ $0400 TILE_HFLIP_BIT equ $0200 diff --git a/src/Tiles.s b/src/Tiles.s index 565e1df..e27397c 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -197,7 +197,10 @@ _SetTile jsr _GetTileAddr sta TileStore+TS_TILE_ADDR,x ; Committed to drawing this tile, so get the address of the tile in the tiledata bank for later -; Set the standard renderer procs for this tile. +; Set the renderer procs for this tile. +; +; NOTE: Later on, optimize this to just take the Tile ID & TILE_CTRL_MASK and lookup the right proc +; table address from a lookup table.... ; ; 1. The dirty render proc is always set the same. ; 2. If BG1 and DYN_TILES are disabled, then the TS_BASE_TILE_DISP is selected from the Fast Renderers, otherwise @@ -243,23 +246,10 @@ _SetTile brl :setTileDyn :not_dyn - lda TileStore+TS_TILE_ID,x - and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value - xba - tay -; ldal DirtyTileProcs,x -; sta TileStore+TS_DIRTY_TILE_DISP,y - -; ldal CopyTileProcs,x -; sta TileStore+TS_DIRTY_TILE_COPY,y - - lda TileStore+TS_TILE_ID,x ; Get the non-sprite dispatch address - and #TILE_CTRL_MASK - xba - tay -; ldal TileProcs,y -; sta TileStore+TS_BASE_TILE_DISP,y - jmp _PushDirtyTileX ; on the next call to _ApplyTiles + ldy #SlowProcs ; safe for now.... + 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. @@ -270,12 +260,22 @@ _SetTile 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. If the old tile and the new tile -; are both Dynamic tiles or both Basic tiles, then we can use an optimized routine. Otherwise -; we must set the opcodes as well as the operands +; 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 + lda newTileId ; Otherwise chose one of the two dynamic tuples + and #TILE_PRIORITY_BIT + beq :pickDynProc ; If the Priority bit is not set, pick the first entry + lda #1 ; If the Priority bit is set, pick the other one + +:pickDynProc ldy #DynProcs + jsr _SetTileProcs + jmp _PushDirtyTileX + +:pickSlowProc ldy #SlowProcs lda procIdx jsr _SetTileProcs jmp _PushDirtyTileX @@ -375,14 +375,20 @@ SlowUnderNV dw CopyTileVSlow,SpriteUnderVSlow,OneSpriteSlowUnderV ; that does not need to worry about a second background. Because dynamic ; tiles don't support horizontal or vertical flipping, there are only two ; sets of procedures: one for Over and one for Under. -;DynOver dw _TBDynamicTile,DynamicOver,_OneSpriteDynamicOver -;DynUnder dw _TBDynamicTile,DynamicUnder,_OneSpriteDynamicUnder +DynProcs +DynOver dw CopyDynamicTile,DynamicOver,OneSpriteDynamicOver +DynUnder dw CopyDynamicTile,DynamicUnder,OneSpriteDynamicUnder ; "Two Layer" procs. These are the most complex procs. Generally, ; all of these methods are implemented by building up the data ; and mask into the direct page space and then calling a common ; function to create the complex code fragments in the code field. ; There is not a lot of opportuinity to optimize these routines. +; +; To improve the performance when two-layer rendering is enabled, +; the TILE_SOLID_BIT hint bit can be set to indicate that a tile +; has no transparency. This allows one of the faster routines +; to be selected. diff --git a/src/Tool.s b/src/Tool.s index b92dc7c..9d4da09 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -366,6 +366,7 @@ _TSGetSeconds put render/Render.s put render/Fast.s put render/Slow.s + put render/Dynamic.s put render/Sprite1.s put render/Sprite2.s put tiles/DirtyTileQueue.s diff --git a/src/blitter/Tiles10001.s b/src/blitter/Tiles10001.s index dd63ee4..54adc98 100644 --- a/src/blitter/Tiles10001.s +++ b/src/blitter/Tiles10001.s @@ -29,7 +29,7 @@ _TBDynamicSpriteTile sta _JTBL_CACHE ; within one tile, the second column is consecutive lda _OP_CACHE - adc #$0200 + adc #$0200 ; Advance to the next word sta _OP_CACHE CopyDynWord 2;$0000 diff --git a/src/render/Dynamic.s b/src/render/Dynamic.s new file mode 100644 index 0000000..6cebb99 --- /dev/null +++ b/src/render/Dynamic.s @@ -0,0 +1,249 @@ +; Rendering functions for Dynamic tiles. There are no Fast/Slow variants here +CopyDynamicTile + ldal TileStore+TS_TILE_ID,x + and #$007F + ora #$4800 + +]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 + --^ + + 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 + plb + rts + +; These routines handle the sprites. They rely on a fairly complicated macro that takes care of +; populating the code field and snippet space +DynamicOver + lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor + and #$007F ; clamp to < (32 * 4) + ora #$B500 + xba + sta _OP_CACHE ; This is the 2-byte opcode for to load the data + + lda TileStore+TS_CODE_ADDR_HIGH,x + pha + ldy TileStore+TS_CODE_ADDR_LOW,x + plb + + CopyDynOver 0;$0003 + CopyDynOver 4;$1003 + CopyDynOver 8;$2003 + CopyDynOver 12;$3003 + CopyDynOver 16;$4003 + CopyDynOver 20;$5003 + CopyDynOver 24;$6003 + CopyDynOver 28;$7003 + + sec + lda _JTBL_CACHE + sbc #32 ; All the snippets are 32 bytes wide and, since we're + sta _JTBL_CACHE ; within one tile, the second column is consecutive + + clc + lda _OP_CACHE + adc #$0200 ; Advance to the next word + sta _OP_CACHE + + CopyDynOver 2;$0000 + CopyDynOver 6;$1000 + CopyDynOver 10;$2000 + CopyDynOver 14;$3000 + CopyDynOver 18;$4000 + CopyDynOver 22;$5000 + CopyDynOver 26;$6000 + CopyDynOver 30;$7000 + + plb + rts + +DynamicUnder + lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor + and #$007F ; clamp to < (32 * 4) + ora #$B500 + xba + sta _OP_CACHE ; This is the 2-byte opcode for to load the data + + lda TileStore+TS_CODE_ADDR_HIGH,x + pha + ldy TileStore+TS_CODE_ADDR_LOW,x + plb + + CopyDynUnder 0;$0003 + CopyDynUnder 4;$1003 + CopyDynUnder 8;$2003 + CopyDynUnder 12;$3003 + CopyDynUnder 16;$4003 + CopyDynUnder 20;$5003 + CopyDynUnder 24;$6003 + CopyDynUnder 28;$7003 + + sec + lda _JTBL_CACHE + sbc #32 ; All the snippets are 32 bytes wide and, since we're + sta _JTBL_CACHE ; within one tile, the second column is consecutive + + clc + lda _OP_CACHE + adc #$0200 ; Advance to the next word + sta _OP_CACHE + + CopyDynUnder 2;$0000 + CopyDynUnder 6;$1000 + CopyDynUnder 10;$2000 + CopyDynUnder 14;$3000 + CopyDynUnder 18;$4000 + CopyDynUnder 22;$5000 + CopyDynUnder 26;$6000 + CopyDynUnder 30;$7000 + +; Now fill in the JMP opcodes + 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 + + plb + rts + +; Create a masked render based on data in the direct page temporary buffer. +; +; If the MASK is $0000, then insert a PEA +; If the MASK is $FFFF, then insert a LDA DP,x / PHA +; If mixed, create a snippet of LDA DP,x / AND #MASK / ORA #DATA / PHA +; +; ]1 : sprite buffer offset +; ]2 : code field offset +CopyDynOver mac + lda tmp_sprite_mask+{]1} ; load the mask value + bne mixed ; a non-zero value may be mixed + +; This is a solid word + lda #$00F4 ; PEA instruction + sta: ]2,y + lda tmp_sprite_data+{]1} ; load the sprite data + sta: ]2+1,y ; PEA operand + bra next + +mixed cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word + beq transparent + + lda #$004C ; JMP to handler + sta: {]2},y + lda _JTBL_CACHE ; Get the offset to the exception handler for this column + ora #{]2&$F000} ; adjust for the current row offset + sta: {]2}+1,y + tax ; This becomes the new address that we use to patch in + + lda _OP_CACHE ; Get the LDA dp,x instruction for this column + sta: $0000,x + + lda #$0029 ; AND #SPRITE_MASK + sta: $0002,x + lda tmp_sprite_mask+{]1} + sta: $0003,x + + lda #$0009 ; ORA #SPRITE_DATA + sta: $0005,x + lda tmp_sprite_data+{]1} + sta: $0006,x + + lda #$0D80 ; branch to the prologue (BRA *+15) + sta: $0008,x + bra next + +; This is a transparent word, so just show the dynamic data +transparent + lda #$4800 ; Put the PHA in the third byte + sta: {]2}+1,y + lda _OP_CACHE ; Store the LDA dp,x instruction with operand + sta: {]2},y +next + <<< + +; Masked renderer for a dynamic tile on top of the sprite data. There are no transparent vs +; solid vs mixed considerations here. This only sets the JMP address, setting the JMP opcodes +; must happen elsewhere +; +; ]1 : sprite plane offset +; ]2 : code field offset +CopyDynUnder MAC + +; Need to fill in the first 9 bytes of the JMP handler with the following code sequence where +; the data and mask from from the sprite plane +; +; lda #DATA +; and $80,x +; ora $00,x +; bra *+16 + + lda _JTBL_CACHE ; Get the offset to the exception handler for this column + ora #{]2&$F000} ; adjust for the current row offset + sta: ]2+1,y + tay ; This becomes the new address that we use to patch in + + lda #$00A9 ; LDA #DATA + sta: $0000,y + ldal tmp_sprite_data+{]1},x + sta: $0001,y + + lda _OP_CACHE + sta: $0003,y ; AND $80,x + eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand + sta: $0005,y ; ORA $00,x + + lda #$0E80 ; branch to the prologue (BRA *+16) + sta: $0007,y + + ldy _Y_REG ; restore original y-register value and move on + eom \ No newline at end of file diff --git a/src/render/Sprite1.s b/src/render/Sprite1.s index 57163c0..1143e9f 100644 --- a/src/render/Sprite1.s +++ b/src/render/Sprite1.s @@ -151,3 +151,36 @@ OneSpriteSlowUnderV plb jsr FillPEAOpcode jmp _OneSpriteFastUnderV + +;------------------------------- +; Dynamic tiles with one sprite. + +OneSpriteDynamicUnder + ldx sprite_ptr0 +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_data+{]line*4} + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_data+{]line*4}+2 +]line equ ]line+1 + --^ + jmp DynamicUnder + +OneSpriteDynamicOver + ldx sprite_ptr0 +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_data+{]line*4} + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_data+{]line*4}+2 + + ldal spritemask+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_mask+{]line*4} + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_mask+{]line*4}+2 +]line equ ]line+1 + --^ + jmp DynamicOver + From 4c1dba0f6852ddce878bbb4bc095953afeb81951 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Tue, 21 Jun 2022 23:13:28 -0500 Subject: [PATCH 61/82] Complete Dynamic Tile renderer --- demos/tool/App.Main.s | 39 +++++++++-- src/Tiles.s | 9 ++- src/Tool.s | 20 ++++++ src/blitter/Tiles.s | 117 -------------------------------- src/render/Dynamic.s | 150 +++++++++++++++++++++++++++++++++++++----- src/render/Render.s | 71 -------------------- src/render/Sprite1.s | 10 ++- 7 files changed, 200 insertions(+), 216 deletions(-) diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 124b444..03aa30c 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -15,7 +15,7 @@ TSZelda EXT ; tileset buffer -MAX_SPRITES equ 16 +MAX_SPRITES equ 1 ScreenX equ 0 ScreenY equ 2 @@ -24,6 +24,8 @@ Tmp1 equ 6 KeyState equ 8 Selected equ 10 Flips equ 12 +DTile equ 14 +Tmp2 equ 16 ; Typical init phk @@ -95,6 +97,8 @@ HERO_SPRITE equ SPRITE_16X16+1 ; Manually fill in the 41x26 tiles of the TileStore with a test pattern of trees +; lda #TILE_DYN_BIT+TILE_PRIORITY_BIT+0 ; fill the screen the the dynamic tile slot 0 + lda #TILE_DYN_BIT+0 ; fill the screen the the dynamic tile slot 0 jsr _fillTileStore ldx #0 @@ -145,6 +149,14 @@ HERO_SPRITE equ SPRITE_16X16+1 ldy #6 jsr _drawTreeFront +; Set up the dynamic tile + lda #65 + sta DTile + + pei DTile + pea $0000 + _GTECopyTileToDynamic ; Copy DTile into the first dynamic tile slot + ; Initialize the frame counter stz FrameCount @@ -273,9 +285,19 @@ HERO_SPRITE equ SPRITE_16X16+1 brl :next :10 cmp #$0A - bne :next + bne :11 dec ScreenY +:11 cmp #'y' + bne :next + lda DTile + inc + and #$007F + sta DTile + pha + pea $0000 + _GTECopyTileToDynamic + :next ; inc ScreenX @@ -283,7 +305,7 @@ HERO_SPRITE equ SPRITE_16X16+1 pei ScreenY _GTESetBG0Origin -; brl no_animate + brl no_animate stz Tmp0 stz Tmp1 @@ -433,11 +455,11 @@ GTEStartUp brk $02 :ok2 - clc ; Give GTE a page of direct page memory + clc ; Give GTE a page of direct page memory tdc adc #$0100 pha - pea $0000 ; No extra capabilities + pea #ENGINE_MODE_DYN_TILES ; Enable Dynamic Tiles lda MyUserId ; Pass the userId for memory allocation pha _GTEStartUp @@ -448,15 +470,20 @@ GTEStartUp rts _fillTileStore + sta Tmp2 stz Tmp0 :oloop stz Tmp1 :iloop pei Tmp1 pei Tmp0 - pea #129 + pei Tmp2 _GTESetTile + lda Tmp2 + eor #TILE_PRIORITY_BIT + sta Tmp2 + lda Tmp1 inc sta Tmp1 diff --git a/src/Tiles.s b/src/Tiles.s index e27397c..b86d32d 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -150,7 +150,7 @@ InitTiles asl ; of this tile asl sta TileStore+TS_WORD_OFFSET,x ; This is the offset from 0 to 82, used in LDA (dp),y instruction - + tay lda Col2CodeOffset+2,y clc @@ -158,6 +158,11 @@ InitTiles ; 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 + clc + adc :base + sta TileStore+TS_JMP_ADDR,x ; Address of the snippet handler for this tile + dec :col bpl :hop dec :row @@ -269,7 +274,7 @@ _SetTile lda newTileId ; Otherwise chose one of the two dynamic tuples and #TILE_PRIORITY_BIT beq :pickDynProc ; If the Priority bit is not set, pick the first entry - lda #1 ; If the Priority bit is set, pick the other one + lda #1 ; If the Priority bit is set, pick the other one :pickDynProc ldy #DynProcs jsr _SetTileProcs diff --git a/src/Tool.s b/src/Tool.s index 9d4da09..312e354 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -58,6 +58,8 @@ _CallTable adrl _TSRemoveSprite-1 adrl _TSGetSeconds-1 + + adrl _TSCopyTileToDynamic-1 _CTEnd _GTEAddSprite MAC UserTool $1000+GTEToolNum @@ -351,6 +353,24 @@ _TSGetSeconds _TSExit #0;#0 +_TSCopyTileToDynamic +:dynId equ FirstParam+0 +:tileId equ FirstParam+2 + _TSEntry + + lda EngineMode + bit #ENGINE_MODE_DYN_TILES + beq :notEnabled + + lda :tileId,s + tax + lda :dynId,s + tay + jsr CopyTileToDyn + +:notEnabled + _TSExit #0;#4 + ; Insert the GTE code put Math.s diff --git a/src/blitter/Tiles.s b/src/blitter/Tiles.s index 5a55228..8a7adf4 100644 --- a/src/blitter/Tiles.s +++ b/src/blitter/Tiles.s @@ -351,123 +351,6 @@ ClearTile rep #$20 rts -; Helper functions to copy tile data to the appropriate location in Bank 0 -; X = tile ID -; Y = dynamic tile ID -CopyTileToDyn 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 mask - rtl - -; X = address of tile -; Y = tile address in bank 0 -CopyTileDToDyn - phb - pea $0000 - plb - plb - - 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 - - plb - rts - -; Helper function to copy tile mask to the appropriate location in Bank 0 -; -; X = address of tile -; Y = tile address in bank 0 -; -; Argument are the same as CopyTileDToDyn, the code takes care of adjust offsets. -; This make is possible to call the two functions back-to-back -; -; ldx tileAddr -; ldy dynTileAddr -; jsr CopyTileDToDyn -; jsr CopyTileMToDyn -CopyTileMToDyn - phb - pea $0000 - plb - plb - - ldal tiledata+32+0,x - sta: $0080,y - ldal tiledata+32+2,x - sta: $0082,y - ldal tiledata+32+4,x - sta $0180,y - ldal tiledata+32+6,x - sta $0182,y - ldal tiledata+32+8,x - sta $0280,y - ldal tiledata+32+10,x - sta $0282,y - ldal tiledata+32+12,x - sta $0380,y - ldal tiledata+32+14,x - sta $0382,y - ldal tiledata+32+16,x - sta $0480,y - ldal tiledata+32+18,x - sta $0482,y - ldal tiledata+32+20,x - sta $0580,y - ldal tiledata+32+22,x - sta $0582,y - ldal tiledata+32+24,x - sta $0680,y - ldal tiledata+32+26,x - sta $0682,y - ldal tiledata+32+28,x - sta $0780,y - ldal tiledata+32+30,x - sta $0782,y - - plb - rts - ; CopyBG0Tile ; ; A low-level function that copies 8x8 tiles directly into the code field space. diff --git a/src/render/Dynamic.s b/src/render/Dynamic.s index 6cebb99..1249d33 100644 --- a/src/render/Dynamic.s +++ b/src/render/Dynamic.s @@ -69,8 +69,8 @@ DynamicOver sec lda _JTBL_CACHE - sbc #32 ; All the snippets are 32 bytes wide and, since we're - sta _JTBL_CACHE ; within one tile, the second column is consecutive + sbc #SNIPPET_SIZE ; Advance to the next snippet (Reverse indexing) + sta _JTBL_CACHE clc lda _OP_CACHE @@ -95,7 +95,7 @@ DynamicUnder lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor and #$007F ; clamp to < (32 * 4) - ora #$B500 + ora #$3580 ; Pre-calc the AND $80,x opcode + operand xba sta _OP_CACHE ; This is the 2-byte opcode for to load the data @@ -115,8 +115,8 @@ DynamicUnder sec lda _JTBL_CACHE - sbc #32 ; All the snippets are 32 bytes wide and, since we're - sta _JTBL_CACHE ; within one tile, the second column is consecutive + sbc #SNIPPET_SIZE + sta _JTBL_CACHE clc lda _OP_CACHE @@ -181,7 +181,7 @@ mixed cmp #$FFFF ; All 1's in the mask is a fully lda #$004C ; JMP to handler sta: {]2},y lda _JTBL_CACHE ; Get the offset to the exception handler for this column - ora #{]2&$F000} ; adjust for the current row offset + ora #{]2&$7000} ; adjust for the current row offset sta: {]2}+1,y tax ; This becomes the new address that we use to patch in @@ -228,22 +228,138 @@ CopyDynUnder MAC ; bra *+16 lda _JTBL_CACHE ; Get the offset to the exception handler for this column - ora #{]2&$F000} ; adjust for the current row offset - sta: ]2+1,y - tay ; This becomes the new address that we use to patch in + ora #{]2&$7000} ; adjust for the current row offset + sta: {]2}+1,y + tax ; This becomes the new address that we use to patch in lda #$00A9 ; LDA #DATA - sta: $0000,y - ldal tmp_sprite_data+{]1},x - sta: $0001,y + sta: $0000,x + lda tmp_sprite_data+{]1} + sta: $0001,x lda _OP_CACHE - sta: $0003,y ; AND $80,x + sta: $0003,x ; AND $80,x eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand - sta: $0005,y ; ORA $00,x + sta: $0005,x ; ORA $00,x lda #$0E80 ; branch to the prologue (BRA *+16) - sta: $0007,y + sta: $0007,x + eom - ldy _Y_REG ; restore original y-register value and move on - eom \ No newline at end of file +; Helper functions to move tile data into the dynamic tile space + +; Helper functions to copy tile data to the appropriate location in Bank 0 +; X = tile ID +; Y = dynamic tile ID +CopyTileToDyn + 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 + jmp CopyTileMToDyn ; Copy the tile mask + +; X = address of tile +; Y = tile address in bank 0 +CopyTileDToDyn + phb + pea $0000 + plb + plb + + 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 + + plb + rts + +; Helper function to copy tile mask to the appropriate location in Bank 0 +; +; X = address of tile +; Y = tile address in bank 0 +; +; Argument are the same as CopyTileDToDyn, the code takes care of adjust offsets. +; This make is possible to call the two functions back-to-back +; +; ldx tileAddr +; ldy dynTileAddr +; jsr CopyTileDToDyn +; jsr CopyTileMToDyn +CopyTileMToDyn + phb + pea $0000 + plb + plb + + ldal tiledata+32+0,x + sta: $0080,y + ldal tiledata+32+2,x + sta: $0082,y + ldal tiledata+32+4,x + sta $0180,y + ldal tiledata+32+6,x + sta $0182,y + ldal tiledata+32+8,x + sta $0280,y + ldal tiledata+32+10,x + sta $0282,y + ldal tiledata+32+12,x + sta $0380,y + ldal tiledata+32+14,x + sta $0382,y + ldal tiledata+32+16,x + sta $0480,y + ldal tiledata+32+18,x + sta $0482,y + ldal tiledata+32+20,x + sta $0580,y + ldal tiledata+32+22,x + sta $0582,y + ldal tiledata+32+24,x + sta $0680,y + ldal tiledata+32+26,x + sta $0682,y + ldal tiledata+32+28,x + sta $0780,y + ldal tiledata+32+30,x + sta $0782,y + + plb + rts \ No newline at end of file diff --git a/src/render/Render.s b/src/render/Render.s index 1d57fed..82f311a 100644 --- a/src/render/Render.s +++ b/src/render/Render.s @@ -34,77 +34,6 @@ ThreeSprites tyx FourSprites tyx jmp CopyFourSpritesDataAndMaskToDP - -; Simple pair of routines that copies just the tile data to the direct page workspace. Data Bank -; must be set to the TileData bank in entry. -; -; Preserves the X-register -CopyTileDataToDPA -]line equ 0 - lup 8 - lda tiledata+{]line*4},y - sta tmp_tile_data+{]line*4} - - lda tiledata+{]line*4}+2,y - sta tmp_tile_data+{]line*4}+2 -]line equ ]line+1 - --^ - rts - - -; Given a populate tmp_sprite_data buffer to use as a base, merge it with a tile and write to the -; code field -MergeSpriteWithTileFast - ldx TileStore+TS_TILE_ADDR,y - lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line - pha ; and put on the stack for later. Has TileStore bank in high byte. - lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field - tay - plb - -]line equ 0 - lup 8 - lda tmp_sprite_data+{]line*4} - andl tiledata+{]line*4}+32,x - oral tiledata+{]line*4},x - sta: $0004+{]line*$1000},y - - lda tmp_sprite_data+{]line*4}+2 - andl tiledata+{]line*4}+32+2,x - oral tiledata+{]line*4}+2,x - sta: $0001+{]line*$1000},y -]line equ ]line+1 - --^ - plb - rts - -MergeSpriteWithTileSlow - ldx TileStore+TS_TILE_ADDR,y - lda TileStore+TS_CODE_ADDR_HIGH,y ; load the bank of the target code field line - pha ; and put on the stack for later. Has TileStore bank in high byte. - lda TileStore+TS_CODE_ADDR_LOW,y ; load the address of the code field - tay - plb - -]line equ 0 - lup 8 - lda tmp_sprite_data+{]line*4} - andl tiledata+{]line*4}+32,x - oral tiledata+{]line*4},x - sta: $0004+{]line*$1000},y - - lda tmp_sprite_data+{]line*4}+2 - andl tiledata+{]line*4}+32+2,x - oral tiledata+{]line*4}+2,x - sta: $0001+{]line*$1000},y -]line equ ]line+1 - --^ - jmp _FillPEAOpcode - - - - - ; 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. diff --git a/src/render/Sprite1.s b/src/render/Sprite1.s index 1143e9f..4e4528c 100644 --- a/src/render/Sprite1.s +++ b/src/render/Sprite1.s @@ -156,7 +156,8 @@ OneSpriteSlowUnderV ; Dynamic tiles with one sprite. OneSpriteDynamicUnder - ldx sprite_ptr0 + txy + tax ]line equ 0 lup 8 ldal spritedata+{]line*SPRITE_PLANE_SPAN},x @@ -165,10 +166,12 @@ OneSpriteDynamicUnder sta tmp_sprite_data+{]line*4}+2 ]line equ ]line+1 --^ + tyx jmp DynamicUnder OneSpriteDynamicOver - ldx sprite_ptr0 + txy + tax ]line equ 0 lup 8 ldal spritedata+{]line*SPRITE_PLANE_SPAN},x @@ -178,9 +181,10 @@ OneSpriteDynamicOver ldal spritemask+{]line*SPRITE_PLANE_SPAN},x sta tmp_sprite_mask+{]line*4} - ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x sta tmp_sprite_mask+{]line*4}+2 ]line equ ]line+1 --^ + tyx jmp DynamicOver From 871a0ac8fbab931c4311489ae14bef8f89b61bda Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 22 Jun 2022 00:06:25 -0500 Subject: [PATCH 62/82] Update sample to exercise dynamic tiles --- demos/tool/App.Main.s | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 03aa30c..f327395 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -15,7 +15,7 @@ TSZelda EXT ; tileset buffer -MAX_SPRITES equ 1 +MAX_SPRITES equ 16 ScreenX equ 0 ScreenY equ 2 @@ -38,8 +38,8 @@ Tmp2 equ 16 ; Initialize the graphics screen to a 256x160 playfield - pea #256 - pea #160 + pea #320 + pea #200 _GTESetScreenMode ; Load a tileset @@ -97,9 +97,10 @@ HERO_SPRITE equ SPRITE_16X16+1 ; Manually fill in the 41x26 tiles of the TileStore with a test pattern of trees -; lda #TILE_DYN_BIT+TILE_PRIORITY_BIT+0 ; fill the screen the the dynamic tile slot 0 +; lda #TILE_DYN_BIT+TILE_PRIORITY_BIT+0 ; fill the screen the the dynamic tile slot 0 lda #TILE_DYN_BIT+0 ; fill the screen the the dynamic tile slot 0 jsr _fillTileStore +; brl :no_trees ldx #0 ldy #0 @@ -136,19 +137,23 @@ HERO_SPRITE equ SPRITE_16X16+1 ldx #6 ldy #0 jsr _drawTreeFront + ldx #6 ldy #3 jsr _drawTreeFront + ldx #6 ldy #6 jsr _drawTreeFront + ldx #3 ldy #6 jsr _drawTreeFront + ldx #0 ldy #6 jsr _drawTreeFront - +:no_trees ; Set up the dynamic tile lda #65 sta DTile @@ -305,7 +310,7 @@ HERO_SPRITE equ SPRITE_16X16+1 pei ScreenY _GTESetBG0Origin - brl no_animate +; brl no_animate stz Tmp0 stz Tmp1 From 05c308d64d6e749907bbf1d84633fb998fb5ede1 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 22 Jun 2022 00:07:33 -0500 Subject: [PATCH 63/82] Add new toolvall definition --- macros/GTE.Macs.s | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s index 68bc12b..48a4a9f 100644 --- a/macros/GTE.Macs.s +++ b/macros/GTE.Macs.s @@ -56,4 +56,7 @@ _GTERemoveSprite MAC <<< _GTEGetSeconds MAC UserTool $1400+GTEToolNum - <<< \ No newline at end of file + <<< +_GTECopyTileToDynamic MAC + UserTool $1500+GTEToolNum + <<< \ No newline at end of file From 280d19876c67f4520aa7e0d21960a10326ec440c Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 22 Jun 2022 00:09:08 -0500 Subject: [PATCH 64/82] Fix a nasty macro/lup bug --- src/render/Fast.s | 20 ++++++++++---------- src/render/Slow.s | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/render/Fast.s b/src/render/Fast.s index 9391692..da51d13 100644 --- a/src/render/Fast.s +++ b/src/render/Fast.s @@ -63,14 +63,14 @@ _SpriteOverVFast ]dest equ 0 lup 8 ldal tiledata+{]src*4},x - and tmp_sprite_mask+{]line*4} - ora tmp_sprite_data+{]line*4} - sta: $0004+{]line*$1000},y + and tmp_sprite_mask+{]dest*4} + ora tmp_sprite_data+{]dest*4} + sta: $0004+{]dest*$1000},y ldal tiledata+{]src*4}+2,x - and tmp_sprite_mask+{]line*4}+2 - ora tmp_sprite_data+{]line*4}+2 - sta: $0001+{]line*$1000},y + and tmp_sprite_mask+{]dest*4}+2 + ora tmp_sprite_data+{]dest*4}+2 + sta: $0001+{]dest*$1000},y ]src equ ]src-1 ]dest equ ]dest+1 --^ @@ -135,15 +135,15 @@ _SpriteUnderVFast ]src equ 7 ]dest equ 0 lup 8 - lda tmp_sprite_data+{]line*4} + lda tmp_sprite_data+{]dest*4} andl tiledata+{]src*4}+32,x oral tiledata+{]src*4},x - sta: $0004+{]line*$1000},y + sta: $0004+{]dest*$1000},y - lda tmp_sprite_data+{]line*4}+2 + lda tmp_sprite_data+{]dest*4}+2 andl tiledata+{]src*4}+32+2,x oral tiledata+{]src*4}+2,x - sta: $0001+{]line*$1000},y + sta: $0001+{]dest*$1000},y ]src equ ]src-1 ]dest equ ]dest+1 --^ diff --git a/src/render/Slow.s b/src/render/Slow.s index 4254fc7..8cb23d7 100644 --- a/src/render/Slow.s +++ b/src/render/Slow.s @@ -76,7 +76,7 @@ FillPEAOpcode sta: $0003+{]line*$1000},y ]line equ ]line+1 --^ - rep #$20 + rep #$20 rts ; This is a dtub; will be removed eventually From 32925722610a3608971bf185ebc8c9a0e0f90c58 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 22 Jun 2022 15:29:09 -0500 Subject: [PATCH 65/82] Add TwoLayer rendering stubs --- src/Tiles.s | 14 +- src/Tool.s | 1 + src/render/Dynamic.s | 300 ++++++++++++++++----------------- src/render/Sprite1.s | 53 ++++++ src/render/TwoLayer.s | 382 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 596 insertions(+), 154 deletions(-) create mode 100644 src/render/TwoLayer.s diff --git a/src/Tiles.s b/src/Tiles.s index b86d32d..7aa7120 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -393,10 +393,16 @@ DynUnder dw CopyDynamicTile,DynamicUnder,OneSpriteDynamicUnder ; To improve the performance when two-layer rendering is enabled, ; the TILE_SOLID_BIT hint bit can be set to indicate that a tile ; has no transparency. This allows one of the faster routines -; to be selected. - - - +; to be selected from the other Proc tables +TwoLyrProcs +TwoLyrOverZA dw Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr +TwoLyrOverZV dw Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr +TwoLyrOverNA dw CopyTileATwoLyr,SpriteOverATwoLyr,OneSpriteTwoLyrOverA +TwoLyrOverNV dw CopyTileVTwoLyr,SpriteOverVTwoLyr,OneSpriteTwoLyrOverV +TwoLyrUnderZA dw Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr ; if sprites are over or under the transparent tile, same rendering code +TwoLyrUnderZV dw Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr +TwoLyrUnderNA dw CopyTileATwoLyr,SpriteUnderATwoLyr,OneSpriteTwoLyrUnderA +TwoLyrUnderNV dw CopyTileVTwoLyr,SpriteUnderVTwoLyr,OneSpriteTwoLyrUnderV ; SetBG0XPos ; diff --git a/src/Tool.s b/src/Tool.s index 312e354..2d76117 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -387,6 +387,7 @@ _TSCopyTileToDynamic put render/Fast.s put render/Slow.s put render/Dynamic.s + put render/TwoLayer.s put render/Sprite1.s put render/Sprite2.s put tiles/DirtyTileQueue.s diff --git a/src/render/Dynamic.s b/src/render/Dynamic.s index 1249d33..041106c 100644 --- a/src/render/Dynamic.s +++ b/src/render/Dynamic.s @@ -4,16 +4,16 @@ CopyDynamicTile and #$007F ora #$4800 -]line equ 0 ; render the first column +]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 ; advance to the next word inc -]line equ 0 ; render the second column +]line equ 0 ; render the second column lup 8 sta: $0001+{]line*$1000},y ]line equ ]line+1 @@ -48,10 +48,10 @@ DynamicOver sta _JTBL_CACHE lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor - and #$007F ; clamp to < (32 * 4) + and #$007F ; clamp to < (32 * 4) ora #$B500 xba - sta _OP_CACHE ; This is the 2-byte opcode for to load the data + sta _OP_CACHE ; This is the 2-byte opcode for to load the data lda TileStore+TS_CODE_ADDR_HIGH,x pha @@ -69,12 +69,12 @@ DynamicOver sec lda _JTBL_CACHE - sbc #SNIPPET_SIZE ; Advance to the next snippet (Reverse indexing) + sbc #SNIPPET_SIZE ; Advance to the next snippet (Reverse indexing) sta _JTBL_CACHE clc lda _OP_CACHE - adc #$0200 ; Advance to the next word + adc #$0200 ; Advance to the next word sta _OP_CACHE CopyDynOver 2;$0000 @@ -94,10 +94,10 @@ DynamicUnder sta _JTBL_CACHE lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor - and #$007F ; clamp to < (32 * 4) - ora #$3580 ; Pre-calc the AND $80,x opcode + operand + and #$007F ; clamp to < (32 * 4) + ora #$3580 ; Pre-calc the AND $80,x opcode + operand xba - sta _OP_CACHE ; This is the 2-byte opcode for to load the data + sta _OP_CACHE ; This is the 2-byte opcode for to load the data lda TileStore+TS_CODE_ADDR_HIGH,x pha @@ -120,7 +120,7 @@ DynamicUnder clc lda _OP_CACHE - adc #$0200 ; Advance to the next word + adc #$0200 ; Advance to the next word sta _OP_CACHE CopyDynUnder 2;$0000 @@ -133,25 +133,25 @@ DynamicUnder CopyDynUnder 30;$7000 ; Now fill in the JMP opcodes - sep #$20 - lda #$4C + 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 + 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 plb rts @@ -164,52 +164,52 @@ DynamicUnder ; ; ]1 : sprite buffer offset ; ]2 : code field offset -CopyDynOver mac - lda tmp_sprite_mask+{]1} ; load the mask value - bne mixed ; a non-zero value may be mixed +CopyDynOver mac + lda tmp_sprite_mask+{]1} ; load the mask value + bne mixed ; a non-zero value may be mixed ; This is a solid word - lda #$00F4 ; PEA instruction - sta: ]2,y - lda tmp_sprite_data+{]1} ; load the sprite data - sta: ]2+1,y ; PEA operand - bra next + lda #$00F4 ; PEA instruction + sta: ]2,y + lda tmp_sprite_data+{]1} ; load the sprite data + sta: ]2+1,y ; PEA operand + bra next -mixed cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word - beq transparent +mixed cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word + beq transparent - lda #$004C ; JMP to handler - sta: {]2},y - lda _JTBL_CACHE ; Get the offset to the exception handler for this column - ora #{]2&$7000} ; adjust for the current row offset - sta: {]2}+1,y - tax ; This becomes the new address that we use to patch in + lda #$004C ; JMP to handler + sta: {]2},y + lda _JTBL_CACHE ; Get the offset to the exception handler for this column + ora #{]2&$7000} ; adjust for the current row offset + sta: {]2}+1,y + tax ; This becomes the new address that we use to patch in - lda _OP_CACHE ; Get the LDA dp,x instruction for this column - sta: $0000,x + lda _OP_CACHE ; Get the LDA dp,x instruction for this column + sta: $0000,x - lda #$0029 ; AND #SPRITE_MASK - sta: $0002,x - lda tmp_sprite_mask+{]1} - sta: $0003,x + lda #$0029 ; AND #SPRITE_MASK + sta: $0002,x + lda tmp_sprite_mask+{]1} + sta: $0003,x - lda #$0009 ; ORA #SPRITE_DATA - sta: $0005,x - lda tmp_sprite_data+{]1} - sta: $0006,x + lda #$0009 ; ORA #SPRITE_DATA + sta: $0005,x + lda tmp_sprite_data+{]1} + sta: $0006,x - lda #$0D80 ; branch to the prologue (BRA *+15) - sta: $0008,x - bra next + lda #$0D80 ; branch to the prologue (BRA *+15) + sta: $0008,x + bra next ; This is a transparent word, so just show the dynamic data transparent - lda #$4800 ; Put the PHA in the third byte - sta: {]2}+1,y - lda _OP_CACHE ; Store the LDA dp,x instruction with operand - sta: {]2},y + lda #$4800 ; Put the PHA in the third byte + sta: {]2}+1,y + lda _OP_CACHE ; Store the LDA dp,x instruction with operand + sta: {]2},y next - <<< + <<< ; Masked renderer for a dynamic tile on top of the sprite data. There are no transparent vs ; solid vs mixed considerations here. This only sets the JMP address, setting the JMP opcodes @@ -227,24 +227,24 @@ CopyDynUnder MAC ; ora $00,x ; bra *+16 - lda _JTBL_CACHE ; Get the offset to the exception handler for this column - ora #{]2&$7000} ; adjust for the current row offset - sta: {]2}+1,y - tax ; This becomes the new address that we use to patch in + lda _JTBL_CACHE ; Get the offset to the exception handler for this column + ora #{]2&$7000} ; adjust for the current row offset + sta: {]2}+1,y + tax ; This becomes the new address that we use to patch in - lda #$00A9 ; LDA #DATA - sta: $0000,x - lda tmp_sprite_data+{]1} - sta: $0001,x + lda #$00A9 ; LDA #DATA + sta: $0000,x + lda tmp_sprite_data+{]1} + sta: $0001,x - lda _OP_CACHE - sta: $0003,x ; AND $80,x - eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand - sta: $0005,x ; ORA $00,x + lda _OP_CACHE + sta: $0003,x ; AND $80,x + eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand + sta: $0005,x ; ORA $00,x - lda #$0E80 ; branch to the prologue (BRA *+16) - sta: $0007,x - eom + lda #$0E80 ; branch to the prologue (BRA *+16) + sta: $0007,x + eom ; Helper functions to move tile data into the dynamic tile space @@ -253,59 +253,59 @@ CopyDynUnder MAC ; Y = dynamic tile ID CopyTileToDyn txa - jsr _GetTileAddr + jsr _GetTileAddr tax tya - and #$001F ; Maximum of 32 dynamic tiles + 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 + 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 - jmp CopyTileMToDyn ; Copy the tile mask + jsr CopyTileDToDyn ; Copy the tile data + jmp CopyTileMToDyn ; Copy the tile mask ; X = address of tile ; Y = tile address in bank 0 CopyTileDToDyn phb - pea $0000 + pea $0000 plb plb - 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 + 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 plb rts @@ -324,42 +324,42 @@ CopyTileDToDyn ; jsr CopyTileMToDyn CopyTileMToDyn phb - pea $0000 + pea $0000 plb plb - ldal tiledata+32+0,x - sta: $0080,y - ldal tiledata+32+2,x - sta: $0082,y - ldal tiledata+32+4,x - sta $0180,y - ldal tiledata+32+6,x - sta $0182,y - ldal tiledata+32+8,x - sta $0280,y - ldal tiledata+32+10,x - sta $0282,y - ldal tiledata+32+12,x - sta $0380,y - ldal tiledata+32+14,x - sta $0382,y - ldal tiledata+32+16,x - sta $0480,y - ldal tiledata+32+18,x - sta $0482,y - ldal tiledata+32+20,x - sta $0580,y - ldal tiledata+32+22,x - sta $0582,y - ldal tiledata+32+24,x - sta $0680,y - ldal tiledata+32+26,x - sta $0682,y - ldal tiledata+32+28,x - sta $0780,y - ldal tiledata+32+30,x - sta $0782,y + ldal tiledata+32+0,x + sta: $0080,y + ldal tiledata+32+2,x + sta: $0082,y + ldal tiledata+32+4,x + sta $0180,y + ldal tiledata+32+6,x + sta $0182,y + ldal tiledata+32+8,x + sta $0280,y + ldal tiledata+32+10,x + sta $0282,y + ldal tiledata+32+12,x + sta $0380,y + ldal tiledata+32+14,x + sta $0382,y + ldal tiledata+32+16,x + sta $0480,y + ldal tiledata+32+18,x + sta $0482,y + ldal tiledata+32+20,x + sta $0580,y + ldal tiledata+32+22,x + sta $0582,y + ldal tiledata+32+24,x + sta $0680,y + ldal tiledata+32+26,x + sta $0682,y + ldal tiledata+32+28,x + sta $0780,y + ldal tiledata+32+30,x + sta $0782,y plb rts \ No newline at end of file diff --git a/src/render/Sprite1.s b/src/render/Sprite1.s index 4e4528c..31f6321 100644 --- a/src/render/Sprite1.s +++ b/src/render/Sprite1.s @@ -188,3 +188,56 @@ OneSpriteDynamicOver tyx jmp DynamicOver + +;------------------------------- +; Two Layer tiles with one sprite. Just copy the data and go through the generic sprite path +_CopySpriteDataAndMaskToDP +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_data+{]line*4} + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_data+{]line*4}+2 + + ldal spritemask+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_mask+{]line*4} + ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_mask+{]line*4}+2 +]line equ ]line+1 + --^ + rts + +OneSpriteOver0TwoLyr + txy + tax + jsr _CopySpriteDataAndMaskToDP + tyx + jmp SpriteOver0TwoLyr + +OneSpriteTwoLyrOverA + txy + tax + jsr _CopySpriteDataAndMaskToDP + tyx + jmp SpriteOverATwoLyr + +OneSpriteTwoLyrOverV + txy + tax + jsr _CopySpriteDataAndMaskToDP + tyx + jmp SpriteOverVTwoLyr + +OneSpriteTwoLyrUnderA + txy + tax + jsr _CopySpriteDataAndMaskToDP + tyx + jmp SpriteUnderATwoLyr + +OneSpriteTwoLyrUnderV + txy + tax + jsr _CopySpriteDataAndMaskToDP + tyx + jmp SpriteUnderVTwoLyr diff --git a/src/render/TwoLayer.s b/src/render/TwoLayer.s new file mode 100644 index 0000000..738445f --- /dev/null +++ b/src/render/TwoLayer.s @@ -0,0 +1,382 @@ +; Collection of render function used when the engine is in "Two Layer" mode. Other than the Tile 0 +; routines, there's nothing in here that is particularly well optimized. + +Tile0TwoLyr + and #$00FF + 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 #$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 + rts + +; Draw from the sprite buffer into a fully transparent tile +SpriteOver0TwoLyr + + lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + lda TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2) + ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand + xba + sta _OP_CACHE + + lda TileStore+TS_CODE_ADDR_HIGH,x + pha + ldy TileStore+TS_CODE_ADDR_LOW,x + plb + + CopyTwoLayerOver tmp_sprite_data+0;$0003 + CopyTwoLayerOver tmp_sprite_data+4;$1003 + CopyTwoLayerOver tmp_sprite_data+8;$2003 + CopyTwoLayerOver tmp_sprite_data+12;$3003 + CopyTwoLayerOver tmp_sprite_data+16;$4003 + CopyTwoLayerOver tmp_sprite_data+20;$5003 + CopyTwoLayerOver tmp_sprite_data+24;$6003 + CopyTwoLayerOver tmp_sprite_data+28;$7003 + + sec + lda _JTBL_CACHE + sbc #SNIPPET_SIZE ; Advance to the next snippet (Reverse indexing) + sta _JTBL_CACHE + + clc + lda _OP_CACHE + adc #$0200 ; Advance to the next word + sta _OP_CACHE + + CopyTwoLayerOver tmp_sprite_data+2;$0000 + CopyTwoLayerOver tmp_sprite_data+6;$1000 + CopyTwoLayerOver tmp_sprite_data+10;$2000 + CopyTwoLayerOver tmp_sprite_data+14;$3000 + CopyTwoLayerOver tmp_sprite_data+18;$4000 + CopyTwoLayerOver tmp_sprite_data+22;$5000 + CopyTwoLayerOver tmp_sprite_data+26;$6000 + CopyTwoLayerOver tmp_sprite_data+30;$7000 + + plb + rts + +TmpTileDataToCodeField + lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + lda TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2) + ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand + xba + sta _OP_CACHE + + lda TileStore+TS_CODE_ADDR_HIGH,x + pha + ldy TileStore+TS_CODE_ADDR_LOW,x + plb + +_TmpTileDataToCodeField + + CopyTwoLayerOver tmp_tile_data+0;$0003 + CopyTwoLayerOver tmp_tile_data+4;$1003 + CopyTwoLayerOver tmp_tile_data+8;$2003 + CopyTwoLayerOver tmp_tile_data+12;$3003 + CopyTwoLayerOver tmp_tile_data+16;$4003 + CopyTwoLayerOver tmp_tile_data+20;$5003 + CopyTwoLayerOver tmp_tile_data+24;$6003 + CopyTwoLayerOver tmp_tile_data+28;$7003 + + sec + lda _JTBL_CACHE + sbc #SNIPPET_SIZE ; Advance to the next snippet (Reverse indexing) + sta _JTBL_CACHE + + clc + lda _OP_CACHE + adc #$0200 ; Advance to the next word + sta _OP_CACHE + + CopyTwoLayerOver tmp_tile_data+2;$0000 + CopyTwoLayerOver tmp_tile_data+6;$1000 + CopyTwoLayerOver tmp_tile_data+10;$2000 + CopyTwoLayerOver tmp_tile_data+14;$3000 + CopyTwoLayerOver tmp_tile_data+18;$4000 + CopyTwoLayerOver tmp_tile_data+22;$5000 + CopyTwoLayerOver tmp_tile_data+26;$6000 + CopyTwoLayerOver tmp_tile_data+30;$7000 + + plb + rts + +; Copy a tile into the tile data buffer and then render to the code field +CopyTileATwoLyr + lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + lda TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2) + ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand + xba + sta _OP_CACHE + + lda TileStore+TS_CODE_ADDR_HIGH,x + pha + ldy TileStore+TS_CODE_ADDR_LOW,x + lda TileStore+TS_TILE_ADDR,x + tax + plb + +]line equ 0 + lup 8 + ldal tiledata+{]line*4},x + sta tmp_tile_data+{]line*4} + ldal tiledata+{]line*4}+32,x + sta tmp_tile_mask+{]line*4} + + ldal tiledata+{]line*4}+2,x + sta tmp_tile_data+{]line*4}+2 + ldal tiledata+{]line*4}+32+2,x + sta tmp_tile_mask+{]line*4}+2 +]line equ ]line+1 + --^ + jmp _TmpTileDataToCodeField + +CopyTileVTwoLyr + lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + lda TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2) + ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand + xba + sta _OP_CACHE + + lda TileStore+TS_CODE_ADDR_HIGH,x + pha + ldy TileStore+TS_CODE_ADDR_LOW,x + lda TileStore+TS_TILE_ADDR,x + tax + plb + +]src equ 7 +]dest equ 0 + lup 8 + ldal tiledata+{]src*4},x + sta tmp_tile_data+{]dest*4} + ldal tiledata+{]src*4}+32,x + sta tmp_tile_mask+{]dest*4} + + ldal tiledata+{]src*4}+2,x + sta tmp_tile_data+{]dest*4}+2 + ldal tiledata+{]src*4}+32+2,x + sta tmp_tile_mask+{]dest*4}+2 +]src equ ]src-1 +]dest equ ]dest+1 + --^ + jmp _TmpTileDataToCodeField + +; Handle sprites + tiles. Strategy is to merge the sprite and tile data and write it to the +; temporary space an defer the actual work to the _TmpTileDataToCodeField helper +SpriteOverATwoLyr + ldy TileStore+TS_TILE_ADDR,x + pei DP2_TILEDATA_AND_TILESTORE_BANKS + plb + +]line equ 0 + lup 8 + lda tiledata+{]line*4},y + and tmp_sprite_mask+{]line*4} + ora tmp_sprite_data+{]line*4} + sta tmp_tile_data+{]line*4} + + lda tiledata+{]line*4}+32,y + and tmp_sprite_mask+{]line*4} + sta tmp_tile_mask+{]line*4} + + lda tiledata+{]line*4}+2,y + and tmp_sprite_mask+{]line*4}+2 + ora tmp_sprite_data+{]line*4}+2 + sta tmp_tile_data+{]line*4}+2 + + lda tiledata+{]line*4}+32+2,y + and tmp_sprite_mask+{]line*4}+2 + sta tmp_tile_mask+{]line*4}+2 +]line equ ]line+1 + --^ + plb + jmp TmpTileDataToCodeField + +SpriteOverVTwoLyr + ldy TileStore+TS_TILE_ADDR,x + pei DP2_TILEDATA_AND_TILESTORE_BANKS + plb + +]src equ 7 +]dest equ 0 + lup 8 + lda tiledata+{]src*4},y + and tmp_sprite_mask+{]dest*4} + ora tmp_sprite_data+{]dest*4} + sta tmp_tile_data+{]dest*4} + + lda tiledata+{]src*4}+32,y + and tmp_sprite_mask+{]dest*4} + sta tmp_tile_mask+{]dest*4} + + lda tiledata+{]src*4}+2,y + and tmp_sprite_mask+{]dest*4}+2 + ora tmp_sprite_data+{]dest*4}+2 + sta tmp_tile_data+{]dest*4}+2 + + lda tiledata+{]src*4}+32+2,y + and tmp_sprite_mask+{]dest*4}+2 + sta tmp_tile_mask+{]dest*4}+2 + +]src equ ]src-1 +]dest equ ]dest+1 + --^ + plb + jmp TmpTileDataToCodeField + +SpriteUnderATwoLyr + ldy TileStore+TS_TILE_ADDR,x + pei DP2_TILEDATA_AND_TILESTORE_BANKS + plb + +]line equ 0 + lup 8 + lda tmp_sprite_data+{]line*4} + and tiledata+{]line*4}+32,y + ora tiledata+{]line*4},y + sta tmp_tile_data+{]line*4} + + lda tiledata+{]line*4}+32,y + and tmp_sprite_mask+{]line*4} + sta tmp_tile_mask+{]line*4} + + lda tmp_sprite_data+{]line*4}+2 + and tiledata+{]line*4}+32+2,y + ora tiledata+{]line*4}+2,y + sta tmp_tile_data+{]line*4}+2 + + lda tiledata+{]line*4}+32+2,y + and tmp_sprite_mask+{]line*4}+2 + sta tmp_tile_mask+{]line*4}+2 +]line equ ]line+1 + --^ + plb + jmp TmpTileDataToCodeField + +SpriteUnderVTwoLyr + ldy TileStore+TS_TILE_ADDR,x + pei DP2_TILEDATA_AND_TILESTORE_BANKS + plb + +]src equ 7 +]dest equ 0 + lup 8 + lda tmp_sprite_data+{]dest*4} + and tiledata+{]src*4}+32,y + ora tiledata+{]src*4},y + sta tmp_tile_data+{]dest*4} + + lda tiledata+{]src*4}+32,y + and tmp_sprite_mask+{]dest*4} + sta tmp_tile_mask+{]dest*4} + + lda tmp_sprite_data+{]dest*4}+2 + and tiledata+{]src*4}+32+2,y + ora tiledata+{]src*4}+2,y + sta tmp_tile_data+{]dest*4}+2 + + lda tiledata+{]src*4}+32+2,y + and tmp_sprite_mask+{]dest*4}+2 + sta tmp_tile_mask+{]dest*4}+2 + +]src equ ]src-1 +]dest equ ]dest+1 + --^ + plb + jmp TmpTileDataToCodeField + +; Macro to fill in the code field from a direct page temporary buffer +; +; ]1 : direct page address with data, the mask direct page address is data + 32 +; ]2 : code field offset +; +; Y is the code field address +CopyTwoLayerOver mac + lda {]1}+32 ; load the mask value + bne mixed ; a non-zero value may be mixed + +; This is a solid word + lda #$00F4 ; PEA instruction + sta: ]2,y + lda {]1} ; load the tile data + sta: ]2+1,y ; PEA operand + bra next + +mixed cmp #$FFFF ; All 1's in the mask is fully transparent + beq transparent + + lda #$004C ; JMP instruction + sta: {]2},y + lda _JTBL_CACHE ; Get the offset to the exception handler for this column + ora #{]2&$7000} ; adjust for the current row offset + sta: {]2}+1,y + tax ; This becomes the new address that we use to patch in + + lda #$29 + sta: $0002,x ; AND #$0000 opcode + lda #$09 + sta: $0005,x ; ORA #$0000 opcode + + lda _OP_CACHE ; Get the LDA (dp),y instruction for this column + sta: $0000,x + + lda {]1}+32 ; insert the tile mask and data into the exception + sta: $0003,x ; handler. + lda {]1} + sta: $0006,x + + lda #$0D80 ; branch to the prologue (BRA *+15) + sta: $0008,x + + bra next + +; This is a transparent word, so just show the second background layer +transparent + lda #$4800 ; put a PHA after the offset + sta: {]2}+1,y + lda _OP_CACHE + sta: {]2},y +next + eom \ No newline at end of file From bcdc5432414eff57c2e82b6208ac655d3e16cb78 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Thu, 23 Jun 2022 11:31:42 -0500 Subject: [PATCH 66/82] Add in all the two-layer tile renderers; update complete. Just need to test --- src/Defs.s | 1 + src/Tiles.s | 25 +++- src/render/Dynamic.s | 350 ++++++++++++++++++++++++++++++++++++++++++- src/render/Slow.s | 2 +- src/render/Sprite1.s | 98 +++++++----- 5 files changed, 428 insertions(+), 48 deletions(-) diff --git a/src/Defs.s b/src/Defs.s index 4eb1430..2e9e66f 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -139,6 +139,7 @@ tmp_tile_mask equ 112 ; 32 byte temporary buffer to build up _X_REG equ 144 _Y_REG equ 146 _T_PTR equ 148 ; Copy of the tile address pointer +_OP_CACHE2 equ 148 ; CAche of second opcode _BASE_ADDR equ 150 ; Copy of BTableLow for this tile _SPR_X_REG equ 152 ; Cache address of sprite plane source for a tile _JTBL_CACHE equ 154 ; Cache the offset to the exception handler for a column diff --git a/src/Tiles.s b/src/Tiles.s index 7aa7120..aa99e57 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -243,15 +243,21 @@ _SetTile lda EngineMode bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER - bne :not_fast - brl :setTileFast + beq :setTileFast -:not_fast bit #ENGINE_MODE_TWO_LAYER + bit #ENGINE_MODE_TWO_LAYER bne :not_dyn brl :setTileDyn :not_dyn - ldy #SlowProcs ; safe for now.... + lda #TILE_DYN_BIT + bit newTileId + beq :pickTwoLyrProc + + ldy #TwoLyrDynProcs + brl :pickDynProc + +:pickTwoLyrProc ldy #SlowProcs ; #TwoLyrProcs lda procIdx jsr _SetTileProcs jmp _PushDirtyTileX @@ -271,12 +277,12 @@ _SetTile 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 :pickDynProc ; If the Priority bit is not set, pick the first entry + 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 - -:pickDynProc ldy #DynProcs jsr _SetTileProcs jmp _PushDirtyTileX @@ -404,6 +410,11 @@ TwoLyrUnderZV dw Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr TwoLyrUnderNA dw CopyTileATwoLyr,SpriteUnderATwoLyr,OneSpriteTwoLyrUnderA TwoLyrUnderNV dw CopyTileVTwoLyr,SpriteUnderVTwoLyr,OneSpriteTwoLyrUnderV +; "Dynamic" procs that can handle the second background. +TwoLyrDynProcs +TwoLyrDynOver dw CopyDynamicTileTwoLyr,DynamicOverTwoLyr,OneSpriteDynamicOverTwoLyr +TwoLyrDynUnder dw CopyDynamicTileTwoLyr,DynamicUnderTwoLyr,OneSpriteDynamicUnderTwoLyr + ; SetBG0XPos ; ; Set the virtual horizontal position of the primary background layer. In addition to diff --git a/src/render/Dynamic.s b/src/render/Dynamic.s index 041106c..d417675 100644 --- a/src/render/Dynamic.s +++ b/src/render/Dynamic.s @@ -133,10 +133,11 @@ DynamicUnder CopyDynUnder 30;$7000 ; Now fill in the JMP opcodes +_DynFillJmpOpcode sep #$20 lda #$4C - sta: $0000,y - sta: $0003,y + sta: $0000,y + sta: $0003,y sta $1000,y sta $1003,y sta $2000,y @@ -156,6 +157,185 @@ DynamicUnder plb rts +; Bank is already set to code field +; Y register is the offset +; X register is the TileStore +; A is the tile address +CopyDynamicTileTwoLyr + + ldal TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + ldal TileStore+TS_WORD_OFFSET,x + ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand + xba + sta _OP_CACHE + +; We need to do an AND dp|$80,x / ORA dp,x. The opcode values are $35 and $15, respectively. +; We pre-calculate the AND opcode with the high bit of the operand set and then, in the macro +; perform and EOR #$2080 to covert the opcode and operand in one instruction + + ldal TileStore+TS_TILE_ID,x ; Get the original tile descriptor + and #$007F ; clamp to < (32 * 4) + ora #$3580 ; Pre-calc the AND $80,x opcode + operand + xba + sta _OP_CACHE2 ; This is an op to load the dynamic tile data + + CopyMaskedDWord $0003 + CopyMaskedDWord $1003 + CopyMaskedDWord $2003 + CopyMaskedDWord $3003 + CopyMaskedDWord $4003 + CopyMaskedDWord $5003 + CopyMaskedDWord $6003 + CopyMaskedDWord $7003 + + sec + lda _JTBL_CACHE + sbc #SNIPPET_SIZE ; Advance to the next snippet (Reverse indexing) + sta _JTBL_CACHE + + clc + lda _OP_CACHE + adc #$0200 ; Advance to the next word + sta _OP_CACHE + + lda _OP_CACHE2 + adc #$0200 + sta _OP_CACHE2 + + CopyMaskedDWord $0000 + CopyMaskedDWord $1000 + CopyMaskedDWord $2000 + CopyMaskedDWord $3000 + CopyMaskedDWord $4000 + CopyMaskedDWord $5000 + CopyMaskedDWord $6000 + CopyMaskedDWord $7000 + + jmp _DynFillJmpOpcode + +; Render a sprite on top of a dyamic tile with transparent areas shwing the second background +DynamicOverTwoLyr + lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + lda TileStore+TS_WORD_OFFSET,x + ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand + xba + sta _OP_CACHE + +; We need to do an AND dp|$80,x / ORA dp,x. The opcode values are $35 and $15, respectively. +; We pre-calculate the AND opcode with the high bit of the operand set and then, in the macro +; perform and EOR #$2080 to covert the opcode and operand in one instruction + + lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor + and #$007F ; clamp to < (32 * 4) + ora #$3580 ; Pre-calc the AND $80,x opcode + operand + xba + sta _OP_CACHE2 ; This is an op to load the dynamic tile data + + lda TileStore+TS_CODE_ADDR_HIGH,x + pha + ldy TileStore+TS_CODE_ADDR_LOW,x + plb + + CopyDynMaskedSpriteWord 0;$0003 + CopyDynMaskedSpriteWord 4;$1003 + CopyDynMaskedSpriteWord 8;$2003 + CopyDynMaskedSpriteWord 12;$3003 + CopyDynMaskedSpriteWord 16;$4003 + CopyDynMaskedSpriteWord 20;$5003 + CopyDynMaskedSpriteWord 24;$6003 + CopyDynMaskedSpriteWord 28;$7003 + + sec + lda _JTBL_CACHE + sbc #SNIPPET_SIZE ; Advance to the next snippet (Reverse indexing) + sta _JTBL_CACHE + + clc + lda _OP_CACHE + adc #$0200 ; Advance to the next word + sta _OP_CACHE + + lda _OP_CACHE2 + adc #$0200 ; Advance to the next word + sta _OP_CACHE2 + + CopyDynMaskedSpriteWord 2;$0000 + CopyDynMaskedSpriteWord 6;$1000 + CopyDynMaskedSpriteWord 10;$2000 + CopyDynMaskedSpriteWord 14;$3000 + CopyDynMaskedSpriteWord 18;$4000 + CopyDynMaskedSpriteWord 22;$5000 + CopyDynMaskedSpriteWord 26;$6000 + CopyDynMaskedSpriteWord 30;$7000 + + plb + rts + +; Render a sprite on top of a dyamic tile with transparent areas shwing the second background +DynamicUnderTwoLyr + lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + lda TileStore+TS_WORD_OFFSET,x + ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand + xba + sta _OP_CACHE + +; We need to do an AND dp|$80,x / ORA dp,x. The opcode values are $35 and $15, respectively. +; We pre-calculate the AND opcode with the high bit of the operand set and then, in the macro +; perform and EOR #$2080 to covert the opcode and operand in one instruction + + lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor + and #$007F ; clamp to < (32 * 4) + ora #$3580 ; Pre-calc the AND $80,x opcode + operand + xba + sta _OP_CACHE2 ; This is an op to load the dynamic tile data + + lda TileStore+TS_CODE_ADDR_HIGH,x + pha + ldy TileStore+TS_CODE_ADDR_LOW,x + plb + + CopyDynPrioMaskedSpriteWord 0;$0003 + CopyDynPrioMaskedSpriteWord 4;$1003 + CopyDynPrioMaskedSpriteWord 8;$2003 + CopyDynPrioMaskedSpriteWord 12;$3003 + CopyDynPrioMaskedSpriteWord 16;$4003 + CopyDynPrioMaskedSpriteWord 20;$5003 + CopyDynPrioMaskedSpriteWord 24;$6003 + CopyDynPrioMaskedSpriteWord 28;$7003 + + sec + lda _JTBL_CACHE + sbc #SNIPPET_SIZE ; Advance to the next snippet (Reverse indexing) + sta _JTBL_CACHE + + clc + lda _OP_CACHE + adc #$0200 ; Advance to the next word + sta _OP_CACHE + + lda _OP_CACHE2 + adc #$0200 ; Advance to the next word + sta _OP_CACHE2 + + CopyDynPrioMaskedSpriteWord 2;$0000 + CopyDynPrioMaskedSpriteWord 6;$1000 + CopyDynPrioMaskedSpriteWord 10;$2000 + CopyDynPrioMaskedSpriteWord 14;$3000 + CopyDynPrioMaskedSpriteWord 18;$4000 + CopyDynPrioMaskedSpriteWord 22;$5000 + CopyDynPrioMaskedSpriteWord 26;$6000 + CopyDynPrioMaskedSpriteWord 30;$7000 + + plb + rts + + ; Create a masked render based on data in the direct page temporary buffer. ; ; If the MASK is $0000, then insert a PEA @@ -246,6 +426,172 @@ CopyDynUnder MAC sta: $0007,x eom +; Masked renderer for a dynamic tile. What's interesting about this renderer is that the mask +; value is not used directly, but simply indicates if we can use a LDA 0,x / PHA sequence, +; a LDA (00),y / PHA, or a JMP to a blended render +; +; If a dynamic tile is animated, there is the possibility to create a special mask that marks +; words of the tile that a front / back / mixed across all frames. +; +; ]1 : code field offset +; +; This macro does not set the opcode since they will all be JMP instructions, they can be +; filled more efficiently in a separate routine. +CopyMaskedDWord MAC + +; Need to fill in the first 6 bytes of the JMP handler with the following code sequence +; +; lda (00),y +; and $80,x +; ora $00,x +; bra *+17 + + lda _JTBL_CACHE + ora #{{]1}&$7000} ; adjust for the current row offset + sta: {]1}+1,y + + tax ; This becomes the new address that we use to patch in + lda _OP_CACHE + sta: $0000,x ; LDA (00),y + lda _OP_CACHE2 + sta: $0002,x ; AND $80,x + eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand + sta: $0004,x ; ORA $00,x + lda #$0F80 ; branch to the prologue (BRA *+17) + sta: $0006,x + eom + + +; Masked renderer for a masked dynamic tile with sprite data overlaid. +; +; ]1 : sprite plane offset +; ]2 : code field offset +CopyDynMaskedSpriteWord MAC + +; Need to fill in the first 14 bytes of the JMP handler with the following code sequence where +; the data and mask from from the sprite plane +; +; lda ($00),y +; and $80,x +; ora $00,x +; and #MASK +; ora #DATA +; bra *+15 +; +; If MASK == 0, then we can do a PEA. If MASK == $FFFF, then fall back to the simple Dynamic Tile +; code and eliminate the constanct AND/ORA instructions. + + lda tmp_sprite_mask+{]1} ; load the mask value + bne mixed ; a non-zero value may be mixed + +; This is a solid word + lda #$00F4 ; PEA instruction + sta: {]2},y + lda tmp_sprite_data+{]1} ; load the sprite data + sta: {]2}+1,y ; PEA operand + bra next + +; We will always do a JMP to the exception handler, so set that up, then check for sprite +; transparency +mixed + lda #$004C ; JMP to handler + sta: {]2},y + lda _JTBL_CACHE ; Get the offset to the exception handler for this column + ora #{]2&$7000} ; adjust for the current row offset + sta: {]2}+1,y + tax ; This becomes the new address that we use to patch in + + lda _OP_CACHE + sta: $0000,x ; LDA (00),y + lda _OP_CACHE2 + sta: $0002,x ; AND $80,x + eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand + sta: $0004,x ; ORA $00,x + + lda #$0029 ; AND #SPRITE_MASK + sta: $0006,x + lda tmp_sprite_mask+{]1} + cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word + beq transparent + sta: $0007,x + + lda #$0009 ; ORA #SPRITE_DATA + sta: $0009,x + lda tmp_sprite_data+{]1} + sta: $000A,x + + lda #$0980 ; branch to the prologue (BRA *+11) + sta: $000C,x + bra next + +; This is a transparent word, so just show the dynamic data +transparent + lda #$0F80 ; branch to the epilogue (BRA *+17) + sta: $0006,x +next + eom + + +; Masked renderer for a masked dynamic tile with sprite data underlaid. +; +; ]1 : sprite plane offset +; ]2 : code field offset +CopyDynPrioMaskedSpriteWord MAC + +; Need to fill in the first 14 bytes of the JMP handler with the following code sequence where +; the data and mask from from the sprite plane +; +; lda ($00),y +; and #MASK +; ora #DATA +; and $80,x +; ora $00,x +; bra *+15 + + lda #$004C ; JMP to handler + sta: {]2},y + lda _JTBL_CACHE ; Get the offset to the exception handler for this column + ora #{]2&$7000} ; adjust for the current row offset + sta: {]2}+1,y + tax ; This becomes the new address that we use to patch in + + lda _OP_CACHE + sta: $0000,x ; LDA (00),y + + lda #$0029 ; AND #SPRITE_MASK + sta: $0002,x + + lda tmp_sprite_mask+{]1} + cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word + beq transparent ; so we can use the Tile00011 method + sta: $0003,x + + lda #$0009 ; ORA #SPRITE_DATA + sta: $0005,x + lda tmp_sprite_data+{]1} + sta: $0006,x + + lda _OP_CACHE2 + sta: $0008,x ; AND $80,x + eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand + sta: $000A,x ; ORA $00,x + + lda #$0980 ; branch to the prologue (BRA *+11) + sta: $000C,x + bra next + +; This is a transparent word, so just show the dynamic data +transparent + lda _OP_CACHE2 + sta: $0002,x ; AND $80,x + eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand + sta: $0004,x ; ORA $00,x + + lda #$0F80 ; branch to the epilogue (BRA *+17) + sta: $0006,x +next + eom + ; Helper functions to move tile data into the dynamic tile space ; Helper functions to copy tile data to the appropriate location in Bank 0 diff --git a/src/render/Slow.s b/src/render/Slow.s index 8cb23d7..ff833f1 100644 --- a/src/render/Slow.s +++ b/src/render/Slow.s @@ -79,7 +79,7 @@ FillPEAOpcode rep #$20 rts -; This is a dtub; will be removed eventually +; This is a stub; will be removed eventually _FillPEAOpcode jsr FillPEAOpcode plb ; Restore the TileStore bank diff --git a/src/render/Sprite1.s b/src/render/Sprite1.s index 31f6321..e976865 100644 --- a/src/render/Sprite1.s +++ b/src/render/Sprite1.s @@ -28,7 +28,7 @@ _OneSpriteFastOver0 plb ; Restore the TileStore bank rts -; Next implementation; drawing a sprite onto a regular tile. The 1-sprite dispatch preerves the +; Next implementation; drawing a sprite onto a regular tile. The 1-sprite dispatch preserves the ; X-register, so it already points to the TileStore OneSpriteFastOverV @@ -158,55 +158,20 @@ OneSpriteSlowUnderV OneSpriteDynamicUnder txy tax -]line equ 0 - lup 8 - ldal spritedata+{]line*SPRITE_PLANE_SPAN},x - sta tmp_sprite_data+{]line*4} - ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_sprite_data+{]line*4}+2 -]line equ ]line+1 - --^ + jsr _CopySpriteDataToDP tyx jmp DynamicUnder OneSpriteDynamicOver txy tax -]line equ 0 - lup 8 - ldal spritedata+{]line*SPRITE_PLANE_SPAN},x - sta tmp_sprite_data+{]line*4} - ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_sprite_data+{]line*4}+2 - - ldal spritemask+{]line*SPRITE_PLANE_SPAN},x - sta tmp_sprite_mask+{]line*4} - ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_sprite_mask+{]line*4}+2 -]line equ ]line+1 - --^ + jsr _CopySpriteDataAndMaskToDP tyx jmp DynamicOver ;------------------------------- ; Two Layer tiles with one sprite. Just copy the data and go through the generic sprite path -_CopySpriteDataAndMaskToDP -]line equ 0 - lup 8 - ldal spritedata+{]line*SPRITE_PLANE_SPAN},x - sta tmp_sprite_data+{]line*4} - ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_sprite_data+{]line*4}+2 - - ldal spritemask+{]line*SPRITE_PLANE_SPAN},x - sta tmp_sprite_mask+{]line*4} - ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_sprite_mask+{]line*4}+2 -]line equ ]line+1 - --^ - rts - OneSpriteOver0TwoLyr txy tax @@ -241,3 +206,60 @@ OneSpriteTwoLyrUnderV jsr _CopySpriteDataAndMaskToDP tyx jmp SpriteUnderVTwoLyr + +;----------------------------------------- +; Dynamic two-layer tiles with one sprite. + +OneSpriteDynamicOverTwoLyr + txy + tax + jsr _CopySpriteDataAndMaskToDP + tyx + jmp DynamicOverTwoLyr + +OneSpriteDynamicUnderTwoLyr + txy + tax + jsr _CopySpriteDataAndMaskToDP + tyx + jmp DynamicUnderTwoLyr + +;------------------------------------- +; Generic helpers +_CopySpriteDataToDP +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_data+{]line*4} + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_data+{]line*4}+2 +]line equ ]line+1 + --^ + rts + +_CopySpriteMaskToDP +]line equ 0 + lup 8 + ldal spritemask+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_mask+{]line*4} + ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_mask+{]line*4}+2 +]line equ ]line+1 + --^ + rts + +_CopySpriteDataAndMaskToDP +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_data+{]line*4} + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_data+{]line*4}+2 + + ldal spritemask+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_mask+{]line*4} + ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_mask+{]line*4}+2 +]line equ ]line+1 + --^ + rts From 78ee683ba8669b369c68e6771902966f8c5dec28 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sat, 25 Jun 2022 11:17:50 -0500 Subject: [PATCH 67/82] Stub in a lot of the remaining functions --- macros/GTE.Macs.s | 76 +++++++++++++++++++- src/GTE.s | 2 +- src/Render.s | 2 + src/Timer.s | 43 +++++++---- src/Tool.s | 177 ++++++++++++++++++++++++++++++++++++++++++++++ src/blitter/SCB.s | 8 +-- 6 files changed, 288 insertions(+), 20 deletions(-) diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s index 48a4a9f..137ffcf 100644 --- a/macros/GTE.Macs.s +++ b/macros/GTE.Macs.s @@ -59,4 +59,78 @@ _GTEGetSeconds MAC <<< _GTECopyTileToDynamic MAC UserTool $1500+GTEToolNum - <<< \ No newline at end of file + <<< +_GTESetPalette MAC + UserTool $1600+GTEToolNum + <<< +_GTECopyPicToBG1 MAC + UserTool $1700+GTEToolNum + <<< +_GTEBindSCBArray MAC + UserTool $1800+GTEToolNum + <<< +_GTEGetBG0TileMapInfo MAC + UserTool $1900+GTEToolNum + <<< +_GTEGetScreenInfo MAC + UserTool $1A00+GTEToolNum + <<< +_GTESetBG1Origin MAC + UserTool $1B00+GTEToolNum + <<< +_GTEGetTileAt MAC + UserTool $1C00+GTEToolNum + <<< +_GTESetBG0TileMapInfo MAC + UserTool $1D00+GTEToolNum + <<< +_GTESetBG1TileMapInfo MAC + UserTool $1E00+GTEToolNum + <<< + +; EngineMode definitions +; Script definition +YIELD equ $8000 +JUMP equ $4000 + +SET_PALETTE_ENTRY equ $0002 +SWAP_PALETTE_ENTRY equ $0004 +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 +ENGINE_MODE_TWO_LAYER equ $0001 +ENGINE_MODE_DYN_TILES equ $0002 +ENGINE_MODE_BNK0_BUFF equ $0004 + +; Tile constants +; TILE_RESERVED_BIT equ $8000 +TILE_PRIORITY_BIT equ $4000 ; Put tile on top of sprite +TILE_FRINGE_BIT equ $2000 ; Unused +TILE_SOLID_BIT equ $1000 ; Hint bit used in TWO_LAYER_MODE to optimize rendering +TILE_DYN_BIT equ $0800 ; Is this a Dynamic Tile? +TILE_VFLIP_BIT equ $0400 +TILE_HFLIP_BIT equ $0200 +TILE_ID_MASK equ $01FF +TILE_CTRL_MASK equ $FE00 + + +; Sprite constants +SPRITE_HIDE equ $2000 +SPRITE_16X16 equ $1800 +SPRITE_16X8 equ $1000 +SPRITE_8X16 equ $0800 +SPRITE_8X8 equ $0000 +SPRITE_VFLIP equ $0400 +SPRITE_HFLIP equ $0200 + +; Stamp storage parameters +VBUFF_STRIDE_BYTES equ {12*4} ; Each line has 4 slots of 16 pixels + 8 buffer pixels +VBUFF_TILE_ROW_BYTES equ {8*VBUFF_STRIDE_BYTES} ; Each row is comprised of 8 lines +VBUFF_TILE_COL_BYTES equ 4 +VBUFF_SPRITE_STEP equ {VBUFF_TILE_ROW_BYTES*3} ; Allocate space for 16 rows + 8 rows of buffer +VBUFF_SPRITE_START equ {VBUFF_TILE_ROW_BYTES+4} ; Start at an offset so $0000 can be used as an empty value +VBUFF_SLOT_COUNT equ 48 ; Have space for this many stamps diff --git a/src/GTE.s b/src/GTE.s index dd30f36..cc08f42 100644 --- a/src/GTE.s +++ b/src/GTE.s @@ -25,7 +25,7 @@ Render 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 +; SetSCBArray EXT BltSCB EXT ; Rotation diff --git a/src/Render.s b/src/Render.s index 2c046dd..98c5403 100644 --- a/src/Render.s +++ b/src/Render.s @@ -20,6 +20,8 @@ ; 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 + stz SpriteRemovedFlag ; If we remove a sprite, then we need to flag a rebuild for the next frame jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen diff --git a/src/Timer.s b/src/Timer.s index a1ca960..be34162 100644 --- a/src/Timer.s +++ b/src/Timer.s @@ -107,33 +107,48 @@ RemoveTimer ENT rtl ; Execute the timer functions -DoTimers ENT - phb - jsr _SetDataBank +;DoTimers ENT +; phb +; jsr _SetDataBank +; +; jsr _GetVBLTicks +; +; cmp LastTick ; Throttle to 60 fps +; beq :exit +; tax ; Calculate the increment +; sec +; sbc LastTick +; stx LastTick +; We don't want times to fire excessively. If the timer hasn't been evaluated for over +; one second, then just skip processing and wait for the next call. +; cmp #60 +; bcs :exit + +; jsr _DoTimers + +;:exit plb +; rtl + +; Countdown the timers +_DoTimers jsr _GetVBLTicks cmp LastTick ; Throttle to 60 fps beq :exit + tax ; Calculate the increment sec sbc LastTick stx LastTick -; We don't want times to fire excessively. If the timer has nt been evaluated for over +; We don't want times to fire excessively. If the timer hasn't been evaluated for over ; one second, then just skip processing and wait for the next call. cmp #60 - bcs :exit + bcc :do_timer +:exit rts - jsr _DoTimers - -:exit plb - rtl - -; Countdown the timers -; -; A = number of elapsed ticks -_DoTimers +:do_timer pha ldx #0 :loop diff --git a/src/Tool.s b/src/Tool.s index 2d76117..d18d614 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -60,6 +60,17 @@ _CallTable adrl _TSGetSeconds-1 adrl _TSCopyTileToDynamic-1 + + adrl _TSSetPalette-1 + adrl _TSCopyPicToBG1-1 + adrl _TSBindSCBArray-1 + adrl _TSGetBG0TileMapInfo-1 + adrl _TSGetScreenInfo-1 + adrl _TSSetBG1Origin-1 + adrl _TSGetTileAt-1 + + adrl _TSSetBG0TileMapInfo-1 + adrl _TSSetBG1TileMapInfo-1 _CTEnd _GTEAddSprite MAC UserTool $1000+GTEToolNum @@ -371,6 +382,171 @@ _TSCopyTileToDynamic :notEnabled _TSExit #0;#4 + +_TSSetPalette +:ptr equ FirstParam+0 +:palNum equ FirstParam+4 + + _TSEntry + + phb + lda :ptr+2 + pha + plb + plb + + lda :ptr + tax + lda :palNum + jsr _SetPalette + plb + + _TSExit #0;#6 + +_TSCopyPicToBG1 +:ptr equ FirstParam+0 + + _TSEntry + + lda BG1DataBank + tay + lda :ptr+2,s + tax + lda :ptr,s + jsr _CopyPicToBG1 + + _TSExit #0;#4 + +_TSBindSCBArray +:ptr equ FirstParam+0 + + _TSEntry + + lda :ptr,s + tax + lda :ptr+2,s + jsr _BindSCBArray + + _TSExit #0;#4 + +_TSGetBG0TileMapInfo +:ptr equ FirstParam+4 +:height equ FirstParam+2 +:width equ FirstParam+0 + _TSEntry + + lda TileMapWidth + sta :width,s + lda TileMapHeight + sta :height,s + lda TileMapPtr + sta :ptr,s + lda TileMapPtr+2 + sta :ptr+2,s + + _TSExit #0;#0 + + +_TSGetScreenInfo +:height equ FirstParam+4 +:width equ FirstParam+4 +:y equ FirstParam+2 +:x equ FirstParam+0 + _TSEntry + + lda ScreenX0 + sta :x,s + lda ScreenY0 + sta :y,s + sta :width,s + lda ScreenWidth + sta :width,s + lda ScreenHeight + sta :height,s + + _TSExit #0;#0 + +; SetBG1Origin(x, y) +_TSSetBG1Origin +:y equ FirstParam +:x equ FirstParam+2 + + _TSEntry + + lda :x,s + jsr _SetBG1XPos + lda :y,s + jsr _SetBG1YPos + + _TSExit #0;#4 + +; GetTileAt(x, y) +_TSGetTileAt +:y equ FirstParam +:x equ FirstParam+2 +:output equ FirstParam+4 + + _TSEntry + +; Convert the x, y coordinated to tile store block coordinates + lda :x,s + tax + lda :y,s + tay + jsr _GetTileAt + bcc :ok + lda #0 + bra :out + +; Load the tile at that tile store location + +:ok + jsr _GetTileStoreOffset0 ; Get the address of the X,Y tile position + tax + lda TileStore+TS_TILE_ID,x +:out + sta :output,s + + _TSExit #0;#4 + +; SetBG0TileMapInfo(width, height, ptr) +_TSSetBG0TileMapInfo +:ptr equ FirstParam+0 +:height equ FirstParam+4 +:width equ FirstParam+6 + + _TSEntry + + lda :width,s + sta TileMapWidth + lda :height,s + sta TileMapHeight + lda :ptr,s + sta TileMapPtr + lda :ptr+2,s + sta TileMapPtr+2 + + _TSExit #0;#8 + +; SetBG1TileMapInfo(width, height, ptr) +_TSSetBG1TileMapInfo +:ptr equ FirstParam+0 +:height equ FirstParam+4 +:width equ FirstParam+6 + + _TSEntry + + lda :width,s + sta BG1TileMapWidth + lda :height,s + sta BG1TileMapHeight + lda :ptr,s + sta BG1TileMapPtr + lda :ptr+2,s + sta TileMapPtr+2 + + _TSExit #0;#8 + ; Insert the GTE code put Math.s @@ -391,6 +567,7 @@ _TSCopyTileToDynamic put render/Sprite1.s put render/Sprite2.s put tiles/DirtyTileQueue.s + put blitter/SCB.s put blitter/Horz.s put blitter/Vert.s put blitter/BG0.s diff --git a/src/blitter/SCB.s b/src/blitter/SCB.s index 361018a..266010b 100644 --- a/src/blitter/SCB.s +++ b/src/blitter/SCB.s @@ -76,11 +76,11 @@ _BltSCB ; Quick helper to set the pointer (X = low word, A = high work) -SetSCBArray ENT - jsr _SetSCBArray - rtl +;SetSCBArray ENT +; jsr _SetSCBArray +; rtl -_SetSCBArray +_BindSCBArray stx SCBArrayPtr sta SCBArrayPtr+2 rts From 814a8c584b5a5cf2dd5d6600d75524011797de39 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sat, 25 Jun 2022 11:18:50 -0500 Subject: [PATCH 68/82] Convert the old sprite demo to use the toolset --- demos/sprites/App.Main.s | 703 +++++++++++++++++------------ demos/sprites/App.s | 26 +- demos/sprites/_FileInformation.txt | 1 + demos/sprites/build-image.bat | 1 + demos/sprites/gen/App.TileMapBG0.s | 21 +- demos/sprites/gen/App.TileSet.s | 2 +- demos/sprites/package.json | 6 +- 7 files changed, 428 insertions(+), 332 deletions(-) diff --git a/demos/sprites/App.Main.s b/demos/sprites/App.Main.s index e92881d..1ea2077 100644 --- a/demos/sprites/App.Main.s +++ b/demos/sprites/App.Main.s @@ -1,305 +1,360 @@ ; Test driver to exercise graphics routines. - REL - DSK MAINSEG + REL + DSK MAINSEG - 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 +; use ../../src/Defs.s -; Feature flags -NO_INTERRUPTS equ 0 ; turn off for crossrunner debugging -NO_MUSIC equ 1 ; turn music + tool loading off + mx %00 + +TSet EXT ; tileset buffer ; Keycodes -LEFT_ARROW equ $08 -RIGHT_ARROW equ $15 -UP_ARROW equ $0B -DOWN_ARROW equ $0A +LEFT_ARROW equ $08 +RIGHT_ARROW equ $15 +UP_ARROW equ $0B +DOWN_ARROW equ $0A -; Typical init - phk - plb +; Direct page space +appTmp0 equ 0 +BankLoad equ 2 +StartX equ 4 +StartY equ 6 +TileMapWidth equ 8 +TileMapHeight equ 10 +ScreenWidth equ 12 +ScreenHeight equ 14 - jsl EngineStartUp + phk + plb - lda #^MyPalette ; Fill Palette #0 with our colors - ldx #MyPalette - ldy #0 - jsl SetPalette + 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 - ldx #5 ; Mode 0 is full-screen, mode 5 is 256x160 - ldx #320 - ldy #200 - jsl SetScreenMode + jsr GTEStartUp ; Load and install the GTE User Tool + +; Initialize local variables + + stz appTmp0 + stz BankLoad + stz StartX + stz StartY + +; Initialize the graphics screen to a 256x160 playfield + + pea #320 + pea #200 + _GTESetScreenMode + +; Load a tileset + + pea #^TSet + pea #TSet + _GTELoadTileSet + + pea $0000 + pea #^MyPalette + pea #MyPalette + _GTESetPalette ; Set up our level data - jsr BG0SetUp - jsr TileAnimInit - jsr SetLimits + jsr BG0SetUp +; jsr TileAnimInit + jsr SetLimits - jsr InitOverlay ; Initialize the status bar - stz frameCount - ldal OneSecondCounter - sta oldOneSecondCounter - jsr UdtOverlay +; jsr InitOverlay ; Initialize the status bar + stz frameCount + pha + _GTEGetSeconds + pla + sta oldOneSecondCounter +; jsr UdtOverlay ; Allocate a buffer for loading files - jsl AllocBank ; Alloc 64KB for Load/Unpack - sta BankLoad ; Store "Bank Pointer" + jsl AllocBank ; Alloc 64KB for Load/Unpack + sta BankLoad ; Store "Bank Pointer" ; Load in the 256 color background into BG1 buffer - brl :nobackground + brl :nobackground DoLoadBG1 - lda BankLoad - ldx #BG1DataFile - jsr LoadFile + lda BankLoad + ldx #BG1DataFile + jsr LoadFile - ldx BankLoad - lda #0 - ldy BG1DataBank - jsl CopyPicToBG1 + lda BankLoad + pha + pea $0000 + _GTECopyPicToBG1 ; Copy the palettes into place - stz tmp0 + stz appTmp0 :ploop - lda tmp0 - tay - asl - asl - asl - asl - asl - clc - adc #$7E00 - tax + lda appTmp0 + pha ; Palette number + ldy BankLoad + phy ; High word pointer to palette - lda BankLoad - jsl SetPalette + asl + asl + asl + asl + asl + clc + adc #$7E00 + pha ; Low word pointer to palette + _GTESetPalette - inc tmp0 - lda tmp0 - cmp #16 - bcc :ploop + inc appTmp0 + lda appTmp0 + cmp #16 + bcc :ploop ; Bind the SCBs - lda BankLoad - ora #$8000 ; set high bit to bind to BG1 Y-position - ldx #$7D00 - jsl SetSCBArray + lda BankLoad + ora #$8000 ; set high bit to bind to BG1 Y-position + pha + pea $7D00 + _GTEBindSCBArray :nobackground ; Initialize the sprite's global position (this is tracked outside of the tile engine) - lda #16 - sta PlayerGlobalX - lda MaxGlobalY - sec - lda #40 ; 32 for tiles, 8 for sprite - sta PlayerGlobalY + lda #16 + sta PlayerGlobalX + lda MaxGlobalY + sec + lda #40 ; 32 for tiles, 8 for sprite + sta PlayerGlobalY - stz PlayerXVel - stz PlayerYVel + stz PlayerXVel + stz PlayerYVel -; Add a sprite to the engine and save it's sprite ID -SPRITE_ID equ {SPRITE_16X16+145} -MUSHROOM_ID equ {SPRITE_16X16+255} +; Create the sprites +HERO_ID equ {SPRITE_16X16+145} +HERO_VBUFF equ VBUFF_SPRITE_START+0*VBUFF_SPRITE_STEP +HERO_SLOT equ 1 +MUSHROOM_ID equ {SPRITE_16X16+255} +MUSHROOM_VBUFF equ VBUFF_SPRITE_START+1*VBUFF_SPRITE_STEP - lda #MUSHROOM_ID ; 16x16 sprite, tile ID = 145 - ldx #80 - ldy #152 - jsl AddSprite + pea HERO_ID ; sprint id + pea HERO_VBUFF ; vbuff address + _GTECreateSpriteStamp - jsr UpdatePlayerLocal - lda #SPRITE_ID ; 16x16 sprite, tile ID = 145 - ldx PlayerX - ldy PlayerY - jsl AddSprite - bcc :sprite_ok - brl Exit ; If we could not allocate a sprite, exit -:sprite_ok - sta PlayerID + pea MUSHROOM_ID ; sprint id + pea MUSHROOM_VBUFF ; vbuff address + _GTECreateSpriteStamp + + pea MUSHROOM_ID ; Put the mushroom in Slot 0 + pea #80 ; at x=80, y=152 + pea #152 + pea $0000 + _GTEAddSprite -; Draw the initial screen + pea $0000 + pea $0000 ; with these flags (h/v flip) + pea MUSHROOM_VBUFF ; and use this stamp + _GTEUpdateSprite - lda #DIRTY_BIT_BG0_REFRESH ; Redraw all of the tiles on the next Render - tsb DirtyBits - jsl Render + jsr UpdatePlayerLocal + + pea HERO_ID + lda PlayerX + pha + lda PlayerY + pha + pea HERO_SLOT ; Put the player in slot 1 + _GTEAddSprite + + pea HERO_SLOT + pea $0000 + pea HERO_VBUFF ; and use this stamp + _GTEUpdateSprite ; 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 ; Check the buttons first - pha + lda 1,s - bit #$0100 - beq :no_jump - lda PlayerStanding - beq :no_jump - lda #$FFF8 - sta PlayerYVel + bit #$0100 + beq :no_jump + lda PlayerStanding + beq :no_jump + lda #$FFF8 + sta PlayerYVel :no_jump - ; 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 #$0400 + beq :no_key_down + and #$007F + cmp #'v' + bne :not_v + lda #$0001 + eor vsync + sta vsync :not_v - cmp #'f' - bne :not_f - lda SpriteToggle - eor #SPRITE_HIDE - sta SpriteToggle - bne :not_f - stz SpriteCount + cmp #'f' + bne :not_f + lda SpriteToggle + eor #SPRITE_HIDE + sta SpriteToggle + bne :not_f + stz SpriteCount :not_f :no_key_down + pla + and #$007F ; Ignore the buttons for now + cmp #'q' + bne :not_q + brl Exit - pla - and #$007F ; Ignore the buttons for now - - cmp #'q' - bne :not_q - brl Exit :not_q - - cmp #'d' - bne :not_d - lda StartX - cmp MaxBG0X - bcs :do_render - inc - jsl SetBG0XPos - bra :do_render + cmp #'d' + bne :not_d + lda StartX + cmp MaxBG0X + bcc *+5 + brl :do_render + inc StartX + pei StartX + pei StartY + _GTESetBG0Origin + brl :do_render :not_d - cmp #'a' - bne :not_a - lda StartX - beq :do_render - dec - jsl SetBG0XPos - bra :do_render + cmp #'a' + bne :not_a + lda StartX + bne *+5 + brl :do_render + dec StartX + pei StartX + pei StartY + _GTESetBG0Origin + brl :do_render :not_a - cmp #'s' - bne :not_s - lda StartY - cmp MaxBG0Y - bcs :do_render - inc - jsl SetBG0YPos - bra :do_render + cmp #'s' + bne :not_s + lda StartY + cmp MaxBG0Y + bcs :do_render + inc StartY + pei StartX + pei StartY + _GTESetBG0Origin + bra :do_render :not_s - cmp #'w' - bne :not_w - lda StartY - beq :do_render - dec - jsl SetBG0YPos - bra :do_render + cmp #'w' + bne :not_w + lda StartY + beq :do_render + dec StartY + pei StartX + pei StartY + _GTESetBG0Origin + bra :do_render :not_w ; Do j,l to move the character left/right - cmp #'j' - bne :not_j - lda PlayerXVel - bpl :pos_xvel - cmp #$FFFA - bcc :not_j -:pos_xvel dec - dec - sta PlayerXVel - bra :do_render + cmp #'j' + bne :not_j + lda PlayerXVel + bpl :pos_xvel + cmp #$FFFA + bcc :not_j +:pos_xvel dec + dec + sta PlayerXVel + bra :do_render :not_j - cmp #'l' - bne :not_l - lda PlayerXVel - bmi :neg_xvel - cmp #6 - bcs :not_l -:neg_xvel inc - inc - sta PlayerXVel - bra :do_render + cmp #'l' + bne :not_l + lda PlayerXVel + bmi :neg_xvel + cmp #6 + bcs :not_l +:neg_xvel inc + inc + sta PlayerXVel + bra :do_render :not_l - ; Update the camera position - :do_render - jsr UpdatePlayerPos ; Moves in global cordinates - jsr UpdateCameraPos ; Moves the screen - jsr UpdatePlayerLocal ; Gets local sprite coordinates + jsr UpdatePlayerPos ; Moves in global cordinates + jsr UpdateCameraPos ; Moves the screen + jsr UpdatePlayerLocal ; Gets local sprite coordinates - lda PlayerID - ldx PlayerX - ldy PlayerY - jsl MoveSprite ; Move the sprite to this local position + pea HERO_SLOT + lda PlayerX + pha + lda PlayerY + pha + _GTEMoveSprite ; Move the sprite to this local position ; Update the timers - jsl DoTimers +; jsl DoTimers ; Let's see what it looks like! - lda vsync - beq :no_vsync -:vsyncloop jsl GetVerticalCounter ; 8-bit value - cmp ScreenY0 - bcc :vsyncloop - sec - sbc ScreenY0 - cmp #8 - bcs :vsyncloop - lda #1 - jsl SetBorderColor -:no_vsync - jsl Render +; lda vsync +; beq :no_vsync +;:vsyncloop jsl GetVerticalCounter ; 8-bit value +; cmp ScreenY0 +; bcc :vsyncloop +; sec +; sbc ScreenY0 +; cmp #8 +; bcs :vsyncloop +; lda #1 +; jsl SetBorderColor +;:no_vsync + _GTERender - lda vsync - beq :no_vsync2 - lda #0 - jsl SetBorderColor -:no_vsync2 +; lda vsync +; beq :no_vsync2 +; lda #0 +; jsl SetBorderColor +;:no_vsync2 ; Update the performance counters inc frameCount - ldal OneSecondCounter + pha + _GTEGetSeconds + pla cmp oldOneSecondCounter beq :noudt sta oldOneSecondCounter - jsr UdtOverlay +; jsr UdtOverlay stz frameCount :noudt brl EvtLoop ; Exit code Exit - jsl EngineShutDown - + _GTEShutDown _QuitGS qtRec bcs Fatal @@ -331,6 +386,7 @@ MaxBG0Y ds 2 oldOneSecondCounter ds 2 frameCount ds 2 +MyUserId ds 2 PLAYER_X_MIN equ 0 PLAYER_X_MAX equ 160-4 @@ -339,22 +395,33 @@ PLAYER_Y_MAX equ 200-8 EMPTY_TILE equ 33 ; the tile that makes up the background -AdjustLocalX - clc - adc StartXMod164 - cmp #164 - bcc *+5 - sbc #164 - rts -AdjustLocalY - clc - adc StartYMod208 - cmp #208 - bcc *+5 - sbc #208 - rts - SetLimits + pha ; Allocate space for width (in tiles), height (in tiles), pointer + pha + pha + pha + _GTEGetBG0TileMapInfo + pla + sta TileMapWidth + pla + sta TileMapHeight + pla + pla ; discard the pointer + + pha ; Allocate space for x, y, width, height + pha + pha + pha + _GTEGetScreenInfo + pla + pla ; Discard screen corner + pla + sta ScreenWidth + pla + sta ScreenHeight + + + lda TileMapWidth asl asl @@ -373,38 +440,41 @@ SetLimits sta MaxBG0Y rts -; Set the scroll position based on the global cooridinate of the player +; Set the scroll position based on the global coordinates of the player ; Try to center the player on the screen UpdateCameraPos lda ScreenWidth lsr - sta tmp0 + sta appTmp0 lda PlayerGlobalX sec - sbc tmp0 + sbc appTmp0 bpl :x_pos lda #0 :x_pos cmp MaxBG0X bcc :x_ok lda MaxBG0X -:x_ok jsl SetBG0XPos +:x_ok pha ; Push the x-position lda ScreenHeight lsr - sta tmp0 + sta appTmp0 lda PlayerGlobalY sec - sbc tmp0 + sbc appTmp0 bpl :y_pos lda #0 :y_pos cmp MaxBG0Y bcc :y_ok lda MaxBG0Y -:y_ok jsl SetBG0YPos +:y_ok pha ; Push the y-position + _GTESetBG0Origin + pea $0000 lda StartY lsr - jsl SetBG1YPos + pha + _GTESetBG1Origin rts ; Convert the global coordinates to adjusted local coordinated (compensating for wrap-around) @@ -412,13 +482,11 @@ UpdatePlayerLocal lda PlayerGlobalX sec sbc StartX -; jsr AdjustLocalX sta PlayerX lda PlayerGlobalY sec sbc StartY -; jsr AdjustLocalY sta PlayerY rts @@ -431,13 +499,15 @@ UpdatePlayerPos ; Check if the player is standing on the ground at their current local position - ldx PlayerX + lda PlayerX + pha lda PlayerY clc adc #16 - tay - jsr GetTileAt - and #$1FF + pha + _GTEGetTileAt + pla + and #TILE_ID_MASK cmp #EMPTY_TILE beq :no_ground_check @@ -501,7 +571,7 @@ UpdatePlayerPos txa ora LastHFlip - ora #SPRITE_ID + ora #HERO_ID sta SpriteFrame lda SpriteCount @@ -514,7 +584,7 @@ UpdatePlayerPos lda PlayerXVel beq :frame - jsl GetVBLTicks + jsr _GetVBLTicks and #$0003 inc and #$0003 @@ -527,65 +597,21 @@ UpdatePlayerPos tax lda PlayerID - jsl UpdateSprite ; Change the tile ID and / or flags +; jsl UpdateSprite ; Change the tile ID and / or flags +; pea HERO_SLOT +; pei Flips ; with these flags (h/v flip) +; pea VBUFF_SPRITE_START ; and use this stamp +; _GTEUpdateSprite + rts +ToolPath str '1/Tool160' LastHFlip dw 0 SpriteFrame ds 2 SpriteCount dw 0 SpriteToggle dw 0 -; X = coordinate -; Y = coordinate -GetTileAt - txa - bmi :out - clc - adc StartXMod164 - cmp #164 - bcc *+5 - sbc #164 - - lsr - lsr - tax - - tya - bmi :out - clc - adc StartYMod208 - cmp #208 - bcc *+5 - sbc #208 - - lsr - lsr - lsr - tay - - jsl GetTileStoreOffset - tax - ldal TileStore+TS_TILE_ID,x - rts - -:out - lda #EMPTY_TILE - rts - -; Position the screen with the botom-left corner of the tilemap visible -MovePlayerToOrigin - lda #0 ; Set the player's position - jsl SetBG0XPos - - lda TileMapHeight - asl - asl - asl - sec - sbc ScreenHeight - jsl SetBG0YPos - rts openRec dw 2 ; pCount ds 2 ; refNum @@ -657,8 +683,91 @@ msgLine2 str 'Press a key :' msgLine3 str ' -> Return to Try Again' msgLine4 str ' -> Esc to Quit' - PUT ../shell/Overlay.s - PUT gen/App.TileMapBG0.s - PUT gen/App.TileSetAnim.s -ANGLEBNK ENT \ No newline at end of file +; 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 #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER ; Enable Dynamic Tiles and Two Layer + lda MyUserId ; Pass the userId for memory allocation + pha + _GTEStartUp + bcc :ok3 + brk $03 + +:ok3 + rts + +_Deref MAC + phb ; save caller's data bank register + pha ; push high word of handle on stack + plb ; sets B to the bank byte of the pointer + lda |$0002,x ; load the high word of the master pointer + pha ; and save it on the stack + lda |$0000,x ; load the low word of the master pointer + tax ; and return it in X + pla ; restore the high word in A + plb ; pull the handle's high word high byte off the + ; stack + plb ; restore the caller's data bank register + <<< + +AllocBank PushLong #0 + PushLong #$10000 + PushWord MyUserId + PushWord #%11000000_00011100 + PushLong #0 + _NewHandle + plx ; base address of the new handle + pla ; high address 00XX of the new handle (bank) + _Deref + rts + +_GetVBLTicks + PushLong #0 + _GetTick + pla + plx + rts + +; PUT ../shell/Overlay.s + PUT gen/App.TileMapBG0.s +; PUT gen/App.TileSetAnim.s diff --git a/demos/sprites/App.s b/demos/sprites/App.s index 9a1c19e..0b40d41 100644 --- a/demos/sprites/App.s +++ b/demos/sprites/App.s @@ -7,31 +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 + SNA TSET \ No newline at end of file diff --git a/demos/sprites/_FileInformation.txt b/demos/sprites/_FileInformation.txt index 38df996..5120c3d 100644 --- a/demos/sprites/_FileInformation.txt +++ b/demos/sprites/_FileInformation.txt @@ -1 +1,2 @@ GTEZelda=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) +GTETestSprites=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) diff --git a/demos/sprites/build-image.bat b/demos/sprites/build-image.bat index f1f3ca4..df1a762 100644 --- a/demos/sprites/build-image.bat +++ b/demos/sprites/build-image.bat @@ -14,6 +14,7 @@ REM Cadius does not overwrite files, so clear the root folder first REM Now copy files and folders as needed %CADIUS% ADDFILE %IMAGE% %FOLDER% .\GTETestSprites +%CADIUS% ADDFILE %IMAGE% %FOLDER% ..\..\src\Tool160 REM Copy in the image assets %CADIUS% ADDFILE %IMAGE% %FOLDER% .\assets\mario1.c1 diff --git a/demos/sprites/gen/App.TileMapBG0.s b/demos/sprites/gen/App.TileMapBG0.s index 2f69a2a..7eb48ac 100644 --- a/demos/sprites/gen/App.TileMapBG0.s +++ b/demos/sprites/gen/App.TileMapBG0.s @@ -5,14 +5,19 @@ BG0SetUp - lda #416 - sta TileMapWidth - lda #30 - sta TileMapHeight - lda #App_TileMapBG0 - sta TileMapPtr - lda #^App_TileMapBG0 - sta TileMapPtr+2 + pea #416 + pea #30 + pea #^App_TileMapBG0 + pea #App_TileMapBG0 + _GTESetBG0TileMapInfo +; lda #416 +; sta TileMapWidth +; lda #30 +; sta TileMapHeight +; lda #App_TileMapBG0 +; sta TileMapPtr +; lda #^App_TileMapBG0 +; sta TileMapPtr+2 rts App_TileMapBG0 diff --git a/demos/sprites/gen/App.TileSet.s b/demos/sprites/gen/App.TileSet.s index e8b3b9d..ffddae4 100644 --- a/demos/sprites/gen/App.TileSet.s +++ b/demos/sprites/gen/App.TileSet.s @@ -2,7 +2,7 @@ ; Palette: ; $068F,$0EDA,$0000,$0F0F,$0BF1,$00A0,$0EEE,$0456,$0FA4,$0F59,$0E30,$01CE,$02E3,$0870,$0F93,$0FD7 ; Converting to BG0 format... -tiledata ENT +TSet ENT ; Reserved space (tile 0 is special... ds 128 diff --git a/demos/sprites/package.json b/demos/sprites/package.json index 936a5c6..a3a4e0c 100644 --- a/demos/sprites/package.json +++ b/demos/sprites/package.json @@ -15,11 +15,13 @@ "scripts": { "test": "npm run build && build-image.bat %npm_package_config_cadius% && %npm_package_config_gsport%", "debug": "%npm_package_config_crossrunner% GTETestSprites -Source GTETestSprites_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/tiled/world_1-1.json --output-dir ./gen", "build:map:masked": "node %npm_package_config_tiled2iigs% ./assets/tiled/world_1-1.json --force-masked --empty-tile 33 --no-gen-tiles --output-dir ./gen", "build:tiles": "node %npm_package_config_png2iigs% ../shell/assets/tilesets/smb-256-128-4bpp.png --max-tiles 360 --as-tile-data --transparent-color FF00FF --background-color 6B8CFF > ./gen/App.TileSet.s", - "build:tiles:1bpp": "node %npm_package_config_png2iigs% ../shell/assets/tilesets/smb-256-128-1bpp.png --max-tiles 360 --as-tile-data --transparent-color FF00FF --palette 010102,00000,000000,000000,000000,000000,000000,000000,000000,000000,000000,000000,000000,000000,000000,FDFEFE > ./gen/App.TileSet.s" + "build:tiles:1bpp": "node %npm_package_config_png2iigs% ../shell/assets/tilesets/smb-256-128-1bpp.png --max-tiles 360 --as-tile-data --transparent-color FF00FF --palette 010102,00000,000000,000000,000000,000000,000000,000000,000000,000000,000000,000000,000000,000000,000000,FDFEFE > ./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", From c257d720604b7d70428694e059d50666dea5bca8 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sat, 25 Jun 2022 21:21:20 -0500 Subject: [PATCH 69/82] Fix argument handling in SetPalette --- src/Tool.s | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Tool.s b/src/Tool.s index d18d614..9082131 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -383,6 +383,7 @@ _TSCopyTileToDynamic _TSExit #0;#4 +; SetPalette(palNum, Pointer) _TSSetPalette :ptr equ FirstParam+0 :palNum equ FirstParam+4 @@ -390,14 +391,15 @@ _TSSetPalette _TSEntry phb - lda :ptr+2 + lda :ptr+3,s ; add one extra byte for the phb + xba pha plb plb - lda :ptr + lda :ptr+1,s tax - lda :palNum + lda :palNum+1,s jsr _SetPalette plb @@ -526,6 +528,9 @@ _TSSetBG0TileMapInfo lda :ptr+2,s sta TileMapPtr+2 + lda #DIRTY_BIT_BG0_REFRESH ; force a refresh of the BG0 on the next Render + tsb DirtyBits + _TSExit #0;#8 ; SetBG1TileMapInfo(width, height, ptr) @@ -553,6 +558,7 @@ _TSSetBG1TileMapInfo put CoreImpl.s put Memory.s put Timer.s + put TileMap.s put Graphics.s put Tiles.s put Sprite.s From 8aafd5812b38c67697b29d8675903e639f0ecaca Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sun, 26 Jun 2022 22:08:42 -0500 Subject: [PATCH 70/82] Work to expose times via Toolset --- demos/sprites/App.Main.s | 28 +++++++--- demos/sprites/gen/App.TileSetAnim.s | 83 +++++++++++++---------------- macros/GTE.Macs.s | 9 ++++ src/Defs.s | 3 ++ src/Render.s | 2 +- src/Script.s | 32 +++++------ src/TileMap.s | 20 +------ src/Timer.s | 19 ++----- src/Tool.s | 67 ++++++++++++++++++++++- 9 files changed, 156 insertions(+), 107 deletions(-) diff --git a/demos/sprites/App.Main.s b/demos/sprites/App.Main.s index 1ea2077..e8cc24f 100644 --- a/demos/sprites/App.Main.s +++ b/demos/sprites/App.Main.s @@ -68,7 +68,8 @@ ScreenHeight equ 14 ; Set up our level data jsr BG0SetUp -; jsr TileAnimInit + + jsr TileAnimInit jsr SetLimits ; jsr InitOverlay ; Initialize the status bar @@ -145,6 +146,7 @@ HERO_VBUFF equ VBUFF_SPRITE_START+0*VBUFF_SPRITE_STEP HERO_SLOT equ 1 MUSHROOM_ID equ {SPRITE_16X16+255} MUSHROOM_VBUFF equ VBUFF_SPRITE_START+1*VBUFF_SPRITE_STEP +MUSHROOM_SLOT equ 0 pea HERO_ID ; sprint id pea HERO_VBUFF ; vbuff address @@ -153,19 +155,19 @@ MUSHROOM_VBUFF equ VBUFF_SPRITE_START+1*VBUFF_SPRITE_STEP pea MUSHROOM_ID ; sprint id pea MUSHROOM_VBUFF ; vbuff address _GTECreateSpriteStamp - + pea MUSHROOM_ID ; Put the mushroom in Slot 0 pea #80 ; at x=80, y=152 pea #152 - pea $0000 + pea MUSHROOM_SLOT _GTEAddSprite - pea $0000 + pea MUSHROOM_SLOT pea $0000 ; with these flags (h/v flip) pea MUSHROOM_VBUFF ; and use this stamp _GTEUpdateSprite - jsr UpdatePlayerLocal + jsr UpdatePlayerLocal pea HERO_ID lda PlayerX @@ -360,12 +362,23 @@ Exit bcs Fatal Fatal brk $00 +Hold + _GTERender +:busy + pha + _GTEReadControl + pla + and #$00FF + cmp #'q' + bne :busy + jmp Exit + BG1DataFile strl '1/sunset.c1' ; Color palette MyPalette dw $068F,$0EDA,$0000,$0000,$0BF1,$00A0,$0EEE,$0456,$0FA4,$0F59,$0E30,$01CE,$02E3,$0870,$0F93,$0FD7 ; B&W Palette -;MyPalette dw $0000,$0EDA,$0000,$0E51,$0BF1,$00A0,$0EEE,$0456,$0FA4,$0F59,$0E30,$01CE,$02E3,$0870,$0F93,$0FFF +; MyPalette dw $0000,$0EDA,$0000,$0E51,$0BF1,$00A0,$0EEE,$0456,$0FA4,$0F59,$0E30,$01CE,$02E3,$0870,$0F93,$0FFF PlayerGlobalX ds 2 PlayerGlobalY ds 2 @@ -499,6 +512,7 @@ UpdatePlayerPos ; Check if the player is standing on the ground at their current local position + pha ; space for result lda PlayerX pha lda PlayerY @@ -770,4 +784,4 @@ _GetVBLTicks ; PUT ../shell/Overlay.s PUT gen/App.TileMapBG0.s -; PUT gen/App.TileSetAnim.s + PUT gen/App.TileSetAnim.s diff --git a/demos/sprites/gen/App.TileSetAnim.s b/demos/sprites/gen/App.TileSetAnim.s index 1f4d5be..7732391 100644 --- a/demos/sprites/gen/App.TileSetAnim.s +++ b/demos/sprites/gen/App.TileSetAnim.s @@ -1,52 +1,41 @@ -TileAnimInit ENT - - ldx #137 - ldy #0 - jsl CopyTileToDyn - ldx #138 - ldy #1 - jsl CopyTileToDyn - ldx #169 - ldy #2 - jsl CopyTileToDyn - ldx #170 - ldy #3 - jsl CopyTileToDyn - lda #TileAnim_136 - ldx #^TileAnim_136 - ldy #15 - jsl StartScript - lda #TileAnim_137 - ldx #^TileAnim_137 - ldy #15 - jsl StartScript - lda #TileAnim_168 - ldx #^TileAnim_168 - ldy #15 - jsl StartScript - lda #TileAnim_169 - ldx #^TileAnim_169 - ldy #15 - jsl StartScript +TileAnimInit + pea #137 + pea #0 + _GTECopyTileToDynamic + pea #138 + pea #1 + _GTECopyTileToDynamic + pea #169 + pea #2 + _GTECopyTileToDynamic + pea #170 + pea #3 + _GTECopyTileToDynamic rts -TileAnim_136 - dw $8006,137,0,0 - dw $8006,139,0,0 - dw $8006,141,0,0 - dw $cd06,143,0,0 -TileAnim_137 - dw $8006,138,1,0 - dw $8006,140,1,0 - dw $8006,142,1,0 - dw $cd06,144,1,0 -TileAnim_168 - dw $8006,169,2,0 - dw $8006,171,2,0 - dw $8006,173,2,0 - dw $cd06,175,2,0 -TileAnim_169 + + pea #15 + pea #^TileAnim + pea #TileAnim + _GTEStartScript + rts +TileAnim + dw $0006,137,0,0 + dw $0006,138,1,0 + dw $0006,169,2,0 dw $8006,170,3,0 + + dw $0006,139,0,0 + dw $0006,140,1,0 + dw $0006,171,2,0 dw $8006,172,3,0 + + dw $0006,141,0,0 + dw $0006,142,1,0 + dw $0006,173,2,0 dw $8006,174,3,0 - dw $cd06,176,3,0 \ No newline at end of file + + dw $0006,143,0,0 + dw $0006,144,1,0 + dw $0006,175,2,0 + dw $cc46,176,3,0 ; STOP; JUMP(-15) -15 = $31 (6 bit) = %110001 = 1100 0100 = C4 diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s index 137ffcf..6106227 100644 --- a/macros/GTE.Macs.s +++ b/macros/GTE.Macs.s @@ -87,6 +87,15 @@ _GTESetBG0TileMapInfo MAC _GTESetBG1TileMapInfo MAC UserTool $1E00+GTEToolNum <<< +_GTEAddTimer MAC + UserTool $1F00+GTEToolNum + <<< +_GTERemoveTimer MAC + UserTool $2000+GTEToolNum + <<< +_GTEStartScript MAC + UserTool $2100+GTEToolNum + <<< ; EngineMode definitions ; Script definition diff --git a/src/Defs.s b/src/Defs.s index 2e9e66f..5d08b47 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -254,3 +254,6 @@ VBuffArray EXT _stamp_step EXT VBuffVertTableSelect EXT VBuffHorzTableSelect EXT + +; Tool error codes +NO_TIMERS_AVAILABLE equ 10 diff --git a/src/Render.s b/src/Render.s index 98c5403..404ed10 100644 --- a/src/Render.s +++ b/src/Render.s @@ -34,7 +34,7 @@ _Render 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 _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 diff --git a/src/Script.s b/src/Script.s index 2771c8b..4f48a65 100644 --- a/src/Script.s +++ b/src/Script.s @@ -33,17 +33,13 @@ ; ; A pointer to the current command instruction is stored in the first 4 bytes of the ; timer's user data section. -StartScript ENT - phb - jsr _SetDataBank - - phx ; Save the script array address +_StartScript phx ; Save the script array address pha lda #_DoScriptSeq ; Try to create a timer for this script ldx #^_DoScriptSeq clc - jsl AddTimer + jsr _AddTimer bcs :err ; No timer slots available :( tax ; Initialize the UserData with the command pointer @@ -52,14 +48,12 @@ StartScript ENT pla sta Timers+10,x - plb - rtl + rts :err pla ; Pop the values and return with the carry flag set pla - plb - rtl + rts ; This routine executes script command until it encounters one with the STOP bit set. In some ; sense, the stop bit acts like a "yield" in high-level languages. @@ -107,18 +101,20 @@ _dss_cmd_rtn ; to the next entry. bit #JUMP ; Just do a fall through and set the jump offset to bne :move_addr ; a hard-coded value of 1 if the jump bit is not set -:retry lda #$0100 -:move_addr and #$0F00 ; mask out the number of commands to move +:retry lda #$0040 +:move_addr and #$0FC0 ; mask out the number of commands to move beq :retry ; Don't allow zeros; will cause infinite loop. Just advance by one. - xba ; put it in the low byte - cmp #$0008 ; Sign-extend the 4-bit value + cmp #$0800 ; Sign-extend the 6-bit value bcc *+5 - ora #$FFF0 + ora #$F000 - asl ; multiply by 8 - asl - asl + cmp #$8000 ; make it a multiple of 8 (preserve sign) + ror + cmp #$8000 + ror + cmp #$8000 + ror clc adc 3,s ; add it to the saved command address sta 3,s diff --git a/src/TileMap.s b/src/TileMap.s index c6e0326..9fa1b5c 100644 --- a/src/TileMap.s +++ b/src/TileMap.s @@ -10,12 +10,6 @@ ; in actual games since the primary background is often large empty areas, or runs ; of repeating tiles. -; Debug locations -LastTop ds 2 -LastBottom ds 2 -LastLeft ds 2 -LastRight ds 2 - ; The ranges are [:Left, :Right] and [:Top, :Bottom], so :Right can be, at most, 40 ; if we are drawing all 41 tiles (Index 0 through 40). The :Bottom value can be ; at most 25. @@ -222,17 +216,6 @@ _UpdateBG0TileMap :NoXUpdate rts -;:Debug -; lda :Top ; Debugging -; sta LastTop -; lda :Bottom -; sta LastBottom -; lda :Left -; sta LastLeft -; lda :Right -; sta LastRight -; rts - ; This is a private subroutine that draws in tiles into the code fields using the ; data from the tilemap and the local :Top, :Left, :Bottom and :Right parameters. :DrawRectBG0 @@ -639,7 +622,7 @@ _DrawRectBG1 ldx :BlkX ldy :BlkY - jsr _CopyBG1Tile +; jsr _CopyBG1Tile lda :BlkX inc @@ -676,4 +659,3 @@ _DrawRectBG1 pla rts - diff --git a/src/Timer.s b/src/Timer.s index be34162..5731c8d 100644 --- a/src/Timer.s +++ b/src/Timer.s @@ -31,16 +31,12 @@ InitTimers ; Return ; C = 0 if success, 1 if no timer slots are available ; A = timer slot ID if C = 0 -AddTimer ENT - phb - +_AddTimer php ; Save the input parameters phx pha phy - jsr _SetDataBank - ldx #0 :loop lda Timers,x ; If the counter is zero, timer is free beq :freeslot @@ -72,8 +68,7 @@ AddTimer ENT lda Timers+0,x ; if not a one-shot, put the counter sta Timers+2,x ; value into the reset field -:oneshot plb - txa ; return the slot ID and a success status +:oneshot txa ; return the slot ID and a success status clc rtl @@ -81,18 +76,15 @@ AddTimer ENT pla plx plp - plb sec ; Return an error status lda #0 - rtl + rts ; Small function to remove a timer ; ; A = Timer ID -RemoveTimer ENT - phb - jsr _SetDataBank +_RemoveTimer cmp #{TIMER_REC_SIZE*{MAX_TIMERS-1}}+1 bcs :exit @@ -103,8 +95,7 @@ RemoveTimer ENT stz Timers+6,x :exit - plb - rtl + rts ; Execute the timer functions ;DoTimers ENT diff --git a/src/Tool.s b/src/Tool.s index 9082131..471b3fd 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -30,6 +30,14 @@ _TSExit mac jml ToStrip <<< +_TSExit1 mac + plb + pld +; ldx ]1 ; Error code already in X + ldy ]1 ; Number of stack bytes to remove + jml ToStrip + <<< + FirstParam equ 10 ; When using the _TSEntry macro, the first parameter is at 10,s mx %00 @@ -71,6 +79,10 @@ _CallTable adrl _TSSetBG0TileMapInfo-1 adrl _TSSetBG1TileMapInfo-1 + + adrl _TSAddTimer-1 + adrl _TSRemoveTimer-1 + adrl _TSStartScript-1 _CTEnd _GTEAddSprite MAC UserTool $1000+GTEToolNum @@ -551,13 +563,66 @@ _TSSetBG1TileMapInfo sta TileMapPtr+2 _TSExit #0;#8 - + +; AddTimer(numTicks, callback, flags) +_TSAddTimer +:flags equ FirstParam+0 +:callback equ FirstParam+2 +:numTicks equ FirstParam+6 +:output equ FirstParam+8 + + _TSEntry + + lda :callback+2,s + tax + lda :callback,s + tay + lda :flags,s + lsr ; put low bit into carry + lda :numTicks,s + jsr _AddTimer + sta :output,s + ldx #0 + bcc :no_err + ldx #NO_TIMERS_AVAILABLE +:no_err + _TSExit1 #8 + +; RemoveTimer(timerId) +_TSRemoveTimer +:timerId equ FirstParam+0 + + _TSEntry + + lda :timerId,s + jsr _RemoveTimer + + _TSExit #0;#2 + + +; StartScript(timerId) +_TSStartScript +:scriptAddr equ FirstParam+0 +:numTicks equ FirstParam+4 + + _TSEntry + + lda :numTicks,s + tay + lda :scriptAddr+2,s + tax + lda :scriptAddr,s + jsr _StartScript + + _TSExit #0;#6 + ; Insert the GTE code put Math.s put CoreImpl.s put Memory.s put Timer.s + put Script.s put TileMap.s put Graphics.s put Tiles.s From b7d061e666c4ddc38a7e8d3639d046e9947d7951 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sun, 26 Jun 2022 23:32:15 -0500 Subject: [PATCH 71/82] Fix simple script handling --- demos/sprites/gen/App.TileSetAnim.s | 1 - src/GTE.s | 2 -- src/Script.s | 4 ++-- src/Timer.s | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/demos/sprites/gen/App.TileSetAnim.s b/demos/sprites/gen/App.TileSetAnim.s index 7732391..142fb2d 100644 --- a/demos/sprites/gen/App.TileSetAnim.s +++ b/demos/sprites/gen/App.TileSetAnim.s @@ -12,7 +12,6 @@ TileAnimInit pea #170 pea #3 _GTECopyTileToDynamic - rts pea #15 pea #^TileAnim diff --git a/src/GTE.s b/src/GTE.s index cc08f42..9af47d7 100644 --- a/src/GTE.s +++ b/src/GTE.s @@ -20,8 +20,6 @@ SetBG1XPos EXT SetBG1YPos EXT CopyBG0Tile EXT CopyBG1Tile EXT -CopyTileToDyn EXT -Render EXT ; SCB/Palette binding (high bit of array point indicates whether to bind to BG0 Y position (0) ; or BG1 Y position (1). diff --git a/src/Script.s b/src/Script.s index 4f48a65..87028a7 100644 --- a/src/Script.s +++ b/src/Script.s @@ -47,7 +47,6 @@ _StartScript phx ; Save the script array address sta Timers+8,x pla sta Timers+10,x - rts :err @@ -84,6 +83,7 @@ _dss_loop phx ; Save the command address txy ; Cache in the y-register lda: 0,x ; Load the command word + pha ; Stash it and #$001E ; Only have 16 built-in commands. Use the _UserCallback @@ -162,7 +162,7 @@ _SetDTile ldx: ARG1,y lda: ARG2,y tay - jsl CopyTileToDyn + jsr CopyTileToDyn brl _dss_cmd_rtn _UserCallback diff --git a/src/Timer.s b/src/Timer.s index 5731c8d..61f3332 100644 --- a/src/Timer.s +++ b/src/Timer.s @@ -70,7 +70,7 @@ _AddTimer :oneshot txa ; return the slot ID and a success status clc - rtl + rts :notimers ply pla From 145fd2afc4f9f235fd0a7aca67e00ade3389e3c9 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sun, 26 Jun 2022 23:42:21 -0500 Subject: [PATCH 72/82] Fix argument definitions --- src/Tool.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tool.s b/src/Tool.s index 471b3fd..118c018 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -462,7 +462,7 @@ _TSGetBG0TileMapInfo _TSGetScreenInfo -:height equ FirstParam+4 +:height equ FirstParam+6 :width equ FirstParam+4 :y equ FirstParam+2 :x equ FirstParam+0 From c501e9f94430cd66156be9de5b60436ef751c7d7 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sun, 26 Jun 2022 23:50:03 -0500 Subject: [PATCH 73/82] Fix positioning bug --- demos/sprites/App.Main.s | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/demos/sprites/App.Main.s b/demos/sprites/App.Main.s index e8cc24f..7e278fb 100644 --- a/demos/sprites/App.Main.s +++ b/demos/sprites/App.Main.s @@ -433,8 +433,6 @@ SetLimits pla sta ScreenHeight - - lda TileMapWidth asl asl @@ -467,7 +465,7 @@ UpdateCameraPos :x_pos cmp MaxBG0X bcc :x_ok lda MaxBG0X -:x_ok pha ; Push the x-position +:x_ok sta StartX lda ScreenHeight lsr @@ -480,7 +478,10 @@ UpdateCameraPos :y_pos cmp MaxBG0Y bcc :y_ok lda MaxBG0Y -:y_ok pha ; Push the y-position +:y_ok sta StartY + + pei StartX + pei StartY _GTESetBG0Origin pea $0000 From 81e0aeb351a532282c1b6ee59124555bad41a06c Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 27 Jun 2022 00:32:44 -0500 Subject: [PATCH 74/82] Fix some minor bugs in the TwoLayer rendering functions --- demos/sprites/App.Main.s | 2 +- src/Render.s | 6 +++--- src/Tiles.s | 2 +- src/render/TwoLayer.s | 22 ++++++++-------------- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/demos/sprites/App.Main.s b/demos/sprites/App.Main.s index 7e278fb..e2f9747 100644 --- a/demos/sprites/App.Main.s +++ b/demos/sprites/App.Main.s @@ -85,7 +85,7 @@ ScreenHeight equ 14 sta BankLoad ; Store "Bank Pointer" ; Load in the 256 color background into BG1 buffer - brl :nobackground +; brl :nobackground DoLoadBG1 lda BankLoad ldx #BG1DataFile diff --git a/src/Render.s b/src/Render.s index 404ed10..56d73f8 100644 --- a/src/Render.s +++ b/src/Render.s @@ -25,12 +25,12 @@ _Render stz SpriteRemovedFlag ; If we remove a sprite, then we need to flag a rebuild for the next frame jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen -; jsr _ApplyBG1YPos + jsr _ApplyBG1YPos ; _ApplyBG0Xpos need to be split because we have to set the offsets, then draw in any updated tiles, and ; finally patch out the code field. Right now, the BRA operand is getting overwritten by tile data. jsr _ApplyBG0XPosPre -; jsr _ApplyBG1XPosPre + jsr _ApplyBG1XPosPre jsr _RenderSprites ; Once the BG0 X and Y positions are committed, update sprite data @@ -40,7 +40,7 @@ _Render jsr _ApplyTilesFast ; This function actually draws the new tiles into the code field jsr _ApplyBG0XPos ; Patch the code field instructions with exit BRA opcode -; jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position + jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position ; The code fields are locked in now and ready to be rendered diff --git a/src/Tiles.s b/src/Tiles.s index aa99e57..ad55581 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -257,7 +257,7 @@ _SetTile ldy #TwoLyrDynProcs brl :pickDynProc -:pickTwoLyrProc ldy #SlowProcs ; #TwoLyrProcs +:pickTwoLyrProc ldy #TwoLyrProcs lda procIdx jsr _SetTileProcs jmp _PushDirtyTileX diff --git a/src/render/TwoLayer.s b/src/render/TwoLayer.s index 738445f..8ca2df6 100644 --- a/src/render/TwoLayer.s +++ b/src/render/TwoLayer.s @@ -2,6 +2,7 @@ ; routines, there's nothing in here that is particularly well optimized. Tile0TwoLyr + ldal TileStore+TS_WORD_OFFSET,x and #$00FF ora #$4800 sta: $0004,y @@ -42,6 +43,7 @@ Tile0TwoLyr sta $7000,y sta $7003,y rep #$20 + plb rts ; Draw from the sprite buffer into a fully transparent tile @@ -140,20 +142,16 @@ _TmpTileDataToCodeField ; Copy a tile into the tile data buffer and then render to the code field CopyTileATwoLyr - lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + ldal TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler sta _JTBL_CACHE - lda TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2) + ldal TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2) ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand xba sta _OP_CACHE - lda TileStore+TS_CODE_ADDR_HIGH,x - pha - ldy TileStore+TS_CODE_ADDR_LOW,x - lda TileStore+TS_TILE_ADDR,x + ldal TileStore+TS_TILE_ADDR,x tax - plb ]line equ 0 lup 8 @@ -171,20 +169,16 @@ CopyTileATwoLyr jmp _TmpTileDataToCodeField CopyTileVTwoLyr - lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + ldal TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler sta _JTBL_CACHE - lda TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2) + ldal TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2) ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand xba sta _OP_CACHE - lda TileStore+TS_CODE_ADDR_HIGH,x - pha - ldy TileStore+TS_CODE_ADDR_LOW,x - lda TileStore+TS_TILE_ADDR,x + ldal TileStore+TS_TILE_ADDR,x tax - plb ]src equ 7 ]dest equ 0 From a51c97386526d3ef07cdb55dd9c8427705985356 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 27 Jun 2022 00:41:02 -0500 Subject: [PATCH 75/82] Fix addressing bug in SCB binding --- src/blitter/SCB.s | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/blitter/SCB.s b/src/blitter/SCB.s index 266010b..6e48756 100644 --- a/src/blitter/SCB.s +++ b/src/blitter/SCB.s @@ -44,7 +44,7 @@ _BltSCB inc clc adc #:scb_end - sta :entry+1 + stal :entry+1 lda ScreenY1 ; Get the SCB address to put into the stack register dec @@ -74,12 +74,6 @@ _BltSCB plb ; restore the bank rts - -; Quick helper to set the pointer (X = low word, A = high work) -;SetSCBArray ENT -; jsr _SetSCBArray -; rtl - _BindSCBArray stx SCBArrayPtr sta SCBArrayPtr+2 From b0affa1b723ae5a32b0aa82a8d4fe545150d05e4 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 27 Jun 2022 00:41:30 -0500 Subject: [PATCH 76/82] Add check to bind SCBs in the render function --- src/Render.s | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Render.s b/src/Render.s index 56d73f8..618211b 100644 --- a/src/Render.s +++ b/src/Render.s @@ -74,9 +74,9 @@ _Render ldy ScreenHeight jsr _BltRange -; ldx #0 -; ldy ScreenHeight -; jsr _BltSCB + ldx #0 + ldy ScreenHeight + jsr _BltSCB lda StartYMod208 ; Restore the fields back to their original state ldx ScreenHeight From a7dad98d50c07194fae3d7b8292953b7db82c776 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 27 Jun 2022 11:24:04 -0500 Subject: [PATCH 77/82] Additional tweaks to get old sprite demo working --- demos/shell/Overlay.s | 245 +++++++++++++++++++++++++++++++-------- demos/sprites/App.Main.s | 17 +-- src/CoreImpl.s | 3 - src/Defs.s | 1 + src/Render.s | 57 ++++++--- src/Tool.s | 49 +++++++- src/static/TileStore.s | 3 + 7 files changed, 301 insertions(+), 74 deletions(-) diff --git a/demos/shell/Overlay.s b/demos/shell/Overlay.s index e0a7b7d..ee3fb46 100644 --- a/demos/shell/Overlay.s +++ b/demos/shell/Overlay.s @@ -10,7 +10,27 @@ ; There are two subroutines that need to be implemented -- one to update the overlay content and a ; second to actually render to the screen -; Initialize the overlay be drawin gin static content that will not change over time +STATE_REG equ $E0C068 + +_R0W0 mac ; Read Bank 0 / Write Bank 0 + ldal STATE_REG + and #$FFCF + stal STATE_REG + <<< + +_R0W1 mac ; Read Bank 0 / Write Bank 1 + ldal STATE_REG + ora #$0010 + stal STATE_REG + <<< + +_R1W1 mac ; Read Bank 0 / Write Bank 1 + ldal STATE_REG + ora #$0030 + stal STATE_REG + <<< + +; Initialize the overlay be drawing in static content that will not change over time CHAR_TILE_BASE equ 193 ; set this to the real tile id that starts an ASCII run starting at '0' through 'Z' @@ -32,35 +52,57 @@ l_mask equ ovrly_mask MASK_OFFSET equ {ovrly_mask-ovrly_buff} +TileDataPtr equ $FC +TileMaskPtr equ $F8 + InitOverlay + + pha + pha + _GTEGetTileDataAddr + pla + sta TileDataPtr + clc + adc #32 + sta TileMaskPtr + pla + sta TileDataPtr+2 + sta TileMaskPtr+2 + lda #'F' - ldy #l_line+{CHAR_WIDTH*0} + ldx #l_line+{CHAR_WIDTH*0} jsr _DrawChar lda #'P' - ldy #l_line+{CHAR_WIDTH*1} + ldx #l_line+{CHAR_WIDTH*1} jsr _DrawChar lda #'S' - ldy #l_line+{CHAR_WIDTH*2} + ldx #l_line+{CHAR_WIDTH*2} jsr _DrawChar lda #':' - ldy #l_line+{CHAR_WIDTH*3} + ldx #l_line+{CHAR_WIDTH*3} jsr _DrawChar lda #'T' - ldy #r_line+{CHAR_WIDTH*0} + ldx #r_line+{CHAR_WIDTH*0} jsr _DrawChar lda #'I' - ldy #r_line+{CHAR_WIDTH*1} + ldx #r_line+{CHAR_WIDTH*1} jsr _DrawChar lda #'C' - ldy #r_line+{CHAR_WIDTH*2} + ldx #r_line+{CHAR_WIDTH*2} jsr _DrawChar lda #'K' - ldy #r_line+{CHAR_WIDTH*3} + ldx #r_line+{CHAR_WIDTH*3} jsr _DrawChar lda #':' - ldy #r_line+{CHAR_WIDTH*4} + ldx #r_line+{CHAR_WIDTH*4} jsr _DrawChar + + pea $0000 + pea $0008 + pea #^StatusBar + pea #StatusBar + _GTESetOverlay rts ; Update the dynamic content of the overlay @@ -79,8 +121,8 @@ UdtOverlay lda frameCount ; render the FPS value xba jsr _num2ascii - ldy #l_line+{CHAR_WIDTH*4} - jsr _DrawChar + ldx #l_line+{CHAR_WIDTH*4} + jsr _DrawChar lda frameCount lsr @@ -88,44 +130,52 @@ UdtOverlay lsr lsr jsr _num2ascii - ldy #l_line+{CHAR_WIDTH*5} + ldx #l_line+{CHAR_WIDTH*5} jsr _DrawChar lda frameCount jsr _num2ascii - ldy #l_line+{CHAR_WIDTH*6} + ldx #l_line+{CHAR_WIDTH*6} jsr _DrawChar - ldal OneSecondCounter ; reder the number of remaining seconds + pha + _GTEGetSeconds + pla + sta oneSecondCounter ; render the number of remaining seconds xba jsr _num2ascii - ldy #r_line+{CHAR_WIDTH*5} + ldx #r_line+{CHAR_WIDTH*5} jsr _DrawChar - ldal OneSecondCounter + lda oneSecondCounter lsr lsr lsr lsr jsr _num2ascii - ldy #r_line+{CHAR_WIDTH*6} + ldx #r_line+{CHAR_WIDTH*6} jsr _DrawChar - ldal OneSecondCounter + lda oneSecondCounter jsr _num2ascii - ldy #r_line+{CHAR_WIDTH*7} - jsr _DrawChar + ldx #r_line+{CHAR_WIDTH*7} + jsr _DrawChar rts +oneSecondCounter ds 2 + ; Draw the overlay ; A = address of the left edge of the screen -Overlay ENT - phb ; Called via JSL +StatusBar phb ; Called via JSL + phd ; save the direct page register + phk plb - phd ; save the direct page register + ldx MyDirectPage ; Preserve the accumulator + phx + pld sta l_addr ; save this value (will go into D-reg later) clc @@ -144,12 +194,12 @@ Overlay ENT sec sbc m_addr ; calculate the number of words between the two ends and #$FFFE - pha - lda #m_end - sec - sbc 1,s + + eor #$FFFF + inc + clc + adc #m_end sta m_patch+1 - pla sei _R1W1 @@ -192,7 +242,7 @@ l_ovrly_rtn _R0W0 cli -:exit +o_exit pld ; restore the direct page and bank and return plb rtl @@ -201,7 +251,6 @@ l_addr ds 2 m_addr ds 2 r_addr ds 2 - r_ovrly ]idx equ 0 lup R_CHAR_COUNT @@ -247,10 +296,7 @@ m_end ; ; A = Tile ID ; Y = overlay address location -tiledata EXT - _DCOut rts - _DrawChar cmp #'0' bcc _DCOut @@ -261,19 +307,122 @@ _DrawChar sbc #'0' clc adc #CHAR_TILE_BASE - jsl GetTileAddr - tax + jsr _GetTileAddr + tay -]idx equ 0 - lup 8 - ldal tiledata+32+{]idx*4},x - sta: {]idx*OVRLY_SPAN}+MASK_OFFSET,y - ldal tiledata+{]idx*4},x - sta: {]idx*OVRLY_SPAN},y - ldal tiledata+32+{]idx*4}+2,x - sta: {]idx*OVRLY_SPAN}+MASK_OFFSET+2,y - ldal tiledata+{]idx*4}+2,x - sta: {]idx*OVRLY_SPAN}+2,y -]idx equ ]idx+1 - --^ + lda [TileMaskPtr],y + sta: {0*OVRLY_SPAN}+MASK_OFFSET,x + lda [TileDataPtr],y + sta: {0*OVRLY_SPAN},x + iny + iny + lda [TileMaskPtr],y + sta: {0*OVRLY_SPAN}+MASK_OFFSET+2,x + lda [TileDataPtr],y + sta: {0*OVRLY_SPAN}+2,x + iny + iny + + lda [TileMaskPtr],y + sta: {1*OVRLY_SPAN}+MASK_OFFSET,x + lda [TileDataPtr],y + sta: {1*OVRLY_SPAN},x + iny + iny + lda [TileMaskPtr],y + sta: {1*OVRLY_SPAN}+MASK_OFFSET+2,x + lda [TileDataPtr],y + sta: {1*OVRLY_SPAN}+2,x + iny + iny + + lda [TileMaskPtr],y + sta: {2*OVRLY_SPAN}+MASK_OFFSET,x + lda [TileDataPtr],y + sta: {2*OVRLY_SPAN},x + iny + iny + lda [TileMaskPtr],y + sta: {2*OVRLY_SPAN}+MASK_OFFSET+2,x + lda [TileDataPtr],y + sta: {2*OVRLY_SPAN}+2,x + iny + iny + + lda [TileMaskPtr],y + sta: {3*OVRLY_SPAN}+MASK_OFFSET,x + lda [TileDataPtr],y + sta: {3*OVRLY_SPAN},x + iny + iny + lda [TileMaskPtr],y + sta: {3*OVRLY_SPAN}+MASK_OFFSET+2,x + lda [TileDataPtr],y + sta: {3*OVRLY_SPAN}+2,x + iny + iny + + lda [TileMaskPtr],y + sta: {4*OVRLY_SPAN}+MASK_OFFSET,x + lda [TileDataPtr],y + sta: {4*OVRLY_SPAN},x + iny + iny + lda [TileMaskPtr],y + sta: {4*OVRLY_SPAN}+MASK_OFFSET+2,x + lda [TileDataPtr],y + sta: {4*OVRLY_SPAN}+2,x + iny + iny + + lda [TileMaskPtr],y + sta: {5*OVRLY_SPAN}+MASK_OFFSET,x + lda [TileDataPtr],y + sta: {5*OVRLY_SPAN},x + iny + iny + lda [TileMaskPtr],y + sta: {5*OVRLY_SPAN}+MASK_OFFSET+2,x + lda [TileDataPtr],y + sta: {5*OVRLY_SPAN}+2,x + iny + iny + + lda [TileMaskPtr],y + sta: {6*OVRLY_SPAN}+MASK_OFFSET,x + lda [TileDataPtr],y + sta: {6*OVRLY_SPAN},x + iny + iny + lda [TileMaskPtr],y + sta: {6*OVRLY_SPAN}+MASK_OFFSET+2,x + lda [TileDataPtr],y + sta: {6*OVRLY_SPAN}+2,x + iny + iny + + lda [TileMaskPtr],y + sta: {7*OVRLY_SPAN}+MASK_OFFSET,x + lda [TileDataPtr],y + sta: {7*OVRLY_SPAN},x + iny + iny + lda [TileMaskPtr],y + sta: {7*OVRLY_SPAN}+MASK_OFFSET+2,x + lda [TileDataPtr],y + sta: {7*OVRLY_SPAN}+2,x + + rts + +_GetTileAddr + asl ; Multiply by 2 + bit #2*TILE_HFLIP_BIT ; Check if the horizontal flip bit is set + beq :no_flip + inc ; Set the LSB +:no_flip asl ; x4 + asl ; x8 + asl ; x16 + asl ; x32 + asl ; x64 + asl ; x128 rts \ No newline at end of file diff --git a/demos/sprites/App.Main.s b/demos/sprites/App.Main.s index e2f9747..f5d042f 100644 --- a/demos/sprites/App.Main.s +++ b/demos/sprites/App.Main.s @@ -38,6 +38,9 @@ ScreenHeight equ 14 plb sta MyUserId ; GS/OS passes the memory manager user ID for the application into the program + tdc + sta MyDirectPage ; Keep a copy for the overlay callback + _MTStartUp ; GTE requires the miscellaneous toolset to be running jsr GTEStartUp ; Load and install the GTE User Tool @@ -68,17 +71,16 @@ ScreenHeight equ 14 ; Set up our level data jsr BG0SetUp - jsr TileAnimInit jsr SetLimits -; jsr InitOverlay ; Initialize the status bar + jsr InitOverlay ; Initialize the status bar stz frameCount pha _GTEGetSeconds pla sta oldOneSecondCounter -; jsr UdtOverlay + jsr UdtOverlay ; Allocate a buffer for loading files jsl AllocBank ; Alloc 64KB for Load/Unpack @@ -349,7 +351,7 @@ EvtLoop cmp oldOneSecondCounter beq :noudt sta oldOneSecondCounter -; jsr UdtOverlay + jsr UdtOverlay stz frameCount :noudt brl EvtLoop @@ -400,6 +402,7 @@ MaxBG0Y ds 2 oldOneSecondCounter ds 2 frameCount ds 2 MyUserId ds 2 +MyDirectPage ds 2 PLAYER_X_MIN equ 0 PLAYER_X_MAX equ 160-4 @@ -783,6 +786,6 @@ _GetVBLTicks plx rts -; PUT ../shell/Overlay.s - PUT gen/App.TileMapBG0.s - PUT gen/App.TileSetAnim.s + PUT ../shell/Overlay.s + PUT gen/App.TileMapBG0.s + PUT gen/App.TileSetAnim.s diff --git a/src/CoreImpl.s b/src/CoreImpl.s index a514c03..fc1dd8c 100644 --- a/src/CoreImpl.s +++ b/src/CoreImpl.s @@ -71,9 +71,6 @@ TileStore EXT spritedata EXT spritemask EXT -; If there are overlays, they are provided as an external -Overlay EXT - ; Core engine functionality. The idea is that that source file can be PUT into ; a main source file and all of the functionality will be available. ; diff --git a/src/Defs.s b/src/Defs.s index 5d08b47..0ef0580 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -254,6 +254,7 @@ VBuffArray EXT _stamp_step EXT VBuffVertTableSelect EXT VBuffHorzTableSelect EXT +Overlays EXT ; Tool error codes NO_TIMERS_AVAILABLE equ 10 diff --git a/src/Render.s b/src/Render.s index 618211b..2a322f0 100644 --- a/src/Render.s +++ b/src/Render.s @@ -42,37 +42,47 @@ _Render 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 +; The code fields are locked in now and ready to be rendered. See if there is an overlay or any +; other reason to render with shadowing off. Otherwise, just do things quickly. -; jsr _ShadowOff + lda Overlays + beq :no_ovrly + + jsr _ShadowOff ; Shadowing is turned off. Render all of the scan lines that need a second pass. 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. -; ldx #0 ; Blit the full virtual buffer to the screen -; ldy #8 -; jsr _BltRange + ldx Overlays+2 ; Blit the full virtual buffer to the screen + ldy Overlays+4 + jsr _BltRange ; Turn shadowing back on -; jsr _ShadowOn + jsr _ShadowOn ; Now render all of the remaining lines in top-to-bottom (or bottom-to-top) order -; lda ScreenY0 ; pass the address of the first line of the overlay -; clc -; adc #0 -; asl -; tax -; lda ScreenAddr,x -; clc -; adc ScreenX0 -; jsl Overlay + ldx #0 + ldy Overlays+2 + beq :skip + jsr _BltRange +:skip + jsr _DoOverlay + ldx Overlays+4 + cpx ScreenHeight + beq :done + ldy ScreenHeight + jsr _BltRange + bra :done + +:no_ovrly ldx #0 ; Blit the full virtual buffer to the screen ldy ScreenHeight jsr _BltRange +:done ldx #0 ldy ScreenHeight @@ -102,6 +112,23 @@ _Render :no_removal rts +_DoOverlay + lda Overlays+6 + stal :disp+1 + lda Overlays+7 + stal :disp+2 + + lda ScreenY0 ; pass the address of the first line of the overlay + clc + adc Overlays+2 + asl + tax + lda ScreenAddr,x + clc + adc ScreenX0 +:disp jsl $000000 + rts + ; The _ApplyTilesFast is the same as _ApplyTiles, but we use the _RenderTileFast subroutine _ApplyTilesFast ldx DirtyTileCount diff --git a/src/Tool.s b/src/Tool.s index 118c018..9e00188 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -83,6 +83,11 @@ _CallTable adrl _TSAddTimer-1 adrl _TSRemoveTimer-1 adrl _TSStartScript-1 + + adrl _TSSetOverlay-1 + adrl _TSClearOverlay-1 + + adrl _TSGetTileDataAddr-1 _CTEnd _GTEAddSprite MAC UserTool $1000+GTEToolNum @@ -472,7 +477,6 @@ _TSGetScreenInfo sta :x,s lda ScreenY0 sta :y,s - sta :width,s lda ScreenWidth sta :width,s lda ScreenHeight @@ -615,6 +619,49 @@ _TSStartScript jsr _StartScript _TSExit #0;#6 +; SetOverlay(top, bottom, proc) +_TSSetOverlay +:proc equ FirstParam+0 +:bottom equ FirstParam+4 +:top equ FirstParam+6 + + _TSEntry + + lda #1 + sta Overlays + lda :top,s + sta Overlays+2 + lda :bottom,s + sta Overlays+4 + lda :proc,s + sta Overlays+6 + lda :proc+2,s + sta Overlays+8 + + _TSExit #0;#8 + +; ClearOverlay() +_TSClearOverlay + + _TSEntry + + lda #0 + sta Overlays + + _TSExit #0;#0 + +; GetTileDataAddr() +_TSGetTileDataAddr +:output equ FirstParam+0 + + _TSEntry + + lda #tiledata + sta :output,s + lda #^tiledata + sta :output+2,s + + _TSExit #0;#0 ; Insert the GTE code diff --git a/src/static/TileStore.s b/src/static/TileStore.s index 7b13242..06c218c 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -388,6 +388,9 @@ OldOneSecVec ENT ds 4 Timers ENT ds TIMER_REC_SIZE*MAX_TIMERS +Overlays ENT + dw 0 ; count + ds 8 ; only support one or now (start_line, end_line, function call) ; From the IIgs ref DefaultPalette ENT From 57ffa8794f9d12511e3f5d9f93f3c2e6e846fbb3 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 27 Jun 2022 11:25:08 -0500 Subject: [PATCH 78/82] Add in some missed updates --- demos/shell/App.Main.s | 107 +++++++++++++++++++++++++--------- demos/shell/App.s | 25 +------- demos/shell/gen/App.TileSet.s | 2 +- demos/tool/App.Main.s | 2 +- 4 files changed, 85 insertions(+), 51 deletions(-) diff --git a/demos/shell/App.Main.s b/demos/shell/App.Main.s index ee941cb..5c2c196 100644 --- a/demos/shell/App.Main.s +++ b/demos/shell/App.Main.s @@ -9,11 +9,14 @@ use Tool222.Macs.s use Util.Macs.s use CORE.MACS.s - use ../../src/GTE.s + use GTE.Macs + use ../../src/Defs.s mx %00 +TSet EXT + ; Feature flags NO_INTERRUPTS equ 0 ; turn off for crossrunner debugging NO_MUSIC equ 1 ; turn music + tool loading off @@ -22,40 +25,39 @@ NO_MUSIC equ 1 ; turn music + tool loadi phk plb - jsl EngineStartUp + 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 - lda #^MyPalette - ldx #MyPalette - ldy #0 - jsl SetPalette + jsr GTEStartUp ; Load and install the GTE User Tool - ldx #0 - jsl SetScreenMode +; Load a tileset + + pea #^TSet + pea #TSet + _GTELoadTileSet + + + pea $0000 + pea #^MyPalette + pea #MyPalette + _GTESetPalette + + pea $0000 + _GTESetScreenMode ; Set up our level data - jsr BG0SetUp - jsr BG1SetUp - jsr TileAnimInit + jsr BG0SetUp + jsr BG1SetUp + jsr TileAnimInit ; Allocate room to load data - jsl AllocBank ; Alloc 64KB for Load/Unpack - sta BankLoad ; Store "Bank Pointer" + jsl AllocBank ; Alloc 64KB for Load/Unpack + sta BankLoad ; Store "Bank Pointer" - jsr MovePlayerToOrigin ; Put the player at the beginning of the map - - jsr InitOverlay ; Initialize the status bar + jsr MovePlayerToOrigin ; Put the player at the beginning of the map + jsr InitOverlay ; Initialize the status bar - lda #DIRTY_BIT_BG0_REFRESH ; Redraw all of the tiles on the next Render - ora #DIRTY_BIT_BG1_REFRESH - tsb DirtyBits - - stz frameCount - ldal OneSecondCounter - sta oldOneSecondCounter - - lda #$FFFF - jsl Render EvtLoop jsl DoTimers jsl Render @@ -439,6 +441,59 @@ closeRec dw 1 ; pCount qtRec adrl $0000 da $00 + +; 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 #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER ; Enable Dynamic Tiles and Two Layer + lda MyUserId ; Pass the userId for memory allocation + pha + _GTEStartUp + bcc :ok3 + brk $03 + +:ok3 + rts + PUT App.Msg.s PUT Actions.s PUT font.s diff --git a/demos/shell/App.s b/demos/shell/App.s index 419314c..aba9045 100644 --- a/demos/shell/App.s +++ b/demos/shell/App.s @@ -7,30 +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) -; ALI BANK - SNA Tiles - -; Segment #4 -- Rotation table data - - ASM ..\..\src\RotData.s - DS 0 - KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data) - ALI BANK - SNA RotData - - + SNA TSET \ No newline at end of file diff --git a/demos/shell/gen/App.TileSet.s b/demos/shell/gen/App.TileSet.s index e6c1692..98af67d 100644 --- a/demos/shell/gen/App.TileSet.s +++ b/demos/shell/gen/App.TileSet.s @@ -2,7 +2,7 @@ ; Palette: ; $0000,$0777,$0F31,$0E51,$00A0,$02E3,$0BF1,$0FA4,$0FD7,$0EE6,$0F59,$068F,$01CE,$09B9,$0EDA,$0EEE ; Converting to BG0 format... -tiledata ENT +TSet ENT ; Reserved space (tile 0 is special... ds 128 diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index f327395..9fd95f5 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -464,7 +464,7 @@ GTEStartUp tdc adc #$0100 pha - pea #ENGINE_MODE_DYN_TILES ; Enable Dynamic Tiles + pea #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER ; Enable Dynamic Tiles and Two Layer lda MyUserId ; Pass the userId for memory allocation pha _GTEStartUp From 19f73c22ed404c1a654955c486559d4562df7161 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 27 Jun 2022 11:25:25 -0500 Subject: [PATCH 79/82] Macro changes --- macros/GTE.Macs.s | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s index 6106227..c752869 100644 --- a/macros/GTE.Macs.s +++ b/macros/GTE.Macs.s @@ -96,6 +96,15 @@ _GTERemoveTimer MAC _GTEStartScript MAC UserTool $2100+GTEToolNum <<< +_GTESetOverlay MAC + UserTool $2200+GTEToolNum + <<< +_GTEClearOverlay MAC + UserTool $2300+GTEToolNum + <<< +_GTEGetTileDataAddr MAC + UserTool $2400+GTEToolNum + <<< ; EngineMode definitions ; Script definition From d720f0ccac99267a1f1c1070c98927f95f19dd4f Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 27 Jun 2022 17:15:27 -0500 Subject: [PATCH 80/82] Add two additional functions --- macros/GTE.Macs.s | 6 ++++++ src/Tool.s | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s index c752869..2f64785 100644 --- a/macros/GTE.Macs.s +++ b/macros/GTE.Macs.s @@ -105,6 +105,12 @@ _GTEClearOverlay MAC _GTEGetTileDataAddr MAC UserTool $2400+GTEToolNum <<< +_GTEFillTileStore MAC + UserTool $2500+GTEToolNum + <<< +_GTERefresh MAC + UserTool $2600+GTEToolNum + <<< ; EngineMode definitions ; Script definition diff --git a/src/Tool.s b/src/Tool.s index 9e00188..bd8970c 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -88,6 +88,8 @@ _CallTable adrl _TSClearOverlay-1 adrl _TSGetTileDataAddr-1 + adrl _TSFillTileStore-1 + adrl _TSRefresh-1 _CTEnd _GTEAddSprite MAC UserTool $1000+GTEToolNum @@ -663,6 +665,47 @@ _TSGetTileDataAddr _TSExit #0;#0 +; FillTileStore(tileId) +_TSFillTileStore +:tileId equ FirstParam+0 + + _TSEntry + + stz tmp0 +:oloop + stz tmp1 +:iloop + ldx tmp1 + ldy tmp0 + lda :tileId,s + jsr _SetTile + + lda tmp1 + inc + sta tmp1 + cmp #TILE_STORE_WIDTH + bcc :iloop + + lda tmp0 + inc + sta tmp0 + cmp #TILE_STORE_HEIGHT + bcc :oloop + + _TSExit #0;#2 + +; _TSRefresh() +_TSRefresh + _TSEntry + + ldx #TILE_STORE_SIZE-2 +:loop jsr _PushDirtyTileX + dex + dex + bpl :loop + + _TSExit #0;#0 + ; Insert the GTE code put Math.s From f18dad0692df0f80dc04f40643143cef65ffa04c Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 27 Jun 2022 17:17:48 -0500 Subject: [PATCH 81/82] Fix typo in tool demo --- demos/tool/App.Main.s | 2 -- 1 file changed, 2 deletions(-) diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 9fd95f5..548d009 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -9,8 +9,6 @@ use EDS.GSOS.Macs use GTE.Macs - use ../../src/Defs.s - mx %00 TSZelda EXT ; tileset buffer From 65be8135da44e1397dcdd7a818e8a7fb3a43a4aa Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Mon, 27 Jun 2022 17:22:22 -0500 Subject: [PATCH 82/82] Add test keys for new functions --- demos/tool/App.Main.s | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/demos/tool/App.Main.s b/demos/tool/App.Main.s index 548d009..179db9e 100644 --- a/demos/tool/App.Main.s +++ b/demos/tool/App.Main.s @@ -290,9 +290,10 @@ HERO_SPRITE equ SPRITE_16X16+1 :10 cmp #$0A bne :11 dec ScreenY + brl :next :11 cmp #'y' - bne :next + bne :12 lda DTile inc and #$007F @@ -300,6 +301,17 @@ HERO_SPRITE equ SPRITE_16X16+1 pha pea $0000 _GTECopyTileToDynamic + brl :next + +:12 cmp #'f' + bne :13 + pea $0000 + _GTEFillTileStore + brl :next + +:13 cmp #'m' + bne :next + _GTERefresh :next ; inc ScreenX