; Untility function related to patching and manipulating the blitter template code

                   mx    %00

; Generalized routine that calculates the on-screen address of the tiles and takes the 
; StartX and StartY values into consideration.  This routine really exists to support
; the dirty tile rendering mode and the tiles *must* be aligned with the playfield.
; That is, StartX % 4 == 0 and StartY % 8 == 0.  If these conditions are not met, then
; screen will not render correctly.
_RecalcTileScreenAddrs
NextColPtr         equ   tmp0
RowAddrPtr         equ   tmp1
OnScreenAddr       equ   tmp2
Counter            equ   tmp3

                   jsr   _OriginToTileStore          ; Get the (col,row) of the tile in the upper-left corner of the playfield

; Manually add the offsets to the NextCol and TileStoreYTable array address and put in a direct page
; location so we can free up the registers.

                   clc
                   txa
                   adc   #NextCol
                   sta   NextColPtr

                   tya
                   adc   #TileStoreYTable
                   sta   RowAddrPtr

; Calculate the on-screen address of the upper-left corner of the playfiled

                   lda   ScreenY0                   ; Calculate the address of the first byte
                   asl                              ; of the right side of the playfield
                   tax
                   lda   ScreenAddr,x               ; This is the address for the left edge of the physical screen
                   clc
                   adc   ScreenX0
                   sta   OnScreenAddr

; Now, loop through the tile store

                   lda   #MAX_TILES
                   sta   Counter
                   ldy   #0
:tsloop
                   lda   (NextColPtr),y             ; Need to recalculate each time since the wrap-around could
                   clc                              ; happen anywhere
                   adc   (RowAddrPtr)               ;
                   tax                              ; NOTE: Try to rework to use new TileStore2DLookup array

                   lda   OnScreenAddr
                   stal  TileStore+TS_SCREEN_ADDR,x

                   clc
                   adc   #4                         ; Go to the next tile

                   iny
                   iny
                   cpy   #2*41                      ; If we've done 41 columns, move to the next line
                   bcc   :nohop

                   inc   RowAddrPtr                 ; Advance the row address (with wrap-around)
                   inc   RowAddrPtr
                   ldy   #0                         ; Reset the column counter
                   clc
                   adc   #{8*160}-{4*41}
:nohop
                   sta   OnScreenAddr               ; Save the updated on-screen address
                   dec   Counter
                   bne   :tsloop

                   rts


; Patch an 8-bit or 16-bit valueS into the bank.  These are a set up unrolled loops to 
; quickly patch in a constanct value, or a value from an array into a given set of 
; templates.
;
; Because we have structured everything as parallel code blocks, most updates to the blitter
; reduce to storing a constant value and have an amortized cost of just a single store.
;
; The utility of these routines is that they also handle setting just a range of lines
; within a single bank.
;
; X = number of lines * 2, 0 to 32
; Y = starting line * $1000
; A = value
;
; Set M to 0 or 1
SetConst                                            ; Need a blank line here, otherwise the :tbl local variable resolveds backwards
                   jmp   (:tbl,x)
:tbl               da    :bottom-00,:bottom-03,:bottom-06,:bottom-09
                   da    :bottom-12,:bottom-15,:bottom-18,:bottom-21
                   da    :bottom-24,:bottom-27,:bottom-30,:bottom-33
                   da    :bottom-36,:bottom-39,:bottom-42,:bottom-45
                   da    :bottom-48
:top               sta   $F000,y
                   sta   $E000,y
                   sta   $D000,y
                   sta   $C000,y
                   sta   $B000,y
                   sta   $A000,y
                   sta   $9000,y
                   sta   $8000,y
                   sta   $7000,y
                   sta   $6000,y
                   sta   $5000,y
                   sta   $4000,y
                   sta   $3000,y
                   sta   $2000,y
                   sta   $1000,y
                   sta:  $0000,y
:bottom            rts

; SetDPAddrs
;
; A = absolute address (largest)
; Y = offset
;
; Initializes a bank of direct page offsets
SetDPAddrs
                   lda   #$0800
                   sta   $F000,y
                   lda   #$0700
                   sta   $E000,y
                   lda   #$0600
                   sta   $D000,y
                   lda   #$0500
                   sta   $C000,y
                   lda   #$0400
                   sta   $B000,y
                   lda   #$0300
                   sta   $A000,y
                   lda   #$0200
                   sta   $9000,y
                   lda   #$0100
                   sta:  $8000,y

                   lda   #$0800
                   sta   $7000,y
                   lda   #$0700
                   sta   $6000,y
                   lda   #$0600
                   sta   $5000,y
                   lda   #$0500
                   sta   $4000,y
                   lda   #$0400
                   sta   $3000,y
                   lda   #$0300
                   sta   $2000,y
                   lda   #$0200
                   sta   $1000,y
                   lda   #$0100
                   sta:  $0000,y
                   rts

