; Basic tile functions

; Copy tileset data from a pointer in memory to the tiledata back
; X = high word
; A = low word
_LoadTileSet
                sta  tmp0
                stx  tmp1
                ldy  #0
                tyx
:loop           lda  [tmp0],y
                stal tiledata,x
                dex
                dex
                dey
                dey
                bne  :loop
                rts


; Low-level function to take a tile descriptor and return the address in the tiledata
; bank.  This is not too useful in the fast-path because the fast-path does more
; incremental calculations, but it is handy for other utility functions
;
; A = tile descriptor
;
; The address is the TileID * 128 + (HFLIP * 64)
_GetTileAddr
                 asl                                               ; Multiply by 2
                 bit   #2*TILE_HFLIP_BIT                           ; Check if the horizontal flip bit is set
                 beq   :no_flip
                 inc                                               ; Set the LSB
:no_flip         asl                                               ; x4
                 asl                                               ; x8
                 asl                                               ; x16
                 asl                                               ; x32
                 asl                                               ; x64
                 asl                                               ; x128
                 rts

; Ignore the horizontal flip bit
_GetBaseTileAddr
                 asl                                               ; Multiply by 2
                 asl                                               ; x4
                 asl                                               ; x8
                 asl                                               ; x16
                 asl                                               ; x32
                 asl                                               ; x64
                 asl                                               ; x128
                 rts


; Helper function to get the address offset into the tile cachce / tile backing store
; X = tile column [0, 40] (41 columns)
; Y = tile row    [0, 25] (26 rows)
_GetTileStoreOffset
                 phx                        ; preserve the registers
                 phy

                 jsr  _GetTileStoreOffset0

                 ply
                 plx
                 rts

_GetTileStoreOffset0
                 tya
                 asl
                 tay
                 txa
                 asl
                 clc
                 adc  TileStoreYTable,y
                 rts

; Initialize the tile storage data structures.  This takes care of populating the tile records with the
; appropriate constant values.
InitTiles
:col             equ  tmp0
:row             equ  tmp1
:vbuff           equ  tmp2
:base            equ  tmp3

; Initialize the Tile Store

                 ldx  #TILE_STORE_SIZE-2
                 lda  #25
                 sta  :row
                 lda  #40
                 sta  :col
                 lda  #$8000
                 sta  :vbuff

:loop 

; The first set of values in the Tile Store that are changed during each frame based on the actions
; that are happening

                 lda  #0
                 sta  TileStore+TS_TILE_ID,x            ; clear the tile store with the special zero tile
                 sta  TileStore+TS_TILE_ADDR,x
                 sta  TileStore+TS_SPRITE_FLAG,x        ; no sprites are set at the beginning
                 sta  TileStore+TS_DIRTY,x              ; none of the tiles are dirty

; Set the default tile rendering functions

                 lda  EngineMode
                 bit  #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER
                 beq  :fast
                 bit  #ENGINE_MODE_TWO_LAYER
                 beq  :dyn

                 lda  #0
                 ldy  #TwoLyrProcs
                 jsr  _SetTileProcs
                 bra  :out
:fast
                 lda  #0                                 ; Initialize with Tile 0
                 ldy  #FastProcs
                 jsr  _SetTileProcs
                 bra  :out

:dyn             lda  #0                                 ; Initialize with Tile 0
                 ldy  #FastProcs
                 jsr  _SetTileProcs

:out

; The next set of values are constants that are simply used as cached parameters to avoid needing to
; calculate any of these values during tile rendering

                 lda  :row                              ; Set the long address of where this tile
                 asl                                    ; exists in the code fields
                 tay
                 lda  #>TileStore                       ; get middle 16 bits: "00 -->BBHH<-- LL"
                 and  #$FF00                            ; merge with code field bank
                 ora  BRowTableHigh,y
                 sta  TileStore+TS_CODE_ADDR_HIGH,x     ; High word of the tile address (just the bank)

                 lda  BRowTableLow,y
                 sta  :base

                 lda  :col                              ; Set the offset values based on the column
                 asl                                    ; of this tile
                 asl
                 sta  TileStore+TS_WORD_OFFSET,x        ; This is the offset from 0 to 82, used in LDA (dp),y instruction

                 tay
                 lda  Col2CodeOffset+2,y
                 clc
                 adc  :base
                 sta  TileStore+TS_CODE_ADDR_LOW,x      ; Low word of the tile address in the code field

                 lda  JTableOffset,y
                 clc
                 adc  :base
                 sta  TileStore+TS_JMP_ADDR,x           ; Address of the snippet handler for this tile

                 dec  :col
                 bpl  :hop
                 dec  :row
                 lda  #40
                 sta  :col
