261 lines
6.6 KiB
PHP
261 lines
6.6 KiB
PHP
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
|
|
def vAsyncProgress equ $ff8e
|
|
|
|
def SAFE_ASYNC_START EQU 146
|
|
def SAFE_ASYNC_END EQU 151
|
|
|
|
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
|
|
|