2021-07-08 12:46:35 +00:00
|
|
|
; Renders a frame of animation
|
|
|
|
;
|
2022-02-21 16:31:35 +00:00
|
|
|
; The render function is the point of committment -- most of the APIs that set sprites and
|
2022-02-25 23:05:32 +00:00
|
|
|
; update coordinates are lazy; they simply save their values and set a dirty flag in the
|
2021-07-09 19:18:49 +00:00
|
|
|
; DirtyBits word.
|
|
|
|
;
|
|
|
|
; This function examines the dirty bits and actually performs the work to update the code field
|
|
|
|
; and internal data structure to properly render the play field. Then the update pipeline is
|
|
|
|
; executed.
|
2022-02-25 23:05:32 +00:00
|
|
|
;
|
|
|
|
; Everything is composited into the tiles in the playfield and then the screen is rendered in
|
|
|
|
; a single pass.
|
2022-04-29 17:38:04 +00:00
|
|
|
;
|
2021-07-09 20:38:32 +00:00
|
|
|
; TODO -- actually check the dirty bits and be selective on what gets updated. For example, if
|
|
|
|
; only the Y position changes, then we should only need to set new values on the
|
|
|
|
; virtual lines that were brought on screen. If the X position only changes by one
|
|
|
|
; byte, then we may have to change the CODE_ENTRY values or restore/set new OPCODE
|
|
|
|
; values, but not both.
|
|
|
|
|
2021-07-18 02:00:46 +00:00
|
|
|
; It's important to do _ApplyBG0YPos first because it calculates the value of StartY % 208 which is
|
|
|
|
; used in all of the other loops
|
2021-08-25 14:38:02 +00:00
|
|
|
_Render
|
2022-07-16 20:22:23 +00:00
|
|
|
sta RenderFlags
|
|
|
|
|
2022-07-07 19:46:37 +00:00
|
|
|
lda LastRender ; Check to see what kind of rendering was done on the last frame. If
|
|
|
|
beq :no_change ; it was not this renderer,
|
|
|
|
jsr _ResetToNormalTileProcs
|
|
|
|
jsr _Refresh
|
|
|
|
:no_change
|
|
|
|
|
2022-07-05 04:55:32 +00:00
|
|
|
jsr _DoTimers ; Run any pending timer tasks
|
2022-06-25 16:17:50 +00:00
|
|
|
|
2022-05-27 00:36:40 +00:00
|
|
|
stz SpriteRemovedFlag ; If we remove a sprite, then we need to flag a rebuild for the next frame
|
|
|
|
|
2022-08-11 19:14:25 +00:00
|
|
|
; If we are doing per-scanline rendering, use the alternate renderer
|
|
|
|
|
|
|
|
lda #RENDER_PER_SCANLINE
|
|
|
|
bit RenderFlags
|
|
|
|
beq *+5
|
|
|
|
jmp _RenderScanlines ; Do the scanline-based renderer
|
|
|
|
|
|
|
|
|
2021-07-09 20:38:32 +00:00
|
|
|
jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen
|
2022-07-22 07:01:34 +00:00
|
|
|
|
|
|
|
lda #RENDER_BG1_ROTATION
|
|
|
|
bit RenderFlags
|
|
|
|
bne :skip_bg1_y
|
|
|
|
jsr _ApplyBG1YPos ; Set the y-register values of the blitter
|
|
|
|
:skip_bg1_y
|
2021-08-06 19:42:18 +00:00
|
|
|
|
|
|
|
; _ApplyBG0Xpos need to be split because we have to set the offsets, then draw in any updated tiles, and
|
|
|
|
; finally patch out the code field. Right now, the BRA operand is getting overwritten by tile data.
|
2022-07-22 07:01:34 +00:00
|
|
|
|
2021-08-06 19:42:18 +00:00
|
|
|
jsr _ApplyBG0XPosPre
|
2022-06-27 05:32:44 +00:00
|
|
|
jsr _ApplyBG1XPosPre
|
2021-08-06 19:42:18 +00:00
|
|
|
|
2022-05-23 20:18:34 +00:00
|
|
|
jsr _RenderSprites ; Once the BG0 X and Y positions are committed, update sprite data
|
2021-10-31 20:42:59 +00:00
|
|
|
|
2022-06-27 03:08:42 +00:00
|
|
|
jsr _UpdateBG0TileMap ; and the tile maps. These subroutines build up a list of tiles
|
2022-05-18 05:34:25 +00:00
|
|
|
; jsr _UpdateBG1TileMap ; that need to be updated in the code field
|
2021-08-06 19:42:18 +00:00
|
|
|
|
2022-07-06 04:48:33 +00:00
|
|
|
jsr _ApplyTiles ; This function actually draws the new tiles into the code field
|
2021-07-09 20:38:32 +00:00
|
|
|
|
2022-07-22 07:01:34 +00:00
|
|
|
jsr _ApplyBG0XPos ; Patch the code field instructions with exit BRA opcode
|
|
|
|
lda #RENDER_BG1_ROTATION
|
|
|
|
bit RenderFlags
|
|
|
|
bne :skip_bg1_x
|
|
|
|
jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position
|
|
|
|
:skip_bg1_x
|
2021-10-21 13:50:07 +00:00
|
|
|
|
2022-06-27 16:24:04 +00:00
|
|
|
; The code fields are locked in now and ready to be rendered. See if there is an overlay or any
|
|
|
|
; other reason to render with shadowing off. Otherwise, just do things quickly.
|
2021-07-20 03:42:51 +00:00
|
|
|
|
2022-06-27 16:24:04 +00:00
|
|
|
lda Overlays
|
|
|
|
beq :no_ovrly
|
|
|
|
|
|
|
|
jsr _ShadowOff
|
2021-07-20 03:42:51 +00:00
|
|
|
|
2021-10-31 00:24:23 +00:00
|
|
|
; Shadowing is turned off. Render all of the scan lines that need a second pass. One
|
|
|
|
; optimization that can be done here is that the lines can be rendered in any order
|
|
|
|
; since it is not shown on-screen yet.
|
2021-10-21 13:50:07 +00:00
|
|
|
|
2022-06-27 16:24:04 +00:00
|
|
|
ldx Overlays+2 ; Blit the full virtual buffer to the screen
|
|
|
|
ldy Overlays+4
|
|
|
|
jsr _BltRange
|
2021-10-21 13:50:07 +00:00
|
|
|
|
|
|
|
; Turn shadowing back on
|
2021-07-20 03:42:51 +00:00
|
|
|
|
2022-06-27 16:24:04 +00:00
|
|
|
jsr _ShadowOn
|
2021-07-20 03:42:51 +00:00
|
|
|
|
2021-10-21 13:50:07 +00:00
|
|
|
; Now render all of the remaining lines in top-to-bottom (or bottom-to-top) order
|
|
|
|
|
2022-06-27 16:24:04 +00:00
|
|
|
ldx #0
|
|
|
|
ldy Overlays+2
|
|
|
|
beq :skip
|
|
|
|
jsr _BltRange
|
|
|
|
:skip
|
|
|
|
jsr _DoOverlay
|
2021-10-31 00:24:23 +00:00
|
|
|
|
2022-06-27 16:24:04 +00:00
|
|
|
ldx Overlays+4
|
|
|
|
cpx ScreenHeight
|
|
|
|
beq :done
|
|
|
|
ldy ScreenHeight
|
|
|
|
jsr _BltRange
|
|
|
|
bra :done
|
|
|
|
|
|
|
|
:no_ovrly
|
2021-11-15 18:23:38 +00:00
|
|
|
ldx #0 ; Blit the full virtual buffer to the screen
|
2021-07-09 20:38:32 +00:00
|
|
|
ldy ScreenHeight
|
|
|
|
jsr _BltRange
|
2022-06-27 16:24:04 +00:00
|
|
|
:done
|
2021-07-09 20:38:32 +00:00
|
|
|
|
2022-06-27 05:41:30 +00:00
|
|
|
ldx #0
|
|
|
|
ldy ScreenHeight
|
|
|
|
jsr _BltSCB
|
2021-11-15 18:23:38 +00:00
|
|
|
|
2022-05-23 04:54:47 +00:00
|
|
|
lda StartYMod208 ; Restore the fields back to their original state
|
2021-07-09 20:38:32 +00:00
|
|
|
ldx ScreenHeight
|
|
|
|
jsr _RestoreBG0Opcodes
|
|
|
|
|
2021-08-10 12:59:14 +00:00
|
|
|
lda StartY
|
|
|
|
sta OldStartY
|
|
|
|
lda StartX
|
|
|
|
sta OldStartX
|
|
|
|
|
2021-08-19 06:22:36 +00:00
|
|
|
lda BG1StartY
|
|
|
|
sta OldBG1StartY
|
|
|
|
lda BG1StartX
|
|
|
|
sta OldBG1StartX
|
|
|
|
|
2021-08-10 12:59:14 +00:00
|
|
|
stz DirtyBits
|
2022-02-19 02:43:55 +00:00
|
|
|
stz LastRender ; Mark that a full render was just performed
|
2022-05-27 00:36:40 +00:00
|
|
|
|
|
|
|
lda SpriteRemovedFlag ; If any sprite was removed, set the rebuild flag
|
|
|
|
beq :no_removal
|
|
|
|
lda #DIRTY_BIT_SPRITE_ARRAY
|
|
|
|
sta DirtyBits
|
|
|
|
:no_removal
|
2021-07-08 12:46:35 +00:00
|
|
|
rts
|
2022-01-20 02:58:57 +00:00
|
|
|
|
2022-06-27 16:24:04 +00:00
|
|
|
_DoOverlay
|
|
|
|
lda Overlays+6
|
|
|
|
stal :disp+1
|
|
|
|
lda Overlays+7
|
|
|
|
stal :disp+2
|
|
|
|
|
|
|
|
lda ScreenY0 ; pass the address of the first line of the overlay
|
|
|
|
clc
|
|
|
|
adc Overlays+2
|
|
|
|
asl
|
|
|
|
tax
|
|
|
|
lda ScreenAddr,x
|
|
|
|
clc
|
|
|
|
adc ScreenX0
|
|
|
|
:disp jsl $000000
|
|
|
|
rts
|
|
|
|
|
2022-08-11 19:14:25 +00:00
|
|
|
; Use the per-scanline tables to set the screen. This is really meant to be used without the built-in tilemap
|
|
|
|
; support and is more of a low-level way to control the background rendering
|
|
|
|
_RenderScanlines
|
|
|
|
|
|
|
|
jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen
|
|
|
|
; jsr _ApplyBG1YPos ; Set the y-register values of the blitter
|
|
|
|
|
|
|
|
; _ApplyBG0Xpos need to be split because we have to set the offsets, then draw in any updated tiles, and
|
|
|
|
; finally patch out the code field. Right now, the BRA operand is getting overwritten by tile data.
|
|
|
|
|
|
|
|
jsr _ApplyBG0XPosPre
|
|
|
|
; jsr _ApplyBG1XPosPre
|
|
|
|
|
|
|
|
; jsr _RenderSprites ; Once the BG0 X and Y positions are committed, update sprite data
|
|
|
|
|
|
|
|
; jsr _ApplyTiles ; This function actually draws the new tiles into the code field
|
|
|
|
|
|
|
|
jsr _ScanlineBG0XPos ; Patch the code field instructions with exit BRA opcode
|
|
|
|
; jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position
|
|
|
|
|
|
|
|
; The code fields are locked in now and ready to be rendered. See if there is an overlay or any
|
|
|
|
; other reason to render with shadowing off. Otherwise, just do things quickly.
|
|
|
|
|
|
|
|
lda Overlays
|
|
|
|
beq :no_ovrly
|
|
|
|
|
|
|
|
jsr _ShadowOff
|
|
|
|
|
|
|
|
; Shadowing is turned off. Render all of the scan lines that need a second pass. One
|
|
|
|
; optimization that can be done here is that the lines can be rendered in any order
|
|
|
|
; since it is not shown on-screen yet.
|
|
|
|
|
|
|
|
ldx Overlays+2 ; Blit the full virtual buffer to the screen
|
|
|
|
ldy Overlays+4
|
|
|
|
jsr _BltRange
|
|
|
|
|
|
|
|
; Turn shadowing back on
|
|
|
|
|
|
|
|
jsr _ShadowOn
|
|
|
|
|
|
|
|
; Now render all of the remaining lines in top-to-bottom (or bottom-to-top) order
|
|
|
|
|
|
|
|
ldx #0
|
|
|
|
ldy Overlays+2
|
|
|
|
beq :skip
|
|
|
|
jsr _BltRange
|
|
|
|
:skip
|
|
|
|
jsr _DoOverlay
|
|
|
|
|
|
|
|
ldx Overlays+4
|
|
|
|
cpx ScreenHeight
|
|
|
|
beq :done
|
|
|
|
ldy ScreenHeight
|
|
|
|
jsr _BltRange
|
|
|
|
bra :done
|
|
|
|
|
|
|
|
:no_ovrly
|
|
|
|
ldx #0 ; Blit the full virtual buffer to the screen
|
|
|
|
ldy ScreenHeight
|
|
|
|
jsr _BltRange
|
|
|
|
:done
|
|
|
|
|
|
|
|
ldx #0
|
|
|
|
ldy ScreenHeight
|
|
|
|
jsr _BltSCB
|
|
|
|
|
|
|
|
lda StartYMod208 ; Restore the fields back to their original state
|
|
|
|
ldx ScreenHeight
|
|
|
|
jsr _RestoreScanlineBG0Opcodes
|
|
|
|
|
|
|
|
lda StartY
|
|
|
|
sta OldStartY
|
|
|
|
lda StartX
|
|
|
|
sta OldStartX
|
|
|
|
|
|
|
|
lda BG1StartY
|
|
|
|
sta OldBG1StartY
|
|
|
|
lda BG1StartX
|
|
|
|
sta OldBG1StartX
|
|
|
|
|
|
|
|
stz DirtyBits
|
|
|
|
stz LastRender ; Mark that a full render was just performed
|
|
|
|
|
|
|
|
lda SpriteRemovedFlag ; If any sprite was removed, set the rebuild flag
|
|
|
|
beq :no_removal
|
|
|
|
lda #DIRTY_BIT_SPRITE_ARRAY
|
|
|
|
sta DirtyBits
|
|
|
|
:no_removal
|
|
|
|
rts
|
|
|
|
|
2022-07-06 04:48:33 +00:00
|
|
|
; Run through all of the tiles on the DirtyTile list and render them
|
|
|
|
_ApplyTiles
|
2022-05-31 13:43:26 +00:00
|
|
|
ldx DirtyTileCount
|
|
|
|
|
2022-07-06 04:48:33 +00:00
|
|
|
phd ; sve the current direct page
|
2022-05-18 05:34:25 +00:00
|
|
|
tdc
|
|
|
|
clc
|
|
|
|
adc #$100 ; move to the next page
|
|
|
|
tcd
|
|
|
|
|
2022-05-31 13:43:26 +00:00
|
|
|
stx DP2_DIRTY_TILE_COUNT ; Cache the dirty tile count
|
2022-05-19 02:39:39 +00:00
|
|
|
jsr _PopDirtyTilesFast
|
2022-05-18 05:34:25 +00:00
|
|
|
|
2022-07-06 04:48:33 +00:00
|
|
|
pld ; Move back to the original direct page
|
2022-05-31 13:43:26 +00:00
|
|
|
stz DirtyTileCount ; Reset the dirty tile count
|
2022-05-18 05:34:25 +00:00
|
|
|
rts
|
|
|
|
|
2022-02-19 02:43:55 +00:00
|
|
|
; This is a specialized render function that only updates the dirty tiles *and* draws them
|
2022-01-20 02:58:57 +00:00
|
|
|
; directly onto the SHR graphics buffer. The playfield is not used at all. In some way, this
|
|
|
|
; ignores almost all of the capabilities of GTE, but it does provide a convenient way to use
|
|
|
|
; the sprite subsystem + tile attributes for single-screen games which should be able to run
|
|
|
|
; close to 60 fps.
|
2022-02-25 23:05:32 +00:00
|
|
|
;
|
|
|
|
; In this renderer, we assume that there is no scrolling, so no need to update any information about
|
2022-01-20 02:58:57 +00:00
|
|
|
; the BG0/BG1 positions
|
|
|
|
_RenderDirty
|
2022-07-06 19:55:27 +00:00
|
|
|
lda LastRender ; If the full renderer was last called, we assume that
|
|
|
|
bne :norecalc ; the scroll positions have likely changed, so recalculate
|
|
|
|
jsr _RecalcTileScreenAddrs ; them to make sure sprites draw at the correct screen address
|
2022-07-07 19:46:37 +00:00
|
|
|
jsr _ResetToDirtyTileProcs ; Switch the tile procs to the dirty tile rendering functions
|
2022-05-18 05:34:25 +00:00
|
|
|
; jsr _ClearSpritesFromCodeField ; Restore the tiles to their non-sprite versions
|
2022-02-04 05:44:46 +00:00
|
|
|
:norecalc
|
2022-07-06 19:55:27 +00:00
|
|
|
jsr _RenderSprites
|
|
|
|
jsr _ApplyDirtyTiles
|
2022-04-29 17:38:04 +00:00
|
|
|
|
2022-02-04 05:44:46 +00:00
|
|
|
lda #1
|
|
|
|
sta LastRender
|
2022-01-20 02:58:57 +00:00
|
|
|
rts
|
|
|
|
|
|
|
|
_ApplyDirtyTiles
|
2022-07-06 19:55:27 +00:00
|
|
|
phd ; save the current direct page
|
|
|
|
tdc
|
|
|
|
clc
|
|
|
|
adc #$100 ; move to the next page
|
|
|
|
tcd
|
|
|
|
|
2022-01-20 02:58:57 +00:00
|
|
|
bra :begin
|
|
|
|
|
|
|
|
:loop
|
|
|
|
; Retrieve the offset of the next dirty Tile Store items in the Y-register
|
|
|
|
|
|
|
|
jsr _PopDirtyTile2
|
|
|
|
|
|
|
|
; Call the generic dispatch with the Tile Store record pointer at by the Y-register.
|
|
|
|
|
|
|
|
jsr _RenderDirtyTile
|
|
|
|
|
|
|
|
; Loop again until the list of dirty tiles is empty
|
|
|
|
|
2022-02-18 19:42:37 +00:00
|
|
|
:begin ldy DirtyTileCount
|
2022-01-20 02:58:57 +00:00
|
|
|
bne :loop
|
2022-07-06 19:55:27 +00:00
|
|
|
|
|
|
|
pld ; Move back to the original direct page
|
|
|
|
stz DirtyTileCount ; Reset the dirty tile count
|
2022-01-20 02:58:57 +00:00
|
|
|
rts
|
|
|
|
|