:hop

                 dex
                 dex
                 bpl  :loop
                 rts

; Put everything visible on the dirty tile list
_Refresh
:col            equ  tmp0
:row            equ  tmp1
:idx            equ  tmp2

                jsr  _OriginToTileStore

                clc
                txa
                adc  TileStoreLookupYTable,y
                sta  :idx

                lda  ScreenTileWidth
                sta  :col

                lda  ScreenTileHeight
                sta  :row

:oloop          ldy  :idx
:iloop          ldx  TileStoreLookup,y
                jsr  _PushDirtyTileX

                iny
                iny
                dec  :col
                bne  :iloop

                lda  ScreenTileWidth
                sta  :col

                lda  :idx
                clc
                adc  #TS_LOOKUP_SPAN*2
                sta  :idx

                dec  :row
                bne  :oloop
                rts

; Reset all of the tile proc values in the playfield.
_ResetToDirtyTileProcs
                 ldx  #TILE_STORE_SIZE-2
:loop
                 lda  TileStore+TS_TILE_ID,x
                 jsr  _SetDirtyTileProcs
                 dex
                 dex
                 bpl  :loop
                 rts

_ResetToNormalTileProcs
                 ldx  #TILE_STORE_SIZE-2
:loop
                 lda  TileStore+TS_TILE_ID,x
                 jsr  _SetNormalTileProcs
                 dex
                 dex
                 bpl  :loop
                 rts

; A = tileID
; X = tile store index
_SetDirtyTileProcs
                 jsr  _CalcTileProcIndex
                 ldy  #DirtyProcs
                 jmp  _SetTileProcs

; A = tileID
; X = tile store index
_SetNormalTileProcs
                 pha                           ; extra space
                 pha                           ; save the tile ID
                 jsr  _CalcTileProcIndex
                 sta  3,s                      ; save for later

                 lda  EngineMode
                 bit  #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER
                 beq  :setTileFast

                 bit  #ENGINE_MODE_TWO_LAYER
                 beq  :setTileDyn

                 pla                           ; restore newTileID
                 bit  #TILE_DYN_BIT
                 beq  :pickTwoLyrProc

                 ldy  #TwoLyrDynProcs
                 brl  :pickDynProc

:pickTwoLyrProc  ldy  #TwoLyrProcs
                 pla                          ; pull of the proc index
                 jmp  _SetTileProcs

; Specialized check for when the engine is in "Fast" mode. If is a simple decision tree based on whether
; the tile priority bit is set, and whether this is the special tile 0 or not.
:setTileFast
                 pla                         ; Throw away tile ID copy
                 ldy  #FastProcs
                 pla
                 jmp  _SetTileProcs

; Specialized check for when the engine has enabled dynamic tiles. In this case we are no longer
; guaranteed that the opcodes in a tile are PEA instructions.  
:setTileDyn
                 pla                           ; get the cached tile ID
                 bit  #TILE_DYN_BIT
                 beq  :pickSlowProc            ; If the Dynamic bit is not set, select a tile proc that sets opcodes

                 ldy  #DynProcs                ; use this table
:pickDynProc
                 and  #TILE_PRIORITY_BIT
                 beq  :pickZeroDynProc         ; If the Priority bit is not set, pick the first entry
                 pla
                 lda  #1                       ; If the Priority bit is set, pick the other one
                 jmp  _SetTileProcs

:pickZeroDynProc pla
                 lda  #0
                 jmp  _SetTileProcs

:pickSlowProc    ldy  #SlowProcs
                 pla
                 jmp  _SetTileProcs

; Helper method to calculate the index into the tile proc table given a TileID
; Calculate the base tile proc selector from the tile Id
_CalcTileProcIndex                 
                 bit  #TILE_PRIORITY_BIT             ; 4 if !0, 0 otherwise
                 beq  :low_priority

                 bit  #TILE_ID_MASK                  ; 2 if !0, 0 otherwise
                 beq  :is_zero_a

                 bit  #TILE_VFLIP_BIT                ; 1 if !0, 0 otherwise
                 beq  :no_flip_a

                 lda  #7
                 rts

