Compare commits

...

18 Commits

Author SHA1 Message Date
Lucas Scharenbroich 576c402d0b Streamline the OAM copy 2023-06-27 21:49:52 -05:00
Lucas Scharenbroich 4c13373bd3 Fix addressing bug that caused serious overdraw 2023-06-27 21:33:59 -05:00
Lucas Scharenbroich cdfc414a9f Fix relative offset from interrupt enable code and fix order bug in exit code path 2023-06-27 21:32:21 -05:00
Lucas Scharenbroich 4645929596 Pretty 2023-06-27 21:31:40 -05:00
Lucas Scharenbroich 1fcf176012 Fix ancient bug in Col2CodeOffset table 2023-06-27 21:31:13 -05:00
Lucas Scharenbroich 29838abb40 Shorter size-agnostic implemention of SetDataBank 2023-06-27 21:30:30 -05:00
Lucas Scharenbroich 2cc839b2c6 Add offset for the interrupt entry point 2023-06-27 21:29:50 -05:00
Lucas Scharenbroich 9c24715217 Enable interrupt processing in the lite blitter 2023-06-27 21:29:32 -05:00
Lucas Scharenbroich e2c9480de4 Better single-step control 2023-06-27 21:27:45 -05:00
Lucas Scharenbroich ae1611fcc9 Add build target for APUSim 2023-06-27 21:26:53 -05:00
Lucas Scharenbroich bcebe6367f Turn APU back on 2023-06-27 21:26:33 -05:00
Lucas Scharenbroich 139a784bfa Fix off-by-one on tile update range and some comment typos 2023-06-27 21:26:17 -05:00
Lucas Scharenbroich a5af4c206c Add hook for half and quarter speed updates 2023-06-27 21:24:59 -05:00
Lucas Scharenbroich cb5e9b0b25 Add interrupt breaks in the lite blitter to allow music to keep playing 2023-06-26 17:17:23 -05:00
Lucas Scharenbroich 6a0b8cbdc1 Fix restore code field bug. SMB demo can use renderer 2023-06-26 14:15:20 -05:00
Lucas Scharenbroich eb548343a7 Fix offset calc 2023-06-26 13:19:12 -05:00
Lucas Scharenbroich 2013771524 Lite render mode bugfixes 2023-06-26 11:21:05 -05:00
Lucas Scharenbroich 94dafd2093 Checkpoint 2023-06-25 21:45:34 -05:00
33 changed files with 16605 additions and 184 deletions

1
demos/lite/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
GTEToolDemo

341
demos/lite/App.Main.s Normal file
View File

@ -0,0 +1,341 @@
REL
DSK MAINSEG
use Locator.Macs
use Load.Macs
use Mem.Macs
use Misc.Macs
use Util.Macs
use EDS.GSOS.Macs
use GTE.Macs
mx %00
TSZelda EXT ; tileset buffer
ScreenX equ 0
ScreenY equ 2
Tmp0 equ 4
Tmp1 equ 6
KeyState equ 8
Selected equ 10
Flips equ 12
DTile equ 14
Tmp2 equ 16
ScreenWidth equ 18
ScreenHeight equ 20
SpriteFlags equ 22
frameCount equ 24
OldOneSecondCounter equ 26
SpriteAddr equ 28
RenderMode equ 30
; Control modes
DefaultMode equ RENDER_WITH_SHADOWING
SlowSprites equ 0
; Typical init
phk
plb
sta MyUserId ; GS/OS passes the memory manager user ID for the application into the program
tdc
sta MyDirectPage ; Keep a copy for the overlay callback
_MTStartUp ; GTE requires the miscellaneous toolset to be running
lda #ENGINE_MODE_USER_TOOL
jsr GTEStartUp ; Load and install the GTE User Tool
; Init local variables
stz frameCount
; Initialize the graphics screen to a 256x160 playfield
pea #160
pea #200
_GTESetScreenMode
; Load a tileset
pea 0
pea 360
pea #^TSZelda
pea #TSZelda
_GTELoadTileSet
; Set the palette
pea $0000
pea #^palette
pea #palette
_GTESetPalette
jsr SetLimits
pha
_GTEGetSeconds
pla
sta OldOneSecondCounter
; Manually fill in the 41x26 tiles of the TileStore with a test pattern of trees
jsr _fillTileStore
pha
pha
pea liteBlitter
_GTEGetAddress
pla
plx
; Set the screen coordinates
lda #0
sta ScreenX
lda #0
sta ScreenY
stz Selected
stz Flips
; Very simple actions
:evt_loop
pha ; space for result, with pattern
_GTEReadControl
pla
jsr HandleKeys ; Do the generic key handlers
bcs :do_more
bra :evt_loop
:do_more
bit #PAD_KEY_DOWN
beq :evt_loop
and #$007F
cmp #'a'
bne :not_a
dec ScreenX
:not_a cmp #'s'
bne :not_s
inc ScreenX
:not_s
:do_render jsr :next_frame
brl :evt_loop
:next_frame
pei ScreenX
pei ScreenY
_GTESetBG0Origin
pea $FFFE
_GTERender
; Update the performance counters
inc frameCount
pha
_GTEGetSeconds
pla
cmp OldOneSecondCounter
beq :noudt
sta OldOneSecondCounter
jsr UdtOverlay
stz frameCount
:noudt
rts
; Shut down everything
Exit
_GTEShutDown
_QuitGS qtRec
qtRec adrl $0000
da $00
; Called by StartUp function callbacks when the screen size changes
SetLimits
pha ; Allocate space for x, y, width, height
pha
pha
pha
_GTEGetScreenInfo
pla
pla ; Discard screen corner
pla
sec
sbc #8
sta ScreenWidth ; Pre-adjust to keep sprites on the visible playfield (for compiled sprites)
pla
sec
sbc #16
sta ScreenHeight
rts
_fillTileStore
sta Tmp2
stz Tmp0
:oloop
stz Tmp1
:iloop
ldx Tmp1
ldy Tmp0
jsr _drawTree
lda Tmp1
clc
adc #2
sta Tmp1
cmp #40
bcc :iloop
lda Tmp0
clc
adc #2
sta Tmp0
cmp #25
bcc :oloop
rts
; Tile 65 Tile 66
; Tile 97 Tile 98
_drawTreeFront
phx
phy
pea #65+TILE_PRIORITY_BIT
inx
phx
phy
pea #66+TILE_PRIORITY_BIT
iny
phx
phy
pea #98+TILE_PRIORITY_BIT
dex
phx
phy
pea #97+TILE_PRIORITY_BIT
_GTESetTile
_GTESetTile
_GTESetTile
_GTESetTile
rts
_drawTree
phx
phy
pea #65
inx
phx
phy
pea #66
iny
phx
phy
pea #98
dex
phx
phy
pea #97
_GTESetTile
_GTESetTile
_GTESetTile
_GTESetTile
rts
_drawTreeH
phx
phy
pea #66+TILE_HFLIP_BIT
inx
phx
phy
pea #65+TILE_HFLIP_BIT
iny
phx
phy
pea #97+TILE_HFLIP_BIT
dex
phx
phy
pea #98+TILE_HFLIP_BIT
_GTESetTile
_GTESetTile
_GTESetTile
_GTESetTile
rts
_drawTreeV
phx
phy
pea #97+TILE_VFLIP_BIT
inx
phx
phy
pea #98+TILE_VFLIP_BIT
iny
phx
phy
pea #66+TILE_VFLIP_BIT
dex
phx
phy
pea #65+TILE_VFLIP_BIT
_GTESetTile
_GTESetTile
_GTESetTile
_GTESetTile
rts
_drawTreeHV
phx
phy
pea #98+TILE_VFLIP_BIT+TILE_HFLIP_BIT
inx
phx
phy
pea #97+TILE_VFLIP_BIT+TILE_HFLIP_BIT
iny
phx
phy
pea #65+TILE_VFLIP_BIT+TILE_HFLIP_BIT
dex
phx
phy
pea #66+TILE_VFLIP_BIT+TILE_HFLIP_BIT
_GTESetTile
_GTESetTile
_GTESetTile
_GTESetTile
rts
MyDirectPage ds 2
MyUserId ds 2
palette dw $0000,$08C1,$0C41,$0F93,$0777,$0FDA,$00A0,$0000,$0D20,$0FFF,$0FD7,$0F59,$0000,$01CE,$0EDA,$0EEE
PUT ../kfest-2022/StartUp.s
PUT ../shell/Overlay.s
; PUT App.Msg.s
; PUT font.s

105
demos/lite/App.Msg.s Normal file
View File

@ -0,0 +1,105 @@
HexToChar dfb '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
; Convert a byte (Acc) into a string and store at (Y)
ByteToString and #$00FF
sep #$20
pha
lsr
lsr
lsr
lsr
and #$0F
tax
ldal HexToChar,x
sta: $0000,y
pla
and #$0F
tax
ldal HexToChar,x
sta: $0001,y
rep #$20
rts
; Convert a word (Acc) into a hexadecimal string and store at (Y)
WordToString pha
bra Addr2ToString
; Pass in Acc = High, X = low
Addr3ToString phx
jsr ByteToString
iny
iny
lda 1,s
Addr2ToString xba
jsr ByteToString
iny
iny
pla
jsr ByteToString
rts
; A=Value
; X=Screen offset
DrawWord phx ; Save register value
phy
ldy #WordBuff+1
jsr WordToString
ply
plx
lda #WordBuff
jsr DrawString
rts
WordBuff str '0000'
Addr3Buff str '000000' ; str adds leading length byte

15
demos/lite/App.s Normal file
View File

@ -0,0 +1,15 @@
; IIgs GTE Lite rendering test
TYP $B3 ; S16 file
DSK GTELiteDemo
XPL
; Segment #1 -- Main execution block
ASM App.Main.s
SNA Main
; Segment #2 -- Tileset
ASM Zelda.TileSet.s
SNA TSET

13687
demos/lite/Zelda.TileSet.s Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
GTEToolDemo=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)
GTELiteDemo=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)

View File

@ -0,0 +1,19 @@
echo off
REM Copy all of the assets into the ProDOS image for emulator testing
REM
REM Pass the path of the Cadius tool as the first argument (%1)
set CADIUS="%1"
set IMAGE="..\\..\\emu\\Target.2mg"
set FOLDER="/GTEDEV/Lite"
REM Cadius does not overwrite files, so clear the root folder first
%CADIUS% DELETEFOLDER %IMAGE% %FOLDER%
%CADIUS% CREATEFOLDER %IMAGE% %FOLDER%
REM Now copy files and folders as needed
%CADIUS% ADDFILE %IMAGE% %FOLDER% .\GTELiteDemo
%CADIUS% ADDFILE %IMAGE% %FOLDER% ..\..\src\Tool160
REM Copy in the image assets

668
demos/lite/font.s Normal file
View File

