Instantly share code, notes, and snippets.
Last active
March 30, 2020 18:01
-
Save ISSOtm/4d251fbc3cecec13eab113645160bf90 to your computer and use it in GitHub Desktop.
Menu system, designed for Motherboard GB, and ported elsewhere...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SECTION "Language menu header", ROMX | |
LanguageMenuHeader:: | |
db BANK("Language menu") | |
dw LanguageMenuInit | |
db PADF_START | PADF_DOWN | PADF_UP | |
db 0 ; Prevent repeat press | |
dw 0, 0, 0, ForceMenuValidation, 0, 0, 0, 0 | |
db 0 ; Previous item | |
db 1 ; Allow wrapping | |
db 0 ; Default item | |
db NB_LANGUAGES ; Size | |
dw LanguageMenuRedraw | |
dw LanguageMenuItems | |
dw 0 | |
SECTION "Language menu", ROMX | |
LanguageMenuInit: | |
rst wait_vblank | |
xor a | |
ldh [rLCDC], a | |
; xor a | |
ld [wTextLetterDelay], a | |
ld hl, _SCRN0 | |
ld bc, SCRN_VX_B * SCRN_Y_B | |
; xor a | |
rst memset | |
ld a, SCRN_X | |
ld [wTextLineLength], a | |
ld a, SCRN_Y_B | |
ld [wTextNbLines], a | |
ld [wTextRemainingLines], a | |
ld [wNewlinesUntilFull], a | |
; a is non-zero | |
ld hl, LanguageMenuItems | |
ld b, BANK(LanguageMenuItems) | |
call PrintVWFText | |
ld hl, _SCRN0 + SCRN_VY_B * 6 + 7 | |
call SetPenPosition | |
call PrintVWFChar | |
; Load extra gfx | |
; Cursor, Up, Down and START button prompts | |
ld de, .gfx | |
ld hl, $8800 | |
ld b, 8 | |
call pb16_unpack_block | |
ld a, 42 | |
ldh [hLangSelMenuTimer1], a | |
xor a | |
ldh [hLangSelMenuTimer2], a | |
; xor a | |
ldh [hBGP], a | |
ldh [hOBP0], a | |
ld hl, wShadowOAM + $A0 - 1 | |
; xor a | |
.clearSprites | |
dec l ; dec hl | |
dec l ; dec hl | |
dec l ; dec hl | |
ld [hld], a | |
jr nz, .clearSprites | |
; xor a | |
ldh [hLangSelMenuCursorPos], a | |
inc hl | |
ld a, h ; ld a, HIGH(wShadowOAM) | |
ldh [hOAMBufferHigh], a | |
; ld de, .oam | |
ld hl, wShadowOAM | |
ld c, .palettePacket - .oam | |
rst memcpy_small | |
ld a, $1B | |
ldh [hLangSelMenuPalette], a | |
ld a, LCDCF_ON | LCDCF_WINOFF | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJON | LCDCF_BGON | |
ldh [hLCDC], a | |
ldh [rLCDC], a | |
ld hl, .palettePacket | |
ldh a, [hIsSGB] | |
and a | |
call nz, SendPacketNoDelay | |
jp DrawVWFChars | |
.gfx | |
INCBIN "res/lang_screen/gfx.chr.pb16" | |
.oam | |
dspr 6 * 8 - 2, 6 * 8, $80, 0 | |
dspr 6 * 8 - 10, 6 * 8, $82, 0 | |
dspr 6 * 8 + 6, 6 * 8, $82, OAMF_YFLIP | |
dspr 11 * 8 , 9 * 8, $83, 0 | |
dspr 12 * 8 + 3, 8 * 8, $85, 0 | |
dspr 12 * 8 + 3, 9 * 8, $86, 0 | |
dspr 12 * 8 + 3, 10 * 8, $87, 0 | |
.palettePacket | |
sgb_packet PAL_SET, 1, 4,0, 4,0, 4,0, 4,0, 1 | $80 | |
LanguageMenuRedraw: ; Called with selected item in `b` | |
ld a, HIGH(wShadowOAM) | |
ldh [hOAMBufferHigh], a | |
; Change flag cel | |
ldh a, [hLangSelMenuTimer1] | |
inc a | |
and $1F | |
ldh [hLangSelMenuTimer1], a | |
jr nz, .keepFlagFrame | |
ld a, [wShadowOAM + 2] | |
xor 1 | |
ld [wShadowOAM + 2], a | |
xor a ; Force next event to trigger, as it's supposed to as well | |
.keepFlagFrame | |
; Apply fade-in (reusing previous timer for efficieny) | |
and $03 | |
jr nz, .paletteOK | |
ldh a, [hBGP] | |
ld e, a | |
ldh a, [hLangSelMenuPalette] | |
add a, a | |
jr z, .paletteOK | |
rl e | |
add a, a | |
ldh [hLangSelMenuPalette], a | |
ld a, e | |
rla | |
ldh [hBGP], a | |
ldh [hOBP0], a | |
.paletteOK | |
; Make arrows blink | |
ldh a, [hLangSelMenuTimer2] | |
inc a | |
and $1F | |
ldh [hLangSelMenuTimer2], a | |
and $18 | |
jr z, .hideArrows | |
ld a, 6 * 8 + 8 | |
.hideArrows | |
ld [wShadowOAM + 5], a | |
ld [wShadowOAM + 9], a | |
; Change START button cel | |
ldh a, [hLangSelMenuTimer2] | |
and $07 | |
jr nz, .keepSTARTFrame | |
ld a, [wShadowOAM + 14] | |
xor 7 | |
ld [wShadowOAM + 14], a | |
.keepSTARTFrame | |
; Move cursor (and attached sprites as well) | |
ldh a, [hLangSelMenuCursorPos] | |
ld c, a | |
ld a, b ; Selected item | |
ld [wLanguage], a | |
add a, a | |
add a, a | |
add a, a | |
sub c | |
ret z | |
sra a ; Halve the difference to lerp a bit | |
jr nz, .ok | |
inc a | |
.ok | |
add a, c | |
ldh [hLangSelMenuCursorPos], a | |
add a, 6 * 8 - 2 + 16 | |
ld [wShadowOAM], a | |
sub 8 | |
ld [wShadowOAM + 4], a | |
add a, 16 | |
ld [wShadowOAM + 8], a | |
ret | |
SECTION "Language menu strings", ROMX | |
LanguageMenuItems: | |
db TEXT_BLANKS,4, "ENGLISH\n" | |
db TEXT_BLANKS,3, "ESPANOL\n" | |
db "FRANCAIS\n" | |
db TEXT_BLANKS,3, "NIHONGO" | |
db 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SECTION "Menu system", ROM0 | |
; Adds a menu on top of the menu stack | |
; @param de A pointer to the menu's header in ROM | |
; @param b The bank where the menu's header is located | |
; @destroy Loaded ROM bank | |
AddMenu:: | |
ld a, b | |
rst bankswitch | |
ld hl, wNbMenus | |
ld a, [hl] | |
cp MENU_STACK_CAPACITY | |
call nc, MenuStackOverflowError | |
inc a | |
ld [hli], a | |
; ld hl, wMenu0 | |
ld bc, sizeof_Menu | |
dec a | |
call GetNthStruct | |
push hl | |
ld c, Menu_ROMSize | |
rst memcpy_small | |
xor a | |
ld c, sizeof_Menu - Menu_ROMSize | |
rst memset_small | |
pop hl | |
ld a, [hli] ; Get bank | |
rst bankswitch | |
; Run init func | |
ld a, [hli] | |
ld h, [hl] | |
ld l, a | |
or h | |
ret z | |
rst call_hl | |
ret | |
; Processes one frame of the menu stack (thus, the topmost) | |
ProcessMenus:: | |
; By default, no menu is closing | |
xor a | |
ld [wMenuClosingReason], a | |
ld a, [wNbMenus] | |
and a | |
ret z | |
ld hl, wMenu0 | |
ld bc, sizeof_Menu | |
dec a | |
call GetNthStruct | |
ld a, [hli] | |
rst bankswitch | |
inc hl | |
inc hl ; Skip init func | |
; Get buttons | |
ld a, [hli] ; Read button mask | |
ld c, a | |
ldh a, [hHeldButtons] | |
and c | |
ld c, a ; Stash this, and DON'T modify it | |
; Try to perform RepeatPress | |
ld a, l ; Get ptr to RepeatPressCounter (will be used even if RepeatPress is disabled) | |
add a, Menu_RepeatPressCounter - Menu_EnableRepeatPress | |
ld e, a | |
adc a, h | |
sub e | |
ld d, a | |
ld b, 0 ; Start by supposing no button will be RepeatPress'd | |
; If any (non-ignored) button is pressed, stop RepeatPress | |
ldh a, [hPressedButtons] | |
and c ; It's fine to do this, since a pressed button is held anyways | |
jr z, .dontResetRepeatPress | |
xor a | |
ld [de], a | |
.dontResetRepeatPress | |
ld a, [hli] ; Read EnableRepeatPress | |
and a | |
jr z, .skipRepeatPress | |
ld a, c ; Get held buttons | |
and PADF_DOWN | PADF_UP | PADF_LEFT | PADF_RIGHT ; Get d-pad only | |
; Check if exactly 1 direction is being held | |
jr z, .skipRepeatPress ; If 0, don't do anything | |
.getFirstDirection | |
add a, a | |
jr nc, .getFirstDirection ; Keep going until the first bit is shifted out | |
jr nz, .skipRepeatPress ; If more bits remain, more than 1 button is being held, so skip | |
; So, only 1 direction is being held, and if it has just been pressed, the state is currently zero | |
; Increment the counter | |
ld a, [de] | |
inc a | |
ld [de], a | |
cp 22 | |
jr c, .skipRepeatPress ; Unless the counter reaches 30, don't do anything | |
ld a, 20 | |
ld [de], a ; Reset counter (to apply delay) | |
ld a, c | |
and PADF_DOWN | PADF_UP | PADF_LEFT | PADF_RIGHT | |
ld b, a ; Mark this button as RepeatPress'd | |
.skipRepeatPress | |
inc de ; Skip RepeatPressCounter | |
; de = MiscState | |
; hl = ButtonHooks | |
ld a, l | |
add a, 2 * 8 ; Get to the end of the ButtonHooks buffer | |
ld l, a | |
jr nc, .nocarry | |
inc h | |
.nocarry | |
; Save currently selected item | |
inc hl ; Skip prev | |
inc hl ; Skip flags | |
ld a, [hld] ; Read cur | |
dec hl ; Skip flags | |
ld [hli], a ; Store cur into prev | |
; hl = AllowWrapping | |
; Check if a button has been pressed | |
ldh a, [hPressedButtons] | |
and c ; Get only those that we are interested in | |
jr nz, .buttonPressed | |
; Check if a button is being RepeatPressed | |
ld a, b | |
and a | |
jr z, .noButtonPressed | |
.buttonPressed | |
push bc | |
push de | |
push hl | |
ld b, 0 | |
; Look for the button we're gonna process | |
; hl is 1 byte past the end of the buffer, so 2 decs will put it at the high byte of the last entry | |
.selectHook | |
dec hl | |
dec hl | |
inc b | |
add a, a | |
jr nc, .selectHook | |
; Load the default menu action | |
ld a, b | |
ld [wMenuAction], a | |
ld a, [hld] ; Read high byte | |
ld l, [hl] | |
ld h, a | |
or l | |
jr z, .skipHook | |
; The hook may choose to override the menu action, if it wishes to do so | |
rst call_hl | |
.skipHook | |
pop hl ; Get back ptr to AllowWrapping | |
ld a, [wMenuAction] | |
dec a | |
cp MENU_ACTION_INVALID - 1 | |
jr nc, .menuActionNone | |
; Perform the requested action | |
push hl | |
add a, a | |
add a, LOW(.menuActions) | |
ld e, a | |
adc a, HIGH(.menuActions) | |
sub e | |
ld d, a | |
ld a, [de] | |
ld b, a | |
inc de | |
ld a, [de] | |
ld d, a | |
ld e, b | |
call CallDE | |
pop hl | |
.menuActionNone | |
pop de | |
pop bc | |
.noButtonPressed | |
inc hl ; Skip flags | |
ld a, [hli] ; Get current item | |
ld b, a | |
inc hl ; Skip size | |
; Run redraw func (if any) | |
ld a, [hli] | |
push hl | |
ld h, [hl] | |
ld l, a | |
or h | |
jr z, .noRedraw | |
rst call_hl | |
.noRedraw | |
pop hl | |
inc hl | |
; Skip items ptr | |
inc hl | |
inc hl | |
; Check if this menu should close | |
ld a, [wMenuClosingReason] | |
and a | |
ret z ; jr z, .dontClose | |
ld a, [hli] | |
ld h, [hl] | |
ld l, a | |
or h | |
jr z, .noCloseHook | |
rst call_hl | |
.noCloseHook | |
ld hl, wNbMenus | |
dec [hl] | |
ld a, [wMenuClosingReason] | |
ld [wPreviousMenuClosingReason], a | |
.dontClose | |
ret | |
.menuActions | |
dw MenuMoveDown ; DOWN | |
dw MenuMoveUp ; UP | |
dw MenuDoNothing ; LEFT | |
dw MenuDoNothing ; RIGHT | |
dw MenuDoNothing ; START | |
dw MenuDoNothing ; SELECT | |
dw MenuCancel ; B | |
dw MenuValidate ; A | |
dw MenuAddNew | |
MenuMoveDown: | |
ld a, [hli] ; Enable wrapping? | |
ld e, a | |
ld a, [hli] ; Current item | |
inc a ; Move 1 down | |
cp [hl] ; Compare to size | |
jr c, .ok | |
ld a, e | |
rra ; Get bit 0 into carry | |
ret nc ; If wrapping is disabled, do nothing | |
xor a ; Wrap back to 0 | |
.ok | |
dec hl ; Get back to current item | |
ld [hl], a ; Write back | |
ret | |
MenuMoveUp: | |
ld a, [hli] ; Enable wrapping? | |
ld e, a | |
ld a, [hl] ; Current item | |
and a ; Are we about to wrap? | |
jr nz, .ok ; No, carry on | |
ld a, e | |
rra ; Get bit 0 into carry | |
ret nc ; If wrapping is disabled, do nothing | |
inc hl | |
ld a, [hld] | |
.ok | |
dec a | |
ld [hl], a | |
ret | |
MenuValidate: | |
inc hl | |
ld a, [hl] ; Current item | |
ld [wPreviousMenuItem], a | |
ld a, MENU_VALIDATED | |
db $11 ; ld de, XXXX | |
MenuCancel: | |
ld a, MENU_CANCELLED | |
ld [wMenuClosingReason], a | |
MenuDoNothing: ; Stub for menu actions that do nothing | |
ret | |
; One of the menu actions | |
MenuAddNew: | |
ld hl, wMenuAction+1 | |
ld a, [hli] | |
ld b, a | |
ld a, [hli] | |
ld d, [hl] | |
ld e, a | |
ldh a, [hCurROMBank] | |
push af | |
call AddMenu | |
pop af | |
rst bankswitch | |
ret | |
ForceMenuValidation:: | |
ld a, MENU_ACTION_VALIDATE | |
ld [wMenuAction], a | |
ret | |
PreventMenuAction:: | |
xor a | |
ld [wMenuAction], a | |
ret |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment