Refactor to support building a tool and new demo to try and use it [not working]

This commit is contained in:
Lucas Scharenbroich 2022-04-23 00:47:13 -05:00
parent 8bb17895a9
commit e2710ace85
24 changed files with 868 additions and 502 deletions

18
build-image.bat Normal file
View File

@ -0,0 +1,18 @@
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/Toolbox"
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% .\src\GTETool
REM Copy in the image assets

83
demos/tool/App.Main.s Normal file
View File

@ -0,0 +1,83 @@
REL
DSK MAINSEG
use Locator.Macs
use Load.Macs
use Mem.Macs
use Misc.Macs
use Util.Macs
use EDS.GSOS.Macs
_GTEStartUp MAC
UserTool $02A0
<<<
; Typical init
phk
plb
jsr ToolStartUp ; Start up the basic tools: Locator, Memory Manager, Misc
jsr GTEStartUp
_QuitGS qtRec
bcs Fatal
Fatal brk $00
qtRec adrl $0000
da $00
ToolStartUp
_TLStartUp ; normal tool initialization
pha
_MMStartUp
pla
sta MasterId ; our master handle references the memory allocated to us
ora #$0100 ; set auxID = $01 (valid values $01-0f)
sta UserId ; any memory we request must use our own id
_MTStartUp
rts
; Load the GTE User Tool and register it
GTEStartUp
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000 ; result space
lda UserId
pha
pea #^ToolPath
pea #ToolPath
pea $0001 ; do not load into special memory
pea $0001 ; GS/OS string for the argument
_InitialLoad2
ply
pla ; Address of the loaded tool
plx
ply
ply
pea $8000 ; User toolset
pea $00A0 ; Set the tool set number
phx
pha ; Address of function pointer table
_SetTSPtr
clc ; Give GTE a page of direct page memory
tdc
adc #$0100
pha
lda UserId
pha
_GTEStartUp
rts
MasterId ds 2
UserId ds 2
ToolPath strl '9:GTETool'

10
demos/tool/App.s Normal file
View File

@ -0,0 +1,10 @@
; IIgs Sprite Testbed
TYP $B3 ; S16 file
DSK GTEToolDemo
XPL
; Segment #1 -- Main execution block
ASM App.Main.s
SNA Main

BIN
demos/tool/GTEToolDemo Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
GTEToolDemo=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/Tool"
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% .\GTEToolDemo
%CADIUS% ADDFILE %IMAGE% %FOLDER% ..\..\src\GTETool
REM Copy in the image assets

30
demos/tool/package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "gte-toolbox-demo",
"version": "1.0.0",
"description": "A testbed for developing a toolbox wrapper around the GTE library",
"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% GTEToolTest -Source GTEToolTest_S02_MAINSEG_Output.txt -Debug -CompatibilityLayer",
"build": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.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": {
}
}

29
package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "generic-tile-engine",
"version": "1.0.0",
"description": "A tile-base game engine for the Apple IIgs",
"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%",
"build": "%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

