295 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			295 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
; async variables somewhere in RAM
 | 
						|
PUSHS "Async Variables", WRAM0[ASYNC_VARS_START]
 | 
						|
vAsyncAF: dw
 | 
						|
vAsyncHL: dw
 | 
						|
vAsyncDE: dw
 | 
						|
vAsyncBC: dw
 | 
						|
vAsyncPC: dw
 | 
						|
vAsyncMainSP: dw
 | 
						|
vAsyncThreadSP: dw
 | 
						|
vAsyncIsBusy: db
 | 
						|
POPS
 | 
						|
; canonical ordering to push should be: AF, BC, DE, HL, 
 | 
						|
 | 
						|
def ASYNC_STACK_TOP equ $ffc0
 | 
						|
; stack top is ffc0
 | 
						|
; first value on the stack is the early return pointer, at ffbe = ffc0-2
 | 
						|
; second value is the destination of the async call, at ffbc = ffbe-2 = ffc0-2-2
 | 
						|
def ASYNC_THREAD_CALL equ ASYNC_STACK_TOP - 2 - 2 
 | 
						|
 | 
						|
; timing for async execution, in scanlines
 | 
						|
def SAFE_ASYNC_START EQU 145
 | 
						|
def SAFE_ASYNC_END EQU 0
 | 
						|
 | 
						|
Async_Kill:
 | 
						|
  ld hl, ASYNC_STACK_TOP ; - 2 ; commenting this out so we see if this ever produces an issues
 | 
						|
  ld a, l
 | 
						|
  ld [vAsyncThreadSP], a 
 | 
						|
  ld a, h
 | 
						|
  ld [vAsyncThreadSP+1], a 
 | 
						|
  
 | 
						|
  ld a, 0
 | 
						|
  ld [vAsyncIsBusy], a
 | 
						|
 | 
						|
  ; unset next call
 | 
						|
  ld hl, rIE  
 | 
						|
  res 1, [hl] ; disable stat interrupt
 | 
						|
  ld hl, rIF 
 | 
						|
  res 1, [hl] ; clear the interrupt 
 | 
						|
  
 | 
						|
  ret
 | 
						|
Async_Spawn_HL:
 | 
						|
  di
 | 
						|
  ld a, l
 | 
						|
  ld [ASYNC_THREAD_CALL], a 
 | 
						|
  ld a, h
 | 
						|
  ld [ASYNC_THREAD_CALL+1], a 
 | 
						|
