Files
oracle-of-secrets/Menu/menu_journal.asm

711 lines
16 KiB
NASM

; Book of Secrets Journal Menu
; ---------------------------------------------------------
; Journal Handler
; ---------------------------------------------------------
Journal_Handler:
{
PHB : PHK : PLB
SEP #$30 ; Ensure 8-bit A and X/Y for input handling
; Check timer
LDA.w $0207 : BEQ .process_input
DEC.w $0207
JMP .exit
.process_input
; Check for L button press (using F2 for continuous poll with timer)
LDA.b $F2 : BIT.b #$20 : BEQ .check_right
REP #$30
JSR Journal_PrevPage
JSL Menu_DrawJournal
SEP #$30
LDA.b #$20 : STA.w $012F ; Play page scroll sound
LDA.b #$0A : STA.w $0207 ; Set delay
JMP .exit
.check_right
; Check for R button press
LDA.b $F2 : BIT.b #$10 : BEQ .exit
REP #$30
JSR Journal_NextPage
JSL Menu_DrawJournal
SEP #$30
LDA.b #$20 : STA.w $012F ; Play page scroll sound
LDA.b #$0A : STA.w $0207 ; Set delay
.exit
SEP #$30
PLB
RTL
}
; ---------------------------------------------------------
; Page Navigation
; ---------------------------------------------------------
Journal_PrevPage:
{
LDA.l JournalState
AND.w #$00FF : BEQ .wrap_to_last
DEC A
STA.l JournalState
RTS
.wrap_to_last
; Find total count to wrap to last
JSR Journal_CountUnlocked
DEC A
STA.l JournalState
RTS
}
Journal_NextPage:
{
LDA.l JournalState
AND.w #$00FF : INC A : STA.b $00
; Check if next page exists
LDA.b $00
JSR Journal_GetNthEntry
CPX.w #$0000 : BEQ .wrap_to_first
LDA.b $00
STA.l JournalState
RTS
.wrap_to_first
LDA.w #$0000
STA.l JournalState
RTS
}
; ---------------------------------------------------------
; Entry Logic
; ---------------------------------------------------------
; Input: A = Index (N)
; Output: X = Text Pointer (or 0 if not found)
Journal_GetNthEntry:
{
PHA
LDY.w #$0000 ; Master List Index
.loop
; Check if we reached end of list
LDA.w Journal_MasterList, Y : BEQ .end_of_list
; Check Flag
; Format: dd dd dd mm (Address Long, Mask)
; But we can't indirect long easily without setup.
; Let's read address to $00.
LDA.w Journal_MasterList, Y : STA.b $02
LDA.w Journal_MasterList+2, Y : STA.b $04 ; Get mask in low byte of $04
SEP #$20
; $04 = Bank, $05 = Mask (from 16-bit read above)
PHB
LDA.b $04 : PHA : PLB ; Set DB to address bank
LDA.b ($02) ; Load flag value at address
PLB ; Restore original data bank
AND.w Journal_MasterList+3, Y ; Apply mask (A is 8-bit, addr is 16-bit)
; Wait, 16-bit read of +2 gets Bank and Mask.
; $02-$03 = Addr Low
; $04 = Bank
; $05 = Mask
; The AND above reads from ROM directly.
BEQ .locked
; Unlocked
PLA : DEC A : PHA ; Decrement target index
BMI .found
.locked
REP #$20
TYA : CLC : ADC.w #$0006 : TAY ; Next Entry (4 bytes header + 2 bytes ptr = 6)
BRA .loop
.found
REP #$20
PLA ; Clean stack
LDA.w Journal_MasterList+4, Y : TAX
RTS
.end_of_list
REP #$20
PLA ; Clean stack
LDX.w #$0000
RTS
}
Journal_CountUnlocked:
{
LDY.w #$0000 ; Master List Index
LDA.w #$0000 : STA.b $06 ; Counter
.loop
LDA.w Journal_MasterList, Y : BEQ .done
; Check Flag
LDA.w Journal_MasterList, Y : STA.b $02
LDA.w Journal_MasterList+2, Y : STA.b $04
SEP #$20
PHB ; Save current data bank
LDA.b $04 : PHA : PLB ; Set DB to address bank
LDA.b ($02) ; Load flag value
PLB ; Restore original data bank
AND.w Journal_MasterList+3, Y ; Apply mask (A is 8-bit, addr is 16-bit)
BEQ .locked
REP #$20
INC.b $06
.locked
REP #$20
TYA : CLC : ADC.w #$0006 : TAY
BRA .loop
.done
LDA.b $06
RTS
}
; ---------------------------------------------------------
; Entry Drawing
; ---------------------------------------------------------
Journal_DrawEntry:
{
REP #$30
LDA.l JournalState : AND.w #$00FF
JSR Journal_GetNthEntry
STX.b $00 ; Store Text Pointer
CPX.w #$0000 : BNE .valid
; Draw "Empty" if no entry found (shouldn't happen with correct logic)
RTS
.valid
LDX.w #$0000 ; Text Offset
LDY.w #$0000 ; VRAM Offset
.loop
PHY ; Save VRAM offset
TXY ; Y = Text Offset
LDA ($00), Y ; Read word from text
PLY ; Restore VRAM offset
CMP.w #$FFFF : BEQ .done ; Check for terminator
STA.w $1292, Y ; Write to VRAM buffer (Row 1)
INY #2 ; Next VRAM word
INX #2 ; Next Text word
; Wrap logic for multiple lines (up to 9 lines supported)
; Row Width = $40 bytes (32 tiles * 2 bytes)
; Each line is 16 chars ($20 bytes), then skip $20 to next row
CPY.w #$0020 : BNE .check_line_2
TYA : CLC : ADC.w #$0020 : TAY
.check_line_2
CPY.w #$0060 : BNE .check_line_3
TYA : CLC : ADC.w #$0020 : TAY
.check_line_3
CPY.w #$00A0 : BNE .check_line_4
TYA : CLC : ADC.w #$0020 : TAY
.check_line_4
CPY.w #$00E0 : BNE .check_line_5
TYA : CLC : ADC.w #$0020 : TAY
.check_line_5
CPY.w #$0120 : BNE .check_line_6
TYA : CLC : ADC.w #$0020 : TAY
.check_line_6
CPY.w #$0160 : BNE .check_line_7
TYA : CLC : ADC.w #$0020 : TAY
.check_line_7
CPY.w #$01A0 : BNE .check_line_8
TYA : CLC : ADC.w #$0020 : TAY
.check_line_8
CPY.w #$01E0 : BNE .check_line_9
TYA : CLC : ADC.w #$0020 : TAY
.check_line_9
BRA .loop
.done
SEP #$30
RTS
}
; ---------------------------------------------------------
; Data Tables
; ---------------------------------------------------------
; Format: Address(3), Mask(1), TextPtr(2) = 6 bytes
Journal_MasterList:
; === Chapter 0: A Hero is Born ===
dl $7EF3C5 : db $02 : dw Entry_TheCall ; GameState = $02 (Farore intro)
dl $7EF3C6 : db $04 : dw Entry_CastAway ; OOSPROG2 bit 2 (Kydrog intro)
; === Chapter 1: The Maku Tree ===
dl $7EF3D4 : db $01 : dw Entry_MakuTree ; MakuTreeQuest = $01
dl $7EF3C7 : db $01 : dw Entry_FirstEssence ; MapIcon = $01 (Mushroom Grotto)
; === Ocarina Quest Chain (HINT -> PROGRESS -> COMPLETE) ===
dl $7EF3D7 : db $01 : dw Entry_MaskShop ; SideQuestProg bit 0 (met salesman)
dl $7EF3D7 : db $02 : dw Entry_CursedGirl ; SideQuestProg bit 1 (found cucco)
dl $7EF3D7 : db $08 : dw Entry_GotMushroom ; SideQuestProg bit 3 (got mushroom)
dl $7EF344 : db $02 : dw Entry_GotPowder ; MagicPowder = $02
dl $7EF3D8 : db $01 : dw Entry_CurseBroken ; SideQuestProg2 bit 0 (ranch girl)
dl $7EF3D8 : db $04 : dw Entry_SongLearned ; SideQuestProg2 bit 2 (song taught)
; === Deku Mask Chain ===
dl $7EF3D7 : db $04 : dw Entry_DyingDeku ; SideQuestProg bit 2 (found deku)
dl $7EF3D8 : db $10 : dw Entry_DekuFreed ; SideQuestProg2 bit 4 (soul freed)
dl $7EF349 : db $01 : dw Entry_DekuMask ; DekuMask obtained
; === Dungeon Completions ===
dl $7EF398 : db $01 : dw Entry_D1Complete ; Scrolls bit 0 (Mushroom Grotto)
dl $7EF398 : db $02 : dw Entry_D2Complete ; Scrolls bit 1 (Tail Palace)
dl $7EF398 : db $04 : dw Entry_D3Complete ; Scrolls bit 2 (Kalyxo Castle)
dl $7EF398 : db $08 : dw Entry_D4Complete ; Scrolls bit 3 (Zora Temple)
dl $7EF398 : db $10 : dw Entry_D5Complete ; Scrolls bit 4 (Glacia Estate)
dl $7EF398 : db $20 : dw Entry_D6Complete ; Scrolls bit 5 (Goron Mines)
dl $7EF398 : db $40 : dw Entry_D7Complete ; Scrolls bit 6 (Dragon Ship)
; === Mask Collection ===
dl $7EF347 : db $01 : dw Entry_ZoraMask ; ZoraMask obtained
dl $7EF358 : db $01 : dw Entry_WolfMask ; WolfMask obtained
dl $7EF348 : db $01 : dw Entry_BunnyHood ; BunnyHood obtained
; === Other Side Quests ===
dl $7EF39B : db $01 : dw Entry_BeanPlanted ; MagicBeanProg bit 0 (planted)
dl $7EF3D7 : db $10 : dw Entry_OldManQuest ; SideQuestProg bit 4 (old man)
dl $7EF3D7 : db $20 : dw Entry_GoronQuest ; SideQuestProg bit 5 (goron)
dw $0000 ; Terminator
; ---------------------------------------------------------
; Chapter 0: A Hero is Born
; ---------------------------------------------------------
Entry_TheCall:
dw "THE_CALL________"
dw "________________"
dw "A_VOICE_CALLED__"
dw "OUT_TO_ME..._IT_"
dw "SAID_ACCEPT_THIS"
dw "QUEST__HERO.____"
dw $FFFF
Entry_CastAway:
dw "CAST_AWAY_______"
dw "________________"
dw "KYDROG_TOOK_____"
dw "FARORE_AND_CAST_"
dw "ME_TO_THE_ABYSS."
dw "FIND_MAKU_TREE._"
dw $FFFF
; ---------------------------------------------------------
; Chapter 1: The Maku Tree
; ---------------------------------------------------------
Entry_MakuTree:
dw "THE_MAKU_TREE___"
dw "________________"
dw "I_FOUND_THE_____"
dw "MAKU_TREE._IMPA_"
dw "AWAITS_IN_THE___"
dw "HALL_OF_SECRETS."
dw $FFFF
Entry_FirstEssence:
dw "FIRST_ESSENCE___"
dw "________________"
dw "DARKNESS_LURKS__"
dw "IN_THE_MUSHROOM_"
dw "GROTTO_TO_THE___"
dw "WEST.___________"
dw $FFFF
; ---------------------------------------------------------
; Ocarina Quest Chain
; ---------------------------------------------------------
Entry_MaskShop:
dw "MASK_SHOP_______"
dw "________________"
dw "A_SALESMAN_EAST_"
dw "OF_VILLAGE_SELLS"
dw "MASKS._HE_NEEDS_"
dw "ME_TO_HAVE_AN___"
dw "OCARINA._A_GIRL_"
dw "AT_TOTO_RANCH___"
dw "MIGHT_HELP._____"
dw $FFFF
Entry_CursedGirl:
dw "CURSED_GIRL_____"
dw "________________"
dw "A_GIRL_AT_TOTO__"
dw "RANCH_IS_CURSED!"
dw "SHE_HAS_BEEN____"
dw "TURNED_INTO_A___"
dw "CUCCO._MAGIC____"
dw "POWDER_MIGHT____"
dw "BREAK_THE_SPELL."
dw $FFFF
Entry_GotMushroom:
dw "STRANGE_MUSHROOM"
dw "________________"
dw "FOUND_A_STRANGE_"
dw "MUSHROOM_IN_THE_"
dw "WOODS._THE______"
dw "POTION_SHOP_____"
dw "MIGHT_WANT_THIS."
dw $FFFF
Entry_GotPowder:
dw "MAGIC_POWDER____"
dw "________________"
dw "THE_WITCH_MADE__"
dw "POWDER_FROM_THE_"
dw "MUSHROOM._IT_HAS"
dw "TRANSFORMATIVE__"
dw "POWER.__________"
dw $FFFF
Entry_CurseBroken:
dw "CURSE_BROKEN____"
dw "________________"
dw "THE_POWDER______"
dw "BROKE_THE_CURSE!"
dw "THE_RANCH_GIRL__"
dw "GAVE_ME_HER_____"
dw "OCARINA_AND_____"
dw "TAUGHT_ME_THE___"
dw "SONG_OF_STORMS._"
dw $FFFF
Entry_SongLearned:
dw "SONG_OF_HEALING_"
dw "________________"
dw "THE_MASK________"
dw "SALESMAN_TAUGHT_"
dw "ME_THE_SONG_OF__"
dw "HEALING._IT_CAN_"
dw "FREE_TROUBLED___"
dw "SPIRITS_FROM____"
dw "THEIR_PAIN._____"
dw $FFFF
; ---------------------------------------------------------
; Deku Mask Chain
; ---------------------------------------------------------
Entry_DyingDeku:
dw "DYING_DEKU______"
dw "________________"
dw "A_DEKU_SCRUB_IN_"
dw "THE_WOODS_IS____"
dw "WITHERING_AWAY._"
dw "HIS_SPIRIT_SEEMS"
dw "TROUBLED..._____"
dw "PERHAPS_A_______"
dw "HEALING_MELODY?_"
dw $FFFF
Entry_DekuFreed:
dw "DEKU_FREED______"
dw "________________"
dw "THE_SONG_FREED__"
dw "THE_DEKUS_______"
dw "TORTURED_SOUL.__"
dw "HE_LEFT_BEHIND__"
dw "A_MASK..._______"
dw $FFFF
Entry_DekuMask:
dw "DEKU_MASK_______"
dw "________________"
dw "WITH_THE_DEKU___"
dw "MASK_I_CAN_TAKE_"
dw "DEKU_FORM._THIS_"
dw "WILL_HELP_ME____"
dw "TRAVERSE_THE____"
dw "SWAMPS_TO_______"
dw "TAIL_PALACE.____"
dw $FFFF
; ---------------------------------------------------------
; Dungeon Completions
; ---------------------------------------------------------
Entry_D1Complete:
dw "GROTTO_CLEARED__"
dw "________________"
dw "THE_MUSHROOM____"
dw "GROTTO_IS_FREE__"
dw "OF_EVIL._I_FOUND"
dw "THE_BOW_WITHIN._"
dw $FFFF
Entry_D2Complete:
dw "TAIL_PALACE_____"
dw "________________"
dw "MOLDORM_FALLS.__"
dw "THE_ROCS_FEATHER"
dw "GRANTS_ME_THE___"
dw "GIFT_OF_FLIGHT._"
dw $FFFF
Entry_D3Complete:
dw "KALYXO_CASTLE___"
dw "________________"
dw "THE_CASTLE_IS___"
dw "RECLAIMED._THE__"
dw "MEADOW_BLADE____"
dw "IS_MINE.________"
dw $FFFF
Entry_D4Complete:
dw "ZORA_TEMPLE_____"
dw "________________"
dw "THE_WATERS_ARE__"
dw "PURIFIED._THE___"
dw "HOOKSHOT_WILL___"
dw "AID_MY_QUEST.___"
dw $FFFF
Entry_D5Complete:
dw "GLACIA_ESTATE___"
dw "________________"
dw "TWINROVA_IS_____"
dw "DEFEATED._THE___"
dw "FIRE_ROD_MELTS__"
dw "ALL_OBSTACLES.__"
dw $FFFF
Entry_D6Complete:
dw "GORON_MINES_____"
dw "________________"
dw "THE_MINES_ARE___"
dw "SAFE._THE_HAMMER"
dw "BREAKS_THROUGH__"
dw "ANY_BARRIER.____"
dw $FFFF
Entry_D7Complete:
dw "DRAGON_SHIP_____"
dw "________________"
dw "THE_ANCIENT_SHIP"
dw "YIELDS_ITS______"
dw "SECRET:_THE_____"
dw "SOMARIA_ROD.____"
dw $FFFF
; ---------------------------------------------------------
; Mask Collection
; ---------------------------------------------------------
Entry_ZoraMask:
dw "ZORA_MASK_______"
dw "________________"
dw "THE_PRINCESS____"
dw "GAVE_ME_A_MASK._"
dw "I_CAN_BREATHE___"
dw "UNDERWATER._____"
dw $FFFF
Entry_WolfMask:
dw "WOLF_MASK_______"
dw "________________"
dw "THE_WOLFOS______"
dw "SPIRIT_IS_AT____"
dw "PEACE._ITS_MASK_"
dw "GRANTS_SPEED____"
dw "AT_NIGHT._______"
dw $FFFF
Entry_BunnyHood:
dw "BUNNY_HOOD______"
dw "________________"
dw "THE_MASK________"
dw "SALESMAN_SOLD___"
dw "ME_A_BUNNY_HOOD."
dw "I_CAN_RUN_______"
dw "FASTER_NOW._____"
dw $FFFF
; ---------------------------------------------------------
; Other Side Quests
; ---------------------------------------------------------
Entry_BeanPlanted:
dw "MAGIC_BEAN______"
dw "________________"
dw "I_PLANTED_A_____"
dw "MAGIC_BEAN._IT__"
dw "NEEDS_WATER_AND_"
dw "A_BEES_BLESSING."
dw $FFFF
Entry_OldManQuest:
dw "OLD_MAN_________"
dw "________________"
dw "AN_OLD_MAN_IS___"
dw "LOST_IN_THE_____"
dw "LAVA_LANDS._IF_I"
dw "ESCORT_HIM_HOME_"
dw "HE_MAY_REWARD___"
dw "ME._____________"
dw $FFFF
Entry_GoronQuest:
dw "GORON_QUEST_____"
dw "________________"
dw "THE_GORON_NEEDS_"
dw "5_ROCK_SIRLOINS_"
dw "TO_OPEN_THE_____"
dw "MINES._I_SHOULD_"
dw "SEARCH_THE______"
dw "MOUNTAINS.______"
dw $FFFF
; ---------------------------------------------------------
; Background Drawing
; ---------------------------------------------------------
Menu_DrawJournal:
{
PHB : PHK : PLB
REP #$30
; Page selection logic:
; 0 -> First page background
; Last -> Last page background
; Else -> Middle page background
LDA.l JournalState : AND.w #$00FF : BNE .not_first
; JournalState == 0, draw first page
JSR Journal_DrawFirstPage
BRA .draw_entry
.not_first
; Check if this is the last page
PHA
JSR Journal_CountUnlocked : DEC A : STA.b $02
PLA
CMP.b $02 : BNE .middle
; This is the last page
JSR Journal_DrawLastPage
BRA .draw_entry
.middle
JSR Journal_DrawMiddlePage
.draw_entry
JSR Journal_DrawEntry
SEP #$30
PLB
RTL
}
Journal_DrawFirstPage:
{
REP #$30
LDX.w #$FE
.loop
LDA.w .first_page_tilemap, X
STA.w $1000, X
LDA.w .first_page_tilemap+$100, X
STA.w $1100, X
LDA.w .first_page_tilemap+$200, X
STA.w $1200, X
LDA.w .first_page_tilemap+$300, X
STA.w $1300, X
LDA.w .first_page_tilemap+$400, X
STA.w $1400, X
LDA.w .first_page_tilemap+$500, X
STA.w $1500, X
LDA.w .first_page_tilemap+$600, X
STA.w $1600, X
LDA.w .first_page_tilemap+$700, X
STA.w $1700, X
DEX : DEX
BPL .loop
RTS
.first_page_tilemap
incbin "tilemaps/journal_begin.bin"
}
Journal_DrawMiddlePage:
{
REP #$30
LDX.w #$FE
.loop
LDA.w .middle_page_tilemap, X
STA.w $1000, X
LDA.w .middle_page_tilemap+$100, X
STA.w $1100, X
LDA.w .middle_page_tilemap+$200, X
STA.w $1200, X
LDA.w .middle_page_tilemap+$300, X
STA.w $1300, X
LDA.w .middle_page_tilemap+$400, X
STA.w $1400, X
LDA.w .middle_page_tilemap+$500, X
STA.w $1500, X
LDA.w .middle_page_tilemap+$600, X
STA.w $1600, X
LDA.w .middle_page_tilemap+$700, X
STA.w $1700, X
DEX : DEX
BPL .loop
RTS
.middle_page_tilemap
incbin "tilemaps/journal_mid.bin"
}
Journal_DrawLastPage:
{
REP #$30
LDX.w #$FE
.loop
LDA.w .last_page_tilemap, X
STA.w $1000, X
LDA.w .last_page_tilemap+$100, X
STA.w $1100, X
LDA.w .last_page_tilemap+$200, X
STA.w $1200, X
LDA.w .last_page_tilemap+$300, X
STA.w $1300, X
LDA.w .last_page_tilemap+$400, X
STA.w $1400, X
LDA.w .last_page_tilemap+$500, X
STA.w $1500, X
LDA.w .last_page_tilemap+$600, X
STA.w $1600, X
LDA.w .last_page_tilemap+$700, X
STA.w $1700, X
DEX : DEX
BPL .loop
RTS
.last_page_tilemap
incbin "tilemaps/journal_end.bin"
}