@ -0,0 +1,668 @@
****************************************
* FONT ENGINE (v3?) *
* *
* Dagen Brock <dagenbrock@gmail.com> *
* 2013-07-20 *
****************************************
* A= ptr to string preceded by length *
* X= screen location *
****************************************
; each char:
; draw char at loc
; update loc
; see if length hit - no? back to draw char
rel
mx %00
]F_Length ds 2 ;length of string (only one byte currently used)
]F_CharIdx ds 2 ;index of current character
]F_CurrentPos ds 2 ;current top left char position
]F_StrPtr equ $01 ;pointer to string (including length byte) / DP
]F_StrClr equ $03
DrawString
pha ; local variable space
pha
tsc
phd
tcd
; sta ]F_StrPtr ; (done in pha init above) store at dp 0 ($00) for indirect loads
stx ]F_CurrentPos
sty ]F_StrClr
stz ]F_CharIdx
lda (]F_StrPtr)
and #$00ff ;strip off first char (len is only one byte)
sta ]F_Length ;get our length byte
NextChar lda ]F_CharIdx
cmp ]F_Length
bne :notDone
ldy ]F_StrClr ;restore the color pattern
pld
pla
pla
rts ;DONE! Return to caller
:notDone inc ]F_CharIdx
ldy ]F_CharIdx
lda (]F_StrPtr),y ;get next char!
and #$00FF ;mask high byte
sec
sbc #' ' ;our table starts with space ' '
asl ;*2
tay
ldx ]F_CurrentPos
jsr :drawChar
inc ]F_CurrentPos ;compare to addition time (?)
inc ]F_CurrentPos
inc ]F_CurrentPos
inc ]F_CurrentPos ;update screen pos (2 words=8 pixels)
bra NextChar
;x = TopLeft screen pos
;y = char table offset
:drawChar lda FontTable,y ;get real address of char data
sec
sbc #FontData ;pivot offset - now a is offset of fontdata
tay ;so we'll index with that
lda FontData,y
and ]F_StrClr
stal $E12000,x
lda FontData+2,y
and ]F_StrClr
stal $E12000+2,x
lda FontData+4,y
and ]F_StrClr
stal $E12000+160,x
lda FontData+6,y
and ]F_StrClr
stal $E12000+160+2,x
lda FontData+8,y
and ]F_StrClr
stal {$E12000+160*2},x
lda FontData+10,y
and ]F_StrClr
stal {$E12000+160*2+2},x
lda FontData+12,y
and ]F_StrClr
stal {$E12000+160*3},x
lda FontData+14,y
and ]F_StrClr
stal {$E12000+160*3+2},x
lda FontData+16,y
and ]F_StrClr
stal {$E12000+160*4},x
lda FontData+18,y
and ]F_StrClr
stal {$E12000+160*4+2},x
lda FontData+20,y
and ]F_StrClr
stal {$E12000+160*5},x
lda FontData+22,y
and ]F_StrClr
stal {$E12000+160*5+2},x
rts
FontTable dw s_Space
dw s_Exclaim
dw s_Quote
dw s_Number
dw s_Dollar
dw s_Percent
dw s_Amper
dw s_Single
dw s_OpenParen
dw s_CloseParen
dw s_Asterix
dw s_Plus
dw s_Comma
dw s_Minus
dw s_Period
dw s_Slash
dw s_N0
dw s_N1
dw s_N2
dw s_N3
dw s_N4
dw s_N5
dw s_N6
dw s_N7
dw s_N8
dw s_N9
dw s_Colon
dw s_Semi
dw s_LAngle
dw s_Equal
dw s_RAngle
dw s_Question
dw s_At
dw s_A
dw s_B
dw s_C
dw s_D
dw s_E
dw s_F
dw s_G
dw s_H
dw s_I
dw s_J
dw s_K
dw s_L
dw s_M
dw s_N
dw s_O
dw s_P
dw s_Q
dw s_R
dw s_S
dw s_T
dw s_U
dw s_V
dw s_W
dw s_X
dw s_Y
dw s_Z
dw s_LBracket
dw s_BackSlash
dw s_RBracket
dw s_Carot
dw s_UnderLine
FontData = *
s_Space hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
s_Exclaim hex 000FF000
hex 000FF000
hex 000FF000
hex 000FF000
hex 00000000
hex 000FF000
s_Quote hex 0FF00FF0
hex 00F000F0
hex 00000000
hex 00000000
hex 00000000
hex 00000000
s_Number hex 00000000
hex 00F00F00
hex 0FFFFFF0
hex 00F00F00
hex 0FFFFFF0
hex 00F00F00
s_Dollar hex 000F0F00
hex 00FFFFF0
hex 0F0F0F00
hex 00FFFF00
hex 000F0FF0
hex 0FFFFF00
s_Percent hex 0FF000F0
hex 00000F00
hex 0000F000
hex 000F0000
hex 00F00000
hex 0F000FF0
s_Amper hex 000FF000
hex 00F00F00
hex 0F00F000
hex 00F000F0
hex 0F0FFF00
hex 00F0F000
s_Single hex 000FF000
hex 0000F000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
s_OpenParen hex 000FF000
hex 00FF0000
hex 0FF00000
hex 0FF00000
hex 00FF0000
hex 000FF000
s_CloseParen hex 000FF000
hex 0000FF00
hex 00000FF0
hex 00000FF0
hex 0000FF00
hex 000FF000
s_Asterix hex 00000000
hex 00F0F0F0
hex 000FFF00
hex 00FFFFF0
hex 000FFF00
hex 00F0F0F0
s_Plus hex 000F0000
hex 000F0000
hex 0FFFFF00
hex 000F0000
hex 000F0000
hex 00000000
s_Comma hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 0000FF00
hex 0000F000
s_Minus hex 00000000
hex 00000000
hex 0FFFFF00
hex 00000000
hex 00000000
hex 00000000
s_Period hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 0000FF00
hex 0000FF00
s_Slash hex 000000F0
hex 00000F00
hex 0000F000
hex 000F0000
hex 00F00000
hex 0F000000
s_N0 hex 00FFFF00
hex 0F000FF0
hex 0F00F0F0
hex 0F0F00F0
hex 0FF000F0
hex 00FFFF00
s_N1 hex 000F0000
hex 00FF0000
hex 000F0000
hex 000F0000
hex 000F0000
hex 00FFF000
s_N2 hex 00FFFF00
hex 0F0000F0
hex 00000F00
hex 000FF000
hex 00F00000
hex 0FFFFFF0
s_N3 hex 00FFFF00
hex 000000F0
hex 000FFF00
hex 000000F0
hex 000000F0
hex 00FFFF00
s_N4 hex 0000FF00
hex 000F0F00
hex 00F00F00
hex 0FFFFFF0
hex 00000F00
hex 00000F00
s_N5 hex 0FFFFFF0
hex 0F000000
hex 0FFFFF00
hex 000000F0
hex 0F0000F0
hex 00FFFF00
s_N6 hex 000FFF00
hex 00F00000
hex 0F000000
hex 0FFFFF00
hex 0F0000F0
hex 00FFFFF0
s_N7 hex 0FFFFFF0
hex 000000F0
hex 00000F00
hex 0000F000
hex 000F0000
hex 000F0000
s_N8 hex 00FFFF00
hex 0F0000F0
hex 00FFFF00
hex 0F0000F0
hex 0F0000F0
hex 00FFFF00
s_N9 hex 00FFFF00
hex 0F0000F0
hex 00FFFF00
hex 0000F000
hex 000F0000
hex 00F00000
s_Colon hex 000FF000
hex 000FF000
hex 00000000
hex 000FF000
hex 000FF000
hex 00000000
s_Semi hex 00000000
hex 000FF000
hex 000FF000
hex 00000000
hex 000FF000
hex 000F0000
s_LAngle hex 0000F000
hex 000F0000
hex 00F00000
hex 000F0000
hex 0000F000
hex 00000000
s_Equal hex 00000000
hex 00000000
hex 0FFFFF00
hex 00000000
hex 0FFFFF00
hex 00000000
s_RAngle hex 0000F000
hex 00000F00
hex 000000F0
hex 00000F00
hex 0000F000
hex 00000000
s_Question hex 00FFF000
hex 0F000F00
hex 00000F00
hex 000FF000
hex 00000000
hex 000FF000
s_At hex 00FFFF00
hex 0F0000F0
hex 0F00F0F0
hex 0FFFF0F0
hex 000000F0
hex 0FFFFF00
s_A hex 000FF000
hex 00F00F00
hex 0F0000F0
hex 0FFFFFF0
hex 0F0000F0
hex 0F0000F0
s_B hex 0FFFFF00
hex 0F0000F0
hex 0FFFFF00
hex 0F0000F0
hex 0F0000F0
hex 0FFFFF00
s_C hex 00FFFFF0
hex 0F000000
hex 0F000000
hex 0F000000
hex 0F000000
hex 00FFFFF0
s_D hex 0FFFFF00
hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
hex 0FFFFF00
s_E hex 0FFFFFF0
hex 0F000000
hex 0FFFF000
hex 0F000000
hex 0F000000
hex 0FFFFFF0
s_F hex 0FFFFFF0
hex 0F000000
hex 0FFFF000
hex 0F000000
hex 0F000000
hex 0F000000
s_G hex 00FFFFF0
hex 0F000000
hex 0F000000
hex 0F00FFF0
hex 0F0000F0
hex 00FFFF00
s_H hex 0F0000F0
hex 0F0000F0
hex 0FFFFFF0
hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
s_I hex 0FFFFF00
hex 000F0000
hex 000F0000
hex 000F0000
hex 000F0000
hex 0FFFFF00
s_J hex 000000F0
hex 000000F0
hex 000000F0
hex 0F0000F0
hex 0F0000F0
hex 00FFFF00
s_K hex 0F000F00
hex 0F00F000
hex 0FFF0000
hex 0F00F000
hex 0F000F00
hex 0F000F00
s_L hex 0F000000
hex 0F000000
hex 0F000000
hex 0F000000
hex 0F000000
hex 0FFFFFF0
s_M hex 0F0000F0
hex 0FF00FF0
hex 0F0FF0F0
hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
s_N hex 0F0000F0
hex 0FF000F0
hex 0F0F00F0
hex 0F00F0F0
hex 0F000FF0
hex 0F0000F0
s_O hex 00FFFF00
hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
hex 00FFFF00
s_P hex 0FFFFF00
hex 0F0000F0
hex 0FFFFF00
hex 0F000000
hex 0F000000
hex 0F000000
s_Q hex 00FFFF00
hex 0F0000F0
hex 0F0000F0
hex 0F00F0F0
hex 0F000FF0
hex 00FFFFF0
s_R hex 0FFFFF00
hex 0F0000F0
hex 0FFFFF00
hex 0F000F00
hex 0F0000F0
hex 0F0000F0
s_S hex 00FFFFF0
hex 0F000000
hex 00FFFF00
hex 000000F0
hex 000000F0
hex 0FFFFF00
s_T hex 0FFFFF00
hex 000F0000
hex 000F0000
hex 000F0000
hex 000F0000
hex 000F0000
s_U hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
hex 00FFFF00
s_V hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
hex 00F00F00
hex 000FF000
s_W hex 0F0000F0
hex 0F0000F0
hex 0F0000F0
hex 0F0FF0F0
hex 0FF00FF0
hex 0F0000F0
s_X hex 0F0000F0
hex 00F00F00
hex 000FF000
hex 000FF000
hex 00F00F00
hex 0F0000F0
s_Y hex F00000F0
hex 0F000F00
hex 00F0F000
hex 000F0000
hex 000F0000
hex 000F0000
s_Z hex 0FFFFFF0
hex 00000F00
hex 0000F000
hex 000F0000
hex 00F00000
hex 0FFFFFF0
s_LBracket hex 000FFF00
hex 000F0000
hex 000F0000
hex 000F0000
hex 000F0000
hex 000FFF00
s_BackSlash hex 0F000000
hex 00F00000
hex 000F0000
hex 0000F000
hex 00000F00
hex 000000F0
s_RBracket hex 00FFF000
hex 0000F000
hex 0000F000
hex 0000F000
hex 0000F000
hex 00FFF000
s_Carot hex 0000F000
hex 000F0F00
hex 00F000F0
hex 00000000
hex 00000000
hex 00000000
s_UnderLine hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex FFFFFFF0
s_Template hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000

