mirror of
https://github.com/lscharen/iigs-game-engine.git
synced 2025-02-21 12:29:07 +00:00
340 lines
12 KiB
ArmAsm
340 lines
12 KiB
ArmAsm
; Scratch space to lay out idealized _MakeDirtySprite
|
|
; On input, X register = Sprite Array Index
|
|
|
|
RowTop equ tmp6
|
|
AreaIndex equ tmp7
|
|
SpriteBit equ tmp8 ; set the bit of the value that if the current sprite index
|
|
|
|
; Table of pre-multiplied vbuff strides
|
|
vbuff_mul
|
|
dw 0*VBUFF_STRIDE_BYTES
|
|
dw 1*VBUFF_STRIDE_BYTES
|
|
dw 2*VBUFF_STRIDE_BYTES
|
|
dw 3*VBUFF_STRIDE_BYTES
|
|
dw 4*VBUFF_STRIDE_BYTES
|
|
dw 5*VBUFF_STRIDE_BYTES
|
|
dw 6*VBUFF_STRIDE_BYTES
|
|
dw 7*VBUFF_STRIDE_BYTES
|
|
|
|
; Marks a sprite as dirty. The work here is mapping from local screen coordinates to the
|
|
; tile store indices. The first step is to adjust the sprite coordinates based on the current
|
|
; code field offsets and then cache variations of this value needed in the rest of the subroutine
|
|
;
|
|
; The SpriteX is always the MAXIMUM value of the corner coordinates. We subtract (SpriteX + StartX) mod 4
|
|
; to find the coordinate in the sprite cache that matches up with the tile in the play field and
|
|
; then use that to calculate the VBUFF address from which to copy sprite data.
|
|
;
|
|
; StartX SpriteX z = * mod 4 (SpriteX - z)
|
|
; ----------------------------------------------
|
|
; 0 8 0 8
|
|
; 1 8 1 7
|
|
; 2 8 2 6
|
|
; 3 8 3 5
|
|
; 4 9 1 8
|
|
; 5 9 2 7
|
|
; 6 9 3 6
|
|
; 7 9 0 9
|
|
; 8 10 2 8
|
|
; ...
|
|
;
|
|
; 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 mdsOut2
|
|
|
|
; 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
|
|
pha ; Cache
|
|
and #$FFF8 ; mask first to ensure LSR will clear the carry
|
|
lsr
|
|
lsr
|
|
tax
|
|
lda TileStoreLookupYTable,x
|
|
sta RowTop ; Even numbers from [0, 100] (51 elements)
|
|
|
|
; Get the position of the top edge within the tile and then add it to the sprite's height
|
|
; to calculate the number of tiles that are overlapped. We use the actual width and height
|
|
; 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.
|
|
|
|
pla
|
|
and #$0007
|
|
adc _Sprites+SPRITE_CLIP_HEIGHT,y ; Nominal value between 0 and 16+7 = 23 = 10111
|
|
dec
|
|
and #$0018
|
|
sta AreaIndex
|
|
|
|
; 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
|
|
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
|
|
|
|
|
|
; Calculate the final amount of visible tiles that need to be refreshed and use that to
|
|
; set the coverage size index.
|
|
|
|
pla
|
|
and #$0003
|
|
adc _Sprites+SPRITE_CLIP_WIDTH,y ; max width = 8 = 0x08
|
|
dec
|
|
and #$000C
|
|
lsr ; max value = 4 = 0x04
|
|
ora AreaIndex ; merge into the area index
|
|
sta _Sprites+TS_COVERAGE_SIZE,y ; Save this value as a key to the coverage size of the sprite
|
|
|
|
|
|
; Part 2: Redo some calculation with the actual (signed) sprite positions that take into
|
|
; account negative coordinates to set the VBuff offset values.
|
|
|
|
clc
|
|
lda _Sprites+SPRITE_Y,y
|
|
adc StartYMod208
|
|
bpl :y_ok
|
|
clc
|
|
adc #208 ; Wrap the actual coordinate around
|
|
:y_ok and #$FFF8 ; mask first to ensure LSR will clear the carry
|
|
lsr
|
|
lsr
|
|
tax ; Tile store lookup index
|
|
|
|
lda _Sprites+SPRITE_X,y
|
|
adc StartXMod164
|
|
bpl :x_ok
|
|
clc
|
|
adc #164
|
|
:x_ok and #$FFFC
|
|
lsr ; Even numbers from [0, 160] (81 elements)
|
|
sta tmp3
|
|
adc TileStoreLookupYTable,x
|
|
pha ; will be PLX later
|
|
|
|
clc ; Carry should still be clear here....
|
|
lda StartYMod208
|
|
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
|
|
|
|
_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
|
|
|
|
; 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
|
|
|
|
:mark1x1
|
|
ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
TSSetSprite 0*{TS_LOOKUP_SPAN*2}
|
|
rts
|
|
|
|
:mark1x2
|
|
ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0
|
|
TSSetSprite 0*{TS_LOOKUP_SPAN*2}+2
|
|
rts
|
|
|
|
:mark1x3
|
|
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
|
|
ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
TSSetSprite 0*{TS_LOOKUP_SPAN*2}+0
|
|
TSSetSprite 1*{TS_LOOKUP_SPAN*2}+0
|
|
rts
|
|
|
|
:mark2x2
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|