:no_flip_a       lda  #6
                 rts

:is_zero_a       bit  #TILE_VFLIP_BIT
                 beq  :no_flip_b

                 lda  #5
                 rts

:no_flip_b       lda  #4
                 rts

:low_priority    bit  #TILE_ID_MASK                  ; 2 if !0, 0 otherwise
                 beq  :is_zero_b

                 bit  #TILE_VFLIP_BIT                ; 1 if !0, 0 otherwise
                 beq  :no_flip_c

                 lda  #3
                 rts

:no_flip_c       lda  #2
                 rts

:is_zero_b       bit  #TILE_VFLIP_BIT
                 beq  :no_flip_d

                 lda  #1
                 rts

:no_flip_d       lda  #0
                 rts

; Set a tile value in the tile backing store.  Mark dirty if the value changes
;
; A = tile id
; X = tile column [0, 40] (41 columns)
; Y = tile row    [0, 25] (26 rows)
;
; Registers are not preserved
oldTileId  equ  blttmp                              ; This location is used in _SetTileProcs, too
newTileId  equ  blttmp+2
procIdx    equ  blttmp+4

_SetTile
                 sta  newTileId
                 jsr  _GetTileStoreOffset0          ; Get the address of the X,Y tile position
                 tax

                 lda  TileStore+TS_TILE_ID,x
;                 cmp  newTileId                    ; Lift this up to the caller
;                 bne  :changed
;                 rts

:changed         sta  oldTileId
                 lda  newTileId
                 sta  TileStore+TS_TILE_ID,x        ; Value is different, store it.
                 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

; Set the renderer procs for this tile.
;
; NOTE: Later on, optimize this to just take the Tile ID & TILE_CTRL_MASK and lookup the right proc
;       table address from a lookup table....
;
;  1. The dirty render proc is always set the same.
;  2. If BG1 and DYN_TILES are disabled, then the TS_BASE_TILE_DISP is selected from the Fast Renderers, otherwise
;     it is selected from the full tile rendering functions.
;  3. The copy process is selected based on the flip bits
;
; When a tile overlaps the sprite, it is the responsibility of the Render function to compose the appropriate
; functionality.  Sometimes it is simple, but in cases of the sprites overlapping Dynamic Tiles and other cases
; it can be more involved.

; Calculate the base tile proc selector from the tile Id (need X-register set to tile store index)

                 lda  newTileId
                 jsr  _SetNormalTileProcs
                 jmp  _PushDirtyTileX

; X = Tile Store offset
; Y = Engine Mode Base Table address
; A = Table proc index
;
; see TileProcTables in static/TileStore.s
tblPtr  equ  blttmp
_SetTileProcs

; Multiple the proc index by 6 to get the correct table entry offset

                 asl
                 sta  tblPtr
                 asl
                 adc  tblPtr
                 sta  tblPtr

; Add this offset to the base table address

                 tya
                 adc  tblPtr
                 sta  tblPtr

; Set the pointer to this bank

                 phk
                 phk
                 pla
                 and  #$00FF
                 sta  tblPtr+2

; Lookup the tile procedures

                 ldy  #0
                 lda  [tblPtr],y
                 stal K_TS_BASE_TILE_DISP,x

                 ldy  #2
                 lda  [tblPtr],y
                 stal K_TS_SPRITE_TILE_DISP,x

                 ldy  #4
                 lda  [tblPtr],y
                 stal K_TS_ONE_SPRITE,x
                 rts

