7800 HSC Technical Reference

From 8BitDev.org - Atari 7800 Development Wiki
Revision as of 12:32, 1 October 2026 by MSaarna (talk | contribs)
Jump to: navigation, search

Atari 7800 High Score Cartridge (HSC) Technical Reference

The Atari 7800 High Score Cartridge (HSC) is an expansion peripheral that provides non-volatile battery-backed RAM (NVRAM) and a standardized high-score interface for Atari 7800 games. It connects via a pass-through architecture: the HSC plugs into the 7800 cartridge slot, and the game cartridge plugs into the top of the HSC.

System Architecture & Memory Map

Hardware Memory Map

Address Range Size Device Description
$1000–$17FF 2 KB Battery-Backed SRAM 2048 bytes of non-volatile storage (directories, scores, owner name).
$3000–$3FFF 4 KB HSC ROM High score BIOS, display list builders, font tiles, and API jump vectors.

Zero-Page Footprint ($80–$BC)

The HSC uses zero-page locations $80 through $BC (61 bytes total) for calculations, string manipulations, and pointers.

  • Automatic Zero-Page Preservation: Upon entry, the HSC backs up the caller's zero-page range $80–$BC into the game-provided RAM workspace at UserRAM + $13 through UserRAM + $4F.
  • Restoration on Exit: Prior to executing RTS, the HSC completely restores $80–$BC from the backup area.
  • $94/$95 Preservation: Locations $94 and $95 are saved to and restored from dedicated NVRAM scratchpad bytes ($1000 and $1001).

Dual-Page RAM Requirement (512 Contiguous Bytes)

When calling any HSC function, the game must provide a page-aligned 512-byte RAM workspace spanning two contiguous 256-byte pages: Page N and Page N+1.

Memory Layout of the 512-Byte Workspace:

Page N ($xx00 - $xxFF):
+-----------------------------------+ $xx00
|  18-Byte Argument Block ($00-$11) |
+-----------------------------------+ $xx12
|  HSC Internal Scratch ($12)       |
+-----------------------------------+ $xx13
|  Zero-Page Backup Area ($13-$4F)  | <- Holds saved ZP $80-$BC (61 bytes)
+-----------------------------------+ $xx50
|  Formatted Display Workspace      | <- Initials and score string buffer
+-----------------------------------+ $xxFF

Page N+1 ($xx00 + 1 page):
+-----------------------------------+ $(xx+1)00
|  MARIA Display Lists (DL)         | <- HSC writes screen graphics DLs here
+-----------------------------------+ $(xx+1)87
|  MARIA Display List List (DLL)    | <- DPPL=$87, DPPH=(Page N + 1)
+-----------------------------------+ $(xx+1)FF

CRITICAL ARCHITECTURAL WARNING:
The calling game must reserve both Page N and Page N+1. When the HSC renders score tables or the interactive initials screen, it points MARIA's Display List List pointer directly into Page N+1 (DPPH = Page N + 1, DPPL = $87). If only 256 bytes are allocated, the HSC display builder will overwrite program variables located in the subsequent page.

Cartridge Detection & Handshake

To verify that the High Score Cartridge is physically installed before invoking API routines, inspect the two signature bytes located in the HSC font table:

; ==============================================================================
; DetectHSC: Checks for HSC ROM presence
; Returns: Carry Flag Set (1) = HSC Present; Carry Flag Clear (0) = HSC Absent
; ==============================================================================
DetectHSC:
    LDA $3900               ; First signature byte in Font Row 6
    CMP #$C6
    BNE .HscNotFound
    LDA $3904               ; Second signature byte in Font Row 6
    CMP #$FE
    BNE .HscNotFound
    SEC                     ; HSC confirmed present
    RTS
.HscNotFound:
    CLC                     ; HSC not detected
    RTS

Public Vector Table & API Specification

The HSC exports seven primary functions via a jump table located at $3FEB–$3FFD.

