From 2edbb942f475a2bb1b7c8072ac58d9965673f6d8 Mon Sep 17 00:00:00 2001 From: shoofle Date: Tue, 7 Jan 2025 11:34:53 -0500 Subject: [PATCH] Copy Tiles with vblank safety --- .gitignore | 1 + CopyRangeSafe.inc | 195 +++++++++++++++++++++++++++++++++++++++ CopyTilesSafe.inc | 212 +++++++++++++++++++++++++++++++++++++++++++ littleletters.asm | 190 +++++++++++++++++++------------------- themagician.aseprite | Bin 6091 -> 6082 bytes 5 files changed, 503 insertions(+), 95 deletions(-) create mode 100644 .gitignore create mode 100644 CopyRangeSafe.inc create mode 100644 CopyTilesSafe.inc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/CopyRangeSafe.inc b/CopyRangeSafe.inc new file mode 100644 index 0000000..b75acdb --- /dev/null +++ b/CopyRangeSafe.inc @@ -0,0 +1,195 @@ +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 diff --git a/CopyTilesSafe.inc b/CopyTilesSafe.inc new file mode 100644 index 0000000..ffd4a24 --- /dev/null +++ b/CopyTilesSafe.inc @@ -0,0 +1,212 @@ +CopyTilesSafe: + ; hl is source + ; de is destination + ; bc is length to copy +; copy an array of bytes to the bgmap in vram. +; this is the safe-copy analogue of CopyTilesToMap + ; 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 + ldh [vSafeCopyOriginalCount], a + ld a, b + ldh [vSafeCopyCount+1], a + ldh [vSafeCopyOriginalCount+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, CopyTilesSafe_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 + + ei + ret ; return address of bytes remaining to copy + +CopyTilesSafe_EnterSafeMode: + push hl ; stash registers! we just took over as an interrupt! + push bc + push de + push af + ld hl, CopyTilesSafe_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 +CopyTilesSafe_TransferLoop: + ei + nop ; ei only sets the flag one instruction later apparently. safety nop! + nop ; we're processing interrupts here + di + jp z, CopyTilesSafe_CleanUp ; zero flag will only be set if the exit handler fired + + ld a, [hl] ; load from the tile map into a + ld [de], a ; load from a into the destination + inc hl ; this is slower than using hli but i'm trying to work with this here + inc de + dec c + ; check if we've completed a line? + ld a, 0 + or a, c ; check if c is zero, if it's not zero go back and copy more bytes + jp nz, CopyTilesSafe_TransferLoop +CopyTilesSafe_DoneWithLine: + ldh a, [vSafeCopyOriginalCount] + ld c, a + ldh a, [vSafeCopyOriginalCount+1] + ld b, a + + ld a, e + add a, 32 + ld e, a + ld a, d + adc a, 0 + ld d, a + ld a, e + sub a, c + ld e, a + ld a, d + sbc a, 0 + ld d, a + + dec b + ld a, b + cp a, 0 + + ldh [vSafeCopyOriginalCount+1], a ; store the decremented line count + jp nz, CopyTilesSafe_TransferLoop + jp CopyTilesSafe_Done + + +CopyTilesSafe_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 + +CopyTilesSafe_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, CopyTilesSafe_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. + +CopyTilesSafe_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, 0 + ldh [vSafeCopyCount], a + ld a, 0 + 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 ; check if the stat interrupt was active before we took over + jp z, CopyRangeSafe_Return + set 1, [hl] ; turn on the lcd interrupt + +CopyTilesSafe_Return: + reti diff --git a/littleletters.asm b/littleletters.asm index f8ef264..8a02acc 100644 --- a/littleletters.asm +++ b/littleletters.asm @@ -4,101 +4,101 @@ littleletters: db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - db $04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$00,$00,$04,$04 - db $28,$28,$28,$28,$28,$28,$08,$08,$00,$00,$00,$00,$00,$00,$00,$00 - db $04,$04,$24,$24,$2c,$2c,$28,$28,$fc,$fc,$28,$28,$fe,$fe,$28,$28 - db $38,$38,$60,$60,$e0,$e0,$70,$70,$30,$30,$20,$20,$e0,$e0,$60,$60 - db $e0,$e0,$b0,$b0,$b2,$b2,$e4,$e4,$08,$08,$16,$16,$35,$35,$27,$27 - db $1c,$1c,$74,$74,$60,$60,$38,$38,$3d,$3d,$37,$37,$3e,$3e,$3c,$3c - db $00,$00,$18,$18,$10,$10,$10,$10,$00,$00,$00,$00,$00,$00,$00,$00 - db $08,$08,$10,$10,$20,$20,$20,$20,$20,$20,$20,$20,$30,$30,$10,$10 - db $00,$00,$00,$00,$0e,$0e,$02,$02,$02,$02,$06,$06,$0c,$0c,$18,$18 - db $02,$02,$46,$46,$6c,$6c,$78,$78,$7c,$7c,$68,$68,$0c,$0c,$00,$00 - db $08,$08,$08,$08,$08,$08,$08,$08,$7c,$7c,$08,$08,$08,$08,$00,$00 - db $00,$00,$00,$00,$00,$00,$00,$00,$08,$08,$08,$08,$18,$18,$10,$10 - db $00,$00,$00,$00,$00,$00,$3c,$3c,$20,$20,$00,$00,$00,$00,$00,$00 - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$20,$20,$00,$00 - db $00,$00,$00,$00,$02,$02,$04,$04,$0c,$0c,$08,$08,$10,$10,$10,$10 - db $00,$00,$00,$00,$3c,$3c,$24,$24,$24,$24,$3c,$3c,$00,$00,$00,$00 - db $00,$00,$04,$04,$04,$04,$04,$04,$04,$04,$0c,$0c,$08,$08,$00,$00 - db $00,$00,$0c,$0c,$12,$12,$12,$12,$02,$02,$0e,$0e,$07,$07,$00,$00 - db $00,$00,$00,$00,$38,$38,$08,$08,$18,$18,$18,$18,$00,$00,$00,$00 - db $02,$02,$32,$32,$26,$26,$3e,$3e,$24,$24,$04,$04,$00,$00,$00,$00 - db $00,$00,$00,$00,$3e,$3e,$20,$20,$38,$38,$08,$08,$38,$38,$20,$20 - db $00,$00,$00,$00,$30,$30,$20,$20,$58,$58,$68,$68,$7c,$7c,$3c,$3c - db $00,$00,$1e,$1e,$03,$03,$03,$03,$02,$02,$04,$04,$04,$04,$00,$00 - db $00,$00,$3c,$3c,$24,$24,$38,$38,$18,$18,$1c,$1c,$38,$38,$00,$00 - db $00,$00,$00,$00,$3e,$3e,$64,$64,$7c,$7c,$0c,$0c,$08,$08,$10,$10 - db $00,$00,$00,$00,$10,$10,$00,$00,$00,$00,$10,$10,$10,$10,$00,$00 - db $00,$00,$08,$08,$00,$00,$00,$00,$0c,$0c,$04,$04,$0c,$0c,$18,$18 - db $00,$00,$04,$04,$1c,$1c,$30,$30,$60,$60,$78,$78,$1e,$1e,$02,$02 - db $00,$00,$00,$00,$00,$00,$3c,$3c,$40,$40,$78,$78,$00,$00,$00,$00 - db $00,$00,$20,$20,$30,$30,$18,$18,$0c,$0c,$1c,$1c,$30,$30,$00,$00 - db $0c,$0c,$3c,$3c,$24,$24,$0c,$0c,$08,$08,$08,$08,$00,$00,$08,$08 - db $00,$00,$04,$04,$1e,$1e,$3f,$3f,$36,$36,$3e,$3e,$1c,$1c,$00,$00 - db $00,$00,$10,$10,$28,$28,$28,$28,$7c,$7c,$44,$44,$82,$82,$82,$82 - db $00,$00,$7c,$7c,$22,$22,$22,$22,$3c,$3c,$22,$22,$22,$22,$7c,$7c - db $00,$00,$3c,$3c,$42,$42,$40,$40,$40,$40,$40,$40,$42,$42,$3c,$3c - db $00,$00,$78,$78,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$78,$78 - db $00,$00,$7c,$7c,$44,$44,$40,$40,$78,$78,$40,$40,$44,$44,$7c,$7c - db $00,$00,$7c,$7c,$24,$24,$20,$20,$38,$38,$20,$20,$20,$20,$20,$20 - db $00,$00,$38,$38,$44,$44,$40,$40,$4c,$4c,$44,$44,$44,$44,$38,$38 - db $00,$00,$44,$44,$44,$44,$44,$44,$7c,$7c,$44,$44,$44,$44,$44,$44 - db $00,$00,$38,$38,$10,$10,$10,$10,$10,$10,$10,$10,$10,$10,$38,$38 - db $00,$00,$0e,$0e,$04,$04,$04,$04,$04,$04,$04,$04,$44,$44,$38,$38 - db $00,$00,$48,$48,$50,$50,$60,$60,$60,$60,$50,$50,$48,$48,$44,$44 - db $00,$00,$70,$70,$20,$20,$20,$20,$20,$20,$20,$20,$22,$22,$7e,$7e - db $00,$00,$42,$42,$66,$66,$5a,$5a,$42,$42,$42,$42,$42,$42,$42,$42 - db $00,$00,$64,$64,$64,$64,$54,$54,$54,$54,$4c,$4c,$4c,$4c,$44,$44 - db $00,$00,$38,$38,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$38,$38 - db $00,$00,$78,$78,$44,$44,$44,$44,$44,$44,$78,$78,$40,$40,$40,$40 - db $00,$00,$38,$38,$44,$44,$44,$44,$44,$44,$4c,$4c,$44,$44,$3a,$3a - db $00,$00,$78,$78,$44,$44,$44,$44,$7c,$7c,$48,$48,$48,$48,$44,$44 - db $00,$00,$1c,$1c,$22,$22,$20,$20,$1c,$1c,$02,$02,$62,$62,$1c,$1c - db $00,$00,$7c,$7c,$10,$10,$10,$10,$10,$10,$10,$10,$10,$10,$10,$10 - db $00,$00,$42,$42,$42,$42,$42,$42,$42,$42,$42,$42,$42,$42,$3c,$3c - db $00,$00,$44,$44,$44,$44,$28,$28,$28,$28,$28,$28,$10,$10,$10,$10 - db $00,$00,$82,$82,$82,$82,$44,$44,$54,$54,$54,$54,$28,$28,$28,$28 - db $00,$00,$44,$44,$28,$28,$28,$28,$10,$10,$28,$28,$28,$28,$44,$44 - db $00,$00,$44,$44,$28,$28,$10,$10,$10,$10,$10,$10,$10,$10,$10,$10 - db $00,$00,$7c,$7c,$08,$08,$08,$08,$10,$10,$20,$20,$20,$20,$7c,$7c - db $00,$00,$3c,$3c,$20,$20,$20,$20,$20,$20,$20,$20,$3c,$3c,$00,$00 - db $00,$00,$c0,$c0,$c0,$c0,$40,$40,$60,$60,$20,$20,$10,$10,$18,$18 - db $18,$18,$0f,$0f,$03,$03,$02,$02,$02,$02,$02,$02,$12,$12,$1e,$1e - db $08,$08,$1c,$1c,$34,$34,$04,$04,$00,$00,$00,$00,$00,$00,$00,$00 - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$3e,$3e,$00,$00 - db $20,$20,$18,$18,$0c,$0c,$04,$04,$00,$00,$00,$00,$00,$00,$00,$00 - db $00,$00,$00,$00,$38,$38,$04,$04,$3c,$3c,$44,$44,$3a,$3a,$00,$00 - db $00,$00,$20,$20,$20,$20,$3c,$3c,$22,$22,$22,$22,$3c,$3c,$00,$00 - db $00,$00,$00,$00,$1c,$1c,$20,$20,$20,$20,$20,$20,$1c,$1c,$00,$00 - db $00,$00,$04,$04,$04,$04,$3c,$3c,$44,$44,$44,$44,$3c,$3c,$00,$00 - db $00,$00,$00,$00,$38,$38,$44,$44,$78,$78,$40,$40,$3c,$3c,$00,$00 - db $00,$00,$0c,$0c,$10,$10,$38,$38,$10,$10,$10,$10,$10,$10,$00,$00 - db $00,$00,$00,$00,$3c,$3c,$44,$44,$44,$44,$3c,$3c,$04,$04,$38,$38 - db $00,$00,$40,$40,$40,$40,$78,$78,$44,$44,$44,$44,$44,$44,$00,$00 - db $00,$00,$10,$10,$00,$00,$30,$30,$10,$10,$10,$10,$38,$38,$00,$00 - db $00,$00,$04,$04,$00,$00,$04,$04,$04,$04,$04,$04,$24,$24,$18,$18 - db $00,$00,$20,$20,$28,$28,$28,$28,$30,$30,$28,$28,$24,$24,$00,$00 - db $00,$00,$10,$10,$10,$10,$10,$10,$10,$10,$10,$10,$0c,$0c,$00,$00 - db $00,$00,$00,$00,$68,$68,$54,$54,$54,$54,$54,$54,$54,$54,$00,$00 - db $00,$00,$00,$00,$78,$78,$44,$44,$44,$44,$44,$44,$44,$44,$00,$00 - db $00,$00,$00,$00,$38,$38,$44,$44,$44,$44,$44,$44,$38,$38,$00,$00 - db $00,$00,$00,$00,$78,$78,$44,$44,$44,$44,$78,$78,$40,$40,$40,$40 - db $00,$00,$00,$00,$3c,$3c,$44,$44,$44,$44,$3c,$3c,$04,$04,$06,$06 - db $00,$00,$00,$00,$1c,$1c,$20,$20,$20,$20,$20,$20,$20,$20,$00,$00 - db $00,$00,$00,$00,$38,$38,$40,$40,$38,$38,$04,$04,$78,$78,$00,$00 - db $00,$00,$10,$10,$38,$38,$10,$10,$10,$10,$10,$10,$0c,$0c,$00,$00 - db $00,$00,$00,$00,$44,$44,$44,$44,$44,$44,$44,$44,$3c,$3c,$00,$00 - db $00,$00,$00,$00,$44,$44,$44,$44,$44,$44,$28,$28,$10,$10,$00,$00 - db $00,$00,$00,$00,$44,$44,$54,$54,$54,$54,$54,$54,$28,$28,$00,$00 - db $00,$00,$00,$00,$44,$44,$28,$28,$10,$10,$28,$28,$44,$44,$00,$00 - db $00,$00,$00,$00,$44,$44,$44,$44,$44,$44,$3c,$3c,$04,$04,$38,$38 - db $00,$00,$00,$00,$7c,$7c,$08,$08,$10,$10,$20,$20,$7c,$7c,$00,$00 - db $0e,$0e,$18,$18,$0c,$0c,$0c,$0c,$38,$38,$08,$08,$18,$18,$1c,$1c - db $20,$20,$30,$30,$20,$20,$10,$10,$10,$10,$10,$10,$00,$00,$00,$00 - db $30,$30,$38,$38,$10,$10,$10,$10,$10,$10,$18,$18,$18,$18,$30,$30 - db $00,$00,$00,$00,$32,$32,$3c,$3c,$78,$78,$00,$00,$00,$00,$00,$00 - db $06,$06,$7c,$7c,$5f,$5f,$5b,$5b,$5e,$5e,$72,$72,$03,$03,$00,$00 + db $03,$03,$07,$07,$0f,$0f,$0e,$0e,$1c,$1c,$30,$30,$c0,$c0,$c0,$c0 + db $24,$24,$6c,$6c,$6c,$6c,$48,$48,$00,$00,$00,$00,$00,$00,$00,$00 + db $1a,$1a,$1a,$1a,$7f,$7f,$34,$34,$34,$34,$fe,$fe,$68,$68,$68,$68 + db $08,$08,$3c,$3c,$6a,$6a,$68,$68,$3c,$3c,$0a,$0a,$6a,$6a,$3c,$3c + db $63,$63,$a6,$a6,$ac,$ac,$d8,$d8,$1b,$1b,$35,$35,$65,$65,$c6,$c6 + db $18,$18,$24,$24,$28,$28,$18,$18,$2c,$2c,$66,$66,$67,$67,$39,$39 + db $18,$18,$18,$18,$18,$18,$30,$30,$00,$00,$00,$00,$00,$00,$00,$00 + db $04,$04,$0c,$0c,$18,$18,$10,$10,$10,$10,$18,$18,$0c,$0c,$04,$04 + db $20,$20,$30,$30,$18,$18,$08,$08,$08,$08,$18,$18,$30,$30,$20,$20 + db $00,$00,$08,$08,$28,$28,$1e,$1e,$78,$78,$14,$14,$10,$10,$00,$00 + db $00,$00,$00,$00,$18,$18,$18,$18,$7e,$7e,$18,$18,$18,$18,$00,$00 + db $00,$00,$00,$00,$00,$00,$00,$00,$0c,$0c,$0c,$0c,$0c,$0c,$18,$18 + db $00,$00,$00,$00,$00,$00,$1c,$1c,$38,$38,$00,$00,$00,$00,$00,$00 + db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$18,$18,$18,$18,$00,$00 + db $04,$04,$0c,$0c,$0c,$0c,$18,$18,$18,$18,$30,$30,$30,$30,$20,$20 + db $00,$00,$38,$38,$64,$64,$64,$64,$64,$64,$64,$64,$64,$64,$38,$38 + db $08,$08,$18,$18,$38,$38,$18,$18,$18,$18,$18,$18,$18,$18,$3c,$3c + db $00,$00,$38,$38,$4c,$4c,$0c,$0c,$0c,$0c,$18,$18,$30,$30,$7c,$7c + db $00,$00,$3c,$3c,$06,$06,$06,$06,$1c,$1c,$06,$06,$06,$06,$3c,$3c + db $00,$00,$04,$04,$26,$26,$26,$26,$3e,$3e,$06,$06,$06,$06,$04,$04 + db $00,$00,$7c,$7c,$64,$64,$60,$60,$78,$78,$44,$44,$04,$04,$38,$38 + db $00,$00,$20,$20,$30,$30,$30,$30,$3c,$3c,$32,$32,$32,$32,$1c,$1c + db $00,$00,$7c,$7c,$4c,$4c,$0c,$0c,$3e,$3e,$18,$18,$30,$30,$20,$20 + db $00,$00,$78,$78,$64,$64,$64,$64,$38,$38,$64,$64,$64,$64,$3c,$3c + db $00,$00,$38,$38,$4c,$4c,$4c,$4c,$3c,$3c,$0c,$0c,$0c,$0c,$18,$18 + db $00,$00,$00,$00,$18,$18,$18,$18,$00,$00,$18,$18,$18,$18,$00,$00 + db $00,$00,$00,$00,$0c,$0c,$0c,$0c,$00,$00,$0c,$0c,$0c,$0c,$18,$18 + db $00,$00,$0c,$0c,$18,$18,$30,$30,$60,$60,$30,$30,$18,$18,$0c,$0c + db $00,$00,$00,$00,$3c,$3c,$3c,$3c,$00,$00,$3c,$3c,$3c,$3c,$00,$00 + db $00,$00,$30,$30,$18,$18,$0c,$0c,$06,$06,$0c,$0c,$18,$18,$30,$30 + db $38,$38,$6c,$6c,$24,$24,$0c,$0c,$18,$18,$18,$18,$00,$00,$18,$18 + db $3c,$3c,$62,$62,$59,$59,$45,$45,$5d,$5d,$5d,$5d,$63,$63,$38,$38 + db $00,$00,$38,$38,$34,$34,$32,$32,$3e,$3e,$32,$32,$32,$32,$32,$32 + db $00,$00,$7c,$7c,$32,$32,$32,$32,$3c,$3c,$32,$32,$32,$32,$1c,$1c + db $00,$00,$3c,$3c,$72,$72,$62,$62,$60,$60,$60,$60,$62,$62,$3c,$3c + db $00,$00,$f8,$f8,$64,$64,$62,$62,$62,$62,$62,$62,$62,$62,$3c,$3c + db $00,$00,$fc,$fc,$60,$60,$60,$60,$78,$78,$60,$60,$60,$60,$3c,$3c + db $00,$00,$fc,$fc,$60,$60,$60,$60,$78,$78,$60,$60,$60,$60,$20,$20 + db $00,$00,$78,$78,$64,$64,$60,$60,$60,$60,$6e,$6e,$64,$64,$3c,$3c + db $00,$00,$40,$40,$62,$62,$62,$62,$7e,$7e,$62,$62,$62,$62,$22,$22 + db $00,$00,$10,$10,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$08,$08 + db $00,$00,$08,$08,$0c,$0c,$0c,$0c,$0c,$0c,$4c,$4c,$6c,$6c,$3c,$3c + db $00,$00,$40,$40,$64,$64,$68,$68,$70,$70,$68,$68,$64,$64,$22,$22 + db $00,$00,$20,$20,$30,$30,$30,$30,$30,$30,$30,$30,$32,$32,$1e,$1e + db $00,$00,$3e,$3e,$6a,$6a,$6a,$6a,$6a,$6a,$62,$62,$62,$62,$22,$22 + db $00,$00,$72,$72,$6a,$6a,$6a,$6a,$6a,$6a,$66,$66,$62,$62,$22,$22 + db $00,$00,$3c,$3c,$66,$66,$62,$62,$62,$62,$62,$62,$32,$32,$1e,$1e + db $00,$00,$f8,$f8,$64,$64,$64,$64,$7c,$7c,$60,$60,$60,$60,$20,$20 + db $00,$00,$38,$38,$64,$64,$62,$62,$62,$62,$6a,$6a,$64,$64,$3b,$3b + db $00,$00,$f8,$f8,$64,$64,$64,$64,$7c,$7c,$68,$68,$64,$64,$23,$23 + db $00,$00,$38,$38,$64,$64,$60,$60,$38,$38,$04,$04,$64,$64,$38,$38 + db $00,$00,$7e,$7e,$5a,$5a,$18,$18,$18,$18,$18,$18,$18,$18,$08,$08 + db $00,$00,$40,$40,$62,$62,$62,$62,$62,$62,$62,$62,$62,$62,$3c,$3c + db $00,$00,$40,$40,$62,$62,$62,$62,$62,$62,$24,$24,$34,$34,$18,$18 + db $00,$00,$c3,$c3,$62,$62,$62,$62,$6a,$6a,$6a,$6a,$6a,$6a,$34,$34 + db $00,$00,$42,$42,$62,$62,$34,$34,$18,$18,$3c,$3c,$66,$66,$42,$42 + db $00,$00,$4c,$4c,$4c,$4c,$4c,$4c,$3c,$3c,$0c,$0c,$4c,$4c,$78,$78 + db $00,$00,$7e,$7e,$46,$46,$0c,$0c,$18,$18,$30,$30,$62,$62,$7e,$7e + db $7c,$7c,$60,$60,$60,$60,$60,$60,$60,$60,$60,$60,$60,$60,$7c,$7c + db $20,$20,$30,$30,$30,$30,$18,$18,$18,$18,$0c,$0c,$0c,$0c,$04,$04 + db $3e,$3e,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$3e,$3e + db $18,$18,$3c,$3c,$66,$66,$42,$42,$00,$00,$00,$00,$00,$00,$00,$00 + db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$7c,$7c,$3e,$3e + db $30,$30,$18,$18,$0c,$0c,$04,$04,$00,$00,$00,$00,$00,$00,$00,$00 + db $00,$00,$00,$00,$38,$38,$0c,$0c,$3c,$3c,$4c,$4c,$3a,$3a,$00,$00 + db $00,$00,$30,$30,$30,$30,$3c,$3c,$32,$32,$32,$32,$3c,$3c,$00,$00 + db $00,$00,$00,$00,$1c,$1c,$30,$30,$30,$30,$30,$30,$1c,$1c,$00,$00 + db $00,$00,$0c,$0c,$0c,$0c,$3c,$3c,$4c,$4c,$4c,$4c,$3c,$3c,$00,$00 + db $00,$00,$00,$00,$38,$38,$64,$64,$78,$78,$60,$60,$3c,$3c,$00,$00 + db $00,$00,$0e,$0e,$18,$18,$3c,$3c,$18,$18,$18,$18,$18,$18,$00,$00 + db $00,$00,$00,$00,$3c,$3c,$4c,$4c,$4c,$4c,$3c,$3c,$0c,$0c,$38,$38 + db $00,$00,$60,$60,$60,$60,$78,$78,$64,$64,$64,$64,$64,$64,$00,$00 + db $00,$00,$10,$10,$00,$00,$38,$38,$18,$18,$18,$18,$3c,$3c,$00,$00 + db $00,$00,$08,$08,$00,$00,$0c,$0c,$0c,$0c,$0c,$0c,$2c,$2c,$18,$18 + db $00,$00,$60,$60,$68,$68,$68,$68,$70,$70,$68,$68,$64,$64,$00,$00 + db $00,$00,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$18,$18,$00,$00 + db $00,$00,$00,$00,$74,$74,$6a,$6a,$6a,$6a,$6a,$6a,$6a,$6a,$00,$00 + db $00,$00,$00,$00,$78,$78,$64,$64,$64,$64,$64,$64,$64,$64,$00,$00 + db $00,$00,$00,$00,$38,$38,$64,$64,$64,$64,$64,$64,$38,$38,$00,$00 + db $00,$00,$00,$00,$78,$78,$64,$64,$64,$64,$78,$78,$60,$60,$60,$60 + db $00,$00,$00,$00,$3c,$3c,$4c,$4c,$4c,$4c,$3c,$3c,$0c,$0c,$0e,$0e + db $00,$00,$00,$00,$1c,$1c,$30,$30,$30,$30,$30,$30,$30,$30,$00,$00 + db $00,$00,$00,$00,$38,$38,$60,$60,$38,$38,$0c,$0c,$78,$78,$00,$00 + db $00,$00,$30,$30,$78,$78,$30,$30,$30,$30,$30,$30,$1c,$1c,$00,$00 + db $00,$00,$00,$00,$64,$64,$64,$64,$64,$64,$64,$64,$3c,$3c,$00,$00 + db $00,$00,$00,$00,$64,$64,$64,$64,$64,$64,$28,$28,$10,$10,$00,$00 + db $00,$00,$00,$00,$62,$62,$6a,$6a,$6a,$6a,$6a,$6a,$34,$34,$00,$00 + db $00,$00,$00,$00,$64,$64,$38,$38,$18,$18,$2c,$2c,$46,$46,$00,$00 + db $00,$00,$00,$00,$4c,$4c,$4c,$4c,$4c,$4c,$3c,$3c,$0c,$0c,$38,$38 + db $00,$00,$00,$00,$7e,$7e,$0c,$0c,$18,$18,$30,$30,$7e,$7e,$00,$00 + db $3c,$3c,$60,$60,$30,$30,$60,$60,$60,$60,$30,$30,$60,$60,$3c,$3c + db $20,$20,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$10,$10 + db $3c,$3c,$06,$06,$0c,$0c,$06,$06,$06,$06,$0c,$0c,$06,$06,$3c,$3c + db $00,$00,$00,$00,$31,$31,$7b,$7b,$de,$de,$8c,$8c,$00,$00,$00,$00 + db $c0,$c0,$a0,$a0,$b8,$b8,$b0,$b0,$da,$da,$12,$12,$1a,$1a,$03,$03 diff --git a/themagician.aseprite b/themagician.aseprite index b47076fe0414263621d0eb5a08856cfc72e7819a..51801b1abc90e37e793a8e7c6f948c00e72d9b4d 100644 GIT binary patch delta 730 zcmV<00ww*+FTyVY!jS=g0Yb5XF$Dq0ld}aie~tnG0C=3OSJ`sIFbLGl{{KIBrjj_8 zOKIbWV9Np)Sg^9z7cP7#ogU6I%ecMw54C6P;`icbSbyiWdvZ4x4KLqk;}!cC5`c*9 z7uz+ivi`!jQ_JTKu2G1BPml-tY5G{ya&W&N$qgMTpFH+8xc%Mt$yRA?Li&pYOzVF&5X+zQjI@&*vchrq>x{0x z$$@Eov;b+GMLG;`x9+a+e#H5d87U+Pf1e+5M}&!u;fSQ^Ns0Eze~kkP2}xSJjr%wY zdVR1Tg72#EZ~f)26u+0}_>lLnXqUYmh3{2Ze=EuQGL0k*ZYy5ZL*(UMZ_W}PW z@KXSiCpoT>Z1ZP$Tk50Hw|$(|%kVbC-?m@Ir&ztQe>9G}Uje-MPwK}BjQqD_e}cm& z_@@NcQzsktH~BB-$Ed#)_k!+RR!y2C&%2Xq$M`3nI6urU%0LyWiIe)c%D?eg37Gxm z+?=EySketI3(nAo7~;9C&F{A^;M1& delta 739 zcmV<90v!FqFUv0h%aH+p0ZXxgF$Dq9ld}aif0qIP0C=3OSb?tNFbJ!}|NrwzuSozK zLwZ`JjW#V{z>sdw^Mk`a%A{v&86`j7`!}~2?AqUf*B1S)+vzHuL^Qa(uY*^6zwHEY z#Ov2y*S3}Y+k!i{yzbaGYDXa_CY$XTSaOLfBt$e;~I6aKc6z+u=a-XF?ERUZzjIZkW)R@1kdu zjCQj+j~^i`_Xs`OjsBr8DE_Pf^FmgW)3nV|N~2xGg`XlW^pS8*yMVJ(M9^zC_*eYc zxBEVoH{t&TfAcT$cN^cIHL)iyx&NK-?ZQBwI;MJH09~!x{7w1=j~_5F_&4&Of8y8e zzx6S78G~qm7-$3m3Gi}W@)x}WxUufo{|H_FE-L-wv%yy85!=iDr9YhOfPnPB=1;z- zi&ZiIXSe14MK6i=hy}W1Y`L8!hXg=4-`{1A{oS(b+D zHGY+5PJ3H`O>f(O^9b4Kw|Uq34*)v7nZ9k?jV!$7pF}eB??8W4$9W{_cinb<0>`=y z^F!<5MyT~$-ih&x{;7ZMm~Kmeb-$fI?jI|Ff)dCTul>~YBdsStKJCWDAZDdUb5Pq0 zL>;%gt#bPAJ$rwD*t%Zu5X$rX1D$&h>$5QrQUjCl19-DU3}*%b1Cy*0DggtN*AgWG V1C#v{DFFkMCle+C0<%sNbObhxa@YU>