mirror of
https://github.com/lscharen/iigs-game-engine.git
synced 2024-11-25 00:30:48 +00:00
Initial shell of generalize sprite size support; just for marking the dirty tiles
This commit is contained in:
parent
13393965b3
commit
678c9a0563
@ -62,7 +62,7 @@ DOWN_ARROW equ $0A
|
||||
; Add a sprite to the engine and save it's sprite ID
|
||||
|
||||
jsr UpdatePlayerLocal
|
||||
lda #64 ; 8x8 sprite, tile ID = 64
|
||||
lda #$1800+145 ; 16x16 sprite, tile ID = 64
|
||||
ldx PlayerX
|
||||
ldy PlayerY
|
||||
jsl AddSprite
|
||||
@ -322,7 +322,7 @@ UpdatePlayerPos
|
||||
ldx PlayerX
|
||||
lda PlayerY
|
||||
clc
|
||||
adc #8
|
||||
adc #16
|
||||
tay
|
||||
jsr GetTileAt
|
||||
cmp #EMPTY_TILE
|
||||
|
@ -119,6 +119,35 @@ min mac
|
||||
lda ]1
|
||||
mout <<<
|
||||
|
||||
; Increment a value mod some number.
|
||||
incmod mac
|
||||
inc
|
||||
cmp ]1
|
||||
bcc out
|
||||
lda #0
|
||||
out <<<
|
||||
|
||||
decmod mac
|
||||
dec
|
||||
bpl out
|
||||
lda ]1
|
||||
dec
|
||||
out <<<
|
||||
|
||||
adcmod mac
|
||||
adc ]1
|
||||
cmp ]2
|
||||
bcc out
|
||||
sbc ]2
|
||||
out <<<
|
||||
|
||||
sbcmod mac
|
||||
sbc ]1
|
||||
bpl out
|
||||
clc
|
||||
adc ]2
|
||||
out <<<
|
||||
|
||||
asr16 mac
|
||||
cmp #$8000
|
||||
ror
|
||||
|
@ -8,7 +8,7 @@
|
||||
use .\Defs.s
|
||||
|
||||
; Feature flags
|
||||
NO_INTERRUPTS equ 0 ; turn off for crossrunner debugging
|
||||
NO_INTERRUPTS equ 1 ; turn off for crossrunner debugging
|
||||
NO_MUSIC equ 1 ; turn music + tool loading off
|
||||
|
||||
; External data provided by the main program segment
|
||||
@ -380,6 +380,7 @@ ReadControl ENT
|
||||
put Memory.s
|
||||
put Graphics.s
|
||||
put Sprite.s
|
||||
put Sprite2.s
|
||||
put Render.s
|
||||
put Timer.s
|
||||
put Script.s
|
||||
|
@ -78,7 +78,9 @@ _Render
|
||||
jsr _ApplyBG0XPosPre
|
||||
jsr _ApplyBG1XPosPre
|
||||
|
||||
nop
|
||||
jsr _RenderSprites ; Once the BG0 X and Y positions are committed, update sprite data
|
||||
nop
|
||||
|
||||
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
|
||||
|
200
src/Sprite.s
200
src/Sprite.s
@ -40,8 +40,8 @@ forceSpriteFlag ds 2
|
||||
_RenderSprites
|
||||
|
||||
; First step is to look at the StartX and StartY values. If the offsets have changed from the
|
||||
; last time that the frame was rederer, then we need to mark all of the sprites as dirty so that
|
||||
; the tiles that they were located at on the previous frame will be refreshed
|
||||
; last time that the frame was redered, then we need to mark all of the sprites as dirty so that
|
||||
; the tiles on which they were located at the previous frame will be refreshed
|
||||
|
||||
stz forceSpriteFlag
|
||||
lda StartX
|
||||
@ -65,7 +65,7 @@ _RenderSprites
|
||||
:loop lda _Sprites+SPRITE_STATUS,x ; If the status is zero, that's the sentinel value
|
||||
beq :out
|
||||
ora forceSpriteFlag
|
||||
bit #SPRITE_STATUS_DIRTY ; If the dirty flag is set, do the things....
|
||||
ora #SPRITE_STATUS_DIRTY ; If the dirty flag is set, do the things....
|
||||
bne :render
|
||||
:next inx
|
||||
inx
|
||||
@ -73,18 +73,17 @@ _RenderSprites
|
||||
:out rts
|
||||
|
||||
; This is the complicated part; we need to draw the sprite into the sprite plane, but then
|
||||
; calculate the code field tiles that this sprite potentially overlaps with and mark those
|
||||
; tiles as dirty and store the appropriate sprite plane address that those tiles need to copy
|
||||
; from.
|
||||
; calculate the tiles that overlap with the sprite potentially and mark those as dirty _AND_
|
||||
; store the appropriate sprite plane address from which those tiles need to copy.
|
||||
:render
|
||||
stx tmp0 ; stash the X register
|
||||
txy ; switch to the Y register
|
||||
|
||||
; Run through the list of tile store offsets that this sprite was last drawn into and mark
|
||||
; those tiles as dirty. The most tiles that a sprite could possibly cover is 20 (a 4x3 sprite)
|
||||
; that is offset, covering a 5x4 area of play field tiles.
|
||||
; those tiles as dirty. The largest number tiles that a sprite could possibly cover is 20
|
||||
; (an unaligned 4x3 sprite), covering a 5x4 area of play field tiles.
|
||||
;
|
||||
; For now, we limit ourselved to 4 tiles until things are working....
|
||||
; For now, we limit ourselves to 4 tiles until things are working....
|
||||
|
||||
lda _Sprites+TILE_STORE_ADDR_1,y
|
||||
beq :erase_done
|
||||
@ -98,14 +97,29 @@ _RenderSprites
|
||||
lda _Sprites+TILE_STORE_ADDR_4,y
|
||||
beq :erase_done
|
||||
jsr _PushDirtyTile
|
||||
lda _Sprites+TILE_STORE_ADDR_5,y
|
||||
beq :erase_done
|
||||
jsr _PushDirtyTile
|
||||
lda _Sprites+TILE_STORE_ADDR_6,y
|
||||
beq :erase_done
|
||||
jsr _PushDirtyTile
|
||||
lda _Sprites+TILE_STORE_ADDR_7,y
|
||||
beq :erase_done
|
||||
jsr _PushDirtyTile
|
||||
lda _Sprites+TILE_STORE_ADDR_8,y
|
||||
beq :erase_done
|
||||
jsr _PushDirtyTile
|
||||
lda _Sprites+TILE_STORE_ADDR_9,y
|
||||
beq :erase_done
|
||||
jsr _PushDirtyTile
|
||||
:erase_done
|
||||
|
||||
; Really, we should only be erasing and redrawing a sprite if its local coordinateds change. Look into this
|
||||
; Really, we should only be erasing and redrawing a sprite if its local coordinates change. Look into this
|
||||
; as a future optimization. Ideally, all of the sprites will be rendered into the sprite plane in a separate
|
||||
; pass from this function, which is primarily concerned with flagging dirty tiles in the Tile Store.
|
||||
|
||||
ldx _Sprites+OLD_VBUFF_ADDR,y
|
||||
jsr _EraseTileSprite ; erase from the old position
|
||||
; jsr _EraseTileSprite ; erase from the old position
|
||||
|
||||
; Draw the sprite into the sprint plane buffer(s)
|
||||
|
||||
@ -113,26 +127,54 @@ _RenderSprites
|
||||
lda _Sprites+TILE_DATA_OFFSET,y ; and the tile address of the tile
|
||||
tay
|
||||
jsr _DrawTileSprite ; draw the sprite into the sprite plane
|
||||
tya
|
||||
clc
|
||||
adc #128
|
||||
tay
|
||||
txa
|
||||
clc
|
||||
adc #4
|
||||
tax
|
||||
jsr _DrawTileSprite
|
||||
|
||||
tya
|
||||
clc
|
||||
adc #128*31
|
||||
tay
|
||||
txa
|
||||
clc
|
||||
adc #{8*256}-4
|
||||
tax
|
||||
jsr _DrawTileSprite
|
||||
tya
|
||||
clc
|
||||
adc #128
|
||||
tay
|
||||
txa
|
||||
clc
|
||||
adc #4
|
||||
tax
|
||||
jsr _DrawTileSprite
|
||||
|
||||
; Mark the appropriate tiles as dirty and as occupied by a sprite so that the ApplyTiles
|
||||
; subroutine will get the drawn data from the sprite plane into the code field where it
|
||||
; can be drawn to the screen
|
||||
|
||||
ldx tmp0 ; Restore the index into the sprite array
|
||||
jsr _MarkDirtySprite8x8 ; Eventually will have routines for all sprite sizes
|
||||
jsr _MarkDirtySprite ; Mark the tiles that this sprite overlaps as dirty
|
||||
|
||||
ldx tmp0 ; Restore the index again
|
||||
bra :next
|
||||
brl :next
|
||||
|
||||
; Marks a 8x8 square as dirty. The work here is mapping from local screen coordinates to the
|
||||
; Marks an 8x8 square 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 SpritX is always the MAXIMUM value of the corner coordinates. We subtract (SpriteX + StartX) mod 4
|
||||
; to find the coordinate in the sprite plane that match up with the tile in the play field and
|
||||
; then use that to calculate the VBUFF address to copy sprite data from.
|
||||
; The SpriteX is always the MAXIMUM value of the corner coordinates. We subtract (SpriteX + StartX) mod 4
|
||||
; to find the coordinate in the sprite plane 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 (SprietX - z)
|
||||
; StartX SpriteX z = * mod 4 (SpriteX - z)
|
||||
; ----------------------------------------------
|
||||
; 0 8 0 8
|
||||
; 1 8 1 7
|
||||
@ -150,10 +192,10 @@ _RenderSprites
|
||||
; On input, X register = Sprite Array Index
|
||||
_MarkDirtySprite8x8
|
||||
|
||||
stz _Sprites+TILE_STORE_ADDR_1,x ; Clear the Dirty Tiles in case of an early exit
|
||||
stz _Sprites+TILE_STORE_ADDR_1,x ; Clear the Dirty Tile list in case of an early exit
|
||||
|
||||
; First, bounds check the X and Y coodinates of the sprite and, if they pass, pre-calculate some
|
||||
; values that we can use later
|
||||
; values that we can use later.
|
||||
|
||||
lda _Sprites+SPRITE_Y,x ; This is a signed value
|
||||
bpl :y_is_pos
|
||||
@ -166,7 +208,7 @@ _MarkDirtySprite8x8
|
||||
:y_is_ok
|
||||
|
||||
; The sprite's Y coordinate is in a range that it will impact the visible tiles that make up the play
|
||||
; field. Figure out what tile(s) they are and what part fo the sprite plane data/mask need to be
|
||||
; field. Figure out what tile(s) they are and what part of the sprite plane data/mask need to be
|
||||
; accessed to overlay with the tile pixels
|
||||
|
||||
clc
|
||||
@ -234,7 +276,7 @@ _MarkDirtySprite8x8
|
||||
; tmp5 = X mod 4
|
||||
; tmp6 = Y mod 8
|
||||
;
|
||||
; Look at these values to determine, up front, exactly which tiles will need to be put into the
|
||||
; Look at these values to determine, up front, exactly which bounding tiles will need to be put into the
|
||||
; dirty tile queue.
|
||||
;
|
||||
; tmp5 tmp6
|
||||
@ -264,6 +306,36 @@ _MarkDirtySprite8x8
|
||||
; column and row in the tile store (tmp2, tmp4). The next step is to add these tile locations to
|
||||
; the dirty queue and set the sprite flag along with the VBUFF location. We try to incrementally
|
||||
; calculate new values to avoid re-doing work.
|
||||
;
|
||||
; The sprite plane address calculation is x + 256 * y and there are no wrap-around considerations,
|
||||
; so we can take the calculated VBUFF address and just add a single, pre-calculate constant for each
|
||||
; tile
|
||||
;
|
||||
; The tile store addresses are more involved, because we could wrap around in the X or Y direction
|
||||
; at any step, so they need to be tracked separately. However, they can be decomposed so that we
|
||||
; can update each independently. If the values are pre-multiplied by 2, then calculating the
|
||||
; Tile Store for X and Y is just
|
||||
;
|
||||
; txa
|
||||
; adc TileStoreYTable,y
|
||||
;
|
||||
; One other consideration is that the visibility tests for the sprite coverage vs the tile store
|
||||
; coverage are different. We get into the main loop is *any* part of the sprite is potentially
|
||||
; visible in the play field. However, for multi-tile sprites, some of the sub-tiles that
|
||||
; comprise the sprite could be totally off-screen.
|
||||
;
|
||||
; To handle this, we pre-filter the tile list while calculating the sprite plane and tile store
|
||||
; addresses to remove any tiles that are off-screen. This provides a natural break in the subroutine
|
||||
; where the actually updating values in the TileStore and _Sprites tables and marking tiles as
|
||||
; dirty involves walking a single list.
|
||||
;
|
||||
; A final note. Although this seems like a lot of code, rendering each tile requires, at a minimum,
|
||||
; 16 LDA/STA pairs plus the overhead of the dirty tile list (~50 cycles), and possible much more.
|
||||
; It's safe to assume that each tile no drawn saves around 500 cycles per frame.
|
||||
;
|
||||
; The worst-case for sprites is 16 sprites, all the maximum size of 4x3 and all unaligned, so
|
||||
; 16 * 5 * 4 = 320 tiles total. There are, at most, 1066 tiles visible on a full-screen. This
|
||||
; would effectively halve the framerate.
|
||||
:mark1x1
|
||||
sta _Sprites+TILE_STORE_ADDR_2,y ; Terminate the list after one item
|
||||
|
||||
@ -273,7 +345,7 @@ _MarkDirtySprite8x8
|
||||
|
||||
:mark1x2
|
||||
sta _Sprites+TILE_STORE_ADDR_3,y ; Terminate the list after two items
|
||||
jsr :calc_col1 ; Calculate the values for the next column
|
||||
; jsr :calc_col1 ; Calculate the values for the next column
|
||||
|
||||
jsr :top_left
|
||||
sta _Sprites+TILE_STORE_ADDR_1,y
|
||||
@ -285,7 +357,7 @@ _MarkDirtySprite8x8
|
||||
|
||||
:mark2x1
|
||||
sta _Sprites+TILE_STORE_ADDR_3,y ; Terminate the list after two items
|
||||
jsr :calc_row1 ; Calculate the values for the next row
|
||||
; jsr :calc_row1 ; Calculate the values for the next row
|
||||
|
||||
jsr :top_left
|
||||
sta _Sprites+TILE_STORE_ADDR_1,y
|
||||
@ -295,10 +367,11 @@ _MarkDirtySprite8x8
|
||||
sta _Sprites+TILE_STORE_ADDR_2,y
|
||||
jmp _PushDirtyTile
|
||||
|
||||
; This is the maximum value, so no need to terminate the list early
|
||||
:mark2x2
|
||||
jsr :calc_col1 ; Calculate the next row and column values
|
||||
jsr :calc_row1
|
||||
sta _Sprites+TILE_STORE_ADDR_3,y ; Terminate the list after four items
|
||||
|
||||
; jsr :calc_col1 ; Calculate the next row and column values
|
||||
; jsr :calc_row1
|
||||
|
||||
jsr :top_left
|
||||
sta _Sprites+TILE_STORE_ADDR_1,y
|
||||
@ -316,34 +389,6 @@ _MarkDirtySprite8x8
|
||||
sta _Sprites+TILE_STORE_ADDR_4,y
|
||||
jmp _PushDirtyTile
|
||||
|
||||
; Functions to advance to the right, or down and cache the values in the direct page
|
||||
; temporary space for re-use. col0 and row0 is the original tile
|
||||
:calc_col1
|
||||
lda tmp3
|
||||
clc
|
||||
adc #4
|
||||
sta tmp7
|
||||
lda tmp4
|
||||
inc
|
||||
cmp #41
|
||||
bcc *+5
|
||||
lda #0
|
||||
sta tmp8
|
||||
rts
|
||||
|
||||
:calc_row1
|
||||
lda tmp1
|
||||
clc
|
||||
adc #8
|
||||
sta tmp9
|
||||
lda tmp2
|
||||
inc
|
||||
cmp #26
|
||||
bcc *+5
|
||||
lda #0
|
||||
sta tmp10
|
||||
rts
|
||||
|
||||
:top_left
|
||||
_TileStoreOffsetX tmp4;tmp2 ; Overwrites X
|
||||
tax
|
||||
@ -551,17 +596,14 @@ _EraseTileSprite
|
||||
;
|
||||
; Bit 9 : Horizontal flip.
|
||||
; Bit 10 : Vertical flip.
|
||||
; Bits 11 - 13 : Sprite Size Selector
|
||||
; 000 - 8x8 (1x1 tile)
|
||||
; 001 - 8x16 (1x2 tiles)
|
||||
; 010 - 16x8 (2x1 tiles)
|
||||
; 011 - 16x16 (2x2 tiles)
|
||||
; 100 - 24x16 (3x2 tiles)
|
||||
; 101 - 16x24 (2x3 tiles)
|
||||
; 110 - 24x24 (3x3 tiles)
|
||||
; 111 - 32x24 (4x3 tiles)
|
||||
; Bit 14 : Low Sprite priority. Draws behind high priority tiles.
|
||||
; Bit 15 : Reserved. Must be zero.
|
||||
; Bits 11 - 12 : Sprite Size Selector
|
||||
; 00 - 8x8 (1x1 tile)
|
||||
; 01 - 8x16 (1x2 tiles)
|
||||
; 10 - 16x8 (2x1 tiles)
|
||||
; 11 - 16x16 (2x2 tiles)
|
||||
; Bit 13 : Reserved. Must be zero.
|
||||
; Bit 14 : Reserved. Must be zero.
|
||||
; Bit 15 : Low Sprite priority. Draws behind high priority tiles.
|
||||
;
|
||||
; When a sprite has a size > 8x8, the horizontal tiles are taken from the next tile index and
|
||||
; the vertical tiles are taken from tileId + 32. This is why tile sheets should be saved
|
||||
@ -601,6 +643,7 @@ _AddSprite
|
||||
:open lda #SPRITE_STATUS_DIRTY
|
||||
sta _Sprites+SPRITE_STATUS,x ; Mark this sprite slot as occupied and that it needs to be drawn
|
||||
pla
|
||||
sta _Sprites+SPRITE_ID,x ; Keep a copy of the full descriptor
|
||||
jsr _GetTileAddr ; This applies the TILE_ID_MASK
|
||||
sta _Sprites+TILE_DATA_OFFSET,x
|
||||
|
||||
@ -698,21 +741,28 @@ _UpdateSprite
|
||||
NUM_BUFF_LINES equ 24
|
||||
|
||||
MAX_SPRITES equ 16
|
||||
SPRITE_REC_SIZE equ 20
|
||||
SPRITE_REC_SIZE equ 34
|
||||
|
||||
SPRITE_STATUS_EMPTY equ 0
|
||||
SPRITE_STATUS_CLEAN equ 1
|
||||
SPRITE_STATUS_DIRTY equ 2
|
||||
|
||||
SPRITE_STATUS equ 0
|
||||
SPRITE_STATUS equ {MAX_SPRITES*0}
|
||||
TILE_DATA_OFFSET equ {MAX_SPRITES*2}
|
||||
VBUFF_ADDR equ {MAX_SPRITES*4}
|
||||
SPRITE_X equ {MAX_SPRITES*6}
|
||||
SPRITE_Y equ {MAX_SPRITES*8}
|
||||
OLD_VBUFF_ADDR 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}
|
||||
SPRITE_ID equ {MAX_SPRITES*6}
|
||||
SPRITE_X equ {MAX_SPRITES*8}
|
||||
SPRITE_Y equ {MAX_SPRITES*10}
|
||||
OLD_VBUFF_ADDR equ {MAX_SPRITES*12}
|
||||
TILE_STORE_ADDR_1 equ {MAX_SPRITES*14}
|
||||
TILE_STORE_ADDR_2 equ {MAX_SPRITES*16}
|
||||
TILE_STORE_ADDR_3 equ {MAX_SPRITES*18}
|
||||
TILE_STORE_ADDR_4 equ {MAX_SPRITES*20}
|
||||
TILE_STORE_ADDR_5 equ {MAX_SPRITES*22}
|
||||
TILE_STORE_ADDR_6 equ {MAX_SPRITES*24}
|
||||
TILE_STORE_ADDR_7 equ {MAX_SPRITES*26}
|
||||
TILE_STORE_ADDR_8 equ {MAX_SPRITES*28}
|
||||
TILE_STORE_ADDR_9 equ {MAX_SPRITES*30}
|
||||
TILE_STORE_ADDR_10 equ {MAX_SPRITES*32}
|
||||
|
||||
_Sprites ds SPRITE_REC_SIZE*MAX_SPRITES
|
||||
|
458
src/Sprite2.s
Normal file
458
src/Sprite2.s
Normal file
@ -0,0 +1,458 @@
|
||||
; 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
|
||||
|
||||
TileTop equ tmp5
|
||||
RowTop equ tmp6
|
||||
AreaIndex equ tmp7
|
||||
|
||||
TileLeft equ tmp8
|
||||
ColLeft equ tmp9
|
||||
|
||||
SpriteBit equ tmp10 ; set the bit of the value that if the current sprite index
|
||||
VBuffOrigin equ tmp11
|
||||
|
||||
mdsOut rts
|
||||
_MarkDirtySprite
|
||||
|
||||
stz _Sprites+TILE_STORE_ADDR_1,x ; Clear the this sprite's dirty tile list in case of an early exit
|
||||
lda _SpriteBits,x
|
||||
sta SpriteBit
|
||||
|
||||
; Clip the sprite's extent to the screen so we can assume (mostly) position values from here on out. Note that
|
||||
; the sprite width and height are _only_ used in the clip and afterward all calculation use the clip rect
|
||||
;
|
||||
; OPTIMIZATION NODE: These values can be calculated in AddSprite/UpdateSprite once and stored in the sprite
|
||||
; record since the screen size doesn't change.
|
||||
|
||||
lda _Sprites+SPRITE_ID,x ; Get an index into the height/width tables based on the sprite bits
|
||||
and #$1800
|
||||
xba
|
||||
lsr
|
||||
lsr
|
||||
tay
|
||||
|
||||
lda _Sprites+SPRITE_X,x
|
||||
bpl :pos_x
|
||||
lda #0
|
||||
:pos_x cmp ScreenWidth
|
||||
bcs mdsOut ; sprite is off-screen, exit early
|
||||
sta Left
|
||||
|
||||
lda _Sprites+SPRITE_Y,x
|
||||
bpl :pos_y
|
||||
lda #0
|
||||
:pos_y cmp ScreenHeight
|
||||
bcs mdsOut ; sprite is off-screen, exit early
|
||||
sta Top
|
||||
|
||||
lda _Sprites+SPRITE_X,x
|
||||
clc
|
||||
adc _SpriteWidthMinus1,y
|
||||
bmi mdsOut ; another off-screen test
|
||||
cmp ScreenWidth
|
||||
bcc :ok_x
|
||||
lda ScreenWidth
|
||||
dec
|
||||
:ok_x sta Right
|
||||
|
||||
lda _Sprites+SPRITE_Y,x
|
||||
clc
|
||||
adc _SpriteHeightMinus1,y
|
||||
bmi mdsOut ; another off-screen test
|
||||
cmp ScreenHeight
|
||||
bcc :ok_y
|
||||
lda ScreenHeight
|
||||
dec
|
||||
:ok_y sta Bottom
|
||||
|
||||
; At this point we know that we have to update the tile that overlap the sprite plane rectangle defined
|
||||
; by (Top, Left), (Bottom, Right). The general process is to figure out the top-left coordinate in the
|
||||
; sprite plane that matches up with the code field and then calculate the number of tiles in each direction
|
||||
; that need to be dirtied to cover the sprite.
|
||||
|
||||
clc
|
||||
lda Top
|
||||
adc StartYMod208 ; Adjust for the scroll offset (could be a negative number!)
|
||||
tay ; Save this value
|
||||
and #$0007 ; Get (StartY + SpriteY) mod 8
|
||||
eor #$FFFF
|
||||
inc
|
||||
clc
|
||||
adc Top ; subtract from the Y position (possible to go negative here)
|
||||
sta TileTop ; This position will line up with the tile that the sprite overlaps with
|
||||
|
||||
tya ; Get back the position of the sprite top in the code field
|
||||
cmp #208 ; check if we went too far positive
|
||||
bcc *+5
|
||||
sbc #208
|
||||
lsr
|
||||
lsr
|
||||
; lsr ; This is the row in the Tile Store for top-left corner of the sprite
|
||||
and #$FFFE ; Store the pre-multiplied by 2 for indexing in the :mark_R_C routines
|
||||
sta RowTop
|
||||
|
||||
lda Bottom ; Figure out how many tiles are needed to cover the sprite's area
|
||||
sec
|
||||
sbc TileTop
|
||||
and #$0018 ; Clear out the lower bits and stash in bits 4 and 5
|
||||
sta AreaIndex
|
||||
|
||||
; Repeat to get the same information for the columns
|
||||
|
||||
clc
|
||||
lda Left
|
||||
adc StartXMod164
|
||||
tay
|
||||
and #$0003
|
||||
eor #$FFFF
|
||||
inc
|
||||
clc
|
||||
adc Left
|
||||
sta TileLeft
|
||||
|
||||
tya
|
||||
cmp #164
|
||||
bcc *+5
|
||||
sbc #164
|
||||
lsr
|
||||
; lsr
|
||||
and #$FFFE ; Same pre-multiply by 2 for later
|
||||
sta ColLeft
|
||||
|
||||
; Sneak a pre-calculation here. Calculate the upper-left corder of the sprite in the sprite plane.
|
||||
; We can reuse this in all of the routines below
|
||||
|
||||
clc
|
||||
lda TileTop
|
||||
adc #NUM_BUFF_LINES
|
||||
xba
|
||||
clc
|
||||
adc TileLeft
|
||||
sta VBuffOrigin ; Save once to use later (constant offsets)
|
||||
|
||||
; Calculate the number of columns and dispatch
|
||||
|
||||
txy ; Swap the sprite index into the Y register
|
||||
|
||||
lda Right
|
||||
sec
|
||||
sbc TileLeft
|
||||
and #$000C
|
||||
lsr ; bit 0 is always zero and width stored in bits 1 and 2
|
||||
ora AreaIndex
|
||||
tax
|
||||
jmp (:mark,x)
|
||||
:mark dw :mark1x1,:mark1x2,:mark1x3,mdsOut
|
||||
dw :mark2x1,:mark2x2,:mark2x3,mdsOut
|
||||
dw :mark3x1,:mark3x2,:mark3x3,mdsOut
|
||||
dw mdsOut,mdsOut,mdsOut,mdsOut
|
||||
|
||||
; Dispatch to the calculated sizing
|
||||
|
||||
; Begin a list of subroutines to cover all of the valid sprite size compinations. This is all unrolled code,
|
||||
; maily 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 up to save a lot of time here.
|
||||
;
|
||||
; These functional are a trade off of being composable versus fast. Having to pay for multiple JSR/RTS invoations
|
||||
; 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
|
||||
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
|
||||
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
|
||||
rts
|
||||
|
||||
:mark1x2
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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 ; This is the tile store offset
|
||||
|
||||
lda VBuffOrigin
|
||||
; adc #{0*4}+{0*256}
|
||||
sta TileStore+TS_SPRITE_ADDR,x
|
||||
|
||||
lda SpriteBit
|
||||
ora TileStore+TS_SPRITE_FLAG,x
|
||||
sta 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
|
||||
|
||||
lda VBuffOrigin
|
||||
adc #{0*4}+{1*8*256}
|
||||
sta TileStore+TS_SPRITE_ADDR,x
|
||||
|
||||
lda SpriteBit
|
||||
ora TileStore+TS_SPRITE_FLAG,x
|
||||
sta TileStore+TS_SPRITE_FLAG,x
|
||||
|
||||
jmp _PushDirtyTileX
|
||||
|
||||
:mark_2_0
|
||||
lda ColLeft
|
||||
ldx RowTop
|
||||
clc
|
||||
adc TileStoreYTable+4,x
|
||||
tax
|
||||
|
||||
lda VBuffOrigin
|
||||
adc #{0*4}+{2*8*256}
|
||||
sta TileStore+TS_SPRITE_ADDR,x
|
||||
|
||||
lda SpriteBit
|
||||
ora TileStore+TS_SPRITE_FLAG,x
|
||||
sta TileStore+TS_SPRITE_FLAG,x
|
||||
|
||||
jmp _PushDirtyTileX
|
||||
|
||||
:mark_0_1
|
||||
ldx ColLeft
|
||||
lda NextCol+2,x
|
||||
ldx RowTop
|
||||
clc
|
||||
adc TileStoreYTable,x
|
||||
tax
|
||||
|
||||
lda VBuffOrigin
|
||||
adc #{1*4}+{0*8*256}
|
||||
sta TileStore+TS_SPRITE_ADDR,x
|
||||
|
||||
lda SpriteBit
|
||||
ora TileStore+TS_SPRITE_FLAG,x
|
||||
sta TileStore+TS_SPRITE_FLAG,x
|
||||
|
||||
jmp _PushDirtyTileX
|
||||
|
||||
:mark_1_1
|
||||
ldx ColLeft
|
||||
lda NextCol+2,x
|
||||
ldx RowTop
|
||||
clc
|
||||
adc TileStoreYTable+2,x
|
||||
tax
|
||||
|
||||
lda VBuffOrigin
|
||||
adc #{1*4}+{1*8*256}
|
||||
sta TileStore+TS_SPRITE_ADDR,x
|
||||
|
||||
lda SpriteBit
|
||||
ora TileStore+TS_SPRITE_FLAG,x
|
||||
sta TileStore+TS_SPRITE_FLAG,x
|
||||
|
||||
jmp _PushDirtyTileX
|
||||
|
||||
:mark_2_1
|
||||
ldx ColLeft
|
||||
lda NextCol+2,x
|
||||
ldx RowTop
|
||||
clc
|
||||
adc TileStoreYTable+4,x
|
||||
tax
|
||||
|
||||
lda VBuffOrigin
|
||||
adc #{1*4}+{2*8*256}
|
||||
sta TileStore+TS_SPRITE_ADDR,x
|
||||
|
||||
lda SpriteBit
|
||||
ora TileStore+TS_SPRITE_FLAG,x
|
||||
sta TileStore+TS_SPRITE_FLAG,x
|
||||
|
||||
jmp _PushDirtyTileX
|
||||
|
||||
:mark_0_2
|
||||
ldx ColLeft
|
||||
lda NextCol+4,x
|
||||
ldx RowTop
|
||||
clc
|
||||
adc TileStoreYTable,x
|
||||
tax
|
||||
|
||||
lda VBuffOrigin
|
||||
adc #{2*4}+{0*8*256}
|
||||
sta TileStore+TS_SPRITE_ADDR,x
|
||||
|
||||
lda SpriteBit
|
||||
ora TileStore+TS_SPRITE_FLAG,x
|
||||
sta TileStore+TS_SPRITE_FLAG,x
|
||||
|
||||
jmp _PushDirtyTileX
|
||||
|
||||
:mark_1_2
|
||||
ldx ColLeft
|
||||
lda NextCol+4,x
|
||||
ldx RowTop
|
||||
clc
|
||||
adc TileStoreYTable+2,x
|
||||
tax
|
||||
|
||||
lda VBuffOrigin
|
||||
adc #{2*4}+{1*8*256}
|
||||
sta TileStore+TS_SPRITE_ADDR,x
|
||||
|
||||
lda SpriteBit
|
||||
ora TileStore+TS_SPRITE_FLAG,x
|
||||
sta TileStore+TS_SPRITE_FLAG,x
|
||||
|
||||
jmp _PushDirtyTileX
|
||||
|
||||
:mark_2_2
|
||||
ldx ColLeft
|
||||
lda NextCol+4,x
|
||||
ldx RowTop
|
||||
clc
|
||||
adc TileStoreYTable+4,x
|
||||
tax
|
||||
|
||||
lda VBuffOrigin
|
||||
adc #{2*4}+{2*8*256}
|
||||
sta TileStore+TS_SPRITE_ADDR,x
|
||||
|
||||
lda SpriteBit
|
||||
ora TileStore+TS_SPRITE_FLAG,x
|
||||
sta 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
|
@ -224,12 +224,33 @@ ScreenAddr ENT
|
||||
; Table of offsets into each row of a Tile Store table. We currently have two tables defined; one
|
||||
; that is the backing store for the tiles rendered into the code field, and another that holds
|
||||
; backlink information on the sprite entries that overlap various tiles.
|
||||
]step equ 0
|
||||
;
|
||||
; This table is double-length to support accessing off the end modulo its legth
|
||||
TileStoreYTable ENT
|
||||
]step equ 0
|
||||
lup 26
|
||||
dw ]step
|
||||
]step = ]step+{41*2}
|
||||
--^
|
||||
]step equ 0
|
||||
lup 26
|
||||
dw ]step
|
||||
]step = ]step+{41*2}
|
||||
--^
|
||||
|
||||
; Create a table to look up the "next" column with modulo wraparound. Basically a[i] = i
|
||||
; and the table is double-length. Use contanct offsets to pick an amount to advance
|
||||
NextCol
|
||||
]step equ 0
|
||||
lup 41
|
||||
dw ]step
|
||||
]step = ]step+2
|
||||
--^
|
||||
]step equ 0
|
||||
lup 41
|
||||
dw ]step
|
||||
]step = ]step+2
|
||||
--^
|
||||
|
||||
; This is a double-length table that holds the right-edge adresses of the playfield on the physical
|
||||
; screen. At most, it needs to hold 200 addresses for a full height playfield. It is double-length
|
||||
|
@ -480,16 +480,16 @@ _CopyBG1Tile
|
||||
;
|
||||
; TileStore+TS_TILE_ID : Tile descriptor
|
||||
; TileStore+TS_DIRTY : $FFFF is clean, otherwise stores a back-reference to the DirtyTiles array
|
||||
; TileStore+TS_SPRITE_FLAG : Set to TILE_SPRITE_BIT is a sprite is present at this tile location
|
||||
; TileStore+TS_SPRITE_FLAG : Set to TILE_SPRITE_BIT if a sprite is present at this tile location
|
||||
; TileStore+TS_SPRITE_ADDR ; Address of the tile in the sprite plane
|
||||
; 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_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
|
||||
; TileStore+TS_WORD_OFFSET : Logical number of word for this location
|
||||
; TileStore+TS_BASE_ADDR : Copy of BTableAddrLow
|
||||
|
||||
TileStore ENT
|
||||
ds TILE_STORE_SIZE*9
|
||||
ds TILE_STORE_SIZE*10
|
||||
|
||||
; A list of dirty tiles that need to be updated in a given frame
|
||||
DirtyTileCount ds 2
|
||||
@ -674,6 +674,9 @@ _PushDirtyTileOld
|
||||
; 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
|
||||
lda TileStore+TS_DIRTY,x
|
||||
bpl :occupied2
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user