Add basic tilemap support back in

This commit is contained in:
Lucas Scharenbroich 2021-08-05 08:20:38 -05:00
parent 19070fa194
commit 1d17b802ad
10 changed files with 389 additions and 265 deletions

View File

@ -276,35 +276,3 @@ _DoTimers
pla
rts

View File

@ -1196,23 +1196,4 @@ qtRec adrl $0000
put blitter/Tiles.s
put blitter/Vert.s
put blitter/BG1.s
; put RotData.s
PUT TileMap.s

View File

@ -64,4 +64,3 @@ tiledata ENT
hex 89ABCDEF
tileend

View File

@ -25,61 +25,5 @@
ASM RotData.s
DS 0
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
; ALI BANK
ALI BANK
SNA RotData

View File

@ -92,14 +92,3 @@ Render
jsr _RestoreBG0Opcodes
rts

325
src/TileMap.s Normal file
View File

@ -0,0 +1,325 @@
; Routines for handling tilemaps
;
; This module contains higher-level functions than the low-level tile rendering routines. The
; goal here is to take a rectangular tilemap data structure and efficiently render it into
; code buffer. Especially inportant is to only draw new tiles as they come into view.
;
; Also, we maintain a tilemap cache to track the current state of the tiles rendered into
; the code field so if, by chance, a tile that comes into view is the same as a tile that
; has already been drawn, then there is no reason to update it. This happen quite often
; in actual games since the primary background is often large empty areas, or runs
; of repeating tiles.
_UpdateBG0TileMap
:Left equ tmp0
:Right equ tmp1
:Top equ tmp2
:Bottom equ tmp3
:Width equ tmp4 ; Used in DrawRectBG0
: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 StartY ; calculate the tile index of the current location
and #$FFF8
lsr
lsr
sta BG0TileOriginY
lda OldStartY
and #$FFF8
lsr
lsr
sta OldBG0TileOriginY
lda StartX
and #$FFF8
lsr
lsr
sta BG0TileOriginX
lda OldStartX
and #$FFF8
lsr
lsr
sta OldBG0TileOriginY
; Figure out the two rectangular regions that need to be updated. We check for changes in Y-direction
; first because it's a bit more efficient to redraw tiles in long horizontal strips, because we do not
; have to skip to different banks.
;
; +---------------------------+----------+ <-- Top
; | | |
; | | New |
; | | |
; | Old Area | (drawn) |
; | | (second) |
; | | |
; +---------------------------+==========|
; | |
; | New Area (drawn first) |
; | |
; +--------------------------------------+ <-- Bottom
; ^ ^
; | |
; +--- Left Right --+
stz :Left ; prepare to do the entire screen
lda ScreenTileWidth ; and then whack off the parts
sta :Right ; that are not needed
stz :Top
lda ScreenTileHeight
sta :Bottom
lda BG0TileOriginY
cmp OldBG0TileOriginY
beq :NoYUpdate ; if equal, don't change Y
sec
sbc OldBG0TileOriginY ; 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
sta :Bottom ; to fill in the top row(s) from 0 to Y_new - Y_old - 1
bra :DoYUpdate
:DoBottom
eor #$FFFF ; same explanation as above, except we are filling in from
inc a ; Bottom - (Y_new - Y_old) to Bottom
clc
adc ScreenTileHeight
sta :Top
:DoYUpdate
jsr :DrawRectBG0 ; 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
;
; +---------------------------+----------+
; | | |
; | | New |
; | | |
; | Old Area | (drawn) |
; | | (second) |
; | | |
; +---------------------------+==========| <-- Top
; |//////////////////////////////////////|
; |// New Area (drawn first) ////////////|
; |//////////////////////////////////////|
; +--------------------------------------+ <-- Bottom
; ^ ^
; | |
; +--- Left Right --+
lda :Top
beq :drewTop
dec a ; 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 a ; so do Y+1 to HEIGHT
sta :Top
lda ScreenTileHeight
sta :Bottom
; +---------------------------+----------+ <-- Top
; | | |
; | | New |
; | | |
; | Old Area | (drawn) |
; | | (second) |
; | | | <-- Bottom
; +---------------------------+==========|
; |//////////////////////////////////////|
; |// New Area (drawn first) ////////////|
; |//////////////////////////////////////|
; +--------------------------------------+
; ^ ^
; | |
; +--- Left Right --+
; The Top an 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 BG0TileOriginX ; Did the first column of the tile map change from before?
cmp OldBG0TileOriginX ; Did it change from before?
beq :NoXUpdate ; no, so we can ignore this
sec
sbc BG0TileOriginX ; 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
sta :Right
bra :DoXUpdate
:DoRightSide
eor #$FFFF
inc
clc
adc ScreenTileWidth
sta :Left
:DoXUpdate
jsr :DrawRectBG0 ; 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.
:DrawRectBG0
lda :Bottom
sec
sbc :Top
inc
sta :Height
lda :Right
sec
sbc :Left
inc
sta :Width
; Compute the offset into the tile array of the top-left corner
lda :Left
clc
adc BG0TileOriginX
sta :GlobalTileIdxX
lda :Top
clc
adc BG0TileOriginY ; This is the global verical index
sta :GlobalTileIdxY
ldx TileMapWidth
jsr :MulAX
clc
adc :GlobalTileIdxX
asl ; Double for word sizes
sta :Offset ; Stash the pointer offset in Y
lda TileMapWidth
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 StartYMod208 ; 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
sta :BlkY ; This is the Y-block we start drawing from
lda StartXMod164 ; Dx the same thing for X, except only need to clamp by 4
and #$FFFC
lsr
lsr
clc
adc :Left
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 [TileMapPtr],y
iny ; pre-increment the address. A bit faster than two "INC DP" instructions
iny
sty :Offset
ldx :BlkX
ldy :BlkY
jsr CopyTile
inc :BlkX ; Move to the next block
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
inc :BlkY
dec :Height ; Have we done all of the rows?
bne :yloop
pla ; Pop off cached values
pla
rts
; Quick multiplication of the accumulator and x-register
; A = A * X
:MulAX
stx :MulA
cmp :MulA ; Put the smaller value in MulA (less shifts on average)
bcc :swap
sta :MulB
bra :entry
:swap stx :MulB
sta :MulA
:entry
lda #0
; Start shifting and adding. We actually do an extra
; shift if MulA is zero, but a zero value does not
; change the result and it allows us to eliminate a
; branch on the inner loop
:loop
lsr :MulA ; shift out the LSB
bcc :skip ; zero is no multiply
clc
adc :MulB
:skip
asl :MulB ; double the multplicand
ldx :MulA
bne :loop
rts

