Fix off-by-one error in overlay

This commit is contained in:
Lucas Scharenbroich 2023-04-26 08:47:56 -05:00
parent b449d983ee
commit e013ff03fd
4 changed files with 57 additions and 320 deletions

View File

@ -47,6 +47,7 @@ PlayerX equ 40
PlayerY equ 42
PlayerXVel equ 44
PlayerYVel equ 46
OverlayTop equ 48
phk
plb
@ -107,6 +108,7 @@ PlayerYVel equ 46
sta StartY
stz frameCount
stz frameCountTotal
stz OverlayTop
pei StartX
pei StartY
@ -222,7 +224,21 @@ EvtLoop
_GTESetBG0Origin
bra :do_render
:not_w
cmp #' '
bne :not_space
lda OverlayTop
inc
and #$3F
sta OverlayTop
pha
clc
adc #8
pha
pea #^StatusBar
pea #StatusBar
_GTEUpdateOverlay
:not_space
:do_render
jsr SetBG1Animation ; Update the per-scanline BG1 offsets

View File

@ -275,6 +275,17 @@ r_ovrly
--^
jmp r_ovrly_rtn ; In R1W1, so can't use the stack
r_ovrly2
]idx equ 0
lup R_CHAR_COUNT
lda r_line+]idx,x
sta ]idx
lda r_line+]idx+2,x
sta ]idx+2
]idx equ ]idx+4
--^
jmp r_ovrly_rtn ; In R1W1, so can't use the stack
l_ovrly
]idx equ 0
lup L_CHAR_COUNT
@ -289,11 +300,23 @@ l_ovrly
]idx equ ]idx+4
--^
jmp l_ovrly_rtn
l_ovrly2
]idx equ 0
lup L_CHAR_COUNT
lda l_line+]idx,x
sta ]idx
lda l_line+]idx+2,x
sta ]idx+2
]idx equ ]idx+4
--^
jmp l_ovrly_rtn
; Single TSB slam
m_line
]idx equ $9E
lup 80 ; 80 words max for a full-width screen
; sta ]idx
tsb ]idx
]idx equ ]idx-2
--^

View File