Async_Spawn: 
 | 
						|
  di
 | 
						|
  nop
 | 
						|
  
 | 
						|
  ; save all the registers
 | 
						|
  push af
 | 
						|
  ld a, l 
 | 
						|
  ld [vAsyncHL], a
 | 
						|
  ld a, h
 | 
						|
  ld [vAsyncHL+1], a
 | 
						|
  ld a, e
 | 
						|
  ld [vAsyncDE], a
 | 
						|
  ld a, d
 | 
						|
  ld [vAsyncDE+1], a
 | 
						|
  ld a, c 
 | 
						|
  ld [vAsyncBC], a
 | 
						|
  ld a, b 
 | 
						|
  ld [vAsyncBC+1], a
 | 
						|
  pop hl
 | 
						|
  ld a, l 
 | 
						|
  ld [vAsyncAF], a 
 | 
						|
  ld a, h 
 | 
						|
  ld [vAsyncAF+1], a 
 | 
						|
  
 | 
						|
  ; save main sp
 | 
						|
  ld hl, sp+0
 | 
						|
  ld a, l 
 | 
						|
  ld [vAsyncMainSP], a 
 | 
						|
  ld a, h 
 | 
						|
  ld [vAsyncMainSP+1], a 
 | 
						|
  
 | 
						|
  ; switch to thread sp
 | 
						|
  ld a, LOW(ASYNC_STACK_TOP)
 | 
						|
  ld l, a 
 | 
						|
  ld a, HIGH(ASYNC_STACK_TOP)
 | 
						|
  ld h, a 
 | 
						|
  ld sp, hl 
 | 
						|
  
 | 
						|
  
 | 
						|
  ; are these next two steps necessary? those values should always be prepopulated
 | 
						|
  
 | 
						|
  ; push early return onto thread stack
 | 
						|
  ld l, LOW(Async_EarlyReturn)
 | 
						|
  ld h, HIGH(Async_EarlyReturn)
 | 
						|
  push hl
 | 
						|
  
 | 
						|
  ; requestedd pc is expected to be set at ASYNC_THREAD_CALL by the calling func
 | 
						|
  ld hl, sp-2
 | 
						|
  ld sp, hl
 | 
						|
  
 | 
						|
  ; push registers
 | 
						|
  ; canonical ordering to push should be: AF, BC, DE, HL, 
 | 
						|
  ld a, [vAsyncAF]
 | 
						|
  ld l, a 
 | 
						|
  ld a, [vAsyncAF+1]
 | 
						|
  ld h, a 
 | 
						|
  push hl 
 | 
						|
  
 | 
						|
  ld a, [vAsyncBC]
 | 
						|
  ld l, a 
 | 
						|
  ld a, [vAsyncBC+1]
 | 
						|
  ld h, a 
 | 
						|
  push hl 
 | 
						|
  
 | 
						|
  ld a, [vAsyncDE]
 | 
						|
  ld l, a 
 | 
						|
  ld a, [vAsyncDE+1]
 | 
						|
  ld h, a 
 | 
						|
  push hl 
 | 
						|
  
 | 
						|
  ld a, [vAsyncHL]
 | 
						|
  ld l, a 
 | 
						|
  ld a, [vAsyncHL+1]
 | 
						|
  ld h, a 
 | 
						|
  push hl 
 | 
						|
  
 | 
						|
  ; save current sp to vAsyncThreadSP
 | 
						|
  ld hl, sp+0
 | 
						|
  ld a, l
 | 
						|
  ld [vAsyncThreadSP], a 
 | 
						|
  ld a, h 
 | 
						|
  ld [vAsyncThreadSP+1], a 
 | 
						|
  
 | 
						|
  ; now the stack looks like: early return, ASYNC_THREAD_CALL, AF, BC, DE, HL
 | 
						|
  ; switch back to main thread.
 | 
						|
  
 | 
						|
  ; register enterthread
 | 
						|
  ld a, LOW(Async_EnterThread)
 | 
						|
  ld [INTERRUPT_LCD], a
 | 
						|
  ld a, HIGH(Async_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
 | 
						|
  
 | 
						|
  ld a, 1
 | 
						|
  ld [vAsyncIsBusy], a 
 | 
						|
  
 | 
						|
  ; restore main sp
 | 
						|
  ld a, [vAsyncMainSP]
 | 
						|
  ld l, a 
 | 
						|
  ld a, [vAsyncMainSP+1]
 | 
						|
  ld h, a 
 | 
						|
  ld sp, hl
 | 
						|
  
 | 
						|
  ; restore registers from memory
 | 
						|
  ld a, [vAsyncAF]
 | 
						|
  ld l, a 
 | 
						|
  ld a, [vAsyncAF+1]
 | 
						|
  ld h, a 
 | 
						|
  push hl ; this is so we can pop af when we're done
 | 
						|
  
 | 
						|
  ld a, [vAsyncHL]
 | 
						|
  ld l, a 
 | 
						|
  ld a, [vAsyncHL+1]
 | 
						|
  ld h, a 
 | 
						|
  
 | 
						|
  ld a, [vAsyncDE]
 | 
						|
  ld e, a 
 | 
						|
  ld a, [vAsyncDE+1]
 | 
						|
  ld d, a 
 | 
						|
  
 | 
						|
  ld a, [vAsyncBC]
 | 
						|
  ld c, a 
 | 
						|
  ld a, [vAsyncBC+1]
 | 
						|
  ld b, a 
 | 
						|
  
 | 
						|
  pop af
 | 
						|
 
 | 
						|
  reti
 | 
						|
  
 | 
						|
Async_EnterThread: 
 | 
						|
  ;stack looks like: 
 | 
						|
  ;c113 (SMC int @ LYC 90 pc), 004b (hw int @ LYC 90 pc), outer context pc
 | 
						|
 | 
						|
  ; 16 cycles
 | 
						|
  push af
 | 
						|
  push bc
 | 
						|
  push de
 | 
						|
  push hl 
 | 
						|
  
 | 
						|
  ;af, de, bc, hl, c113 (SMC interrupt pc), 004b (hardwired interrput pc), outer context pc
 | 
						|
  
 | 
						|
  ; 18 cycles
 | 
						|
  ld a, LOW(Async_ExitThread) 
 | 
						|
  ld [INTERRUPT_LCD], a  
 | 
						|
  ld a, HIGH(Async_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 
 | 
						|
 | 
						|
  ; 5 cycles
 | 
						|
  ; save main thread stack pointer
 | 
						|
  ld [vAsyncMainSP], sp 
 | 
						|
 | 
						|
  ; 12 cycless
 | 
						|
  ; load side thread stack pointer
 | 
						|
  ld a, [vAsyncThreadSP] ; 4
 | 
						|
  ld l, a ; 1
 | 
						|
  ld a, [vAsyncThreadSP+1] ; 4 
 | 
						|
  ld h, a ; 1 
 | 
						|
  ld sp, hl ; 2
 | 
						|
  
 | 
						|
  ; 12 cycles
 | 
						|
  ; pop registers
 | 
						|
  ; canonical ordering to push should be: AF, BC, DE, HL, 
 | 
						|
  ; pop is HL, DE, BC, AF
 | 
						|
  pop hl 
 | 
						|
  pop de 
 | 
						|
  pop bc
 | 
						|
  pop af 
 | 
						|
 | 
						|
  ; 4 cycles
 | 
						|
  reti ; "return" to the vAsyncPC wee put on the stack previously. ; 4
 | 
						|
  ; 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
 | 
						|
 | 
						|
Async_ExitThread:
 | 
						|
  ; save interrupt registers (AF, BC, DE, HL)
 | 
						|
  push af
 | 
						|
  push bc
 | 
						|
  push de
 | 
						|
  push hl
 | 
						|
  
 | 
						|
  ld a, LOW(Async_EnterThread)
 | 
						|
  ld [INTERRUPT_LCD], a 
 | 
						|
  ld a, HIGH(Async_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
 | 
						|
 | 
						|
  ; save side thread stack pointer
 | 
						|
  ld [vAsyncThreadSP], sp
 | 
						|
  
 | 
						|
  ; load main thread stack pointer
 | 
						|
  ld a, [vAsyncMainSP]
 | 
						|
  ld l, a 
 | 
						|
  ld a, [vAsyncMainSP+1]
 | 
						|
  ld h, a 
 | 
						|
  ld sp, hl
 | 
						|
  
 | 
						|
  ; pop registers
 | 
						|
  ; canonical ordering to push should be: AF, BC, DE, HL, 
 | 
						|
  ; pop is HL, DE, BC, AF
 | 
						|
  pop hl
 | 
						|
  pop de
 | 
						|
  pop bc
 | 
						|
  pop af
 | 
						|
  
 | 
						|
  reti
 | 
						|
  
 | 
						|
Async_EarlyReturn:
 | 
						|
  di
 | 
						|
  ; don't care about current registers bc we're done executing.
 | 
						|
  
 | 
						|
  ; store side thread SP, so everyone knows that the side thread stack is empty
 | 
						|
  ld [vAsyncThreadSP], sp ; 5 cycles instead of 13
 | 
						|
  ld a, 0 
 | 
						|
  ld [vAsyncIsBusy], a 
 | 
						|
 | 
						|
  ; unset next call
 | 
						|
  ld hl, rIE  
 | 
						|
  res 1, [hl] ; disable stat interrupt
 | 
						|
  ld hl, rIF 
 | 
						|
  res 1, [hl] ; clear the interrupt 
 | 
						|
  
 | 
						|
  ; load main thread stack pointer
 | 
						|
  ld a, [vAsyncMainSP]
 | 
						|
  ld l, a 
 | 
						|
  ld a, [vAsyncMainSP+1]
 | 
						|
  ld h, a 
 | 
						|
  ld sp, hl
 | 
						|
  
 | 
						|
   ; restore the pre-interrupt registers, return and enable interrupts
 | 
						|
  pop af 
 | 
						|
  pop de
 | 
						|
  pop bc
 | 
						|
  pop hl
 | 
						|
  
 | 
						|
  reti
 |