gb-tarot/CopyRangeSafe.inc

196 lines
4.9 KiB
PHP
Raw Normal View History

2025-01-07 11:34:53 -05:00
CopyRangeSafe:
; hl is source
; de is destination
; bc is length to copy
; copy an array of bytes to a destination in memory.
; this is completely different from CopyRangeUnsafe.
; this initiates an asynchronous, interrupt-driven copy of BC bytes of memory
; from HL to DE.
; it may return a memory address to look at for progress of the transfer,
; or else it's just going to do it with a hard-coded address.
; check the transfer status address for zero; when it's zero, the transfer is done!
; this works by using the STAT interrupt in LYC mode to interrupt its own execution at
; two scanlines inside the vblank interval to know when to start and stop.
; stash arguments in memory
di
ld a, l
ldh [vSafeCopySource], a
ld a, h
ldh [vSafeCopySource+1], a
ld a, e
ldh [vSafeCopyDest], a
ld a, d
ldh [vSafeCopyDest+1], a
ld a, c
ldh [vSafeCopyCount], a
ld a, b
ldh [vSafeCopyCount+1], a
; stash interrupt state in memory
ldh a, [$ff41]
ldh [vSafeCopySTAT], a ; stashes $FF41, the STAT register
ldh a, [$ff45]
ldh [vSafeCopyLYC], a ; stashes $FF45, the LYC register
ld a, [INTERRUPT_LCD]
ld [vSafeCopyInterruptFirst], a
ld a, [INTERRUPT_LCD + 1]
ld [vSafeCopyInterruptSecond], a ; stashes the current STAT interrupt handler
ld a, [$ffff]
and a, %0000_0010
ld [vSafeCopyInterruptEnable], a ; stashes whether LCD interrupt are enabled
ld hl, CopyRangeSafe_EnterSafeMode
ld a, l
ld [INTERRUPT_LCD], a
ld a, h
ld [INTERRUPT_LCD + 1], a; set interrupt handler to "ENTER SAFE MODE"
ld a, 148 ; CHANGE ME TO ADJUST SAFE TRANSFER TIMING
ld [$ff45], a
ld hl, $ffff
set 1, [hl]
ld a, %0100_0000
ld [$ff41], a
ld hl, vSafeCopyCount
ld a, 0
ld [$ff0f], a
nop
nop
nop
ei
nop
nop
nop
ret ; return address of bytes remaining to copy
CopyRangeSafe_EnterSafeMode:
push hl
push bc
push de
push af
ld hl, CopyRangeSafe_ExitSafeMode
ld a, l
ld [INTERRUPT_LCD], a
ld a, h
ld [INTERRUPT_LCD+1], a
ld a, 153 ; CHANGE ME TO ADJUST SAFE TRANSFER TIMING
ld [$ff45], a ; set lcd interrupt handler to EXIT SAFE MODE on line 153
ldh a, [vSafeCopySource]
ld l, a
ldh a, [vSafeCopySource+1]
ld h, a ; fetch the source
ldh a, [vSafeCopyDest]
ld e, a
ldh a, [vSafeCopyDest+1]
ld d, a ; fetch the dest
ldh a, [vSafeCopyCount]
ld c, a
ldh a, [vSafeCopyCount+1]
ld b, a ; fetch the count
; before starting transfer, make sure the zero flag is false.
ld a, 1
cp a, 0
CopyRangeSafe_TransferLoop:
ei
nop ; ei only sets the flag one instruction later apparently. safety nop!
nop
di ; process interrupts
jp z, CopyRangeSafe_CleanUp ; zero flag will only be set if the exitsafemode handler fired
ld a, [hl+]
ld [de], a ;
inc de
dec bc
ld a, b
or a, c
jp nz, CopyRangeSafe_TransferLoop
jp CopyRangeSafe_Done
CopyRangeSafe_ExitSafeMode:
ld a, 0
cp a, 0 ; set the zero flag, which we're using as a signal to stop transferring
reti ; set a to zero and set the zero flag true. now the transfer loop will end
CopyRangeSafe_CleanUp:
ld a, l
ldh [vSafeCopySource], a
ld a, h
ldh [vSafeCopySource+1], a ; store new source
ld a, e
ldh [vSafeCopyDest], a
ld a, d
ldh [vSafeCopyDest+1], a ; store new dest
ld a, c
ldh [vSafeCopyCount], a
ld a, b
ldh [vSafeCopyCount+1], a ; store new count
ld hl, CopyRangeSafe_EnterSafeMode
ld a, l
ld [INTERRUPT_LCD], a
ld a, h
ld [INTERRUPT_LCD+1], a
ld a, 148 ; CHANGE ME TO ADJUST SAFE TRANSFER TIMING
ld [$ff45], a ; set lcd interrupt handler to ENTER SAFE MODE on line 148
pop af
pop de
pop bc
pop hl
reti ; we're done with this memcpy cycle so we return from interrupt.
CopyRangeSafe_Done: ; called when the complete transfer is finished,
; this restores interrupts to how they were.
; stash interrupt state in memory
ld a, l
ldh [vSafeCopySource], a
ld a, h
ldh [vSafeCopySource+1], a ; store new source
ld a, e
ldh [vSafeCopyDest], a
ld a, d
ldh [vSafeCopyDest+1], a ; store new dest
ld a, c
ldh [vSafeCopyCount], a
ld a, b
ldh [vSafeCopyCount+1], a ; store new count
ld hl, CopyRangeSafe_EnterSafeMode
ld a, l
ld [INTERRUPT_LCD], a
ld a, h
ld [INTERRUPT_LCD+1], a
ld a, 148 ; CHANGE ME TO ADJUST SAFE TRANSFER TIMING
ld [$ff45], a ; set lcd interrupt handler to ENTER SAFE MODE on line 148
pop af
pop de
pop bc
pop hl
ldh a, [vSafeCopySTAT]
ldh [$ff41], a
ldh a, [vSafeCopyLYC]
ldh [$ff45], a
ldh a, [vSafeCopyInterruptFirst]
ld [INTERRUPT_LCD], a
ldh a, [vSafeCopyInterruptSecond]
ldh [INTERRUPT_LCD+1], a
ld hl, $ffff
ld a, [hl]
cpl
set 1, a
cpl ; turn off the lcd interrupt enable
ld [hl], a
ld a, [vSafeCopyInterruptEnable]
cp a, 0 ; if the stashed enable was 0 return. if the stashed enable was 1 then turn it on
jp z, CopyRangeSafe_Return
set 1, [hl] ; turn on the lcd interrupt
CopyRangeSafe_Return:
reti