@ -8,94 +8,6 @@
use .\Defs.s
; Feature flags
NO_INTERRUPTS equ 1 ; turn off for crossrunner debugging
NO_MUSIC equ 1 ; turn music + tool loading off
; External data space provided by the main program segment
tiledata EXT
TileStore EXT
; Sprite plane data and mask banks are provided as an exteral segment
;
; The sprite data holds a set of pre-rendered sprites that are optimized to support the rendering pipeline. There
; are four copies of each sprite, along with the cooresponding mask laid out into 4x4 tile regions where the
; empty row and column is shared between adjacent blocks.
;
; Logically, the memory is laid out as 4 columns of sprites and 4 rows.
;
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | | | | | | | | | | | | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | 0 | 0 | | 1 | 1 | | 2 | 2 | | 3 | 3 | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | 0 | 0 | | 1 | 1 | | 2 | 2 | | 3 | 3 | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | | | | | | | | | | | | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | 4 | 4 | | 5 | 5 | | 6 | 6 | | 7 | 7 | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | 4 | 4 | | 5 | 5 | | 6 | 6 | | 7 | 7 | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | | | | | | | | | | | | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
;
; For each sprite, when it needs to be copied into an on-screen tile, it could exist at any offset compared to its
; natural alignment. By having a buffer around the sprite data, an address pointer can be set to a different origin
; and a simple 8x8 block copy can cut out the appropriate bit of the sprite. For example, here is a zoomed-in look
; at a sprite with an offset, O, at (-2,-3). As shown, by selecting an appropriate origin, just the top corner
; of the sprite data will be copied.
;
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | || | || | | | || | | | |
; +---+-- O----------------+ --+---++---+---+---+---++---+---+---+---+..
; | | | | | || | | | || | | | |
; +---+-- | | --+---++---+---+---+---++---+---+---+---+..
; | | | | | || | | | || | | | |
; +---+-- | | --+---++---+---+---+---++---+---+---+---+..
; | | | | | || | | | || | | | |
; +===+== | ++===+== | ==+===++===+===+===+===++===+===+===+===+..
; | | | || | S | S | S || S | S | S | || | | | |
; +---+-- +----------------+ --+---++---+---+---+---++---+---+---+---+..
; | | || S | S S | S || S | S | S | S || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | | | || S | S | S | S || S | S | S | S || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | | | || S | S | S | S || S | S | S | S || | | | |
; +===+===+===+===++===+===+===+===++===+===+===+===++===+===+===+===+..
; | | | | || S | S | S | S || S | S | S | S || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | | | || S | S | S | S || S | S | S | S || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | | | || S | S | S | S || S | S | S | S || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | | | || | S | S | S || S | S | S | || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; . . . . . . . . . . . . . . . . .
;
; Each sprite will take up, effectively 9 tiles of storage space per
; instance (plus edges) and there are 4 instances for the H/V bits
; and 4 more for the masks. This results in a need for 43,264 bytes
; for all 16 sprites.
spritedata EXT
spritemask EXT
; If there are overlays, they are provided as an external
Overlay EXT
; Core engine functionality. The idea is that that source file can be PUT into
; a main source file and all of the functionality will be available.
;
; There are some constancts that must be externally defined that can affect how
; the GTE runtime works
;
; NO_MUSIC : Set to non-zero to avoid using any source
; NO_INTERRUPTS : Set to non-zero to avoid installing custom interrupt handlers
mx %00
; High-Level StartUp and ShutDown functions
EngineStartUp ENT
phb
phk
@ -103,41 +15,23 @@ EngineStartUp ENT
jsr ToolStartUp ; Start up the toolbox tools we rely on
jsr _CoreStartUp
jsr SoundStartUp ; Start up any sound/music tools
plb
rtl
_CoreStartUp
jsr SoundStartUp ; Start up any sound/music tools
jsr IntStartUp ; Enable certain iterrupts
jsr InitMemory ; Allocate and initialize memory for the engine
jsr EngineReset ; All of the resources are allocated, put the engine in a known state
jsr InitGraphics ; Initialize all of the graphics-related data
nop
jsr InitSprites ; Initialize the sprite subsystem
jsr InitTiles ; Initialize the tile subsystem
jsr InitTimers ; Initialize the timer subsystem
rts
EngineShutDown ENT
phb
phk
plb
jsr SoundShutDown
jsr _CoreShutDown
jsr ToolShutDown
plb
rtl
_CoreShutDown
jsr IntShutDown
jsr SoundShutDown
rts
ToolStartUp
_TLStartUp ; normal tool initialization
pha
@ -152,7 +46,7 @@ ToolStartUp
rts
MasterId ds 2
UserId ds 2
;UserId ds 2
; Fatal error handler invoked by the _Err macro
PgmDeath tax
@ -168,9 +62,6 @@ PgmDeath0 pha
ContDeath ldx #$1503
jsl $E10000
ToolShutDown
rts
; Use Tool222 (NinjaTrackerPlus) for music playback
SoundStartUp
lda #NO_MUSIC
@ -194,299 +85,10 @@ SoundShutDown
:no_music
rts
; Install interrupt handlers. We use the VBL interrupt to keep animations
; moving at a consistent rate, regarless of the rendered frame rate. The
; one-second timer is generally just used for counters and as a handy
; frames-per-second trigger.
IntStartUp
lda #NO_INTERRUPTS
bne :no_interrupts
PushLong #0
pea $0015 ; Get the existing 1-second interrupt handler and save
_GetVector
PullLong OldOneSecVec
pea $0015 ; Set the new handler and enable interrupts
PushLong #OneSecHandler
_SetVector
pea $0006
_IntSource
PushLong #VBLTASK ; Also register a Heart Beat Task
_SetHeartBeat
:no_interrupts
rts
IntShutDown
lda #NO_INTERRUPTS
bne :no_interrupts
pea $0007 ; disable 1-second interrupts
_IntSource
PushLong #VBLTASK ; Remove our heartbeat task
_DelHeartBeat
pea $0015
PushLong OldOneSecVec ; Reset the interrupt vector
_SetVector
:no_interrupts
rts
; Interrupt handlers. We install a heartbeat (1/60th second and a 1-second timer)
OneSecHandler mx %11
phb
pha
phk
plb
rep #$20
inc OneSecondCounter
sep #$20
ldal $E0C032
and #%10111111 ;clear IRQ source
stal $E0C032
pla
plb
clc
rtl
mx %00
OneSecondCounter ENT
dw 0
OldOneSecVec ds 4
VBLTASK hex 00000000
dw 0
hex 5AA5
; Reset the engine to a known state
; Blitter initialization
EngineReset
stz ScreenHeight
stz ScreenWidth
stz ScreenY0
stz ScreenY1
stz ScreenX0
stz ScreenX1
stz ScreenTileHeight
stz ScreenTileWidth
stz StartX
stz OldStartX
stz StartXMod164
stz StartY
stz OldStartY
stz StartYMod208
stz EngineMode
stz DirtyBits
stz LastRender
stz LastPatchOffset
stz BG1StartX
stz BG1StartXMod164
stz BG1StartY
stz BG1StartYMod208
stz BG1OffsetIndex
stz BG0TileOriginX
stz BG0TileOriginY
stz OldBG0TileOriginX
stz OldBG0TileOriginY
stz BG1TileOriginX
stz BG1TileOriginY
stz OldBG1TileOriginX
stz OldBG1TileOriginY
stz TileMapWidth
stz TileMapHeight
stz TileMapPtr
stz TileMapPtr+2
stz FringeMapPtr
stz FringeMapPtr+2
stz BG1TileMapWidth
stz BG1TileMapHeight
stz BG1TileMapPtr
stz BG1TileMapPtr+2
stz SCBArrayPtr
stz SCBArrayPtr+2
stz SpriteBanks
stz SpriteMap
stz ActiveSpriteCount
stz OneSecondCounter
lda #13
sta tmp15
stz tmp14
:loop
ldx #BlitBuff
lda #^BlitBuff
ldy tmp14
jsr BuildBank
lda tmp14
clc
adc #4
sta tmp14
dec tmp15
bne :loop
rts
; 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
;
; 0. Full Screen : 40 x 25 320 x 200 (32,000 bytes (100.0%))
; 1. Sword of Sodan : 34 x 24 272 x 192 (26,112 bytes ( 81.6%))
; 2. ~NES : 32 x 25 256 x 200 (25,600 bytes ( 80.0%))
; 3. Task Force : 32 x 22 256 x 176 (22,528 bytes ( 70.4%))
; 4. Defender of the World : 35 x 20 280 x 160 (22,400 bytes ( 70.0%))
; 5. Rastan : 32 x 20 256 x 160 (20,480 bytes ( 64.0%))
; 6. Game Boy Advanced : 30 x 20 240 x 160 (19,200 bytes ( 60.0%))
; 7. Ancient Land of Y's : 36 x 16 288 x 128 (18,432 bytes ( 57.6%))
; 8. Game Boy Color : 20 x 18 160 x 144 (11,520 bytes ( 36.0%))
; 9. Agony (Amiga) : 36 x 24 288 x 192 (27,648 bytes ( 86.4%))
; 10. Atari Lynx : 20 x 13 160 x 102 (8,160 bytes ( 25.5%))
;
; 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
txa
asl
tax
ldy ScreenModeHeight,x
lda ScreenModeWidth,x
tax
:direct cpy #201
bcs :exit
cpx #321
bcs :exit
txa
lsr
pha ; Save X (width / 2) and Y (height)
phy
lda #160 ; Center the screen
sec
sbc 3,s
lsr
xba
pha ; Save half the origin coordinate
lda #200
sec
sbc 3,s ; This is now Y because of the PHA above
lsr
ora 1,s
plx ; Throw-away to pop the stack
ply
plx
jsr SetScreenRect
jmp FillScreen ; tail return
:exit
rts
WaitForKey sep #$20
stal KBD_STROBE_REG ; clear the strobe
:WFK ldal KBD_REG
bpl :WFK
rep #$20
and #$007F
rts
ClearKbdStrobe sep #$20
stal KBD_STROBE_REG
rep #$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
sep #$20
ldal OPTION_KEY_REG ; 'B' button
and #$80
beq :BNotDown
lda #PAD_BUTTON_B
ora 2,s
sta 2,s
:BNotDown
ldal COMMAND_KEY_REG
and #$80
beq :ANotDown
lda #PAD_BUTTON_A
ora 2,s
sta 2,s
:ANotDown
ldal KBD_STROBE_REG ; read the keyboard
bit #$80
beq :KbdNotDwn ; check the key-down status
and #$7f
ora 1,s
sta 1,s
cmpl LastKey
beq :KbdDown
stal LastKey
lda #PAD_KEY_DOWN ; set the keydown flag
ora 2,s
sta 2,s
bra :KbdDown
:KbdNotDwn
lda #0
stal LastKey
:KbdDown
rep #$20
pla
ToolShutDown
rts
put CoreImpl.s
put blitter/Template.s
put Memory.s