; SetAbsAddrs
;
; A = absolute address (largest)
; Y = offset
; X = number of lines
;
; Stores a value and decrements by $1000 for each line
SetAbsAddrs        sec
                   jmp   (:tbl,x)
:tbl               da    :bottom-00,:bottom-03,:bottom-09,:bottom-15
                   da    :bottom-21,:bottom-27,:bottom-33,:bottom-39
                   da    :bottom-45,:bottom-51,:bottom-57,:bottom-63
                   da    :bottom-69,:bottom-75,:bottom-81,:bottom-87
                   da    :bottom-93
:top               sta   $F000,y
                   sbc   #$1000
                   sta   $E000,y
                   sbc   #$1000
                   sta   $D000,y
                   sbc   #$1000
                   sta   $C000,y
                   sbc   #$1000
                   sta   $B000,y
                   sbc   #$1000
                   sta   $A000,y
                   sbc   #$1000
                   sta   $9000,y
                   sbc   #$1000
                   sta   $8000,y
                   sbc   #$1000
                   sta   $7000,y
                   sbc   #$1000
                   sta   $6000,y
                   sbc   #$1000
                   sta   $5000,y
                   sbc   #$1000
                   sta   $4000,y
                   sbc   #$1000
                   sta   $3000,y
                   sbc   #$1000
                   sta   $2000,y
                   sbc   #$1000
                   sta   $1000,y
                   sbc   #$1000
                   sta:  $0000,y
:bottom            rts

; Fill up a full bank with blitter templates.  Currently we can fit 16 lines per bank, so need
; a total of 13 banks to hold the 208 lines for full-screen support
;
; A = high word of bank table
; Y = index * 4 of the bank to initialize
BuildBank

:bankArray         equ   tmp0
:target            equ   tmp2
:nextBank          equ   tmp4

                   stx   :bankArray
                   sta   :bankArray+2

                   stz   :target
                   iny
                   iny
                   lda   [:bankArray],y
                   sta   :target+2

                   iny                              ; move to the next item
                   iny
                   iny                              ; middle byte
                   cpy   #4*13                      ; if greater than the array length, wrap back to zero
                   bcc   :ok
                   ldy   #1
:ok                lda   [:bankArray],y             ; Get the middle and high bytes of the address
                   sta   :nextBank

:next
                   jsr   :BuildLine2
                   lda   :target
                   clc
                   adc   #$1000
                   sta   :target
                   bcc   :next

                   phb
                   pei   :target+1
                   plb
                   plb

; Change the patched value to one of DP_ENTRY, TWO_LYR_ENTRY or ONE_LYR_ENTRY based on the capabilities
; that the engine needs.

                   lda   #$F000+{DP_ENTRY}          ; Set the address from each line to the next
                   ldy   #CODE_EXIT+1
                   ldx   #15*2
                   jsr   SetAbsAddrs

                   ldy   #DP_ADDR
                   jsr   SetDPAddrs

                   ldy   #$F000+CODE_EXIT           ; Patch the last line with a JML to go to the next bank
                   lda   #{$005C+{DP_ENTRY}*256}
                   sta   [:target],y
                   ldy   #$F000+CODE_EXIT+2
                   lda   :nextBank
                   sta   [:target],y

                   ldy   #$8000+CODE_EXIT           ; Patch one line per bank to enable interrupts
                   lda   #{$004C+{ENABLE_INT}*256}
                   sta   [:target],y

                   plb
                   rts

; This is the relocation subroutine, it is responsible for copying the template to a
; memory location and patching up the necessary instructions.
;
; X = low word of address (must be a multiple of $1000)
; A = high word of address (bank)
:BuildLine
                   stx   :target
                   sta   :target+2

:BuildLine2
                   lda   #CODE_LEN                  ; round up to an even number of bytes
                   inc
                   and   #$FFFE
                   beq   :nocopy
                   dec
                   dec
                   tay
:loop              lda   base,y
                   sta   [:target],y

                   dey
                   dey
                   bpl   :loop

:nocopy            lda   #0                         ; copy is complete, now patch up the addresses
                   sep   #$20

                   ldx   #0
                   lda   :target+2                  ; patch in the bank for the absolute long addressing mode
:dobank            ldy   BankPatches,x
                   sta   [:target],y
                   inx
                   inx
                   cpx   #BankPatchNum
                   bcc   :dobank

                   ldx   #0
:dopage            ldy   PagePatches,x              ; patch the page addresses by adding the page offset to each
                   lda   [:target],y
                   clc
                   adc   :target+1
                   sta   [:target],y
                   inx
                   inx
                   cpx   #PagePatchNum
                   bcc   :dopage

:out
                   rep   #$20
                   rts