32
demos/lite/package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "gte-lite-demo",
"version": "1.0.0",
"description": "A testbed for testing hte GTE lite rendering mode",
"main": "index.js",
"config": {
"merlin32": "C:\\Programs\\IIgsXDev\\bin\\Merlin32-1.1.10.exe",
"cadius": "C:\\Programs\\IIgsXDev\\bin\\Cadius.exe",
"gsport": "C:\\Programs\\gsport\\gsport_0.31\\GSPort.exe",
"macros": "../../macros",
"crossrunner": "C:\\Programs\\Crossrunner\\Crossrunner.exe"
},
"scripts": {
"test": "npm run build && build-image.bat %npm_package_config_cadius% && %npm_package_config_gsport%",
"debug": "%npm_package_config_crossrunner% GTELiteDemo -Source MAINSEG_Output.txt -Debug -CompatibilityLayer -Map App.s",
"build": "npm run build:tool && npm run build:sys16",
"build:sys16": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.s",
"build:tool": "%npm_package_config_merlin32% -V %npm_package_config_macros% ../../src/Master.s"
},
"repository": {
"type": "git",
"url": "git+https://github.com/lscharen/iigs-game-engine.git"
},
"author": "Lucas Scharenbroich",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/lscharen/iigs-game-engine/issues"
},
"homepage": "https://github.com/lscharen/iigs-game-engine#readme",
"devDependencies": {
}
}

View File

@ -87,7 +87,7 @@ FTblTmp equ 228
; Initialize the sound hardware for APU emulation
; jsr APUStartUp
jsr APUStartUp
; Start up GTE to drive the graphics
; brl :debug

View File

@ -348,6 +348,8 @@ setup_interrupt = *
rts
interrupt_oscillator = 31
;reference_freq = 299 ; interrupt frequence (60Hz)
;reference_freq = 598 ; interrupt frequence (120Hz)
reference_freq = 1195 ; interrupt frequence (240Hz)
timer_sound_settings = * ; set up oscillator 30 for interrupts
dfb $00+interrupt_oscillator,reference_freq ; frequency low register
@ -523,6 +525,27 @@ tick_envelope
sta ]1+{APU_PULSE1_ENVELOPE-APU_PULSE1}
envelope_out <<<
half_frame_clock
; clock the length counters
clock_length_counter APU_PULSE1;#PULSE_HALT_FLAG
clock_length_counter APU_PULSE2;#PULSE_HALT_FLAG
clock_length_counter APU_TRIANGLE;#TRIANGLE_HALT_FLAG
clock_length_counter APU_NOISE;#NOISE_HALT_FLAG
; clock the sweep units
clock_sweep APU_PULSE1;0
clock_sweep APU_PULSE2;1
rts
quarter_frame_clock
; clock the envelopes and triangle linear counter
clock_linear_counter APU_TRIANGLE
clock_envelope APU_PULSE1
clock_envelope APU_PULSE2
clock_envelope APU_NOISE
rts
;-----------------------------------------------------------------------------------------
; interupt handler
;-----------------------------------------------------------------------------------------
@ -577,29 +600,29 @@ interrupt_handler = *
stx apu_frame_counter
jmp (:frame_counter_proc,x)
:frame_counter_proc da :quarter_frame,:half_frame,:quarter_frame,:no_frame,:half_frame
:half_frame
; clock the length counters
clock_length_counter APU_PULSE1;#PULSE_HALT_FLAG
clock_length_counter APU_PULSE2;#PULSE_HALT_FLAG
clock_length_counter APU_TRIANGLE;#TRIANGLE_HALT_FLAG
clock_length_counter APU_NOISE;#NOISE_HALT_FLAG
; Full speed emulation (240Hz)
:half_frame jsr half_frame_clock
:quarter_frame jsr quarter_frame_clock
; clock the sweep units
clock_sweep APU_PULSE1;0
clock_sweep APU_PULSE2;1
; Half-speed interrupts (120Hz) -- clock twice in each handler
;:half_frame
;:quarter_frame
; jsr half_frame_clock
; jsr quarter_frame_clock
; jsr quarter_frame_clock
; quarter frame updates run every APU frame
:quarter_frame
; Quarter-speed interrupts (60Hz) -- clock four times in each handler
;:half_frame
;:quarter_frame
; jsr half_frame_clock
; jsr quarter_frame_clock
; jsr quarter_frame_clock
; jsr half_frame_clock
; jsr quarter_frame_clock
; jsr quarter_frame_clock
; clock the envelopes and triangle linear counter
clock_linear_counter APU_TRIANGLE
clock_envelope APU_PULSE1
clock_envelope APU_PULSE2
clock_envelope APU_NOISE
:no_frame
; Apply any changes to the DOC registers
jsr access_doc_registers
; Set the parameters for the first square wave channel.
@ -779,7 +802,7 @@ interrupt_handler = *
:high_pitch pla
sta sound_data
:no_frame
:not_timer
ldal show_border
beq :no_show2

View File

@ -20,6 +20,7 @@
"build:tool": "%npm_package_config_merlin32% -V %npm_package_config_macros% ../../src/Master.s",
"build:image": "build-image.bat %npm_package_config_cadius%",
"build:swizzle": "./mkswizzetbl.sh > pal_w11.s",
"build:image:sim": "npm run build:sim && npm run build:image",
"run:sim": "npm run build:sim && npm run build:image && npm run gsport",
"debug": "\"%npm_package_config_crossrunner%\" SuperMarioGS -Source SuperMarioGS_S02__Output.txt -Source SuperMarioGS_S03__Output.txt -Debug -CompatibilityLayer"

View File

@ -572,30 +572,18 @@ no_pal
; Trigger a copy from a page of memory to OAM. Since this is a DMA operation, we can cheat and do a 16-bit copy
PPUDMA_WRITE ENT
php
phb
phk
plb
phx
pha
rep #$30
xba
and #$FF00
tax
]n equ 0
lup 128
ldal ROMBase+]n,x
sta PPU_OAM+]n
lda ROMBase+$200+]n
stal PPU_OAM+]n
]n = ]n+2
--^
sep #$30
pla
plx
plb
plp
rtl
@ -626,7 +614,7 @@ scanOAMSprites
:loop
lda PPU_OAM,x ; Y-coordinate
cmp #max_nes_y+1 ; Skip anything that is beyond this lint
cmp #max_nes_y+1 ; Skip anything that is beyond this line
bcs :skip
cmp #y_offset
bcc :skip
@ -816,7 +804,7 @@ buildShadowBitmap
; ldy PPU_OAM,x
ldy OAM_COPY,x
cpy #max_nes_y ; Don't increment something right on the edge (allows )
; cpy #max_nes_y ; Don't increment something right on the edge (allows )
; iny ; This is the y-coordinate of the top of the sprite
ldx y2idx,y ; Get the index into the shadowBitmap array for this y coordinate (y -> blk_y)
@ -981,7 +969,6 @@ exposeShadowList
stx :last
cpx shadowListCount
beq :exit
:loop
phx
@ -1037,7 +1024,7 @@ shadowBitmapToList
sep #$30
ldx #y_offset_rows ; Start at he third row (y_offset = 16) walk the bitmap for 25 bytes (200 lines of height)
ldx #y_offset_rows ; Start at the third row (y_offset = 16) walk the bitmap for 25 bytes (200 lines of height)
lda #0
sta shadowListCount ; zero out the shadow list count
@ -1054,7 +1041,7 @@ shadowBitmapToList
:zero_next
inx
cpx #y_height_rows+y_offset_rows+1 ; End at byte 27
cpx #y_height_rows+y_offset_rows ; +1 ; End at byte 27
bcc :zero_loop
bra :exit ; ended while not tracking a sprite, so exit the function
@ -1171,9 +1158,6 @@ drawOAMSprites
pha
jsr scanOAMSprites ; Filter out any sprites that don't need to be drawn
pla
cmpl nmiCount
beq *+4
brk $1F ; Should not have serviced the VBL interrupt here....
cli
jsr buildShadowBitmap ; Run though and quickly create a bitmap of lines with sprites
@ -1356,6 +1340,28 @@ drawTileToScreen
bit #$0100
jne :drawTileToScreenV
; If we compile the sprites, then each word can be implemented as:
;
; x = screen address
;
; ldy #LOOKUP_VAL ; 3 constant 6-bit tile lookup value from NES CHR rom
; lda: 0,x ; 6
; and #MASK ; 3
; ora [USER_FREE_SPACE],y ; 7 lookup and merge in swizzled tile data = *(SwizzlePtr + palbits)
; sta: 0,x ; 6 = 25 cycles / word
;
; Current implementation below is 4+6+6+4+6+7+6 = 39 cycles
;
; Most tiles don't have 4 consecutive transparent pixels, but there will be some minor savings
; by avoiding those operations. For MASK = $FFFF, the simplified code is and solid words are
; quite common, at least 25 - 30% of the words are solid. So conservative estimate of
; 25 * 0.75 + 16 * 0.25 = ~22 cycles/word on average. Throw in the 100% savings from MASK=0
; words and it's close to twice the speed of the current routine.
;
; ldy #LOOKUP_VAL ; 3 constant 6-bit tile lookup value from NES CHR rom
; lda [USER_FREE_SPACE],y ; 7 lookup and merge in swizzled tile data = *(SwizzlePtr + palbits)
; sta: 0,x ; 6 = 16 cycles / word
]line equ 0
lup 8
ldx USER_TILE_ADDR
@ -1558,6 +1564,17 @@ drawTileToScreen
; Assume that when the tile is updated, it includes a full 10-bit value with the
; palette bits included with the lookup bits
;
; If we could compile all of the tiles, then the code becomes
;
; ldy #DATA
; lda [USER_FREE_SPACE],y
; sta: code,x
;
; And we save _at_least_ 11 cycles / word. 6 + 7 + 4 + 4 + 6 = 27 vs 16.
;
; Also, by exposing/short-circuiting the draw_tile stuff to avoid the GTE tile queue, we significantly
; reduce overhead and probably solve the tile column bug.
NESTileBlitter
lda USER_TILE_ID
and #$0600 ; Select the tile palette from the tile id

View File

@ -190,6 +190,7 @@ extSpriteRenderer equ $0005
rawDrawTile equ $0006
extBG0TileUpdate equ $0007
userTileCallback equ $0008
liteBlitter equ $0009
; CopyPicToBG1 flags
COPY_PIC_NORMAL equ $0000 ; Copy into BG1 buffer in "normal mode"

View File