369
src/CoreImpl.s Normal file
View File

@ -0,0 +1,369 @@
; Feature flags
NO_INTERRUPTS equ 1 ; turn off for crossrunner debugging
NO_MUSIC equ 1 ; turn music + tool loading off
; External data space provided by the main program segment
tiledata EXT
TileStore EXT
; Sprite plane data and mask banks are provided as an external segment
;
; The sprite data holds a set of pre-rendered sprites that are optimized to support the rendering pipeline. There
; are four copies of each sprite, along with the cooresponding mask laid out into 4x4 tile regions where the
; empty row and column is shared between adjacent blocks.
;
; Logically, the memory is laid out as 4 columns of sprites and 4 rows.
;
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | | | | | | | | | | | | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | 0 | 0 | | 1 | 1 | | 2 | 2 | | 3 | 3 | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | 0 | 0 | | 1 | 1 | | 2 | 2 | | 3 | 3 | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | | | | | | | | | | | | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | 4 | 4 | | 5 | 5 | | 6 | 6 | | 7 | 7 | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | 4 | 4 | | 5 | 5 | | 6 | 6 | | 7 | 7 | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
; | | | | | | | | | | | | | ...
; +---+---+---+---+---+---+---+---+---+---+---+---+-...
;
; For each sprite, when it needs to be copied into an on-screen tile, it could exist at any offset compared to its
; natural alignment. By having a buffer around the sprite data, an address pointer can be set to a different origin
; and a simple 8x8 block copy can cut out the appropriate bit of the sprite. For example, here is a zoomed-in look
; at a sprite with an offset, O, at (-2,-3). As shown, by selecting an appropriate origin, just the top corner
; of the sprite data will be copied.
;
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | || | || | | | || | | | |
; +---+-- O----------------+ --+---++---+---+---+---++---+---+---+---+..
; | | | | | || | | | || | | | |
; +---+-- | | --+---++---+---+---+---++---+---+---+---+..
; | | | | | || | | | || | | | |
; +---+-- | | --+---++---+---+---+---++---+---+---+---+..
; | | | | | || | | | || | | | |
; +===+== | ++===+== | ==+===++===+===+===+===++===+===+===+===+..
; | | | || | S | S | S || S | S | S | || | | | |
; +---+-- +----------------+ --+---++---+---+---+---++---+---+---+---+..
; | | || S | S S | S || S | S | S | S || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | | | || S | S | S | S || S | S | S | S || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | | | || S | S | S | S || S | S | S | S || | | | |
; +===+===+===+===++===+===+===+===++===+===+===+===++===+===+===+===+..
; | | | | || S | S | S | S || S | S | S | S || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | | | || S | S | S | S || S | S | S | S || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | | | || S | S | S | S || S | S | S | S || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; | | | | || | S | S | S || S | S | S | || | | | |
; +---+---+---+---++---+---+---+---++---+---+---+---++---+---+---+---+..
; . . . . . . . . . . . . . . . . .
;
; Each sprite will take up, effectively 9 tiles of storage space per
; instance (plus edges) and there are 4 instances for the H/V bits
; and 4 more for the masks. This results in a need for 43,264 bytes
; for all 16 sprites.
spritedata EXT
spritemask EXT
; If there are overlays, they are provided as an external
Overlay EXT
; Core engine functionality. The idea is that that source file can be PUT into
; a main source file and all of the functionality will be available.
;
; There are some constancts that must be externally defined that can affect how
; the GTE runtime works
;
; NO_MUSIC : Set to non-zero to avoid using any source
; NO_INTERRUPTS : Set to non-zero to avoid installing custom interrupt handlers
mx %00
; A = memory manager userId
; X = tool number
_CoreStartUp
sta UserId ; This is the first thing to do
stx ToolNum
jsr IntStartUp ; Enable certain iterrupts
jsr InitMemory ; Allocate and initialize memory for the engine
jsr EngineReset ; All of the resources are allocated, put the engine in a known state
; jsr InitGraphics ; Initialize all of the graphics-related data
; jsr InitSprites ; Initialize the sprite subsystem
; jsr InitTiles ; Initialize the tile subsystem
jsr InitTimers ; Initialize the timer subsystem
_CoreShutDown
jsr IntShutDown
rts
; Install interrupt handlers. We use the VBL interrupt to keep animations
; moving at a consistent rate, regarless of the rendered frame rate. The
; one-second timer is generally just used for counters and as a handy
; frames-per-second trigger.
IntStartUp
lda #NO_INTERRUPTS
bne :no_interrupts
PushLong #0
pea $0015 ; Get the existing 1-second interrupt handler and save
_GetVector
PullLong OldOneSecVec
pea $0015 ; Set the new handler and enable interrupts
PushLong #OneSecHandler
_SetVector
pea $0006
_IntSource
PushLong #VBLTASK ; Also register a Heart Beat Task
_SetHeartBeat
:no_interrupts
rts
IntShutDown
lda #NO_INTERRUPTS
bne :no_interrupts
pea $0007 ; disable 1-second interrupts
_IntSource
PushLong #VBLTASK ; Remove our heartbeat task
_DelHeartBeat
pea $0015
PushLong OldOneSecVec ; Reset the interrupt vector
_SetVector
:no_interrupts
rts
; Interrupt handlers. We install a heartbeat (1/60th second and a 1-second timer)
OneSecHandler mx %11
phb
pha
phk
plb
rep #$20
inc OneSecondCounter
sep #$20
ldal $E0C032
and #%10111111 ;clear IRQ source
stal $E0C032
pla
plb
clc
rtl
mx %00
OneSecondCounter ENT
dw 0
OldOneSecVec ds 4
VBLTASK hex 00000000
dw 0
hex 5AA5
; Reset the engine to a known state
; Blitter initialization
EngineReset
stz ScreenHeight
stz ScreenWidth
stz ScreenY0
stz ScreenY1
stz ScreenX0
stz ScreenX1
stz ScreenTileHeight
stz ScreenTileWidth
stz StartX
stz OldStartX
stz StartXMod164
stz StartY
stz OldStartY
stz StartYMod208
stz EngineMode
stz DirtyBits
stz LastRender
stz LastPatchOffset
stz BG1StartX
stz BG1StartXMod164
stz BG1StartY
stz BG1StartYMod208
stz BG1OffsetIndex
stz BG0TileOriginX
stz BG0TileOriginY
stz OldBG0TileOriginX
stz OldBG0TileOriginY
stz BG1TileOriginX
stz BG1TileOriginY
stz OldBG1TileOriginX
stz OldBG1TileOriginY
stz TileMapWidth
stz TileMapHeight
stz TileMapPtr
stz TileMapPtr+2
stz FringeMapPtr
stz FringeMapPtr+2
stz BG1TileMapWidth
stz BG1TileMapHeight
stz BG1TileMapPtr
stz BG1TileMapPtr+2
stz SCBArrayPtr
stz SCBArrayPtr+2
stz SpriteBanks
stz SpriteMap
stz ActiveSpriteCount
stz OneSecondCounter
lda #13
sta tmp15
stz tmp14
:loop
ldx #BlitBuff
lda #^BlitBuff
ldy tmp14
; jsr BuildBank
lda tmp14
clc
adc #4
sta tmp14
dec tmp15
bne :loop
rts
WaitForKey sep #$20
stal KBD_STROBE_REG ; clear the strobe
:WFK ldal KBD_REG
bpl :WFK
rep #$20
and #$007F
rts
ClearKbdStrobe sep #$20
stal KBD_STROBE_REG
rep #$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
sep #$20
ldal OPTION_KEY_REG ; 'B' button
and #$80
beq :BNotDown
lda #PAD_BUTTON_B
ora 2,s
sta 2,s
:BNotDown
ldal COMMAND_KEY_REG
and #$80
beq :ANotDown
lda #PAD_BUTTON_A
ora 2,s
sta 2,s
:ANotDown
ldal KBD_STROBE_REG ; read the keyboard
bit #$80
beq :KbdNotDwn ; check the key-down status
and #$7f
ora 1,s
sta 1,s
cmpl LastKey
beq :KbdDown
stal LastKey
lda #PAD_KEY_DOWN ; set the keydown flag
ora 2,s
sta 2,s
bra :KbdDown
:KbdNotDwn
lda #0
stal LastKey
:KbdDown
rep #$20
pla
rts
; Helper function to take a local pixel coordinate [0, ScreenWidth-1],[0, ScreenHeight-1] and return the
; row and column in the tile store that is corresponds to. This takes into consideration the StartX and
; StartY offsets.
;
; This is more specialized than the code in the _MarkDirtySprite routine below since it does not deal with
; negative or off-screen values.
_OriginToTileStore
lda StartYMod208
lsr
lsr
and #$FFFE ; Store the pre-multiplied by 2 for indexing
tay
lda StartXMod164
lsr
and #$FFFE ; Same pre-multiply by 2 for later
tax
rts
; X = local x-coordinate (0, playfield width)
; Y = local y-coordinate (0, playfield height)
_LocalToTileStore
clc
tya
adc StartYMod208 ; Adjust for the scroll offset
cmp #208 ; check if we went too far positive
bcc *+5
sbc #208
lsr
lsr
and #$FFFE ; Store the pre-multiplied by 2 for indexing
tay
clc
txa
adc StartXMod164
cmp #164
bcc *+5
sbc #164
lsr
and #$FFFE ; Same pre-multiply by 2 for later
tax
rts