; TileProcTables
;
; Tables of tuples used to populate the K_TS_* dispatch arrays for different combinations. This is
; easier to maintain than a bunch of conditional code.  Each etry hold three addresses.
;
; First address:  Draw a tile directly into the code buffer (no sprites)
; Second address: Draw a tile merged with sprite data from the direct page
; Third address:  Specialize routine to draw a tile merged with one sprite
;
; There are unique tuples of routines for all of the different combinations of tile properties
; and engine modes.  This is an extesive number of combinations, but it simplified the development
; and maintainence of the rendering subroutines.  Also, the difference subroutines can be written
; in any way and can make use of their on subroutines to reduce code size.
;
; Properties:
;
;  [MODE]         ENGINE_MODE: Fast, Dyn, TwoLayer
;  [Z | N]        Is Tile 0? : Yes, No
;  [A | V]        Is VFLIP?  : Yes, No
;  [Over | Under] Priority?  : Yes, No
;
; So eight tuples per engine mode; 24 tuples total.  Table name convention
;
; <MODE><Over|Under><Z|N><A|V>
FastProcs
FastOverZA   dw   ConstTile0Fast,SpriteOver0Fast,OneSpriteFastOver0
FastOverZV   dw   ConstTile0Fast,SpriteOver0Fast,OneSpriteFastOver0
FastOverNA   dw   CopyTileAFast,SpriteOverAFast,OneSpriteFastOverA
FastOverNV   dw   CopyTileVFast,SpriteOverVFast,OneSpriteFastOverV
FastUnderZA  dw   ConstTile0Fast,SpriteUnder0Fast,SpriteUnder0Fast
FastUnderZV  dw   ConstTile0Fast,SpriteUnder0Fast,SpriteUnder0Fast
FastUnderNA  dw   CopyTileAFast,SpriteUnderAFast,OneSpriteFastUnderA
FastUnderNV  dw   CopyTileVFast,SpriteUnderVFast,OneSpriteFastUnderV

; "Slow" procs.  These are duplicates of the "Fast" functions, but also
; set the PEA opcode in all cases.
SlowProcs
SlowOverZA   dw   ConstTile0Slow,SpriteOver0Slow,OneSpriteSlowOver0
SlowOverZV   dw   ConstTile0Slow,SpriteOver0Slow,OneSpriteSlowOver0
SlowOverNA   dw   CopyTileASlow,SpriteOverASlow,OneSpriteSlowOverA
SlowOverNV   dw   CopyTileVSlow,SpriteOverVSlow,OneSpriteSlowOverV
SlowUnderZA  dw   ConstTile0Slow,SpriteUnder0Slow,SpriteUnder0Slow
SlowUnderZV  dw   ConstTile0Slow,SpriteUnder0Slow,SpriteUnder0Slow
SlowUnderNA  dw   CopyTileASlow,SpriteUnderASlow,OneSpriteSlowUnderA
SlowUnderNV  dw   CopyTileVSlow,SpriteUnderVSlow,OneSpriteSlowUnderV

; "Dynamic" procs. These are the specialized routines for a dynamic tiles
; that does not need to worry about a second background.  Because dynamic
; tiles don't support horizontal or vertical flipping, there are only two 
; sets of procedures: one for Over and one for Under.
DynProcs
DynOver      dw   CopyDynamicTile,DynamicOver,OneSpriteDynamicOver
DynUnder     dw   CopyDynamicTile,DynamicUnder,OneSpriteDynamicUnder

; "Two Layer" procs. These are the most complex procs.  Generally,
; all of these methods are implemented by building up the data
; and mask into the direct page space and then calling a common
; function to create the complex code fragments in the code field.
; There is not a lot of opportuinity to optimize these routines.
;
; To improve the performance when two-layer rendering is enabled,
; the TILE_SOLID_BIT hint bit can be set to indicate that a tile
; has no transparency.  This allows one of the faster routines
; to be selected from the other Proc tables
TwoLyrProcs
TwoLyrOverZA   dw   Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr
TwoLyrOverZV   dw   Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr
TwoLyrOverNA   dw   CopyTileATwoLyr,SpriteOverATwoLyr,OneSpriteTwoLyrOverA
TwoLyrOverNV   dw   CopyTileVTwoLyr,SpriteOverVTwoLyr,OneSpriteTwoLyrOverV
TwoLyrUnderZA  dw   Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr   ; if sprites are over or under the transparent tile, same rendering code
TwoLyrUnderZV  dw   Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr
TwoLyrUnderNA  dw   CopyTileATwoLyr,SpriteUnderATwoLyr,OneSpriteTwoLyrUnderA
TwoLyrUnderNV  dw   CopyTileVTwoLyr,SpriteUnderVTwoLyr,OneSpriteTwoLyrUnderV

; "Dynamic" procs that can handle the second background.
TwoLyrDynProcs
TwoLyrDynOver  dw   CopyDynamicTileTwoLyr,DynamicOverTwoLyr,OneSpriteDynamicOverTwoLyr
TwoLyrDynUnder dw   CopyDynamicTileTwoLyr,DynamicUnderTwoLyr,OneSpriteDynamicUnderTwoLyr

