From b449d983ee2dcec48d2a3c89e2dc75deeddaa156 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Wed, 26 Apr 2023 00:41:17 -0500 Subject: [PATCH] Checkpoint for WIP scanline render fix for overlays+sprites and user-defined tiles --- ORCACDefs/gte.h | 2 +- macros/CORE.MACS.S | 26 +++++++++++++++++++++++++ macros/GTE.Macs.s | 5 ++++- src/Defs.s | 17 +++++++++------- src/README.md | 40 ++++++++++++++++++++++++++++++++++++++ src/SpriteRender.s | 4 ++++ src/Tiles.s | 30 ++++++++++++++++++++++++++-- src/Tool.s | 14 ++++++++++++- src/blitter/Blitter.s | 5 +++++ src/blitter/PEISlammer.s | 19 ++++++++++++------ src/blitter/Rotation.s | 7 ++++++- src/static/TileStore.s | 9 +++++++++ src/static/TileStoreDefs.s | 7 +++++++ 13 files changed, 166 insertions(+), 19 deletions(-) diff --git a/ORCACDefs/gte.h b/ORCACDefs/gte.h index 9b294f7..a46f743 100644 --- a/ORCACDefs/gte.h +++ b/ORCACDefs/gte.h @@ -155,7 +155,7 @@ extern pascal Word GTEClearOverlay() inline(0x23A0, tool_dispatcher); /* GTE Tile Constants */ #define TILE_PRIORITY_BIT 0x4000 /* Put tile on top of sprite */ -#define TILE_FRINGE_BIT 0x2000 /* Unused */ +#define TILE_USER_BIT 0x2000 /* User-defined tile */ #define TILE_SOLID_BIT 0x1000 /* Hint bit used in TWO_LAYER_MODE to optimize rendering */ #define TILE_DYN_BIT 0x0800 /* Is this a Dynamic Tile? */ #define TILE_VFLIP_BIT 0x0400 diff --git a/macros/CORE.MACS.S b/macros/CORE.MACS.S index 82c0a3c..7c1b77c 100644 --- a/macros/CORE.MACS.S +++ b/macros/CORE.MACS.S @@ -93,6 +93,16 @@ _PullReg2 mac pla <<< +phxy mac + phx + phy + <<< + +plyx mac + ply + plx + <<< + jne mac beq *+5 jmp ]1 @@ -103,6 +113,16 @@ jeq mac jmp ]1 <<< +jmi mac + bpl *+5 + jmp ]1 + <<< + +jpl mac + bmi *+5 + jmp ]1 + <<< + jcc mac bcs *+5 jmp ]1 @@ -113,6 +133,12 @@ jcs mac jmp ]1 <<< +max mac + cmp ]1 + bcs mout + lda ]1 +mout <<< + min mac cmp ]1 bcc mout diff --git a/macros/GTE.Macs.s b/macros/GTE.Macs.s index a022808..5e5cb27 100644 --- a/macros/GTE.Macs.s +++ b/macros/GTE.Macs.s @@ -135,6 +135,9 @@ _GTECompileSpriteStamp MAC _GTESetAddress MAC UserTool $2E00+GTEToolNum <<< +_GTEUpdateOverlay MAC + UserTool $2F00+GTEToolNum + <<< ; EngineMode definitions ; Script definition @@ -183,7 +186,7 @@ COPY_PIC_SCANLINE equ $0001 ; Copy in a way to support BG1 + RENDER_ ; Tile constants ; TILE_RESERVED_BIT equ $8000 TILE_PRIORITY_BIT equ $4000 ; Put tile on top of sprite -TILE_FRINGE_BIT equ $2000 ; Unused +TILE_USER_BIT equ $2000 ; User-defined tile. Execute registered callback. TILE_SOLID_BIT equ $1000 ; Hint bit used in TWO_LAYER_MODE to optimize rendering TILE_DYN_BIT equ $0800 ; Is this a Dynamic Tile? TILE_VFLIP_BIT equ $0400 diff --git a/src/Defs.s b/src/Defs.s index 3d48e3d..4747c96 100644 --- a/src/Defs.s +++ b/src/Defs.s @@ -179,12 +179,6 @@ RENDER_PER_SCANLINE equ $0010 RENDER_WITH_SHADOWING equ $0020 RENDER_SPRITES_SORTED equ $0040 ; Draw the sprites in y-sorted order. Otherwise, use the index. -; Overlay flags -OVERLAY_MASKED equ $0000 ; Overlay has a mask, so the background must be draw first -OVERLAY_SOLID equ $8000 ; Overlay covers the scan line and is fully opaque -OVERLAY_ABOVE equ $0000 ; Overlay is drawn above scanline sprites -OVERLAY_BELOW equ $4000 ; Overlay is drawn below scanline sprites - ; DirtyBits definitions DIRTY_BIT_BG0_X equ $0001 DIRTY_BIT_BG0_Y equ $0002 @@ -197,6 +191,7 @@ DIRTY_BIT_SPRITE_ARRAY equ $0040 ; GetAddress table IDs scanlineHorzOffset equ $0001 ; Table of 416 words, a double-array of scanline offset values. Values must be in range [0, 163] scanlineHorzOffset2 equ $0002 ; Table of 416 words, a double-array of scanline offset values. Values must be in range [0, 163] +tileStore equ $0003 ; CopyPicToBG1 flags COPY_PIC_NORMAL equ $0000 ; Copy into BG1 buffer in "normal mode" treating the buffer as a 164x208 pixmap with stride of 256 @@ -239,6 +234,12 @@ SPRITE_8X8 equ $0000 ; 8 pixels wide x 8 pixels SPRITE_VFLIP equ $0400 ; Flip the sprite vertically SPRITE_HFLIP equ $0200 ; Flip the sprite horizontally +; Overlay bits (aliases of SPRITE_ID bits) +OVERLAY_MASKED equ $0000 ; Overlay has a mask, so the background must be draw first +OVERLAY_SOLID equ $4000 ; Overlay covers the scan line and is fully opaque +OVERLAY_ABOVE equ $0000 ; Overlay is drawn above scanline sprites +OVERLAY_BELOW equ $2000 ; Overlay is drawn below scanline sprites + ; Stamp storage parameters VBUFF_STRIDE_BYTES equ {12*4} ; Each line has 4 slots of 16 pixels + 8 buffer pixels VBUFF_TILE_ROW_BYTES equ {8*VBUFF_STRIDE_BYTES} ; Each row is comprised of 8 lines @@ -299,7 +300,9 @@ _ShadowListBottom EXT _DirectListCount EXT _DirectListTop EXT _DirectListBottom EXT - +ObjectListCount EXT +ObjectListHead EXT +ObjectList EXT StartXMod164Tbl EXT LastOffsetTbl EXT BG1StartXMod164Tbl EXT diff --git a/src/README.md b/src/README.md index 6ec0573..af8ae01 100644 --- a/src/README.md +++ b/src/README.md @@ -93,3 +93,43 @@ Possibilities lda 0000,y lda 0002,y ... + += HOWTO: Custom tiles = + + pea #^MyTileRenderer ; Define the callback function + pea #MyTileRenderer + _GTESetCustomTileCallback + + pea 0 ; Put tiles with the TILE_USER_BIT set + pea 0 + pea TILE_ID+TILE_USER_BIT + _GTESetTile + +; On entry +; +; A = Tile Id. Use this to select what to draw +; Y = Address of tile in the code field +; X = Tile Store array offset +; B = code field bank +; +; Use absolute addressing to fill in the code bank and address offsets $0000, $0003, $1000, $1003, ..., $7000, $7003. If +; code needs to create snippets, it can be loaded from the tile store array by using the address returned from +; GTEGetAddress(tileStoreJmpAddr) +; +; User-defined tiles are always marked as damaged. + +MyTileRenderer + lda #00A3 ; Inserts LDA 00,s / PHA code to show a static background from Bank 0 + sta: $0000,y + sta: $0003,y + sta: $1000,y + sta: $1003,y + ... + lda #$4800 + sta: $0001,y + sta: $0004,y + sta: $1001,y + sta: $1004,y + ... + rtl ; Must do a long return + \ No newline at end of file diff --git a/src/SpriteRender.s b/src/SpriteRender.s index 772aa78..aaf8b57 100644 --- a/src/SpriteRender.s +++ b/src/SpriteRender.s @@ -147,6 +147,10 @@ _DrawStampToScreen lda _Sprites+IS_OFF_SCREEN,x ; If the sprite is off-screen, don't draw it bne _DSTSOut + lda _Sprites+SPRITE_ID,x ; If the sprite is hidden or an overlay, don't draw it + bit #SPRITE_OVERLAY+SPRITE_HIDE + bne _DSTSOut + lda _Sprites+SPRITE_CLIP_WIDTH,x ; If the sprite is clipped to the playfield, don't draw it cmp _Sprites+SPRITE_WIDTH,x bne _DSTSOut diff --git a/src/Tiles.s b/src/Tiles.s index 2a3a9fc..d109938 100644 --- a/src/Tiles.s +++ b/src/Tiles.s @@ -374,6 +374,12 @@ _SetTile :changed sta oldTileId lda newTileId sta TileStore+TS_TILE_ID,x ; Value is different, store it. + +; If the user bit is set, then skip most of the setup and just fill in the TileProcs with the user callback +; target + bit #TILE_USER_BIT + bne :set_user_tile + jsr _GetTileAddr sta TileStore+TS_TILE_ADDR,x ; Committed to drawing this tile, so get the address of the tile in the tiledata bank for later @@ -397,6 +403,26 @@ _SetTile jsr _SetNormalTileProcs jmp _PushDirtyTileX +:set_user_tile + lda #UserTileDispatch + stal K_TS_BASE_TILE_DISP,x + lda #UserTileDispatch + stal K_TS_SPRITE_TILE_DISP,x + lda #UserTileDispatch + stal K_TS_ONE_SPRITE,x + jmp _PushDirtyTileX + +; Trampoline / Dispatch table for user-defined tiles. If the user calls the GTESetCustomTileCallback toolbox routine, +; then this value is patched out. Calling GTESetCustomTileCallback with NULL will disconnect the user's routine. +UserTileDispatch + ldal TileStore+TS_TILE_ID,x ; Replace the tile data address (unset) with the tile id +_UTDPatch jsl UserHook1 ; Call the users code + plb ; Restore the data bank + rts + +; Stub to have a valid address for initialization / reset +UserHook1 rtl + ; X = Tile Store offset ; Y = Engine Mode Base Table address ; A = Table proc index @@ -745,7 +771,7 @@ b_15_3 endbit 15;3;]4 ; Store some tables in the K bank that will be used exclusively for jmp (abs,x) dispatch K_TS_BASE_TILE_DISP ds TILE_STORE_SIZE ; draw the tile without a sprite -K_TS_COPY_TILE_DATA ds TILE_STORE_SIZE ; copy/merge the tile into temp storage +;K_TS_COPY_TILE_DATA ds TILE_STORE_SIZE ; copy/merge the tile into temp storage K_TS_SPRITE_TILE_DISP ds TILE_STORE_SIZE ; select the sprite routine for this tile K_TS_ONE_SPRITE ds TILE_STORE_SIZE ; specialized sprite routine when only one sprite covers the tile -K_TS_APPLY_TILE_DATA ds TILE_STORE_SIZE ; move tile from temp storage into code field \ No newline at end of file +;K_TS_APPLY_TILE_DATA ds TILE_STORE_SIZE ; move tile from temp storage into code field \ No newline at end of file diff --git a/src/Tool.s b/src/Tool.s index 4c03deb..45bfb00 100644 --- a/src/Tool.s +++ b/src/Tool.s @@ -100,6 +100,7 @@ _CallTable adrl _TSCompileSpriteStamp-1 adrl _TSSetAddress-1 + adrl _TSUpdateOverlay-1 _CTEnd _GTEAddSprite MAC @@ -721,6 +722,7 @@ _TSSetOverlay ldx #0 ; Always just use the first spot lda #SPRITE_OVERLAY +; lda #SPRITE_OVERLAY+SPRITE_HIDE ; Type 2 overlays re-use the HIDE bit sta Overlays+OVERLAY_ID,x stz Overlays+OVERLAY_FLAGS,x @@ -739,7 +741,7 @@ _TSSetOverlay lda :proc+2,s sta Overlays+OVERLAY_PROC+2,x - ldx #{MAX_SPRITES+0}*2 ; Adjust to call the generic routings + ldx #{MAX_SPRITES+0}*2 ; Adjust to call the generic routine jsr _InsertSprite _TSExit #0;#8 @@ -753,15 +755,25 @@ _TSUpdateOverlay ldx #0 stz Overlays+OVERLAY_FLAGS,x + lda :top,s sta Overlays+OVERLAY_TOP lda :bottom,s + dec sta Overlays+OVERLAY_BOTTOM + sec + sbc :top,s + inc + sta Overlays+OVERLAY_HEIGHT,x + lda :proc,s sta Overlays+OVERLAY_PROC lda :proc+2,s sta Overlays+OVERLAY_PROC+2 + ldx #{MAX_SPRITES+0}*2 ; Adjust to call the generic routings + jsr _UpdateSprite + _TSExit #0;#8 ; ClearOverlay() diff --git a/src/blitter/Blitter.s b/src/blitter/Blitter.s index 25a5c11..bf5419f 100644 --- a/src/blitter/Blitter.s +++ b/src/blitter/Blitter.s @@ -13,6 +13,11 @@ _BltRange :exit_ptr equ tmp0 :jmp_low_save equ tmp2 + sty :jmp_low_save ; Steal some temp space and check for empty ranges + cpx :jmp_low_save ; This makes it simpler for some callers + bcc *+3 + rts + phb ; preserve the bank register clc` diff --git a/src/blitter/PEISlammer.s b/src/blitter/PEISlammer.s index b6c084c..4f1d199 100644 --- a/src/blitter/PEISlammer.s +++ b/src/blitter/PEISlammer.s @@ -13,19 +13,26 @@ ; X = first line (inclusive), valid range of 0 to 199 ; Y = last line (exclusive), valid range >X up to 200 _PEISlam - cpx #201 - bcc *+4 - brk $A9 + cpx #200 + bcc *+3 + rts cpy #201 - bcc *+4 - brk $A8 + bcc *+3 + rts + + txa + stal :screen_width_1 + tya + cmpl :screen_width_1 + bcs *+3 + rts + lda ScreenWidth dec stal :screen_width_1 ; save the width-1 outside of the direct page lda #:pei_end ; patch the PEI entry address - and #$FFFE ; should always be even, but.... sec sbc ScreenWidth stal :inner+1 diff --git a/src/blitter/Rotation.s b/src/blitter/Rotation.s index 6c8b0bc..70a44ad 100644 --- a/src/blitter/Rotation.s +++ b/src/blitter/Rotation.s @@ -6,8 +6,13 @@ ; ; This is about as fast of a rotation as we can do. ; -; When possible, off-screen locations are calculate to produce an address of $FFFE, so that the last two bytes +; When possible, off-screen locations are calculated to produce an address of $FFFE, so that the last two bytes ; of the BG1 data buffer provides the "fill value". +; +; Having a fixed table of addresses is limiting due to the size an inability to control what happens at the +; boundaries. Consider generating some pre-processed step + error parameters that can be used in a fast +; DDA stepper to allow a more compact representation or different (scale, angle) pairs. This could allow for +; a full range of 256 rotation angles + multiple scalings. ANGLEBNK EXT _ApplyBG1XPosAngle diff --git a/src/static/TileStore.s b/src/static/TileStore.s index ee60cee..350482c 100644 --- a/src/static/TileStore.s +++ b/src/static/TileStore.s @@ -407,6 +407,7 @@ OldOneSecVec ENT Timers ENT ds TIMER_REC_SIZE*MAX_TIMERS +; Keep a count of the different overlays ;Overlays ENT ; dw 0 ; count ; ds 10 ; only support one for now (flags, start_line, end_line, function call) @@ -543,6 +544,14 @@ _DirectListTop ENT _DirectListBottom ENT ds {2*{MAX_ELEMENTS+1}} +; List of filtered objects +ObjectListCount ENT + ds 2 +ObjectListHead ENT + ds 2 +ObjectList ENT + ds {10*{MAX_ELEMENTS+2}} ; Extra space at the end for a sentinel marker + ; Steps to the different sprite stamps _stamp_step ENT diff --git a/src/static/TileStoreDefs.s b/src/static/TileStoreDefs.s index 6d300bf..e80c541 100644 --- a/src/static/TileStoreDefs.s +++ b/src/static/TileStoreDefs.s @@ -31,6 +31,13 @@ SPRITE_REC_SIZE equ 42 MAX_OVERLAYS equ 2 MAX_ELEMENTS equ {MAX_SPRITES+MAX_OVERLAYS} +; Object list used in renderer +OL_SPRITE_ID equ 0 ; Usual parallel arrays +OL_SPRITE_TOP equ {2*{MAX_ELEMENTS+1}} +OL_SPRITE_BOTTOM equ {4*{MAX_ELEMENTS+1}} +OL_NEXT equ {6*{MAX_ELEMENTS+1}} +OL_INDEX equ {8*{MAX_ELEMENTS+1}} ; Reference to the index in the _Sprites array + ; 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