View File

@ -88,7 +88,9 @@ BankLoad equ 104
TileStoreBankAndBank01 equ 106
TileStoreBankAndTileDataBank equ 108
TileStoreBankDoubled equ 110
Next equ 112
UserId equ 112 ; Memory manager user Id to use
ToolNum equ 114 ; Tool number assigned to us
Next equ 116
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

View File

@ -24,6 +24,82 @@ DefaultPalette dw $0000,$007F,$0090,$0FF0
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
;
; 0. Full Screen : 40 x 25 320 x 200 (32,000 bytes (100.0%))
; 1. Sword of Sodan : 34 x 24 272 x 192 (26,112 bytes ( 81.6%))
; 2. ~NES : 32 x 25 256 x 200 (25,600 bytes ( 80.0%))
; 3. Task Force : 32 x 22 256 x 176 (22,528 bytes ( 70.4%))
; 4. Defender of the World : 35 x 20 280 x 160 (22,400 bytes ( 70.0%))
; 5. Rastan : 32 x 20 256 x 160 (20,480 bytes ( 64.0%))
; 6. Game Boy Advanced : 30 x 20 240 x 160 (19,200 bytes ( 60.0%))
; 7. Ancient Land of Y's : 36 x 16 288 x 128 (18,432 bytes ( 57.6%))
; 8. Game Boy Color : 20 x 18 160 x 144 (11,520 bytes ( 36.0%))
; 9. Agony (Amiga) : 36 x 24 288 x 192 (27,648 bytes ( 86.4%))
; 10. Atari Lynx : 20 x 13 160 x 102 (8,160 bytes ( 25.5%))
;
; 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
txa
asl
tax
ldy ScreenModeHeight,x
lda ScreenModeWidth,x
tax
:direct cpy #201
bcs :exit
cpx #321
bcs :exit
txa
lsr
pha ; Save X (width / 2) and Y (height)
phy
lda #160 ; Center the screen
sec
sbc 3,s
lsr
xba
pha ; Save half the origin coordinate
lda #200
sec
sbc 3,s ; This is now Y because of the PHA above
lsr
ora 1,s
plx ; Throw-away to pop the stack
ply
plx
jsr SetScreenRect
jmp FillScreen ; tail return
:exit
rts
; Return the current border color ($0 - $F) in the accumulator
_GetBorderColor lda #0000
sep #$20

