diff --git a/src/Defs.s b/src/Defs.s index 4eb1430..2e9e66f 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -139,6 +139,7 @@ tmp_tile_mask equ 112 ; 32 byte temporary buffer to build up _X_REG equ 144 _Y_REG equ 146 _T_PTR equ 148 ; Copy of the tile address pointer +_OP_CACHE2 equ 148 ; CAche of second opcode _BASE_ADDR equ 150 ; Copy of BTableLow for this tile _SPR_X_REG equ 152 ; Cache address of sprite plane source for a tile _JTBL_CACHE equ 154 ; Cache the offset to the exception handler for a column diff --git a/src/Tiles.s b/src/Tiles.s index 7aa7120..aa99e57 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -243,15 +243,21 @@ _SetTile lda EngineMode bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER - bne :not_fast - brl :setTileFast + beq :setTileFast -:not_fast bit #ENGINE_MODE_TWO_LAYER + bit #ENGINE_MODE_TWO_LAYER bne :not_dyn brl :setTileDyn :not_dyn - ldy #SlowProcs ; safe for now.... + lda #TILE_DYN_BIT + bit newTileId + beq :pickTwoLyrProc + + ldy #TwoLyrDynProcs + brl :pickDynProc + +:pickTwoLyrProc ldy #SlowProcs ; #TwoLyrProcs lda procIdx jsr _SetTileProcs jmp _PushDirtyTileX @@ -271,12 +277,12 @@ _SetTile bit newTileId beq :pickSlowProc ; If the Dynamic bit is not set, select a tile proc that sets opcodes + ldy #DynProcs ; use this table +:pickDynProc lda newTileId ; Otherwise chose one of the two dynamic tuples and #TILE_PRIORITY_BIT - beq :pickDynProc ; If the Priority bit is not set, pick the first entry + beq *+5 ; If the Priority bit is not set, pick the first entry lda #1 ; If the Priority bit is set, pick the other one - -:pickDynProc ldy #DynProcs jsr _SetTileProcs jmp _PushDirtyTileX @@ -404,6 +410,11 @@ TwoLyrUnderZV dw Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr TwoLyrUnderNA dw CopyTileATwoLyr,SpriteUnderATwoLyr,OneSpriteTwoLyrUnderA TwoLyrUnderNV dw CopyTileVTwoLyr,SpriteUnderVTwoLyr,OneSpriteTwoLyrUnderV +; "Dynamic" procs that can handle the second background. +TwoLyrDynProcs +TwoLyrDynOver dw CopyDynamicTileTwoLyr,DynamicOverTwoLyr,OneSpriteDynamicOverTwoLyr +TwoLyrDynUnder dw CopyDynamicTileTwoLyr,DynamicUnderTwoLyr,OneSpriteDynamicUnderTwoLyr + ; SetBG0XPos ; ; Set the virtual horizontal position of the primary background layer. In addition to diff --git a/src/render/Dynamic.s b/src/render/Dynamic.s index 041106c..d417675 100644 --- a/src/render/Dynamic.s +++ b/src/render/Dynamic.s @@ -133,10 +133,11 @@ DynamicUnder CopyDynUnder 30;$7000 ; Now fill in the JMP opcodes +_DynFillJmpOpcode sep #$20 lda #$4C - sta: $0000,y - sta: $0003,y + sta: $0000,y + sta: $0003,y sta $1000,y sta $1003,y sta $2000,y @@ -156,6 +157,185 @@ DynamicUnder plb rts +; Bank is already set to code field +; Y register is the offset +; X register is the TileStore +; A is the tile address +CopyDynamicTileTwoLyr + + ldal TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + ldal TileStore+TS_WORD_OFFSET,x + ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand + xba + sta _OP_CACHE + +; We need to do an AND dp|$80,x / ORA dp,x. The opcode values are $35 and $15, respectively. +; We pre-calculate the AND opcode with the high bit of the operand set and then, in the macro +; perform and EOR #$2080 to covert the opcode and operand in one instruction + + ldal TileStore+TS_TILE_ID,x ; Get the original tile descriptor + and #$007F ; clamp to < (32 * 4) + ora #$3580 ; Pre-calc the AND $80,x opcode + operand + xba + sta _OP_CACHE2 ; This is an op to load the dynamic tile data + + CopyMaskedDWord $0003 + CopyMaskedDWord $1003 + CopyMaskedDWord $2003 + CopyMaskedDWord $3003 + CopyMaskedDWord $4003 + CopyMaskedDWord $5003 + CopyMaskedDWord $6003 + CopyMaskedDWord $7003 + + sec + lda _JTBL_CACHE + sbc #SNIPPET_SIZE ; Advance to the next snippet (Reverse indexing) + sta _JTBL_CACHE + + clc + lda _OP_CACHE + adc #$0200 ; Advance to the next word + sta _OP_CACHE + + lda _OP_CACHE2 + adc #$0200 + sta _OP_CACHE2 + + CopyMaskedDWord $0000 + CopyMaskedDWord $1000 + CopyMaskedDWord $2000 + CopyMaskedDWord $3000 + CopyMaskedDWord $4000 + CopyMaskedDWord $5000 + CopyMaskedDWord $6000 + CopyMaskedDWord $7000 + + jmp _DynFillJmpOpcode + +; Render a sprite on top of a dyamic tile with transparent areas shwing the second background +DynamicOverTwoLyr + lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + lda TileStore+TS_WORD_OFFSET,x + ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand + xba + sta _OP_CACHE + +; We need to do an AND dp|$80,x / ORA dp,x. The opcode values are $35 and $15, respectively. +; We pre-calculate the AND opcode with the high bit of the operand set and then, in the macro +; perform and EOR #$2080 to covert the opcode and operand in one instruction + + lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor + and #$007F ; clamp to < (32 * 4) + ora #$3580 ; Pre-calc the AND $80,x opcode + operand + xba + sta _OP_CACHE2 ; This is an op to load the dynamic tile data + + lda TileStore+TS_CODE_ADDR_HIGH,x + pha + ldy TileStore+TS_CODE_ADDR_LOW,x + plb + + CopyDynMaskedSpriteWord 0;$0003 + CopyDynMaskedSpriteWord 4;$1003 + CopyDynMaskedSpriteWord 8;$2003 + CopyDynMaskedSpriteWord 12;$3003 + CopyDynMaskedSpriteWord 16;$4003 + CopyDynMaskedSpriteWord 20;$5003 + CopyDynMaskedSpriteWord 24;$6003 + CopyDynMaskedSpriteWord 28;$7003 + + sec + lda _JTBL_CACHE + sbc #SNIPPET_SIZE ; Advance to the next snippet (Reverse indexing) + sta _JTBL_CACHE + + clc + lda _OP_CACHE + adc #$0200 ; Advance to the next word + sta _OP_CACHE + + lda _OP_CACHE2 + adc #$0200 ; Advance to the next word + sta _OP_CACHE2 + + CopyDynMaskedSpriteWord 2;$0000 + CopyDynMaskedSpriteWord 6;$1000 + CopyDynMaskedSpriteWord 10;$2000 + CopyDynMaskedSpriteWord 14;$3000 + CopyDynMaskedSpriteWord 18;$4000 + CopyDynMaskedSpriteWord 22;$5000 + CopyDynMaskedSpriteWord 26;$6000 + CopyDynMaskedSpriteWord 30;$7000 + + plb + rts + +; Render a sprite on top of a dyamic tile with transparent areas shwing the second background +DynamicUnderTwoLyr + lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler + sta _JTBL_CACHE + + lda TileStore+TS_WORD_OFFSET,x + ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand + xba + sta _OP_CACHE + +; We need to do an AND dp|$80,x / ORA dp,x. The opcode values are $35 and $15, respectively. +; We pre-calculate the AND opcode with the high bit of the operand set and then, in the macro +; perform and EOR #$2080 to covert the opcode and operand in one instruction + + lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor + and #$007F ; clamp to < (32 * 4) + ora #$3580 ; Pre-calc the AND $80,x opcode + operand + xba + sta _OP_CACHE2 ; This is an op to load the dynamic tile data + + lda TileStore+TS_CODE_ADDR_HIGH,x + pha + ldy TileStore+TS_CODE_ADDR_LOW,x + plb + + CopyDynPrioMaskedSpriteWord 0;$0003 + CopyDynPrioMaskedSpriteWord 4;$1003 + CopyDynPrioMaskedSpriteWord 8;$2003 + CopyDynPrioMaskedSpriteWord 12;$3003 + CopyDynPrioMaskedSpriteWord 16;$4003 + CopyDynPrioMaskedSpriteWord 20;$5003 + CopyDynPrioMaskedSpriteWord 24;$6003 + CopyDynPrioMaskedSpriteWord 28;$7003 + + sec + lda _JTBL_CACHE + sbc #SNIPPET_SIZE ; Advance to the next snippet (Reverse indexing) + sta _JTBL_CACHE + + clc + lda _OP_CACHE + adc #$0200 ; Advance to the next word + sta _OP_CACHE + + lda _OP_CACHE2 + adc #$0200 ; Advance to the next word + sta _OP_CACHE2 + + CopyDynPrioMaskedSpriteWord 2;$0000 + CopyDynPrioMaskedSpriteWord 6;$1000 + CopyDynPrioMaskedSpriteWord 10;$2000 + CopyDynPrioMaskedSpriteWord 14;$3000 + CopyDynPrioMaskedSpriteWord 18;$4000 + CopyDynPrioMaskedSpriteWord 22;$5000 + CopyDynPrioMaskedSpriteWord 26;$6000 + CopyDynPrioMaskedSpriteWord 30;$7000 + + plb + rts + + ; Create a masked render based on data in the direct page temporary buffer. ; ; If the MASK is $0000, then insert a PEA @@ -246,6 +426,172 @@ CopyDynUnder MAC sta: $0007,x eom +; Masked renderer for a dynamic tile. What's interesting about this renderer is that the mask +; value is not used directly, but simply indicates if we can use a LDA 0,x / PHA sequence, +; a LDA (00),y / PHA, or a JMP to a blended render +; +; If a dynamic tile is animated, there is the possibility to create a special mask that marks +; words of the tile that a front / back / mixed across all frames. +; +; ]1 : code field offset +; +; This macro does not set the opcode since they will all be JMP instructions, they can be +; filled more efficiently in a separate routine. +CopyMaskedDWord MAC + +; Need to fill in the first 6 bytes of the JMP handler with the following code sequence +; +; lda (00),y +; and $80,x +; ora $00,x +; bra *+17 + + lda _JTBL_CACHE + ora #{{]1}&$7000} ; adjust for the current row offset + sta: {]1}+1,y + + tax ; This becomes the new address that we use to patch in + lda _OP_CACHE + sta: $0000,x ; LDA (00),y + lda _OP_CACHE2 + sta: $0002,x ; AND $80,x + eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand + sta: $0004,x ; ORA $00,x + lda #$0F80 ; branch to the prologue (BRA *+17) + sta: $0006,x + eom + + +; Masked renderer for a masked dynamic tile with sprite data overlaid. +; +; ]1 : sprite plane offset +; ]2 : code field offset +CopyDynMaskedSpriteWord MAC + +; Need to fill in the first 14 bytes of the JMP handler with the following code sequence where +; the data and mask from from the sprite plane +; +; lda ($00),y +; and $80,x +; ora $00,x +; and #MASK +; ora #DATA +; bra *+15 +; +; If MASK == 0, then we can do a PEA. If MASK == $FFFF, then fall back to the simple Dynamic Tile +; code and eliminate the constanct AND/ORA instructions. + + lda tmp_sprite_mask+{]1} ; load the mask value + bne mixed ; a non-zero value may be mixed + +; This is a solid word + lda #$00F4 ; PEA instruction + sta: {]2},y + lda tmp_sprite_data+{]1} ; load the sprite data + sta: {]2}+1,y ; PEA operand + bra next + +; We will always do a JMP to the exception handler, so set that up, then check for sprite +; transparency +mixed + lda #$004C ; JMP to handler + sta: {]2},y + lda _JTBL_CACHE ; Get the offset to the exception handler for this column + ora #{]2&$7000} ; adjust for the current row offset + sta: {]2}+1,y + tax ; This becomes the new address that we use to patch in + + lda _OP_CACHE + sta: $0000,x ; LDA (00),y + lda _OP_CACHE2 + sta: $0002,x ; AND $80,x + eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand + sta: $0004,x ; ORA $00,x + + lda #$0029 ; AND #SPRITE_MASK + sta: $0006,x + lda tmp_sprite_mask+{]1} + cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word + beq transparent + sta: $0007,x + + lda #$0009 ; ORA #SPRITE_DATA + sta: $0009,x + lda tmp_sprite_data+{]1} + sta: $000A,x + + lda #$0980 ; branch to the prologue (BRA *+11) + sta: $000C,x + bra next + +; This is a transparent word, so just show the dynamic data +transparent + lda #$0F80 ; branch to the epilogue (BRA *+17) + sta: $0006,x +next + eom + + +; Masked renderer for a masked dynamic tile with sprite data underlaid. +; +; ]1 : sprite plane offset +; ]2 : code field offset +CopyDynPrioMaskedSpriteWord MAC + +; Need to fill in the first 14 bytes of the JMP handler with the following code sequence where +; the data and mask from from the sprite plane +; +; lda ($00),y +; and #MASK +; ora #DATA +; and $80,x +; ora $00,x +; bra *+15 + + lda #$004C ; JMP to handler + sta: {]2},y + lda _JTBL_CACHE ; Get the offset to the exception handler for this column + ora #{]2&$7000} ; adjust for the current row offset + sta: {]2}+1,y + tax ; This becomes the new address that we use to patch in + + lda _OP_CACHE + sta: $0000,x ; LDA (00),y + + lda #$0029 ; AND #SPRITE_MASK + sta: $0002,x + + lda tmp_sprite_mask+{]1} + cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word + beq transparent ; so we can use the Tile00011 method + sta: $0003,x + + lda #$0009 ; ORA #SPRITE_DATA + sta: $0005,x + lda tmp_sprite_data+{]1} + sta: $0006,x + + lda _OP_CACHE2 + sta: $0008,x ; AND $80,x + eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand + sta: $000A,x ; ORA $00,x + + lda #$0980 ; branch to the prologue (BRA *+11) + sta: $000C,x + bra next + +; This is a transparent word, so just show the dynamic data +transparent + lda _OP_CACHE2 + sta: $0002,x ; AND $80,x + eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand + sta: $0004,x ; ORA $00,x + + lda #$0F80 ; branch to the epilogue (BRA *+17) + sta: $0006,x +next + eom + ; Helper functions to move tile data into the dynamic tile space ; Helper functions to copy tile data to the appropriate location in Bank 0 diff --git a/src/render/Slow.s b/src/render/Slow.s index 8cb23d7..ff833f1 100644 --- a/src/render/Slow.s +++ b/src/render/Slow.s @@ -79,7 +79,7 @@ FillPEAOpcode rep #$20 rts -; This is a dtub; will be removed eventually +; This is a stub; will be removed eventually _FillPEAOpcode jsr FillPEAOpcode plb ; Restore the TileStore bank diff --git a/src/render/Sprite1.s b/src/render/Sprite1.s index 31f6321..e976865 100644 --- a/src/render/Sprite1.s +++ b/src/render/Sprite1.s @@ -28,7 +28,7 @@ _OneSpriteFastOver0 plb ; Restore the TileStore bank rts -; Next implementation; drawing a sprite onto a regular tile. The 1-sprite dispatch preerves the +; Next implementation; drawing a sprite onto a regular tile. The 1-sprite dispatch preserves the ; X-register, so it already points to the TileStore OneSpriteFastOverV @@ -158,55 +158,20 @@ OneSpriteSlowUnderV OneSpriteDynamicUnder txy tax -]line equ 0 - lup 8 - ldal spritedata+{]line*SPRITE_PLANE_SPAN},x - sta tmp_sprite_data+{]line*4} - ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_sprite_data+{]line*4}+2 -]line equ ]line+1 - --^ + jsr _CopySpriteDataToDP tyx jmp DynamicUnder OneSpriteDynamicOver txy tax -]line equ 0 - lup 8 - ldal spritedata+{]line*SPRITE_PLANE_SPAN},x - sta tmp_sprite_data+{]line*4} - ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_sprite_data+{]line*4}+2 - - ldal spritemask+{]line*SPRITE_PLANE_SPAN},x - sta tmp_sprite_mask+{]line*4} - ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_sprite_mask+{]line*4}+2 -]line equ ]line+1 - --^ + jsr _CopySpriteDataAndMaskToDP tyx jmp DynamicOver ;------------------------------- ; Two Layer tiles with one sprite. Just copy the data and go through the generic sprite path -_CopySpriteDataAndMaskToDP -]line equ 0 - lup 8 - ldal spritedata+{]line*SPRITE_PLANE_SPAN},x - sta tmp_sprite_data+{]line*4} - ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_sprite_data+{]line*4}+2 - - ldal spritemask+{]line*SPRITE_PLANE_SPAN},x - sta tmp_sprite_mask+{]line*4} - ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x - sta tmp_sprite_mask+{]line*4}+2 -]line equ ]line+1 - --^ - rts - OneSpriteOver0TwoLyr txy tax @@ -241,3 +206,60 @@ OneSpriteTwoLyrUnderV jsr _CopySpriteDataAndMaskToDP tyx jmp SpriteUnderVTwoLyr + +;----------------------------------------- +; Dynamic two-layer tiles with one sprite. + +OneSpriteDynamicOverTwoLyr + txy + tax + jsr _CopySpriteDataAndMaskToDP + tyx + jmp DynamicOverTwoLyr + +OneSpriteDynamicUnderTwoLyr + txy + tax + jsr _CopySpriteDataAndMaskToDP + tyx + jmp DynamicUnderTwoLyr + +;------------------------------------- +; Generic helpers +_CopySpriteDataToDP +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_data+{]line*4} + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_data+{]line*4}+2 +]line equ ]line+1 + --^ + rts + +_CopySpriteMaskToDP +]line equ 0 + lup 8 + ldal spritemask+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_mask+{]line*4} + ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_mask+{]line*4}+2 +]line equ ]line+1 + --^ + rts + +_CopySpriteDataAndMaskToDP +]line equ 0 + lup 8 + ldal spritedata+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_data+{]line*4} + ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_data+{]line*4}+2 + + ldal spritemask+{]line*SPRITE_PLANE_SPAN},x + sta tmp_sprite_mask+{]line*4} + ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x + sta tmp_sprite_mask+{]line*4}+2 +]line equ ]line+1 + --^ + rts