Vector Address Routine Name Description
$3FEB HSCSETRS Configures MARIA registers (CTRL, CHARBASE, and P0C2–P4C2) for HSC font display.
$3FEE HSCCPYFM Raw copy: transfers 25 bytes of score data from Cart NVRAM to the pointer in ($8F).
$3FF1 HSCCPYTO Raw copy: transfers 25 bytes of score data from ($8F) into Cart NVRAM.
$3FF4 HSCENTER2 Headless entry: enters score without initials screen (initials passed in buffer).
$3FF7 HSCSTAT Status/rank query: allocates slot if missing; checks if score qualifies.
$3FFA HSCATRCT Attract mode: renders and cycles high score tables for each difficulty level.
$3FFD HSCENTER Interactive entry: checks rank, displays initials screen, shifts table, and saves.


Calling Conventions

All HSC API routines require the following register state upon entry:

  • Y Register: Must contain the Page Number (High Byte) of the argument block (e.g., LDY #$25 for $2500).
  • X Register: Must be set to #$00.
  • Interrupts & Decimal Flag: Handled automatically by HSCInitRoutine (executes SEI and CLD).
  • Stack Note: HSCInitRoutine pushes temporary frame marker bytes ($3C, $2B) to the stack. Routines called via standard JSR will return cleanly via RTS.

User Argument Block Specification (Page N)

The argument block resides at Page N + $00 through Page N + $11 (18 bytes).

Offset Size Field Name Description
+$00 1 ArgGameID1 Game ID byte 1 (Unique Atari Cart ID, e.g., $8B for Joust).
+$01 1 ArgGameID2 Game ID byte 2 (e.g., $23 for Joust).
+$02 1 ArgGameDifficulty Bits 7–2: Difficulty setting (masked with $FC).
Bits 1–0: Active controller index (0 = Player 1, 1 = Player 2).
+$03 1 ArgGameController Controller driver: $00/$01 = Standard Joystick; $02 = Custom Routine.
+$04 2 ArgGameNamePtr Pointer (16-bit Lo/Hi) to Watkins-encoded Game Title string (or custom driver).
+$06 2 ArgDifficultyNamePtr Pointer (16-bit Lo/Hi) to Watkins-encoded Difficulty text string.
+$08 2 ArgGameScorePtr Pointer (16-bit Lo/Hi) to 4-byte packed BCD score data in game RAM.
+$0A 2 ArgLogoDllPtr Pointer (16-bit Lo/Hi) to Game Logo Display List List (DLL), or 0 if using text title.
+$0C 2 ArgSoundRoutinePtr Pointer (16-bit Lo/Hi) to sound callback executed during attract wait loops.
+$0E 1 ArgHSDisplayTime Attract mode screen display timer multiplier.
+$0F 4 Returned Status Rank and checksum status bytes returned by HSCSTAT (offsets +$0F–$12).
+$13 61 ZP Preservation Area Scratchpad reserved for HSC to save/restore Zero-Page $80–$BC.

Field Specifications

ArgGameDifficulty (Offset +$02)
Bits 7..2 represent the game difficulty index. The HSC maintains separate 5-score tables for each difficulty level. Bits 1..0 specify which player's controller is polled during initials entry and attract screen skipping.
ArgGameController (Offset +$03)
When set to $00 or $01, the HSC directly reads the RIOT and TIA registers (SWCHA, INPT4, INPT5). When set to $02, the HSC executes an indirect jump to (ArgGameNamePtr) to poll custom controllers.
ArgGameScorePtr (Offsets +$08, +$09)
Pointer to a 4-byte (8-digit) packed BCD score in Big-Endian order:
1,234,560 points = $01, $23, $45, $60
Maximum score value: 16,777,215 ($16 $77 $72 $15 in BCD). Values higher than this will be clamped to $FFFFFF.
ArgSoundRoutinePtr (Offsets +$0C, +$0D)
Pointer to the game's audio driver. During attract mode display and initials entry loops, the HSC executes JSR (ArgSoundRoutineLo) once per frame.
Returned Rank Status (Offset +$05 on return from HSCSTAT)
* $01–$05: The submitted score qualifies for 1st through 5th place.
* $FF: The submitted score did not qualify.

Data Representations & Internal Encodings

Watkins Character Set & Font Map

The HSC does not use ASCII. Text strings must use the proprietary Watkins encoding matching the internal font table at $3900–$3F00.

Hex Dec Glyph Hex Dec Glyph Hex Dec Glyph
$00 0 A $0F 15 P $1E 30 ' (Apostrophe)
$01 1 B $10 16 Q $1F 31 ← (Left / Erase)
$02 2 C $11 17 R $20 32 █ (Block Cursor)
$03 3 D $12 18 S $21 33 0
$04 4 E $13 19 T $22 34 1
$05 5 F $14 20 U $23 35 2
$06 6 G $15 21 V $24 36 3
$07 7 H $16 22 W $25 37 4
$08 8 I $17 23 X $26 38 5
$09 9 J $18 24 Y $27 39 6
$0A 10 K $19 25 Z $28 40 7
$0B 11 L $1A 26 . (Period) $29 41 8
$0C 12 M $1B 27 : (Colon) $2A 42 9
$0D 13 N $1C 28 - (Hyphen) $2B 43 ? (Question Mark)
$0E 14 O $1D 29 (Space)

DASM Watkins macro:

    mac WATKINS
        dc.b {1}-"A"
    endm

15-Bit Packed Initials Format

Initials consist of three 5-bit characters ($00–$1F) packed into 2 consecutive bytes:

Character 0: C0 [4..0]
Character 1: C1 [4..0]
Character 2: C2 [4..0]

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 ]