View File

@ -609,61 +609,3 @@ ApplyBG1OffsetValues
:none rts
BG1YCache ds 32

View File

@ -1,87 +1,81 @@
; Direct page locations used by the engine
ScreenHeight equ 0 ; Height of the playfield in scan lines
ScreenWidth equ 2 ; Width of the playfield in bytes
ScreenY0 equ 4 ; First vertical line on the physical screen of the playfield
ScreenY1 equ 6 ; End of playfield on the physical screen. If the height is 20 and Y0 is
ScreenX0 equ 8 ; 100, then ScreenY1 = 120.
ScreenX1 equ 10
ScreenTileHeight equ 12 ; Height of the playfield in 8x8 blocks
ScreenTileWidth equ 14 ; Width of the playfield in 8x8 blocks
StartX equ 16 ; Which code buffer byte is the left edge of the screen. Range = 0 to 167
StartY equ 18 ; Which code buffer line is the top of the screen. Range = 0 to 207
EngineMode equ 20 ; Defined the mode/capabilities that are enabled
; bit 0: 0 = Single Background, 1 = Parallax
DirtyBits equ 22 ; Identify values that have changed between frames
BG1DataBank equ 24 ; Data bank that holds BG1 layer data
BG1AltBank equ 26 ; Alternate BG1 bank
BlitterDP equ 28 ; Direct page address the holder blitter data
OldStartX equ 30
OldStartY equ 32
LastPatchOffset equ 34 ; Offset into code field that was patched with BRA instructions
StartXMod164 equ 36
StartYMod208 equ 38
BG1StartX equ 40 ; Logical offset of the second background
BG1StartXMod164 equ 42
BG1StartY equ 44
BG1StartYMod208 equ 46
OldBG1StartX equ 48
OldBG1StartY equ 50
BG1OffsetIndex equ 52
BankLoad equ 128
bstk equ 208 ; 16-byte stack to push bank addresses
tmp8 equ 224
tmp9 equ 226
tmp10 equ 228
tmp0 equ 240 ; 16 bytes of temporary space to be used as scratch
tmp1 equ 242
tmp2 equ 244
tmp3 equ 246
tmp4 equ 248
tmp5 equ 250
tmp6 equ 252
tmp7 equ 254
DIRTY_BIT_BG0_X equ $0001
DIRTY_BIT_BG0_Y equ $0002
DIRTY_BIT_BG1_X equ $0004
DIRTY_BIT_BG1_Y equ $0008
ScreenHeight equ 0 ; Height of the playfield in scan lines
ScreenWidth equ 2 ; Width of the playfield in bytes
ScreenY0 equ 4 ; First vertical line on the physical screen of the playfield
ScreenY1 equ 6 ; End of playfield on the physical screen. If the height is 20 and Y0 is
ScreenX0 equ 8 ; 100, then ScreenY1 = 120.
ScreenX1 equ 10
ScreenTileHeight equ 12 ; Height of the playfield in 8x8 blocks
ScreenTileWidth equ 14 ; Width of the playfield in 8x8 blocks
StartX equ 16 ; Which code buffer byte is the left edge of the screen. Range = 0 to 167
StartY equ 18 ; Which code buffer line is the top of the screen. Range = 0 to 207
EngineMode equ 20 ; Defined the mode/capabilities that are enabled
; bit 0: 0 = Single Background, 1 = Parallax
DirtyBits equ 22 ; Identify values that have changed between frames
BG1DataBank equ 24 ; Data bank that holds BG1 layer data
BG1AltBank equ 26 ; Alternate BG1 bank
BlitterDP equ 28 ; Direct page address the holder blitter data
OldStartX equ 30
OldStartY equ 32
LastPatchOffset equ 34 ; Offset into code field that was patched with BRA instructions
StartXMod164 equ 36
StartYMod208 equ 38
BG1StartX equ 40 ; Logical offset of the second background
BG1StartXMod164 equ 42
BG1StartY equ 44
BG1StartYMod208 equ 46
OldBG1StartX equ 48
OldBG1StartY equ 50
BG1OffsetIndex equ 52
BG0TileOriginX equ 54 ; Coordinate in the tile map that corresponds to the top-left corner
BG0TileOriginY equ 56
OldBG0TileOriginX equ 58
OldBG0TileOriginY equ 60
BG1TileOriginX equ 62 ; Coordinate in the tile map that corresponds to the top-left corner
BG1TileOriginY equ 64
OldBG1TileOriginX equ 66
OldBG1TileOriginY equ 68
TileMapWidth equ 70
TileMapHeight equ 72
TileMapPtr equ 74
Next equ 78
BankLoad equ 128
blttmp equ 192 ; 32 bytes of local cache/scratch space
tmp8 equ 224
tmp9 equ 226
tmp10 equ 228
tmp11 equ 230
tmp12 equ 232
tmp13 equ 234
tmp14 equ 236
tmp15 equ 238
tmp0 equ 240 ; 16 bytes of temporary space to be used as scratch
tmp1 equ 242
tmp2 equ 244
tmp3 equ 246
tmp4 equ 248
tmp5 equ 250
tmp6 equ 252
tmp7 equ 254
DIRTY_BIT_BG0_X equ $0001
DIRTY_BIT_BG0_Y equ $0002
DIRTY_BIT_BG1_X equ $0004
DIRTY_BIT_BG1_Y equ $0008

View File

@ -235,19 +235,3 @@ CopyTile
sta $7003,y
rep #$20
rts

View File

@ -189,5 +189,3 @@ CopyRTableToStkAddr
:x01 ldal RTable+00,x
sta: STK_ADDR+$0000,y
:none rts