diff --git a/src/Defs.s b/src/Defs.s index 28e7a9e..4d181cf 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -182,13 +182,6 @@ OVERLAY_SOLID equ $8000 ; Overlay covers the scan line and is fu OVERLAY_ABOVE equ $0000 ; Overlay is drawn above scanline sprites OVERLAY_BELOW equ $4000 ; Overlay is drawn below scanline sprites -OVERLAY_BASE equ 2 ; 2 bytes for the number of overlays -OVERLAY_REC_SIZE equ 10 ; Size of an overlay record (10 bytes) -OVERLAY_FLAGS equ {OVERLAY_BASE+0} -OVERLAY_TOP equ {OVERLAY_BASE+2} -OVERLAY_BOTTOM equ {OVERLAY_BASE+4} -OVERLAY_PROC equ {OVERLAY_BASE+6} - ; DirtyBits definitions DIRTY_BIT_BG0_X equ $0001 DIRTY_BIT_BG0_Y equ $0002 @@ -228,6 +221,7 @@ TILE_CTRL_MASK equ $7E00 ; TILE_PROC_MASK equ $7800 ; Select tile proc for rendering ; Sprite constants +SPRITE_OVERLAY equ $8000 ; This is an overlay record. Stored as a sprite for render ordering purposes SPRITE_COMPILED equ $4000 ; This is a compiled sprite (SPRITE_DISP points to a routine in the compiled cache bank) SPRITE_HIDE equ $2000 ; Do not render the sprite SPRITE_16X16 equ $1800 ; 16 pixels wide x 16 pixels tall @@ -283,7 +277,7 @@ VBuffArray EXT _stamp_step EXT VBuffVertTableSelect EXT VBuffHorzTableSelect EXT -Overlays EXT +; Overlays EXT BG1YCache EXT ScalingTables EXT diff --git a/src/Render.s b/src/Render.s index d5ba62e..9d9b7b2 100644 --- a/src/Render.s +++ b/src/Render.s @@ -71,7 +71,7 @@ _Render ; 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 + lda Overlays+OVERLAY_ID beq :no_ovrly jsr _ShadowOff @@ -82,6 +82,7 @@ _Render ldx Overlays+OVERLAY_TOP ; Blit the full virtual buffer to the screen ldy Overlays+OVERLAY_BOTTOM + iny jsr _BltRange ; Turn shadowing back on @@ -93,6 +94,7 @@ _Render jsr _DoOverlay ldx Overlays+OVERLAY_BOTTOM + inx cpx ScreenHeight beq :done ldy ScreenHeight @@ -343,14 +345,15 @@ _RenderWithShadowing jsr _BuildShadowList ; Create the rages based on the sorted sprite y-values ; jsr _MergeOverlay ; Add the overlay range into the shadow list (treat it as a sprite) - jsr _ComplementList ; Create the complement to identify non-sprite / non-overlay scanlines +; jsr _ComplementList ; Create the complement to identify non-sprite / non-overlay scanlines 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 _ShadowOn ; Turn shadowing back on - jsr _DrawComplementList ; Alternate drawing scanlines and PEI slam to expose the full fram +; jsr _DrawComplementList ; Alternate drawing scanlines and PEI slam to expose the full fram + jsr _DrawFinalPass ; ; The objects that need to be reasoned about are @@ -598,13 +601,16 @@ _DrawShadowList rts -; Run through the list of sprites that are not IS_OFFSCREEN and draw them directly to the graphics screen. We can use +; Run through the list of sprites that are not IS_OFFSCREEN and not OVERLAYS and draw them directly to the graphics screen. We can use ; compiled sprites here, with limitations. _DrawDirectSprites ldx _SortedHead bmi :empty :loop + lda _Sprites+SPRITE_ID,x + bit #SPRITE_OVERLAY + bne :next lda _Sprites+SPRITE_STATUS,x bit #SPRITE_STATUS_HIDDEN bne :next @@ -622,7 +628,144 @@ _DrawDirectSprites :empty rts -; Run through the complement list and alternate between calling _PEISlam and _BltRange to show to full screen +; Run through the sorted list and perform a final render the jumps between calling _PEISlam for shadowed lines, +; _BltRange for clean backgrounds and Overlays as needed. +; +; The trick here is to merge runs of shared render types. +; +; Loop invariant: X-register is the current object index, Y-register is the next object index +; +; TODO: This does not yet handle the case of a narrow overlay in the middle of a sprite. The second half of the sprite will not be exposed +; by a PEISlam. +; +; e.g. |--- Overlay ---| +; |-------------- Sprite ----------------| +; +; Output Should be |-- PEI --||--- Overlay ---||--- PEI --| +; But currently is |-- PEI --||--- Overlay ---| + +_DrawFinalPass +:curr_top equ tmp0 +:curr_bottom equ tmp1 +:curr_type equ tmp2 + + ldx _SortedHead + bmi :empty + + lda _Sprites+SPRITE_CLIP_TOP,x ; Load the first object's top edge + sta :curr_top + beq :loop ; If it's at the top edge of the screen, proceed. Othrewise _BltRange the top range + + ldx #0 + tay + jsr _BltRange + ldx _SortedHead ; Reload the register + +:loop + lda _Sprites+SPRITE_ID,x ; Save the type of the current segment. Do this first because it can be skipped + and #SPRITE_OVERLAY ; when merging ranges of the same type + sta :curr_type + + lda _Sprites+SPRITE_CLIP_TOP,x + sta :curr_top + lda _Sprites+SPRITE_CLIP_BOTTOM,x ; Optimistically set the end of the segment to the bottom of this object + inc ; Clip values are on the scanline, so add one to make it a proper interval + +:update + sta :curr_bottom + +:skip + ldy _Sprites+SORTED_NEXT,x ; If there another object in the list? + bmi :no_more ; If not, we can finish up + + lda :curr_bottom ; If the bottom of the current object is _less than_ the top of the next + cmp _Sprites+SPRITE_CLIP_TOP,y ; sprite, then there is a gap and we can draw the current object and a + bcc :advance ; _BltRange up to the next one + +; Here, we've established that there is another object segment that starts at or within the bounds of the current +; object. If they are of the same type, then we can merge them and look at the next object in the list; treating +; the merges range as a larger, single object range. +; +; If they are different, then clip the current object range to the top of the next one, render the current object +; range and then take the new object as the current one. +; +; If the first object extends past the second, we are going to miss the remainder of that object. We really need a +; stack to put it on so that it can eventually be processed later. + + lda _Sprites+SPRITE_ID,y + and #SPRITE_OVERLAY + cmp :curr_type + bne :no_merge + + tyx ; Move the next index into the current + lda _Sprites+SPRITE_CLIP_BOTTOM,y ; Get the bottom value of the next sprite. + inc + cmp :curr_bottom ; If it extends the segment then replace the bottom value, otherwise skip. In + bcc :skip ; either case, the type and top value remain the same + bra :update + +; This is a simpler version of the 'advance' below. In this case there are overlapping ranges, so we just need to draw a +; clipped version of the top range and then restart the loop with the next range. +:no_merge + lda _Sprites+SPRITE_CLIP_TOP,y ; Get the top of the next segment + sta :curr_bottom ; Use it as the bottom of the current segment + phy ; Save the next index... + jsr :PEIOrOverlay ; Draw the current segment type + plx ; ...and restore as the current + bra :loop ; Start again + +:advance + phy + jsr :PEIOrOverlay ; Draw the appropriate filler + lda 1,s + tax + ldy _Sprites+SPRITE_CLIP_TOP,x ; Draw the background in between + ldx :curr_bottom + jsr _BltRange + plx + bra :loop + +; List is empty, so just do one big _BltRange with a tail call +:empty + ldx #0 +:no_more2 + ldy ScreenHeight + jmp _BltRange + +; Found the end of the list. Draw current object and then blit the rest of the screen +:no_more + jsr :PEIOrOverlay + ldx :curr_bottom + cpx ScreenHeight + bcc :no_more2 + rts + +; Help to select between calling an Overlay or PEISlam routine +:PEIOrOverlay + lda :curr_type + bne :overlay + + ldx :curr_top + ldy :curr_bottom + jmp _PEISlam +:overlay + lda _Sprites+OVERLAY_PROC,x + stal :disp+1 + lda _Sprites+OVERLAY_PROC+1,x + stal :disp+2 + + lda ScreenY0 ; pass the address of the first line of the overlay + clc + adc _Sprites+OVERLAY_TOP,x + asl + tax + lda ScreenAddr,x + clc + adc ScreenX0 +:disp jsl $000000 + rts + + _DrawComplementList ldx #0 diff --git a/src/Tool.s b/src/Tool.s index 642247c..d442127 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -698,6 +698,11 @@ _TSStartScript _TSExit #0;#6 ; SetOverlay(top, bottom, proc) +; +; Overlays are handled as quasi-sprites. They need to be included in the y-sorted list of "stuff", but they are not drawn like +; sprites. As such, they set a special flag in the SPRITE_ID field which allows them to be ignored for other purposes. Also, +; they are not added into the "normal" sprite range on 0 - 15, but are stored in locations 16 and up to further seggregate them from +; the rest of the system. A lot of the SPRITE_* locations are repurposed for Overlay-specific information. _TSSetOverlay :proc equ FirstParam+0 :bottom equ FirstParam+4 @@ -705,9 +710,40 @@ _TSSetOverlay _TSEntry - lda #1 - sta Overlays - stz Overlays+OVERLAY_FLAGS + ldx #0 ; Always just use the first spot + lda #SPRITE_OVERLAY + sta Overlays+OVERLAY_ID,x + stz Overlays+OVERLAY_FLAGS,x + + lda :top,s + sta Overlays+OVERLAY_TOP,x + lda :bottom,s + dec + sta Overlays+OVERLAY_BOTTOM,x + sec + sbc :top,s + inc + sta Overlays+OVERLAY_HEIGHT,x + + lda :proc,s + sta Overlays+OVERLAY_PROC,x + lda :proc+2,s + sta Overlays+OVERLAY_PROC+2,x + + ldx #{MAX_SPRITES+0}*2 ; Adjust to call the generic routings + jsr _InsertSprite + + _TSExit #0;#8 + +_TSUpdateOverlay +:proc equ FirstParam+0 +:bottom equ FirstParam+4 +:top equ FirstParam+6 + + _TSEntry + + ldx #0 + stz Overlays+OVERLAY_FLAGS,x lda :top,s sta Overlays+OVERLAY_TOP lda :bottom,s diff --git a/src/static/TileStore.s b/src/static/TileStore.s index 5951097..0042dca 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -27,7 +27,7 @@ DirtyTiles ENT ds \,$00 ; pad to the next page boundary _Sprites ENT - ds SPRITE_REC_SIZE*MAX_SPRITES + ds SPRITE_REC_SIZE*MAX_ELEMENTS ;------------------------------------------------------------------------------------- ; @@ -389,9 +389,10 @@ OldOneSecVec ENT ds 4 Timers ENT ds TIMER_REC_SIZE*MAX_TIMERS -Overlays ENT - dw 0 ; count - ds 10 ; only support one for now (flags, start_line, end_line, function call) + +;Overlays ENT +; dw 0 ; count +; ds 10 ; only support one for now (flags, start_line, end_line, function call) ; From the IIgs ref DefaultPalette ENT @@ -513,17 +514,17 @@ _SortedHead ENT _ShadowListCount ENT ds 2 _ShadowListTop ENT - ds {2*{MAX_SPRITES+1}} ; space for all of the sprites + overlay range + ds {2*{MAX_ELEMENTS}} ; space for all of the sprites + overlay range _ShadowListBottom ENT - ds {2*{MAX_SPRITES+1}} + ds {2*{MAX_ELEMENTS}} ; Complement of the Shadow List. Can have one more segment than that list _DirectListCount ENT ds 2 _DirectListTop ENT - ds {2*{MAX_SPRITES+2}} + ds {2*{MAX_ELEMENTS+1}} _DirectListBottom ENT - ds {2*{MAX_SPRITES+2}} + ds {2*{MAX_ELEMENTS+1}} ; Steps to the different sprite stamps diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index 5dccf78..6d300bf 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -28,6 +28,9 @@ TILE_STORE_NUM equ 12 ; Need this many parallel arra MAX_SPRITES equ 16 SPRITE_REC_SIZE equ 42 +MAX_OVERLAYS equ 2 +MAX_ELEMENTS equ {MAX_SPRITES+MAX_OVERLAYS} + ; Mark each sprite as ADDED, UPDATED, MOVED, REMOVED depending on the actions applied to it ; on this frame. Quick note, the same Sprite ID cannot be removed and added in the same frame. ; A REMOVED sprite if removed from the sprite list during the Render call, so it's ID is not @@ -41,28 +44,39 @@ SPRITE_STATUS_REMOVED equ $0008 ; Sprite has been removed. SPRITE_STATUS_HIDDEN equ $0010 ; Sprite is in a hidden state ; These values are set by the user -SPRITE_STATUS equ {MAX_SPRITES*0} -SPRITE_ID equ {MAX_SPRITES*2} -SPRITE_X equ {MAX_SPRITES*4} -SPRITE_Y equ {MAX_SPRITES*6} -VBUFF_ADDR equ {MAX_SPRITES*8} ; Base address of the sprite's stamp in the data/mask banks +SPRITE_STATUS equ {MAX_ELEMENTS*0} +SPRITE_ID equ {MAX_ELEMENTS*2} +SPRITE_X equ {MAX_ELEMENTS*4} +SPRITE_Y equ {MAX_ELEMENTS*6} +VBUFF_ADDR equ {MAX_ELEMENTS*8} ; Base address of the sprite's stamp in the data/mask banks ; These values are cached / calculated during the rendering process -TS_LOOKUP_INDEX equ {MAX_SPRITES*10} ; The index from the TileStoreLookup table that corresponds to the top-left corner of the sprite -TS_COVERAGE_SIZE equ {MAX_SPRITES*12} ; Representation of how many TileStore tiles (NxM) are covered by this sprite -SPRITE_DISP equ {MAX_SPRITES*14} ; Cached address of the specific stamp based on sprite flags -SPRITE_CLIP_LEFT equ {MAX_SPRITES*16} -SPRITE_CLIP_RIGHT equ {MAX_SPRITES*18} -SPRITE_CLIP_TOP equ {MAX_SPRITES*20} -SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*22} -IS_OFF_SCREEN equ {MAX_SPRITES*24} -SPRITE_WIDTH equ {MAX_SPRITES*26} -SPRITE_HEIGHT equ {MAX_SPRITES*28} -SPRITE_CLIP_WIDTH equ {MAX_SPRITES*30} -SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*32} -TS_VBUFF_BASE equ {MAX_SPRITES*34} ; Finalized VBUFF address based on the sprite position and tile offsets -SORTED_PREV equ {MAX_SPRITES*36} ; Doubly-Linked List that maintains the sprites in sorted order based on SPRITE_Y -SORTED_NEXT equ {MAX_SPRITES*38} +TS_LOOKUP_INDEX equ {MAX_ELEMENTS*10} ; The index from the TileStoreLookup table that corresponds to the top-left corner of the sprite +TS_COVERAGE_SIZE equ {MAX_ELEMENTS*12} ; Representation of how many TileStore tiles (NxM) are covered by this sprite +SPRITE_DISP equ {MAX_ELEMENTS*14} ; Cached address of the specific stamp based on sprite flags +SPRITE_CLIP_LEFT equ {MAX_ELEMENTS*16} +SPRITE_CLIP_RIGHT equ {MAX_ELEMENTS*18} +SPRITE_CLIP_TOP equ {MAX_ELEMENTS*20} +SPRITE_CLIP_BOTTOM equ {MAX_ELEMENTS*22} +IS_OFF_SCREEN equ {MAX_ELEMENTS*24} +SPRITE_WIDTH equ {MAX_ELEMENTS*26} +SPRITE_HEIGHT equ {MAX_ELEMENTS*28} +SPRITE_CLIP_WIDTH equ {MAX_ELEMENTS*30} +SPRITE_CLIP_HEIGHT equ {MAX_ELEMENTS*32} +TS_VBUFF_BASE equ {MAX_ELEMENTS*34} ; Finalized VBUFF address based on the sprite position and tile offsets +SORTED_PREV equ {MAX_ELEMENTS*36} ; Doubly-Linked List that maintains the sprites in sorted order based on SPRITE_Y +SORTED_NEXT equ {MAX_ELEMENTS*38} + +; The Overlays are part of the _Sprites memory space and come after the maximum number of sprites +Overlays equ {_Sprites+{MAX_SPRITES*2}} + +; Aliases of SPRITE_* memory locations that are used for Overlay info when the SPRITE_OVERLAY bit is set on SPRITE_ID +OVERLAY_ID equ SPRITE_ID +OVERLAY_FLAGS equ SPRITE_STATUS +OVERLAY_TOP equ SPRITE_CLIP_TOP ; This is important because SPRITE_CLIP_TOP is used for sorting +OVERLAY_BOTTOM equ SPRITE_CLIP_BOTTOM +OVERLAY_HEIGHT equ SPRITE_HEIGHT +OVERLAY_PROC equ VBUFF_ADDR ; 52 rows by 82 columns + 2 extra rows and columns for sprite sizes ;