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/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) 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/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/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/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/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/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 79398d7..598e30d 100644 Binary files a/demos/shell/assets/fatdog-1.png and b/demos/shell/assets/fatdog-1.png differ diff --git a/demos/shell/assets/fatdog-2.png b/demos/shell/assets/fatdog-2.png index 59ffaeb..6025a66 100644 Binary files a/demos/shell/assets/fatdog-2.png and b/demos/shell/assets/fatdog-2.png differ 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/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/App.Main.s b/demos/sprites/App.Main.s index e92881d..f5d042f 100644 --- a/demos/sprites/App.Main.s +++ b/demos/sprites/App.Main.s @@ -1,293 +1,353 @@ ; 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 + tdc + sta MyDirectPage ; Keep a copy for the overlay callback - ldx #5 ; Mode 0 is full-screen, mode 5 is 256x160 - ldx #320 - ldy #200 - jsl SetScreenMode + _MTStartUp ; GTE requires the miscellaneous toolset to be running + + 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 +MUSHROOM_SLOT equ 0 - 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 -; Draw the initial screen + pea MUSHROOM_ID ; Put the mushroom in Slot 0 + pea #80 ; at x=80, y=152 + pea #152 + pea MUSHROOM_SLOT + _GTEAddSprite - lda #DIRTY_BIT_BG0_REFRESH ; Redraw all of the tiles on the next Render - tsb DirtyBits - jsl Render + pea MUSHROOM_SLOT + pea $0000 ; with these flags (h/v flip) + pea MUSHROOM_VBUFF ; and use this stamp + _GTEUpdateSprite + + 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 @@ -298,19 +358,29 @@ EvtLoop ; Exit code Exit - jsl EngineShutDown - + _GTEShutDown _QuitGS qtRec 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 @@ -331,6 +401,8 @@ 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 @@ -339,22 +411,31 @@ 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 +454,44 @@ 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 sta StartX 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 sta StartY + pei StartX + pei StartY + _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 +499,11 @@ UpdatePlayerLocal lda PlayerGlobalX sec sbc StartX -; jsr AdjustLocalX sta PlayerX lda PlayerGlobalY sec sbc StartY -; jsr AdjustLocalY sta PlayerY rts @@ -431,13 +516,16 @@ UpdatePlayerPos ; Check if the player is standing on the ground at their current local position - ldx PlayerX + pha ; space for result + 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 +589,7 @@ UpdatePlayerPos txa ora LastHFlip - ora #SPRITE_ID + ora #HERO_ID sta SpriteFrame lda SpriteCount @@ -514,7 +602,7 @@ UpdatePlayerPos lda PlayerXVel beq :frame - jsl GetVBLTicks + jsr _GetVBLTicks and #$0003 inc and #$0003 @@ -527,65 +615,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 +701,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 3d84606..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/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/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/gen/App.TileSetAnim.s b/demos/sprites/gen/App.TileSetAnim.s index 1f4d5be..142fb2d 100644 --- a/demos/sprites/gen/App.TileSetAnim.s +++ b/demos/sprites/gen/App.TileSetAnim.s @@ -1,52 +1,40 @@ -TileAnimInit ENT +TileAnimInit + pea #137 + pea #0 + _GTECopyTileToDynamic + pea #138 + pea #1 + _GTECopyTileToDynamic + pea #169 + pea #2 + _GTECopyTileToDynamic + pea #170 + pea #3 + _GTECopyTileToDynamic - 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 + pea #15 + pea #^TileAnim + pea #TileAnim + _GTEStartScript 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 +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/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", 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 new file mode 100644 index 0000000..179db9e --- /dev/null +++ b/demos/tool/App.Main.s @@ -0,0 +1,654 @@ + REL + DSK MAINSEG + + use Locator.Macs + use Load.Macs + use Mem.Macs + use Misc.Macs + use Util.Macs + use EDS.GSOS.Macs + use GTE.Macs + + mx %00 + +TSZelda EXT ; tileset buffer + +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 +DTile equ 14 +Tmp2 equ 16 + +; Typical init + phk + plb + + 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 + +; Initialize the graphics screen to a 256x160 playfield + + pea #320 + pea #200 + _GTESetScreenMode + +; Load a tileset + + pea #^TSZelda + 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 equ SPRITE_16X16+1 + + pea HERO_SPRITE ; sprint id + pea VBUFF_SPRITE_START ; vbuff address + _GTECreateSpriteStamp + +; Create sprites + stz Tmp0 + stz Tmp1 + + ldx Tmp0 +:sloop + pea HERO_SPRITE ; sprite id + lda PlayerX,x + pha + lda PlayerY,x + pha + pei Tmp1 + _GTEAddSprite + + 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 + +; 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 + jsr _drawTree + + ldx #3 + ldy #0 + jsr _drawTreeH + + ldx #0 + ldy #3 + jsr _drawTreeV + + ldx #3 + ldy #3 + jsr _drawTreeHV + + ldx #9 + ldy #0 + jsr _drawTree + + 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 +:no_trees +; 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 + +; Set the screen coordinates + + lda #128 + sta ScreenX + lda #128 + sta ScreenY + + stz Selected + stz Flips + +; Very simple actions +:evt_loop + pha ; space for result, with pattern + _GTEReadControl + pla + and #$00FF + cmp #'q' + 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 + ldx Selected + inc PlayerY,x + brl :next +:4 + cmp #'w' + bne :5 + ldx Selected + dec PlayerY,x + brl :next +:5 + cmp #'d' + bne :6 + ldx Selected + inc PlayerX,x + brl :next +:6 + cmp #'a' + bne :7 + ldx Selected + dec PlayerX,x + brl :next +:7 + cmp #$15 ; left = $08, right = $15, up = $0B, down = $0A + bne :8 + inc ScreenX + bra :next + +:8 cmp #$08 + bne :9 + dec ScreenX + brl :next + +:9 cmp #$0B + bne :10 + inc ScreenY + brl :next + +:10 cmp #$0A + bne :11 + dec ScreenY + brl :next + +:11 cmp #'y' + bne :12 + lda DTile + inc + and #$007F + sta DTile + 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 + + pei ScreenX + pei ScreenY + _GTESetBG0Origin + +; brl no_animate + + stz Tmp0 + stz Tmp1 + + ldx Tmp0 +loopX + lda PlayerX,x + clc + adc PlayerU,x + sta PlayerX,x + bpl is_posx + cmp #-15 + bcs do_y + 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 + cmp #-15 + bcs do_z + 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 + 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 + lda PlayerY,x + pha + _GTEMoveSprite + + inc Tmp1 + ldx Tmp0 + inx + inx + stx Tmp0 + cpx #MAX_SPRITES*2 + bcc loopY + + _GTERender + inc FrameCount + +; Debug stuff + pha + _GTEGetSeconds + pla + cmp LastSecond + beq :no_fps + sta LastSecond + + lda FrameCount + ldx #0 + ldy #$FFFF + jsr DrawWord + + stz FrameCount +:no_fps + +; tdc +; ldx #160*32 +; jsr DrawWord + + brl :evt_loop + +; Shut down everything +:exit + _GTEShutDown + _QuitGS qtRec +qtRec adrl $0000 + da $00 + +; Array of sprite positions and velocities +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 + +; 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 + +_fillTileStore + sta Tmp2 + stz Tmp0 +:oloop + stz Tmp1 +:iloop + pei Tmp1 + pei Tmp0 + pei Tmp2 + _GTESetTile + + lda Tmp2 + eor #TILE_PRIORITY_BIT + sta Tmp2 + + 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 +LastSecond dw 0 + + 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/App.s b/demos/tool/App.s new file mode 100644 index 0000000..097e4d1 --- /dev/null +++ b/demos/tool/App.s @@ -0,0 +1,15 @@ +; IIgs Sprite Testbed + + TYP $B3 ; S16 file + DSK GTEToolDemo + XPL + +; Segment #1 -- Main execution block + + ASM App.Main.s + SNA Main + +; Segment #2 -- Tileset + + ASM Zelda.TileSet.s + SNA TSET diff --git a/demos/tool/Zelda.TileSet.s b/demos/tool/Zelda.TileSet.s new file mode 100644 index 0000000..8743dee --- /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 00000000 + hex 00006567 + hex 00656666 + hex 00667666 + hex 06666666 + hex 06666676 + hex 66666666 + hex 66766666 + + hex ffffffff + hex ffff0000 + hex ff000000 + hex f0000000 + hex f0000000 + hex 00000000 + hex 00000000 + hex 00000000 + + hex 00000000 + hex 76560000 + hex 66665600 + hex 66676600 + hex 66666660 + hex 67666660 + hex 66666666 + hex 66666766 + + hex ffffffff + hex 0000ffff + hex 000000ff + hex 000000ff + hex 0000000f + hex 0000000f + hex 00000000 + hex 00000000 + +; Tile ID 66 +; From image coordinates 8, 16 + hex 00000000 + hex 77000000 + hex 67777000 + hex 66776700 + hex 66677700 + hex 66767770 + hex 66777700 + hex 66677770 + + hex ffffffff + hex 00ffffff + hex 00000fff + hex 000000ff + hex 000000ff + hex 0000000f + hex 000000ff + hex 0000000f + + hex 00000000 + hex 00000077 + hex 00077776 + hex 00767766 + hex 00777666 + hex 07776766 + hex 00777766 + hex 07777666 + + hex ffffffff + hex ffffff00 + hex fff00000 + hex ff000000 + hex ff000000 + hex f0000000 + hex ff000000 + hex f0000000 + +; 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 06666666 + hex 66667666 + hex 66666666 + hex 66766666 + hex 06666667 + hex 00066a6a + hex 00007aaa + hex 00077777 + + hex f0000000 + hex 00000000 + hex 00000000 + hex 00000000 + hex f0000000 + hex fff00000 + hex ffff0000 + hex fff00000 + + hex 66666660 + hex 66676666 + hex 66666666 + hex 66666766 + hex 76666660 + hex a6a66000 + hex aaa70000 + hex 77777000 + + hex 0000000f + hex 00000000 + hex 00000000 + hex 00000000 + hex 0000000f + hex 00000fff + hex 0000ffff + hex 00000fff + +; Tile ID 98 +; From image coordinates 8, 24 + hex 76677670 + hex 66677770 + hex 67777700 + hex 66767700 + hex 77777000 + hex 77600000 + hex aa777777 + hex 77777700 + + hex 0000000f + hex 0000000f + hex 000000ff + hex 000000ff + hex 00000fff + hex 000fffff + hex 00000000 + hex 000000ff + + hex 07677667 + hex 07777666 + hex 00777776 + hex 00776766 + hex 00077777 + hex 00000677 + hex 777777aa + hex 00777777 + + hex f0000000 + hex f0000000 + hex ff000000 + hex ff000000 + hex fff00000 + hex fffff000 + hex 00000000 + hex ff000000 + +; 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/tool/_FileInformation.txt b/demos/tool/_FileInformation.txt new file mode 100644 index 0000000..f79c6dd --- /dev/null +++ b/demos/tool/_FileInformation.txt @@ -0,0 +1 @@ +GTEToolDemo=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) diff --git a/demos/tool/build-image.bat b/demos/tool/build-image.bat new file mode 100644 index 0000000..462afd6 --- /dev/null +++ b/demos/tool/build-image.bat @@ -0,0 +1,19 @@ +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/Tool" + +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% .\GTEToolDemo +%CADIUS% ADDFILE %IMAGE% %FOLDER% ..\..\src\Tool160 + +REM Copy in the image assets 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/tool/package.json b/demos/tool/package.json new file mode 100644 index 0000000..70528d0 --- /dev/null +++ b/demos/tool/package.json @@ -0,0 +1,32 @@ +{ + "name": "gte-toolbox-demo", + "version": "1.0.0", + "description": "A testbed for developing a toolbox wrapper around the GTE library", + "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": "../../macros", + "crossrunner": "C:\\Programs\\Crossrunner\\Crossrunner.exe" + }, + "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 -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" + }, + "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/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/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": { 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/macros/GTE.Macs.s b/macros/GTE.Macs.s new file mode 100644 index 0000000..2f64785 --- /dev/null +++ b/macros/GTE.Macs.s @@ -0,0 +1,160 @@ +* Generic Tile Engine Macros +* by Lucas Scharenbroich + +GTEToolNum equ $A0 + +_GTEBootInit MAC + UserTool $0100+GTEToolNum + <<< +_GTEStartUp MAC + UserTool $0200+GTEToolNum + <<< +_GTEShutDown MAC + UserTool $0300+GTEToolNum + <<< +_GTEVersion MAC + UserTool $0400+GTEToolNum + <<< +_GTEReset MAC + UserTool $0500+GTEToolNum + <<< +_GTEStatus MAC + UserTool $0600+GTEToolNum + <<< +_GTEReadControl MAC + UserTool $0900+GTEToolNum + <<< +_GTESetScreenMode MAC + UserTool $0A00+GTEToolNum + <<< +_GTESetTile MAC + UserTool $0B00+GTEToolNum + <<< +_GTESetBG0Origin MAC + UserTool $0C00+GTEToolNum + <<< +_GTERender MAC + UserTool $0D00+GTEToolNum + <<< +_GTELoadTileSet MAC + 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 + <<< +_GTEGetSeconds MAC + UserTool $1400+GTEToolNum + <<< +_GTECopyTileToDynamic MAC + UserTool $1500+GTEToolNum + <<< +_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 + <<< +_GTEAddTimer MAC + UserTool $1F00+GTEToolNum + <<< +_GTERemoveTimer MAC + UserTool $2000+GTEToolNum + <<< +_GTEStartScript MAC + UserTool $2100+GTEToolNum + <<< +_GTESetOverlay MAC + UserTool $2200+GTEToolNum + <<< +_GTEClearOverlay MAC + UserTool $2300+GTEToolNum + <<< +_GTEGetTileDataAddr MAC + UserTool $2400+GTEToolNum + <<< +_GTEFillTileStore MAC + UserTool $2500+GTEToolNum + <<< +_GTERefresh MAC + UserTool $2600+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/package.json b/package.json new file mode 100644 index 0000000..711ed14 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "generic-tile-engine", + "version": "1.0.0", + "description": "A tile-base game engine for the Apple IIgs", + "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": "./macros", + "crossrunner": "C:\\Programs\\Crossrunner\\Crossrunner.exe" + }, + "scripts": { + "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", + "debug": "%npm_package_config_crossrunner% ./src/Debug160 -Source ./src/Debug160_S02__Output.txt -Debug -CompatibilityLayer" + }, + "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/src/Core.s b/src/Core.s index 3b2a66d..f102699 100644 --- a/src/Core.s +++ b/src/Core.s @@ -8,94 +8,6 @@ use .\Defs.s -; 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 exteral 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 - -; High-Level StartUp and ShutDown functions EngineStartUp ENT phb phk @@ -103,41 +15,23 @@ EngineStartUp ENT jsr ToolStartUp ; Start up the toolbox tools we rely on jsr _CoreStartUp + jsr SoundStartUp ; Start up any sound/music tools plb rtl -_CoreStartUp - jsr SoundStartUp ; Start up any sound/music tools - 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 - nop - jsr InitSprites ; Initialize the sprite subsystem - jsr InitTiles ; Initialize the tile subsystem - - jsr InitTimers ; Initialize the timer subsystem - rts - EngineShutDown ENT phb phk plb + jsr SoundShutDown jsr _CoreShutDown jsr ToolShutDown plb rtl -_CoreShutDown - jsr IntShutDown - jsr SoundShutDown - rts - ToolStartUp _TLStartUp ; normal tool initialization pha @@ -152,7 +46,6 @@ ToolStartUp rts MasterId ds 2 -UserId ds 2 ; Fatal error handler invoked by the _Err macro PgmDeath tax @@ -168,9 +61,6 @@ PgmDeath0 pha ContDeath ldx #$1503 jsl $E10000 -ToolShutDown - rts - ; Use Tool222 (NinjaTrackerPlus) for music playback SoundStartUp lda #NO_MUSIC @@ -194,299 +84,10 @@ SoundShutDown :no_music 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 - -; 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 - - -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..fc1dd8c --- /dev/null +++ b/src/CoreImpl.s @@ -0,0 +1,353 @@ +; Feature flags +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 +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 + +; 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 + +; Assumes the direct page is set and EngineMode and UserId has been initialized +_CoreStartUp + 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 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 + rts + +_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 + + jsr _SetDataBank + + rep #$20 + inc OneSecondCounter + sep #$20 + + ldal $E0C032 + and #%10111111 ;clear IRQ source + stal $E0C032 + + pla + plb + clc + rtl + mx %00 + +; This is OK, it's referenced by a long address +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 +_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 + + cmp LastKey + beq :KbdDown + sta LastKey + + lda #PAD_KEY_DOWN ; set the keydown flag + ora 2,s + sta 2,s + bra :KbdDown + +:KbdNotDwn + stz 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 568d504..0ef0580 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 @@ -80,18 +82,25 @@ 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 BankLoad equ 104 TileStoreBankAndBank01 equ 106 TileStoreBankAndTileDataBank equ 108 -Next equ 110 +TileStoreBankDoubled equ 110 +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 +SpriteRemovedFlag equ 122 ; Indicate if any sprites were removed this frame + + 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 +121,50 @@ 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 +_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 +_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 +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 +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 + +; 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 @@ -135,14 +188,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_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 +; TILE_PROC_MASK equ $F800 ; Select tile proc for rendering ; Sprite constants SPRITE_HIDE equ $2000 @@ -153,17 +208,53 @@ 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_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 -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_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 +; This is 13 blocks wide +SPRITE_PLANE_SPAN equ VBUFF_STRIDE_BYTES + +; 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 +_SpriteBits EXT +_SpriteBitsNot EXT +VBuffArray EXT +_stamp_step EXT +VBuffVertTableSelect EXT +VBuffHorzTableSelect EXT +Overlays EXT + +; Tool error codes +NO_TIMERS_AVAILABLE equ 10 diff --git a/src/GTE.s b/src/GTE.s index 803c929..9af47d7 100644 --- a/src/GTE.s +++ b/src/GTE.s @@ -20,12 +20,10 @@ 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). -SetSCBArray EXT +; SetSCBArray EXT BltSCB EXT ; Rotation @@ -73,21 +71,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 5fe5f94..c4d7d3d 100644 --- a/src/Graphics.s +++ b/src/Graphics.s @@ -11,18 +11,81 @@ 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 - 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 +; +; 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) +_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 @@ -33,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) @@ -55,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 @@ -148,9 +196,139 @@ _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 edge of the physical screen + clc + adc ScreenX1 + dec + pha ; Save for second loop + + ldx #0 + ldy ScreenHeight +:loop clc + sta RTable,x + adc #160 + inx + inx + 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 + 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 + sta 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/Master.s b/src/Master.s new file mode 100644 index 0000000..9c9a012 --- /dev/null +++ b/src/Master.s @@ -0,0 +1,38 @@ +; IIgs Generic Tile Engine User Toolset + + TYP $BA ; Tool set file + DSK Tool160 + 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) + 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 + + 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/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/Memory.s b/src/Memory.s index 59b75bc..c4c449d 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 @@ -24,9 +28,10 @@ InitMemory PushLong #0 ; space for result _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 PushLong #$008000 ; size (32k) @@ -36,12 +41,20 @@ InitMemory PushLong #0 ; space for result _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 - 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 @@ -80,6 +97,7 @@ InitMemory PushLong #0 ; space for result 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 @@ -93,10 +111,12 @@ InitMemory PushLong #0 ; space for result --^ 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 @@ -104,6 +124,7 @@ InitMemory PushLong #0 ; space for result brl :bloop :exit1 + ldx #0 ldy #0 :bloop2 @@ -135,9 +156,6 @@ InitMemory PushLong #0 ; space for result :exit rts -Buff00 ds 4 -Buff01 ds 4 - ; Bank allocator (for one full, fixed bank of memory. Can be immediately deferenced) AllocOneBank PushLong #0 @@ -149,19 +167,11 @@ 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 ; 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 @@ -172,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 b010f38..2a322f0 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 @@ -28,6 +20,10 @@ Render ENT ; 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 jsr _ApplyBG1YPos @@ -36,55 +32,63 @@ _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 +; 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 _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 -; 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 -; jsr _BltSCB + ldx #0 + 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 @@ -100,6 +104,83 @@ _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 + +_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 + + tdc + clc + adc #$100 ; move to the next page + tcd + + stx DP2_DIRTY_TILE_COUNT ; Cache the dirty tile count + jsr _PopDirtyTilesFast + + 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 +; 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 @@ -108,58 +189,18 @@ _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 +; jsr _ClearSpritesFromCodeField ; Restore the tiles to their non-sprite versions :norecalc - lda #3 ; purple - jsr _SetBorderColor - jsr _RenderSprites - lda #4 ; dk. green - jsr _SetBorderColor - jsr _ApplyDirtyTiles +; jsr _RenderSprites +; jsr _ApplyDirtyTiles + lda #1 sta LastRender rts @@ -186,34 +227,25 @@ _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 + lda TileStore+TS_SPRITE_FLAG,y + beq NoSpritesDirty ; This is faster if there are no sprites + +; TODO: handle sprite drawing ; The rest of this function handles that non-sprite blit, which is super fast since it blits directly from the ; tile data store to the graphics screen with no masking. The only extra work is selecting a blit function ; based on the tile flip flags. - - 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 - 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 - - 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 +; lda TileStore+TS_DIRTY_TILE_DISP,y +; stal :nsd+1 + ldx TileStore+TS_SCREEN_ADDR,y ; Get the on-screen address of this tile + lda TileStore+TS_TILE_ADDR,y ; load the address of this tile's data (pre-calculated) + plb ; set the code field bank +:nsd jmp $0000 ; Use some temporary space for the spriteIdx array (maximum of 4 entries) stkSave equ tmp9 @@ -221,781 +253,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 - -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 - -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 ; stack value. -BlitFourSprites -BlitThreeSprites -BlitTwoSprites - plb - tsc - sta stkSave ; Save the stack on the direct page +FourSpritesDirty +ThreeSpritesDirty +TwoSpritesDirty + + sta tileAddr + stx 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/Script.s b/src/Script.s index bbeb01d..87028a7 100644 --- a/src/Script.s +++ b/src/Script.s @@ -33,18 +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 - phk - plb - - 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,15 +47,12 @@ StartScript ENT sta Timers+8,x 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. @@ -69,14 +61,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 @@ -99,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 @@ -116,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 @@ -175,14 +162,14 @@ _SetDTile ldx: ARG1,y lda: ARG2,y tay - jsl CopyTileToDyn + jsr CopyTileToDyn brl _dss_cmd_rtn _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 b50cd47..6632a3b 100644 --- a/src/Sprite.s +++ b/src/Sprite.s @@ -19,37 +19,321 @@ InitSprites cpx #$FFFE bne :loop2 -; Clear values in the sprite array +; 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 - 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 -; 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 +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 - lda #VBUFF_SPRITE_START - clc -:loop4 sta _Sprites+VBUFF_ADDR,x - adc #VBUFF_SPRITE_STEP - inx - inx - cpx #MAX_SPRITES*2 - bcc :loop4 + 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 + +; 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 + jsr _CacheSpriteBanks + rts +; 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 + + 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 +; 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 +;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: +; 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 +_DoPhase1 + lda _Sprites+SPRITE_STATUS,y + ora ForceSpriteFlag + +; 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 + 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 + +; Need to calculate new VBUFF information. The could be required for UPDATED, ADDED or MOVED +; sprites, so we do it unconditionally, but we do need to mark the current sprite for erasure if +; needed +:no_clear + +; If the sprite is marked as ADDED, then it does not need to have its old tile locations cleared + + bit #SPRITE_STATUS_ADDED + bne :no_move + +; 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 + + phy + jsr _ClearSpriteFromTileStore + ply + +; Anything else (MOVED, UPDATED, ADDED) will need to have the VBUFF information updated and the +; current tiles marked for update +:no_move + jsr _CalcDirtySprite ; This function preserves Y + + 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 +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 + 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 +; 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 exist independent of the sprites +; and sprite reference a specific stamp. This is necessary because it's common for a +; 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. +; +; 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 +; 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 + + tax ; Tile data address + pla ; Pop the sprite ID + jmp _DrawSpriteStamp ; Render the sprite data and create a stamp ; Add a new sprite to the rendering pipeline ; @@ -70,17 +354,9 @@ VBUFF_SPRITE_START equ {8*VBUFF_TILE_ROW_BYTES}+4 ; 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 ENT - phb - phk - plb - jsr _AddSprite - plb - rtl - _AddSprite pha txa @@ -90,12 +366,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 +382,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 @@ -117,265 +392,112 @@ _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 -; 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. +; Macro to make the unrolled loop more concise ; -; Y register = sprite record index -_CSFTS_Out rts +; 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 + + tya + ldy DirtyTileCount + sta DirtyTiles,y + iny + iny + sty DirtyTileCount +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 _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 + 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 + 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 - 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 +csfts_out rts - 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. -; -; 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 +csfts_3x3 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 2 + TSClearSprite 4 + 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}+0 + TSClearSprite 2*{TS_LOOKUP_SPAN*2}+2 + TSClearSprite 2*{TS_LOOKUP_SPAN*2}+4 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 +csfts_3x2 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 2 + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 + TSClearSprite 2*{TS_LOOKUP_SPAN*2}+0 + TSClearSprite 2*{TS_LOOKUP_SPAN*2}+2 + rts -: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 +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 -_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 +csfts_2x3 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 2 + TSClearSprite 4 + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2 + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+4 + rts -; Last thing to do, so go ahead and clear the flags +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 - lda #SPRITE_STATUS_OCCUPIED - sta _Sprites+SPRITE_STATUS,y +csfts_2x1 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 1*{TS_LOOKUP_SPAN*2}+0 + rts -; 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 +csfts_1x3 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 2 + TSClearSprite 4 + rts - jmp _MarkDirtySprite -:out +csfts_1x2 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 + TSClearSprite 2 + rts + +csfts_1x1 ldx _Sprites+TS_LOOKUP_INDEX,y + TSClearSprite 0 rts ; Use the blttmp space to build the active sprite list. Since the sprite tiles are not drawn until later, @@ -383,7 +505,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 @@ -420,58 +542,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 -; 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 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 @@ -531,42 +601,29 @@ _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 #>spritedata + and #$FF00 + ora #^tiledata + sta DP2_TILEDATA_AND_SPRITEDATA_BANKS,x + + 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 + sta TileStoreBankDoubled + rts -; This is 13 blocks wide -SPRITE_PLANE_SPAN equ VBUFF_STRIDE_BYTES ; 52 - -; 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. ; @@ -576,9 +633,15 @@ SPRITE_PLANE_SPAN equ VBUFF_STRIDE_BYTES ; 52 ; X = sprite index _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 sprite's width and height lda #4 @@ -594,7 +657,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 @@ -653,6 +716,7 @@ _PrecalcAllSpriteInfo sbc _Sprites+SPRITE_CLIP_TOP,x inc sta _Sprites+SPRITE_CLIP_HEIGHT,x + rts :offscreen @@ -664,97 +728,81 @@ _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 + 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 -UpdateSprite ENT - phb - phk - plb - jsr _UpdateSprite - plb - rtl - +; A = Sprite slot +; X = New Sprite Flags +; Y = New Sprite Stamp Address _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 +; 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 - 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 ; -; A = sprite ID +; A = sprite slot ; X = x position ; Y = y position -MoveSprite ENT - phb - phk - plb - jsr _MoveSprite - plb - 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 tya cmp _Sprites+SPRITE_Y,x bne :changed2 @@ -766,61 +814,8 @@ _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 - -; 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} ; Fixed address in sprite/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 -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 + jmp _PrecalcAllSpriteInfo ; Can be specialized to only update (x,y) values diff --git a/src/Sprite2.s b/src/Sprite2.s index 82a6dd6..4d131e0 100644 --- a/src/Sprite2.s +++ b/src/Sprite2.s @@ -1,63 +1,20 @@ ; 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 +SpriteBit equ tmp8 ; set the bit of the value that if the current sprite index -TileLeft equ tmp8 -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 +; 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 @@ -80,454 +37,307 @@ _LocalToTileStore ; 8 10 2 8 ; ... ; -; For the Y-coordinate, we just use "mod 8" instead of "mod 4" -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 +; For the Y-coordinate, we 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 +; +; 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 + rts +_CalcDirtySprite 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 +; 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 +; 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 - 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 - sta RowTop + lsr + tax + lda TileStoreLookupYTable,x + sta RowTop ; Even numbers from [0, 100] (51 elements) -; 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 + pla and #$0007 - sta tmp0 ; save to adjust sprite origin - - 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 +; 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 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 + pha + and #$FFFC + lsr ; Even numbers from [0, 160] (81 elements) + adc RowTop + sta _Sprites+TS_LOOKUP_INDEX,y ; This is the index into the TileStoreLookup table - txa + +; Calculate the final amount of visible tiles that need to be refreshed and use that to +; set the coverage size index. + + pla and #$0003 - sta tmp1 ; save to adjust sprite origin - - lda _Sprites+SPRITE_CLIP_WIDTH,y ; max width = 8 = 0x08 + 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 + sta _Sprites+TS_COVERAGE_SIZE,y ; Save this value as a key to the coverage size of the sprite -; 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 +; Part 2: Redo some calculation with the actual (signed) sprite positions that take into +; account negative coordinates to set the VBuff offset values. + 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_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 -; Dispatch to cover the tiles + 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 - ldx AreaIndex - jmp (:mark,x) -:mark dw :mark1x1,:mark1x2,:mark1x3,mdsOut + clc ; Carry should still be clear here.... + lda StartYMod208 + adc _Sprites+SPRITE_Y,y + bpl :pos_y + clc + adc #208 +:pos_y + and #$0007 + 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 + adc _Sprites+SPRITE_X,y + bpl :pos_x + clc + adc #164 +:pos_x + and #$0003 + clc + 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 _Sprites+TS_VBUFF_BASE,y + +; 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 0, 12 or 24 values + clc + ldx tmp3 + adc VBuffHorzTableSelect,x ; A bunch of 0, 4 or 8 values + clc + adc #VBuffArray + plx +; ldx _Sprites+TS_LOOKUP_INDEX,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 + +tmp_out + tya + ora #$100 + tax + lda tmp1 + sta SPRITE_VBUFF_PTR,x + +mdsOut rts + + +; NOTE: The VBuffArray table is set up so that each sprite's vbuff address is stored in a +; parallel structure to the Tile Store. This allows up to use the same TileStoreLookup +; offset to index into the array of 16 sprite VBUFF addresses that are bound to a given tile +_MarkDirtySpriteTiles + lda _SpriteBits,y + sta SpriteBit + + clc + ldx _Sprites+TS_COVERAGE_SIZE,y + jmp (mdsmark,x) + +mdsmark 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 +; Pair of macros to make the unrolled loop more concise +; +; 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 +; +; 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 + + lda SpriteBit + ora TileStore+TS_SPRITE_FLAG,y + sta TileStore+TS_SPRITE_FLAG,y + + lda TileStore+TS_DIRTY,y + bne next + + inc + sta TileStore+TS_DIRTY,y + + tya + ldy DirtyTileCount + sta DirtyTiles,y + iny + iny + sty DirtyTileCount +next + <<< + +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 -; 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. -; -; 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. -; -; 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 - 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} 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 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 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 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+4 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 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0 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 + TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+2 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 + 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 - 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 + TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0 + TSSetSprite 2*{TS_LOOKUP_SPAN*2}+0 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 + 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 - 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 + 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 - -; 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 - -; 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/SpriteRender.s b/src/SpriteRender.s index 39041fd..71d5ced 100644 --- a/src/SpriteRender.s +++ b/src/SpriteRender.s @@ -1,28 +1,27 @@ -; Function to render a sprite from a sprite definition into the internal data buffers +; Alternate entry point that takes arguments in registers instead of using a _Sprite +; record ; -; X = sprite index -_DrawSpriteSheet +; 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 ; Isolate the size bits +DISP_MASK equ $0018 ; Preserve the size bits - phx - - lda _Sprites+VBUFF_ADDR,x - sta tmp1 - - lda _Sprites+TILE_DATA_OFFSET,x - sta tmp2 - - lda _Sprites+SPRITE_DISP,x +_DrawSpriteStamp + sty tmp1 + stx tmp2 + xba and #DISP_MASK ; dispatch to all of the different orientations sta tmp3 -; 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 @@ -34,7 +33,7 @@ DISP_MASK equ $0018 ; Isolate the size bits ldy tmp2 lda tmp1 clc - adc #4*3 + adc #3*4 jsr _DrawSprite lda tmp3 @@ -43,7 +42,7 @@ DISP_MASK equ $0018 ; Isolate the size bits ldy tmp2 lda tmp1 clc - adc #4*6 + adc #6*4 jsr _DrawSprite lda tmp3 @@ -52,19 +51,16 @@ DISP_MASK equ $0018 ; Isolate the size bits ldy tmp2 lda tmp1 clc - adc #4*9 + adc #9*4 jsr _DrawSprite ; Restore bank plb ; pop extra byte plb - - plx rts ; ; 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 @@ -166,6 +162,9 @@ draw_16x8hv ply jmp _DrawTile8x8V +; X = sprite ID +; Y = Tile Data +; A = VBUFF address draw_16x16 clc tax @@ -262,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 @@ -270,7 +269,7 @@ draw_16x16hv adc #4 tax lda 1,s - adc #{128*32} + adc #{128*32}+64 tay jsr _DrawTile8x8V @@ -278,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 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/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/Tiles.s b/src/Tiles.s new file mode 100644 index 0000000..ad55581 --- /dev/null +++ b/src/Tiles.s @@ -0,0 +1,628 @@ +; 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 tmp1 + 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 +; incremental calculations, but it is handy for other utility functions +; +; A = tile descriptor +; +; 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 + 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 + + +; 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 +:col equ tmp0 +:row equ tmp1 +:vbuff equ tmp2 +:base equ tmp3 + +; Initialize the Tile Store + + 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 that are changed during each frame based on the actions +; that are happening + + lda #0 + 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 + +; Set the default tile rendering functions + + 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 #FastProcs + jsr _SetTileProcs + bra :out + +:dyn lda #0 ; Initialize with Tile 0 + ldy #FastProcs + jsr _SetTileProcs + +: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 + + lda :row ; Set the long address of where this tile + asl ; exists in the code fields + tay + lda #>TileStore ; get middle 16 bits: "00 -->BBHH<-- LL" + and #$FF00 ; merge with code field bank + ora BRowTableHigh,y + sta TileStore+TS_CODE_ADDR_HIGH,x ; High word of the tile address (just the bank) + + lda BRowTableLow,y + sta :base +; sta TileStore+TS_BASE_ADDR,x ; May not be needed later if we can figure out the right constant... + + lda :col ; Set the offset values based on the column + asl ; of this tile + 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 + adc :base +; adc TileStore+TS_BASE_ADDR,x + sta TileStore+TS_CODE_ADDR_LOW,x ; Low word of the tile address in the code field + + lda JTableOffset,y + clc + adc :base + sta TileStore+TS_JMP_ADDR,x ; Address of the snippet handler for this tile + + dec :col + bpl :hop + dec :row + lda #40 + sta :col +:hop + + dex + 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 +oldTileId equ blttmp ; This location is used in _SetTileProcs, too +newTileId equ blttmp+2 +procIdx equ blttmp+4 + +_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,x ; Committed to drawing this tile, so get the address of the tile in the tiledata bank for later + +; 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 +; 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. + +; Calculate the base tile proc selector from the tile Id + stz procIdx + + lda #TILE_PRIORITY_BIT + bit newTileId + beq :low_priority + lda #4 + sta procIdx +:low_priority + lda #TILE_ID_MASK + bit newTileId + beq :is_zero + lda #2 + tsb procIdx +:is_zero + + lda #TILE_VFLIP_BIT + bit newTileId + beq :no_vflip + lda #1 + tsb procIdx +:no_vflip + +; Now integrate with the engine mode indicator + + lda EngineMode + bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER + beq :setTileFast + + bit #ENGINE_MODE_TWO_LAYER + bne :not_dyn + brl :setTileDyn + +:not_dyn + lda #TILE_DYN_BIT + bit newTileId + beq :pickTwoLyrProc + + ldy #TwoLyrDynProcs + brl :pickDynProc + +:pickTwoLyrProc ldy #TwoLyrProcs + lda procIdx + jsr _SetTileProcs + jmp _PushDirtyTileX + +; Specialized check for when the engine is in "Fast" mode. If is a simple decision tree based on whether +; the tile priority bit is set, and whether this is the special tile 0 or not. +:setTileFast + ldy #FastProcs + lda procIdx + jsr _SetTileProcs + jmp _PushDirtyTileX + +; Specialized check for when the engine has enabled dynamic tiles. In this case we are no longer +; guaranteed that the opcodes in a tile are PEA instructions. +:setTileDyn + lda #TILE_DYN_BIT + bit newTileId + beq :pickSlowProc ; If the Dynamic bit is not set, select a tile proc that sets opcodes + + ldy #DynProcs ; use this table +:pickDynProc + lda newTileId ; Otherwise chose one of the two dynamic tuples + and #TILE_PRIORITY_BIT + beq *+5 ; If the Priority bit is not set, pick the first entry + lda #1 ; If the Priority bit is set, pick the other one + jsr _SetTileProcs + jmp _PushDirtyTileX + +:pickSlowProc ldy #SlowProcs + lda procIdx + jsr _SetTileProcs + jmp _PushDirtyTileX + +; X = Tile Store offset +; Y = Engine Mode Base Table address +; A = Table proc index +; +; see TileProcTables in static/TileStore.s +tblPtr equ blttmp +_SetTileProcs + +; 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 + and #$00FF + sta tblPtr+2 + +; Lookup the tile procedures + + ldy #0 + lda [tblPtr],y + stal K_TS_BASE_TILE_DISP,x + + ldy #2 + lda [tblPtr],y + stal K_TS_SPRITE_TILE_DISP,x + + 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. This is +; easier to maintain than a bunch of conditional code. Each etry hold three addresses. +; +; 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 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. +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 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 + +; "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 +; 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 + 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 + 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 + +; 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+{]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 + <<< + +; Specialization for the first sprite which can optimize its dispatch if its the only one +; dobit bit_position,dest;next;exit +dobit1 mac + lsr + bcc next_bit + 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} + tyx + jmp (K_TS_ONE_SPRITE,x) +next_bit + <<< + +; 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 + <<< + +; 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 + <<< + +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 ; 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 + 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 +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 + <<< + +; 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/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 +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/Timer.s b/src/Timer.s index fbb2b53..61f3332 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 @@ -63,17 +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 - phk - plb - ldx #0 :loop lda Timers,x ; If the counter is zero, timer is free beq :freeslot @@ -105,28 +68,23 @@ 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 + rts :notimers ply 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 - phk - plb +_RemoveTimer cmp #{TIMER_REC_SIZE*{MAX_TIMERS-1}}+1 bcs :exit @@ -137,38 +95,51 @@ RemoveTimer ENT stz Timers+6,x :exit - plb - rtl + rts ; Execute the timer functions -DoTimers ENT - phb - phk - plb +;DoTimers ENT +; phb +; jsr _SetDataBank +; +; jsr _GetVBLTicks +; +; cmp LastTick ; Throttle to 60 fps +; beq :exit +; tax ; Calculate the increment +; sec +; sbc LastTick +; stx LastTick - 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 +; cmp #60 +; bcs :exit - jsr _DoTimers +; jsr _DoTimers -:exit plb - rtl +;:exit plb +; rtl ; Countdown the timers -; -; A = number of elapsed ticks _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 hasn't been evaluated for over +; one second, then just skip processing and wait for the next call. + cmp #60 + bcc :do_timer +:exit rts + +:do_timer pha ldx #0 :loop @@ -186,9 +157,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 c1ba0dd..bd8970c 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -3,10 +3,47 @@ ; Ref: Toolbox Reference, Volume 2, Appendix A ; Ref: IIgs Tech Note #73 + use Mem.Macs.s + use Misc.Macs.s + use Util.Macs + use Locator.Macs + use Core.MACS.s + + use Defs.s + use static/TileStoreDefs.s + ToStrip equ $E10184 +; Define some macros to help streamline the entry and exit from the toolbox calls +_TSEntry mac + phd + phb + tcd + jsr _SetDataBank + <<< + +_TSExit mac + plb + pld + ldx ]1 ; Error code + ldy ]2 ; Number of stack bytes to remove + 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 + _CallTable - dw {_CTEnd-_CallTable}/4,0 + adrl {_CTEnd-_CallTable}/4 adrl _TSBootInit-1 adrl _TSStartUp-1 adrl _TSShutDown-1 @@ -16,8 +53,63 @@ _CallTable adrl _TSReserved-1 adrl _TSReserved-1 - adrl _TSSetScreenMode + adrl _TSReadControl-1 + adrl _TSSetScreenMode-1 + adrl _TSSetTile-1 + adrl _TSSetBG0Origin-1 + adrl _TSRender-1 + adrl _TSLoadTileSet-1 + adrl _TSCreateSpriteStamp-1 + adrl _TSAddSprite-1 + adrl _TSMoveSprite-1 + adrl _TSUpdateSprite-1 + adrl _TSRemoveSprite-1 + + 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 + + adrl _TSAddTimer-1 + adrl _TSRemoveTimer-1 + adrl _TSStartScript-1 + + adrl _TSSetOverlay-1 + adrl _TSClearOverlay-1 + + adrl _TSGetTileDataAddr-1 + adrl _TSFillTileStore-1 + adrl _TSRefresh-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 + pha + plb + rep #$20 + rts ; Do nothing when the tool set is installed _TSBootInit @@ -25,40 +117,66 @@ _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 number in high byte +; +; StartUp(dPageAddr, capFlags, userId) _TSStartUp -:zpToUse equ 7 - pea #$8000 +userId = 7 +capFlags = userId+2 +zpToUse = userId+4 + + 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 - pha + and #$00FF ; Get just the tool number + sta ToolNum - pea $0000 - lda :zpToUse+6,s - pha + lda userId+2,s ; Get the userId for memory allocations + sta UserId + + lda capFlags+2,s ; Get the engine capability bits + sta EngineMode + + phb + jsr _SetDataBank + jsr _CoreStartUp ; Initialize the library + plb + +; 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 + ldx #0 ; No error + ldy #6 ; Remove the 6 input bytes + jml ToStrip + +; ShutDown() _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 + phb + jsr _SetDataBank + jsr _CoreShutDown ; Shut down the library + plb pea $8000 - and #$00FF - pha + pei ToolNum pea $0000 ; Set WAP to null pea $0000 _SetWAP @@ -71,7 +189,7 @@ _TSShutDown rtl _TSVersion - lda #$0100 ; Version 1 + lda #$0100 ; Version 1 sta 7,s lda #0 @@ -88,7 +206,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 @@ -101,31 +219,523 @@ _TSReserved sec rtl +; SetScreenMode(width, height) _TSSetScreenMode - phd ; Preserve the direct page - pha - pld +height equ FirstParam +width equ FirstParam+2 - lda 9,s + _TSEntry + + lda height,s tay - lda 9,s + lda width,s tax jsr _SetScreenMode - pld - ldx #0 ; No error - ldy #4 ; Remove the 4 input bytes - jml ToStrip + _TSExit #0;#4 +; ReadControl() _TSReadControl - phd ; Preserve the direct page - pha - pld +:output equ FirstParam + + _TSEntry jsr _ReadControl - sta 9,s + sta :output,s - pld - ldx #0 ; No error - ldy #0 ; Remove zero input bytes - jml ToStrip + _TSExit #0;#0 + +; SetTile(xTile, yTile, tileId) +_TSSetTile +tileId equ FirstParam +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,s + jsr _SetTile + + _TSExit #0;#6 + +; SetBG0Origin(x, y) +_TSSetBG0Origin +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 + +; LoadTileSet(Pointer) +_TSLoadTileSet +TSPtr equ FirstParam + + _TSEntry + + lda TSPtr+2,s + tax + lda TSPtr,s + jsr _LoadTileSet + + _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 + +_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 +:spriteSlot equ FirstParam+4 + _TSEntry + + lda :spriteFlags,s + tax + lda :vbuff,s + tay + lda :spriteSlot,s + jsr _UpdateSprite + + _TSExit #0;#6 + +_TSRemoveSprite +:spriteSlot equ FirstParam+0 + _TSEntry + + lda :spriteSlot,s + jsr _RemoveSprite + + _TSExit #0;#2 + +_TSGetSeconds +:output equ FirstParam + + _TSEntry + + ldal OneSecondCounter + sta :output,s + + _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 + + +; SetPalette(palNum, Pointer) +_TSSetPalette +:ptr equ FirstParam+0 +:palNum equ FirstParam+4 + + _TSEntry + + phb + lda :ptr+3,s ; add one extra byte for the phb + xba + pha + plb + plb + + lda :ptr+1,s + tax + lda :palNum+1,s + 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+6 +:width equ FirstParam+4 +:y equ FirstParam+2 +:x equ FirstParam+0 + _TSEntry + + lda ScreenX0 + sta :x,s + lda ScreenY0 + sta :y,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 + + lda #DIRTY_BIT_BG0_REFRESH ; force a refresh of the BG0 on the next Render + tsb DirtyBits + + _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 + +; 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 +; 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 + +; 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 + put CoreImpl.s + put Memory.s + put Timer.s + put Script.s + put TileMap.s + put Graphics.s + put Tiles.s + put Sprite.s + put Sprite2.s + put SpriteRender.s + put Render.s + put render/Render.s + 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 + put blitter/SCB.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/Blitter.s + put blitter/TileProcs.s + put blitter/Tiles00000.s +; put blitter/Tiles.s diff --git a/src/_FileInformation.txt b/src/_FileInformation.txt new file mode 100644 index 0000000..8b4a06a --- /dev/null +++ b/src/_FileInformation.txt @@ -0,0 +1 @@ +Tool160=Type(BA),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000) diff --git a/src/blitter/BG0.s b/src/blitter/BG0.s index 31da819..32519af 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 @@ -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 c8718fe..71639fa 100644 --- a/src/blitter/BG1.s +++ b/src/blitter/BG1.s @@ -4,20 +4,11 @@ _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 ; 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 @@ -40,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 @@ -193,59 +176,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 +196,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) @@ -357,6 +205,8 @@ _ApplyBG1YPos :draw_count equ tmp2 :ytbl_idx equ tmp3 + phb ; Save the bank + lda BG1StartY jsr Mod208 sta BG1StartYMod208 @@ -413,7 +263,6 @@ _ApplyBG1YPos jne :loop - phk plb rts @@ -494,97 +343,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 ; @@ -596,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) @@ -738,7 +494,3 @@ ApplyBG1OffsetValues :none rts BG1YCache ds 32 - - - - diff --git a/src/blitter/Blitter.s b/src/blitter/Blitter.s index 54c3316..44d5f36 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,16 +27,16 @@ _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 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 @@ -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 @@ -59,7 +64,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/Horz.s b/src/blitter/Horz.s index a069313..eb7aa5d 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 @@ -48,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 @@ -57,7 +32,6 @@ _RestoreBG0Opcodes lda LastPatchOffset ; If zero, there are no saved opcodes sta :exit_offset - beq :loop :loop ldx :virt_line_x2 @@ -102,7 +76,6 @@ _RestoreBG0Opcodes stz LastPatchOffset ; Clear the value once completed :out - phk plb rts @@ -306,6 +279,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 @@ -380,7 +354,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 @@ -401,7 +375,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 new file mode 100644 index 0000000..fed6a55 --- /dev/null +++ b/src/blitter/Rotation.s @@ -0,0 +1,220 @@ +; 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 +; 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 +: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 + + phb +: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 + + 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 + + + jsr _SetDataBank ; restore access to this bank + jsr SaveBG1AngleValues + + plb + plx ; x is used directly in this routine + jmp ApplyBG1OffsetValues + +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/SCB.s b/src/blitter/SCB.s index f60f3c9..6e48756 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 @@ -36,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 @@ -52,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 @@ -82,13 +74,7 @@ _BltSCB plb ; restore the bank rts - -; Quick helper to set the pointer (X = low word, A = high work) -SetSCBArray ENT - jsr _SetSCBArray - rtl - -_SetSCBArray +_BindSCBArray stx SCBArrayPtr sta SCBArrayPtr+2 rts diff --git a/src/blitter/Template.s b/src/blitter/Template.s index ac7662b..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,538 +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 - -; Return - 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. diff --git a/src/blitter/TemplateUtils.s b/src/blitter/TemplateUtils.s new file mode 100644 index 0000000..9f29eed --- /dev/null +++ b/src/blitter/TemplateUtils.s @@ -0,0 +1,336 @@ +; 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 + sta 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 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 +; 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 +:entryOffset equ tmp6 + + 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 #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 + + ldy #DP_ADDR + jsr SetDPAddrs + + ldy #$F000+CODE_EXIT ; Patch the last line with a JML to go to the next bank + lda :entryOffset + xba + ora #$005C + 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 + 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 + 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 + + rep #$20 + plb + rts \ No newline at end of file diff --git a/src/blitter/TileProcs.s b/src/blitter/TileProcs.s new file mode 100644 index 0000000..b5c77db --- /dev/null +++ b/src/blitter/TileProcs.s @@ -0,0 +1,121 @@ +; 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 +]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. +; +; 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 + +; 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 +_TBConstTileX + 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 + +_TBConstTileSlow0 + tax + jsr _TBFillPEAOpcode + jmp _TBConstTileX + +_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/blitter/Tiles.s b/src/blitter/Tiles.s index ba76c3f..8a7adf4 100644 --- a/src/blitter/Tiles.s +++ b/src/blitter/Tiles.s @@ -37,132 +37,45 @@ ; ; 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 - -; 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 ; -; 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 - -; 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. ; -; Y = address of tile +; 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 + _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 +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 -; txa -; jsr BuildActiveSpriteArray ; Build the max 4 array of active sprites for this tile -; sta ActiveSpriteCount +; 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_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_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 +84,147 @@ _RenderTile2 :tiledisp jmp $0000 ; render the tile +; 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 that are used across all copy routines + + 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 + +; 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 + 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] +LDA_ILY equ $B7 ; lda [dp],y +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 + 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 + 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 + 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 + +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 + 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: ; @@ -297,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. @@ -421,14 +358,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 @@ -474,14 +403,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 @@ -518,7 +439,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 @@ -543,262 +464,117 @@ _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 - -; 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 +; 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 #41*2 - iny - iny - cpy #26*2 - bcc :yloop + adc #$100 + tcd -; Next, initialize the Tile Store itself + phb ; Save the current bank + tsc + sta tmp0 ; Save it on the direct page + bra at_loop - ldx #TILE_STORE_SIZE-2 - lda #25 - sta :row - lda #40 - sta :col - lda #$8000 - sta :vbuff +; 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. -:loop -; The first set of values in the Tile Store are changed during each frame based on the actions -; that are happening +at_exit + tdc ; Move back to the original direct page + sec + sbc #$100 + tcd - 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 - - 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 + plb ; Restore the original data bank and return rts +dt_base equ $FE ; top of second direct page space -_ClearDirtyTiles - bra :hop -:loop - jsr _PopDirtyTile -:hop - lda DirtyTileCount - bne :loop - rts +at_loop + lda tmp0 + tcs -; 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 + lda DirtyTileCount ; This is pre-multiplied by 2 + beq at_exit ; If there are no items, exit + ldx TileStoreBankDoubled + phx + phx + phx -_GetTileStoreOffset - phx ; preserve the registers - phy + cmp #16 ; If there are >= 8 elements, then + bcs at_chunk ; do a full chunk - 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 + stz DirtyTileCount ; Otherwise, this pass will handle them all tax - pla - - cmpl TileStore+TS_TILE_ID,x ; Only set to dirty if the value changed - beq :nochange + jmp (at_table,x) +at_table da at_exit,at_one,at_two,at_three + da at_four,at_five,at_six,at_seven - 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 +at_chunk sec + sbc #16 + sta DirtyTileCount ; Fall through - ldal TileStore+TS_TILE_ID,x - and #TILE_VFLIP_BIT+TILE_HFLIP_BIT ; get the lookup value - xba - stal TileStore+TS_TILE_DISP,x +; 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 -; txa ; Add this tile to the list of dirty tiles to refresh - jmp _PushDirtyTileX ; on the next call to _ApplyTiles + 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 -:nochange 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 - bpl :occupied2 - - txa ; any non-negative value will work, this saves work below - stal TileStore+TS_DIRTY,x ; and is 1 cycle faster than loading a constant value - - 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 - -_ApplyTiles - 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 +at_seven + ldy dt_base + ldx DirtyTiles+12,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile2 plb -; Loop again until the list of dirty tiles is empty +at_six + ldy dt_base + ldx DirtyTiles+10,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile2 + plb -:begin ldy DirtyTileCount - bne :loop - rts \ No newline at end of file +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/Tiles00000.s b/src/blitter/Tiles00000.s index 709b54d..19fc409 100644 --- a/src/blitter/Tiles00000.s +++ b/src/blitter/Tiles00000.s @@ -36,6 +36,53 @@ _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 +CopyTileAFast + tax +_CopyTileAFast +]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 + +CopyTileASlow + tax + jsr FillPEAOpcode + jmp _CopyTileAFast + +CopyTileVFast + tax +_CopyTileVFast +]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 + +CopyTileVSlow + tax + jsr FillPEAOpcode + jmp _CopyTileVFast + + + +; Old routines _TBCopyData ]line equ 0 lup 8 @@ -47,17 +94,6 @@ _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 - _TBCopyDataV ]src equ 7 ]dest equ 0 @@ -71,40 +107,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/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/Tiles10000.s b/src/blitter/Tiles10000.s index df1b0ab..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} @@ -46,118 +45,46 @@ _TBApplySpriteData --^ 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 +_TBApplySpriteDataOne + ldx spriteIdx ]line equ 0 lup 8 - ldal tiledata+{]line*4},x - sta blttmp+{]line*4} + lda blttmp+{]line*4} + andl spritemask+{]line*SPRITE_PLANE_SPAN},x + oral spritedata+{]line*SPRITE_PLANE_SPAN},x + sta: $0004+{]line*$1000},y - ldal tiledata+{]line*4}+2,x - sta blttmp+{]line*4}+2 + 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 + 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 +_TBApplySpriteDataTwo ]line equ 0 lup 8 - ldal tiledata+{]line*4}+32,x - sta blttmp+{]line*4}+32 + 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 - ldal tiledata+{]line*4}+32+2,x - sta blttmp+{]line*4}+32+2 + 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 - -;_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 - + rts ; Copy just the data into the code field from the composite buffer _TBSolidComposite diff --git a/src/blitter/Tiles10001.s b/src/blitter/Tiles10001.s index 438921f..54adc98 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 ; Advance to the next word + 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. ; @@ -70,13 +162,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 - bne mixed ; a non-zero value may be mixed + 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 +187,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/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..042cce3 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 @@ -67,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 @@ -112,9 +91,9 @@ _ApplyBG0YPos sta :lines_left jne :loop - - phk plb + +:out rts ; Unrolled copy routine to move RTable intries into STK_ADDR position. @@ -167,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 @@ -175,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 @@ -183,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/render/Dynamic.s b/src/render/Dynamic.s new file mode 100644 index 0000000..d417675 --- /dev/null +++ b/src/render/Dynamic.s @@ -0,0 +1,711 @@ +; 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 #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 + + 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 #$3580 ; Pre-calc the AND $80,x opcode + operand + 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 #SNIPPET_SIZE + sta _JTBL_CACHE + + 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 +_DynFillJmpOpcode + 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 + +; 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 +; 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&$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 #$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&$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 _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 + +; 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 +; 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/Fast.s b/src/render/Fast.s new file mode 100644 index 0000000..da51d13 --- /dev/null +++ b/src/render/Fast.s @@ -0,0 +1,258 @@ +; 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. + +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 + lda TileStore+TS_TILE_ADDR,x + tax + plb + +_SpriteOverAFast ; Alternate entry point for the "Slow" routines +]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 + +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 + lda TileStore+TS_TILE_ADDR,x + tax + plb + +_SpriteOverVFast +]src equ 7 +]dest equ 0 + lup 8 + ldal tiledata+{]src*4},x + 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+{]dest*4}+2 + ora tmp_sprite_data+{]dest*4}+2 + sta: $0001+{]dest*$1000},y +]src equ ]src-1 +]dest equ ]dest+1 + --^ + plb + rts + +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} + sta: $0004+{]line*$1000},y + + lda tmp_sprite_data+{]line*4}+2 + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + + plb + rts + +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 + lda TileStore+TS_TILE_ADDR,x + tax + plb + +_SpriteUnderAFast +]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 + +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 + lda TileStore+TS_TILE_ADDR,x + tax + plb + +_SpriteUnderVFast +]src equ 7 +]dest equ 0 + lup 8 + lda tmp_sprite_data+{]dest*4} + andl tiledata+{]src*4}+32,x + oral tiledata+{]src*4},x + sta: $0004+{]dest*$1000},y + + lda tmp_sprite_data+{]dest*4}+2 + andl tiledata+{]src*4}+32+2,x + oral tiledata+{]src*4}+2,x + sta: $0001+{]dest*$1000},y +]src equ ]src-1 +]dest equ ]dest+1 + --^ + + plb + rts + +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 + +_SpriteUnder0Fast + lda #0 +]line equ 0 + lup 8 + sta: $0004+{]line*$1000},y + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + + 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 + +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/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..82f311a --- /dev/null +++ b/src/render/Render.s @@ -0,0 +1,86 @@ +; 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.) +; +; 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 + jmp CopyTwoSpritesDataAndMaskToDP + +ThreeSprites tyx + jmp CopyThreeSpritesDataAndMaskToDP + +FourSprites tyx + jmp CopyFourSpritesDataAndMaskToDP + +; Now, implement the generic Two, Three and Four sprite routines for both Over and Under rendering. These +; are fairly involved, so we try to only have a single implementation of them for now without excessve +; specialization. + + +FourSpriteLine mac +; and [sprite_ptr3],y + db $37,sprite_ptr3 + ora (sprite_ptr3),y +; and [sprite_ptr2],y + db $37,sprite_ptr2 + ora (sprite_ptr2),y +; and [sprite_ptr1],y + db $37,sprite_ptr1 + ora (sprite_ptr1),y +; and [sprite_ptr0],y + db $37,sprite_ptr0 + ora (sprite_ptr0),y + <<< + +FourSpritesFast + tyx ; save for after compositing the sprites + + ldy TileStore+TS_TILE_ADDR,x + pei DP2_TILEDATA_AND_TILESTORE_BANKS + plb + jsr (K_TS_COPY_TILE_DATA,x) + plb + + pei DP2_SPRITEDATA_AND_TILESTORE_BANKS + plb ; set the sprite data bank + +]line equ 0 + lup 8 + ldy #{]line*SPRITE_PLANE_SPAN} + lda tmp_tile_data+{]line*4} + FourSpriteLine + sta tmp_tile_data+{]line*4} + + ldy #{]line*SPRITE_PLANE_SPAN}+2 + lda tmp_tile_data+{]line*4}+2 + FourSpriteLine + sta tmp_tile_data+{]line*4}+2 +]line equ ]line+1 + --^ + + plb + jmp (K_TS_APPLY_TILE_DATA,x) + + diff --git a/src/render/Slow.s b/src/render/Slow.s new file mode 100644 index 0000000..ff833f1 --- /dev/null +++ b/src/render/Slow.s @@ -0,0 +1,86 @@ +; 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 + +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 + +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 + sta: $0000+{]line*$1000},y + sta: $0003+{]line*$1000},y +]line equ ]line+1 + --^ + rep #$20 + rts + +; This is a stub; 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 new file mode 100644 index 0000000..e976865 --- /dev/null +++ b/src/render/Sprite1.s @@ -0,0 +1,265 @@ +; 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 + +_OneSpriteFastOver0 +]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. The 1-sprite dispatch preserves the +; X-register, so it already points to the TileStore + +OneSpriteFastOverV + jsr FastCopyTileDataV + bra _OneSpriteFastOver + +OneSpriteFastOverA + jsr FastCopyTileDataA + +_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 + +_OneSpriteFastOverA +_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 + --^ + 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 + jsr FillPEAOpcode + jmp _OneSpriteFastOver0 + +; Slow variant for regular tile. +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 + +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 + jsr FastCopyTileDataAndMaskA + bra _OneSpriteFastUnder + +OneSpriteFastUnderV + jsr FastCopyTileDataAndMaskV + +_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 + +_OneSpriteFastUnderA +_OneSpriteFastUnderV +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + 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 + and tmp_tile_mask+{]line*4}+2 + ora tmp_tile_data+{]line*4}+2 + sta: $0001+{]line*$1000},y +]line equ ]line+1 + --^ + + plb + rts + +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 + ldx sprite_ptr0 + plb + jsr FillPEAOpcode + jmp _OneSpriteFastUnderA + +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 + +;------------------------------- +; Dynamic tiles with one sprite. + +OneSpriteDynamicUnder + txy + tax + jsr _CopySpriteDataToDP + tyx + jmp DynamicUnder + +OneSpriteDynamicOver + txy + tax + jsr _CopySpriteDataAndMaskToDP + tyx + jmp DynamicOver + + +;------------------------------- +; Two Layer tiles with one sprite. Just copy the data and go through the generic sprite path +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 + +;----------------------------------------- +; 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 diff --git a/src/render/Sprite2.s b/src/render/Sprite2.s new file mode 100644 index 0000000..452beec --- /dev/null +++ b/src/render/Sprite2.s @@ -0,0 +1,37 @@ +; Specialize routines for handling two sprites. +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 + <<< + +CopyFourSpritesDataAndMaskToDP +CopyThreeSpritesDataAndMaskToDP +CopyTwoSpritesDataAndMaskToDP + pei DP2_SPRITEDATA_AND_TILESTORE_BANKS + plb + +]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 + --^ + + plb + 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..e69de29 diff --git a/src/render/Sprite4.s b/src/render/Sprite4.s new file mode 100644 index 0000000..e69de29 diff --git a/src/render/TwoLayer.s b/src/render/TwoLayer.s new file mode 100644 index 0000000..8ca2df6 --- /dev/null +++ b/src/render/TwoLayer.s @@ -0,0 +1,376 @@ +; 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 + ldal TileStore+TS_WORD_OFFSET,x + 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 + plb + 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 + ldal TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + 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 + + ldal TileStore+TS_TILE_ADDR,x + tax + +]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 + ldal TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + 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 + + ldal TileStore+TS_TILE_ADDR,x + tax + +]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 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/static/SprData.s b/src/static/SprData.s new file mode 100644 index 0000000..8c9150b --- /dev/null +++ b/src/static/SprData.s @@ -0,0 +1,5 @@ +; sprite stamp pixel data +spritedata ENT +; ds 65535 + ds 65536 + diff --git a/src/static/SprMask.s b/src/static/SprMask.s new file mode 100644 index 0000000..d8e2f9b --- /dev/null +++ b/src/static/SprMask.s @@ -0,0 +1,5 @@ +; sprite stamp masks +spritemask ENT +; ds 65535 + ds 65536 + diff --git a/src/static/TileData.s b/src/static/TileData.s new file mode 100644 index 0000000..aa77f32 --- /dev/null +++ b/src/static/TileData.s @@ -0,0 +1,5 @@ +; Bank of memory that holds the 8x8 tile data +tiledata ENT +; ds 65535 + ds 65536 + diff --git a/src/blitter/Tables.s b/src/static/TileStore.s similarity index 52% rename from src/blitter/Tables.s rename to src/static/TileStore.s index 8493199..06c218c 100644 --- a/src/blitter/Tables.s +++ b/src/static/TileStore.s @@ -1,5 +1,88 @@ -; Collection of data tables +; Bank of memory that holds the core sprite and tile store data structures + + put ../Defs.s + put TileStoreDefs.s + +;------------------------------------------------------------------------------------- ; + put ../blitter/Template.s + +;------------------------------------------------------------------------------------- + +TileStore ENT + ds {TILE_STORE_SIZE*TILE_STORE_NUM} + +;------------------------------------------------------------------------------------- +; +; 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 updated 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 +]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 + +; First 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*2*TILE_STORE_WIDTH+2 +]row equ ]row+1 + --^ + +; 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*2*TILE_STORE_WIDTH+2 +]row equ ]row+1 + --^ + +; Last two rows + TileStoreData 0*2*TILE_STORE_WIDTH + TileStoreData 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 + +;------------------------------------------------------------------------------------- +; +; Other data tables ; Col2CodeOffset ; @@ -19,21 +102,21 @@ ; ; 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 +]step equ 0 dw CODE_TOP ; There is a spot where we load Col2CodeOffet-2,x -Col2CodeOffset lup 82 +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 -SNIPPET_SIZE equ 32 ]step equ 0 dw SNIPPET_BASE -JTableOffset lup 82 +JTableOffset ENT + lup 82 dw SNIPPET_BASE+{{81-]step}*SNIPPET_SIZE} ]step equ ]step+1 --^ @@ -46,7 +129,7 @@ JTableOffset lup 82 ; ; 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 +CodeFieldEvenBRA ENT bra *+6 ; 81 -- need to skip over the JMP loop that passed control back bra *+9 ; 80 bra *+12 ; 79 @@ -130,7 +213,7 @@ CodeFieldEvenBRA bra *-6 ; 1 bra *-3 ; 0 -CodeFieldOddBRA +CodeFieldOddBRA ENT bra *+9 ; 81 -- need to skip over two JMP instructions bra *+12 ; 80 bra *+15 ; 79 @@ -240,7 +323,7 @@ TileStoreYTable ENT ; 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 +NextCol ENT ]step equ 0 lup 41 dw ]step @@ -252,16 +335,12 @@ 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 constan value. -;TileStore2DLookup ds {26*41*2}*4 - ; 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 +RTable ENT + ds 400 ds 400 ; Array of addresses for the banks that hold the blitter. @@ -271,17 +350,22 @@ BlitBuff ENT ; 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 +BTableHigh ENT + ds 208*2*2 +BTableLow ENT + ds 208*2*2 ; A shorter table that just holds the blitter row addresses -BRowTableHigh ds 26*2*2 -BRowTableLow ds 26*2*2 +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 lup 208 +BG1YTable ENT + lup 208 dw ]step ]step = ]step+256 --^ @@ -292,11 +376,135 @@ BG1YTable lup 208 --^ ; Repeat -BG1YOffsetTable lup 26 +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 +Overlays ENT + dw 0 ; count + ds 8 ; only support one or now (start_line, end_line, function call) +; From the IIgs ref +DefaultPalette ENT + 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%)) +; 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 +; 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. +; +; It is *critical* that this array be placed in a memory location that is greater than the largest +; 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 ; 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 + 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 + ds {TILE_STORE_WIDTH*2}*3 + +; 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 + +; Steps to the different sprite stamps +_stamp_step ENT + dw 0,12,24,36 + +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..8a5d3d1 --- /dev/null +++ b/src/static/TileStoreDefs.s @@ -0,0 +1,113 @@ +; 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 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_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 + +; 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 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. +; 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. + +; 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} +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 +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 +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 +; +; 53 rows = TILE_STORE_HEIGHT + TILE_STORE_HEIGHT + 1 +; 83 cols = TILE_STORE_WIDTH + TILE_STORE_WIDTH + 1 +; +; 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} + +; Blitter template constants +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/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/DirtyTileQueue.s b/src/tiles/DirtyTileQueue.s new file mode 100644 index 0000000..459c00e --- /dev/null +++ b/src/tiles/DirtyTileQueue.s @@ -0,0 +1,181 @@ +_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 + lda TileStore+TS_DIRTY,x + bne :occupied2 + + 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 ; 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 + +; 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. +_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 + +; 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 than 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 + + 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 _RenderTile + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+12,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+10,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+8,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+6,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+4,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+2,y + stz TileStore+TS_DIRTY,x + jsr _RenderTile + + ldy DP2_DIRTY_TILE_COUNT + ldx DirtyTiles+0,y + stz TileStore+TS_DIRTY,x + 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 _RenderTile + +at_six + ldx DirtyTiles+10 + stz TileStore+TS_DIRTY,x + jsr _RenderTile + +at_five + ldx DirtyTiles+8 + stz TileStore+TS_DIRTY,x + jsr _RenderTile + +at_four + ldx DirtyTiles+6 + stz TileStore+TS_DIRTY,x + jsr _RenderTile + +at_three + ldx DirtyTiles+4 + stz TileStore+TS_DIRTY,x + jsr _RenderTile + +at_two + ldx DirtyTiles+2 + stz TileStore+TS_DIRTY,x + jsr _RenderTile + +at_one + ldx DirtyTiles+0 + stz TileStore+TS_DIRTY,x + jmp _RenderTile