gb-tarot/Async.inc

261 lines
6.6 KiB
PHP
Raw Permalink Normal View History

2025-01-13 15:44:01 -05:00
def vAsyncAF equ $ff80
def vAsyncHL EQU $ff82
def vAsyncDE EQU $ff84
def vAsyncBC equ $ff86
def vAsyncPC equ $ff88
def vAsyncNext equ $ff8a
def vAsyncAfter equ $ff8c
2025-01-14 15:06:27 -05:00
def vAsyncProgress equ $ff8e
2025-01-13 15:44:01 -05:00
2025-01-14 15:06:27 -05:00
def SAFE_ASYNC_START EQU 146
def SAFE_ASYNC_END EQU 153
2025-01-13 15:44:01 -05:00
DoInAsyncVBlank:
di
push af
ld a, l
ldh [vAsyncHL], a
ld a, h
ldh [vAsyncHL+1], a
ld a, e
ldh [vAsyncDE], a
ld a, d
ldh [vAsyncDE+1], a
ld a, c
ldh [vAsyncBC], a
ld a, b
ldh [vAsyncBC+1], a
pop hl
ld a, l
ldh [vAsyncAF], a
ld a, h
ldh [vAsyncAF+1], a
; set pu the next call!
ldh a, [vAsyncNext]
ldh [vAsyncPC], a
ldh a, [vAsyncNext+1]
ldh [vAsyncPC+1], a ; put next into pc
ldh a, [vAsyncAfter]
ldh [vAsyncNext], a
ldh a, [vAsyncAfter+1]
ldh [vAsyncNext+1], a ; puut after into next
ld a, 0
ldh [vAsyncAfter], a ; unless this gets overwritten later, end execution after that
ldh [vAsyncAfter+1], a
ld a, LOW(DoInAsyncVBlank_EnterThread)
ld [INTERRUPT_LCD], a
ld a, HIGH(DoInAsyncVBlank_EnterThread)
ld [INTERRUPT_LCD + 1], a; set interrupt handler to "ENTER THREAD"
ld a, SAFE_ASYNC_START ; CHANGE ME TO ADJUST SAFE TRANSFER TIMING
ld [rLYC], a ; set LYC
ld hl, rIE
set 1, [hl] ; enable vblank
ld hl, rSTAT
set 6, [hl] ; set the stat interrupt to LYC mode
ld hl, rIF
res 1, [hl] ; clear the interrupt so we don't immediately fire it
ei
ret
DoInAsyncVBlank_EnterThread:
;stack looks like:
;c113 (SMC int @ LYC 90 pc), 004b (hw int @ LYC 90 pc), outer context pc
push hl
push bc
push de
push af
;af, de, bc, hl, c113 (SMC interrupt pc), 004b (hardwired interrput pc), outer context pc
; check if there's anything queued up for next execution
ldh a, [vAsyncPC]
ld l, a
ldh a, [vAsyncPC+1]
ld h, a
or a, l
jp z, DoInAsyncVBlank_EndThread ; if nothing is queued up, jump to cleanup
ld a, LOW(DoInAsyncVBlank_ExitThread)
ld [INTERRUPT_LCD], a
ld a, HIGH(DoInAsyncVBlank_ExitThread)
ld [INTERRUPT_LCD+1], a
ld a, SAFE_ASYNC_END ; CHANGE ME TO ADJUST SAFE TRANSFER TIMING
ld [rLYC], a ; set lcd interrupt handler to EXIT SAFE MODE on line 153
; what if our async thread calls return?
; we need to have a PC to return to. if that happens, we will want to
; reti.
ld hl, DoInAsyncVBlank_EarlyReturn
push hl ; address for if the thread retrns
ldh a, [vAsyncPC]
ld l, a
ldh a, [vAsyncPC+1]
ld h, a
push hl ; put vAsyncPC on the stack! for jumping to it!
; stack looks like: vasyncpc, early return, af, de, bc, hll, SMC interrput pc, hardwired interrupt pc, outer context pc
ldh a, [vAsyncAF]
ld l, a
ldh a, [vAsyncAF+1]
ld h, a
push hl
ldh a, [vAsyncHL]
ld l, a
ldh a, [vAsyncHL+1]
ld h, a
ldh a, [vAsyncDE]
ld e, a
ldh a, [vAsyncDE+1]
ld d, a
ldh a, [vAsyncBC]
ld c, a
ldh a, [vAsyncBC+1]
ld b, a
pop af; putting vAsyncAF into af requires this hoop-jumping
ei
ret ; "return" to the vAsyncPC wee put on the stack previously.
; this is more or less a jump, not a return.
; is that the source of our problems?
; after this instruction executes, stack looks like:
; early return, af, de, bc, hl, PC (smc int), PC (hardwired int), PC (outer context)
; and we'll be in the async thread, executing
DoInAsyncVBlank_EarlyReturn:
; stack:
; af, de, bc, hl, PC (smc int @ LYC90), PC (hw int @ LYC 90), PC (outer context)
PRINTln "early return handle is ", DoInAsyncVBlank_EarlyReturn
; save state of registers
di
push af
ld a, l
ldh [vAsyncHL], a
ld a, h
ldh [vAsyncHL+1], a
ld a, e
ldh [vAsyncDE], a
ld a, d
ldh [vAsyncDE+1], a
ld a, c
ldh [vAsyncBC], a
ld a, b
ldh [vAsyncBC+1], a
pop hl
ld a, l
ldh [vAsyncAF], a
ld a, h
ldh [vAsyncAF+1], a
; set pu the next call!
ldh a, [vAsyncNext]
ldh [vAsyncPC], a
ldh a, [vAsyncNext+1]
ldh [vAsyncPC+1], a ; put next into pc
ldh a, [vAsyncAfter]
ldh [vAsyncNext], a
ldh a, [vAsyncAfter+1]
ldh [vAsyncNext+1], a ; puut after into next
ld a, 0
ldh [vAsyncAfter], a ; unless this gets overwritten later, end execution after that
ldh [vAsyncAfter+1], a
ld a, LOW(DoInAsyncVBlank_EnterThread) ; set up next call
ld [INTERRUPT_LCD], a
ld a, HIGH(DoInAsyncVBlank_EnterThread)
ld [INTERRUPT_LCD+1], a
ld a, SAFE_ASYNC_START
ld [rLYC], a
; restore the pre-interrupt registers, return and enable interrupts
pop af
pop de
pop bc
pop hl
reti
DoInAsyncVBlank_EndThread:
; end execution of the thread by disabling the interrupt
; and restore the state for the outer thread
ld hl, rIE
res 1, [hl] ; disable STAT/vblank interrupt
pop af
pop de
pop bc
pop hl
reti
DoInAsyncVBlank_ExitThread:
; at this point, it's an interrpt being called from inside the interrupt
; our stack probably looks like this:
; PC (smc int), PC (hardwired int), PC (inside thread), early return, af, de, bc, hl, PC (smc int), PC (hardwired int), PC (outer context)
; first, save the interrpt thread registers.
push af
ld a, l
ldh [vAsyncHL], a
ld a, h
ldh [vAsyncHL+1], a
ld a, e
ldh [vAsyncDE], a
ld a, d
ldh [vAsyncDE+1], a
ld a, c
ldh [vAsyncBC], a
ld a, b
ldh [vAsyncBC+1], a
pop hl
ld a, l
ldh [vAsyncAF], a
ld a, h
ldh [vAsyncAF+1], a
;god i'm such a genius
; this was called as an interrupt inside the interrupt
;so our stack looks like this:
; PC (smc int), PC (hardwired int), PC (inside thread), early return, af, de, bc, hl, PC (smc int), PC (hardwired int), PC (outer context)
; we've got the two PCs from the inner interrupt call stack.
; get rid of them! we're running without handrails! i know what i'm doing! I hope!
pop hl
pop hl
; now, save the interrupt thread's pc., which is on the stack from this
; interrupt being called.
pop hl
ld a, l
ldh [vAsyncPC], a
ld a, h
ldh [vAsyncPC+1], a
; now we are done with this entire execution of thread. now we need to set up
; the next execution of this thread.
ld a, LOW(DoInAsyncVBlank_EnterThread)
ld [INTERRUPT_LCD], a
ld a, HIGH(DoInAsyncVBlank_EnterThread)
ld [INTERRUPT_LCD+1], a
ld a, SAFE_ASYNC_START ; CHANGE ME TO ADJUST SAFE TRANSFER TIMING
ld [rLYC], a ; set lcd interrupt handler to EXIT SAFE MODE on line 153
;at present the stack looks like:
; early return, af, de, bc, hl, pc (smc int @ LYC 90), pc (hardwired int at LYC 90), PC (outer context)
; pop the early return and discard it.
pop hl
; and finally, we can restore the state of the registers to what they were
; before this whole handler got called.
pop af
pop de
pop bc
pop hl
reti ; this is a proper return