; 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 ora #SNIPPET_ENTRY_2 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 ora #SNIPPET_ENTRY_3 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 ora #SNIPPET_ENTRY_4 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 showing the second background DynamicOverTwoLyr lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler ora #SNIPPET_ENTRY_1 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 under a dyamic tile with transparent areas showing the second background ; ; This is a special case where we cannot fit the code into the fixed snippet structure. As such, ; any tile drawn with this routine will set a DAMAGED flag on the TileStore flags. If another ; tile blitter in the TwoLayer function set sees that a tile is marked as DAMAGED, it must ; restore the original code structure before proceeding. ; ; The damaged area is not too bad -- just the 10 bytes from [2, 10] are overwritten and must be ; restored. This is actually less work than a lot of the snippet macros were doing before ; applying the fixed snippet optimization. DynamicUnderTwoLyr lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler ora #SNIPPET_ENTRY_1 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 ora #TILE_DAMAGED_BIT ; Set the DAMAGED bit here since we have to load TILE_ID anyway sta TileStore+TS_TILE_ID,x 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 tmp_sprite_mask+{]1} sta: $0003,x lda tmp_sprite_data+{]1} sta: $0006,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 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 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 eom ; Version 2 will set the JMP to Entry Point 1 and set the Opcode at Entry Point 2 to a ora $00,x. Also ; the mask transparency check can be performed earlier. ; ; 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_2 ; switch from AND to ORA instruction cached in setup ; sta: $0004,x ; ORA $00,x 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 constant 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, but the entry point changes depending on ; whether the mask is transparent or not mixed cmp #$FFFF 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_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 tmp_sprite_mask+{]1} sta: $0007,x lda tmp_sprite_data+{]1} sta: $000A,x bra next ; This is a transparent word, so just show the dynamic data overlaid on layer 2 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}.SNIPPET_ENTRY_4 ; adjust for the current row offset and OR in the offset since snippets are 32-byte aligned sta: {]2}+1,y tax 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 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 ; This macro has different targets based on the transparency lda tmp_sprite_mask+{]1} 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 #$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 _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 bra next transparent ; This is a transparent word, so just show the dynamic data lda #$004C ; JMP to handler sta: {]2},y lda _JTBL_CACHE ; Get the offset to the exception handler for this column ora #{]2&$7000}.SNIPPET_ENTRY_4 ; adjust for the current row offset and OR in the offset since snippets are 32-byte aligned sta: {]2}+1,y tax 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 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