34
src/Master.s Normal file
View File

@ -0,0 +1,34 @@
; IIgs Generic Tile Engine User Toolset
TYP $BA ; TOL file
DSK GTETool
XPL
; Main toolbox interface and code
ASM Tool.s
SNA Main
; 64KB Tile Memory
ASM static\TileData.s
KND #$1001 ; Type and Attributes ($10=Static,$01=Data)
SNA TDATA
; 64KB Sprite Plane Data
ASM static\SprData.s
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
SNA SDATA
;64KB Sprite Mask Data
ASM static\SprMask.s
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
SNA SMASK
; 64KB Tile Store
ASM static\TileStore.s
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
SNA TSTORE

View File

@ -62,9 +62,9 @@ InitSprites
; covering the exact same tiles.
; Render a sprite stamp into the sprite buffer. Stamps exits independent of the sprites
; Render a sprite stamp into the sprite buffer. Stamps exist independent of the sprites
; and sprite reference a specific stamp. This is necessary because it's common for a
; spite to change its graphic as its animating, but it is too costly to have to set up
; sprite to change its graphic as its animating, but it is too costly to have to set up
; the stamp every time. So this allows users to create stamps in advance and then
; assign them to the sprites as needed.
;

View File

@ -16,49 +16,6 @@ ColLeft equ tmp9
SpriteBit equ tmp10 ; set the bit of the value that if the current sprite index
VBuffOrigin equ tmp11
; Helper function to take a local pixel coordinate [0, ScreenWidth-1],[0, ScreenHeight-1] and return the
; row and column in the tile store that is corresponds to. This takes into consideration the StartX and
; StartY offsets.
;
; This is more specialized than the code in the _MarkDirtySprite routine below since it does not deal with
; negative or off-screen values.
_OriginToTileStore
lda StartYMod208
lsr
lsr
and #$FFFE ; Store the pre-multiplied by 2 for indexing
tay
lda StartXMod164
lsr
and #$FFFE ; Same pre-multiply by 2 for later
tax
rts
; X = local x-coordinate (0, playfield width)
; Y = local y-coordinate (0, playfield height)
_LocalToTileStore
clc
tya
adc StartYMod208 ; Adjust for the scroll offset
cmp #208 ; check if we went too far positive
bcc *+5
sbc #208
lsr
lsr
and #$FFFE ; Store the pre-multiplied by 2 for indexing
tay
clc
txa
adc StartXMod164
cmp #164
bcc *+5
sbc #164
lsr
and #$FFFE ; Same pre-multiply by 2 for later
tax
rts
; Marks a sprite as dirty. The work here is mapping from local screen coordinates to the
; tile store indices. The first step is to adjust the sprite coordinates based on the current
; code field offsets and then cache variations of this value needed in the rest of the subroutine

