From 4b100cf2a2c3faf97f98db424b61d726830a9c7d Mon Sep 17 00:00:00 2001 From: shoofle Date: Sun, 5 Jan 2025 18:33:30 -0500 Subject: [PATCH] progress on the safe copy routine --- tarot.asm | 261 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 218 insertions(+), 43 deletions(-) diff --git a/tarot.asm b/tarot.asm index d91af6d..3ab6566 100644 --- a/tarot.asm +++ b/tarot.asm @@ -9,11 +9,32 @@ DEF SCENE_SETUP EQU $FF81 DEF SCENE_UPDATE EQU SCENE_SETUP + 4 ; call then ret is 3+1 bytes DEF SCENE_DRAW EQU SCENE_UPDATE + 4 DEF SCENE_TEARDOWN EQU SCENE_DRAW + 4 -DEF rMYBTN EQU $FF90 +DEF rMYBTN EQU $FFA0 DEF rMYBTNP EQU rMYBTN + 1 -DEF vSelectedTileIndex EQU $FF90+2 -DEF vPreviousCardIndex EQU $FF90+3 + +DEF INTERRUPT_LCD EQU $FF91 + + +; tile index variables +DEF vSelectedTileIndex EQU $FFA0+2 +DEF vPreviousCardIndex EQU $FFA0+3 + +; variables for safe transfer async function +DEF vSafeCopySource EQU $FFA0+4 +DEF vSafeCopyDest EQU vSafeCopySource + 2 +DEF vSafeCopyCount EQU vSafeCopyDest + 2 ; check this for safe transfer being complete +; stash previous interrupt state before using the interrupts +DEF vSafeCopyLYC EQU vSafeCopyCount + 2 ; stashes $FF45, the LYC register +DEF vSafeCopySTAT EQU vSafeCopyLYC + 1 ; stashes $FF41, the STAT register +DEF vSafeCopyInterruptFirst EQU vSafeCopySTAT + 1 ; stashes $0048 the STAT interrupt +DEF vSafeCopyInterruptSecond EQU vSafeCopyInterruptFirst + 1 +DEF vSafeCopyInterruptEnable EQU vSafeCopyInterruptSecond + 1 ; stashes $FFFF, which interrupts are enabled + INCLUDE "hardware.inc" +SECTION "Interrupts", ROM0[$0] + ds $48 - @, 0 + call INTERRUPT_LCD - 1 + ret SECTION "Header", ROM0[$100] @@ -35,6 +56,8 @@ EntryPoint: ld [hl], a ld hl, SCENE_TEARDOWN - 1 ld [hl], a + ld hl, INTERRUPT_LCD - 1 + ld [hl], a ld a, [Instructions + 3] ; get the value of a ret instruction ld hl, SCENE_SETUP + 2 @@ -45,6 +68,8 @@ EntryPoint: ld [hl], a ld hl, SCENE_TEARDOWN + 2 ld [hl], a + ld hl, INTERRUPT_LCD + 2 + ld [hl], a ; set up our scene vectors ld hl, SCENE_SETUP @@ -71,6 +96,14 @@ EntryPoint: ld [hl+], a ld a, d ld [hl+], a + + ; set up the interrupt vector to just be ret. + ld hl, INTERRUPT_LCD + ld de, INTERRUPT_LCD + 2 + ld a, e + ld [hl+], a + ld a, d + ld [hl+], a ; Do not turn the LCD off outside of VBlank WaitVBlank: @@ -81,6 +114,7 @@ WaitVBlank: call SCENE_SETUP - 1 Loop: + di ld hl, rP1 ld [hl], P1F_GET_DPAD ld a, [hl] @@ -107,13 +141,15 @@ Loop: ld a, b ld [rMYBTN], a + di call SCENE_UPDATE - 1 - + ei ld b, 144 call AwaitLine + di call SCENE_DRAW - 1 - + ei jp Loop @@ -126,17 +162,17 @@ CardReadSetup: ld hl, UITiles ; source ld de, $8800 ; destination of copy ld bc, UITilesEnd - UITiles ; length to copy - call CopyRange + call CopyRangeUnsafe ld hl, LetterTiles ; map the small font into vram at ascii locations ld de, $8000 + (127 + $21)*16 ld bc, LetterTilesEnd - LetterTiles - call CopyRange + call CopyRangeUnsafe ld hl, BigLetterTiles ld de, $8000 + (128 + 16 + 32)*16 ld bc, BigLetterTilesEnd - BigLetterTiles - ; call CopyRange + ; call CopyRangeUnsafe ld de, UITilemap ; origin ld hl, $9800 ; destination @@ -150,11 +186,6 @@ CardReadSetup: ld c, 8 call CopyTilesToMap - ld a, $FF - ld [vPreviousCardIndex], a - ld a, 0 - call LoadCardData - ; Turn the LCD on ld a, LCDCF_BLK01 | LCDCF_ON | LCDCF_BGON ld [rLCDC], a @@ -163,43 +194,44 @@ CardReadSetup: ld a, %11100100 ld [rBGP], a + ld a, $FF + ldh [vPreviousCardIndex], a ld a, 0 ldh [vSelectedTileIndex], a - + call LoadCardData + ei ret ; return from cardreadsetup CardReadUpdate: ldh a, [rMYBTNP] - and a, %0000_1000 ; check if down is held + and a, %0000_1000 ; select the down key jp z, CardReadUpdateDoneDown ldh a, [vSelectedTileIndex] inc a ; increment when they press down because the stack has card 0 at the top ldh [vSelectedTileIndex], a CardReadUpdateDoneDown: ldh a, [rMYBTNP] - and a, %0000_0100 + and a, %0000_0100 ; select the up key jp z, CardReadUpdateDoneUp ldh a, [vSelectedTileIndex] dec a ; decrement when they press up because the stack has card 0 at the top ldh [vSelectedTileIndex], a CardReadUpdateDoneUp: ldh a, [vSelectedTileIndex] - cp a, 22 + cp a, 2 jp nz, CardReadUpdateDoneZero ld a, 0 ldh [vSelectedTileIndex], a CardReadUpdateDoneZero: cp a, $FF jp nz, CardReadUpdateReturn - ld a, 21 + ld a, 1 ldh [vSelectedTileIndex], a CardReadUpdateReturn: ret CardReadDraw: - ldh a, [vSelectedTileIndex] - call LoadCardData - ld b, 11 ; 11 rows, and we stop before drawing row zero + ld b, 1 ; 11 rows, and we stop before drawing row zero ld hl, $9800+32*4+19 ; start point ld de, 32 ; stride CardReadDrawCopyTile: @@ -240,33 +272,23 @@ CardReadDraw_PickTileWithTop: ld a, $82 ld [hl], a CardReadDrawReturn: - ld a, [vSelectedTileIndex] - call LoadCardData + ldh a, [vSelectedTileIndex] + ld hl, vPreviousCardIndex + cp a, [hl] + call nz, LoadCardData ; only load new card data if the selected card has changed ret CardReadTeardown: ret LoadCardData: - ld hl, vPreviousCardIndex - cp a, [hl] - ret z + ld a, [vSelectedTileIndex] ld [vPreviousCardIndex], a - ld [vSelectedTileIndex], a - - ld a, [rLCDC] - cp 0 - jp z, LoadCardData_DoneTurningOff ld b, 144 - call AwaitLine ; put the line you want to reach in b - - ld a, 0 - ld [rLCDC], a ; turn off the lcd so we don't cause memory corruption + call AwaitLine ; wait for vblank before starting to work LoadCardData_DoneTurningOff: - ld a, [vSelectedTileIndex] - ld b, 0 ld c, a ; load bc from a. coming into this we have the number of the card in the card array in a ld hl, Cards + 1 ; skip the length prefix @@ -316,7 +338,7 @@ LoadCardData_DoneTurningOff: ld de, $8000 ; always load tile data into the same spot in vram - call CopyRange + call CopyRangeSafe pop hl ld a, [hl+] @@ -337,8 +359,7 @@ LoadCardData_DoneTurningOff: ld c, 8 ; width call CopyTilesToMap - ld a, LCDCF_BLK01 | LCDCF_ON | LCDCF_BGON - ld [rLCDC], a + ret Instructions: @@ -368,7 +389,7 @@ AwaitLine: ; put the line you want to reach in b jp nz, AwaitLine ret -CopyRange: +CopyRangeUnsafe: ; hl is source ; de is destination ; bc is length to copy @@ -378,7 +399,161 @@ CopyRange: dec bc ld a, b or a, c ; check if bc is zero - jp nz, CopyRange + jp nz, CopyRangeUnsafe + ret + +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 $0048 the STAT interrupt handler + ld a, [$ffff] + and a, %0000_0010 + ld [vSafeCopyInterruptEnable], a ; stashes whether LCD interrupt are enabled + + ld a, 148 ; adjust this to change timing of copy + ld [$ff45], a + + 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 + ei + ret ; return address of bytes remaining to copy + +CopyRangeSafe_EnterSafeMode: + 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 + 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 + 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: ret CopyTilesToMap: @@ -438,7 +613,7 @@ TheFool: dw FoolTilesEnd - FoolTiles dw FoolTiles dw FoolMapEnd - FoolMap - dw FoolMap, FoolMapEnd + dw FoolMap FoolTiles: db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01,$00,$01,$00,$02