mirror of
https://github.com/lscharen/iigs-game-engine.git
synced 2024-11-23 03:33:11 +00:00
Early peek at the Timers/Scripting engine
This commit is contained in:
parent
31ab86b7fc
commit
48bb361730
148
src/Script.s
Normal file
148
src/Script.s
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
; A simple scripting engine
|
||||||
|
;
|
||||||
|
; The scripting engine is driven by the GTE Timers, which are
|
||||||
|
; driven by the VBL Tick count.
|
||||||
|
;
|
||||||
|
; The scripting engine is composed of a sequence of simple commands, defined as follows
|
||||||
|
;
|
||||||
|
; COMMAND ARG1 ARG2 ARG3
|
||||||
|
;
|
||||||
|
; The COMMAND word has its bits defined as:
|
||||||
|
;
|
||||||
|
; bit 15 = 1 if the end of a sequece
|
||||||
|
; bit 14 = 0 proceed to next action, 1 jump
|
||||||
|
; bit 13 = 0 (Reserved)
|
||||||
|
; bit 12 = 0 (Reserved)
|
||||||
|
; bit 11 - 8 = signed jump displacement
|
||||||
|
; bit 8 - 0 = command number
|
||||||
|
;
|
||||||
|
; The defined commands are
|
||||||
|
;
|
||||||
|
; COMMANDS ARG1 ARG2 ARG3
|
||||||
|
; -----------------------------------------------------
|
||||||
|
; $0002 SET_PALETTE_ENTRY ADDR COLOR ---- : Set the palette entry at ARG1 to the color in ARG2
|
||||||
|
; $0004 SWAP_PALETTE_ENTRY ADDR1 ADDR2 ---- : Swap the palette entries in ADDR1 <-> ADDR2
|
||||||
|
; $0010 CALLBACK LONG_ADDR PARAM : Call a user-defined function (JSL) with a parameter value in accumulator
|
||||||
|
|
||||||
|
; Start a new script
|
||||||
|
;
|
||||||
|
; A = low word of script command array
|
||||||
|
; X = high word of script command array
|
||||||
|
; Y = number of ticks between each command step
|
||||||
|
StartScript ENT
|
||||||
|
phb
|
||||||
|
phk
|
||||||
|
plb
|
||||||
|
|
||||||
|
phx ; Save the script array address
|
||||||
|
pha
|
||||||
|
|
||||||
|
lda #_DoScriptStep ; Try to create a timer for this script
|
||||||
|
ldx #^_DoScriptStep
|
||||||
|
clc
|
||||||
|
jsl AddTimer
|
||||||
|
bcs :err ; No timer slots available :(
|
||||||
|
|
||||||
|
tax ; Initialize the UeerData with the command array and PC
|
||||||
|
pla
|
||||||
|
sta Timers+8,x
|
||||||
|
pla
|
||||||
|
sta Timers+10,x
|
||||||
|
stz Timers+12,x ; Index of the commands
|
||||||
|
|
||||||
|
plb
|
||||||
|
rtl
|
||||||
|
|
||||||
|
:err
|
||||||
|
pla ; Pop the values and return with the carry flag set
|
||||||
|
pla
|
||||||
|
plb
|
||||||
|
rtl
|
||||||
|
|
||||||
|
_DoScriptStep
|
||||||
|
lda Timers+8,x
|
||||||
|
lda Timers+10,x
|
||||||
|
ldy Timers+12,y
|
||||||
|
lda [CmdList],y
|
||||||
|
pha ; save the full command word
|
||||||
|
|
||||||
|
and #$001E
|
||||||
|
tax
|
||||||
|
jsr (:commands,x)
|
||||||
|
|
||||||
|
pla ; restore the command word
|
||||||
|
bit #$4000 ; If the branch bit is set, change the command pointer
|
||||||
|
beq :no_jump
|
||||||
|
|
||||||
|
:no_jump
|
||||||
|
bit #$8000 ; If the terminate bit is set, remove this handler
|
||||||
|
beq :no_term
|
||||||
|
txa
|
||||||
|
jsl RemoveTimer
|
||||||
|
:no_term
|
||||||
|
rtl
|
||||||
|
|
||||||
|
:commands dw _Null,_SetPalEntry,_SwapPalEntry,_Null,_Null,_Null,_Null,_Null
|
||||||
|
dw _UserCallback,_Null,_Null,_Null,_Null,_Null,_Null,_Null
|
||||||
|
|
||||||
|
ARG1 equ 2
|
||||||
|
ARG2 equ 4
|
||||||
|
ARG3 equ 6
|
||||||
|
|
||||||
|
; Implementation of the built-in commands
|
||||||
|
_Null rts
|
||||||
|
|
||||||
|
_SetPalEntry
|
||||||
|
txy
|
||||||
|
ldx: ARG1,y
|
||||||
|
lda: ARG2,y
|
||||||
|
stal SHR_PALETTES,x
|
||||||
|
rts
|
||||||
|
|
||||||
|
_SwapPalEntry txy
|
||||||
|
|
||||||
|
ldx ARG1,y ; Load palette values
|
||||||
|
ldal SHR_PALETTES,x
|
||||||
|
pha
|
||||||
|
ldx ARG2,y
|
||||||
|
ldal SHR_PALETTES,x
|
||||||
|
|
||||||
|
ldx ARG1,y ; and swap
|
||||||
|
stal SHR_PALETTES,x
|
||||||
|
|
||||||
|
ldx ARG2,y
|
||||||
|
pla
|
||||||
|
stal SHR_PALETTES,x
|
||||||
|
rts
|
||||||
|
|
||||||
|
_UserCallback lda ARG1,x
|
||||||
|
sta :dispatch+1
|
||||||
|
lda ARG1+1,x
|
||||||
|
sta :dispatch+2
|
||||||
|
lda ARG3,x
|
||||||
|
:dispatch jsl $000000
|
||||||
|
rts
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
237
src/Timer.s
Normal file
237
src/Timer.s
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
; Timer implementation
|
||||||
|
;
|
||||||
|
; The engire provides four timer slot that can be used by one-shot or
|
||||||
|
; recurring timers. Each timer is given an initial tick count, a
|
||||||
|
; reset tick count (0 = one-shot), and an action to perform.
|
||||||
|
;
|
||||||
|
; The timers handle overflow, so if a recurring timer has a tick count of 3
|
||||||
|
; and 7 VBL ticks have passed, then the timer will be fired twice and
|
||||||
|
; a tick count of 2 will be set.
|
||||||
|
;
|
||||||
|
; As such, the timers are appropriate to drive physical and other game
|
||||||
|
; behaviors at a frame-independent rate.
|
||||||
|
;
|
||||||
|
; A collection of 4 timers that are triggered when their countdown
|
||||||
|
; goes below zero. Each timer takes up 16 bytes
|
||||||
|
;
|
||||||
|
; A timer can fire multiple times during a singular evaluation. For example, if the
|
||||||
|
; timer delay is set to 1 and 3 VBL ticks happen, then the timer delta is -2, will fire,
|
||||||
|
; have the delay added and get -1, fire again, increment to zero, first again and then
|
||||||
|
; finally reset to 1.
|
||||||
|
;
|
||||||
|
; +0 counter decremented by the number of ticks since last run
|
||||||
|
; +2 reset copied into counter when triggered. 0 turns off the timer.
|
||||||
|
; +4 addr long address of timer routine
|
||||||
|
; +8 user 8 bytes of user data space for timer state
|
||||||
|
MAX_TIMERS equ 4
|
||||||
|
TIMER_REC_SIZE equ 16
|
||||||
|
|
||||||
|
lastTick ds 2
|
||||||
|
Timers ds TIMER_REC_SIZE*MAX_TIMERS
|
||||||
|
|
||||||
|
_GetVBLTicks
|
||||||
|
PushLong #0
|
||||||
|
_GetTick
|
||||||
|
pla
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
|
||||||
|
; Initialize the timers
|
||||||
|
InitTimers
|
||||||
|
jsr _GetVBLTicks
|
||||||
|
sta lastTick
|
||||||
|
|
||||||
|
lda #0
|
||||||
|
ldx #{TIMER_REC_SIZE*MAX_TIMERS}-2
|
||||||
|
:loop sta Timers,x
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
bne :loop
|
||||||
|
rts
|
||||||
|
|
||||||
|
; Add a new timer
|
||||||
|
;
|
||||||
|
; Input
|
||||||
|
; A = low word of timer callback function
|
||||||
|
; X = high word of timer callback function
|
||||||
|
; Y = number of ticks for the timer to delay
|
||||||
|
; C = set to 1 if the timer is a one-shot timer
|
||||||
|
;
|
||||||
|
; Return
|
||||||
|
; C = 0 if success, 1 if no timer slots are available
|
||||||
|
; A = timer slot ID if C = 0
|
||||||
|
AddTimer ENT
|
||||||
|
phb
|
||||||
|
php ; Save the input parameters
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
phy
|
||||||
|
|
||||||
|
phk
|
||||||
|
plb
|
||||||
|
|
||||||
|
ldx #0
|
||||||
|
:loop lda Timers,x ; If the counter is zero, timer is free
|
||||||
|
beq :freeslot
|
||||||
|
|
||||||
|
txa ; Advance to the next timer record
|
||||||
|
clc
|
||||||
|
adc #TIMER_REC_SIZE
|
||||||
|
tax
|
||||||
|
|
||||||
|
cpx #{TIMER_REC_SIZE*MAX_TIMERS}
|
||||||
|
bcc :loop
|
||||||
|
bra :notimers
|
||||||
|
|
||||||
|
:freeslot pla
|
||||||
|
sta Timer+0,x ; set the counter and
|
||||||
|
stz Timer+2,x ; default to a zero reset value
|
||||||
|
pla
|
||||||
|
sta Timer+4,x ; set the callback address
|
||||||
|
pla
|
||||||
|
sta Timer+6,x
|
||||||
|
|
||||||
|
stz Timer+8,x ; Clear the user data space
|
||||||
|
stz Timer+10,x ; Clear the user data space
|
||||||
|
stz Timer+12,x ; Clear the user data space
|
||||||
|
stz Timer+14,x ; Clear the user data space
|
||||||
|
|
||||||
|
plp
|
||||||
|
bcc :oneshot
|
||||||
|
lda Timer+0,x ; if not a one-shot, put the counter
|
||||||
|
sta Timer+2,x ; value into the reset field
|
||||||
|
|
||||||
|
:oneshot plb
|
||||||
|
txa ; return the slot ID and a success status
|
||||||
|
clc
|
||||||
|
rtl
|
||||||
|
|
||||||
|
:notimers ply
|
||||||
|
pla
|
||||||
|
plx
|
||||||
|
plp
|
||||||
|
plb
|
||||||
|
|
||||||
|
sec ; Return an error status
|
||||||
|
lda #0
|
||||||
|
rtl
|
||||||
|
|
||||||
|
; Small function to remove a timer
|
||||||
|
;
|
||||||
|
; A = Timer ID
|
||||||
|
RemoveTimer ENT
|
||||||
|
phb
|
||||||
|
phk
|
||||||
|
plb
|
||||||
|
cmp #{TIMER_REC_SIZE*{MAX_TIMERS-1}}+1
|
||||||
|
bcs :exit
|
||||||
|
|
||||||
|
tax
|
||||||
|
stz Timer,x
|
||||||
|
stz Timer+2,x
|
||||||
|
stz Timer+4,x
|
||||||
|
stz Timer+6,x
|
||||||
|
|
||||||
|
:exit
|
||||||
|
plb
|
||||||
|
rtl
|
||||||
|
; Execute the timer functions
|
||||||
|
DoTimers ENT
|
||||||
|
phb
|
||||||
|
phk
|
||||||
|
plb
|
||||||
|
|
||||||
|
jsr _GetVBLTicks
|
||||||
|
cmp lastTick ; Throttle to 60 fps
|
||||||
|
beq :exit
|
||||||
|
tax ; Calculate the increment
|
||||||
|
sec
|
||||||
|
sbc lastTick
|
||||||
|
stx lastTick
|
||||||
|
jsr _DoTimers
|
||||||
|
|
||||||
|
:exit plb
|
||||||
|
rtl
|
||||||
|
|
||||||
|
; Countdown the timers
|
||||||
|
;
|
||||||
|
; A = number of elapsed ticks
|
||||||
|
_DoTimers
|
||||||
|
pha
|
||||||
|
ldx #0
|
||||||
|
:loop
|
||||||
|
lda Timers,x ; A zero means do not fire
|
||||||
|
beq :skip
|
||||||
|
|
||||||
|
sec
|
||||||
|
sbc 1,s ; subtract the number of ticks
|
||||||
|
sta Timers,x
|
||||||
|
|
||||||
|
:retry beq :fire ; getting <= zero triggers
|
||||||
|
bpl :skip
|
||||||
|
|
||||||
|
:fire pha ; Save the counter
|
||||||
|
phx ; Save our index
|
||||||
|
|
||||||
|
lda Timers+4,x ; execute the timer callback
|
||||||
|
sta :dispatch+1
|
||||||
|
lda Timers+5,x
|
||||||
|
sta :dispatch+2
|
||||||
|
:dispatch jsl $000000
|
||||||
|
|
||||||
|
plx
|
||||||
|
pla
|
||||||
|
|
||||||
|
clc
|
||||||
|
adc Timers+2,x ; Add the increment
|
||||||
|
sta Timers,x ; Store in the count
|
||||||
|
bra :retry ; See if we have >0 ticks to wait until the next trigger
|
||||||
|
|
||||||
|
:skip txa
|
||||||
|
clc
|
||||||
|
adc #8
|
||||||
|
tax
|
||||||
|
cpx #8*MAX_TIMERS
|
||||||
|
bcc :loop
|
||||||
|
|
||||||
|
pla
|
||||||
|
rts
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user