@ -18,7 +18,8 @@
"test": "npm run build && build-image.bat %npm_package_config_cadius% && %npm_package_config_gsport%",
"build": "%npm_package_config_merlin32% -V %npm_package_config_macros% ./src/Master.s",
"build:debug": "%npm_package_config_merlin32% -V %npm_package_config_macros% ./src/Debug.s",
"debug": "%npm_package_config_crossrunner% ./src/Debug160 -Source ./src/Debug160_S02__Output.txt -Debug -CompatibilityLayer"
"debug": "%npm_package_config_crossrunner% ./src/Debug160 -Source ./src/Debug160_S02__Output.txt -Debug -CompatibilityLayer",
"install:header": "copy .\\ORCACDefs\\gte.h C:\\ProgramData\\GoldenGate\\Libraries\\ORCACDefs\\gte.h"
},
"repository": {
"type": "git",

View File

@ -238,11 +238,25 @@ EngineReset
stz OneSecondCounter
; Fill in the state register values
sep #$20
ldal STATE_REG
and #$CF ; R0W0
sta STATE_REG_OFF ; Put this value in to return to "normal" blitter
ora #$10 ; R0W1
sta STATE_REG_BLIT ; Running the blitter, this is the mode to put us into
rep #$20
lda EngineMode
bit #ENGINE_MODE_TWO_LAYER+ENGINE_MODE_DYN_TILES
beq :lite
lda #13
sta tmp15
stz tmp14
; Rebuild all of the bank blitters
; Rebuild all of the bank blitters if not in fast mode
:loop
ldx #BlitBuff
lda #^BlitBuff
@ -256,16 +270,31 @@ EngineReset
dec tmp15
bne :loop
bra :done
; Set the scanline tables to reasonable default values
; ldx #{416*2}-2
; lda #0
;:sxm_loop
; sta StartXMod164Arr,x
; dex
; dex
; bpl :sxm_loop
; Insert jumps to the interrupt enable code every 16 lines
:lite
lda #12
sta tmp15
ldx #_EXIT_EVEN+{_LINE_SIZE*15}+1 ; Patch the JMP operand here
ldy #_LINE_BASE+_ENTRY_INT+{_LINE_SIZE*16} ; Jump to this address on the next line
:lloop
tya
stal lite_base,x
clc
adc #{_LINE_SIZE*16}
tay
txa
clc
adc #{_LINE_SIZE*16}
tax
dec tmp15
bne :lloop
:done
rts

View File

@ -105,6 +105,9 @@ BG1Scaling equ 126
activeSpriteList equ 128 ; 32 bytes for the active sprite list (can persist across frames)
; Free space from 160 to 192
STATE_REG_OFF equ 160
STATE_REG_BLIT equ 161
STK_SAVE equ 162 ; Only used by the lite renderer
blttmp equ 192 ; 32 bytes of local cache/scratch space for blitter
@ -206,6 +209,7 @@ extSpriteRenderer equ $0005
rawDrawTile equ $0006
extBG0TileUpdate equ $0007
userTileCallback equ $0008
liteBlitter equ $0009
; CopyPicToBG1 flags
COPY_PIC_NORMAL equ $0000 ; Copy into BG1 buffer in "normal mode" treating the buffer as a 164x208 pixmap with stride of 256
@ -329,3 +333,16 @@ ExtUpdateBG0Tiles EXT
; Tool error codes
NO_TIMERS_AVAILABLE equ 10
; Offsets for the Lite blitter
_ENTRY_1 equ 0
_ENTRY_JMP equ 4
_ENTRY_ODD equ 11
_ENTRY_INT equ 14
_LOOP equ 38
_EXIT_ODD equ 290
_EXIT_EVEN equ 293
_LOW_SAVE equ 296
_LINE_BASE equ 4 ; header size
_LINE_SIZE equ 298 ; number of bytes for each blitter line

View File

@ -2,7 +2,7 @@
TYP $BA ; Tool set file
DSK Tool160
XPL
XPL
; Main toolbox interface and code
@ -44,6 +44,12 @@
ALI BANK
SNA ROTDATA
; GTE "Lite" blitter. One bank of static code, no relocation needed.
ASM blitter/TemplateLite.s
ALI BANK
SNA BLTLITE
; Additional code
; ASM FastCopies.s

View File

@ -74,6 +74,15 @@ InitMemory lda EngineMode
sta CompileBank
stz CompileBank0
; If ENGINE_MODE_TWO_LAYER and ENGINE_MODE_DYN_TILES are turned off, then
; we are in "GTE lite" mode and use a blitter template that only requires
; one bank of memory
lda EngineMode
bit #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER
bne InitFullBlitter
brl InitLiteBlitter
InitFullBlitter
; Allocate the 13 banks of memory we need and store in double-length array
]step equ 0
lup 13
@ -155,6 +164,70 @@ InitMemory lda EngineMode
:exit
rts
InitLiteBlitter
lite_base EXT
; Overwrite the Col2CodeOffset tables. This array needs to hold the offset from the start of the line (BTableLow),
; to each of the PEA words *in logical order*. This means the actual addresses are reversed.
lda #_LOOP
sta Col2CodeOffset-2
ldx #0
lda #_LOOP+{81*PER_TILE_SIZE}
sta Col2CodeOffset+{4*82} ; Store a copy one off the end
:loop0 sta Col2CodeOffset,x
sta Col2CodeOffset+{2*82},x
sec
sbc #PER_TILE_SIZE
inx
inx
cpx #82*2
bcc :loop0
; Fill in the BTable and BRowTable values
ldx #0
ldy #lite_base
:loop1
tya
sta BTableLow,x
sta BTableLow+{208*2},x
clc
adc #_LINE_SIZE
tay
lda #^lite_base
sta BTableHigh,x
sta BTableHigh+{208*2},x
inx
inx
cpx #208*2
bcc :loop1
ldx #0
ldy #lite_base
:loop2
lda BTableHigh ; This is the same value for the lite blitter
sta BRowTableHigh,x
sta BRowTableHigh+{26*2},x
tya
sta BRowTableLow,x
sta BRowTableLow+{26*2},x
clc
adc #{8*_LINE_SIZE}
tay
inx
inx
cpx #26*2
bcc :loop2
rts
; Bank allocator (for one full, fixed bank of memory. Can be immediately deferenced)
AllocOneBank PushLong #0

View File

@ -160,7 +160,9 @@ ExtFuncBlock
; Special NES renderer that externalizes the sprite rendering in order to exceed the internal limit of 16 sprites
_RenderNES
jsr _ApplyBG0YPos
; jsr _ApplyBG0YPos
; jsr _ApplyBG0XPosPre
jsr _ApplyBG0YPosLite
jsr _ApplyBG0XPosPre
; Callback to update the tilestore with any new tiles
@ -182,7 +184,8 @@ _RenderNES
lda #16*2
sta tmp2 ; lines_left_x2
lda #0 ; Xmod164
jsr _ApplyBG0XPosAlt
; jsr _ApplyBG0XPosAlt
jsr _ApplyBG0XPosAltLite
lda tmp4 ; :exit_offset
stal nesTopOffset
@ -194,7 +197,8 @@ _RenderNES
asl
sta tmp2 ; lines_left_x2
lda StartXMod164 ; Xmod164
jsr _ApplyBG0XPosAlt
; jsr _ApplyBG0XPosAlt
jsr _ApplyBG0XPosAltLite
lda tmp4
stal nesBottomOffset
@ -246,7 +250,8 @@ _RenderNES
sta tmp2 ; :lines_left_x2
ldal nesTopOffset
sta tmp4 ; :exit_offset
jsr _RestoreBG0OpcodesAlt
; jsr _RestoreBG0OpcodesAlt
jsr _RestoreBG0OpcodesAltLite
lda #16*2
sta tmp1 ; :virt_line_x2
@ -257,7 +262,8 @@ _RenderNES
sta tmp2 ; lines_left_x2
ldal nesBottomOffset
sta tmp4 ; :exit_offset
jsr _RestoreBG0OpcodesAlt
; jsr _RestoreBG0OpcodesAlt
jsr _RestoreBG0OpcodesAltLite
; lda StartYMod208 ; Restore the fields back to their original state
; ldx ScreenHeight
@ -277,7 +283,7 @@ _RenderNES
stz LastRender ; Mark that a full render was just performed
rts
nesTopOffset ds 2
nesTopOffset ds 2
nesBottomOffset ds 2
; Use the per-scanline tables to set the screen. This is really meant to be used without the built-in tilemap
@ -626,6 +632,60 @@ _RenderWithShadowing
:no_removal
rts
; Specail mode for rendering in GTE-lite mode. No secondary background is possible
_RenderLite
sta RenderFlags
jsr _DoTimers ; Run any pending timer tasks
jsr _ApplyBG0YPosLite ; Set stack addresses for the virtual lines to the physical screen
jsr _ApplyBG0XPosPre ; Lock in certain rendering variables (not lite/non-lite specific)
jsr _UpdateBG0TileMap ; and the tile maps. These subroutines build up a list of tiles
jsr _ApplyTiles ; This function actually draws the new tiles into the code field
jsr _ApplyBG0XPosLite ; Patch the code field instructions with exit BRA opcode
; At this point, everything in the background has been rendered into the code field. Next, we need
; to create priority lists of scanline ranges.
; jsr _FilterObjectList ; Walk the sorted list and create an array of objects that need to be rendered
;
; jsr _ShadowOff ; Turn off shadowing and draw all the scanlines with sprites on them
; jsr _DrawObjShadow ; Draw the background
; jsr _DrawDirectSprites ; Draw the sprites directly to the Bank $01 graphics buffer (skipping the render-to-tile step)
;
jsr _ShadowOn ; Turn shadowing back on
;
; jsr _DrawFinalPass
ldx #0
ldy ScreenHeight
jsr _BltRange
lda StartYMod208 ; Restore the fields back to their original state
ldx ScreenHeight
jsr _RestoreBG0OpcodesLite
lda StartY
sta OldStartY
lda StartX
sta OldStartX
lda BG1StartY
sta OldBG1StartY
lda BG1StartX
sta OldBG1StartX
stz DirtyBits
stz LastRender ; Mark that a full render was just performed
lda SpriteRemovedFlag ; If any sprite was removed, set the rebuild flag
beq :no_removal
lda #DIRTY_BIT_SPRITE_ARRAY
sta DirtyBits
:no_removal
rts
; Run through the list of sprites that are not OFFSCREEN and not OVERLAYS and draw them directly to the graphics screen. We can use
; compiled sprites here, with limitations.
_DrawDirectSprites

View File

@ -125,7 +125,8 @@ InitTiles
bra :out
:fast
lda #0 ; Initialize with Tile 0
ldy #FastProcs
; ldy #FastProcs
ldy #LiteProcs
jsr _SetTileProcs
bra :out
@ -275,7 +276,8 @@ _SetNormalTileProcs
; 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
; ldy #FastProcs
ldy #LiteProcs
pla
jmp _SetTileProcs
@ -440,7 +442,8 @@ _UTDPatch jsl UserHook1 ; Call the users code
cmp #0
beq :done
jmp FastCopyTmpDataA ; Non-zero value to continue additional work
; jmp FastCopyTmpDataA ; Non-zero value to continue additional work
jmp LiteCopyTmpDataA
:done
rts
@ -465,6 +468,22 @@ FastCopyTmpDataA
plb
rts
LiteCopyTmpDataA
pei USER_TILE_CODE_PTR+2
ldy USER_TILE_CODE_PTR
plb
]line equ 0
lup 8
lda tmp_tile_data+{]line*4}
sta: $0004+{]line*_LINE_SIZE},y
lda tmp_tile_data+{]line*4}+2
sta: $0001+{]line*_LINE_SIZE},y
]line equ ]line+1
--^
plb
rts
; X = Tile Store offset
; Y = Engine Mode Base Table address
; A = Table proc index
@ -544,6 +563,18 @@ FastUnderZV dw ConstTile0Fast,SpriteUnder0Fast,SpriteUnder0Fast
FastUnderNA dw CopyTileAFast,SpriteUnderAFast,OneSpriteFastUnderA
FastUnderNV dw CopyTileVFast,SpriteUnderVFast,OneSpriteFastUnderV
; "Lite" procs. For when the engine is in GTE Lite mode. Different
; stride than the "Fast" procs.
LiteProcs
LiteOverZA dw ConstTile0Lite,SpriteOver0Lite,OneSpriteLiteOver0
LiteOverZV dw ConstTile0Lite,SpriteOver0Lite,OneSpriteLiteOver0
LiteOverNA dw CopyTileALite,SpriteOverALite,OneSpriteLiteOverA
LiteOverNV dw CopyTileVLite,SpriteOverVLite,OneSpriteLiteOverV
LiteUnderZA dw ConstTile0Lite,SpriteUnder0Lite,SpriteUnder0Lite
LiteUnderZV dw ConstTile0Lite,SpriteUnder0Lite,SpriteUnder0Lite
LiteUnderNA dw CopyTileALite,SpriteUnderALite,OneSpriteLiteUnderA
LiteUnderNV dw CopyTileVLite,SpriteUnderVLite,OneSpriteLiteUnderV
; "Slow" procs. These are duplicates of the "Fast" functions, but also
; set the PEA opcode in all cases.
SlowProcs

View File