; "Dirty" procs that are for dirty tile direct rendering.  No moving background.
DirtyProcs
DirtyOverZA   dw   ConstTile0Dirty,SpriteOver0Dirty,OneSpriteDirtyOver0
DirtyOverZV   dw   ConstTile0Dirty,SpriteOver0Dirty,OneSpriteDirtyOver0
DirtyOverNA   dw   CopyTileADirty,SpriteOverADirty,OneSpriteDirtyOverA
DirtyOverNV   dw   CopyTileVDirty,SpriteOverVDirty,OneSpriteDirtyOverV
DirtyUnderZA  dw   ConstTile0Dirty,SpriteUnder0Dirty,SpriteUnder0Dirty
DirtyUnderZV  dw   ConstTile0Dirty,SpriteUnder0Dirty,SpriteUnder0Dirty
DirtyUnderNA  dw   CopyTileADirty,SpriteUnderADirty,OneSpriteDirtyUnderA
DirtyUnderNV  dw   CopyTileVDirty,SpriteUnderVDirty,OneSpriteDirtyUnderV

; SetBG0XPos
;
; Set the virtual horizontal position of the primary background layer.  In addition to 
; updating the direct page state locations, this routine needs to preserve the original
; value as well.  This is a bit subtle, because if this routine is called multiple times
; with different values, we need to make sure the *original* value is preserved and not
; continuously overwrite it.
;
; We assume that there is a clean code field in this routine
_SetBG0XPos
                    cmp   StartX
                    beq   :out                       ; Easy, if nothing changed, then nothing changes

                    ldx   StartX                     ; Load the old value (but don't save it yet)
                    sta   StartX                     ; Save the new position

                    lda   #DIRTY_BIT_BG0_X
                    tsb   DirtyBits                  ; Check if the value is already dirty, if so exit
                    bne   :out                       ; without overwriting the original value

                    stx   OldStartX                  ; First change, so preserve the value
:out                rts


; SetBG0YPos
;
; Set the virtual position of the primary background layer.
_SetBG0YPos
                     cmp   StartY
                     beq   :out                 ; Easy, if nothing changed, then nothing changes

                     ldx   StartY               ; Load the old value (but don't save it yet)
                     sta   StartY               ; Save the new position

                     lda   #DIRTY_BIT_BG0_Y
                     tsb   DirtyBits            ; Check if the value is already dirty, if so exit
                     bne   :out                 ; without overwriting the original value

                     stx   OldStartY            ; First change, so preserve the value
:out                 rts

; Macro helper for the bit test tree
;           dobit   bit_position,dest;next;exit
dobit       mac
            lsr
            bcc   next_bit
            beq   last_bit
            tax
            lda   (SPRITE_VBUFF_PTR+{]1*2}),y
            clc
            adc   _Sprites+TS_VBUFF_BASE+{]1*2}
            sta   sprite_ptr0+{]2*4}
            txa
            jmp   ]3
last_bit    lda   (SPRITE_VBUFF_PTR+{]1*2}),y
            clc    ; pre-adjust these later
            adc   _Sprites+TS_VBUFF_BASE+{]1*2}
            sta   sprite_ptr0+{]2*4}
            jmp   ]4
next_bit
            <<<

; Specialization for the first sprite which can optimize its dispatch if its the only one
;           dobit   bit_position,dest;next;exit
dobit1      mac
            lsr
            bcc   next_bit
            beq   last_bit
            tax
            lda   (SPRITE_VBUFF_PTR+{]1*2}),y
            clc
            adc   _Sprites+TS_VBUFF_BASE+{]1*2}
            sta   sprite_ptr0+{]2*4}
            txa
            jmp   ]3
last_bit    lda   (SPRITE_VBUFF_PTR+{]1*2}),y
            clc    ; pre-adjust these later
            adc   _Sprites+TS_VBUFF_BASE+{]1*2}
            sta   sprite_ptr0+{]2*4}
            tyx
            jmp   (K_TS_ONE_SPRITE,x)
next_bit
            <<<

; If we find a last bit (4th in this case) and will exit
stpbit      mac
            lsr
            bcc   next_bit
            lda   (SPRITE_VBUFF_PTR+{]1*2}),y
            clc    ; pre-adjust these later
            adc   _Sprites+TS_VBUFF_BASE+{]1*2}
            sta   sprite_ptr0+{]2*4}
            jmp   ]3
