From e607612344db23ae69c689ebe5e303589fe0574c Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Thu, 19 Aug 2021 01:22:36 -0500 Subject: [PATCH] Add initial build-out of BG1 tile map support --- macros/APP.MACS.S | 5 +- src/Actions.s | 13 +- src/App.Main.s | 31 +++-- src/Render.s | 17 ++- src/TileMap.s | 277 +++++++++++++++++++++++++++++++++++++++++- src/blitter/BG1.s | 12 +- src/blitter/Blitter.s | 11 +- src/blitter/Tables.s | 6 +- 8 files changed, 341 insertions(+), 31 deletions(-) diff --git a/macros/APP.MACS.S b/macros/APP.MACS.S index 07964fb..4a22059 100644 --- a/macros/APP.MACS.S +++ b/macros/APP.MACS.S @@ -145,7 +145,7 @@ mixed cmp #$FFFF ; All 1's in the mask is fully transparent ldx _X_REG ; Get the addressing offset ldal JTableOffset,x ; Get the address offset and add to the base address - clc ; may be able to be removed... +; clc ; may be able to be removed... adc _BASE_ADDR ; of the current code field line adc #{]3&$F000} ; adjust for the current row offset sta: ]3+1,y @@ -175,3 +175,6 @@ transparent next eom + + + diff --git a/src/Actions.s b/src/Actions.s index bed6f11..ddc25d0 100644 --- a/src/Actions.s +++ b/src/Actions.s @@ -3,9 +3,9 @@ MoveLeft adc StartX ; Increment the virtual X-position jsr SetBG0XPos -; lda StartX -; lsr -; jsr SetBG1XPos + lda StartX + lsr + jsr SetBG1XPos jsr Render rts @@ -19,9 +19,9 @@ MoveRight lda #0 jsr SetBG0XPos -; lda StartX -; lsr -; jsr SetBG1XPos + lda StartX + lsr + jsr SetBG1XPos jsr Render pla @@ -294,3 +294,4 @@ _DoTimers + diff --git a/src/App.Main.s b/src/App.Main.s index f8e38f7..d8c5bb0 100644 --- a/src/App.Main.s +++ b/src/App.Main.s @@ -128,13 +128,7 @@ NO_MUSIC equ 1 ; turn music + tool load ; Set up our level data jsr BG0SetUp - - ldx #0 - ldy #0 - lda #56 - jsr CopyTile -; jsr Render - jsr WaitForKey + jsr BG1SetUp ; Allocate room to load data @@ -142,13 +136,17 @@ NO_MUSIC equ 1 ; turn music + tool load sta BankLoad ; Store "Bank Pointer" jsr MovePlayerToOrigin ; Put the player at the beginning of the map + lda #74 + jsr SetBG0XPos lda #DIRTY_BIT_BG0_REFRESH ; Redraw all of the tiles on the next Render + ora #DIRTY_BIT_BG1_REFRESH tsb DirtyBits ; jsr DoTiles ; jsr DoLoadBG1 ; jsr Demo + jsr Render EvtLoop jsr ReadControl and #$007F ; Ignore the buttons for now @@ -273,6 +271,8 @@ Fatal brk $00 MovePlayerToOrigin lda #0 ; Set the player's position jsr SetBG0XPos + lda #0 + jsr SetBG1XPos lda TileMapHeight asl @@ -280,7 +280,10 @@ MovePlayerToOrigin asl sec sbc ScreenHeight + pha jsr SetBG0YPos + pla + jsr SetBG1YPos rts @@ -397,10 +400,6 @@ DoTiles lda :tile,s jsr CopyTile -; lda :tile,s -; eor #$0003 -; sta :tile,s - lda :column,s inc sta :column,s @@ -1298,3 +1297,13 @@ qtRec adrl $0000 PUT TileMap.s PUT App.TileMapBG0.s PUT App.TileMapBG1.s + + + + + + + + + + diff --git a/src/Render.s b/src/Render.s index 7eed5d5..b24f3b7 100644 --- a/src/Render.s +++ b/src/Render.s @@ -65,17 +65,18 @@ Render ; used in all of the other loops jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen + jsr _ApplyBG1YPos ; _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 _UpdateBG0TileMap +; jsr _UpdateBG1TileMap jsr _ApplyBG0XPos ; Patch the PEA instructions with exit BRA opcode - -; jsr _ApplyBG1YPos ; Adjust the index values into the BG1 bank buffer -; jsr _ApplyBG1XPos ; Adjust the direct page pointers to the BG1 bank + jsr _ApplyBG1XPos ; Patch the PEA instructions with exit BRA opcode ; Copy any tiles that have come into view @@ -112,6 +113,11 @@ Render lda StartX sta OldStartX + lda BG1StartY + sta OldBG1StartY + lda BG1StartX + sta OldBG1StartX + stz DirtyBits rts @@ -129,5 +135,10 @@ Render + + + + + diff --git a/src/TileMap.s b/src/TileMap.s index 7d4e294..791d1fe 100644 --- a/src/TileMap.s +++ b/src/TileMap.s @@ -230,7 +230,6 @@ _UpdateBG0TileMap ; This is a private subroutine that draws in tiles into the code fields using the ; data from the tilemap and the local :Top, :Left, :Bottom and :Right parameters. :DrawRectBG0 - lda :Bottom sec sbc :Top @@ -383,3 +382,279 @@ _UpdateBG0TileMap bne :loop rts + +; Exact same logic as _UpdateBG0TileMap, except for the other background +_UpdateBG1TileMap +:Left equ tmp0 +:Right equ tmp1 +:Top equ tmp2 +:Bottom equ tmp3 + + lda BG1StartY ; calculate the tile index of the current location + lsr + lsr + lsr + sta BG1TileOriginY + + lda OldBG1StartY + lsr + lsr + lsr + sta OldBG1TileOriginY + + lda BG1StartX + lsr + lsr + sta BG1TileOriginX + + lda OldBG1StartX + lsr + lsr + sta OldBG1TileOriginX + +; Figure out the two rectangular regions that need to be updated. + + stz :Left ; prepare to do the entire screen + lda ScreenTileWidth ; and then whack off the parts + sta :Right ; that are not needed + + stz :Top ; since the ranges are inclusive, we are + lda ScreenTileHeight ; always going to be drawing width+1 tiles + sta :Bottom ; which takes care of edge tiles. + +; If we are supposed to refresh the whole field, just do that and return + + lda #DIRTY_BIT_BG1_REFRESH + bit DirtyBits + beq :NoRefresh + trb DirtyBits ; Clear the dirty bit +:FullScreen jmp _DrawRectBG1 ; Let the DrawRectBG1 RTS take care of the return for us + +:NoRefresh + lda BG1TileOriginY + cmp OldBG1TileOriginY + beq :NoYUpdate ; if equal, don't change Y + + sec + sbc OldBG1TileOriginY ; find the difference; D = Y_new - Y_old + bpl :DoBottom ; if we scrolled up, fill in the bottom row(s) + + eor #$FFFF ; if we scrolled down, Y_new < Y_old and we need + cmp :Bottom ; to fill in the top row(s) from 0 to Y_new - Y_old - 1 + bcs :FullScreen ; If the displacement was very large, just fill in the whole screen + + sta :Bottom + bra :DoYUpdate + +:DoBottom + eor #$FFFF ; same explanation as above, except we are filling in from + inc ; Bottom - (Y_new - Y_old) to Bottom + clc + adc ScreenTileHeight + bmi :FullScreen + sta :Top + +:DoYUpdate + jsr _DrawRectBG1 ; Fill in the rectangle. + +; We performed an update in the Y-direction, so now change the bounds so +; an update in the X-direction will not draw too many rows + + lda :Top + beq :drewTop + dec ; already did Y to HEIGHT, so only need to draw from + sta :Bottom ; 0 to (Y-1) for any horizontal updates + stz :Top + bra :NoYUpdate + +:drewTop + lda :Bottom ; opposite, did 0 to Y + inc ; so do Y+1 to HEIGHT + sta :Top + lda ScreenTileHeight + sta :Bottom + +; The Top and Bottom are set the the correct values to draw in whatever potential range of tiles +; need to be draws if there was any horizontal displacement +:NoYUpdate + lda BG1TileOriginX ; Did the first column of the tile map change from before? + cmp OldBG1TileOriginX ; Did it change from before? + beq :NoXUpdate ; no, so we can ignore this + + sec + sbc OldBG1TileOriginX ; find the difference + bpl :DoRightSide ; did we move in a pos or neg? + +; Handle the two sides in an analagous way as the vertical code + + eor #$FFFF + cmp :Right + bcs :FullScreen + sta :Right + bra :DoXUpdate + +:DoRightSide + eor #$FFFF + inc + clc + adc ScreenTileWidth + bmi :FullScreen + sta :Left + +:DoXUpdate + jsr _DrawRectBG1 ; Fill in the rectangle. + +:NoXUpdate + rts + +; This is a private subroutine that draws in tiles into the code fields using the +; data from the tilemap and the local :Top, :Left, :Bottom and :Right parameters. +_DrawRectBG1 +:Left equ tmp0 +:Right equ tmp1 +:Top equ tmp2 +:Bottom equ tmp3 + +:Width equ tmp4 +:Height equ tmp5 + +:MulA equ tmp6 ; Scratch space for multiplication +:MulB equ tmp7 + +:Offset equ tmp8 ; Address offset into the tilemap +:Span equ tmp9 + +:GlobalTileIdxX equ tmp10 +:GlobalTileIdxY equ tmp11 + +:BlkX equ tmp12 +:BlkY equ tmp13 + + lda :Bottom + sec + sbc :Top + inc + sta :Height ; Maximum value of 26 (top = 0, bottom = 25) + + lda :Right + sec + sbc :Left + inc + sta :Width ; Maximum value of 41 (left = 0, right = 40) + +; Compute the offset into the tile array of the top-left corner + + lda :Left + clc + adc BG1TileOriginX + sta :GlobalTileIdxX + + lda :Top + clc + adc BG1TileOriginY ; This is the global verical index + sta :GlobalTileIdxY + + ldx BG1TileMapWidth + jsr :MulAX + clc + adc :GlobalTileIdxX + asl ; Double for word sizes + sta :Offset ; Stash the pointer offset in Y + +; Draw the tiles + lda BG1TileMapWidth + sec + sbc :Width + asl ; This is the number of bytes to move the Offset to advance from the end of + sta :Span ; one line to the beginning of the next + +; Now we need to figure out the code field tile coordinate of corner of +; play field. That is, becuase the screen is scrolling, the location of +; tile (0, 0) could be anywhere within the code field + + lda BG1StartYMod208 ; This is the code field line that is at the top of the screen + and #$FFF8 ; Clamp to the nearest block + lsr + lsr + lsr ; Could optimize because the Tile code shifts back.... + clc + adc :Top + cmp #MAX_TILE_Y+1 ; Top can be less than or equal to 25 + bcc *+5 + sbc #MAX_TILE_Y+1 + sta :BlkY ; This is the Y-block we start drawing from + + lda BG1StartXMod164 ; Do the same thing for X, except only need to clamp by 4 + and #$FFFC + lsr + lsr + clc + adc :Left + cmp #MAX_TILE_X+1 ; Left can be less than or equal to 40 + bcc *+5 + sbc #MAX_TILE_X+1 + sta :BlkX + +; Call the copy tile routine to blit the tile data into the playfield +; +; A = Tile ID (0 - 1023) +; X = Tile column (0 - 40) +; Y = Tile row (0 - 25) + + pei :BlkX ; cache the starting X-block index to restore later + pei :Width ; cache the Width value to restore later +:yloop +:xloop + ldy :Offset ; Set up the arguments and call the tile blitter + lda [BG1TileMapPtr],y + iny ; pre-increment the address. A bit faster than two "INC DP" instructions + iny + sty :Offset + + ldx :BlkX + ldy :BlkY + jsr CopyTileBG1 + + lda :BlkX + inc + cmp #MAX_TILE_X+1 ; If we go past the maximum block index, wrap around + bcc *+5 + lda #0 + sta :BlkX + + dec :Width ; Decrement out count + bne :xloop + + lda :Offset ; Move to the next line of the Tile Map + clc + adc :Span + sta :Offset + + lda 3,s ; Reset the BlkX + sta :BlkX + + lda 1,s ; Reset the width + sta :Width + + lda :BlkY ; The y lookup has a double-length array, may not need the bounds check + inc + cmp #MAX_TILE_Y+1 + bcc *+5 + lda #0 + sta :BlkY + + dec :Height ; Have we done all of the rows? + bne :yloop + + pla ; Pop off cached values + pla + + rts + + + + + + + + diff --git a/src/blitter/BG1.s b/src/blitter/BG1.s index d3f8b5c..24c4b67 100644 --- a/src/blitter/BG1.s +++ b/src/blitter/BG1.s @@ -40,14 +40,16 @@ SetBG1YPos ; these are all direct page values ; ; Note: This routine can be optimized as an unrolled loop of PEI instructions -_ApplyBG1XPos - lda BG1StartX +_ApplyBG1XPosPre + lda BG1StartX ; This is the starting byte offset (0 - 163) jsr Mod164 sta BG1StartXMod164 + rts +_ApplyBG1XPos lda #162 sec - sbc StartXMod164 + sbc StartXMod164 ; Need to compensate for both BG0 and BG1 positions bpl *+6 clc adc #164 @@ -609,3 +611,7 @@ ApplyBG1OffsetValues :none rts BG1YCache ds 32 + + + + diff --git a/src/blitter/Blitter.s b/src/blitter/Blitter.s index e8ab5a8..654f2e3 100644 --- a/src/blitter/Blitter.s +++ b/src/blitter/Blitter.s @@ -54,11 +54,11 @@ _BltRange ; Now we need to set up the Bank, Stack Pointer and Direct Page registers for calling into ; the code field - lda StartX - bit #$01 - beq :primary - lda BG1AltBank - bra :alt +; lda StartX +; bit #$01 +; beq :primary +; lda BG1AltBank +; bra :alt :primary lda BG1DataBank :alt pha @@ -95,3 +95,4 @@ stk_save lda #0000 ; load the stack ; we don't do anything sprite related; just call function pointers provided to us. _RenderSprites rts + diff --git a/src/blitter/Tables.s b/src/blitter/Tables.s index c544c4a..f3aab05 100644 --- a/src/blitter/Tables.s +++ b/src/blitter/Tables.s @@ -27,7 +27,7 @@ Col2CodeOffset lup 82 dw CODE_TOP+{{81-]step}*PER_TILE_SIZE} ]step equ ]step+1 --^ -; dw CODE_TOP + dw CODE_TOP+{81*PER_TILE_SIZE} ; A parallel table to Col2CodeOffset that holds the offset to the exception handler address for each column SNIPPET_SIZE equ 32 @@ -37,6 +37,7 @@ JTableOffset lup 82 dw SNIPPET_BASE+{{81-]step}*SNIPPET_SIZE} ]step equ ]step+1 --^ + dw SNIPPET_BASE+{81*SNIPPET_SIZE} ; Table of BRA instructions that are used to exit the code field. Separate tables for ; even and odd aligned cases. @@ -252,3 +253,6 @@ BG1YTable lup 208 BG1YOffsetTable lup 26 dw 1,1,1,2,2,2,2,2,1,1,1,0,0,0,0,0 --^ + + +