@ -119,11 +119,16 @@ _GTERemoveSprite MAC
UserTool $1300+GTEToolNum
<<<
; Helper function to set the data back to the toolset default
_SetDataBank sep #$20
lda #^TileStore
pha
;_SetDataBank sep #$20
; lda #^TileStore
; pha
; plb
; rep #$20
; rts
_SetDataBank pea #>TileStore
plb
plb
rep #$20
rts
; Do nothing when the tool set is installed
@ -305,6 +310,8 @@ _TSRender
lda :flags,s
cmp #$FFFF ; Hack! Special mode...
beq :nes
cmp #$FFFE ; Experimental gte-lite mode
beq :lite
bit #RENDER_WITH_SHADOWING
beq :no_shadowing
@ -323,6 +330,10 @@ _TSRender
:nes
jsr _RenderNES
bra :done
:lite
jsr _RenderLite
:done
_TSExit #0;#2
@ -945,7 +956,17 @@ _TSGetAddress
lda #^_DrawTileToScreen
sta :output+2,s
bra :out
:next_3
:next_3 cmp #liteBlitter
bne :next_4
lda #lite_base
sta :output,s
lda #^lite_base
sta :output+2,s
bra :out
:next_4
:out
_TSExit #0;#2
@ -1104,6 +1125,7 @@ _TSEnableBackground
put SpriteRender.s
put Render.s
put render/Render.s
put render/Lite.s
put render/Fast.s
put render/Slow.s
put render/Dynamic.s
@ -1114,11 +1136,14 @@ _TSEnableBackground
put tiles/DirtyTileQueue.s
put blitter/SCB.s
put blitter/Horz.s
put blitter/HorzLite.s
put blitter/Vert.s
put blitter/VertLite.s
put blitter/BG0.s
put blitter/BG1.s
put blitter/Rotation.s
put blitter/Template.s
put blitter/TemplateUtils.s
put blitter/Blitter.s
put blitter/BlitterLite.s
put blitter/PEISlammer.s

View File