View File

@ -3,10 +3,19 @@
; Ref: Toolbox Reference, Volume 2, Appendix A
; Ref: IIgs Tech Note #73
; use Load.Macs.s
use Mem.Macs.s
use Misc.Macs.s
use Util.Macs
use Locator.Macs
use Core.MACS.s
use Defs.s
ToStrip equ $E10184
_CallTable
dw {_CTEnd-_CallTable}/4,0
adrl {_CTEnd-_CallTable}/4
adrl _TSBootInit-1
adrl _TSStartUp-1
adrl _TSShutDown-1
@ -25,40 +34,58 @@ _TSBootInit
clc
rtl
; Call the regular GTE startup function after setting the Work Area Point (WAP). The caller much provide
; one page of Bank 0 memory for the tool set's private use
; Call the regular GTE startup function after setting the Work Area Pointer (WAP). The caller must provide
; one page of Bank 0 memory for the tool set's private use and a userId to use for allocating memory
;
; X =
; X = tool set number in low byte and function umber in high byte
;
; StartUp(dPageAddr, userId)
_TSStartUp
:zpToUse equ 7
pea #$8000
rtll = 1
rtl2 = rtl1+3
userId = 7
zpToUse = userId+2
txa
and #$00FF
pha
and #$00FF ; Get just the tool number
tax
pea $0000
lda :zpToUse+6,s
pha
lda userId,s ; Get the userId for memory allocations
tay
lda zpToUse,s ; Get the direct page address
phd ; Save the current direct page
tcd ; Set to our working direct page space
tya ; A = memory manager user Id, X = tool number
jsr _CoreStartUp ; Initialize the library
; SetWAP(userOrSystem, tsNum, waptPtr)
pea #$8000 ; $8000 = user tool set
pei ToolNum ; Push the tool number from the direct page
pea $0000 ; High word of WAP is zero (bank 0)
phd ; Low word of WAP is the direct page
_SetWAP
jsr _CoreStartUp
pld ; Restore the caller's direct page
lda #0
clc
rtl
_TSShutDown
cmp #0 ; Acc is low word of the WAP (direct page)
beq :inactive
phd
pha
pld ; Set the direct page for the toolset
tcd ; Set the direct page for the toolset
phx ; Preserve the X register
jsr _CoreShutDown ; Shut down GTE
pla
jsr _CoreShutDown ; Shut down the library
pea $8000
and #$00FF
pha
pei ToolNum
pea $0000 ; Set WAP to null
pea $0000
_SetWAP
@ -71,7 +98,7 @@ _TSShutDown
rtl
_TSVersion
lda #$0100 ; Version 1
lda #$0100 ; Version 1
sta 7,s
lda #0
@ -101,31 +128,45 @@ _TSReserved
sec
rtl
; SetScreenMode(width, height)
_TSSetScreenMode
phd ; Preserve the direct page
pha
pld
:height equ 9
:width equ 11
lda 9,s
phd ; Preserve the direct page
tcd ; Set the tool set direct pafe from WAP
lda :height,s
tay
lda 9,s
lda :width,s
tax
jsr _SetScreenMode
pld
; jsr _SetScreenMode ; Not implemented yet
pld ; Restore direct page
ldx #0 ; No error
ldy #4 ; Remove the 4 input bytes
jml ToStrip
_TSReadControl
:output equ 9
phd ; Preserve the direct page
pha
pld
tcd
jsr _ReadControl
sta 9,s
sta :output,s
pld
ldx #0 ; No error
ldy #0 ; Remove zero input bytes
jml ToStrip
; Insert the core GTE functions
put CoreImpl.s
put Memory.s
put Timer.s
; put Graphics.s
; put blitter/Template.s
put blitter/Tables.s

