mirror of
https://github.com/lscharen/iigs-game-engine.git
synced 2024-11-22 11:34:14 +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