@ -26,8 +26,15 @@ _BltRange
brl DisabledBG
:normal
; If we are in "lite" mode, defer to that implementation
lda EngineMode
bit #ENGINE_MODE_TWO_LAYER+ENGINE_MODE_DYN_TILES
bne :dark
jmp _BltRangeLite
:dark
phb ; preserve the bank register
clc`
clc
dey
tya ; Get the address of the line that we want to return from
@ -98,6 +105,9 @@ stk_save lda #0000 ; load the stack
plp ; re-enable interrupts (maybe, if interrupts disabled when we are called, they are not re-endabled)
pld ; restore the direct page
:exit_ptr equ tmp0
:jmp_low_save equ tmp2
sep #$20
ldy #CODE_EXIT+1
lda :jmp_low_save
@ -164,15 +174,13 @@ DisabledBG
lda tmp15 ; restore the stack
tcs
sep #$30 ; 8-bit mode
ldal STATE_REG
tax ; Save the value
and #$CF ; Read Bank 0 / Write Bank 0
lda STATE_REG_OFF ; Could be on the direct page....
stal STATE_REG
cli
nop ; Give a couple of cycles
sei
txa ; Restore the state
lda STATE_REG_BLIT ; External values
stal STATE_REG
rep #$30
bra :next

83
src/blitter/BlitterLite.s Normal file
View File

@ -0,0 +1,83 @@
; This is the method that is most useful from the high-level code. We want the
; freedom to blit a range of lines. This subroutine can assume that all of the
; data in the code fields is set up properly.
;
; X = first line (inclusive), valid range of 0 to 199
; Y = last line (exclusive), valid range >X up to 200
;
; The lines are based on the appearance of lines in the play field, so blitting lines 0 through
; 19 will draw the first 20 lines on the play field, regardless of where the playfield is physically
; on the SHR screen or the current value of StartY
lite_base EXT
lite_full_return EXT
; This should only be called from _Render when it is determined to be safe
_BltRangeLite
:exit_ptr equ tmp0
:jmp_low_save equ tmp2
phb
clc
dey
tya ; Get the address of the line that we want to return from
adc StartYMod208 ; and create a pointer to it
asl
tay
lda BTableLow,y
sta :exit_ptr
lda BTableHigh,y
sta :exit_ptr+2
txa ; get the first line (0 - 199)
adc StartYMod208 ; add in the virtual offset (0, 207) -- max value of 406
asl
tax ; this is the offset into the blitter table
lda BTableLow,x ; patch in the address
stal blt_entry_lite+1
; 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
; executed.
;
; The trick we use is to patch the low byte to force the code to jump to a special return
; function (jml blt_return) in the *next* code field line.
ldy #_EXIT_EVEN+1 ; this is a JMP instruction that points to the next line.
lda [:exit_ptr],y ; we have to save because not every line points to the same
sta :jmp_low_save ; position in the next code line
lda #lite_full_return ; this is the address of the return code
sta [:exit_ptr],y ; patch out the address of the JMP
php ; save the current processor flags
sep #$20 ; run the lite blitter in 8-bit accumulator mode
lda :exit_ptr+2 ; set the bank to the code field
pha
plb
sei ; disable interrupts
_R0W1
tsx ; save the stack pointer in Y
txy
blt_entry_lite jml lite_base ; Jump into the blitter code $ZZ/YYXX (Does not modify Y or X)
blt_return_lite ENT
_R0W0
tyx
txs ; restore the stack
plp ; re-enable interrupts (maybe, if interrupts disabled when we are called, they are not re-endabled)
:exit_ptr equ tmp0
:jmp_low_save equ tmp2
mx %00
ldy #_EXIT_EVEN+1
lda :jmp_low_save
sta [:exit_ptr],y
plb
rts

656
src/blitter/HorzLite.s Normal file
View File

@ -0,0 +1,656 @@
; Subroutines that deal with the horizontal scrolling in the "lite" blitter. The
; advantage of the lite blitter is that the entire code field is in one bank, so
; there is no need to chunk up the updates into 16-line pieces. The entire height
; of the playfield can be done with a single unrolled loop.
;
; A = starting virtual line in the code field (0 - 207)
; X = number of lines to render (0 - 200)
_RestoreBG0OpcodesLite
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
:exit_offset equ tmp4
asl
sta :virt_line_x2 ; Keep track of it
txa
asl
sta :lines_left_x2
lda LastPatchOffset ; If zero, there are no saved opcodes
sta :exit_offset
_RestoreBG0OpcodesAltLite
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
:draw_count_x2 equ tmp3
:exit_offset equ tmp4
:base_address equ tmp5
:exit_address equ tmp6
:draw_count_x6 equ blttmp+10
; We do need to split the update into two parts so that we can handle the wrap-around portion
ldx :lines_left_x2
lda #208*2
sec
sbc :virt_line_x2 ; calculate number of lines to the end of the buffer
cmp :lines_left_x2
bcs :one_pass ; if there's room, do it in one shot
; If the virtual screen wraps around the bank, then we need to split the update up
; into two pieces to efficiently restore the values without having to do the
; virtual_line -> physical_line conversion each time.
tax
jsr :one_pass ; Go through with this draw count
stz :virt_line_x2
lda :lines_left_x2
sec
sbc :draw_count_x2 ; this many left to draw. Fall through to finish up
tax
:one_pass
txa
sta :draw_count_x2 ; this is the number of lines we will do right now
asl
adc :draw_count_x2
sta :draw_count_x6
phb
sep #$20 ; Set the data bank to the code field
lda BTableHigh
pha
plb
rep #$21 ; clear the carry while we're here...
ldx :virt_line_x2
ldal BTableLow,x ; Get the address of the first code field line
sta :base_address
adc #_LOW_SAVE
sta :low_save_addr
lda :base_address
adc :exit_offset ; Add some offsets to get the base address in the code field line
sta :exit_address
sec
CopyXToYPrep :do_restore;:draw_count_x6
ldx :low_save_addr
ldy :exit_address
:do_restore jsr $0000 ; Jump in to do SCREEN_HEIGHT lines
stz LastPatchOffset ; Clear the value once completed
plb
rts
; Based on the current value of StartX in the direct page, patch up the code fields
; to render the correct data. Note that we do *not* do the OpcodeRestore in this
; routine. The reason is that the restore *must* be applied using the (StartX, StartY)
; values from the previous frame, which requires logic that is not relevant to setting
; up the code field.
;
; This function is where the reverse-mapping aspect of the code field is compensated
; for. In the initialize case where X = 0, the exit point is at the *end* of
; the code buffer line
;
; +----+----+ ... +----+----+----+
; | 82 | 80 | | 04 | 02 | 00 |
; +----+----+ ... +----+----+----+
; ^ x=0
;
; As the screen scrolls right-to-left, the exit position moves to earlier memory
; locations until wrapping around from 163 to 0.
;
; The net calculation are
;
; x_exit = (164 - x) % 164
; x_enter = (164 - x - width) % 164
;
_ApplyBG0XPosLite
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
; If there are saved opcodes that have not been restored, do not run this routine
lda LastPatchOffset
beq *+3
rts
; This code is fairly succinct. See the corresponding code in Vert.s for more detailed comments.
lda StartYMod208 ; This is the base line of the virtual screen
asl
sta :virt_line_x2 ; Keep track of it
lda ScreenHeight
asl
sta :lines_left_x2
; Calculate the exit and entry offsets into the code fields. This is a bit tricky, because odd-aligned
; rendering causes the left and right edges to move in a staggered fashion.
;
; ... +----+----+----+----+----+- ... -+----+----+----+----+----+
; | 04 | 06 | 08 | 0A | 0C | | 44 | 46 | 48 | 4A |
; ... +----+----+----+----+----+- ... -+----+----+----+----+----+
; | |
; +---- screen width --------------+
; entry | | exit
;
; Here is an example of a screen 64 bytes wide. When everything is aligned to an even offset
; then the entry point is column $08 and the exit point is column $48
;
; If we move the screen forward one byte (which means the pointers move backwards) then the low-byte
; of column $06 will be on the right edge of the screen and the high-byte of column $46 will left-edge
; of the screen. Since the one-byte edges are handled specially, the exit point shifts one column, but
; the entry point does not.
;
; ... +----+----+----+----+----+- ... -+----+----+----+----+----+
; | 04 | 06 | 08 | 0A | 0C | | 44 | 46 | 48 | 4A |
; ... +----+----+----+----+----+- ... -+----+----+----+----+----+
; | | | |
; +--|------ screen width -------|--+
; entry | | exit
;
; When the screen is moved one more byte forward, then the entry point will move to the
; next column.
;
; ... +----+----+----+----+----+- ... -+----+----+----+----+----+
; | 04 | 06 | 08 | 0A | 0C | | 44 | 46 | 48 | 4A |
; ... +----+----+----+----+----+- ... -+----+----+----+----+----+
; | |
; +------ screen width ------------+
; entry | | exit
;
; So, in short, the entry tile position is rounded up from the x-position and the exit
; tile position is rounded down.
;
; Now, the left edge of the screen is pushed last, so we need to exit one instruction *after*
; the location (163 - StartX % 164)
;
; x = 0
;
; | PEA $0000 |
; +-----------+
; | PEA $0000 |
; +-----------+
; | JMP loop | <-- Exit here
; +-----------+
;
; x = 1 and 2
;
; | PEA $0000 |
; +-----------+
; | PEA $0000 | <-- Exit Here
; +-----------+
; | JMP loop |
; +-----------+
lda StartXMod164
; Alternate entry point if the virt_line_x2 and lines_left_x2 and XMod164 values are passed in externally
_ApplyBG0XPosAltLite
;:stk_save equ tmp0
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
:draw_count_x2 equ tmp3
:exit_offset equ tmp4
:entry_offset equ tmp5
:exit_bra equ tmp6
:exit_address equ tmp7
:base_address equ tmp8
:opcode equ tmp9
:odd_entry_offset equ tmp10
:draw_count_x3 equ blttmp ; steal even mode direct page temp space...
:draw_count_x6 equ blttmp+2
:entry_jmp_addr equ blttmp+4
:low_save_addr equ blttmp+6
:draw_count_x3 equ blttmp+8
:draw_count_x6 equ blttmp+10
:entry_odd_addr equ blttmp+12
:exit_odd_addr equ blttmp+14
bit #$0001
jne :odd_case ; Specialized routines for even/odd cases
; If the exit byte is even, then the left edge is odd-aligned and we exit at this word.
tax
lda CodeFieldEvenBRA-2,x
sta :exit_bra
lda Col2CodeOffset-2,x ; offset from :base that is the exit location
sta :exit_offset
sta LastPatchOffset ; Cache as a flag for later
; Calculate the entry point into the code field by calculating the right edge
txa ; lda StartXMod164
clc
adc ScreenWidth ; move to the right edge
cmp #164 ; Keep the value in range
bcc *+5
sbc #164
; Lookup the relative offset that we will be entering the code field. Need to adjust the Col2CodeOffset
; to account for the position of the BRL instruction
tax
lda Col2CodeOffset-2,x ; offset from base
clc
adc #-{_ENTRY_JMP+3}
sta :opcode
; Now update the code field to get ready to execute. We set the bank register to the code
; field to make updates faster. The primary actions to do are.
;
; 1. Saves the low operand byte in the code field (opcode is always $F4)
; 2. Writes the BRA instruction to exit the code field
; 3. Writes the JMP entry point to enter the code field
;
; We do need to split the update into two parts so that we can handle the wrap-around portion
ldx :lines_left_x2
lda #208*2
sec
sbc :virt_line_x2 ; calculate number of lines to the end of the buffer
cmp :lines_left_x2
bcs :one_pass_even ; if there's room, do it in one shot
; Since the screen height can be up to 200 lines and the virtual buffer size is 208, the common
; case will be that the blit will wrap around the end of the code field
tax
jsr :one_pass_even ; Go through with this draw count
stz :virt_line_x2
lda :lines_left_x2
sec
sbc :draw_count_x2 ; this many left to draw. Fall through to finish up
tax
:one_pass_even
txa
sta :draw_count_x2 ; this is the number of lines we will do right now
asl
adc :draw_count_x2
sta :draw_count_x6
lsr
sta :draw_count_x3
phb ; Save the existing bank
sep #$20 ; Set the data bank to the code field
lda BTableHigh
pha
plb
rep #$21 ; clear the carry while we're here...
ldx :virt_line_x2
ldal BTableLow,x ; Get the address of the code field line
sta :base_address ; Will use this address a few times
adc #_ENTRY_JMP ; Add the offsets in order to get absolute addresses
sta :entry_jmp_addr
adc #{_LOW_SAVE-_ENTRY_JMP}
sta :low_save_addr
lda :base_address
adc :exit_offset ; Add the offset to get the absolute address in the code field line
sta :exit_address
; First step is to set the BRA instruction to exit the code field at the proper location. There
; are two sub-steps to do here; we need to save the 8-bit value that exists at the location+1 and
; then overwrite it with the branch instruction.
sec ; These macros preform subtractions that do not underflow
CopyXToYPrep :do_save_entry_e;:draw_count_x6
LiteSetConstPrep :do_set_bra_e;:draw_count_x3
stal :do_setopcode_e+1
stal :do_set_rel_e+1
sep #$20
ldy :entry_jmp_addr
lda #$82
:do_setopcode_e jsr $0000 ; Copy in the BRL opcode into the entry point
ldx :exit_address
inx
ldy :low_save_addr
iny
:do_save_entry_e jsr $0000 ; Copy a byte from offset x to y
rep #$20
ldy :exit_address
lda :exit_bra
:do_set_bra_e jsr $0000 ; Set the BRA instruction in the code field to exit
ldy :entry_jmp_addr
iny
lda :opcode
:do_set_rel_e jsr $0000 ; Set the relative offset for all BRL instructions
plb
rts
; Odd case if very close to the even case, except that the code is entered a word later. It is still
; exited at the same word. There is extra work done because we have to save the third byte of the
; exit location to fill in the left edge and we have to patch a different BRL to enter the code field
; afte the right-edge byte is pushed onto the screen
:odd_case
dec
tax
lda CodeFieldOddBRA,x
sta :exit_bra
lda Col2CodeOffset,x
sta :exit_offset
sta LastPatchOffset ; Cache as a flag for later
txa ; StartXMod164 - 1
clc
adc ScreenWidth
cmp #164 ; Keep the value in range
bcc *+5
sbc #164
tax
lda Col2CodeOffset,x
clc
adc #-{_ENTRY_JMP+3} ; In this case it gets loaded in the X-register
sta :opcode
lda Col2CodeOffset-2,x
clc
adc #-{_ENTRY_ODD+3}
sta :odd_entry_offset
; Main loop
ldx :lines_left_x2
lda #208*2
sec
sbc :virt_line_x2 ; calculate number of lines to the end of the buffer
cmp :lines_left_x2
bcs :one_pass_odd ; if there's room, do it in one shot
tax
jsr :one_pass_odd
stz :virt_line_x2
lda :lines_left_x2
sec
sbc :draw_count_x2 ; this many left to draw. Fall through to finish up
tax
:one_pass_odd
txa
sta :draw_count_x2 ; this is the number of lines we will do right now
asl
adc :draw_count_x2
sta :draw_count_x6
lsr
sta :draw_count_x3
phb ; Save the existing bank
sep #$20
lda BTableHigh ; Get the bank
pha
plb
rep #$21
ldx :virt_line_x2
ldal BTableLow,x ; Get the address of the first code field line
sta :base_address ; Save it to use as the base address
adc #_ENTRY_JMP ; Add the offsets in order to get absolute addresses
sta :entry_jmp_addr
adc #{_ENTRY_ODD-_ENTRY_JMP}
sta :entry_odd_addr
adc #{_EXIT_ODD-_ENTRY_ODD}
sta :exit_odd_addr
adc #{_LOW_SAVE-_EXIT_ODD}
sta :low_save_addr
lda :base_address
adc :exit_offset ; Add some offsets to get the base address in the code field line
sta :exit_address
; Setup the jumps into the unrolled loops
sec
CopyXToYPrep :do_save_entry_o;:draw_count_x6
stal :do_save_high_byte+1
LiteSetConstPrep :do_set_bra_o;:draw_count_x3
stal :do_setopcode_o+1
stal :do_set_rel_o+1
stal :do_odd_code_entry+1
sep #$20
ldy :entry_jmp_addr
lda #$A2
:do_setopcode_o jsr $0000 ; Copy in the LDX opcode into the entry point
ldx :exit_address
inx
inx
ldy :exit_odd_addr
iny
:do_save_high_byte jsr $0000 ; Copy high byte of the exit location into the odd handling path
ldx :exit_address
inx
ldy :low_save_addr
iny
:do_save_entry_o jsr $0000 ; Save the low byte of the exit operand into a save location for restore later
rep #$20
ldy :exit_address
lda :exit_bra
:do_set_bra_o jsr $0000 ; Insert a BRA instruction over the saved word
ldy :entry_jmp_addr
iny
lda :opcode ; Store the same relative address to use for loading the entry word data
:do_set_rel_o jsr $0000
; The odd case need to do a bit of extra work
ldy :entry_odd_addr
iny
lda :odd_entry_offset
:do_odd_code_entry jsr $0000 ; Fill in the BRL argument for the odd entry
plb
rts
_RestoreScanlineBG0OpcodesLite
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
:exit_offset equ tmp4
; Avoid local var collisions
:virt_line_pos_x2 equ tmp11
:total_left_x2 equ tmp12
:current_count_x2 equ tmp13
:ptr equ tmp14
asl
sta :virt_line_pos_x2
tay
txa
asl
sta :total_left_x2
lda StartXMod164Tbl
sta :ptr
lda StartXMod164Tbl+2
sta :ptr+2
; Patch our the ranges from the StartXMod164Tbl array starting at the first virtual line
:loop
lda [:ptr],y
and #$FF00 ; Determine how many sequential lines to restore
xba
inc
asl
min :total_left_x2 ; Don't draw more than the number of lines that are left to process
sta :current_count_x2 ; Save a copy for later
sta :lines_left_x2 ; Set the parameter
sty :virt_line_x2 ; Set the parameter
lda LastOffsetTbl,y
sta :exit_offset
jsr _RestoreBG0OpcodesAltLite
clc
lda :virt_line_pos_x2
adc :current_count_x2
cmp #208*2 ; Do the modulo check in this loop
bcc *+5
sbc #208*2
sta :virt_line_pos_x2
tay
lda :total_left_x2
sec
sbc :current_count_x2
sta :total_left_x2
bne :loop
rts
; This is a variant of the above routine that allows each x-position to be set independently from a table of value. This is
; quite a bit slower than the other routine since we cannot store constant values for each line.
;
; This routine operates at a higher level and does not try to be super optimized for the case where every line has a different
; set of parameters. Instead, we optimize for the case where there are a few large ranges of the screen moving at different
; rates, e.g. a fixed status bar area on top, a slow-scrolling area in the middle and a fast are in the foreground.
;
; The table that drives this is dense and has the following format for each word
;
; Bits 0 - 7: X mod 164 value
; Bits 8 - 15: Number of scanline to persist this mod value
;
; So, if the first 10 entries has a mod value of 5, they would look like: $0905, $0805, $0705, ... $0105, $0005
;
; This allows the code to start an an arbitrary location and immeditely sync up with the modulo list. It also allows
; the code to easily skip ranges of constant values using the existing _ApplyBG0XPos function as a subroutine.
;
; NOTE: This function is *exactly* the same as _ApplyScanlineBG0XPos with the exception that it calls
; _ApplyBG0XPosAltLite instead of _ApplyBG0XPosAlt. Should unify with an subroutine selector
_ApplyScanlineBG0XPosLite
; Copies of the local variables in _ApplyBG0XPos
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
:exit_offset equ tmp4
; Avoid local var collision with _ApplyBG0XPos
:virt_line_pos_x2 equ tmp11
:total_left_x2 equ tmp12
:current_count_x2 equ tmp13
:ptr equ tmp14
lda StartXMod164Tbl
sta :ptr
lda StartXMod164Tbl+2
sta :ptr+2
ora :ptr
bne *+3 ; null pointer check
rts
lda StartYMod208 ; This is the base line of the virtual screen
asl
sta :virt_line_pos_x2
tay
lda ScreenHeight
asl
sta :total_left_x2
; Patch our the ranges from the StartXMod164Tbl array starting at the first virtual line
:loop
lda [:ptr],y
tax
and #$FF00 ; Determine how many sequential lines have this mod value
xba
inc
asl
min :total_left_x2 ; Don't draw more than the number of lines that are left to process
sta :current_count_x2 ; Save a copy for later
sta :lines_left_x2 ; Set the parameter
sty :virt_line_x2 ; Set the parameter
txa ; Put the X mod 164 value in the accumulator
and #$00FF
jsr _ApplyBG0XPosAltLite
lda :exit_offset ; Get the direct address in the code field that was overwritten
ldy :virt_line_pos_x2
sta LastOffsetTbl,y ; Stash it for use by the per-scanline resotre function
tya
clc
adc :current_count_x2
cmp #208*2 ; Do the modulo check in this loop
bcc *+5
sbc #208*2
sta :virt_line_pos_x2
tay
lda :total_left_x2
sec
sbc :current_count_x2
sta :total_left_x2
bne :loop
rts
; Copy from the offset at X to the offset at Y
;
; Y = code field offset
; X = value
CopyXToYPrep mac
lda #x2y_bottom
sbc ]2 ; count_x6
stal ]1+1 ; A jmp/jsr instruction
<<<
]line equ 199
lup 200
lda: {]line*_LINE_SIZE},x
sta: {]line*_LINE_SIZE},y
]line equ ]line-1
--^
x2y_bottom rts
; Set a constant 8-bit value across the code field
;
; Y = code field offset
LiteSetConstPrep mac
lda #lsc_bottom
sbc ]2 ; count_x3
stal ]1+1 ; A jmp/jsr instruction
<<<
]line equ 199
lup 200
sta: {]line*_LINE_SIZE},y
]line equ ]line-1
--^
lsc_bottom rts

View File

@ -38,7 +38,7 @@ PagePatches da {long_0-base+2}
da {long_2-base+2}
da {long_3-base+2}
; da {long_4-base+2}
da {long_5-base+2}
; da {long_5-base+2}
da {long_6-base+2}
da {odd_entry-base+2}
da {loop_exit_1-base+2}
@ -63,7 +63,7 @@ BankPatches da {long_0-base+3}
da {long_2-base+3}
da {long_3-base+3}
; da {long_4-base+3}
da {long_5-base+3}
; da {long_5-base+3}
da {long_6-base+3}
BankPatchNum equ *-BankPatches
@ -94,13 +94,13 @@ entry_jmp jmp $0100
; update the low-byte of the address, the means it takes only
; an amortized 4-cycles per line to set the entry point break
bit #$000B ; Check the bottom nibble to quickly identify a PEA instruction
bne r_is_not_pea ; This costs 5 cycles in the fast-path
; bit #$000B ; Check the bottom nibble to quickly identify a PEA instruction
; bne r_is_not_pea ; This costs 5 cycles in the fast-path
xba ; fast code for PEA
r_jmp_rtn sep #$20 ; shared return code path by all methods
two_byte_rtn pha
rep #$61 ; Clear Carry, Overflow and M bits #$20
; rep #$61 ; Clear Carry, Overflow and M bits #$20
odd_entry jmp $0100 ; unconditionally jump into the "next" instruction in the
; code field. This is OK, even if the entry point was the
; last instruction, because there is a JMP at the end of
@ -179,9 +179,9 @@ loop_back jmp loop-base ; +252 Ensure execution cont
loop_exit_3 jmp even_exit-base ; +255
odd_exit sep #$21 ; 8-bit mode and set the carry just in case we get to a snippet JMP
long_5 ldal OPCODE_SAVE ; Load the opcode that was saved
bit #$0B
bne :chk_jmp
;long_5 ldal OPCODE_SAVE ; Load the opcode that was saved
; bit #$0B
; bne :chk_jmp
long_6 ldal OPCODE_HIGH_SAVE+1 ; get the high byte of the PEA operand
; Fall-through when we have to push a byte on the left edge. Must be 8-bit on entry. Optimized

229
src/blitter/TemplateLite.s Normal file
View File

@ -0,0 +1,229 @@
; Template and equates for GTE blitter
blt_return_lite EXT
use ../Defs.s ; this causes merlin32 to terminate early with no error output
mx %00
REL_BRA equ $E0 ; pre-calculated branch back from the interrupt enabler
; Return to caller -- this is the target address to patch in the JMP instruction on the last rendered line. We
; put it at the beginning so the rest of the bank can be replicated line templates.
lite_full_return ENT
jml blt_return_lite ; Full exit
; Start of the template code. This code is replicated 208 times in the code field
; bank, which is what is required to render 26 tiles to cover the full screen vertical
; scrolling. The lite blitter is crafted to allow the accumulator to be in 8-bit
; mode and avoid any need for rep/sep instructions to handle the odd-aligned case
lite_base ENT
lite_entry_1 ldx #0000 ; Sets screen address (right edge)
txs
lite_entry_jmp brl $0000 ; If the screen is odd-aligned, then the opcode is set to
; $A2 to convert to a LDX #imm instruction. This puts the
; relative offset of the instruction field in the register
; and falls through to the next instruction.
lda: *+1,x ; Get the low byte and push onto the stack
pha
lite_odd_entry brl $0000 ; unconditionally jump into the "next" instruction in the
; code field. This is OK, even if the entry point was the
; last instruction, because there is a JMP at the end of
; the code field, so the code will simply jump to that
; instruction directly.
;
; As with the original entry point, because all of the
; code field is page-aligned, only the low byte needs to
; be updated when the scroll position changes
; Re-enable interrupts and continue -- the even_exit JMP from the previous line will jump here every
; 8 or 16 lines in order to give the system time to handle interrupts.
lite_enable_int tyx
txs ; restore the stack. No 2-layer support, so B and D are set to GTE data bank
lda STATE_REG_OFF ; we are in 8-bit mode the whole time...
stal STATE_REG
cli
sei
lda STATE_REG_BLIT ; External values
stal STATE_REG
bra lite_entry_1
lite_loop_exit_1 jmp lite_odd_exit ; +0 Alternate exit point depending on whether the left edge is
lite_loop_exit_2 jmp lite_even_exit ; +3 odd-aligned
lite_loop lup 82 ; +6 Set up 82 PEA instructions, which is 328 pixels and consumes 246 bytes
pea $0000 ; This is 41 8x8 tiles in width. Need to have N+1 tiles for screen overlap
--^
lite_loop_back jmp lite_loop ; +252 Ensure execution continues to loop around
lite_loop_exit_3 jmp lite_even_exit ; +255
mx %10
lite_odd_exit lda #0 ; get the high byte of the saved PEA operand (odd-case is already in 8-bit mode)
pha
lite_even_exit jmp *+5 ; Jump to the next line.
dfb $F4,$00 ; low-word of the saved PEA instruction
; Now repeat the code above 207 more times. Loop 206 times and then manually do the last one
]line equ 1 ; start at line 1 (line zero was just done above)
lup 206
ldx #0000
txs
dfb $82,$00,$00
lda: *+1,x
pha
dfb $82,$00,$00
tyx
txs
lda STATE_REG_OFF
stal STATE_REG
cli
sei
lda STATE_REG_BLIT
stal STATE_REG
; bra *-34
dfb $80,REL_BRA
jmp _LINE_BASE+{_LINE_SIZE*]line}+_EXIT_ODD
jmp _LINE_BASE+{_LINE_SIZE*]line}+_EXIT_EVEN
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000
jmp _LINE_BASE+{_LINE_SIZE*]line}+_LOOP
jmp _LINE_BASE+{_LINE_SIZE*]line}+_EXIT_EVEN
mx %10
lda #0
pha
jmp *+5
dfb $F4,$00
]line equ ]line+1
--^
:entry_207 ldx #0000
txs
dfb $82,$00,$00 ; brl $0000 starts at the next instruction
lda: *+1,x
sep #$20
pha
dfb $82,$00,$00
tyx
txs
lda STATE_REG_OFF
stal STATE_REG
cli
sei
lda STATE_REG_BLIT
stal STATE_REG
bra :entry_207
jmp :odd_out_207
jmp :exit_207
:loop_207
lup 82
pea $0000
--^
jmp :loop_207
jmp :exit_207
mx %10
:odd_out_207 lda #0
pha
:exit_207 jmp lite_enable_int
dfb $F4,$00
ds 3546 ; pad to the end of the bank to make sure we start at address $0000

View File

@ -5,92 +5,6 @@
; Based on the current value of StartY in the direct page. Set up the dispatch
; information so that the BltRange driver will render the correct code field
; lines in the correct order
_ApplyBG0YPosOld
:rtbl_idx_x2 equ tmp0
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
:draw_count_x2 equ tmp3
:stk_save equ tmp4
; First task is to fill in the STK_ADDR values by copying them from the RTable array. We
; copy from RTable[i] into BlitField[StartY+i]. As with all of this code, the difficult part
; is decomposing the update across banks
stz :rtbl_idx_x2 ; Start copying from the first entry in the table
lda StartY ; This is the base line of the virtual screen
jsr Mod208
sta StartYMod208
asl
sta :virt_line_x2 ; Keep track of it
phb ; Save the current bank
tsc ; we intentionally leak one byte of stack in each loop
sta :stk_save ; iteration, so save the stack to repair at the end
; copy a range of address from the table into the destination bank. If we restrict ourselves to
; rectangular playfields, this can be optimized to just subtracting a constant value. See the
; Templates::SetScreenAddrs subroutine.
lda ScreenHeight
asl
sta :lines_left_x2
; This is the verbose part -- figure out how many lines to draw. We don't want to artificially limit
; the height of the visible screen (for example, doing an animated wipe while scrolling), so the screen
; height could be anything from 1 to 200.
;
; For larger values, we want to break things up on 16-line boundaries based on the virt_line value. So,
;
; draw_count = min(lines_left, (16 - (virt_line % 16))
:loop
ldx :virt_line_x2
ldal BTableLow,x ; Get the address of the first code field line
tay
ldal BTableHigh,x ; Target bank in low byte
pha
txa
and #$001E
eor #$FFFF
sec
adc #32
min :lines_left_x2
sta :draw_count_x2 ; Do this many lines
tax
clc ; pre-advance virt_line_2 because we have the value
adc :virt_line_x2
sta :virt_line_x2
plb
jsr _CopyRTableToStkAddr
; CopyRTableToStkAddr :rtbl_idx_x2 ; X = rtbl_idx_x2 on return
txa ; carry flag is unchanged
adc :draw_count_x2 ; advance the index into the RTable
sta :rtbl_idx_x2
lda :lines_left_x2 ; subtract the number of lines we just completed
sec
sbc :draw_count_x2
sta :lines_left_x2
jne :loop
lda :stk_save
tcs
plb
rts
; This is an optimized version of _ApplyBG0YPos. We pre-compute the breakdown across the bank
; boundries in order to eliminate the the minimum calculation and some loop variable updates
; from the inner loop.
_ApplyBG0YPos

93
src/blitter/VertLite.s Normal file
View File

@ -0,0 +1,93 @@
; Subroutines that deal with the vertical scrolling and rendering. The primary function
; of these routines are to adjust tables and patch in new values into the code field
; when the virtual Y-position of the play field changes.
_ApplyBG0YPosLite
:rtbl_idx_x2 equ tmp0
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
:draw_count_x2 equ tmp3
:stk_save equ tmp4
:line_count equ tmp5
; First task is to fill in the STK_ADDR values by copying them from the RTable array. We
; copy from RTable[i] into BlitField[StartY+i].
stz :rtbl_idx_x2 ; Start copying from the first entry in the table
lda StartY ; This is the base line of the virtual screen
jsr Mod208
sta StartYMod208
asl
sta :virt_line_x2 ; Keep track of it
; copy a range of address from the table into the destination bank. If we restrict ourselves to
; rectangular playfields, this can be optimized to just subtracting a constant value. See the
; Templates::SetScreenAddrs subroutine.
lda ScreenHeight
asl
sta :lines_left_x2
; Check to see if we need to split the update into two parts, e.g. do we wrap around the end
; of the code field?
ldx :lines_left_x2
lda #208*2
sec
sbc :virt_line_x2 ; calculate number of lines to the end of the buffer
cmp :lines_left_x2
bcs :one_pass ; if there's room, do it in one shot
tax ; Only do this many lines right now (saved to draw_count_x2)
jsr :one_pass ; Go through with this draw count
stz :virt_line_x2 ; virtual line is at the top (by construction)
lda :lines_left_x2
sec
sbc :draw_count_x2 ; this many left to draw. Fall through to finish up
tax
; Set up the addresses for filling in the code field
:one_pass
stx :draw_count_x2
phb ; Save the current bank
ldx :virt_line_x2
lda BTableLow,x ; Get the address of the first code field line
tay
iny ; Fill in the first byte (_ENTRY_1 = 0)
sep #$20 ; Set the data bank to the code field
lda BTableHigh
pha
plb
rep #$21 ; clear the carry while we're here...
lda :draw_count_x2 ; Do this many lines
asl ; x4
asl ; x8
asl ; x16
sec
sbc :draw_count_x2 ; x14
lsr ; x7
eor #$FFFF
sec
adc #:bottom
stal :entry+1 ; patch in the dispatch address
; This is an inline, unrolled version of CopyRTableToStkAddr
:entry jmp $0000
]line equ 199
lup 200
ldal RTable+{]line*2},x
sta {]line*_LINE_SIZE},y
]line equ ]line-1
--^
:bottom
plb
rts

View File

@ -284,7 +284,6 @@ CopyTileAFast
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated)
plb
tax
_CopyTileAFast
]line equ 0

282
src/render/Lite.s Normal file
View File

@ -0,0 +1,282 @@
; Collection of render function used when the engine is in "GTE Lite" mode. In this mode
; there are no dynamic tile or two layer tiles enabled, so all of the tiles are comprised
; of PEA opcodes. These functions take advantage of this and the fact that masks are
; not needed to improve rendering speed.
;
; The GTE Lite mode uses a compact code field that fits in a single bank of memory, so
; all of the rendering routines are basically the same as those in Fast.s, but use a
; different stride.
SpriteUnder0Lite
ConstTile0Lite
lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
pha ; and put on the stack for later. Has TileStore bank in high byte.
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
plb
lda #0
]line equ 0
lup 8
sta: {_LINE_SIZE*]line}+$0001,y
sta: {_LINE_SIZE*]line}+$0004,y
]line equ ]line+1
--^
plb
rts
SpriteOverALite
lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
pha ; and put on the stack for later. Has TileStore bank in high byte.
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
lda TileStore+TS_TILE_ADDR,x
tax
plb
_SpriteOverALite
]line equ 0
lup 8
ldal tiledata+{]line*4},x
and tmp_sprite_mask+{]line*4}
ora tmp_sprite_data+{]line*4}
sta: $0004+{]line*_LINE_SIZE},y
ldal tiledata+{]line*4}+2,x
and tmp_sprite_mask+{]line*4}+2
ora tmp_sprite_data+{]line*4}+2
sta: $0001+{]line*_LINE_SIZE},y
]line equ ]line+1
--^
plb
rts
SpriteOverVLite
lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
pha ; and put on the stack for later. Has TileStore bank in high byte.
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
lda TileStore+TS_TILE_ADDR,x
tax
plb
_SpriteOverVLite
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4},x
and tmp_sprite_mask+{]dest*4}
ora tmp_sprite_data+{]dest*4}
sta: $0004+{]dest*_LINE_SIZE},y
ldal tiledata+{]src*4}+2,x
and tmp_sprite_mask+{]dest*4}+2
ora tmp_sprite_data+{]dest*4}+2
sta: $0001+{]dest*_LINE_SIZE},y
]src equ ]src-1
]dest equ ]dest+1
--^
plb
rts
SpriteOver0Lite
lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
pha ; and put on the stack for later. Has TileStore bank in high byte.
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
plb
_SpriteOver0Lite
]line equ 0
lup 8
lda tmp_sprite_data+{]line*4}
sta: $0004+{]line*_LINE_SIZE},y
lda tmp_sprite_data+{]line*4}+2
sta: $0001+{]line*_LINE_SIZE},y
]line equ ]line+1
--^
plb
rts
SpriteUnderALite
lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
pha ; and put on the stack for later. Has TileStore bank in high byte.
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
lda TileStore+TS_TILE_ADDR,x
tax
plb
_SpriteUnderALite
]line equ 0
lup 8
lda tmp_sprite_data+{]line*4}
andl tiledata+{]line*4}+32,x
oral tiledata+{]line*4},x
sta: $0004+{]line*_LINE_SIZE},y
lda tmp_sprite_data+{]line*4}+2
andl tiledata+{]line*4}+32+2,x
oral tiledata+{]line*4}+2,x
sta: $0001+{]line*_LINE_SIZE},y
]line equ ]line+1
--^
plb
rts
SpriteUnderVLite
lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
pha ; and put on the stack for later. Has TileStore bank in high byte.
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
lda TileStore+TS_TILE_ADDR,x
tax
plb
_SpriteUnderVLite
]src equ 7
]dest equ 0
lup 8
lda tmp_sprite_data+{]dest*4}
andl tiledata+{]src*4}+32,x
oral tiledata+{]src*4},x
sta: $0004+{]dest*_LINE_SIZE},y
lda tmp_sprite_data+{]dest*4}+2
andl tiledata+{]src*4}+32+2,x
oral tiledata+{]src*4}+2,x
sta: $0001+{]dest*_LINE_SIZE},y
]src equ ]src-1
]dest equ ]dest+1
--^
plb
rts
CopyTileALite
lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
pha ; and put on the stack for later. Has TileStore bank in high byte.
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated)
plb
tax
; brk $ac
_CopyTileALite
]line equ 0
lup 8
ldal tiledata+{]line*4},x
sta: $0004+{]line*_LINE_SIZE},y
ldal tiledata+{]line*4}+2,x
sta: $0001+{]line*_LINE_SIZE},y
]line equ ]line+1
--^
plb
rts
CopyTileVLite
lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
pha ; and put on the stack for later. Has TileStore bank in high byte.
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
lda TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated)
plb
tax
_CopyTileVLite
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4},x
sta: $0004+{]dest*_LINE_SIZE},y
ldal tiledata+{]src*4}+2,x
sta: $0001+{]dest*_LINE_SIZE},y
]src equ ]src-1
]dest equ ]dest+1
--^
plb
rts
OneSpriteLiteOver0
ldy TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
phy ; and put on the stack for later. Has TileStore bank in high byte.
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
tax ; VBuff address from SpriteBitsToVBuffAddrs macro
plb ; set to the code field bank
_OneSpriteLiteOver0
]line equ 0
lup 8
ldal spritedata+{]line*SPRITE_PLANE_SPAN},x
sta: $0004+{]line*_LINE_SIZE},y
ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta: $0001+{]line*_LINE_SIZE},y
]line equ ]line+1
--^
plb ; Restore the TileStore bank
rts
; Next implementation; drawing a sprite onto a regular tile. The 1-sprite dispatch preserves the
; X-register, so it already points to the TileStore
OneSpriteLiteOverV
jsr FastCopyTileDataV
bra _OneSpriteLiteOver
OneSpriteLiteOverA
jsr FastCopyTileDataA
_OneSpriteLiteOver
lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
pha ; and put on the stack for later. Has TileStore bank in high byte.
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
ldx sprite_ptr0
plb
_OneSpriteLiteOverA
_OneSpriteLiteOverV
]line equ 0
lup 8
lda tmp_tile_data+{]line*4}
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
oral spritedata+{]line*SPRITE_PLANE_SPAN},x
sta: $0004+{]line*_LINE_SIZE},y
lda tmp_tile_data+{]line*4}+2
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta: $0001+{]line*_LINE_SIZE},y
]line equ ]line+1
--^
plb
rts
OneSpriteLiteUnderA
jsr FastCopyTileDataAndMaskA
bra _OneSpriteLiteUnder
OneSpriteLiteUnderV
jsr FastCopyTileDataAndMaskV
_OneSpriteLiteUnder
lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
pha ; and put on the stack for later. Has TileStore bank in high byte.
ldy TileStore+TS_CODE_ADDR_LOW,x ; load the address of the code field
ldx sprite_ptr0
plb
_OneSpriteLiteUnderA
_OneSpriteLiteUnderV
]line equ 0
lup 8
ldal spritedata+{]line*SPRITE_PLANE_SPAN},x
and tmp_tile_mask+{]line*4}
ora tmp_tile_data+{]line*4}
sta: $0004+{]line*_LINE_SIZE},y
ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
and tmp_tile_mask+{]line*4}+2
ora tmp_tile_data+{]line*4}+2
sta: $0001+{]line*_LINE_SIZE},y
]line equ ]line+1
--^
plb
rts

View File

@ -1,8 +1,8 @@
This folder contains the rendering tuples for the different type of tile rendering modes
that are defined by both the engine mode and the specific tile attributes. There are
a *lot* or variants, so they are cataloged here.
a *lot* of variants, so they are cataloged here.
The top-level TileRender function in the main entry point that defined the overal tile render
The top-level TileRender function in the main entry point that defines the overal tile render
flow as well as the register parameters and calling conventions for each of the modular
components.
@ -50,7 +50,7 @@ There are 5 pluggable functions that make up a rendering mode
4. K_TS_COPY_TILE_DATA & K_TS_APPLY_TILE_DATA
A pair of function that copye tile data (and possible mask information) into a temporary
A pair of function that copy tile data (and possible mask information) into a temporary
direct page space and then render that workspace into the code field.
These functions are used as building blocks by the generic Over/Under multi-sprite

View File

@ -102,14 +102,17 @@ TileStoreLookup ENT
;
; Remember, because the data is pushed on to the stack, the last instruction, which is
; in the highest memory location, pushed data that apepars on the left edge of the screen.
;
; This table is overwritten by the "Lite" engine intialization code to provide a different set
; of offsets since the lite blitter has a different offset than the normal one.
]step equ 0
dw CODE_TOP ; There is a spot where we load Col2CodeOffet-2,x
dw CODE_TOP ; There is a spot where we load Col2CodeOffset-2,x
Col2CodeOffset ENT
lup 82
dw CODE_TOP+{{81-]step}*PER_TILE_SIZE}
]step equ ]step+1
--^
]step equ 0
lup 82 ; Make is a double-length table so we can add the ScreenWidth without testing for wrap-around
dw CODE_TOP+{{81-]step}*PER_TILE_SIZE}
]step equ ]step+1
@ -135,7 +138,7 @@ JTableOffset ENT
; physical word index that each instruction is intended to be placed at is in the comment.
bra *-3 ; wrap around
CodeFieldEvenBRA ENT
bra *+6 ; 81 -- need to skip over the JMP loop that passed control back
bra *+6 ; 81 -- need to skip over the JMP loop that passes control back
bra *+9 ; 80
bra *+12 ; 79
bra *+15 ; 78
@ -220,7 +223,7 @@ CodeFieldEvenBRA ENT
bra *-6 ; wrap around
CodeFieldOddBRA ENT
bra *+9 ; 81 -- need to skip over two JMP instructions
bra *+9 ; 81 / 0 -- need to skip over two JMP instructions
bra *+12 ; 80
bra *+15 ; 79
bra *+18 ; 78
@ -230,7 +233,7 @@ CodeFieldOddBRA ENT
bra *+30 ; 74
bra *+33 ; 73
bra *+36 ; 72
bra *+39 ; 71
bra *+39 ; 71 /10
bra *+42 ; 70
bra *+45 ; 69
bra *+48 ; 68
@ -240,7 +243,7 @@ CodeFieldOddBRA ENT
bra *+60 ; 64
bra *+63 ; 64
bra *+66 ; 62
bra *+69 ; 61
bra *+69 ; 61 / 20
bra *+72 ; 60
bra *+75 ; 59
bra *+78 ; 58
@ -250,7 +253,7 @@ CodeFieldOddBRA ENT
bra *+90 ; 54
bra *+93 ; 53
bra *+96 ; 52
bra *+99 ; 51
bra *+99 ; 51 / 30
bra *+102 ; 50
bra *+105 ; 49
bra *+108 ; 48
@ -260,7 +263,7 @@ CodeFieldOddBRA ENT
bra *+120 ; 44
bra *+123 ; 43
bra *+126 ; 42
bra *+129 ; 41
bra *+129 ; 41 / 40
bra *-126 ; 40
bra *-123 ; 39
bra *-120 ; 38
@ -270,7 +273,7 @@ CodeFieldOddBRA ENT
bra *-108 ; 34
bra *-105 ; 33
bra *-102 ; 32
bra *-99 ; 31
bra *-99 ; 31 / 50
bra *-96 ; 30
bra *-93 ; 29
bra *-90 ; 28
@ -280,7 +283,7 @@ CodeFieldOddBRA ENT
bra *-78 ; 24
bra *-75 ; 23
bra *-72 ; 22
bra *-69 ; 21
bra *-69 ; 21 / 60
bra *-66 ; 20
bra *-63 ; 19
bra *-60 ; 18
@ -290,7 +293,7 @@ CodeFieldOddBRA ENT
bra *-48 ; 14
bra *-45 ; 13
bra *-42 ; 12
bra *-39 ; 11
bra *-39 ; 11 / 70
bra *-36 ; 10
bra *-33 ; 9
bra *-30 ; 8
@ -300,7 +303,7 @@ CodeFieldOddBRA ENT
bra *-18 ; 4
bra *-15 ; 3
bra *-12 ; 2
bra *-9 ; 1
bra *-9 ; 1 / 80
bra *-6 ; 0 -- branch back 6 to skip the JMP even path
]step equ $2000