2
src/_FileInformation.txt Normal file
View File

@ -0,0 +1,2 @@
Tool=Type(00),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)
GTETool=Type(BA),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)

View File

@ -19,25 +19,26 @@
;
; 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.
PER_TILE_SIZE equ 3
]step equ 0
dw CODE_TOP ; There is a spot where we load Col2CodeOffet-2,x
Col2CodeOffset lup 82
dw CODE_TOP+{{81-]step}*PER_TILE_SIZE}
]step equ ]step+1
--^
dw CODE_TOP+{81*PER_TILE_SIZE}
;PER_TILE_SIZE equ 3
;]step equ 0
; dw CODE_TOP ; There is a spot where we load Col2CodeOffet-2,x
;Col2CodeOffset lup 82
; dw CODE_TOP+{{81-]step}*PER_TILE_SIZE}
;]step equ ]step+1
; --^
; dw CODE_TOP+{81*PER_TILE_SIZE}
; A parallel table to Col2CodeOffset that holds the offset to the exception handler address for each column
SNIPPET_SIZE equ 32
]step equ 0
dw SNIPPET_BASE
JTableOffset lup 82
dw SNIPPET_BASE+{{81-]step}*SNIPPET_SIZE}
]step equ ]step+1
--^
dw SNIPPET_BASE+{81*SNIPPET_SIZE}
;SNIPPET_SIZE equ 32
;]step equ 0
; dw SNIPPET_BASE
;JTableOffset lup 82
; dw SNIPPET_BASE+{{81-]step}*SNIPPET_SIZE}
;]step equ ]step+1
; --^
; dw SNIPPET_BASE+{81*SNIPPET_SIZE}
; Table of BRA instructions that are used to exit the code field. Separate tables for
; even and odd aligned cases.

View File

@ -155,7 +155,6 @@ SetScreenRect sty ScreenHeight ; Save the screen height and
cpx #TILE_STORE_SIZE-2
bcc :tsloop
; Return
rts
; Generalized routine that calculates the on-screen address of the tiles and takes the

View File

