7800 HSC CART
From 8BitDev.org - Atari 7800 Development Wiki
; ==============================================================================
; MODULE: HIGH_SCORE_CART.ASM
;
; Atari 7800 High Score Cartridge (HSC) Operating System ROM Disassembly
;
; V1.00 29/04/09 - Initial release by Mark Ball
; V1.11 05/12/14 - Further disassembly by Mike Saarna
; V1.12 10/04/25 - Further disassembly by Mike Saarna, addition of PAL hack
; V1.20 2026 - Architectural annotation, semantic clarification, and
; technical corrections (Score binary model, Watkins font mapping,
; 15-bit packed initials bitfields, dual-page RAM specifications).
;
; Assembles with DASM:
; DASM HIGH_SCORE_CART.ASM -F3 -OREBUILD_HSC.BIN
;
; ==============================================================================
; ARCHITECTURAL SPECIFICATION & MEMORY CONTRACT
; ==============================================================================
;
; 1. MEMORY MAP:
; $1000 - $17FF : 2KB Non-Volatile Battery-Backed SRAM (NVRAM)
; $3000 - $3FFF : 4KB HSC Operating System ROM
;
; 2. CALLING CONVENTION & DUAL-PAGE RAM CONTRACT (512 CONTIGUOUS BYTES):
; When invoking any vector, the game MUST provide a page-aligned 512-byte
; workspace spanning two contiguous 256-byte pages: Page N and Page N+1.
; - Y Register = High byte of Argument Block (Page N). e.g., LDY #$25
; - X Register = ignored. The low byte of the block is always $00.
;
; Page N ($xx00 - $xxFF):
; $00 - $11 : 18-Byte Caller Argument Block
; $12 : HSCSTAT result (last byte of the BCD top score at $0F-$12)
; $13 - $4F : 61-Byte Zero-Page Preservation Area (backs up ZP $80-$BC)
; $50 - $FF : Text formatting and display workspace
;
; Page N+1 ($xx00 + 1 page):
; $00 - $86 : MARIA Display Lists (DL) generated dynamically by HSC
; $87 - $FF : MARIA Display List List (DLL) (DPPL=$87, DPPH=Page N + 1)
;
; 3. DATA REPRESENTATIONS:
; - Scores: The caller passes a 4-byte packed BCD score pointer. The HSC
; converts this value into a 24-bit unsigned binary integer ($000000-$FFFFFF)
; for ranking and Cart RAM storage. In Cart RAM, scores are stored as
; 3-byte LITTLE-ENDIAN binary integers (LSB, MID, MSB), NOT BCD.
; - Initials: Three 5-bit characters packed into 2 consecutive bytes (15 bits).
; - Font (Watkins Encoding):
; $00-$19 : A - Z (0 - 25)
; $1A : '_' (Underscore, 26 - empty entry initials)
; $1B : '.' (Period, 27)
; $1C : '-' (Hyphen, 28)
; $1D : ' ' (Space, 29)
; $1E : ''' (Apostrophe, 30)
; $1F : Solid Block (31 - END in text entry)
; $20 : '<-' (Left Arrow, 32 - BACKSPACE in text entry)
; $21-$2A : '0' - '9' (Digits 0-9, 33 - 42)
; $2B : '?' (Question Mark, 43)
; ==============================================================================
processor 6502
; ---------------------------------------------------------------
; Assembly-time configurables
; ---------------------------------------------------------------
; uncomment line to build the HSC PAL HACK v1
;PALHACK = 1
; ---------------------------------------------------------------
; MACROs.
; ---------------------------------------------------------------
; Watkins character encoding macro (ASCII character - 'A')
mac WATKINS
dc.b {1}-"A"
endm
; ---------------------------------------------------------------
; TIA Hardware Registers
; ---------------------------------------------------------------
INPT4 = $0C ; Player 1 Fire Button Input (Bit 7: 0 = Pressed, 1 = Open)
INPT5 = $0D ; Player 2 Fire Button Input (Bit 7: 0 = Pressed, 1 = Open)
AUDF1 = $18 ; Audio Frequency Channel 1
AUDV0 = $19 ; Audio Volume Channel 0
AUDV1 = $1A ; Audio Volume Channel 1
; ---------------------------------------------------------------
; MARIA Graphics Registers
; ---------------------------------------------------------------
MSTAT = $28 ; MARIA Status Register (Bit 7: VBLANK status)
DPPH = $2C ; Display List List Pointer High Byte
DPPL = $30 ; Display List List Pointer Low Byte
CHARBASE = $34 ; Character Base Address (Font bank pointer)
CTRL = $3C ; MARIA Control Register ($4B = DMA On, $7F = DMA Off)
; ---------------------------------------------------------------
; Zero Page RAM Workspace ($80 - $BC)
; ---------------------------------------------------------------
; The HSC backs up these 61 bytes to (OrigArgRamLo) + $13..$4F on entry
; and restores them on exit, ensuring transparency to the caller.
ArgGameID1 = $80 ; Game ID Byte 1 (Bit 7 set = score passed /10, shown with trailing 0)
ArgGameID2 = $81 ; Game ID Byte 2
ArgGameDifficulty = $82 ; Bits 7..2: Difficulty ID; Bits 1..0: Player Index
ArgGameController = $83 ; Controller type ($00/$01 = Joystick, $02 = Custom Routine)
ArgGameNameLo = $84 ; Pointer (Lo) to Game Title string (or custom controller driver)
ArgGameNameHi = $85 ; Pointer (Hi) to Game Title string
ArgDifficultyNameLo = $86 ; Pointer (Lo) to Difficulty text string
ArgDifficultyNameHi = $87 ; Pointer (Hi) to Difficulty text string
ArgGameScoreLo = $88 ; Pointer (Lo) to caller's 4-byte packed BCD score
ArgLogoDllLo = $8A ; Pointer (Lo) to Logo Display List List (DLL) (0 if text)
ArgLogoDllHi = $8B ; Pointer (Hi) to Logo Display List List (DLL)
ArgSoundRoutineLo = $8C ; Pointer (Lo) to caller sound update subroutine
ArgHSDisplayTime = $8E ; Display hold time in seconds (60 frames each), used by every vector
z8F = $8F ; Initials char 0 / Indirect transfer pointer Lo
z90 = $90 ; Initials char 1
z91 = $91 ; Initials char 2
z92 = $92 ; General 16-bit indirect scratch pointer (Lo)
z93 = $93 ; General 16-bit indirect scratch pointer (Hi)
OrigArgRamLo = $94 ; Caller Argument Block base pointer Lo (Always $00)
OrigArgRamHi = $95 ; Caller Argument Block base pointer Hi (Page N)
z96 = $96 ; Display List workspace base pointer Lo ($00)
z97 = $97 ; Display List workspace base pointer Hi (Page N + 1)
z98 = $98 ; Text line buffer pointer (Lo)
z99 = $99 ; Text line buffer pointer (Hi)
z9A = $9A ; DL cursor entry pointer 1 (Lo) - Character byte
z9B = $9B ; DL cursor entry pointer 1 (Hi)
z9C = $9C ; DL cursor entry pointer 2 (Lo) - Palette/Attribute byte
z9D = $9D ; DL cursor entry pointer 2 (Hi)
z9E = $9E ; DL cursor entry pointer 3 (Lo) - Horizontal X-position
z9F = $9F ; DL cursor entry pointer 3 (Hi)
zA0 = $A0 ; Owner name DL adjust pointer 1 (Lo)
zA1 = $A1 ; Owner name DL adjust pointer 1 (Hi)
zA2 = $A2 ; Owner name DL adjust pointer 2 (Lo)
zA3 = $A3 ; Owner name DL adjust pointer 2 (Hi)
zA4 = $A4 ; DLL generation write pointer Lo (Initialized to $87)
zA5 = $A5 ; DLL generation write pointer Hi (Page N + 1)
GameScoreTableLo = $A6 ; Cart RAM current 25-byte score block pointer (Lo)
GameScoreTableHi = $A7 ; Cart RAM current 25-byte score block pointer (Hi)
zA8 = $A8 ; Screen line height limit / countdown counter
PlayerController = $A9 ; Player index, arg +$02 & 3 (0 = left port, 1-3 = right port)
zAA = $AA ; Temporary loop counter / slot index
zAB = $AB ; Temporary state / RAM initialization flag
zAC = $AC ; Unpacked character temp / leading-zero suppression flag
zAD = $AD ; 24-bit binary score - LSB (Bits 0..7)
zAE = $AE ; 24-bit binary score - MID (Bits 8..15)
zAF = $AF ; 24-bit binary score - MSB (Bits 16..23)
zB0 = $B0 ; 24-bit binary math scratchpad - LSB
zB1 = $B1 ; 24-bit binary math scratchpad - MID
zB2 = $B2 ; 24-bit binary math scratchpad - MSB / DLL zone height total
zB3 = $B3 ; Cursor blink countdown timer (Reloads to $1E = 30 frames)
zB4 = $B4 ; Target score insertion offset in Cart RAM table
CurrentRankCounter = $B5 ; Rank counter (counts 5 down; final rank 0 = 1st .. 4 = 5th)
zB6 = $B6 ; Directory slot index ($00-$44): matched slot, or LRU victim
zB7 = $B7 ; Input repeat throttle timer (Reloads to $0A = 10 frames)
zB8 = $B8 ; RIOT console switch comparison mask
IdleTimerFine = $B9 ; Screen saver idle timer, fine (Reloads to $E9)
; (also the logo DLI flag while the DLL is built)
IdleTimerCoarse = $BA ; Screen saver idle timer, coarse (Reloads to $E9)
zBB = $BB ; Y register preserve temporary storage
zSWCHBSave = $BC ; Stashed state of RIOT SWCHB console switches
; ---------------------------------------------------------------
; RIOT Hardware Registers (6532)
; ---------------------------------------------------------------
SWCHA = $0280 ; Port A: Joysticks (P1: D7-D4, P2: D3-D0; 0=Active)
SWACNT = $0281 ; Port A Direction Register
SWCHB = $0282 ; Port B: Console Switches (Reset, Select, Pause, Difficulty)
SWBCNT = $0283 ; Port B Direction Register
; ---------------------------------------------------------------
; Battery Backed NVRAM Memory Map (Size 2K: $1000 - $17FF)
; ---------------------------------------------------------------
CartRAMSaveZ94 = $1000 ; Preserved caller ZP $94 register
CartRAMSaveZ95 = $1001 ; Preserved caller ZP $95 register
CartRAMMagic1 = $1002 ; Magic Byte 1 ($68)
CartRAMMagic2 = $1003 ; Magic Byte 2 ($83)
CartRAMMagic3 = $1004 ; Magic Byte 3 ($AA)
CartRAMMagic4 = $1005 ; Magic Byte 4 ($55)
CartRAMMagic5 = $1006 ; Magic Byte 5 ($9C)
CartRAMEntryID = $1007 ; Active Calling Vector ID
CartRAMOwnerName = $1008 ; Cartridge Owner Personalization String (32 bytes)
CartRAMOwnerNameLength = $1028 ; Owner string length (1 to 32 bytes)
CartRAMGameID1Table = $1029 ; Directory: Game ID Byte 1 look-up table (69 entries)
CartRAMGameID2Table = $106E ; Directory: Game ID Byte 2 look-up table (69 entries)
CartRAMGameDifficultyTable = $10B3 ; Directory: Difficulty table (69 entries, masked with $FC)
CartRAMGameAgeTable = $10F8 ; Directory: LRU Age table (69 entries, 1 = MRU, $7F = Empty)
; Score Data Base Address: $113D = $113D + (SlotIndex * 25)
; Total 69 slots * 25 bytes = 1725 bytes ($113D - $17F9).
; Each 25-byte block stores 5 ranked score entries (5 bytes each).
; Score Entry Format (5 Bytes):
; Offset +0 : Packed Initials Byte 0: [C1.4 C1.3 C1.2 C0.4 C0.3 C0.2 C0.1 C0.0]
; Offset +1 : Packed Initials Byte 1: [C1.1 C1.0 0 C2.4 C2.3 C2.2 C2.1 C2.0]
; Offset +2 : Score LSB (24-bit unsigned binary integer, Little-Endian)
; Offset +3 : Score MID
; Offset +4 : Score MSB
CartRAMSavedEntry = $17FA ; Saved 5th place entry (5 bytes), restored on cancel
; $17FF ; Unused trailing byte
; ==============================================================================
; Function: HSCCPYFM (Vector @ $3FEE)
; Purpose: Copies the active 25-byte high-score table from Cart NVRAM to the
; user memory buffer pointed to by argument +$0F/$10 (copied to $8F).
; Inputs: Y = Page number of Argument Block; ($8F) = Destination pointer
; Outputs: A = $00, or $EF with nothing copied if the game has no table
; ==============================================================================
org $3000,$FF
e3000:
LDA #$EF ; Function ID: HSCCPYFM
JSR HSCInitRoutine ; Initialize context and locate score table
LDY #$18 ; 25 bytes total (offsets 24 down to 0)
e3007:
LDA (GameScoreTableLo),Y ; Read byte from Cart NVRAM
STA (z8F),Y ; Write byte to caller's destination buffer
DEY
BPL e3007
LDA #$00
RTS
; ==============================================================================
; Function: HSCCPYTO (Vector @ $3FF1)
; Purpose: Copies a 25-byte high-score table from the user memory buffer
; pointed to by ($8F) directly into Cart NVRAM. The table is created
; first if the game has none.
; Inputs: Y = Page number of Argument Block; ($8F) = Source pointer
; Outputs: A = $01
; ==============================================================================
e3011:
LDA #$04 ; Function ID: HSCCPYTO
JSR HSCInitRoutine ; Initialize context and locate score table
LDY #$18 ; 25 bytes total (offsets 24 down to 0)
e3018:
LDA (z8F),Y ; Read byte from caller's source buffer
STA (GameScoreTableLo),Y ; Write byte to Cart NVRAM
DEY
BPL e3018
LDA #$01
RTS
; ==============================================================================
; Function: HSCSTAT (Vector @ $3FF7)
; Purpose: Checks the status of the High Score Cartridge and evaluates whether
; the caller's new score qualifies for ranking. Never creates a
; table and never updates the LRU ages.
; Inputs: Y = Page number of Argument Block
; Outputs: (OrigArgRamLo)+$04 = Number of existing difficulty tables found
; (OrigArgRamLo)+$05 = Rank (0 = 1st to 4 = 5th), or $FF if score does
; not rank ($FF for any score if no table exists)
; (OrigArgRamLo)+$0F..$12 = Current 1st place score, packed BCD,
; big-endian, in the caller's units (0 if no table)
; A = $00
; ==============================================================================
HSCSTAT:
LDA #$02 ; Function ID: HSCSTAT
JSR HSCInitRoutine
LDX #$44 ; Scan all 69 directory slots ($44 down to $00)
LDY #$00 ; Match counter
e302B:
LDA CartRAMGameID1Table,X
CMP ArgGameID1
BNE e3041
LDA CartRAMGameID2Table,X
CMP ArgGameID2
BNE e3041
LDA CartRAMGameDifficultyTable,X
CMP #$7F ; Check if slot is unallocated (age not checked)
BEQ e3041
INY ; Increment matching difficulty counter
e3041:
DEX
BPL e302B
TYA
LDY #$04
STA (OrigArgRamLo),Y ; Store matching table count at Argument + $04
LDA GameScoreTableHi
BNE RankNewScore ; If valid score table exists, evaluate rank
; No valid score table exists (status check only on new table):
LDY #$0F
STA (OrigArgRamLo),Y ; Clear status bytes $0F..$12
INY
STA (OrigArgRamLo),Y
INY
STA (OrigArgRamLo),Y
INY
STA (OrigArgRamLo),Y
LDY #$05
LDA #$FF ; Set rank = $FF (unranked)
STA (OrigArgRamLo),Y
RTS
; ==============================================================================
; Routine: RankNewScore
; Purpose: Evaluates candidate 24-bit binary score (in zAD, zAE, zAF) against
; the 5 saved scores in the current NVRAM table.
; The table entries are ranked in descending order:
; Entry 0 (Offsets $00-$04) = 1st Place (Highest)
; Entry 4 (Offsets $14-$18) = 5th Place (Lowest)
; Evaluation starts at Entry 4 and proceeds upwards.
; Outputs: (OrigArgRamLo)+$05 contains rank (0-4) or $FF if not ranked.
; ==============================================================================
RankNewScore:
JSR IsScoreNonZero ; Check if candidate score is > 0
BEQ RankNewScore_NotRanked ; A score of 0 cannot rank
LDY #$18 ; Offset of 5th place entry MSB (Offset 24)
LDA #$05 ; Start rank evaluation at 5th place
STA CurrentRankCounter
RankNewScore_CheckLoop:
LDX #$02 ; 3-byte comparison: X = 2 (MSB), 1 (MID), 0 (LSB)
RankNewScore_CompareByte:
LDA (GameScoreTableLo),Y ; Load byte from NVRAM table
CMP zAD,X ; Compare TableByte with CandidateByte
BCC RankNewScore_IsLowerOrEqual ; If Table < Candidate: Candidate is strictly higher!
BNE RankNewScore_IsHigher ; If Table > Candidate: Table is strictly higher!
DEY ; Bytes are equal: advance to less significant byte
DEX
BPL RankNewScore_CompareByte ; Continue comparing all 3 bytes
BMI RankNewScore_IsLowerOrEqual ; Scores are identical: treat as qualifying rank
RankNewScore_IsHigher:
; Table entry is strictly higher than candidate score: candidate cannot beat it.
JSR AdvanceToNextScoreEntry
TYA
CLC
ADC #$05
CMP #$19
BNE RankNewScore_StoreRank ; Store current qualifying rank
BEQ RankNewScore_NotRanked ; Failed to beat 5th place: unranked
RankNewScore_IsLowerOrEqual:
; Candidate score is greater than or equal to this table entry:
JSR AdvanceToNextScoreEntry ; Advance pointer to check next higher table entry
DEY
DEC CurrentRankCounter ; Decrement rank counter (e.g., from 5th to 4th)
BNE RankNewScore_CheckLoop ; Loop to test against higher entry
RankNewScore_StoreRank:
LDA CurrentRankCounter
LDY #$05
BNE RankNewScore_FinalizeStatus
RankNewScore_NotRanked:
LDA #$FF ; $FF = Candidate score did not rank
LDY #$05
RankNewScore_FinalizeStatus:
STA (OrigArgRamLo),Y ; Return rank result at Argument + $05
JSR FormatAllScoresForDisplay ; Render formatted table into display buffer
LDX #$07
LDY #$53
BIT ArgGameID1
BMI e30A9 ; Custom alignment flag if Bit 7 of GameID1 is set
INY
e30A9:
; Pack the 8 digit characters of the formatted 1st place score back into
; BCD (spaces count as 0) and return them at Argument + $0F..$12
LDA (OrigArgRamLo),Y
CMP #$1D ; Watkins Space ($1D)
BNE e30B1
LDA #$21 ; Watkins Digit '0' ($21)
e30B1:
SEC
SBC #$21
ASL
ASL
ASL
ASL
INY
DEX
PHA
LDA (OrigArgRamLo),Y
CMP #$1D
BEQ e30C9
PLA
CLC
ADC (OrigArgRamLo),Y
SEC
SBC #$21
PHA
e30C9:
INY
DEX
BPL e30A9
LDY #$12
e30CF:
PLA
STA (OrigArgRamLo),Y ; Store BCD top score to Argument + $0F..$12
DEY
CPY #$0F
BPL e30CF
LDA #$00
RTS
; ==============================================================================
; Function: ShowHighScores / HSCATRCT (Vector @ $3FFA)
; Purpose: Attract mode screen display. Builds the MARIA display lists in
; Page N+1 and shows the table for the one game/difficulty given.
; Inputs: Y = Page number of Argument Block
; Outputs: A = $00 after the hold, or $FF at once (no hold, DMA left off) if
; the game has no table
; ==============================================================================
ShowHighScores:
LDA #$FF ; Function ID: ShowHighScores ($FF)
JSR HSCInitRoutine
JSR BuildDisplayLists ; Construct MARIA display list structure
e30E2:
DEC CartRAMEntryID
DEC CartRAMEntryID ; Entry ID -2: $FF->$FD, $00->$FE, $01->$FF
; (negative = attract layout)
e30E8:
JSR FormatAllScoresForDisplay ; Unpack initials and format decimal scores
JSR e3FD8 ; Reset DLL write pointers to Page N+1 : $87
e30EE:
LDA ArgLogoDllHi
BNE e30FC
; Caller did not provide custom logo DLL: use the text game-name header
LDA #<DllDisplayGameNameHeaderLut
LDY #>DllDisplayGameNameHeaderLut
JSR SetupDLL ; Build logo DLL in Page N+1
JMP e3109
e30FC:
LDA #$41
LDY ArgDifficultyNameHi
BNE e3104
LDA #$4F
e3104:
STA zA8
JSR e36DE ; Decode caller's custom logo DLL
e3109:
LDA ArgDifficultyNameHi
BEQ e3114
; Decode and display difficulty text header
LDA #<DllDisplayGameDifficultyLut
LDY #>DllDisplayGameDifficultyLut
JSR SetupDLL
e3114:
LDY #>DllDisplayFiveScoresLut
LDA CartRAMEntryID
BPL e311F ; Branch if qualifying score entry
LDA #<DllDisplayFiveScoresLut ; Attract layout: five-score table ($3D3D)
BNE e3121
e311F:
LDA #<e3D31 ; Entry layout: CONGRATULATIONS/PLAYER n + table ($3D31)
e3121:
JSR SetupDllPlusSome ; Finalize DLL, enable MARIA DMA ($4B)
LDA #$00
RTS
; ==============================================================================
; Function: EnterHighScore_SuppliedInitials / HSCENTER2 (Vector @ $3FF4)
; Purpose: Score entry with caller-supplied initials (argument +$0F..+$11,
; copied to z8F, z90, z91). No interactive initials entry, but the
; congratulations table is still shown for the hold time.
; ==============================================================================
EnterHighScore_SuppliedInitials:
LDA #$01 ; Function ID: HSCENTER2
BNE EnterHighScore_Shared
; ==============================================================================
; Function: EnterHighScore / HSCENTER (Vector @ $3FFD)
; Purpose: Interactive score entry. Evaluates candidate score rank, runs the
; interactive initials entry screen, shifts existing scores down,
; and commits the new score and initials to Cart NVRAM.
; ==============================================================================
EnterHighScore:
LDA #$00 ; Function ID: HSCENTER
EnterHighScore_Shared:
JSR HSCInitRoutine
JSR BuildDisplayLists
JSR IsScoreNonZero
BEQ e30E2 ; If score is 0, skip entry and show table
LDY #$13 ; Back up lowest score (5th place, 5 bytes)
LDX #$00
e313C:
INY
LDA (GameScoreTableLo),Y
STA CartRAMSavedEntry,X ; Stash 5th place entry into temporary buffer
INX
CPY #$18
BNE e313C
LDA #$05
STA CurrentRankCounter ; Start search at 5th place
e314B:
LDX #$02 ; Compare 3-byte binary score (MSB to LSB)
e314D:
LDA (GameScoreTableLo),Y
CMP zAD,X
BCC EnterHighScore_CheckNextRank
BNE EnterHighScore_RankFound
DEY
DEX
BPL e314D
BMI EnterHighScore_CheckNextRank
EnterHighScore_RankFound:
JSR AdvanceToNextScoreEntry
TYA
CLC
ADC #$05
CMP #$19
BNE e3173
JMP e30E2 ; Score does not rank; show table and exit
EnterHighScore_CheckNextRank:
JSR AdvanceToNextScoreEntry
DEY
DEC CurrentRankCounter
BNE e314B
INY
TYA
e3173:
STA zB4 ; zB4 = Target slot insertion byte offset
LDY #$14
LDX #$18
STX zAA
EnterHighScore_ShiftScoresDown:
; Shift all lower-ranking scores down by 5 bytes to open up the target slot
CPY zB4
BEQ EnterHighScore_InsertNewScore
DEY
TYA
TAX
LDA (GameScoreTableLo),Y
LDY zAA
STA (GameScoreTableLo),Y
DEC zAA
TXA
TAY
JMP EnterHighScore_ShiftScoresDown
EnterHighScore_InsertNewScore:
; Insert the new 3-byte binary score into the opened slot
LDY zAA
LDX #$02
e3193:
LDA zAD,X
STA (GameScoreTableLo),Y
DEY
DEX
BPL e3193
JSR e30E8 ; Format display buffer with updated table
LDA CurrentRankCounter
TAY
TAX
LDA #$F4
e31A4:
CLC
ADC #$0C
DEY
BPL e31A4
CLC
ADC #$50
STA z98 ; z98 = Text line buffer pointer
LDA CartRAMEntryID
CMP #$01 ; HSCENTER2?
BNE e31C8
; HSCENTER2 path: Copy pre-provided initials directly from z8F-z91
LDY #$00
LDA z8F
STA (z98),Y
INY
LDA z90
STA (z98),Y
INY
LDA z91
STA (z98),Y
BPL EncodeAndSaveInitialsToTable ; Initial 3 with bit 7 set falls into
; interactive entry below
e31C8:
; Interactive path: Launch joystick initials entry screen
LDA #$EC
e31CA:
CLC
ADC #$14
DEX
BPL e31CA
CLC
ADC #$31
TAX
JSR e3242 ; Run interactive initials input loop
; ==============================================================================
; Routine: EncodeAndSaveInitialsToTable
; Purpose: Bit-packs three 5-bit Watkins initial characters into 2 bytes and
; commits them to Cart NVRAM at the target score slot. Characters are
; not masked to 5 bits. Also reached after a cancelled entry, where it
; re-packs the restored entry's own initials.
; Bitfield Encoding:
; Byte 0: [C1.4 C1.3 C1.2 C0.4 C0.3 C0.2 C0.1 C0.0]
; Byte 1: [C1.1 C1.0 0 C2.4 C2.3 C2.2 C2.1 C2.0]
; ==============================================================================
EncodeAndSaveInitialsToTable:
LDY #$01
LDA (z98),Y ; Load Character 1
AND #$1C ; Extract bits 4, 3, 2
ASL
ASL
ASL ; Shift to bits 7, 6, 5
DEY
ORA (z98),Y ; Combine with Character 0 (bits 4..0)
PHA ; Stash packed Byte 0
INY
LDA (z98),Y ; Load Character 1 again
AND #$03 ; Extract bits 1, 0
CLC
ROR
ROR
ROR ; Rotate bits 1, 0 into bits 7, 6
INY
ORA (z98),Y ; Combine with Character 2 (bits 4..0)
LDY zB4
INY
STA (GameScoreTableLo),Y ; Store packed Byte 1 to NVRAM (Offset +1)
DEY
PLA
STA (GameScoreTableLo),Y ; Store packed Byte 0 to NVRAM (Offset +0)
LDA #$01
RTS
;
; Sets cursor character attribute (OR with $80)
;
e31FC:
LDA (z9C,X)
ORA #$80
STA (z9C,X)
RTS
;
; Resets input repeat throttle and cursor blink timers
;
e3203:
JSR e3AF9
LDA #$0A ; Throttle repeat timer = 10 frames
STA zB7
LDA #$1E ; Cursor blink timer = 30 frames
STA zB3
RTS
;
; Configures display list address pointers for interactive character entry
;
e320F:
JSR e3203
LDY z97
STX z9A
STY z9B
INX
STX z9C
STY z9D
INX
INX
STX z9E
STY z9F
RTS
; ==============================================================================
; Routine: WaitVBlankAndTickIdleTimer
; Purpose: Waits for vertical blank and ticks the two-stage idle timer.
; When it expires (233 x 233 frames), runs the screen saver.
; ==============================================================================
WaitVBlankAndTickIdleTimer:
JSR WaitForVBlank
TickIdleTimer:
DEC IdleTimerFine ; Decrement inner loop counter
BNE TickIdleTimer_Exit
LDA #$E9 ; Reload inner loop counter
STA IdleTimerFine
DEC IdleTimerCoarse ; Decrement outer loop counter
BNE TickIdleTimer_Exit
STA IdleTimerCoarse
JSR ScreenSaverWaitForInput ; Blank the screen until any input
TickIdleTimer_Exit:
RTS
;
; Stores current character and replaces buffer with temporary space ($1D)
;
e3239:
LDA (z98),Y
STA (z9A,X)
LDA #$1D ; Watkins Space character
STA (z98),Y
RTS
; ==============================================================================
; Routine: e3242
; Purpose: Interactive Initials Entry Input Loop. UP/RIGHT = next character,
; DOWN/LEFT = previous (A-Z, _ . - space ' END BACKSPACE; no digits),
; FIRE (on release) to confirm. Position 3 offers only END/BACKSPACE.
; RESET/SELECT finish at the current position; finishing at
; position 0 cancels and restores the table.
; ==============================================================================
e3242:
JSR e320F
LDX #$00
LDY #$00
LDA #$1F ; Cursor DL palette/width byte: palette 0, width 1
STA (z9C,X)
e324D:
LDA #$00
STA zAA ; Nothing changed yet at this position
LDA #$1F ; Position 3 starts on END (solid block)
CPY #$03
BEQ e325C
JSR e3239
LDA (z9A,X)
e325C:
STA (z9A,X)
e325E:
STY zBB
JSR e397C ; Read joystick input
CMP #$F0 ; Check if direction is centered
BEQ e326A
JSR e3AF9 ; Reset throttle timer on movement
e326A:
TYA
BMI e3270 ; Branch if fire button is NOT pressed
JMP e3318 ; Fire button pressed: confirm character
e3270:
LDY zBB
JSR WaitForVBlank
LDA #$03
AND SWCHB ; Check Reset / Select console switches
CMP #$03
BEQ e3281
JMP e334E ; Console switch pressed: finish entry here
e3281:
TYA
PHA
JSR e36DB ; Call caller's sound update callback
PLA
TAY
LDX #$00
JSR TickIdleTimer
DEC zB7 ; Decrement throttle timer
BNE e3300
LDA #$0A
STA zB7
JSR e3974
AND #$90
CMP #$90
BNE e32CD
JSR e3974
AND #$60
CMP #$60
BEQ e3300
; Joystick DOWN/LEFT: previous character
LDA (z9A,X)
CPY #$03
BNE e32B1
CMP #$1F
BEQ e32C3
e32B1:
SEC
SBC #$01
STA (z9A,X)
PHP
LDX #$01
STX zAA
LDX #$00
PLP
BPL e330E
TYA
BEQ e32C7
e32C3:
LDA #$20
BNE e32C9
e32C7:
LDA #$1F
e32C9:
STA (z9A,X)
BNE e330E
e32CD:
; Joystick UP/RIGHT: next character
LDA (z9A,X)
CMP #$1A ; '_' with nothing changed yet goes straight to 'A'
BNE e32D9
LDX zAA
BNE e32D9
LDA #$FF
e32D9:
LDX #$01
STA zAA
LDX #$00
CLC
ADC #$01
CMP #$21
BEQ e32F1
CMP #$20
BNE e32FB
TYA
BEQ e32FB
LDA #$20
BNE e32FB
e32F1:
CPY #$03
BNE e32F9
LDA #$1F
BNE e32FB
e32F9:
LDA #$00
e32FB:
STA (z9A,X)
JMP e330E
e3300:
; Handle cursor blink
DEC zB3
BNE e3315
LDX #$00
LDA (z9C,X)
EOR #$80 ; Toggle cursor attribute bit 7
STA (z9C,X)
BNE e3311
e330E:
JSR e31FC
e3311:
LDA #$1E
STA zB3
e3315:
JMP e325E
e3318:
; Fire button pressed: Handle character confirmation or backspace
JSR e3AF0 ; Wait for fire button release
LDY zBB
JSR WaitForVBlank
JSR e31FC
LDA (z9A,X)
CMP #$20 ; Left arrow (BACKSPACE)?
BNE e333D
CPY #$03
BEQ e332F
STA (z98),Y ; Arrow glyph stays in the position left behind
e332F:
DEY ; Backspace: move left
LDA (z9E,X)
SEC
SBC #$04 ; Move cursor DL X-coordinate left
STA (z9E,X)
SEC
BCC e333D
JMP e324D
e333D:
CMP #$1F ; Solid block (END)?
BEQ e334E ; Terminate entry
STA (z98),Y ; Store selected character into initials buffer
INY ; Advance to next initial character
LDA (z9E,X)
CLC
ADC #$04 ; Move cursor DL X-coordinate right
STA (z9E,X)
JMP e324D
e334E:
; Finish initials entry
LDA #$00
STA (z9C,X) ; Zero byte 1 ends the DL here: cursor removed
TYA
BNE e336E
; Initials entry was cancelled on first character: revert score table insertion
LDA zB4
STA zAA
CLC
ADC #$05
TAX
e335D:
TAY
CPY #$19
BEQ e337A
LDA (GameScoreTableLo),Y
LDY zAA
INC zAA
STA (GameScoreTableLo),Y
INX
TXA
BNE e335D
e336E:
; Pad remaining initial slots with spaces ($1D)
CPY #$03
BPL e3379
LDA #$1D
STA (z98),Y
INY
BNE e336E
e3379:
RTS
;
; Restores backed-up 5th place entry from CartRAMSavedEntry if entry cancelled.
; A table created by this call stays in the directory.
;
e337A:
LDX #$04
e337C:
DEY
LDA CartRAMSavedEntry,X
STA (GameScoreTableLo),Y
DEX
BPL e337C
JSR FormatAllScoresForDisplay
RTS
; ==============================================================================
; Routine: e3389
; Purpose: Interactive Owner Name entry loop for cartridge personalization.
; Up to 32 characters (limit in zA8); END finishes and is stored after
; the last character. No console abort and no sound callback.
; ==============================================================================
e3389:
JSR e320F
LDA z97
LDX #$18
STX zA0
STA zA1
INX
STX zA2
STA zA3
LDX #$00
TXA
TAY
STA (z98),Y
LDA #$1F
STA (z9C,X)
e33A3:
JSR e3239
e33A6:
STY zBB
JSR e397C
CMP #$F0
BEQ e33B2
JSR e3AF9
e33B2:
TYA
BMI e33B8
JMP e3420
e33B8:
LDY zBB
JSR WaitVBlankAndTickIdleTimer
DEC zB7
BNE e3408
LDA #$0A
STA zB7
JSR e3974
AND #$90
CMP #$90
BNE e33ED
JSR e3974
AND #$60
CMP #$60
BEQ e3408
LDA (z9A,X)
SEC
SBC #$01
STA (z9A,X)
BPL e3416
TYA
BEQ e33E7
LDA #$20
BNE e33E9
e33E7:
LDA #$1F
e33E9:
STA (z9A,X)
BNE e3416
e33ED:
LDA (z9A,X)
CLC
ADC #$01
CMP #$21
BEQ e3401
CMP #$20
BNE e3403
TYA
BEQ e3403
LDA #$20
BNE e3403
e3401:
LDA #$00
e3403:
STA (z9A,X)
JMP e3416
e3408:
DEC zB3
BNE e341D
LDX #$00
LDA (z9C,X)
EOR #$80
STA (z9C,X)
BNE e3419
e3416:
JSR e31FC
e3419:
LDA #$1E
STA zB3
e341D:
JMP e33A6
e3420:
JSR e3AF0
LDY zBB
JSR WaitForVBlank
JSR e31FC
LDA (z9A,X)
CMP #$20
BNE e344C
STA (z98),Y
DEY
LDA (z9E,X)
SEC
SBC #$02
STA (z9E,X)
LDA (zA0,X)
CLC
ADC #$01
STA (zA0,X)
LDA (zA2,X)
CLC
ADC #$02
STA (zA2,X)
JMP e33A3
e344C:
STA (z98),Y
CMP #$1F
BEQ e3471
INY
CPY zA8
BEQ e3471
STA (z98),Y
LDA (z9E,X)
CLC
ADC #$02
STA (z9E,X)
LDA (zA0,X)
SEC
SBC #$01
STA (zA0,X)
LDA (zA2,X)
SEC
SBC #$02
STA (zA2,X)
JMP e33A3
e3471:
LDA #$00
STA (z9C,X)
STY CartRAMOwnerNameLength
TYA
BNE e3482
LDA #$1D
STA (z98),Y
INC CartRAMOwnerNameLength
e3482:
RTS
; ==============================================================================
; Routine: HSCInitRoutine
; Purpose: Master context initialization for the High Score Cartridge.
; 1. Stashes caller's ZP $94/$95 into NVRAM ($1000-$1001).
; 2. Preserves caller's ZP $80-$BC to UserRAM + $13..$4F (61 bytes).
; 3. Copies caller's Argument Block ($00-$11) into ZP $80-$91.
; 4. Verifies Cart NVRAM magic bytes ($68,$83,$AA,$55,$9C). If corrupt,
; initializes directory structures and owner personalization.
; 5. Searches 69-slot directory for Game ID + Difficulty match.
; 6. Implements LRU aging policy and allocates score table pointer.
; Inputs: A = Calling Function ID
; Y = Page number of Caller Argument Block (Page N)
; ==============================================================================
HSCInitRoutine:
STA CartRAMEntryID ; Store entry ID for routing
SEI ; Disable interrupts
CLD ; Clear decimal mode
CMP #$02 ; HSCSTAT
BEQ HSCInit_SkipDmaOff
CMP #$EF ; HSCCPYFM
BEQ HSCInit_SkipDmaOff
CMP #$04 ; HSCCPYTO
BEQ HSCInit_SkipDmaOff
LDA #$7F ; Disable MARIA DMA
STA CTRL
LDA #$00 ; Mute TIA audio channels
STA AUDV0
STA AUDV1
HSCInit_SkipDmaOff:
LDA $94
STA CartRAMSaveZ94 ; Preserve caller ZP $94
LDA $95
STA CartRAMSaveZ95 ; Preserve caller ZP $95
LDX #$00 ; Low byte is strictly $00
STX OrigArgRamLo ; OrigArgRamLo = $94
STY OrigArgRamHi ; OrigArgRamHi = $95 (Page N)
LDY #$13
LDX #$3C
HSCInit_BackupZpToUserRam:
; Back up ZP $80-$BC (61 bytes) into (OrigArgRamLo) + $13..$4F
LDA ArgGameID1,X
STA (OrigArgRamLo),Y
INY
DEX
BPL HSCInit_BackupZpToUserRam
LDA OrigArgRamLo
LDY OrigArgRamHi
STA z96
INY
STY z97 ; z96/z97 points to Page N + 1 (DL Workspace)
LDA SWCHB ; Read RIOT console switches
STA zSWCHBSave ; Stash for restoration on exit
LDA #$FF
STA SWCHB ; Set Port B pins high
LDY #$11
HSCInit_CopyArgsToZp:
; Copy 18-byte Argument Block into zero-page $80-$91
LDA (OrigArgRamLo),Y
STA ArgGameID1,Y
DEY
BPL HSCInit_CopyArgsToZp
LDA ArgGameDifficulty
AND #$03
STA PlayerController ; Extract player index (bits 1..0), before the
; default set below can replace the arguments
LDA ArgLogoDllHi
ORA ArgGameNameHi
BNE e34EE ; Skip if custom name or logo provided
LDY #$0E
HSCInit_SetDefaultArgs:
; Load default argument set (bytes $00-$0E) if no name or logo provided
LDA e39EF,Y
STA ArgGameID1,Y
DEY
BPL HSCInit_SetDefaultArgs
e34EE:
; Stack manipulation: Pushes $3C, $2B beneath the JSR return address,
; so the vector body's final RTS continues at $3C2C (display hold).
PLA
TAX
PLA
TAY
LDA #$3C
PHA
LDA #$2B
PHA
TYA
PHA
TXA
PHA
LDA CartRAMEntryID
CMP #$02 ; HSCSTAT
BEQ e350E
CMP #$EF ; HSCCPYFM
BEQ e350E
CMP #$04 ; HSCCPYTO
BEQ e350E
JSR HSCSETRS2 ; Set font palette and CHARBASE
e350E:
LDA #$00
STA zAB
HSCInit_CheckRamMagic:
; Check NVRAM initialization signature ($68, $83, $AA, $55, $9C)
LDA #$68
CMP CartRAMMagic1
BNE HSCInit_RamIsUninitialized
LDA #$83
CMP CartRAMMagic2
BNE HSCInit_RamIsUninitialized
LDA #$AA
CMP CartRAMMagic3
BNE HSCInit_RamIsUninitialized
LDA #$55
CMP CartRAMMagic4
BNE HSCInit_RamIsUninitialized
LDA #$9C
CMP CartRAMMagic5
BEQ HSCInit_RamInitialized
HSCInit_RamIsUninitialized:
LDA zAB
INC zAB
TAX
BEQ HSCInit_CheckRamMagic
; NVRAM signature missing: format directory tables
LDA #$68
STA CartRAMMagic1
LDA #$83
STA CartRAMMagic2
LDA #$AA
STA CartRAMMagic3
LDA #$55
STA CartRAMMagic4
LDA #$9C
STA CartRAMMagic5
JSR e3FCF ; Initialize all 69 directory slots to $7F
LDA CartRAMEntryID
CMP #$EF ; HSCCPYFM
BEQ HSCInit_RamInitialized
CMP #$04 ; HSCCPYTO
BEQ HSCInit_RamInitialized
JSR e3C95 ; Launch cart personalization setup
; (leaves PlayerController = 0 for the rest of the call)
JSR DisableMariaDMA
HSCInit_RamInitialized:
JSR e3A81
LDA #$00
STA zAA
; Search backwards through 69 directory slots ($44 down to $00)
LDX #$45
HSCInit_FindGameInTable:
DEX
BMI HSCInit_CheckIfNewGameEntryNeeded ; Not found: allocate new slot
LDA zAA
CMP CartRAMGameAgeTable,X
BPL e3583
LDA CartRAMGameAgeTable,X
STA zAA
STX zB6 ; zB6 = Oldest LRU slot index candidate
; (left unset if no slot has an age above 0)
e3583:
LDA ArgGameID1
CMP CartRAMGameID1Table,X
BNE HSCInit_FindGameInTable
LDA ArgGameID2
CMP CartRAMGameID2Table,X
BNE HSCInit_FindGameInTable
LDA ArgGameDifficulty
AND #$FC
CMP CartRAMGameDifficultyTable,X
BNE HSCInit_FindGameInTable
; Matching Game ID and Difficulty found!
STX zB6
LDA CartRAMGameAgeTable,X
STA zAA
HSCInit_SetupScoreTablePointers:
TXA
PHA
LDA CartRAMEntryID
CMP #$02 ; HSCSTAT
BEQ e35BE
; Update LRU Aging Indices: active slot is set to 0, then aged along with
; the others below, so it ends at 1 (Most Recently Used)
LDA #$00
STA CartRAMGameAgeTable,X
LDX #$44
HSCInit_UpdateGameAges:
LDA CartRAMGameAgeTable,X
CMP zAA
BPL e35BB
INC CartRAMGameAgeTable,X ; Age every slot younger than the old age
e35BB:
DEX
BPL HSCInit_UpdateGameAges
e35BE:
; Calculate NVRAM score block address: TableAddress = $113D + (SlotIndex * 25)
PLA
LDY #$00
JSR Setupz92WithAY
ASL
JSR e3A2C
JSR Addz92ToAYStoreInz92
JSR e3A2C
JSR e3A2C
JSR Addz92ToAYStoreInz92
LDA #$3D
LDY #$11
JSR Addz92ToAYStoreInAY ; Add base $113D
STA GameScoreTableLo ; GameScoreTableLo/Hi = 25-byte score block
STY GameScoreTableHi
RTS
;
; Allocates new slot if game entry not found
;
HSCInit_CheckIfNewGameEntryNeeded:
LDY CartRAMEntryID
BPL HSCInit_HandleNewGameEntry
; Negative entry ID (HSCATRCT $FF, HSCCPYFM $EF): no table. A negative
; hold time skips the hold; dropping the return address skips the vector
; body, so RTS goes straight to $3C2C and the normal restore/exit.
STY ArgHSDisplayTime
PLA
PLA
TYA
RTS
HSCInit_HandleNewGameEntry:
CPY #$02 ; Status check only?
BNE HSCInit_CreateNewGameEntry
LDA #$00
STA GameScoreTableHi ; Flag to HSCSTAT that table does not exist
RTS
HSCInit_CreateNewGameEntry:
; Allocate oldest LRU slot index (in zB6) to new game
LDX zB6
LDA ArgGameID1
STA CartRAMGameID1Table,X
LDA ArgGameID2
STA CartRAMGameID2Table,X
LDA ArgGameDifficulty
AND #$FC
STA CartRAMGameDifficultyTable,X
JSR HSCInit_SetupScoreTablePointers
LDY #$18 ; Initialize 5 entries with default score 0
HSCInit_InitNewScoreTableLoop:
LDX #$02
LDA #$00
HSCInit_ClearScoreBytes:
STA (GameScoreTableLo),Y ; Clear score bytes (Offset +2, +3, +4)
DEY
DEX
BPL HSCInit_ClearScoreBytes
HSCInit_SetDefaultInitials:
; Set default initials to "___" (Underscore x3: packed as $DA, $9A)
LDA #$9A ; Packed Initials Byte 1
STA (GameScoreTableLo),Y
DEY
LDA #$DA ; Packed Initials Byte 0
STA (GameScoreTableLo),Y
DEY
BPL HSCInit_InitNewScoreTableLoop
RTS
; ==============================================================================
; Routine: BuildDisplayLists
; Purpose: Constructs the MARIA Display Lists (DL) and Display List Lists (DLL)
; in Page N+1 to render high-score tables and headers.
; ==============================================================================
BuildDisplayLists:
LDY #$86
e3625:
LDX #$13
e3627:
LDA e3E4F,X
STA (z96),Y
DEY
DEX
BPL e3627
TYA
BPL e3625
LDY #$22
e3635:
LDA e3E2C,Y
CMP #$FD
BNE e363E
LDA OrigArgRamHi
e363E:
STA (z96),Y
DEY
BPL e3635
LDY #$1F
SEC
LDA e3E4B
SBC CartRAMOwnerNameLength
STA (z96),Y
INY
SEC
LDA e3E4C
SBC CartRAMOwnerNameLength
SEC
SBC CartRAMOwnerNameLength
STA (z96),Y
LDA #$01
LDY #$23
JSR e3DE7
LDA OrigArgRamHi
LDY #$29
STA (z96),Y
LDA #$00
JSR e3DE7
LDY #$2E
STA (z96),Y
JSR e3DE9
LDA #$0C
LDY #$27
JSR e3DE7
LDY #$2C
JSR e3DE9
LDY #$24
LDA #$9F
STA (z96),Y
LDY #$2A
LDA #$97
STA (z96),Y
LDY #$2F
LDA #$9D
STA (z96),Y
LDA #$8C
LDY OrigArgRamHi
STY z99
JSR Setupz92WithAY
LDY #$19
e369E:
LDA (ArgGameNameLo),Y
STA (z92),Y
DEY
BPL e369E
LDA #$A6
STA z92
LDY #$19
e36AB:
LDA (ArgDifficultyNameLo),Y
STA (z92),Y
DEY
BPL e36AB
LDA #$C0
STA z92
LDY #$18
LDA e3F2C,Y
CLC
ADC PlayerController
STA (z92),Y
DEY
e36C1:
LDA e3F2C,Y
STA (z92),Y
DEY
BPL e36C1
LDA #$DA
LDY OrigArgRamHi
JSR Setupz92WithAY
LDY #$1F
e36D2:
LDA CartRAMOwnerName,Y
STA (z92),Y
DEY
BPL e36D2
RTS
; ==============================================================================
; Routine: e36DB
; Purpose: Calls the caller's sound update callback subroutine.
; ==============================================================================
e36DB:
JMP (ArgSoundRoutineLo)
;
; Game Logo DLL Decompressor: copies the caller's 3-byte DLL entries verbatim
; until the zone height total reaches zA8 ($41 or $4F lines). A DLI on the
; first entry sets the DLI flag ($40) for SetupDLL.
;
e36DE:
LDA #$00
TAY
TAX
e36E2:
LDA (ArgLogoDllLo),Y
BPL e36F0
CPY #$00
BNE e36F0
LDA #$40
STA IdleTimerFine
LDA (ArgLogoDllLo),Y
e36F0:
STA (zA4,X)
PHA
INY
INC zA4
LDA (ArgLogoDllLo),Y
STA (zA4,X)
INY
INC zA4
LDA (ArgLogoDllLo),Y
STA (zA4,X)
PLA
JSR UpdateDllDecodeState
CMP zA8
BMI e36E2
RTS
; ==============================================================================
; Routine: UpdateDllDecodeState
; Purpose: Advances DLL generation write pointer and adds the zone height
; ((byte0 & $0F) + 1) to the running line total in zB2.
; ==============================================================================
UpdateDllDecodeState:
INY
INC zA4
SEC
AND #$0F
ADC zB2
STA zB2
UpdateDllDecodeState_Exit:
RTS
; ==============================================================================
; Routine: SetupDLL / SetupDllPlusSome
; Purpose: Decompresses encoded DLL byte stream into Page N+1 : $87 and enables
; MARIA DMA ($4B).
; ==============================================================================
SetupDLL: ; $3715
JSR Setupz92WithAY
LDY #$00
LDX #$00
e371C:
LDA (z92),Y ; Read encoded DLL byte
BEQ UpdateDllDecodeState_Exit ; 0 = End of DLL stream
BPL e3728
ASL IdleTimerFine ; DLI kept only on the first flagged entry
BNE e3728 ; after a logo whose first entry had a DLI
AND #$7F
e3728:
STA (zA4,X) ; Store DLL entry byte 1
JSR UpdateDllDecodeState
LDA z97
STA (zA4,X) ; Store DLL entry byte 2 (Page N + 1)
INC zA4
INY
LDA (z92),Y
STA (zA4,X) ; Store DLL entry byte 3
INC zA4
INY
BNE e371C
SetupDllPlusSome:
JSR SetupDLL
LDA #$F2
SEC
SBC zB2
PHP
LDA #$61
LDY #$3D
PLP
BPL SetupDllPlusSome
JSR WaitForVBlank
LDA z97
STA DPPH ; DPPH = Page N + 1
LDA #$87
STA DPPL ; DPPL = $87
LDA #$4B ; Enable MARIA DMA
STA CTRL
RTS
; ---------------------------------------------------------------
; Wait for 2 Vertical Blanks
; ---------------------------------------------------------------
WaitFor2VBlanks:
JSR WaitForVBlank
; Fall through...
; ---------------------------------------------------------------
; Wait for Vertical Blank: waits for the start of the next VBLANK
; (MSTAT bit 7 rising edge; if already in VBLANK, waits for the next)
; ---------------------------------------------------------------
WaitForVBlank:
WaitForVBlankOffLoop:
BIT MSTAT
BMI WaitForVBlankOffLoop
WaitForVBlankOnLoop:
BIT MSTAT
BPL WaitForVBlankOnLoop
RTS
; ==============================================================================
; Routine: FormatAllScoresForDisplay
; Purpose: Reads all 5 high-score entries from Cart NVRAM. For each entry:
; 1. Decodes 15-bit packed initials into 3 characters.
; 2. Converts 24-bit binary score into decimal characters with
; leading-zero suppression.
; 3. Writes formatted text into the display workspace.
; ==============================================================================
FormatAllScoresForDisplay:
LDY #$00
LDA #$50
STA z92
LDA OrigArgRamHi
STA z93 ; z92/z93 points to Page N : $50
e3773:
LDA (GameScoreTableLo),Y ; Load Packed Initials Byte 0
INY
PHA
LDA (GameScoreTableLo),Y ; Load Packed Initials Byte 1
INY
STY zAA
TAX
PLA
JSR e3AD6 ; Unpack initials: A=C0, X=C1, Y=C2
STY zAC
LDY #$00
STA (z92),Y ; Store Initial Character 0
INY
TXA
STA (z92),Y ; Store Initial Character 1
INY
LDA zAC
STA (z92),Y ; Store Initial Character 2
INY
LDA #$03
LDY #$00
JSR Addz92ToAYStoreInz92
LDY zAA
LDA (GameScoreTableLo),Y ; Load 24-bit binary score LSB
STA zAD
INY
LDA (GameScoreTableLo),Y ; Load 24-bit binary score MID
STA zAE
INY
LDA (GameScoreTableLo),Y ; Load 24-bit binary score MSB
STA zAF
INY
STY zAA
JSR e37BC ; Convert 24-bit binary to decimal characters
LDA #$09
LDY #$00
JSR Addz92ToAYStoreInz92
LDY zAA
CPY #$19
BNE e3773 ; Loop for all 5 score entries
RTS
; ==============================================================================
; Routine: e37BC
; Purpose: Converts 24-bit unsigned binary integer in zAD, zAE, zAF into decimal
; digits using successive subtractions against table e3EC7 (powers of 10).
; Replaces leading zeros with Watkins space ($1D).
; ==============================================================================
e37BC:
LDY #$00
STY zAB
STY zAC ; zAC = Leading zero suppression flag
LDA #$FD
PHA
LDA ArgGameID1
BPL e381B
PLA
e37CA:
LDA #$FF
STA zB1
e37CE:
INC zB1
LDA zAD
SEC
SBC e3EC7,Y ; Subtract power of 10 LSB
STA zAD
LDA zAE
SBC e3EC8,Y ; Subtract power of 10 MID
STA zAE
LDA zAF
SBC e3EC9,Y ; Subtract power of 10 MSB
STA zAF
BCS e37CE ; Loop until borrow occurs
; Add back power of 10 to restore remainder:
LDA zAD
CLC
ADC e3EC7,Y
STA zAD
LDA zAE
ADC e3EC8,Y
STA zAE
LDA zAF
ADC e3EC9,Y
STA zAF
TYA
PHA
LDA zB1
LDY zAB
CLC
ADC #$21 ; Convert remainder count to Watkins digit '0'-'9'
CPY #$08
BNE e380F
CMP #$21
BEQ e3821
e380F:
CMP #$21 ; Is digit '0'?
BEQ e381B
PHA
LDA #$80 ; Set flag: non-zero digit found (disable zero suppression)
STA zAC
PLA
BNE e3821
e381B:
BIT zAC ; Check if leading zero suppression active
BMI e3821
LDA #$1D ; Replace leading zero with Watkins space ($1D)
e3821:
STA (z92),Y ; Write digit character to buffer
INY
STY zAB
PLA
TAY
INY
INY
INY
CPY #$18
BNE e37CA ; Loop for all powers of 10
LDY zAB
CPY #$08
BNE e3839
LDA #$21
STA (z92),Y
e3839:
RTS
; ==============================================================================
; Routine: e383A
; Purpose: Cart Personalization and Clear High Scores Menu Navigation Logic.
; ==============================================================================
e383A:
JSR e3AF9
LDY #$03
JSR e3EF9
JSR e3EE8
LDY #$06
JSR e3EF9
JSR e3EE7
PLA
AND #$10
BNE e386C
LDA #$4B
LDX #$2A
CPX z92
BEQ e385F
LDA z92
SEC
SBC #$0B
e385F:
STA z92
JSR e3EDF
LDY #$04
JSR e3EDF
JMP e3BCC
e386C:
LDA #$2A
LDX #$4B
CPX z92
BEQ e385F
LDA z92
CLC
ADC #$0B
BNE e385F
e387B:
LDA z92
STA zAD
LDY #$03
JSR e3EF0
LDY #$06
JSR e3EF0
LDY #$00
LDA #$56
STA (z9A),Y
JSR e3203
LDA #$60
STA z92
JSR e3AF0
e3899:
JSR WaitVBlankAndTickIdleTimer
DEC zB7
BNE e38B0
LDA #$0A
STA zB7
JSR e397C
AND #$C0
CMP #$C0
BNE e38C0
TYA
BPL e38DE
e38B0:
DEC zB3
BNE e3899
LDA #$1E
STA zB3
LDY #$03
JSR e3EF3
JMP e3899
e38C0:
JSR e3AF9
LDY #$03
JSR e3EF9
LDA #$60
CMP z92
BNE e38D0
LDA #$5B
e38D0:
STA z92
JSR e3EF0
e38D5:
JSR e397C
CMP #$F0
BNE e38D5
BEQ e3899
e38DE:
LDA #$05
LDY #$00
STA (z9A),Y
JSR e3AF0
LDA z92
LDY zAD
STY z92
CMP #$60
BEQ e38F4
JMP e39C8
e38F4:
JMP e3BEB
; ---------------------------------------------------------------
; Font Bitmap Data - Scanline Row 6 ($3900 - $392B)
; Contains detection signature bytes at $3900 ($C6) and $3904 ($FE)
; ---------------------------------------------------------------
org $3900,$FF
FontDataStartAddr:
FontDataRow6:
dc.b $c6 ; HSC Signature Byte 1 ($3900)
dc.b $fc
dc.b $70
dc.b $f8
dc.b $fe ; HSC Signature Byte 2 ($3904)
dc.b $c0
dc.b $3e,$c6,$7e,$38,$ce,$fe
dc.b $c6,$c6,$38,$c0,$36,$ce
dc.b $7c,$18,$7c,$10,$6c,$c6
dc.b $30,$fe,$ff,$38,$00,$00
dc.b $00,$ff,$00,$38,$7e,$fe
dc.b $7c,$0c,$78,$38,$30,$38
dc.b $78,$30
; ==============================================================================
; Routine: ScreenSaverWaitForInput
; Purpose: Disables MARIA DMA and loops polling joysticks and console switches
; until user activity is detected. Re-enables DMA on exit.
; ==============================================================================
ScreenSaverWaitForInput:
JSR DisableMariaDMA ; Disable DMA before polling RIOT
LDA #$06
STA SWBCNT
LDA #$00
STA SWCHB
LDA #$FF
STA SWACNT
LDA #$11
STA SWCHA
LDA #$06
STA SWCHB
LDA #$00
STA SWACNT
ScreenSaverWaitForInput_SetPortBDir:
STA SWBCNT
ScreenSaverWaitForInput_Loop:
LDA SWCHA ; Check joysticks
CMP #$FF
BNE EnableMariaDMA
LDA INPT4 ; Check Player 1 fire button
BPL EnableMariaDMA
LDA INPT5 ; Check Player 2 fire button
BPL EnableMariaDMA
LDA SWCHB ; Check console switches (Reset/Select/Pause)
ScreenSaverWaitForInput_CheckSwitches:
AND #$0B
CMP #$0B
BEQ ScreenSaverWaitForInput_Loop
EnableMariaDMA:
LDA #$4B ; Enable MARIA DMA ($4B)
_SetMariaCtrlAndWaitForVBlank:
JSR WaitForVBlank
STA CTRL
RTS
; ------------------------------------------------------------------------------
; Routine: DisableMariaDMA
; Purpose: Disables MARIA DMA ($7F) cleanly at vertical blank.
; ------------------------------------------------------------------------------
DisableMariaDMA:
LDA #$7F
BNE _SetMariaCtrlAndWaitForVBlank
e3974:
STY zBB
JSR e397C
LDY zBB
RTS
; ==============================================================================
; Routine: e397C
; Purpose: Reads controller input based on ArgGameController configuration:
; $00/$01: Standard digital joystick (SWCHA, INPT4, INPT5)
; $02 : Custom controller driver callback at (ArgGameNameLo)
; ==============================================================================
e397C:
LDA ArgGameController
CMP #$02
BEQ e399C
LDA PlayerController
BNE e3990
LDA SWCHA
AND #$F0 ; Player 1 joystick directions
LDY INPT4 ; Player 1 fire button
JMP e39A5
e3990:
LDA SWCHA
ASL
ASL
ASL
ASL ; Player 2 joystick directions
LDY INPT5 ; Player 2 fire button
JMP e39A5
e399C:
LDX PlayerController
JSR e39A6 ; Call custom controller routine
AND #$F0
LDX #$00
e39A5:
RTS
;
; Indirect jump to caller's custom controller driver
;
e39A6:
JMP (ArgGameNameLo)
; ==============================================================================
; Function: HSCSETRS (Vector @ $3FEB)
; Purpose: Sets MARIA control, character base font bank, and palette registers
; (P0C2 - P4C2) to standard HSC configuration.
; ==============================================================================
HSCSETRS:
LDA #$4B ; DMA on
STA CTRL
HSCSETRS2:
LDX #$E0
LDA #$00
STA $40,X
INX
INX
LDY #$04
e39B7:
LDA e3D2C,Y
STA $40,X
INX
INX
INX
INX
DEY
BPL e39B7
LDA #>FontDataStartAddr ; Point CHARBASE to $3900 font bank
STA CHARBASE
RTS
; ==============================================================================
; Routine: e39C8
; Purpose: Dispatches menu actions: Clear Game, Clear All, Personalize, Exit.
; ==============================================================================
e39C8:
LDA #$2A
CMP z92
BNE e39D1
JSR e3FC4 ; Clear this game's high scores
e39D1:
LDA #$35
CMP z92
BNE e39DA
JSR e3FCF ; Clear all games from NVRAM
e39DA:
LDA #$40
CMP z92
BNE e39E6
JSR e3C95 ; Personalize cart
e39E3:
JMP e3B7F
e39E6:
LDA #$4B
CMP z92
BNE e39E3
JMP RestoreVarsAndRegsAndExit
e39EF:
; Default configuration template if game does not provide title/logo
dc.b $80,$01,$f1,$01,$79,$e9,$00,$1a,$4c,$00,$00,$00,$ff,$3c,$0a
; ---------------------------------------------------------------
; Font Bitmap Data - Scanline Row 5 ($3A00 - $3A2B)
; ---------------------------------------------------------------
org $3A00,$FF
FontDataRow5:
dc.b $c6,$c6,$dc,$cc,$c0,$c0
dc.b $e6,$c6,$18,$cc,$cc,$fe
dc.b $c6,$c6,$ee,$c0,$ec,$cc
dc.b $c6,$18,$ee,$38,$7c,$c6
dc.b $30,$c6,$00,$38,$00,$00
dc.b $00,$ff,$10,$ee,$18,$c0
dc.b $ce,$0c,$ee,$ee,$30,$ee
dc.b $0c,$00
;
; 16-bit shift left (Multiply A/Y by 2)
;
e3A2C:
ASL
PHA
TYA
ROL
TAY
PLA
RTS
;
; 16-bit Addition: Add z92/z93 to A/Y and return in A/Y
;
Addz92ToAYStoreInAY:
CLC
ADC z92
PHA
TYA
ADC z93
TAY
PLA
RTS
;
; 16-bit Addition: Add z92/z93 to A/Y and store result in z92/z93
;
Addz92ToAYStoreInz92:
JSR Addz92ToAYStoreInAY
Setupz92WithAY:
STA z92
STY z93
RTS
;
; 24-bit Binary Addition: Add zAD..zAF[Y] to zAD..zAF[X]
;
e3A45:
TXA
TAY
e3A47:
LDA zAD,X
CLC
ADC zAD,Y
STA zAD,X
LDA zAE,X
ADC zAE,Y
STA zAE,X
LDA zAF,X
ADC zAF,Y
STA zAF,X
RTS
; ==============================================================================
; Routine: e3A5E
; Purpose: Multiplies 24-bit binary integer in zAD, zAE, zAF by 10.
; ==============================================================================
e3A5E:
TYA
PHA
LDA zAD,X
STA zB0
LDA zAE,X
STA zB1
LDA zAF,X
STA zB2
JSR e3A45 ; Value * 2
JSR e3A45 ; Value * 4
LDY #$03
JSR e3A47 ; (Value * 4) + Original
JSR e3A45 ; ((Value * 4) + Original) * 2 = Value * 10
PLA
TAY
RTS
; Maximum allowable BCD score: 16,777,215 ($FFFFFF)
e3A7D:
dc.b $16,$77,$72,$15
e3A81:
dc.b $a0,$ff
; ==============================================================================
; Routine: e3A83 / e3AA8
; Purpose: Unpacks 4-byte packed BCD score from (ArgGameScoreLo) and converts
; it into a 24-bit unsigned binary integer stored in zAD, zAE, zAF.
; ==============================================================================
e3A83:
INY
CPY #$04
BEQ e3A9D
LDA e3A7D,Y
CMP (ArgGameScoreLo),Y
BMI e3A93 ; Clamp to 16,777,215 if exceeded (sign of the
; difference, so $97-$99 bytes are not clamped)
BEQ e3A83
BPL e3A9D
e3A93:
STA (ArgGameScoreLo),Y ; Clamped value is written back to the caller's score
INY
LDA e3A7D,Y
CPY #$04
BNE e3A93
e3A9D:
LDA #$00
STA zAD
STA zAE
STA zAF
TAY
LDX #$00
e3AA8:
JSR e3A5E ; Multiply accumulator by 10
LDA (ArgGameScoreLo),Y
PHA
AND #$F0 ; Extract high BCD nibble
LSR
LSR
LSR
LSR
CLC
ADC zAD
STA zAD
LDA zAE
ADC #$00 ; Carry out of zAE is not added into zAF
STA zAE
JSR e3A5E ; Multiply accumulator by 10
PLA
AND #$0F ; Extract low BCD nibble
CLC
ADC zAD
STA zAD
LDA zAE
ADC #$00
STA zAE
INY
CPY #$04
BNE e3AA8 ; Loop for all 4 BCD bytes (8 decimal digits)
RTS
; ==============================================================================
; Routine: e3AD6
; Purpose: Unpacks two 15-bit packed initials bytes into three 5-bit Watkins
; characters.
; Inputs: A = Packed Byte 0: [C1.4 C1.3 C1.2 C0.4 C0.3 C0.2 C0.1 C0.0]
; X = Packed Byte 1: [C1.1 C1.0 0 C2.4 C2.3 C2.2 C2.1 C2.0]
; Outputs: A = Character 0
; X = Character 1
; Y = Character 2
; ==============================================================================
e3AD6:
PHA
AND #$E0 ; Extract C1 bits 4..2 from Byte 0
LSR
LSR
LSR
STA zB1
TXA
AND #$1F ; Extract C2 bits 4..0 from Byte 1
TAY ; Y = Character 2
TXA
AND #$C0 ; Extract C1 bits 1..0 from Byte 1
CLC
ROL
ROL
ROL ; Rotate into bits 1..0
ORA zB1
TAX ; X = Character 1
PLA
AND #$1F ; A = Character 0
RTS
;
; Waits for vertical blank and confirms fire button release, then falls into
; e3AF9, which restarts the screen saver idle timers
;
e3AF0:
JSR WaitForVBlank
JSR e397C
TYA
BPL e3AF0
e3AF9:
LDA #$E9
STA IdleTimerFine
STA IdleTimerCoarse
RTS
; ---------------------------------------------------------------
; Font Bitmap Data - Scanline Row 4 ($3B00 - $3B2B)
; ---------------------------------------------------------------
org $3B00,$FF
FontDataRow4:
dc.b $fe,$c6,$c0,$c6,$c0,$c0
dc.b $c6,$c6,$18,$cc,$d8,$c0
dc.b $c6,$ce,$c6,$f8,$dc,$c8
dc.b $0e,$18,$c6,$6c,$d6,$6c
dc.b $30,$60,$00,$00,$00,$00
dc.b $00,$ff,$20,$e6,$18,$60
dc.b $04,$fe,$02,$c6,$30,$ee
dc.b $06,$10
;
; Console switch debounce polling
;
e3B2C:
LDA #$08
JSR e3C63
e3B31:
BIT SWCHB
BNE e3B3C
JSR WaitFor2VBlanks
BIT SWCHB
e3B3C:
RTS
e3B3D:
LDA #$08
e3B3F:
STA zB8
AND SWCHB
CMP zB8
e3B46:
RTS
e3B47:
JSR ScreenSaverWaitForInput
PHA
PHA
JSR e3C63
PLA
PLA
e3B51:
JSR e3B71
LDA #$00 ; TIA audio off
STA AUDV0
STA AUDV1
LDY #$E9
STY IdleTimerCoarse
e3B5E:
DEC IdleTimerCoarse
BEQ e3B47
e3B62:
DEC IdleTimerFine
BEQ e3B5E
JSR e3B2C
BNE e3B62
JSR e3B71
JMP e3C31
e3B71:
JSR e3B3D
BEQ e3B46
LDA #$0A
JSR e3B31
BNE e3B71
PLA
PLA
e3B7F:
LDA #$00
STA ArgGameController
STA PlayerController
JSR DisableMariaDMA
LDA #$8C
LDY OrigArgRamHi
JSR Setupz92WithAY
LDY #$54
e3B91:
LDA e3F6F,Y
STA (z92),Y
DEY
BPL e3B91
LDA #$23
INC z93
STA z92
LDY #$43
e3BA1:
LDA e3E83,Y
CMP #$FD
BNE e3BAA
LDA OrigArgRamHi
e3BAA:
STA (z92),Y
DEY
BPL e3BA1
JSR e3FD8
LDA #<e3DA4
LDY #>e3DA4
JSR SetupDllPlusSome
e3BB9:
LDA #$0A
JSR e3B3F
BNE e3BB9
JSR e320F
LDA #$4B
JSR Setupz92WithAY
LDA #$B6
STA z9A
e3BCC:
JSR WaitVBlankAndTickIdleTimer
DEC zB7
BNE e3BE3
LDA #$0A
STA zB7
JSR e397C
AND #$30
CMP #$30
BNE e3BF8
TYA
BPL e3BFC
e3BE3:
DEC zB3
BNE e3BCC
LDA #$1E
STA zB3
e3BEB:
LDY #$03
JSR e3EF3
LDY #$06
JSR e3EF3
JMP e3BCC
e3BF8:
PHA
JMP e383A
e3BFC:
JMP e387B
; ---------------------------------------------------------------
; Font Bitmap Data - Scanline Row 3 ($3C00 - $3C2B)
; ---------------------------------------------------------------
org $3C00,$FF
FontDataRow3:
dc.b $c6,$fc,$c0,$c6,$f8,$f8
dc.b $ce,$fe,$18,$0c,$f0,$c0
dc.b $d6,$de,$c6,$ce,$c6,$f8
dc.b $3c,$18,$c6,$c6,$d6,$38
dc.b $30,$30,$00,$00,$3c,$00
dc.b $10,$ff,$7c,$d6,$18,$38
dc.b $38,$cc,$0e,$dc,$18,$38
dc.b $76,$18
;
; Display hold: every vector except HSCSETRS returns through here. Holds for
; ArgHSDisplayTime x 60 frames; each frame waits for VBLANK, checks the exit
; inputs, checks PAUSE, then calls the sound callback.
;
e3C2C:
PHA
e3C2D:
DEC ArgHSDisplayTime
BMI RestoreVarsAndRegsAndExit
e3C31:
LDA #$3C
STA zA8
e3C35:
JSR e3B2C
BNE e3C3D
JMP e3B51
e3C3D:
JSR e36DB ; Call caller sound callback during the hold
DEC zA8
BNE e3C35
BEQ e3C2D
; ==============================================================================
; Routine: RestoreVarsAndRegsAndExit
; Purpose: Restores caller zero page $80-$BC from UserRAM + $13..$4F, restores
; $94/$95, restores console switches, and exits via RTS.
; ==============================================================================
RestoreVarsAndRegsAndExit:
LDA zSWCHBSave
STA SWCHB ; Restore RIOT console switch state
LDX #$3C
LDY #$13
e3C4F:
LDA (OrigArgRamLo),Y ; Restore ZP $80-$BC (61 bytes)
STA ArgGameID1,X
INY
DEX
BPL e3C4F
LDA CartRAMSaveZ94
STA OrigArgRamLo ; Restore caller ZP $94
LDA CartRAMSaveZ95
STA OrigArgRamHi ; Restore caller ZP $95
PLA
RTS
;
; Hold exit check. Level-sensitive: an input already held ends the hold on the
; first frame. P1 fire and any SWCHA bit are skipped for custom controllers.
;
e3C63:
JSR WaitForVBlank
PHA
LDA ArgGameController
CMP #$02
BEQ e3C78
BIT INPT4
BPL e3C8D
LDA SWCHA
CMP #$FF
BNE e3C8D
e3C78:
LDA SWCHB
AND #$03
EOR #$03
BNE e3C8D
JSR e397C
CMP #$F0
BNE e3C8D
TYA
BPL e3C8D
PLA
RTS
;
; User requested exit: pop stack and exit cleanly
;
e3C8D:
PLA
PLA
PLA
PLA
PLA
JMP RestoreVarsAndRegsAndExit
; ==============================================================================
; Routine: e3C95
; Purpose: Sets up and runs the Cartridge Personalization screen (Owner Name).
; ==============================================================================
e3C95:
JSR DisableMariaDMA
JSR HSCSETRS2
LDY OrigArgRamHi
LDA #$D1
JSR Setupz92WithAY
LDY #$28
e3CA4:
LDA e3F45,Y
STA (z92),Y
DEY
BPL e3CA4
LDA #$8C
STA z92
LDY #$22
e3CB2:
LDA e3E63,Y
CMP #$FD
BNE e3CBB
LDA OrigArgRamHi
e3CBB:
STA (z96),Y
DEY
BPL e3CB2
JSR e3FD8
LDA #<DllDisplayPersonalizeCartLut
LDY #>DllDisplayPersonalizeCartLut
JSR SetupDllPlusSome
LDY #$09
e3CCC:
STY CartRAMOwnerNameLength
DEY
e3CD0:
LDA e3DCC,Y
STA CartRAMOwnerName,Y
DEY
BPL e3CD0
LDA #$00
STA PlayerController
LDA #$20
STA zA8
LDA #$8C
STA z98
LDY OrigArgRamHi
STY z99
LDX #$1A
JSR e3389 ; Run owner name character input loop
LDY OrigArgRamHi
LDA #$8C
JSR Setupz92WithAY
LDY #$1F
e3CF7:
LDA (z92),Y
STA CartRAMOwnerName,Y
DEY
BPL e3CF7
RTS
; ---------------------------------------------------------------
; Font Bitmap Data - Scanline Row 2 ($3D00 - $3D2B)
; ---------------------------------------------------------------
org $3D00,$FF
FontDataRow2:
dc.b $6c,$c8,$c0,$c6,$c0,$c0
dc.b $c0,$c6,$18,$0c,$d0,$c0
dc.b $ee,$f6,$c6,$c6,$c6,$ce
dc.b $e0,$18,$c6,$c6,$c6,$38
dc.b $38,$18,$00,$00,$00,$00
dc.b $18,$ff,$20,$ce,$18,$0c
dc.b $04,$6c,$f8,$c0,$0c,$ee
dc.b $c6,$0c
;
; Palette configuration values (P0C2 - P4C2)
;
e3D2C:
dc.b $9d,$0a,$96,$98,$fa
e3D31:
; Encoded DLL data for compact score display
dc.b $8f,$61,$05
dc.b $06,$61,$00
dc.b $06,$61,$05
dc.b $06,$61,$07
DllDisplayFiveScoresLut:
; Encoded DLL data for standard five-score table display
dc.b $8f,$61,$05
dc.b $06,$61,$1c
ifnconst PALHACK
dc.b $06,$61,$05 ; 3D43
else
dc.b $0E,$61,$05 ; 3D43
endif
dc.b $06,$61,$23
ifnconst PALHACK
dc.b $06,$61,$05 ; 3D49
else
dc.b $0E,$61,$05 ; 3D49
endif
dc.b $06,$61,$37
ifnconst PALHACK
dc.b $06,$61,$05 ; 3D4F 0F
else
dc.b $0E,$61,$05 ; 3D4F 0F
endif
dc.b $06,$61,$4b
ifnconst PALHACK
dc.b $06,$61,$05 ; 3D55 0F
else
dc.b $0E,$61,$05 ; 3D55 0F
endif
dc.b $06,$61,$5f
ifnconst PALHACK
dc.b $06,$61,$05 ; 3D5B 0F
else
dc.b $0E,$61,$05 ; 3D5B 0F
endif
dc.b $06,$61,$73
ifnconst PALHACK
dc.b $06,$61,$05 ; 3D61 0F
else
dc.b $0F,$61,$05 ; 3D61 0F
endif
ifnconst PALHACK
dc.b $00
endif
DllDisplayGameNameHeaderLut:
; Encoded DLL data for the text game-name header (used when no logo)
ifnconst PALHACK
dc.b $09,$61,$05 ; 3D65 0F
else
dc.b $0F,$61,$05 ; 3D65 0F
endif
dc.b $0f,$61,$05
dc.b $0f,$61,$05
dc.b $06,$61,$0e
dc.b $0f,$61,$05
dc.b $00
ifconst PALHACK
dc.b $00
endif
DllDisplayGameDifficultyLut:
; Encoded DLL data for game difficulty header string
ifnconst PALHACK
dc.b $86,$61,$05 ; 3D75 8F
else
dc.b $8E,$61,$05 ; 3D75 8F
endif
dc.b $06,$61,$15
dc.b $00
DllDisplayPersonalizeCartLut:
; Encoded DLL data for cartridge personalization screen
dc.b $0f,$61,$05
dc.b $08,$61,$05
dc.b $0f,$61,$05
dc.b $0f,$61,$05
dc.b $06,$61,$05
dc.b $06,$61,$00
dc.b $0b,$61,$05
dc.b $06,$61,$07
dc.b $0b,$61,$05
dc.b $06,$61,$0e
dc.b $0f,$61,$05
dc.b $04,$61,$05
dc.b $06,$61,$15
dc.b $00
e3DA4:
; Encoded DLL data for menu display
dc.b $0f,$61,$05
dc.b $0f,$61,$05
dc.b $0f,$61,$05
dc.b $06,$61,$23
dc.b $0f,$61,$05
dc.b $0f,$61,$05
dc.b $06,$61,$2a
dc.b $0b,$61,$05
dc.b $06,$61,$35
dc.b $0b,$61,$05
dc.b $06,$61,$40
dc.b $0b,$61,$05
dc.b $06,$61,$4b
dc.b $00
; Default owner name "A T A R I" (9 bytes), written before name entry starts
e3DCC:
dc.b $00,$1d,$13,$1d,$00,$1d,$11,$1d,$08
; ==============================================================================
; Routine: AdvanceToNextScoreEntry
; Purpose: Advances pointer indices Y and X to the start of the next 3-byte
; binary score entry in Cart NVRAM.
; ==============================================================================
AdvanceToNextScoreEntry:
DEY
DEX
CPX #$FE
BNE AdvanceToNextScoreEntry
RTS
; ==============================================================================
; Routine: IsScoreNonZero
; Purpose: Checks if the 24-bit candidate binary score in zAD..zAF is non-zero.
; Outputs: Z flag set (1) if score is 0; Z flag clear (0) if score > 0.
; ==============================================================================
IsScoreNonZero:
LDA zAD
BNE IsScoreNonZero_Exit
LDA zAE
BNE IsScoreNonZero_Exit
LDA zAF
IsScoreNonZero_Exit:
RTS
;
; Builds repeated display list entries
;
e3DE7:
STA zB3
e3DE9:
LDA (z96),Y
LDX #$04
e3DED:
PHA
TYA
CLC
ADC #$14
TAY
PLA
CLC
ADC zB3
STA (z96),Y
DEX
BNE e3DED
RTS
; ---------------------------------------------------------------
; Font Bitmap Data - Scanline Row 1 ($3E00 - $3E2B)
; ---------------------------------------------------------------
org $3E00,$FF
FontDataRow1:
dc.b $78,$cc,$dc,$cc,$c0,$c0
dc.b $e0,$c6,$18,$0c,$c8,$c0
dc.b $c6,$e6,$ee,$ce,$ee,$ce
dc.b $e6,$7e,$c6,$c6,$c6,$6c
dc.b $6c,$cc,$00,$00,$00,$00
dc.b $0c,$ff,$10,$ee,$78,$c6
dc.b $ce,$3c,$c0,$e0,$ce,$ee
dc.b $ec,$cc
; Display list templates for screen formatting
e3E2C:
dc.b $c0,$60,$fd,$11,$30,$00
dc.b $00,$d1,$60,$fd,$18,$40
dc.b $00,$00,$8c,$60,$fd,$66
dc.b $1c,$00,$00,$a6,$60,$fd
dc.b $66,$1c,$00,$00,$da,$60
dc.b $fd
e3E4B:
dc.b $80
e3E4C:
dc.b $50,$00,$00
e3E4F:
dc.b $22,$5f,$39,$32,$53,$60
dc.b $60,$37,$3a,$50,$60,$60
dc.b $3d,$62,$00,$00,$39,$62
dc.b $00,$00
e3E63:
dc.b $da,$60,$fd,$15,$3a,$00
dc.b $00,$e5,$60,$fd,$0b,$26
dc.b $00,$00,$d1,$60,$fd,$17
dc.b $3e,$00,$00,$8c,$60,$fd
dc.b $1f,$4e,$00,$00,$39,$4e
dc.b $00,$00
e3E83:
dc.b $8c,$60,$fd,$83,$16,$00
dc.b $00,$a9,$60,$fd,$57,$18
dc.b $1f,$7f,$39,$46,$00,$00
dc.b $c1,$60,$fd,$57,$18,$1f
dc.b $7f,$39,$46,$00,$00,$b2
dc.b $60,$fd,$51,$00,$1f,$7f
dc.b $39,$46,$00,$00,$ca,$60
dc.b $fd,$59,$70,$1f,$7f,$39
dc.b $96,$00,$00,$d1,$60,$fd
dc.b $93
e3EBA:
dc.b $24,$de,$60,$fd,$7d,$60
dc.b $ca,$60,$fd,$3e,$74,$00
dc.b $00
; ==============================================================================
; Table: Powers of 10 Subtraction Constants (24-bit Little-Endian)
; Used by e37BC to convert 24-bit binary integers into decimal digits
; ==============================================================================
e3EC7:
dc.b $80 ; 10,000,000 ($989680) - LSB
e3EC8:
dc.b $96 ; 10,000,000 - MID
e3EC9:
dc.b $98 ; 10,000,000 - MSB
e3ECA:
dc.b $40,$42,$0f ; 1,000,000 ($0F4240)
dc.b $a0,$86,$01 ; 100,000 ($0186A0)
dc.b $10,$27,$00 ; 10,000 ($002710)
dc.b $e8,$03,$00 ; 1,000 ($0003E8)
dc.b $64,$00,$00 ; 100 ($000064)
dc.b $0a,$00,$00 ; 10 ($00000A)
dc.b $01,$00,$00 ; 1 ($000001)
;
; Display string attribute manipulation routines
;
e3EDF:
LDA (z92),Y
CLC
ADC #$50
e3EE4:
STA (z92),Y
RTS
e3EE7:
INY
e3EE8:
INY
LDA (z92),Y
SEC
SBC #$50
BPL e3EE4
e3EF0:
JSR e3EF9
e3EF3:
LDA (z92),Y
EOR #$40
BNE e3EE4
e3EF9:
dc.b $b1
e3EFA:
dc.b $92
e3EFB:
dc.b $09
e3EFC:
dc.b $40,$d0,$e5
; ---------------------------------------------------------------
; Font Bitmap Data - Scanline Row 0 ($3F00 - $3F2B)
; ---------------------------------------------------------------
org $3F00,$FF
FontDataRow0:
dc.b $10,$f8,$70,$f8,$fc,$fe
dc.b $3c,$c6,$7e,$7e,$cc,$c0
dc.b $82,$c6,$38,$f8,$38,$f8
dc.b $7c,$7e,$c6,$c6,$c6,$c6
dc.b $c6,$fe,$00,$00,$00,$00
dc.b $04,$ff,$00,$38,$38,$7c
dc.b $7c,$1c,$fe,$38,$fe,$38
dc.b $38,$f8
; ---------------------------------------------------------------
; Watkins Stored String Constants
; ---------------------------------------------------------------
e3F2C:
WATKINS "C"
WATKINS "O"
WATKINS "N"
WATKINS "G"
WATKINS "R"
WATKINS "A"
WATKINS "T"
WATKINS "U"
WATKINS "L"
WATKINS "A"
WATKINS "T"
WATKINS "I"
WATKINS "O"
WATKINS "N"
WATKINS "S"
dc.b $1D
dc.b $1D
WATKINS "P"
WATKINS "L"
WATKINS "A"
WATKINS "Y"
WATKINS "E"
WATKINS "R"
dc.b $1D
dc.b $22
e3F45:
WATKINS "C"
WATKINS "A"
WATKINS "R"
WATKINS "T"
WATKINS "R"
WATKINS "I"
WATKINS "D"
WATKINS "G"
WATKINS "E"
WATKINS "P"
WATKINS "E"
WATKINS "R"
WATKINS "S"
WATKINS "O"
WATKINS "N"
WATKINS "A"
WATKINS "L"
WATKINS "I"
WATKINS "Z"
WATKINS "E"
WATKINS "Y"
WATKINS "O"
WATKINS "U"
WATKINS "R"
dc.b $1D
WATKINS "A"
WATKINS "T"
WATKINS "A"
WATKINS "R"
WATKINS "I"
dc.b $1D
WATKINS "H"
WATKINS "I"
WATKINS "G"
WATKINS "H"
dc.b $1D
WATKINS "S"
WATKINS "C"
WATKINS "O"
WATKINS "R"
WATKINS "E"
WATKINS "A"
e3F6F:
WATKINS "W"
WATKINS "H"
WATKINS "A"
WATKINS "T"
dc.b $1D
WATKINS "W"
WATKINS "O"
WATKINS "U"
WATKINS "L"
WATKINS "D"
dc.b $1D
WATKINS "Y"
WATKINS "O"
WATKINS "U"
dc.b $1D
WATKINS "L"
WATKINS "I"
WATKINS "K"
WATKINS "E"
dc.b $1D
WATKINS "T"
WATKINS "O"
dc.b $1D
WATKINS "C"
WATKINS "L"
WATKINS "E"
WATKINS "A"
WATKINS "R"
dc.b $2B ; Watkins '?' ($2B)
WATKINS "T"
WATKINS "H"
WATKINS "I"
WATKINS "S"
dc.b $1D
WATKINS "G"
WATKINS "A"
WATKINS "M"
WATKINS "E"
WATKINS "P"
WATKINS "E"
WATKINS "R"
WATKINS "S"
WATKINS "O"
WATKINS "N"
WATKINS "A"
WATKINS "L"
WATKINS "I"
WATKINS "Z"
WATKINS "A"
WATKINS "T"
WATKINS "I"
WATKINS "O"
WATKINS "N"
WATKINS "A"
WATKINS "L"
WATKINS "L"
dc.b $1D
WATKINS "G"
WATKINS "A"
WATKINS "M"
WATKINS "E"
WATKINS "S"
WATKINS "N"
WATKINS "O"
WATKINS "T"
WATKINS "H"
WATKINS "I"
WATKINS "N"
WATKINS "G"
WATKINS "A"
WATKINS "R"
WATKINS "E"
dc.b $1D
WATKINS "Y"
WATKINS "O"
WATKINS "U"
dc.b $1D
WATKINS "S"
WATKINS "U"
WATKINS "R"
WATKINS "E"
dc.b $2B ; Watkins '?' ($2B)
WATKINS "Y"
WATKINS "E"
WATKINS "S"
; ==============================================================================
; Routine: e3FC4
; Purpose: Erases active game slot from directory (sets difficulty & index to $7F).
; Uses zB6: the matched slot, or the LRU victim if no table was found.
; ==============================================================================
e3FC4:
LDX zB6
e3FC6:
LDA #$7F
STA CartRAMGameDifficultyTable,X
STA CartRAMGameAgeTable,X
RTS
; ==============================================================================
; Routine: e3FCF
; Purpose: Formats entire directory (resets all 69 slots to $7F).
; ==============================================================================
e3FCF:
LDX #$44
e3FD1:
JSR e3FC6
DEX
BPL e3FD1
RTS
;
; Resets DLL write pointers to Page N+1 : $87
;
e3FD8:
LDA #$00
STA zB2
STA IdleTimerFine
LDA #$87
STA zA4
LDA z97
STA zA5
RTS
; ==============================================================================
; HSC PUBLIC JUMP VECTORS ($3FEB - $3FFD)
; ==============================================================================
org $3feb,$FF
JumpVectors:
; Vector @ $3FEB: HSCSETRS - Initialize MARIA registers and font palette
JMP HSCSETRS
; Vector @ $3FEE: HSCCPYFM - Copy 25-byte score block from NVRAM to buffer ($8F)
JMP e3000
; Vector @ $3FF1: HSCCPYTO - Copy 25-byte score block from buffer ($8F) to NVRAM
JMP e3011
; Vector @ $3FF4: HSCENTER2 - Enter high score, initials from argument +$0F..+$11
JMP EnterHighScore_SuppliedInitials
; Vector @ $3FF7: HSCSTAT - Check status, rank candidate score, return top score
JMP HSCSTAT
; Vector @ $3FFA: HSCATRCT - Show one game/difficulty high-score table
JMP ShowHighScores
; Vector @ $3FFD: HSCENTER - Interactive initials entry and score ranking
JMP EnterHighScore