Packing Algorithm

; Inputs: Initials in ZP buffer (z98) at offsets 0, 1, 2
; Outputs: A = Packed Byte 0, Y = Packed Byte 1
PackInitials:
    LDY #$01
    LDA (z98),Y         ; Character 1
    AND #$1C            ; Mask 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                 ; Save Byte 0
    INY
    LDA (z98),Y         ; Character 1
    AND #$03            ; Mask 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)
    TAY                 ; Y = Byte 1
    PLA                 ; A = Byte 0
    RTS

Unpacking Algorithm

; Inputs: A = Byte 0, X = Byte 1
; Outputs: A = Char 0, X = Char 1, Y = Char 2
UnpackInitials:
    PHA
    AND #$E0            ; Mask bits 7..5 of Byte 0
    LSR
    LSR
    LSR
    STA zB1             ; zB1 = C1 bits 4..2
    TXA
    AND #$1F
    TAY                 ; Y = Char 2 (bits 4..0 of Byte 1)
    TXA
    AND #$C0            ; Mask bits 7..6 of Byte 1
    CLC
    ROL
    ROL
    ROL                 ; Rotate to bits 1..0
    ORA zB1
    TAX                 ; X = Char 1
    PLA
    AND #$1F            ; A = Char 0
    RTS
  • Default Initials: The placeholder initials "..." (Period, Period, Period = 26, 26, 26 / $1A, $1A, $1A) pack into Byte 0 = $DA, Byte 1 = $9A.

Score Conversion: BCD to 24-Bit Binary

While games supply a 4-byte packed BCD score pointer, the HSC converts the score into a 24-bit binary integer for ranking and Cart NVRAM storage:

  1. Unpack and Multiply: Routine e3AA8 converts the 4-byte BCD digits into a 24-bit unsigned binary integer stored across three zero-page registers: zAD (LSB), zAE (MID), and zAF (MSB).
  2. NVRAM Storage: The score is saved as a little-endian 24-bit integer occupying 3 bytes:
    • Offset +0: Initials Byte 0
    • Offset +1: Initials Byte 1
    • Offset +2: Score LSB (bits 0–7)
    • Offset +3: Score MID (bits 8–15)
    • Offset +4: Score MSB (bits 16–23)
  3. Display Formatting: When drawing score tables, routine e37BC converts the 24-bit binary integer back into decimal characters using a table of powers of 10 ($989680 = 10,000,000 down to $000001 = 1).

Cartridge NVRAM Layout ($1000–$17FF)

Address Range Size Field Identifier Description
$1000–$1001 2 CartRAMSaveZ94/Z95 Storage for caller's zero-page $94/$95.
$1002–$1006 5 CartRAMMagic Magic bytes ($68, $83, $AA, $55, $9C).
$1007 1 CartRAMEntryID ID of entry point currently executing.
$1008–$1027 32 CartRAMOwnerName Cartridge owner string (Watkins encoding).
$1028 1 CartRAMOwnerNameLength Length of owner name string.
$1029–$106D 69 CartRAMGameID1Table Game ID 1 table (up to 69 game configurations).
$106E–$10B2 69 CartRAMGameID2Table Game ID 2 table.
$10B3–$10F7 69 CartRAMGameDifficultyTable Game Difficulty table (difficulty masked with $FC).
$10F8–$113C 69 CartRAMGameIndexTable LRU aging table (prefilled with $7F).
$113D–$17F9 1725 Score Data Blocks 69 tables × 25 bytes (5 scores per table).
$17FA–$17FE 5 CartRAMTempScore Scratch buffer used when shifting scores down.
$17FF 1 Unused Unused byte.