@ -74,7 +74,7 @@ _GetBaseTileAddr
asl ; x64
asl ; x128
rts
; On entry
;
; B is set to the correct BG1 data bank
@ -102,9 +102,14 @@ _RenderTileBG1
; Given an address to a Tile Store record, dispatch to the appropriate tile renderer. The Tile
; Store record contains all of the low-level information that's needed to call the renderer.
;
; There are two execution paths that are handled here. First, if there is no sprite, then
; the tile data is read directly and written into the code field in a single pass. If there
; are sprites that overlap the tile, then the sprite data is combined with the tile data
; and written to a temporary direct page buffer. If
;
; This routine sets the direct page register to the second page since we use that space to
; build and cache tile and sprite data, when necessary
; Y = address of tile
_RenderTile2
lda TileStore+TS_SPRITE_FLAG,x ; This is a bitfield of all the sprites that intersect this tile, only care if non-zero or not
bne do_dirty_sprite
@ -159,10 +164,19 @@ do_dirty_sprite
pei TileStoreBankAndTileDataBank ; Special value that has the TileStore bank in LSB and TileData bank in MSB
plb
; Cache a couple of values into the direct page, but preserve the Accumulator
; Cache a couple of values into the direct page that are used across all copy routines
ldy TileStore+TS_TILE_ADDR,x ; load the address of this tile's data (pre-calculated)
sty tileAddr
lda TileStore+TS_TILE_ADDR,y ; load the address of this tile's data (pre-calculated)
sta tileAddr
ldx TileStore+TS_VBUFF_ADDR_COUNT,y
jmp (dirty_sprite_dispatch,x)
dirty_sprite_dispatch
da CopyNoSprites
da CopyOneSprite
da CopyTwoSprites
da CopyThreeSprites
da CopyFourSprites ; MAX, don't bother with more than 4 sprites per tile
; This is very similar to the code in the dirty tile renderer, but we can't reuse
; because that code draws directly to the graphics screen, and this code draws
@ -243,7 +257,15 @@ do_dirty_sprite
; We set up direct page pointers to the mask bank and use the bank register for the
; data.
CopyFourSpritesAbove
CopyFourSprites
lda TileStore+TS_VBUFF_ADDR_0,y
sta spriteIdx
lda TileStore+TS_VBUFF_ADDR_1,y
sta spriteIdx+4
lda TileStore+TS_VBUFF_ADDR_2,y
sta spriteIdx+8
lda TileStore+TS_VBUFF_ADDR_3,y
sta spriteIdx+12
; Copy three sprites into a temporary direct page buffer
LDA_IL equ $A7 ; lda [dp]
@ -252,6 +274,13 @@ AND_IL equ $27 ; and [dp]
AND_ILY equ $37 ; and [dp],y
CopyThreeSprites
lda TileStore+TS_VBUFF_ADDR_0,y
sta spriteIdx
lda TileStore+TS_VBUFF_ADDR_1,y
sta spriteIdx+4
lda TileStore+TS_VBUFF_ADDR_2,y
sta spriteIdx+8
]line equ 0
lup 8
ldy #]line*SPRITE_PLANE_SPAN
@ -285,6 +314,11 @@ CopyThreeSprites
; Copy two sprites into a temporary direct page buffer
CopyTwoSprites
lda TileStore+TS_VBUFF_ADDR_0,y
sta spriteIdx
lda TileStore+TS_VBUFF_ADDR_1,y
sta spriteIdx+4
]line equ 0
lup 8
ldy #]line*SPRITE_PLANE_SPAN
@ -311,9 +345,56 @@ CopyTwoSprites
; jmp FinishTile
; Copy a single piece of sprite data into a temporary direct page . X = spriteIdx
;
; X register is the offset of the underlying tile data
; Y register is the line offset into the sprite data and mask buffers
; There is a pointer for each sprite on the direct page that can be used
; to access both the data and mask components of a sprite
; The Data Bank reigster points to the sprite data
;
; ldal tiledata,x
; and [spriteIdx],y
; ora (spriteIdx),y
; sta tmp_sprite_data
;
; For multiple sprites, we can chain together the and/ora instructions to stack the sprites
;
; ldal tiledata,x
; and [spriteIdx],y
; ora (spriteIdx),y
; and [spriteIdx+4],y
; ora (spriteIdx+4),y
; and [spriteIdx+8],y
; ora (spriteIdx+8),y
; sta tmp_sprite_data
;
; When the sprites need to be drawn on top of the background, then change the order of operations
;
; lda (spriteIdx),y
; and [spriteIdx+4],y
; ora (spriteIdx+4),y
; and [spriteIdx+8],y
; ora (spriteIdx+8),y
; sta tmp_sprite_data
; andl tiledata+32,x
; oral tiledata,x
;
CopyOneSprite
clc
lda TileStore+TS_VBUFF_ADDR_0,y
sta spriteIdx
adc #2
sta spriteIdx+4
]line equ 0
lup 8
ldal tiledata,x
and [spriteIdx]
ora (spriteIdx)
sta tmp_sprite_data+{]line*4}
ldal spritedata+{]line*SPRITE_PLANE_SPAN},x
sta tmp_sprite_data+{]line*4}
ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x

3
src/static/SprData.s Normal file
View File

@ -0,0 +1,3 @@
; sprite stamp pixel data
spritedata ENT
ds 65536

3
src/static/SprMask.s Normal file
View File

@ -0,0 +1,3 @@
; sprite stamp masks
spritemask ENT
ds 65536

3
src/static/TileData.s Normal file
View File

@ -0,0 +1,3 @@
; Bank of memory that holds the 8x8 tile data
tiledata ENT
ds 65536

3
src/static/TileStore.s Normal file
View File

@ -0,0 +1,3 @@
; Bank of memory that holds the core sprite and tile store data structures
TileStore ENT
ds 65536