mirror of
https://github.com/lscharen/iigs-game-engine.git
synced 2025-02-13 05:30:29 +00:00
Move most data storage to separate bank; fix many banking bugs
This commit is contained in:
parent
01e92a7b62
commit
755ac3fbfd
@ -11,6 +11,8 @@
|
||||
|
||||
mx %00
|
||||
|
||||
TSZelda EXT ; tileset buffer
|
||||
|
||||
ScreenX equ 0
|
||||
ScreenY equ 2
|
||||
|
||||
@ -29,12 +31,41 @@ ScreenY equ 2
|
||||
pea #160
|
||||
_GTESetScreenMode
|
||||
|
||||
; Load a tileset in from an uncompressed $C1 picture. The top-left 256x128 rectangle is used
|
||||
; to populate the 512 tiles.
|
||||
; Load a tileset
|
||||
|
||||
pea #^TSZelda
|
||||
pea #TSZelda
|
||||
_GTELoadTileSet
|
||||
|
||||
; Manually fill in the 41x26 tiles of the TileStore with a test pattern.
|
||||
|
||||
ldx #0
|
||||
ldy #0
|
||||
|
||||
:loop
|
||||
phx
|
||||
phy
|
||||
|
||||
phx
|
||||
phy
|
||||
pei 0
|
||||
_GTESetTile
|
||||
|
||||
lda 0
|
||||
inc
|
||||
and #$001F
|
||||
sta 0
|
||||
|
||||
ply
|
||||
plx
|
||||
inx
|
||||
cpx #41
|
||||
bcc :loop
|
||||
|
||||
ldx #0
|
||||
iny
|
||||
cpy #26
|
||||
bcc :loop
|
||||
|
||||
; Set the origin of the screen
|
||||
stz ScreenX
|
||||
@ -53,7 +84,7 @@ ScreenY equ 2
|
||||
pei ScreenY
|
||||
_GTESetBG0Origin
|
||||
|
||||
; _GTERender
|
||||
_GTERender
|
||||
|
||||
inc ScreenX ; Just keep incrementing, it's OK
|
||||
bra :loop
|
||||
|
@ -8,3 +8,8 @@
|
||||
|
||||
ASM App.Main.s
|
||||
SNA Main
|
||||
|
||||
; Segment #2 -- Tileset
|
||||
|
||||
ASM Zelda.TileSet.s
|
||||
SNA TSET
|
||||
|
@ -325,3 +325,57 @@ transparent
|
||||
sta: ]3+1,y
|
||||
next
|
||||
eom
|
||||
|
||||
; Large code blocks that can be used in sprite blitters
|
||||
; ]1: line number
|
||||
OneSpriteToCodeField mac
|
||||
lda blttmp+{]1*4}
|
||||
andl spritemask+{]1*SPRITE_PLANE_SPAN},x
|
||||
oral spritedata+{]1*SPRITE_PLANE_SPAN},x
|
||||
sta: $0004+{]1*$1000},y
|
||||
|
||||
lda blttmp+{]1*4}+2
|
||||
andl spritemask+{]1*SPRITE_PLANE_SPAN}+2,x
|
||||
oral spritedata+{]1*SPRITE_PLANE_SPAN}+2,x
|
||||
sta: $0001+{]1*$1000},y
|
||||
eom
|
||||
|
||||
TwoSpritesToCodeField mac
|
||||
ldy #{]1*SPRITE_PLANE_SPAN}
|
||||
lda blttmp+{]1*4}
|
||||
andl [spritemask_1],y
|
||||
oral [spritedata_1],y
|
||||
andl [spritemask_0],y
|
||||
oral [spritedata_0],y
|
||||
sta: $0004+{]1*$1000},x
|
||||
|
||||
ldy #{]1*SPRITE_PLANE_SPAN}+2
|
||||
lda blttmp+{]1*4}+2
|
||||
andl [spritemask_1],y
|
||||
oral [spritedata_1],y
|
||||
andl [spritemask_0],y
|
||||
oral [spritedata_0],y
|
||||
sta: $0001+{]1*$1000},x
|
||||
eom
|
||||
|
||||
ThreeSpritesToCodeField mac
|
||||
ldy #{]1*SPRITE_PLANE_SPAN}
|
||||
lda blttmp+{]1*4}
|
||||
andl [spritemask_2],y
|
||||
oral [spritedata_2],y
|
||||
andl [spritemask_1],y
|
||||
oral [spritedata_1],y
|
||||
andl [spritemask_0],y
|
||||
oral [spritedata_0],y
|
||||
sta: $0004+{]1*$1000},x
|
||||
|
||||
ldy #{]1*SPRITE_PLANE_SPAN}+2
|
||||
lda blttmp+{]1*4}+2
|
||||
andl [spritemask_2],y
|
||||
oral [spritedata_2],y
|
||||
andl [spritemask_1],y
|
||||
oral [spritedata_1],y
|
||||
andl [spritemask_0],y
|
||||
oral [spritedata_0],y
|
||||
sta: $0001+{]1*$1000},x
|
||||
eom
|
||||
|
@ -150,8 +150,8 @@ IntShutDown
|
||||
OneSecHandler mx %11
|
||||
phb
|
||||
pha
|
||||
phk
|
||||
plb
|
||||
|
||||
jsr _SetDataBank
|
||||
|
||||
rep #$20
|
||||
inc OneSecondCounter
|
||||
@ -167,10 +167,7 @@ OneSecHandler mx %11
|
||||
rtl
|
||||
mx %00
|
||||
|
||||
OneSecondCounter ENT
|
||||
dw 0
|
||||
OldOneSecVec ds 4
|
||||
|
||||
; This is OK, it's referenced by a long address
|
||||
VBLTASK hex 00000000
|
||||
dw 0
|
||||
hex 5AA5
|
||||
@ -270,11 +267,6 @@ ClearKbdStrobe sep #$20
|
||||
rts
|
||||
|
||||
; Read the keyboard and paddle controls and return in a game-controller-like format
|
||||
LastKey db 0
|
||||
ReadControl ENT
|
||||
jsr _ReadControl
|
||||
rtl
|
||||
|
||||
_ReadControl
|
||||
pea $0000 ; low byte = key code, high byte = %------AB
|
||||
|
||||
|
68
src/Defs.s
68
src/Defs.s
@ -92,7 +92,10 @@ TileStoreBankAndTileDataBank equ 108
|
||||
TileStoreBankDoubled equ 110
|
||||
UserId equ 112 ; Memory manager user Id to use
|
||||
ToolNum equ 114 ; Tool number assigned to us
|
||||
Next equ 116
|
||||
LastKey equ 116
|
||||
LastTick equ 118
|
||||
|
||||
Next equ 120
|
||||
|
||||
activeSpriteList equ 128 ; 32 bytes for the active sprite list (can persist across frames)
|
||||
; tiletmp equ 178 ; 16 bytes of temp storage for the tile renderers
|
||||
@ -141,8 +144,9 @@ _OP_CACHE equ 156 ; Cache of a relevant operand / oeprato
|
||||
_TILE_ID equ 158 ; Copy of the tile descriptor
|
||||
|
||||
; Define free space the the application to use
|
||||
FREE_SPACE_DP2 equ 160
|
||||
|
||||
; FREE_SPACE_DP2 equ 160
|
||||
DP2_DIRTY_TILE_COUNT equ 160 ; Local copy of dirty tile count to avoid banking
|
||||
DP2_DIRTY_TILE_CALLBACK equ 162
|
||||
; End direct page values
|
||||
|
||||
; EngineMode definitions
|
||||
@ -197,6 +201,7 @@ SPRITE_HFLIP equ $0200
|
||||
; 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
|
||||
VBUFF_TILE_COL_BYTES equ 4
|
||||
VBUFF_SPRITE_STEP equ VBUFF_TILE_ROW_BYTES*3 ; Allocate space fo 16 rows + 8 rows of buffer
|
||||
VBUFF_SPRITE_START equ {8*VBUFF_TILE_ROW_BYTES}+4 ; Start at an offset so $0000 can be used as an empty value
|
||||
VBUFF_SLOT_COUNT equ 48 ; Have space for this many stamps
|
||||
@ -204,31 +209,32 @@ VBUFF_SLOT_COUNT equ 48 ; Have space for this m
|
||||
; This is 13 blocks wide
|
||||
SPRITE_PLANE_SPAN equ VBUFF_STRIDE_BYTES
|
||||
|
||||
; Tile storage parameters
|
||||
TILE_DATA_SPAN equ 4
|
||||
TILE_STORE_WIDTH equ 41
|
||||
TILE_STORE_HEIGHT equ 26
|
||||
MAX_TILES equ {26*41} ; Number of tiles in the code field (41 columns * 26 rows)
|
||||
TILE_STORE_SIZE equ {MAX_TILES*2} ; The tile store contains a tile descriptor in each slot
|
||||
|
||||
TS_TILE_ID equ TILE_STORE_SIZE*0 ; tile descriptor for this location
|
||||
TS_DIRTY equ TILE_STORE_SIZE*1 ; Flag. Used to prevent a tile from being queued multiple times per frame
|
||||
TS_SPRITE_FLAG equ TILE_STORE_SIZE*2 ; Bitfield of all sprites that intersect this tile. 0 if no sprites.
|
||||
TS_TILE_ADDR equ TILE_STORE_SIZE*3 ; cached value, the address of the tiledata for this tile
|
||||
TS_CODE_ADDR_LOW equ TILE_STORE_SIZE*4 ; const value, address of this tile in the code fields
|
||||
TS_CODE_ADDR_HIGH equ TILE_STORE_SIZE*5
|
||||
TS_WORD_OFFSET equ TILE_STORE_SIZE*6 ; const value, word offset value for this tile if LDA (dp),y instructions re used
|
||||
TS_BASE_ADDR equ TILE_STORE_SIZE*7 ; const value, because there are two rows of tiles per bank, this is set to $0000 ot $8000.
|
||||
TS_SCREEN_ADDR equ TILE_STORE_SIZE*8 ; cached value of on-screen location of tile. Used for DirtyRender.
|
||||
;TS_VBUFF_ARRAY_ADDR equ TILE_STORE_SIZE*9 ; const value to an aligned 32-byte array starting at $8000 in TileStore bank
|
||||
|
||||
TS_BASE_TILE_COPY equ TILE_STORE_SIZE*9 ; derived from TS_TILE_ID to optimize tile copy to support sprite rendering
|
||||
TS_BASE_TILE_DISP equ TILE_STORE_SIZE*10 ; derived from TS_TILE_ID to optimize base (non-sprite) tile dispatch in the Render function
|
||||
TS_DIRTY_TILE_DISP equ TILE_STORE_SIZE*11 ; derived from TS_TILE_ID to optimize dirty tile dispatch in the Render function
|
||||
|
||||
; Hold values for up to 4 sprites per tile
|
||||
TS_VBUFF_ADDR_0 equ TILE_STORE_SIZE*12
|
||||
TS_VBUFF_ADDR_1 equ TILE_STORE_SIZE*13
|
||||
TS_VBUFF_ADDR_2 equ TILE_STORE_SIZE*14
|
||||
TS_VBUFF_ADDR_3 equ TILE_STORE_SIZE*15
|
||||
TS_VBUFF_ADDR_COUNT equ TILE_STORE_SIZE*16 ; replace usage of TS_VBUFF_ARRAY_ADDR with this later
|
||||
; External references to data bank
|
||||
TileStore EXT
|
||||
DirtyTileCount EXT
|
||||
DirtyTiles EXT
|
||||
_Sprites EXT
|
||||
TileStore EXT
|
||||
TileStoreLookupYTable EXT
|
||||
TileStoreLookup EXT
|
||||
Col2CodeOffset EXT
|
||||
JTableOffset EXT
|
||||
CodeFieldEvenBRA EXT
|
||||
CodeFieldOddBRA EXT
|
||||
ScreenAddr EXT
|
||||
TileStoreYTable EXT
|
||||
NextCol EXT
|
||||
RTable EXT
|
||||
BlitBuff EXT
|
||||
BTableHigh EXT
|
||||
BTableLow EXT
|
||||
BRowTableHigh EXT
|
||||
BRowTableLow EXT
|
||||
BG1YTable EXT
|
||||
BG1YOffsetTable EXT
|
||||
OldOneSecVec EXT
|
||||
OneSecondCounter EXT
|
||||
Timers EXT
|
||||
DefaultPalette EXT
|
||||
ScreenModeWidth EXT
|
||||
ScreenModeHeight EXT
|
||||
|
18
src/GTE.s
18
src/GTE.s
@ -73,21 +73,3 @@ AllocBank EXT
|
||||
ScreenAddr EXT
|
||||
OneSecondCounter EXT
|
||||
BlitBuff EXT
|
||||
|
||||
;; Helper function to load the GTE User Toolset
|
||||
;GTEInstall
|
||||
; php
|
||||
; ~InitialLoad userId;localToolPath;#0
|
||||
|
||||
; pea $8000 ; User tool
|
||||
; pea $00A5 ; Tool 165
|
||||
; PushLong toolPtr
|
||||
; _SetTSPtr
|
||||
|
||||
; plp
|
||||
; rtl
|
||||
|
||||
; Look for the tool set in the System Tools folder and then next to the application
|
||||
;sysToolPath strl '*:System:Tools:ToolGTE'
|
||||
;localToolPath strl '9:ToolGTE'
|
||||
;toolPtr adrl 0
|
@ -24,11 +24,6 @@ InitGraphics
|
||||
:no_bg1
|
||||
rts
|
||||
|
||||
DefaultPalette dw $0000,$007F,$0090,$0FF0
|
||||
dw $000F,$0080,$0f70,$0FFF
|
||||
dw $0fa9,$0ff0,$00e0,$04DF
|
||||
dw $0d00,$078f,$0ccc,$0FFF
|
||||
|
||||
; Allow the user to dynamically select one of the pre-configured screen sizes, or pass
|
||||
; in a specific width and height. The screen is automatically centered. If this is
|
||||
; not desired, then SetScreenRect should be used directly
|
||||
@ -47,18 +42,6 @@ DefaultPalette dw $0000,$007F,$0090,$0FF0
|
||||
;
|
||||
; X = mode number OR width in pixels (must be multiple of 2)
|
||||
; Y = height in pixels (if X > 8)
|
||||
|
||||
ScreenModeWidth dw 320,272,256,256,280,256,240,288,160,288,160,320
|
||||
ScreenModeHeight dw 200,192,200,176,160,160,160,128,144,192,102,1
|
||||
|
||||
SetScreenMode ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _SetScreenMode
|
||||
plb
|
||||
rtl
|
||||
|
||||
_SetScreenMode
|
||||
cpx #11
|
||||
bcs :direct ; if x > 10, then assume X and Y are the dimensions
|
||||
@ -113,10 +96,6 @@ _GetBorderColor lda #0000
|
||||
rts
|
||||
|
||||
; Set the border color to the accumulator value.
|
||||
SetBorderColor ENT
|
||||
jsr _SetBorderColor
|
||||
rtl
|
||||
|
||||
_SetBorderColor sep #$20 ; ACC = $X_Y, REG = $W_Z
|
||||
eorl BORDER_REG ; ACC = $(X^Y)_(Y^Z)
|
||||
and #$0F ; ACC = $0_(Y^Z)
|
||||
@ -135,17 +114,6 @@ _ClearToColor
|
||||
rts
|
||||
|
||||
; Set a palette values
|
||||
; A = high word of palette data pointer, X = low word of palette data pointer, Y = palette number
|
||||
SetPalette ENT
|
||||
phb ; save old data bank
|
||||
pha ; push 16-bit value
|
||||
plb ; pop 8-bit bank register
|
||||
tya
|
||||
jsr _SetPalette
|
||||
plb ; pop the other half of the 16-bit push off
|
||||
plb ; restore the original data bank
|
||||
rtl
|
||||
|
||||
; A = palette number, X = palette address
|
||||
_SetPalette
|
||||
and #$000F ; palette values are 0 - 15 and each palette is 32 bytes
|
||||
@ -307,7 +275,7 @@ SetScreenRect sty ScreenHeight ; Save the screen height and
|
||||
ldx #0
|
||||
ldy #0
|
||||
:tsloop
|
||||
stal TileStore+TS_SCREEN_ADDR,x
|
||||
sta TileStore+TS_SCREEN_ADDR,x
|
||||
|
||||
clc
|
||||
adc #4 ; Go to the next tile
|
||||
|
26
src/Memory.s
26
src/Memory.s
@ -28,9 +28,9 @@ InitMemory lda EngineMode
|
||||
_NewHandle ; returns LONG Handle on stack
|
||||
plx ; base address of the new handle
|
||||
pla ; high address 00XX of the new handle (bank)
|
||||
_Deref
|
||||
stx Buff00
|
||||
sta Buff00+2
|
||||
; _Deref
|
||||
; stx Buff00
|
||||
; sta Buff00+2
|
||||
:no_bnk0_buff
|
||||
|
||||
PushLong #0 ; space for result
|
||||
@ -41,9 +41,9 @@ InitMemory lda EngineMode
|
||||
_NewHandle ; returns LONG Handle on stack
|
||||
plx ; base address of the new handle
|
||||
pla ; high address 00XX of the new handle (bank)
|
||||
_Deref
|
||||
stx Buff01
|
||||
sta Buff01+2
|
||||
; _Deref
|
||||
; stx Buff01
|
||||
; sta Buff01+2
|
||||
|
||||
PushLong #0 ; space for result
|
||||
|
||||
@ -153,8 +153,8 @@ InitMemory lda EngineMode
|
||||
:exit
|
||||
rts
|
||||
|
||||
Buff00 ds 4
|
||||
Buff01 ds 4
|
||||
;Buff00 ds 4
|
||||
;Buff01 ds 4
|
||||
|
||||
; Bank allocator (for one full, fixed bank of memory. Can be immediately deferenced)
|
||||
|
||||
@ -172,14 +172,6 @@ AllocOneBank PushLong #0
|
||||
rts
|
||||
|
||||
; Variation that returns the pointer in the X/A registers (X = low, A = high)
|
||||
AllocBank ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr AllocOneBank2
|
||||
plb
|
||||
rtl
|
||||
|
||||
AllocOneBank2 PushLong #0
|
||||
PushLong #$10000
|
||||
PushWord UserId
|
||||
@ -190,5 +182,3 @@ AllocOneBank2 PushLong #0
|
||||
pla ; high address 00XX of the new handle (bank)
|
||||
_Deref
|
||||
rts
|
||||
|
||||
|
||||
|
25
src/Render.s
25
src/Render.s
@ -21,12 +21,12 @@
|
||||
; used in all of the other loops
|
||||
_Render
|
||||
jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen
|
||||
jsr _ApplyBG1YPos
|
||||
; 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 _ApplyBG1XPosPre
|
||||
|
||||
; jsr _RenderSprites ; Once the BG0 X and Y positions are committed, update sprite data
|
||||
|
||||
@ -36,7 +36,7 @@ _Render
|
||||
jsr _ApplyTilesFast ; This function actually draws the new tiles into the code field
|
||||
|
||||
jsr _ApplyBG0XPos ; Patch the code field instructions with exit BRA opcode
|
||||
jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position
|
||||
; jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position
|
||||
|
||||
; The code fields are locked in now and ready to be rendered
|
||||
|
||||
@ -99,26 +99,13 @@ _ApplyTilesFast
|
||||
adc #$100 ; move to the next page
|
||||
tcd
|
||||
|
||||
ldy DirtyTileCount
|
||||
beq :out
|
||||
|
||||
:loop
|
||||
; Retrieve the offset of the next dirty Tile Store items in the X-register
|
||||
lda DirtyTileCount ; Cache the dirty tile count
|
||||
sta DP2_DIRTY_TILE_COUNT
|
||||
|
||||
jsr _PopDirtyTile2
|
||||
|
||||
; Call the generic dispatch with the Tile Store record pointer at by the X-register.
|
||||
stz DirtyTileCount
|
||||
|
||||
phb
|
||||
jsr _RenderTileFast
|
||||
plb
|
||||
|
||||
; Loop again until the list of dirty tiles is empty
|
||||
|
||||
ldy DirtyTileCount
|
||||
bne :loop
|
||||
|
||||
:out
|
||||
tdc ; Move back to the original direct page
|
||||
sec
|
||||
sbc #$100
|
||||
|
15
src/Script.s
15
src/Script.s
@ -35,8 +35,7 @@
|
||||
; timer's user data section.
|
||||
StartScript ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _SetDataBank
|
||||
|
||||
phx ; Save the script array address
|
||||
pha
|
||||
@ -69,14 +68,6 @@ ARG1 equ 2
|
||||
ARG2 equ 4
|
||||
ARG3 equ 6
|
||||
|
||||
DoScriptSeq ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsl _DoScriptSeq ; Yes, this is a special JSL, because _DoScriptSeq is a time callback
|
||||
plb
|
||||
rtl
|
||||
|
||||
_DoScriptSeq
|
||||
phx ; save the timer index; will need to update user data at the end
|
||||
phb ; save the current data bank
|
||||
@ -180,9 +171,9 @@ _SetDTile
|
||||
|
||||
_UserCallback
|
||||
lda: ARG1,y
|
||||
sta :dispatch+1
|
||||
stal :dispatch+1
|
||||
lda: ARG1+1,y
|
||||
sta :dispatch+2
|
||||
stal :dispatch+2
|
||||
lda: ARG3,y
|
||||
:dispatch jsl $000000
|
||||
brl _dss_cmd_rtn
|
||||
|
139
src/Sprite.s
139
src/Sprite.s
@ -27,24 +27,6 @@ InitSprites
|
||||
; dex
|
||||
; bpl :loop3
|
||||
|
||||
; Initialize the VBUFF address offsets in the data and mask banks for each sprite
|
||||
;
|
||||
; The internal grid 12 tiles wide where each sprite has a 2x2 interior square with a
|
||||
; tile-size buffer all around. We pre-render each sprite with all four vert/horz flips
|
||||
;
|
||||
; Eventually we should be able to have a separate rendering path for vertically flipped
|
||||
; sprites and will be able to double the capacity of the stamp buffer
|
||||
|
||||
ldx #0
|
||||
lda #VBUFF_SPRITE_START
|
||||
clc
|
||||
:loop4 sta VBuffAddrTable,x
|
||||
adc #VBUFF_SPRITE_STEP
|
||||
inx
|
||||
inx
|
||||
cpx #VBUFF_SLOT_COUNT*2
|
||||
bcc :loop4
|
||||
|
||||
; Precalculate some bank values
|
||||
jsr _CacheSpriteBanks
|
||||
rts
|
||||
@ -68,38 +50,25 @@ InitSprites
|
||||
; the stamp every time. So this allows users to create stamps in advance and then
|
||||
; assign them to the sprites as needed.
|
||||
;
|
||||
; Currently, we support a maximum of 48 stamps.
|
||||
; Note that the user had full freedom to create a stamp at any VBUFF address, however,
|
||||
; without leaving a buffer around each stamp, graphical corruption will occur. It is
|
||||
; recommended that the defines for VBUFF_SPRITE_START, VBUFF_TILE_ROW_BYTES and
|
||||
; VBUFF_TILE_COL_BYTES to calculate tile-aligned corner locations to lay out the
|
||||
; sprite stamps in VBUFF memory.
|
||||
;
|
||||
; Input:
|
||||
; A = sprite descriptor
|
||||
; X = stamp slot
|
||||
; Return:
|
||||
; A = vbuff address to be assigned to Sprite[VBUFF_ADDR]
|
||||
CreateSpriteStamp ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _CreateSpriteStamp
|
||||
plb
|
||||
rtl
|
||||
|
||||
; Y = vbuff address
|
||||
;
|
||||
; The Sprite[VBUFF_ADDR] property must be set to the vbuff address passed into this function
|
||||
; to bind the sprite stamp to the sprite record.
|
||||
_CreateSpriteStamp
|
||||
pha ; Save the descriptor
|
||||
jsr _GetBaseTileAddr ; Get the address of the tile data
|
||||
pha
|
||||
|
||||
txa
|
||||
asl
|
||||
tax
|
||||
ldy VBuffAddrTable,x ; Load the address of the stamp slot
|
||||
|
||||
plx ; Pop the tile address
|
||||
tax ; Tile data address
|
||||
pla ; Pop the sprite ID
|
||||
phy ; VBUFF_ADDR value
|
||||
jsr _DrawSpriteStamp ; Render the sprite data and create a stamp
|
||||
|
||||
pla ; Pop the VBUFF_ADDR and return
|
||||
rts
|
||||
jmp _DrawSpriteStamp ; Render the sprite data and create a stamp
|
||||
|
||||
; Add a new sprite to the rendering pipeline
|
||||
;
|
||||
@ -123,14 +92,6 @@ _CreateSpriteStamp
|
||||
; A = tileId + flags
|
||||
; Y = High Byte = x-pos, Low Byte = y-pos
|
||||
; X = Sprite Slot (0 - 15)
|
||||
AddSprite ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _AddSprite
|
||||
plb
|
||||
rtl
|
||||
|
||||
_AddSprite
|
||||
pha
|
||||
txa
|
||||
@ -888,14 +849,6 @@ _PrecalcAllSpriteInfo
|
||||
; picked up in the next AddSprite.
|
||||
;
|
||||
; A = Sprite ID
|
||||
RemoveSprite ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _RemoveSprite
|
||||
plb
|
||||
rtl
|
||||
|
||||
_RemoveSprite
|
||||
cmp #MAX_SPRITES
|
||||
bcc :ok
|
||||
@ -917,14 +870,6 @@ _RemoveSprite
|
||||
; A = Sprite ID
|
||||
; X = New Sprite Flags
|
||||
; Y = New Sprite Stamp Address
|
||||
UpdateSprite ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _UpdateSprite
|
||||
plb
|
||||
rtl
|
||||
|
||||
_UpdateSprite
|
||||
cmp #MAX_SPRITES
|
||||
bcc :ok
|
||||
@ -961,14 +906,6 @@ _UpdateSprite
|
||||
; A = sprite ID
|
||||
; X = x position
|
||||
; Y = y position
|
||||
MoveSprite ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _MoveSprite
|
||||
plb
|
||||
rtl
|
||||
|
||||
_MoveSprite
|
||||
cmp #MAX_SPRITES
|
||||
bcc :ok
|
||||
@ -999,57 +936,3 @@ _MoveSprite
|
||||
sta _Sprites+SPRITE_STATUS,x
|
||||
|
||||
jmp _PrecalcAllSpriteInfo ; Can be specialized to only update (x,y) values
|
||||
|
||||
; Sprite data structures. We cache quite a few pieces of information about the sprite
|
||||
; to make calculations faster, so this is hidden from the caller.
|
||||
;
|
||||
;
|
||||
; Number of "off-screen" lines above logical (0,0)
|
||||
; NUM_BUFF_LINES equ 24
|
||||
|
||||
MAX_SPRITES equ 16
|
||||
SPRITE_REC_SIZE equ 52
|
||||
|
||||
; 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
|
||||
; available to the AddSprite function until the next frame.
|
||||
|
||||
SPRITE_STATUS_EMPTY equ $0000 ; If the status value is zero, this sprite slot is available
|
||||
SPRITE_STATUS_OCCUPIED equ $8000 ; Set the MSB to flag it as occupied
|
||||
SPRITE_STATUS_ADDED equ $0001 ; Sprite was just added (new sprite)
|
||||
SPRITE_STATUS_MOVED equ $0002 ; Sprite's position was changed
|
||||
SPRITE_STATUS_UPDATED equ $0004 ; Sprite's non-position attributes were changed
|
||||
SPRITE_STATUS_REMOVED equ $0008 ; Sprite has been removed.
|
||||
|
||||
SPRITE_STATUS equ {MAX_SPRITES*0}
|
||||
; TILE_DATA_OFFSET equ {MAX_SPRITES*2}
|
||||
VBUFF_ADDR equ {MAX_SPRITES*4} ; Base address of the sprite's stamp in the data/mask banks
|
||||
SPRITE_ID equ {MAX_SPRITES*6}
|
||||
SPRITE_X equ {MAX_SPRITES*8}
|
||||
SPRITE_Y equ {MAX_SPRITES*10}
|
||||
; TILE_STORE_ADDR_1 equ {MAX_SPRITES*12}
|
||||
TS_LOOKUP_INDEX equ {MAX_SPRITES*12} ; The index into the TileStoreLookup table corresponding to the top-left corner of the sprite
|
||||
; TILE_STORE_ADDR_2 equ {MAX_SPRITES*14}
|
||||
TS_COVERAGE_SIZE equ {MAX_SPRITES*14} ; Index into the lookup table of how many TileStore tiles are covered by this sprite
|
||||
;TILE_STORE_ADDR_3 equ {MAX_SPRITES*16}
|
||||
TS_VBUFF_BASE_ADDR equ {MAX_SPRITES*16} ; Fixed address of the TS_VBUFF_X memory locations
|
||||
;TILE_STORE_ADDR_4 equ {MAX_SPRITES*18}
|
||||
;TILE_STORE_ADDR_5 equ {MAX_SPRITES*20}
|
||||
;TILE_STORE_ADDR_6 equ {MAX_SPRITES*22}
|
||||
;TILE_STORE_ADDR_7 equ {MAX_SPRITES*24}
|
||||
;TILE_STORE_ADDR_8 equ {MAX_SPRITES*26}
|
||||
;TILE_STORE_ADDR_9 equ {MAX_SPRITES*28}
|
||||
;TILE_STORE_ADDR_10 equ {MAX_SPRITES*30}
|
||||
SPRITE_DISP equ {MAX_SPRITES*32} ; cached address of the specific stamp based on flags
|
||||
SPRITE_CLIP_LEFT equ {MAX_SPRITES*34}
|
||||
SPRITE_CLIP_RIGHT equ {MAX_SPRITES*36}
|
||||
SPRITE_CLIP_TOP equ {MAX_SPRITES*38}
|
||||
SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*40}
|
||||
IS_OFF_SCREEN equ {MAX_SPRITES*42}
|
||||
SPRITE_WIDTH equ {MAX_SPRITES*44}
|
||||
SPRITE_HEIGHT equ {MAX_SPRITES*46}
|
||||
SPRITE_CLIP_WIDTH equ {MAX_SPRITES*48}
|
||||
SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*50}
|
||||
|
||||
_Sprites ds SPRITE_REC_SIZE*MAX_SPRITES
|
||||
|
@ -134,8 +134,11 @@ InitTiles
|
||||
lda :row ; Set the long address of where this tile
|
||||
asl ; exists in the code fields
|
||||
tay
|
||||
lda BRowTableHigh,y
|
||||
lda #>TileStore ; get middle 16 bits: "00 -->BBHH<-- LL"
|
||||
and #$FF00 ; merge with code field bank
|
||||
ora BRowTableHigh,y
|
||||
stal TileStore+TS_CODE_ADDR_HIGH,x ; High word of the tile address (just the bank)
|
||||
|
||||
lda BRowTableLow,y
|
||||
stal TileStore+TS_BASE_ADDR,x ; May not be needed later if we can figure out the right constant...
|
||||
|
||||
|
55
src/Timer.s
55
src/Timer.s
@ -1,37 +1,5 @@
|
||||
; Timer implementation
|
||||
;
|
||||
; The engire provides four timer slot that can be used by one-shot or
|
||||
; recurring timers. Each timer is given an initial tick count, a
|
||||
; reset tick count (0 = one-shot), and an action to perform.
|
||||
;
|
||||
; The timers handle overflow, so if a recurring timer has a tick count of 3
|
||||
; and 7 VBL ticks have passed, then the timer will be fired twice and
|
||||
; a tick count of 2 will be set.
|
||||
;
|
||||
; As such, the timers are appropriate to drive physical and other game
|
||||
; behaviors at a frame-independent rate.
|
||||
;
|
||||
; A collection of 4 timers that are triggered when their countdown
|
||||
; goes below zero. Each timer takes up 16 bytes
|
||||
;
|
||||
; A timer can fire multiple times during a singular evaluation. For example, if the
|
||||
; timer delay is set to 1 and 3 VBL ticks happen, then the timer delta is -2, will fire,
|
||||
; have the delay added and get -1, fire again, increment to zero, first again and then
|
||||
; finally reset to 1.
|
||||
;
|
||||
; +0 counter decremented by the number of ticks since last run
|
||||
; +2 reset copied into counter when triggered. 0 turns off the timer.
|
||||
; +4 addr long address of timer routine
|
||||
; +8 user 8 bytes of user data space for timer state
|
||||
MAX_TIMERS equ 4
|
||||
TIMER_REC_SIZE equ 16
|
||||
mx %00
|
||||
|
||||
lastTick ds 2
|
||||
Timers ds TIMER_REC_SIZE*MAX_TIMERS
|
||||
|
||||
GetVBLTicks ENT
|
||||
jsr _GetVBLTicks
|
||||
rtl
|
||||
_GetVBLTicks
|
||||
PushLong #0
|
||||
_GetTick
|
||||
@ -42,7 +10,7 @@ _GetVBLTicks
|
||||
; Initialize the timers
|
||||
InitTimers
|
||||
jsr _GetVBLTicks
|
||||
sta lastTick
|
||||
sta LastTick
|
||||
|
||||
lda #0
|
||||
ldx #{TIMER_REC_SIZE*MAX_TIMERS}-2
|
||||
@ -71,8 +39,7 @@ AddTimer ENT
|
||||
pha
|
||||
phy
|
||||
|
||||
phk
|
||||
plb
|
||||
jsr _SetDataBank
|
||||
|
||||
ldx #0
|
||||
:loop lda Timers,x ; If the counter is zero, timer is free
|
||||
@ -125,8 +92,7 @@ AddTimer ENT
|
||||
; A = Timer ID
|
||||
RemoveTimer ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _SetDataBank
|
||||
cmp #{TIMER_REC_SIZE*{MAX_TIMERS-1}}+1
|
||||
bcs :exit
|
||||
|
||||
@ -143,17 +109,16 @@ RemoveTimer ENT
|
||||
; Execute the timer functions
|
||||
DoTimers ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _SetDataBank
|
||||
|
||||
jsr _GetVBLTicks
|
||||
|
||||
cmp lastTick ; Throttle to 60 fps
|
||||
cmp LastTick ; Throttle to 60 fps
|
||||
beq :exit
|
||||
tax ; Calculate the increment
|
||||
sec
|
||||
sbc lastTick
|
||||
stx lastTick
|
||||
sbc LastTick
|
||||
stx LastTick
|
||||
|
||||
; We don't want times to fire excessively. If the timer has nt been evaluated for over
|
||||
; one second, then just skip processing and wait for the next call.
|
||||
@ -186,9 +151,9 @@ _DoTimers
|
||||
phx ; Save our index
|
||||
|
||||
lda Timers+4,x ; execute the timer callback
|
||||
sta :dispatch+1
|
||||
stal :dispatch+1
|
||||
lda Timers+5,x
|
||||
sta :dispatch+2
|
||||
stal :dispatch+2
|
||||
:dispatch jsl $000000
|
||||
|
||||
plx
|
||||
|
21
src/Tool.s
21
src/Tool.s
@ -10,6 +10,7 @@
|
||||
use Core.MACS.s
|
||||
|
||||
use Defs.s
|
||||
use static/TileStoreDefs.s
|
||||
|
||||
ToStrip equ $E10184
|
||||
|
||||
@ -18,8 +19,7 @@ _TSEntry mac
|
||||
phd
|
||||
phb
|
||||
tcd
|
||||
phk ; Default to setting the data back to the current bank.
|
||||
plb
|
||||
jsr _SetDataBank
|
||||
<<<
|
||||
|
||||
_TSExit mac
|
||||
@ -53,6 +53,14 @@ _CallTable
|
||||
adrl _TSLoadTileSet-1
|
||||
_CTEnd
|
||||
|
||||
; Helper function to set the data back to the toolset default
|
||||
_SetDataBank sep #$20
|
||||
lda #^TileStore
|
||||
pha
|
||||
plb
|
||||
rep #$20
|
||||
rts
|
||||
|
||||
; Do nothing when the tool set is installed
|
||||
_TSBootInit
|
||||
lda #0
|
||||
@ -86,8 +94,7 @@ zpToUse = userId+4
|
||||
sta EngineMode
|
||||
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _SetDataBank
|
||||
jsr _CoreStartUp ; Initialize the library
|
||||
plb
|
||||
|
||||
@ -114,8 +121,7 @@ _TSShutDown
|
||||
tcd ; Set the direct page for the toolset
|
||||
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _SetDataBank
|
||||
jsr _CoreShutDown ; Shut down the library
|
||||
plb
|
||||
|
||||
@ -201,7 +207,7 @@ xTile equ FirstParam+4
|
||||
tax
|
||||
lda yTile,s ; Valid range [0, 25] (26 rows)
|
||||
tay
|
||||
lda tileId
|
||||
lda tileId,s
|
||||
jsr _SetTile
|
||||
|
||||
_TSExit #0;#6
|
||||
@ -255,7 +261,6 @@ TSPtr equ FirstParam
|
||||
put blitter/BG1.s
|
||||
put blitter/Template.s
|
||||
put blitter/TemplateUtils.s
|
||||
put blitter/Tables.s
|
||||
put blitter/Blitter.s
|
||||
put blitter/TileProcs.s
|
||||
put blitter/Tiles00000.s
|
||||
|
@ -8,14 +8,6 @@ _InitBG0
|
||||
;
|
||||
; A=low word of picture address
|
||||
; X=high word of pixture address
|
||||
CopyBinToField ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _CopyBinToField
|
||||
plb
|
||||
rtl
|
||||
|
||||
_CopyBinToField
|
||||
:srcptr equ tmp0
|
||||
:line_cnt equ tmp2
|
||||
@ -222,14 +214,6 @@ _CopyBinToField
|
||||
; X=high workd of pixture address
|
||||
;
|
||||
; Picture must be within one bank
|
||||
CopyPicToField ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _CopyPicToField
|
||||
plb
|
||||
rtl
|
||||
|
||||
_CopyPicToField
|
||||
:srcptr equ tmp0
|
||||
:line_cnt equ tmp2
|
||||
|
@ -9,14 +9,6 @@ _InitBG1
|
||||
; A=low word of picture address
|
||||
; X=high word of pixture address
|
||||
; Y=high word of BG1 bank
|
||||
CopyBinToBG1 ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _CopyBinToBG1
|
||||
plb
|
||||
rtl
|
||||
|
||||
_CopyBinToBG1
|
||||
:src_width equ tmp6
|
||||
:src_height equ tmp7
|
||||
@ -39,14 +31,6 @@ _CopyBinToBG1
|
||||
; A=low word of picture address
|
||||
; X=high word of pixture address
|
||||
; Y=high word of BG1 bank
|
||||
CopyPicToBG1 ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _CopyPicToBG1
|
||||
plb
|
||||
rtl
|
||||
|
||||
_CopyPicToBG1
|
||||
:src_width equ tmp6
|
||||
:src_height equ tmp7
|
||||
@ -221,6 +205,8 @@ _ApplyBG1YPos
|
||||
:draw_count equ tmp2
|
||||
:ytbl_idx equ tmp3
|
||||
|
||||
phb ; Save the bank
|
||||
|
||||
lda BG1StartY
|
||||
jsr Mod208
|
||||
sta BG1StartYMod208
|
||||
@ -277,7 +263,6 @@ _ApplyBG1YPos
|
||||
|
||||
jne :loop
|
||||
|
||||
phk
|
||||
plb
|
||||
rts
|
||||
|
||||
@ -369,16 +354,14 @@ CopyBG1YTableToBG1Addr2
|
||||
phx
|
||||
phb
|
||||
|
||||
phk ; restore access to this bank
|
||||
plb
|
||||
jsr _SetDataBank ; restore access to this bank
|
||||
ldy BG1OffsetIndex ; Get the offset and save the values
|
||||
jsr SaveBG1OffsetValues
|
||||
|
||||
plb
|
||||
plx ; x is used directly in this routine
|
||||
ply
|
||||
jsr ApplyBG1OffsetValues
|
||||
rts
|
||||
jmp ApplyBG1OffsetValues
|
||||
|
||||
SaveBG1OffsetValues
|
||||
jmp (:tbl,x)
|
||||
|
@ -33,10 +33,10 @@ _BltRange
|
||||
|
||||
sep #$20 ; 8-bit Acc
|
||||
lda BTableHigh,x ; patch in the bank
|
||||
sta blt_entry+3
|
||||
stal blt_entry+3
|
||||
|
||||
lda BTableLow+1,x ; patch in the page
|
||||
sta blt_entry+2
|
||||
stal blt_entry+2
|
||||
|
||||
; The way we patch the exit code is subtle, but very fast. The CODE_EXIT offset points to
|
||||
; an JMP/JML instruction that transitions to the next line after all of the code has been
|
||||
@ -74,6 +74,7 @@ _BltRange
|
||||
tsc ; save the stack pointer
|
||||
stal stk_save+1
|
||||
|
||||
bra blt_return
|
||||
blt_entry jml $000000 ; Jump into the blitter code $XX/YY00
|
||||
|
||||
blt_return _R0W0
|
||||
@ -88,5 +89,6 @@ stk_save lda #0000 ; load the stack
|
||||
sta [:exit_ptr],y
|
||||
rep #$20
|
||||
|
||||
blt_out
|
||||
plb ; restore the bank
|
||||
rts
|
||||
|
@ -21,6 +21,8 @@ _RestoreBG0Opcodes
|
||||
:draw_count_x2 equ tmp3
|
||||
:exit_offset equ tmp4
|
||||
|
||||
phb ; Save data bank
|
||||
|
||||
asl
|
||||
sta :virt_line_x2 ; Keep track of it
|
||||
|
||||
@ -75,7 +77,6 @@ _RestoreBG0Opcodes
|
||||
stz LastPatchOffset ; Clear the value once completed
|
||||
|
||||
:out
|
||||
phk
|
||||
plb
|
||||
rts
|
||||
|
||||
@ -279,6 +280,7 @@ _ApplyBG0XPos
|
||||
; 2. Writes the BRA instruction to exit the code field
|
||||
; 3. Writes the JMP entry point to enter the code field
|
||||
|
||||
phb ; Save the existing bank
|
||||
:loop
|
||||
lda :virt_line
|
||||
asl ; This will clear the carry bit
|
||||
@ -353,7 +355,7 @@ _ApplyBG0XPos
|
||||
ldx :draw_count_x2
|
||||
ldy :base_address ; Y-register is preserved, this can be removed
|
||||
pei :exit_address
|
||||
jmp :SaveHighOperand ; Only used once, so "inline" it
|
||||
jmp :SaveHighOperand ; Only used once, so "inline" it
|
||||
:save_high_op_rtn
|
||||
|
||||
:not_odd
|
||||
@ -374,7 +376,6 @@ _ApplyBG0XPos
|
||||
|
||||
jne :loop
|
||||
|
||||
phk
|
||||
plb
|
||||
rts
|
||||
|
||||
|
@ -15,13 +15,13 @@
|
||||
_PEISlam
|
||||
lda ScreenWidth
|
||||
dec
|
||||
sta :screen_width_1 ; save the width-1 outside of the direct page
|
||||
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
|
||||
sta :inner+1
|
||||
stal :inner+1
|
||||
|
||||
phx
|
||||
tya
|
||||
@ -43,7 +43,7 @@ _PEISlam
|
||||
tcd ; screen address to the direct page register
|
||||
|
||||
tsc
|
||||
sta :stk_save ; save the stack pointer to restore later
|
||||
stal :stk_save ; save the stack pointer to restore later
|
||||
|
||||
clc ; clear before the loop -- nothing in the loop affect the carry bit
|
||||
brl :outer ; hop into the entry point.
|
||||
@ -57,7 +57,7 @@ _PEISlam
|
||||
tdc ; Move to the next line
|
||||
adc #160
|
||||
tcd
|
||||
adc :screen_width_1
|
||||
adcl :screen_width_1
|
||||
tcs
|
||||
|
||||
dey ; decrement the total counter, if zero then we're done
|
||||
@ -79,7 +79,7 @@ _PEISlam
|
||||
:restore
|
||||
tsx ; save the current stack
|
||||
_R0W0 ; restore the execution environment and
|
||||
lda :stk_save ; give a few cycles to catch some interrupts
|
||||
ldal :stk_save ; give a few cycles to catch some interrupts
|
||||
tcs
|
||||
cli ; fall through here -- saves a BRA instruction
|
||||
|
||||
@ -92,7 +92,7 @@ _PEISlam
|
||||
|
||||
:exit
|
||||
_R0W0
|
||||
lda :stk_save
|
||||
ldal :stk_save
|
||||
tcs
|
||||
cli
|
||||
|
||||
|
@ -10,14 +10,6 @@
|
||||
; of the BG1 data buffer provides the "fill value".
|
||||
|
||||
ANGLEBNK ext
|
||||
ApplyBG1XPosAngle ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _ApplyBG1XPosAngle
|
||||
plb
|
||||
rtl
|
||||
|
||||
_ApplyBG1XPosAngle
|
||||
; phy
|
||||
|
||||
@ -62,14 +54,6 @@ _ApplyBG1XPosAngle
|
||||
pld
|
||||
rts
|
||||
|
||||
ApplyBG1YPosAngle ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _ApplyBG1YPosAngle
|
||||
plb
|
||||
rtl
|
||||
|
||||
_ApplyBG1YPosAngle
|
||||
:virt_line equ tmp0
|
||||
:lines_left equ tmp1
|
||||
@ -90,6 +74,7 @@ _ApplyBG1YPosAngle
|
||||
lda ScreenHeight
|
||||
sta :lines_left
|
||||
|
||||
phb
|
||||
:loop
|
||||
lda :virt_line
|
||||
asl
|
||||
@ -141,7 +126,6 @@ _ApplyBG1YPosAngle
|
||||
|
||||
jne :loop
|
||||
|
||||
phk
|
||||
plb
|
||||
rts
|
||||
|
||||
@ -155,14 +139,13 @@ CopyAngleYTableToBG1Addr
|
||||
phx
|
||||
phb
|
||||
|
||||
phk ; restore access to this bank
|
||||
plb
|
||||
|
||||
jsr _SetDataBank ; restore access to this bank
|
||||
jsr SaveBG1AngleValues
|
||||
|
||||
plb
|
||||
plx ; x is used directly in this routine
|
||||
jsr ApplyBG1OffsetValues
|
||||
rts
|
||||
jmp ApplyBG1OffsetValues
|
||||
|
||||
SaveBG1AngleValues
|
||||
jmp (:tbl,x)
|
||||
|
@ -9,15 +9,7 @@
|
||||
; on the SHR screen or the current value of StartY
|
||||
;
|
||||
; This could be made faster by forcing a SCB array to be copied into PEAs ahead of time, but this
|
||||
; is a bit more flexible
|
||||
BltSCB ENT
|
||||
phb
|
||||
phk
|
||||
plb
|
||||
jsr _BltSCB
|
||||
plb
|
||||
rtl
|
||||
|
||||
; is a bit more flexible
|
||||
_BltSCBOut
|
||||
rts
|
||||
_BltSCB
|
||||