Difference between revisions of "7800 HSC Technical Reference"
| Line 326: | Line 326: | ||
* '''LRU Eviction:''' If all 69 slots are occupied and a new game or difficulty is registered, the slot with the highest aging index in <code>CartRAMGameIndexTable</code> is purged and replaced. | * '''LRU Eviction:''' If all 69 slots are occupied and a new game or difficulty is registered, the slot with the highest aging index in <code>CartRAMGameIndexTable</code> is purged and replaced. | ||
* '''Table Address Formula:''' | * '''Table Address Formula:''' | ||
| − | + | <pre>TableAddress = $113D + (SlotIndex * 25)</pre> | |
* Each 25-byte block stores 5 entries of 5 bytes each, ordered from 1st place (offset <code>+$00</code>) to 5th place (offset <code>+$14</code>). | * Each 25-byte block stores 5 entries of 5 bytes each, ordered from 1st place (offset <code>+$00</code>) to 5th place (offset <code>+$14</code>). | ||
Revision as of 12:32, 1 October 2026
Contents
- 1 Atari 7800 High Score Cartridge (HSC) Technical Reference
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–$BCinto the game-provided RAM workspace atUserRAM + $13throughUserRAM + $4F. - Restoration on Exit: Prior to executing
RTS, the HSC completely restores$80–$BCfrom the backup area. - $94/$95 Preservation: Locations
$94and$95are saved to and restored from dedicated NVRAM scratchpad bytes ($1000and$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:
-
YRegister: Must contain the Page Number (High Byte) of the argument block (e.g.,LDY #$25for$2500). -
XRegister: Must be set to#$00. - Interrupts & Decimal Flag: Handled automatically by
HSCInitRoutine(executesSEIandCLD). - Stack Note:
HSCInitRoutinepushes temporary frame marker bytes ($3C,$2B) to the stack. Routines called via standardJSRwill return cleanly viaRTS.
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
$00or$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 $15in 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 intoByte 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:
- Unpack and Multiply: Routine
e3AA8converts the 4-byte BCD digits into a 24-bit unsigned binary integer stored across three zero-page registers:zAD(LSB),zAE(MID), andzAF(MSB). - 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)
-
- Display Formatting: When drawing score tables, routine
e37BCconverts 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:
HSCInitRoutinesearches 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
CartRAMGameIndexTableis 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