next_bit
            <<<

; Last bit test which *must* be set
endbit      mac
            lda   (SPRITE_VBUFF_PTR+{]1*2}),y
            clc    ; pre-adjust these later
            adc   _Sprites+TS_VBUFF_BASE+{]1*2}
            sta   sprite_ptr0+{]2*4}
            jmp   ]3
            <<<

endbit1     mac
            lda   (SPRITE_VBUFF_PTR+{]1*2}),y
            clc    ; pre-adjust these later
            adc   _Sprites+TS_VBUFF_BASE+{]1*2}
            sta   sprite_ptr0+{]2*4}
            tyx
            jmp   (K_TS_ONE_SPRITE,x)
            <<<

; OPTIMIZATION:
;
;           bit     #$00FF                    ; Skip the first 8 bits if they are all zeros
;           bne     norm_entry
;           xba
;           jmp     skip_entry
;
; Placed at the entry point

; This is a complex, but fast subroutine that is called from the core tile rendering code.  It
; Takes a bitmap of sprites in the Accumulator and then extracts the VBuff addresses for the
; target TileStore entry and places them in specific direct page locations.
;
; Inputs:
;  A = sprite bitmap (assumed to be non-zero)
;  Y = tile store index
;  D = second work page
;  B = vbuff array bank
; Output:
;  X = 
;
; ]1 address of single sprite process
; ]2 address of two sprite process
; ]3 address of three sprite process
; ]4 address of four sprite process

SpriteBitsToVBuffAddrs mac
           dobit1  0;0;b_1_1
           dobit1  1;0;b_2_1
           dobit1  2;0;b_3_1
           dobit1  3;0;b_4_1
           dobit1  4;0;b_5_1
           dobit1  5;0;b_6_1
           dobit1  6;0;b_7_1
           dobit1  7;0;b_8_1
           dobit1  8;0;b_9_1
           dobit1  9;0;b_10_1
           dobit1  10;0;b_11_1
           dobit1  11;0;b_12_1
           dobit1  12;0;b_13_1
           dobit1  13;0;b_14_1
           dobit1  14;0;b_15_1
           endbit1 15;0

b_1_1      dobit  1;1;b_2_2;]2
b_2_1      dobit  2;1;b_3_2;]2
b_3_1      dobit  3;1;b_4_2;]2
b_4_1      dobit  4;1;b_5_2;]2
b_5_1      dobit  5;1;b_6_2;]2
b_6_1      dobit  6;1;b_7_2;]2
b_7_1      dobit  7;1;b_8_2;]2
b_8_1      dobit  8;1;b_9_2;]2
b_9_1      dobit  9;1;b_10_2;]2
b_10_1     dobit  10;1;b_11_2;]2
b_11_1     dobit  11;1;b_12_2;]2
b_12_1     dobit  12;1;b_13_2;]2
b_13_1     dobit  13;1;b_14_2;]2
b_14_1     dobit  14;1;b_15_2;]2
b_15_1     endbit 15;1;]2

b_2_2      dobit  2;2;b_3_3;]3
b_3_2      dobit  3;2;b_4_3;]3
b_4_2      dobit  4;2;b_5_3;]3
b_5_2      dobit  5;2;b_6_3;]3
b_6_2      dobit  6;2;b_7_3;]3
b_7_2      dobit  7;2;b_8_3;]3
b_8_2      dobit  8;2;b_9_3;]3
b_9_2      dobit  9;2;b_10_3;]3
b_10_2     dobit  10;2;b_11_3;]3
b_11_2     dobit  11;2;b_12_3;]3
b_12_2     dobit  12;2;b_13_3;]3
b_13_2     dobit  13;2;b_14_3;]3
b_14_2     dobit  14;2;b_15_3;]3
b_15_2     endbit 15;2;]3

b_3_3      stpbit 3;3;]4
b_4_3      stpbit 4;3;]4
b_5_3      stpbit 5;3;]4
b_6_3      stpbit 6;3;]4
b_7_3      stpbit 7;3;]4
b_8_3      stpbit 8;3;]4
b_9_3      stpbit 9;3;]4
b_10_3     stpbit 10;3;]4
b_11_3     stpbit 11;3;]4
b_12_3     stpbit 12;3;]4
b_13_3     stpbit 13;3;]4
b_14_3     stpbit 14;3;]4
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_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