2022-02-03 14:50:11 +00:00
|
|
|
; Functions for sprite handling. Mostly maintains the sprite list and provides
|
2021-10-21 13:50:07 +00:00
|
|
|
; utility functions to calculate sprite/tile intersections
|
2021-08-25 14:38:02 +00:00
|
|
|
;
|
2022-02-18 18:12:32 +00:00
|
|
|
; Initialize the sprite data and mask banks (all data = $0000, all masks = $FFFF)
|
2021-10-24 03:31:38 +00:00
|
|
|
InitSprites
|
2021-10-21 13:50:07 +00:00
|
|
|
ldx #$FFFE
|
|
|
|
lda #0
|
2021-10-24 03:31:38 +00:00
|
|
|
:loop1 stal spritedata,x
|
2021-10-21 13:50:07 +00:00
|
|
|
dex
|
|
|
|
dex
|
2021-10-29 02:52:48 +00:00
|
|
|
cpx #$FFFE
|
|
|
|
bne :loop1
|
2021-10-21 13:50:07 +00:00
|
|
|
|
|
|
|
ldx #$FFFE
|
|
|
|
lda #$FFFF
|
2021-10-24 03:31:38 +00:00
|
|
|
:loop2 stal spritemask,x
|
2021-10-21 13:50:07 +00:00
|
|
|
dex
|
|
|
|
dex
|
2021-10-29 02:52:48 +00:00
|
|
|
cpx #$FFFE
|
|
|
|
bne :loop2
|
2021-10-21 13:50:07 +00:00
|
|
|
|
2021-11-02 04:36:53 +00:00
|
|
|
; Clear values in the sprite array
|
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
; ldx #{MAX_SPRITES-1}*2
|
|
|
|
;:loop3 stz _Sprites+TILE_STORE_ADDR_1,x
|
|
|
|
; dex
|
|
|
|
; dex
|
|
|
|
; bpl :loop3
|
2021-11-02 04:36:53 +00:00
|
|
|
|
2022-02-02 16:21:31 +00:00
|
|
|
; Precalculate some bank values
|
|
|
|
jsr _CacheSpriteBanks
|
2021-10-21 13:50:07 +00:00
|
|
|
rts
|
|
|
|
|
2022-05-27 00:36:40 +00:00
|
|
|
|
|
|
|
; _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
|
|
|
|
;
|
|
|
|
; 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
|
|
|
|
tmpY equ tmp15
|
|
|
|
tmpA equ tmp14
|
|
|
|
_DoPhase1
|
|
|
|
lda _Sprites+SPRITE_STATUS,y
|
|
|
|
ora ForceSpriteFlag
|
|
|
|
sta tmpA
|
|
|
|
sty tmpY
|
|
|
|
|
|
|
|
; 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
|
|
|
|
|
|
|
|
jmp _ClearSpriteFromTileStore ; Clear the tile flags, add to the dirty tile list and done
|
|
|
|
|
|
|
|
; Need to calculate new VBUFF information. The could be reuqired for UPDATED, ADDED or MOVED
|
|
|
|
; sprites, so we do it unconditionally.
|
|
|
|
:no_clear
|
|
|
|
jsr _CalcDirtySprite
|
|
|
|
|
|
|
|
; If the sprite is marked as ADDED, then it does not need to have its old tile locations cleared
|
|
|
|
lda tmpA
|
|
|
|
bit #SPRITE_STATUS_ADDED
|
|
|
|
bne :no_move
|
|
|
|
jsr _ClearSpriteFromTileStore
|
|
|
|
ldy tmpY
|
|
|
|
|
|
|
|
; Anything else (MOVED, UPDATED, ADDED) will need to have the VBUFF information updated and the
|
|
|
|
; current tiles marked for update
|
|
|
|
:no_move
|
|
|
|
jmp _MarkDirtySpriteTiles
|
|
|
|
|
|
|
|
; Once all of the sprite values have been calculated, we need to scan the dirty tile list and
|
|
|
|
; collapse the sprite information down to no more than 4 vbuff references per tile. We used to
|
|
|
|
; do this on the fly in the renderer, but that required differentiating between tile with and
|
|
|
|
; without sprites in the core rendering function. My lifting this up, we simplify the core code
|
|
|
|
; and possible open up some optimization opportunities.
|
|
|
|
_SetTileStoreVBuffAddrs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
; 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
|
|
|
|
jsr _DoPhase1
|
|
|
|
:phase1_0 jmp _SetTileStoreVBuffAddrs
|
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
; 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.
|
|
|
|
|
|
|
|
|
2022-04-23 05:47:13 +00:00
|
|
|
; Render a sprite stamp into the sprite buffer. Stamps exist independent of the sprites
|
2022-04-20 12:43:16 +00:00
|
|
|
; and sprite reference a specific stamp. This is necessary because it's common for a
|
2022-04-23 05:47:13 +00:00
|
|
|
; sprite to change its graphic as its animating, but it is too costly to have to set up
|
2022-04-20 12:43:16 +00:00
|
|
|
; the stamp every time. So this allows users to create stamps in advance and then
|
|
|
|
; assign them to the sprites as needed.
|
|
|
|
;
|
2022-05-19 02:00:06 +00:00
|
|
|
; 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.
|
2022-04-20 12:43:16 +00:00
|
|
|
;
|
|
|
|
; Input:
|
|
|
|
; A = sprite descriptor
|
2022-05-19 02:00:06 +00:00
|
|
|
; 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.
|
2022-04-20 12:43:16 +00:00
|
|
|
_CreateSpriteStamp
|
|
|
|
pha ; Save the descriptor
|
|
|
|
jsr _GetBaseTileAddr ; Get the address of the tile data
|
|
|
|
|
2022-05-19 02:00:06 +00:00
|
|
|
tax ; Tile data address
|
2022-04-20 12:43:16 +00:00
|
|
|
pla ; Pop the sprite ID
|
2022-05-19 02:00:06 +00:00
|
|
|
jmp _DrawSpriteStamp ; Render the sprite data and create a stamp
|
2022-02-19 02:43:55 +00:00
|
|
|
|
|
|
|
; Add a new sprite to the rendering pipeline
|
|
|
|
;
|
|
|
|
; The tile id in the range 0 - 511. The top 7 bits are used as sprite control bits
|
|
|
|
;
|
|
|
|
; Bit 9 : Horizontal flip.
|
|
|
|
; Bit 10 : Vertical flip.
|
|
|
|
; 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
|
|
|
|
; with a width of 256 pixels.
|
|
|
|
;
|
|
|
|
; A = tileId + flags
|
2022-02-25 23:05:32 +00:00
|
|
|
; Y = High Byte = x-pos, Low Byte = y-pos
|
|
|
|
; X = Sprite Slot (0 - 15)
|
2022-02-19 02:43:55 +00:00
|
|
|
_AddSprite
|
2022-02-25 23:05:32 +00:00
|
|
|
pha
|
|
|
|
txa
|
|
|
|
and #$000F
|
|
|
|
asl
|
|
|
|
tax
|
|
|
|
pla
|
2022-02-19 02:43:55 +00:00
|
|
|
|
|
|
|
sta _Sprites+SPRITE_ID,x ; Keep a copy of the full descriptor
|
|
|
|
|
|
|
|
lda #SPRITE_STATUS_OCCUPIED+SPRITE_STATUS_ADDED
|
|
|
|
sta _Sprites+SPRITE_STATUS,x
|
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
stz _Sprites+VBUFF_ADDR,x ; Clear the VBUFF address, just to initialize it
|
|
|
|
|
2022-02-25 23:05:32 +00:00
|
|
|
phy
|
2022-02-19 02:43:55 +00:00
|
|
|
tya
|
2022-02-25 23:05:32 +00:00
|
|
|
and #$00FF
|
2022-02-19 02:43:55 +00:00
|
|
|
sta _Sprites+SPRITE_Y,x ; Y coordinate
|
2022-02-25 23:05:32 +00:00
|
|
|
pla
|
|
|
|
xba
|
|
|
|
and #$00FF
|
|
|
|
sta _Sprites+SPRITE_X,x ; X coordinate
|
2022-02-19 02:43:55 +00:00
|
|
|
|
|
|
|
jsr _PrecalcAllSpriteInfo ; Cache sprite property values (simple stuff)
|
|
|
|
|
|
|
|
; Mark the dirty bit to indicate that the active sprite list needs to be rebuilt in the next
|
|
|
|
; render call
|
|
|
|
|
|
|
|
lda #DIRTY_BIT_SPRITE_ARRAY
|
|
|
|
tsb DirtyBits
|
|
|
|
|
|
|
|
lda _SpriteBits,x ; Get the bit flag for this sprite slot
|
|
|
|
tsb SpriteMap ; Mark it in the sprite map bit field
|
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
; txa ; And return the sprite ID
|
|
|
|
; clc ; Mark that the sprite was successfully added
|
2022-02-19 02:43:55 +00:00
|
|
|
|
|
|
|
rts
|
|
|
|
|
2022-05-23 20:18:34 +00:00
|
|
|
; Macro to make the unrolled loop more concise
|
|
|
|
;
|
|
|
|
; 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
|
2022-05-27 00:36:40 +00:00
|
|
|
|
|
|
|
tya
|
|
|
|
ldy DirtyTileCount
|
|
|
|
sta DirtyTiles,y
|
|
|
|
iny
|
|
|
|
iny
|
|
|
|
sty DirtyTileCount
|
2022-05-23 20:18:34 +00:00
|
|
|
next
|
|
|
|
<<<
|
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
; 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
|
2022-05-23 20:18:34 +00:00
|
|
|
_ClearSpriteFromTileStore
|
|
|
|
lda _SpriteBitsNot,y ; Cache this value in a direct page location
|
|
|
|
sta tmp0
|
2022-04-20 12:43:16 +00:00
|
|
|
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
|
|
|
|
|
2022-05-27 00:36:40 +00:00
|
|
|
csfts_out rts
|
|
|
|
|
2022-05-23 20:18:34 +00:00
|
|
|
csfts_3x3 ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
|
|
TSClearSprite 0
|
|
|
|
TSClearSprite 2
|
|
|
|
TSClearSprite 4
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}+4
|
|
|
|
TSClearSprite 2*{TS_LOOKUP_SPAN*2}
|
|
|
|
TSClearSprite 2*{TS_LOOKUP_SPAN*2}+2
|
|
|
|
TSClearSprite 2*{TS_LOOKUP_SPAN*2}+4
|
2022-05-27 00:36:40 +00:00
|
|
|
rts
|
2022-05-23 20:18:34 +00:00
|
|
|
|
|
|
|
csfts_3x2 ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
|
|
TSClearSprite 0
|
|
|
|
TSClearSprite 2
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2
|
|
|
|
TSClearSprite 2*{TS_LOOKUP_SPAN*2}
|
|
|
|
TSClearSprite 2*{TS_LOOKUP_SPAN*2}+2
|
2022-05-27 00:36:40 +00:00
|
|
|
rts
|
2022-05-23 20:18:34 +00:00
|
|
|
|
|
|
|
csfts_3x1 ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
|
|
TSClearSprite 0
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}
|
|
|
|
TSClearSprite 2*{TS_LOOKUP_SPAN*2}
|
2022-05-27 00:36:40 +00:00
|
|
|
rts
|
2022-05-23 20:18:34 +00:00
|
|
|
|
|
|
|
csfts_2x3 ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
|
|
TSClearSprite 0
|
|
|
|
TSClearSprite 2
|
|
|
|
TSClearSprite 4
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}+4
|
2022-05-27 00:36:40 +00:00
|
|
|
rts
|
2022-05-23 20:18:34 +00:00
|
|
|
|
|
|
|
csfts_2x2 ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
|
|
TSClearSprite 0
|
|
|
|
TSClearSprite 2
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}+2
|
2022-05-27 00:36:40 +00:00
|
|
|
rts
|
2022-05-23 20:18:34 +00:00
|
|
|
|
|
|
|
csfts_2x1 ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
|
|
TSClearSprite 0
|
|
|
|
TSClearSprite 1*{TS_LOOKUP_SPAN*2}
|
2022-05-27 00:36:40 +00:00
|
|
|
rts
|
2022-05-23 20:18:34 +00:00
|
|
|
|
|
|
|
csfts_1x3 ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
|
|
TSClearSprite 0
|
|
|
|
TSClearSprite 2
|
|
|
|
TSClearSprite 4
|
2022-05-27 00:36:40 +00:00
|
|
|
rts
|
2022-05-23 20:18:34 +00:00
|
|
|
|
|
|
|
csfts_1x2 ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
|
|
TSClearSprite 0
|
|
|
|
TSClearSprite 2
|
2022-05-27 00:36:40 +00:00
|
|
|
rts
|
2022-04-20 12:43:16 +00:00
|
|
|
|
2022-05-23 20:18:34 +00:00
|
|
|
csfts_1x1 ldx _Sprites+TS_LOOKUP_INDEX,y
|
|
|
|
TSClearSprite 0
|
2022-02-07 07:19:31 +00:00
|
|
|
rts
|
2021-11-22 19:26:25 +00:00
|
|
|
|
2022-02-18 18:12:32 +00:00
|
|
|
; Use the blttmp space to build the active sprite list. Since the sprite tiles are not drawn until later,
|
|
|
|
; it's OK to use that scratch space here. And it's just the right size, 32 bytes
|
2022-02-07 07:19:31 +00:00
|
|
|
RebuildSpriteArray
|
|
|
|
lda SpriteMap ; Get the bit field
|
2022-02-18 18:12:32 +00:00
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
; Unrolled loop to get the sprite index values that correspond to the set bit positions
|
2022-02-18 18:12:32 +00:00
|
|
|
|
|
|
|
pea $FFFF ; end-of-list marker
|
|
|
|
]step equ 0
|
|
|
|
lup 4
|
2022-02-21 16:31:35 +00:00
|
|
|
lsr
|
2022-02-18 18:12:32 +00:00
|
|
|
bcc :skip_1
|
|
|
|
pea ]step
|
2022-02-21 16:31:35 +00:00
|
|
|
:skip_1 lsr
|
2022-02-18 18:12:32 +00:00
|
|
|
bcc :skip_2
|
|
|
|
pea ]step+2
|
2022-02-21 16:31:35 +00:00
|
|
|
:skip_2 lsr
|
2022-02-18 18:12:32 +00:00
|
|
|
bcc :skip_3
|
|
|
|
pea ]step+4
|
2022-02-21 16:31:35 +00:00
|
|
|
:skip_3 lsr
|
2022-02-18 18:12:32 +00:00
|
|
|
bcc :skip_4
|
|
|
|
pea ]step+6
|
|
|
|
:skip_4 beq :end_1
|
|
|
|
]step equ ]step+8
|
2022-02-07 07:19:31 +00:00
|
|
|
--^
|
2022-02-18 18:12:32 +00:00
|
|
|
:end_1
|
2021-10-21 13:50:07 +00:00
|
|
|
|
2022-02-18 18:12:32 +00:00
|
|
|
; Now pop the values off of the stack until reaching the sentinel value. This could be unrolled, but
|
|
|
|
; it is only done once per frame.
|
|
|
|
|
|
|
|
ldx #0
|
|
|
|
:loop
|
|
|
|
pla
|
|
|
|
bmi :out
|
2022-02-21 16:31:35 +00:00
|
|
|
sta activeSpriteList,x
|
2022-02-18 18:12:32 +00:00
|
|
|
inx
|
|
|
|
inx
|
|
|
|
bra :loop
|
|
|
|
:out
|
|
|
|
stx ActiveSpriteCount
|
2022-02-07 07:19:31 +00:00
|
|
|
rts
|
|
|
|
|
2021-10-21 13:50:07 +00:00
|
|
|
; _GetTileAt
|
|
|
|
;
|
|
|
|
; Given a relative playfield coordinate [0, ScreenWidth), [0, ScreenHeight) return the
|
|
|
|
; X = horizontal point [0, ScreenTileWidth]
|
|
|
|
; Y = vertical point [0, ScreenTileHeight]
|
|
|
|
;
|
|
|
|
; Return
|
|
|
|
; C = 1, out of range
|
|
|
|
; C = 0, X = column, Y = row
|
|
|
|
_GetTileAt
|
|
|
|
cpx ScreenWidth
|
|
|
|
bcc *+3
|
|
|
|
rts
|
|
|
|
|
|
|
|
cpy ScreenHeight
|
|
|
|
bcc *+3
|
|
|
|
rts
|
|
|
|
|
|
|
|
tya ; carry is clear here
|
|
|
|
adc StartYMod208 ; This is the code field line that is at the top of the screen
|
|
|
|
cmp #208
|
|
|
|
bcc *+5
|
|
|
|
sbc #208
|
|
|
|
|
|
|
|
lsr
|
|
|
|
lsr
|
|
|
|
lsr
|
|
|
|
tay ; This is the code field row for this point
|
|
|
|
|
|
|
|
clc
|
|
|
|
txa
|
|
|
|
adc StartXMod164
|
|
|
|
cmp #164
|
|
|
|
bcc *+5
|
|
|
|
sbc #164
|
|
|
|
|
|
|
|
lsr
|
|
|
|
lsr
|
|
|
|
tax ; Could call _CopyBG0Tile with these arguments
|
|
|
|
|
|
|
|
clc
|
|
|
|
rts
|
|
|
|
|
2022-02-22 08:35:21 +00:00
|
|
|
; Small initialization routine to cache the banks for the sprite data and mask and tile/sprite stuff
|
2022-02-02 16:21:31 +00:00
|
|
|
_CacheSpriteBanks
|
|
|
|
lda #>spritemask
|
|
|
|
and #$FF00
|
|
|
|
ora #^spritedata
|
|
|
|
sta SpriteBanks
|
2022-02-22 08:35:21 +00:00
|
|
|
|
|
|
|
lda #$0100
|
|
|
|
ora #^TileStore
|
|
|
|
sta TileStoreBankAndBank01
|
|
|
|
|
|
|
|
lda #>tiledata
|
|
|
|
and #$FF00
|
|
|
|
ora #^TileStore
|
|
|
|
sta TileStoreBankAndTileDataBank
|
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
lda #>TileStore
|
|
|
|
and #$FF00
|
|
|
|
ora #^TileStore
|
|
|
|
sta TileStoreBankDoubled
|
|
|
|
|
2022-02-02 16:21:31 +00:00
|
|
|
rts
|
|
|
|
|
2022-02-03 14:50:11 +00:00
|
|
|
; A = x coordinate
|
|
|
|
; Y = y coordinate
|
2022-02-18 18:12:32 +00:00
|
|
|
;GetSpriteVBuffAddr ENT
|
|
|
|
; jsr _GetSpriteVBuffAddr
|
|
|
|
; rtl
|
2022-02-03 14:50:11 +00:00
|
|
|
|
|
|
|
; A = x coordinate
|
|
|
|
; Y = y coordinate
|
2022-02-18 18:12:32 +00:00
|
|
|
;_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
|
2022-02-03 14:50:11 +00:00
|
|
|
|
|
|
|
; Version that uses temporary space (tmp15)
|
2022-02-18 18:12:32 +00:00
|
|
|
;_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
|
2022-02-03 14:50:11 +00:00
|
|
|
|
2022-02-04 18:37:05 +00:00
|
|
|
; Precalculate some cached values for a sprite. These are *only* to make other part of code,
|
|
|
|
; specifically the draw/erase routines more efficient.
|
|
|
|
;
|
2022-02-18 18:12:32 +00:00
|
|
|
; There are variations of this routine based on whether we are adding a new sprite, updating
|
2022-02-04 18:37:05 +00:00
|
|
|
; it's tile information, or changing its position.
|
|
|
|
;
|
|
|
|
; X = sprite index
|
2022-04-20 12:43:16 +00:00
|
|
|
_stamp_step dw 0,12,24,36
|
2022-02-04 18:37:05 +00:00
|
|
|
_PrecalcAllSpriteInfo
|
|
|
|
lda _Sprites+SPRITE_ID,x
|
2022-04-20 12:43:16 +00:00
|
|
|
; and #$3E00
|
2022-02-07 07:19:31 +00:00
|
|
|
xba
|
2022-04-20 12:43:16 +00:00
|
|
|
and #$0006
|
|
|
|
|
2022-05-27 00:36:40 +00:00
|
|
|
txy ; swap X/Y for this...
|
|
|
|
tax
|
|
|
|
lda _Sprites+VBUFF_ADDR,y
|
|
|
|
clc
|
|
|
|
adcl _stamp_step,x
|
|
|
|
sta _Sprites+SPRITE_DISP,y
|
|
|
|
tyx
|
2022-02-07 07:19:31 +00:00
|
|
|
|
2022-02-21 19:33:39 +00:00
|
|
|
; Set the sprite's width and height
|
|
|
|
lda #4
|
|
|
|
sta _Sprites+SPRITE_WIDTH,x
|
|
|
|
lda #8
|
|
|
|
sta _Sprites+SPRITE_HEIGHT,x
|
|
|
|
|
|
|
|
lda _Sprites+SPRITE_ID,x
|
|
|
|
bit #$1000 ; width select
|
|
|
|
beq :width_4
|
|
|
|
lda #8
|
|
|
|
sta _Sprites+SPRITE_WIDTH,x
|
|
|
|
:width_4
|
|
|
|
|
|
|
|
lda _Sprites+SPRITE_ID,x
|
|
|
|
bit #$0800 ; width select
|
|
|
|
beq :height_8
|
|
|
|
lda #16
|
|
|
|
sta _Sprites+SPRITE_HEIGHT,x
|
|
|
|
:height_8
|
|
|
|
|
2022-02-07 07:19:31 +00:00
|
|
|
; Clip the sprite's bounding box to the play field size and also set a flag if the sprite
|
2022-02-21 19:33:39 +00:00
|
|
|
; is fully off-screen or not
|
|
|
|
|
2022-02-07 07:19:31 +00:00
|
|
|
lda _Sprites+SPRITE_X,x
|
|
|
|
bpl :pos_x
|
|
|
|
lda #0
|
|
|
|
:pos_x cmp ScreenWidth
|
|
|
|
bcs :offscreen ; sprite is off-screen, exit early
|
|
|
|
sta _Sprites+SPRITE_CLIP_LEFT,x
|
|
|
|
|
|
|
|
lda _Sprites+SPRITE_Y,x
|
|
|
|
bpl :pos_y
|
|
|
|
lda #0
|
|
|
|
:pos_y cmp ScreenHeight
|
|
|
|
bcs :offscreen ; sprite is off-screen, exit early
|
|
|
|
sta _Sprites+SPRITE_CLIP_TOP,x
|
|
|
|
|
|
|
|
lda _Sprites+SPRITE_X,x
|
|
|
|
clc
|
2022-02-21 19:33:39 +00:00
|
|
|
adc _Sprites+SPRITE_WIDTH,x
|
|
|
|
dec
|
2022-02-07 07:19:31 +00:00
|
|
|
bmi :offscreen
|
|
|
|
cmp ScreenWidth
|
|
|
|
bcc :ok_x
|
|
|
|
lda ScreenWidth
|
|
|
|
dec
|
|
|
|
:ok_x sta _Sprites+SPRITE_CLIP_RIGHT,x
|
|
|
|
|
|
|
|
lda _Sprites+SPRITE_Y,x
|
|
|
|
clc
|
2022-02-21 19:33:39 +00:00
|
|
|
adc _Sprites+SPRITE_HEIGHT,x
|
|
|
|
dec
|
2022-02-07 07:19:31 +00:00
|
|
|
bmi :offscreen
|
|
|
|
cmp ScreenHeight
|
|
|
|
bcc :ok_y
|
|
|
|
lda ScreenHeight
|
|
|
|
dec
|
|
|
|
:ok_y sta _Sprites+SPRITE_CLIP_BOTTOM,x
|
|
|
|
|
2022-02-21 19:33:39 +00:00
|
|
|
stz _Sprites+IS_OFF_SCREEN,x ; passed all of the off-screen tests
|
|
|
|
|
|
|
|
; Calculate the clipped width and height
|
|
|
|
lda _Sprites+SPRITE_CLIP_RIGHT,x
|
|
|
|
sec
|
|
|
|
sbc _Sprites+SPRITE_CLIP_LEFT,x
|
|
|
|
inc
|
|
|
|
sta _Sprites+SPRITE_CLIP_WIDTH,x
|
|
|
|
|
|
|
|
lda _Sprites+SPRITE_CLIP_BOTTOM,x
|
|
|
|
sec
|
|
|
|
sbc _Sprites+SPRITE_CLIP_TOP,x
|
|
|
|
inc
|
|
|
|
sta _Sprites+SPRITE_CLIP_HEIGHT,x
|
2022-02-07 07:19:31 +00:00
|
|
|
rts
|
|
|
|
|
|
|
|
:offscreen
|
|
|
|
lda #1
|
|
|
|
sta _Sprites+IS_OFF_SCREEN,x
|
2022-02-04 18:37:05 +00:00
|
|
|
rts
|
|
|
|
|
2022-01-20 02:58:57 +00:00
|
|
|
; Remove a sprite from the list. Just mark its STATUS as FREE and it will be
|
2022-02-03 14:50:11 +00:00
|
|
|
; picked up in the next AddSprite.
|
2022-01-20 02:58:57 +00:00
|
|
|
;
|
|
|
|
; A = Sprite ID
|
|
|
|
_RemoveSprite
|
2022-04-20 12:43:16 +00:00
|
|
|
cmp #MAX_SPRITES
|
|
|
|
bcc :ok
|
|
|
|
rts
|
|
|
|
|
|
|
|
:ok
|
|
|
|
asl
|
2022-02-03 14:50:11 +00:00
|
|
|
tax
|
2022-01-20 02:58:57 +00:00
|
|
|
|
2022-02-07 07:19:31 +00:00
|
|
|
lda _Sprites+SPRITE_STATUS,x
|
|
|
|
ora #SPRITE_STATUS_REMOVED
|
|
|
|
sta _Sprites+SPRITE_STATUS,x
|
2022-04-20 12:43:16 +00:00
|
|
|
|
2022-01-20 02:58:57 +00:00
|
|
|
rts
|
|
|
|
|
2022-02-03 14:50:11 +00:00
|
|
|
; Update the sprite's flags. We do not allow the size of a sprite to be changed. That requires
|
2021-11-20 18:16:03 +00:00
|
|
|
; the sprite to be removed and re-added.
|
|
|
|
;
|
2022-05-23 04:54:47 +00:00
|
|
|
; A = Sprite slot
|
2022-04-20 12:43:16 +00:00
|
|
|
; X = New Sprite Flags
|
|
|
|
; Y = New Sprite Stamp Address
|
2021-11-20 18:16:03 +00:00
|
|
|
_UpdateSprite
|
2022-04-20 12:43:16 +00:00
|
|
|
cmp #MAX_SPRITES
|
2021-11-20 18:16:03 +00:00
|
|
|
bcc :ok
|
|
|
|
rts
|
|
|
|
|
|
|
|
:ok
|
2022-04-20 12:43:16 +00:00
|
|
|
phx ; Save X to swap into A
|
|
|
|
asl
|
|
|
|
tax
|
|
|
|
pla
|
2022-02-23 14:41:02 +00:00
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
cmp _Sprites+SPRITE_ID,x ; If the flags changed, need to redraw the sprite
|
|
|
|
bne :sprite_flag_change ; on the next frame
|
|
|
|
tya
|
|
|
|
cmp _Sprites+VBUFF_ADDR,x ; Did the stamp change?
|
|
|
|
bne :sprite_stamp_change
|
|
|
|
rts ; Nothing changed, so just return
|
2021-11-20 18:16:03 +00:00
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
:sprite_flag_change
|
|
|
|
sta _Sprites+SPRITE_ID,x ; Keep a copy of the full descriptor
|
|
|
|
tya
|
|
|
|
:sprite_stamp_change
|
|
|
|
sta _Sprites+VBUFF_ADDR,x ; Just save this to stay in sync
|
2022-02-04 18:37:05 +00:00
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
lda _Sprites+SPRITE_STATUS,x ; Mark this sprite as updated
|
2022-02-07 07:19:31 +00:00
|
|
|
ora #SPRITE_STATUS_UPDATED
|
2021-11-20 18:16:03 +00:00
|
|
|
sta _Sprites+SPRITE_STATUS,x
|
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
jmp _PrecalcAllSpriteInfo ; Cache stuff and return
|
2021-11-20 18:16:03 +00:00
|
|
|
|
2021-10-24 03:31:38 +00:00
|
|
|
; 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
|
|
|
|
; X = x position
|
|
|
|
; Y = y position
|
2021-11-20 18:16:03 +00:00
|
|
|
_MoveSprite
|
2022-04-20 12:43:16 +00:00
|
|
|
cmp #MAX_SPRITES
|
2021-10-24 03:31:38 +00:00
|
|
|
bcc :ok
|
|
|
|
rts
|
|
|
|
|
|
|
|
:ok
|
2022-04-20 12:43:16 +00:00
|
|
|
phx ; Save X to swap into A
|
|
|
|
asl
|
|
|
|
tax
|
|
|
|
pla
|
|
|
|
|
2022-02-21 21:45:11 +00:00
|
|
|
cmp _Sprites+SPRITE_X,x
|
|
|
|
bne :changed1
|
2022-02-03 14:50:11 +00:00
|
|
|
sta _Sprites+SPRITE_X,x ; Update the X coordinate
|
2022-02-04 05:44:46 +00:00
|
|
|
tya
|
2022-02-21 21:45:11 +00:00
|
|
|
cmp _Sprites+SPRITE_Y,x
|
|
|
|
bne :changed2
|
|
|
|
rts
|
2022-02-03 14:50:11 +00:00
|
|
|
|
2022-02-21 21:45:11 +00:00
|
|
|
:changed1
|
|
|
|
sta _Sprites+SPRITE_X,x ; Update the X coordinate
|
|
|
|
tya
|
|
|
|
:changed2
|
|
|
|
sta _Sprites+SPRITE_Y,x ; Update the Y coordinate
|
2022-02-18 18:12:32 +00:00
|
|
|
|
2022-02-07 07:19:31 +00:00
|
|
|
lda _Sprites+SPRITE_STATUS,x
|
|
|
|
ora #SPRITE_STATUS_MOVED
|
|
|
|
sta _Sprites+SPRITE_STATUS,x
|
2021-10-24 03:31:38 +00:00
|
|
|
|
2022-04-20 12:43:16 +00:00
|
|
|
jmp _PrecalcAllSpriteInfo ; Can be specialized to only update (x,y) values
|