; FF80 CALL ; FF81 LOW ; FF82 HIGH ; FF83 RET ; pattern repeats for the first 16 bytes so we can have some call vectors for the scene system DEF SCENE_SETUP EQU $c101 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 INTERRUPT_LCD EQU $c111 def ZEROES equ $D000 DEF rMYBTN EQU $FFA8 DEF rMYBTNP EQU rMYBTN + 1 DEF rDELTAT EQU rMYBTNP + 1 DEF VARIABLES_START EQU rDELTAT+1 INCLUDE "hardware.inc" SECTION "Interrupts", ROM0[$0] ds $48 - @, 0 call INTERRUPT_LCD - 1 ret SECTION "Header", ROM0[$100] jp EntryPoint ds $150 - @, 0 ; Make room for the header EntryPoint: ; Shut down audio circuitry ld a, 0 ldh [rNR52], a ; shut down audio ldh [rDIV], a ; reset timer just in case?? ld a, 0 ld hl, ZEROES ld bc, $200 .buildZeros ld a, 0 ld [hl+], a ld a, b or a, c dec bc jp nz, .buildZeros ; set up the scene and interrupt vectors ld hl, Instructions ld a, [hl] ; get the value of a call instruction so we can shove it into our handles ld hl, SCENE_SETUP - 1 ld [SCENE_SETUP - 1], a ld hl, SCENE_UPDATE - 1 ld [hl], a ld hl, SCENE_DRAW - 1 ld [hl], a ld hl, SCENE_TEARDOWN - 1 ld [hl], a ld hl, INTERRUPT_LCD - 1 ld [hl], a ld hl, Instructions inc hl ld a, [hl] ; get the value of a call instruction so we can shove it into our handles ld hl, SCENE_SETUP ld [hl], a ld hl, SCENE_UPDATE ld [hl], a ld hl, SCENE_DRAW ld [hl], a ld hl, SCENE_TEARDOWN ld [hl], a ld hl, INTERRUPT_LCD ld [hl], a ld hl, Instructions inc hl inc hl ld a, [hl] ; get the value of a call instruction so we can shove it into our handles ld hl, SCENE_SETUP + 1 ld [hl], a ld hl, SCENE_UPDATE + 1 ld [hl], a ld hl, SCENE_DRAW + 1 ld [hl], a ld hl, SCENE_TEARDOWN + 1 ld [hl], a ld hl, INTERRUPT_LCD + 1 ld [hl], a ld hl, Instructions inc hl inc hl inc hl ld a, [hl] ; get the value of a call instruction so we can shove it into our handles ld hl, SCENE_SETUP + 2 ld [hl], a ld hl, SCENE_UPDATE + 2 ld [hl], a ld hl, SCENE_DRAW + 2 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, ScreenMainMenu call ChangeScene Loop: ; okay this is kinda sketchy. we want a delta time variable. ; we've got two eight-bit counters, one at 4096hz and one at 16384hz ; we can definitely reset the faster counter. ; but! that's gonna overflow every frame. ; however! it should always overflow the same amount ; because we're basically running once per frame (AwaitLine call later) ; if we assume it overflows once per frame, then the total value should be ; 256 + [rDIV]. so if we divide by two (or more!) the total value will be ; 256/2 + [rDIV]/2. so we just have to right shift and add 128. ; or right shift twice and add 64. this loses us precision but we didn't need ; it anyway. ldh a, [rDIV] ldh [rDIV], a ; this will reset the timer sra a sra a add a, 64 ldh [rDELTAT], a xor a, a ; zero a out in one cycle ; store dpad and btn state in the rMYBTN register ld hl, rP1 ld [hl], P1F_GET_DPAD ld a, [hl] ld a, [hl] ld a, [hl] ld a, [hl] cpl and a, %00001111 ld b, a ld [hl], P1F_GET_BTN ld a, [hl] ld a, [hl] ld a, [hl] ld a, [hl] cpl and a, %00001111 swap a or a, b ld b, a ; put new input state in b ldh a, [rMYBTN] ; previous input state in a cpl ; a holds buttons which weren't pressed last frame and a, b ; select buttons which were pressed this frame, but not last frame ldh [rMYBTNP], a ; save that as btnp state ld a, b ; select buttons which were pressed this frame ldh [rMYBTN], a ; save that as btn state call SCENE_UPDATE - 1 ; hope this takes not too many scanlines! ld b, 144 call AwaitLine call SCENE_DRAW - 1 ; hope this takes fewer than 9 scanlines! ; either way it's going to eat into the update timing ; at this point we want to make sure that scanline 153 has passed ; we should check if we're past there and skip this await if necessary ld b, 5 call AwaitLine jp Loop ChangeScene: ; hl should be a pointer to, in sequence, setup update draw teardown call SCENE_TEARDOWN - 1 ld a, [hl+] ld [SCENE_SETUP], a ld a, [hl+] ld [SCENE_SETUP+1], a ld a, [hl+] ld [SCENE_UPDATE], a ld a, [hl+] ld [SCENE_UPDATE+1], a ld a, [hl+] ld [SCENE_DRAW], a ld a, [hl+] ld [SCENE_DRAW+1], a ld a, [hl+] ld [SCENE_TEARDOWN], a ld a, [hl+] ld [SCENE_TEARDOWN+1], a call SCENE_SETUP - 1 ret AwaitLine: ; put the line you want to reach in b ld a, [rLY] cp b jp nz, AwaitLine ret ArrayClampLooping: ; loops a to be in the array, assuming hl points to the length cp a, [hl] ; if a == length... jp nz, :+ ld a, 0 ; set it to 0 : cp a, $FF ; otherwise if a == $FF... jp nz, :+ ld a, [hl] dec a ; then set it to length-1 : ret ; a is return value PrintString: ; write ascii string which has been prefixed by its length. at hl ld b, [hl] ld a, b cp a, 0 ret z inc hl PrintBChars: ;write ascii characters. will not respect newlines or anything like that ; hl should be the source of ascii text ; de should be the location in the tile map to start writing at ; b should be the length ld a, [hli] or a, %10000000 ld [de], a inc de dec b jp nz, PrintBChars ret ;thanks to https://gbdev.io/pandocs/OAM_DMA_Transfer.html for this routine ; a should be the high byte of where we're DMAing from (ld a, $c0 in my case probably) ; de should have the address in hram for where to paste to RunDMA: push de push af ld hl, run_dma_tail ld bc, run_dma_tail.run_dma_tail_end - run_dma_tail call CopyRangeUnsafe pop af run_dma: ; This part must be in ROM. ld bc, $2846 ; B: wait time; C: LOW($FF46) ret ; we pushed de before, so this should "return" to execute that (HRAM) address run_dma_tail: ; This part must be in HRAM. ldh [c], a .wait dec b jr nz, .wait ret z ; Conditional `ret` is 1 M-cycle slower, which avoids ; reading from the stack on the last M-cycle of DMA. .run_dma_tail_end Instructions: call SCENE_UPDATE + 2 ret INCLUDE "Async.inc" INCLUDE "ScreenMainMenu.inc" INCLUDE "ScreenSpreadSelect.inc" INCLUDE "ScreenCardRead.inc" INCLUDE "CardLibrary.inc"