@ -135,6 +135,7 @@ _Render
:no_removal
rts
; Small helper function to draw a single overlay
_DoOverlay
lda Overlays+OVERLAY_PROC
stal :disp+1
@ -162,12 +163,9 @@ _RenderScanlines
jsr _ResetBG1YTable
:ytbl_ok
jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen
jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen
jsr _ApplyScanlineBG1YPos ; 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
@ -233,7 +231,7 @@ _RenderScanlines
; i. Draw the overlay
; ii. Change the sprite segment to start after the overlay
; iii. Go to [1]
_DrawFinalPass2
_DrawFinalPass
:cursor equ tmp8
:bottom equ tmp9
@ -306,9 +304,10 @@ _DrawFinalPass2
:_somerge
phxy
ldx :cursor
ldy _Sprites+SPRITE_CLIP_TOP,x ; PEI Slam to the top of the overlay (:bottom is greater than this value)
ldx :cursor
sty :cursor
; brk $44
jsr _PEISlam
lda 3,s ; Retrieve the sprite index
tax
@ -371,112 +370,6 @@ _DrawFinalPass2
sta :cursor
brl :_so_loop
_DrawFinalPass
:cursor equ tmp8
stz :cursor ; Current mark in the sweep down the screen
ldy ObjectListHead ; If there are no items, just _BltRange the rest of the screen and return
jmi :finish
:loop
ldx :cursor
lda ObjectList+OL_SPRITE_TOP,y
sta :cursor
jsr :_BltRange3 ; Expose from the cursor to the top and update the cursor
lda ObjectList+OL_SPRITE_ID,y ; See if we are processing an overlay or a sprite region
bit #SPRITE_OVERLAY
jne :obj_is_overlay
:obj_is_sprite
ldx ObjectList+OL_NEXT,y
jmi :sprite_complete
; Look at the next item, if it's below the current sprite range, do a slam and move on to the next item
lda ObjectList+OL_CLIP_BOTTOM,y
cmp ObjectList+OL_CLIP_TOP,x
bcs :sprite_overlap
txy ; Move to the next sprite
ldx :cursor
sta :cursor ; A = bottom, x = :cursor
jsr _PEISlam
bra :loop ; Loop back and do the BltRange up to the top of the next sprite
; Now we know that the next item must be an overlay (because sprite ranges are already combined), so go ahead
; and PEI slam up to the top of the overlay
:sprite_overlap
lda ObjectList+OL_CLIP_TOP,x
ldx :cursor
sta :cursor
jsr _PEISlam
lda ObjectList+OL_CLIP_BOTTOM,x ; If the overlay is fully within the sprite, do extra work.
cmp ObjectList+OL_CLIP_BOTTOM,y ; Otherwise continue knowing we are currently handling an overlay
bcc :split
txy
bra :overlay_next
:split
jsr split
bra :obj_is_sprite
; Do a similar process for the overlays
:obj_is_overlay
ldx ObjectList+OL_NEXT,y
bmi :ovrly_complete
; Look at the next item, if it's below the current overlay range, draw the overlay and move on to the next item
lda ObjectList+OL_CLIP_BOTTOM,y
cmp ObjectList+OL_CLIP_TOP,x
bcs :ovrly_overlap
jsr DrawOverlayY
txy ; Move to the next item
bra :loop ; Loop back and do the BltRange up to the top of the next sprite
; Now we know that the next item must be a sprite. Skip any sprite that are completely covered by the overlay. If
; a sprite is split by the overlay, then reduce the top value
:ovrly_overlap
lda ObjectList+OL_CLIP_TOP,x
ldx :cursor
sta :cursor
jsr PEISlam
lda ObjectList+OL_CLIP_BOTTOM,x ; If the next
cmp ObjectList+OL_CLIP_BOTTOM,y ; Otherwise continue knowing we are currently handling an overlay
bcc :split
txy
bra :overlay_next
; When a sprite is the last item before the end of the screen, jump here
:sprite_complete
ldx :cursor
lda ObjectList+OL_CLIP_BOTTOM,y
sta :cursor
tay
jsr PEISlam
; Jump here when there are no items left to process.
:finish
ldx :cursor
ldy ScreenHeight
jmp _BltRange
lda ObjectList+OL_CLIP_BOTTOM,x ; If the overlay is fully within the sprite, do extra work.
cmp ObjectList+OL_CLIP_BOTTOM,y ; Otherwise continue knowing we are currently handling an overlay
bcc :split
txy
bra :overlay_next
; Run through all of the tiles on the DirtyTile list and render them
_ApplyTiles
ldx DirtyTileCount
@ -575,97 +468,14 @@ _RenderWithShadowing
; At this point, everything in the background has been rendered into the code field. Next, we need
; to create priority lists of scanline ranges.
; jsr _BuildShadowList ; Create the ranges based on the sorted sprite y-values
jsr _ShadowOff ; Turn off shadowing and draw all the scanlines with sprites on them
jsr _ShadowOff ; Turn off shadowing and draw all the scanlines with sprites on them
jsr _DrawShadowList
jsr _DrawDirectSprites ; Draw the sprites directly to the Bank $01 graphics buffer (skipping the render-to-tile step)
jsr _DrawDirectSprites ; Draw the sprites directly to the Bank $01 graphics buffer (skipping the render-to-tile step)
jsr _ShadowOn ; Turn shadowing back on
jsr _ShadowOn ; Turn shadowing back on
jsr _DrawFinalPass
;
; The objects that need to be reasoned about are
;
; 1. Sprites
; 2. Overlays
; a. Solid High Priority
; b. Solid Low Priority
; c. Masked High Priority
; d. Masked Low Priority
; 3. Background
;
; Notes:
;
; A High Priority overlay is rendered above the sprites
; A Low Priority overlay is rendered below the sprites
; A Solid High Priority overlay obscured everything and if the only thing drawn on the scanline
;
; The order of draw oprations is:
;
; 1. Turn off shadowing
; 2. Draw the background for scanlines with (Sprites OR a Masked Low Priority overlay) AND NOT a Solid Low Priority overlay
; 3. Draw the Solid Low Priority overlays
; 4. Draw the Sprites
; 5. Draw the Masked Low Priority overlays
; 6. Turn on shadowing
; 7. Draw, in top-to-bottom order
; a. Background lines not drawn yet
; b. PEI Slam lines with (Sprites OR a Masked Low Priority Overlay) AND NOT a High Priority overlay
; c. High Priority overlays
;
; The work of this routine is to quickly build a sorted list of scanline ranges that can call the appropriate
; sub-renderer
; jsr BuildShadowSegments
;
; The trick is to create a bit-field mapping for the different actions to define
; 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+OVERLAY_TOP ; Blit the full virtual buffer to the screen
; ldy Overlays+OVERLAY_BOTTOM
; 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+OVERLAY_TOP
; beq :skip
; jsr _BltRange
;:skip
; jsr _DoOverlay
; ldx Overlays+OVERLAY_BOTTOM
; 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 _RestoreBG0Opcodes
@ -690,95 +500,6 @@ _RenderWithShadowing
:no_removal
rts
; Look at the overlay list and the sprite list and figure out which scanline ranges need to be
; blitted in what order. We try to build all of the scan line segments lists because that
; saves the work of re-scanning the lists.
;
; The semgent list definitions are:
;
; BLIT_W_SHADOW_OF
BuildShadowSegments
; ldx _SortedHead
; bmi :no_sprite
;:loop
; lda _Sprites+CLIP_TOP,x
; lda _Sprites+SORTED_NEXT,x
; tax
; bpl :loop
;
; lda #0 ; Start at the top of the
rts
; Function go through the object list and draw the background for areas that will need to draw
; additional items on top
_DrawShadowBkgnd
ldx _SortedHead
bmi :empty ; If there is nothing, do nothing
lda _Sprites+SPRITE_ID,x
:empty
rts
; Function to iterate through the object list and build a merged scanline list of areas of the screen that
; need to be drawn with shadowing off.
_BuildShadowList
ldy #0 ; This is the index into the list of shadow segments
ldx _SortedHead
:preloop
bmi :empty ; If the list is empty / skipped, do nothing
lda _Sprites+SPRITE_ID,x
bit #SPRITE_HIDE ; Make sure we don't do extra work for hidden objects
beq :insert
lda _Sprites+SORTED_NEXT,x
tax
bra :preloop
; Start of loop
:advance
iny
iny
:insert
lda _Sprites+SPRITE_CLIP_TOP,x ; Load the sprite's top line
sta _ShadowListTop,y ; Set the top entry of the list to the sprite top
lda _Sprites+SPRITE_CLIP_BOTTOM,x ; Optimistically set the end of the segment to the bottom of this sprite
inc ; Clip values are on the scanline, so add one to make it a proper interval
:replace
sta _ShadowListBottom,y
:skip
lda _Sprites+SORTED_NEXT,x ; If there another sprite in the list?
bmi :no_more_sprites ; If not, we can finish up
tax
lda _Sprites+SPRITE_ID,x
bit #SPRITE_HIDE
bne :skip
lda _ShadowListBottom,y ; If the bottom of the current sprite is _less than_ the top of the next
cmp _Sprites+SPRITE_CLIP_TOP,x ; sprite, then there is a gap and we create a new entry
bcc :advance
lda _Sprites+SPRITE_CLIP_BOTTOM,x ; Get the bottom value of the next sprite.
inc
cmp _ShadowListBottom,y ; If it extends the segment then replace the value, otherwise skip
bcc :skip
bra :replace
:no_more_sprites
iny ; Set the list count to N * 2
iny
:empty
sty _ShadowListCount
rts
; Iterate through the shadow list and call _BltRange on each
_DrawShadowList
ldx #0
@ -882,12 +603,6 @@ _DrawDirectSprites
;
; We can't alter that actual sorted list of items, so we create a reduced list which allows items to be filtered and
; to keep a simple, single-linked list
isNotHidden mac
bit #SPRITE_OVERLAY
bne ]1
bit #SPRITE_HIDE
beq ]1
EOL equ $FFFF
@ -1009,9 +724,6 @@ _DrawObjShadow
;:empty
; rts
; Walk the object list and call _BltRange for the sprite
_DrawShadowRanges
; Split
;
@ -1023,21 +735,21 @@ _DrawShadowRanges
split
:prev equ tmp15
lda ObjectList+OL_CLIP_BOTTOM,x ; If the next item is fully within the current one, split
cmp ObjectList+OL_CLIP_BOTTOM,y
lda ObjectList+OL_SPRITE_BOTTOM,x ; If the next item is fully within the current one, split
cmp ObjectList+OL_SPRITE_BOTTOM,y
bcc :do_split
rts
:do_split
sta ObjectList+OL_CLIP_TOP,y ; Set the top of the current item past the bottom of the next item
sta ObjectList+OL_SPRITE_TOP,y ; Set the top of the current item past the bottom of the next item
:split_lp
lda ObjectList+OL_NEXT,x ; search to find the spot in the linked list that we should
bmi :insert_after ; move the fragment forward to
stx :prev
tax
lda ObjectList+OL_CLIP_TOP,y
cmp ObjectList+OL_CLIP_TOP,x
lda ObjectList+OL_SPRITE_TOP,y
cmp ObjectList+OL_SPRITE_TOP,x
bcc :insert_before ; If the modified node's top value is <= the node we are inspecting,
beq :insert_before ; then it can be inserted here
bra :split_lp
@ -1067,12 +779,6 @@ split
plx
rts
_BltRange2
phx
jsr _BltRange
plx
rts
_GetNextItem
cpx #EOL ; early out if we're at the end of the list
bne *+3
@ -1107,14 +813,6 @@ DrawOverlayY
plx
rts
DrawOverlayX
phx
phy
jsr _DrawOverlay
ply
plx
rts
; A = top line
; X = sprite record
; Y = bottom line

View File

@ -729,11 +729,11 @@ _TSSetOverlay
lda :top,s
sta Overlays+OVERLAY_TOP,x
lda :bottom,s
dec
; dec
sta Overlays+OVERLAY_BOTTOM,x
sec
sbc :top,s
inc
; inc
sta Overlays+OVERLAY_HEIGHT,x
lda :proc,s
@ -759,11 +759,11 @@ _TSUpdateOverlay
lda :top,s
sta Overlays+OVERLAY_TOP
lda :bottom,s
dec
; dec
sta Overlays+OVERLAY_BOTTOM
sec
sbc :top,s
inc
; inc
sta Overlays+OVERLAY_HEIGHT,x
lda :proc,s