Directory Lookup & LRU Allocation

  • Capacity: Up to 69 distinct (Game ID 1 + Game ID 2 + Difficulty) entries.
  • Lookup: HSCInitRoutine searches backwards (indices 68 down to 0) for matching ID and difficulty bytes.
  • LRU Eviction: If all 69 slots are occupied and a new game or difficulty is registered, the slot with the highest aging index in CartRAMGameIndexTable is purged and replaced.
  • Table Address Formula:
TableAddress = $113D + (SlotIndex * 25)
  • Each 25-byte block stores 5 entries of 5 bytes each, ordered from 1st place (offset +$00) to 5th place (offset +$14).

7800 Assembly Integration Example

Argument Block Template

; ==============================================================================
; ROM Argument Template
; ==============================================================================
HscRomTemplate:
    .byte $8B, $23          ; Game ID 1 and 2
    .byte $00               ; Difficulty (Bits 7..2) | Controller (Bits 1..0)
    .byte $00               ; Controller Type: $00 = Standard Joystick
    .word HscTitleText      ; Pointer to Watkins Game Name string
    .word HscDiffText       ; Pointer to Watkins Difficulty string
    .word GameScoreBcd      ; Pointer to 4-byte packed BCD score in RAM
    .word 0                 ; Logo DLL Pointer (0 = Use standard text title)
    .word GameSoundCallback ; Sound playback callback routine
    .byte $04               ; Display Duration Multiplier (Attract speed)

HscTitleText:
    WATKINS "M"
    WATKINS "Y"
    .byte $1D               ; Watkins space
    WATKINS "G"
    WATKINS "A"
    WATKINS "M"
    WATKINS "E"
    .byte $1D

HscDiffText:
    WATKINS "E"
    WATKINS "A"
    WATKINS "S"
    WATKINS "Y"
    .byte $1D

Attract Mode Screen Display

; ==============================================================================
; ShowHighScores: Displays high score table during attract loop
; ==============================================================================
ShowHighScores:
    JSR DetectHSC
    BCC .ExitNoHsc          ; Skip if HSC is not present

    ; Copy configuration template to Page N RAM ($2500)
    LDX #14
.CopyArgs:
    LDA HscRomTemplate,X
    STA $2500,X
    DEX
    BPL .CopyArgs

    ; Set desired difficulty (Bits 7..2)
    LDA CurrentDifficulty   ; e.g., 0, 1, 2
    ASL
    ASL
    STA $2502

    ; Call HSC Attract Mode Vector
    LDX #$00
    LDY #$25                ; Y = Page $25 ($2500 argument block)
    JSR $3FFA               ; HSCATRCT

    ; Restore game video registers and display list pointers
    JSR RestoreGameScreen
.ExitNoHsc:
    RTS

Game Over Score Submission

; ==============================================================================
; SubmitScore: Checks rank qualification and launches initials entry screen
; ==============================================================================
SubmitScore:
    JSR DetectHSC
    BCC .ExitNoHsc

    ; Setup argument block at $2500
    LDX #14
.SetupArgs:
    LDA HscRomTemplate,X
    STA $2500,X
    DEX
    BPL .SetupArgs

    LDA CurrentDifficulty
    ASL
    ASL
    STA $2502

    ; Check if score qualifies
    LDX #$00
    LDY #$25
    JSR $3FF7               ; HSCSTAT

    LDA $2505               ; Read returned rank
    CMP #$FF                ; $FF = Did not qualify
    BEQ .DidNotQualify

    ; Score qualifies! Run interactive initials entry screen and save
    LDX #$00
    LDY #$25
    JSR $3FFD               ; HSCENTER

    JSR RestoreGameScreen
.DidNotQualify:
.ExitNoHsc:
    RTS