Difference between revisions of "7800 NTSC BIOS"
From 8BitDev.org - Atari 7800 Development Wiki
| Line 5: | Line 5: | ||
<pre> | <pre> | ||
| − | ; Disassembly of bios7800.bin | + | ; Disassembly of the Atari 7800 NTSC BIOS (bios7800.bin) |
| − | ; | + | ; Originally disassembled by DiStella v2.0 on Sun Mar 08 20:30:38 1998 |
| − | |||
; | ; | ||
| − | ; | + | ; This version has been heavily commented and refactored for maximum clarity, |
| − | ; | + | ; with generic labels (e.g., LF400) renamed to descriptive ones. |
| − | ; | + | ; The goal is to explain the inner workings of the BIOS for educational |
| − | ; | + | ; and development purposes. |
| + | ; | ||
| + | ; Original comments by: | ||
| + | ; - Keith Henrickson <flipper@phin.com> | ||
| + | ; - Daniel Boris <dboris@home.com> | ||
| + | ; - Mike Saarna (dasm fixups, additional comments) | ||
| + | ; - Gemini 2.5 Pro (semantic meaningful label names, comments galore) | ||
| + | ; | ||
| + | ; The code must be assembled with DASM. | ||
; | ; | ||
processor 6502 | processor 6502 | ||
| − | ; | + | ;============================================================================= |
| + | ; | ||
| + | ; I. HARDWARE AND MEMORY EQUATES | ||
| + | ; | ||
| + | ;============================================================================= | ||
| + | |||
| + | ; --- Misc System Equates --- | ||
LFD80 = $FD80 | LFD80 = $FD80 | ||
| − | CartKey = $FF80 | + | CartKey = $FF80 ; Start address of the 8-byte cartridge security key |
| − | CartRegion = $FFF8 | + | CartRegion = $FFF8 ; Cartridge region/signature byte |
| − | CartKeyStartPage = $FFF9 | + | CartKeyStartPage = $FFF9 ; Cartridge ROM start page and signature byte |
| − | CartResetVectorHi = $FFFD | + | CartResetVectorHi = $FFFD ; High byte of the cartridge's RESET vector address |
| − | LF112 = $F112 | + | LF112 = $F112 |
| − | LF118 = $F118 | + | LF118 = $F118 |
| − | LF460 | + | LF460 = $F460 |
| − | LF700 | + | LF700 = $F700 |
| − | LF800 | + | LF800 = $F800 |
| + | ; --- 7800 System Control --- | ||
| + | INPTCTRL = $01 ; Input Control Register. Controls memory mapping (BIOS, RAM, Cart) and TIA access. | ||
| − | ; | + | ; --- TIA (Television Interface Adapter) Registers (for 2600 Mode) --- |
| + | VSYNC = $00 ; Vertical Sync | ||
| + | VBLANK = $01 ; Vertical Blank | ||
| + | WSYNC = $02 ; Wait for Horizontal Sync | ||
| + | RSYNC = $03 ; Reset Horizontal Sync Counter | ||
| + | COLUP0 = $06 ; Color-Luminance for Player 0 | ||
| + | COLUPF = $08 ; Color-Luminance for Playfield | ||
| + | COLUBK = $09 ; Color-Luminance for Background | ||
| + | PF2 = $0F ; Playfield Register 2 | ||
| + | RESP1 = $11 ; Reset Player 1 | ||
| + | GRP0 = $1B ; Player 0 Graphics | ||
| + | GRP1 = $1C ; Player 1 Graphics | ||
| − | + | ; --- Maria (Custom Graphics Chip) Registers --- | |
| + | BACKGRND = $20 ; Background Color | ||
| + | POC1 = $21 ; Palette 0, Color 1 | ||
| + | P0C2 = $22 ; Palette 0, Color 2 | ||
| + | POC3 = $23 ; Palette 0, Color 3 | ||
| + | MWSYNC = $24 ; Maria Wait for Sync | ||
| + | P1C1 = $25 ; Palette 1, Color 1 | ||
| + | P1C2 = $26 ; Palette 1, Color 2 | ||
| + | P1C3 = $27 ; Palette 1, Color 3 | ||
| + | MSTAT = $28 ; Maria Status Register (VBLANK, etc.) | ||
| + | DPPH = $2C ; Display List Pointer High Byte | ||
| + | DPPL = $30 ; Display List Pointer Low Byte | ||
| + | CTRL = $3C ; Maria Control Register (DMA, graphics mode) | ||
| − | ; | + | ; --- Zero Page RAM Vectors/Variables --- |
| + | DLIAddr = $F4 ; RAM vector for Display List Interrupts (NMI) | ||
| + | ConsoleRegion = $FE ; Console region byte (used in cart verification) | ||
| − | + | ;============================================================================= | |
| − | + | ; | |
| − | + | ; II. CARTRIDGE AUTHENTICATION & MODE SWITCHING LOGIC | |
| − | + | ; | |
| − | + | ; This entire block of code from $F000 to $F880 is not executed from ROM. | |
| − | + | ; Instead, it is copied to RAM ($2300-$2700) by the main BIOS init routine | |
| − | + | ; at Bios_InitSystem. The jump to $2306 then begins execution from RAM. | |
| − | + | ; | |
| − | + | ;============================================================================= | |
| − | |||
| − | |||
| − | |||
| − | ; | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
ORG $F000 | ORG $F000 | ||
| − | + | ; --- NMI Vector (ROM) --- | |
| − | jmp (DLIAddr) ; RAM NMI vector | + | ; This is the hardware NMI vector. It immediately jumps to a RAM vector at $F4, |
| + | ; which allows the running program (including the BIOS itself) to define its | ||
| + | ; own NMI handler. | ||
| + | NmiVector_Rom: | ||
| + | pha ; This is the NMI routine. | ||
| + | jmp (DLIAddr) ; Jump to RAM NMI vector. | ||
ORG $F400 | ORG $F400 | ||
| − | ; | + | ; --- RAM Routine: Cartridge Detection and Authentication --- |
| − | + | ; This code is designed to be executed from RAM starting at address $2300. | |
| − | + | ; Its primary job is to determine if a valid, signed 7800 cartridge is | |
| − | + | ; present. If it is, the system boots in 7800 mode. If not, it attempts | |
| − | + | ; to boot in 2600 mode. | |
| − | |||
| − | + | RamExec_Start: | |
| − | + | SwitchTo2600Mode1: | |
| − | + | jmp Enter2600Mode ; Branch to the 2600 mode initialization routine. | |
| + | SwitchTo2600Mode2: | ||
| + | jmp Enter2600Mode ; Branch to the 2600 mode initialization routine. | ||
| − | ldy #$FF ; | + | CartTest: |
| − | ldx #$7F ; | + | lda #$16 ; Value to enable Maria and map in the cartridge ROM. |
| − | + | sta INPTCTRL ; Switch in cart + enable Maria. | |
| − | lda LFE00,X ; | + | |
| − | cmp LFD80,Y ; | + | ldy #$FF ; Initialize Y to $FF. |
| − | bne SwitchTo2600Mode1 ; | + | ldx #$7F ; Initialize X to $7F. |
| + | ; --- Cartridge Presence Test --- | ||
| + | ; This loop compares two different ROM regions ($FD80-$FDFF and $FE00-$FE7F). | ||
| + | ; If a cartridge is not present, the data bus "floats", and reading any address | ||
| + | ; might return the last value seen on the bus (or garbage). By comparing two | ||
| + | ; regions that should contain different data, the BIOS can detect this state. | ||
| + | ; If the data is the same, it's likely a floating bus, so no cart is present. | ||
| + | CartPresenceCheck_Loop: | ||
| + | lda LFE00,X ; Read from the $FExx range. | ||
| + | cmp LFD80,Y ; Compare with the $FDxx range. | ||
| + | bne SwitchTo2600Mode1 ; If they differ, the test passes (so far). Branch if they are the same (fail). | ||
dey ; | dey ; | ||
dex ; | dex ; | ||
| − | bpl | + | bpl CartPresenceCheck_Loop ; Loop for 128 bytes. |
| + | ; --- Cartridge Reset Vector Test --- | ||
| + | ; A valid 7800 or 2600 cart must have a valid reset vector. A vector of $FFFF | ||
| + | ; or $0000 typically indicates an empty socket or a problem. | ||
lda CartResetVectorLo ; | lda CartResetVectorLo ; | ||
and CartResetVectorHi ; | and CartResetVectorHi ; | ||
cmp #$FF ; | cmp #$FF ; | ||
| − | beq SwitchTo2600Mode1 ; If RESET vector is FFFF, then go 2600. | + | beq SwitchTo2600Mode1 ; If RESET vector is $FFFF, then go 2600. |
lda CartResetVectorLo ; | lda CartResetVectorLo ; | ||
ora CartResetVectorHi ; | ora CartResetVectorHi ; | ||
| − | beq SwitchTo2600Mode1 ; If RESET vector is 0000, then go 2600. | + | beq SwitchTo2600Mode1 ; If RESET vector is $0000, then go 2600. |
| − | lda CartRegion ; | + | |
| − | ora #ConsoleRegion | + | ; --- Cartridge Signature/Region Verification --- |
| − | cmp #$FF ; | + | ; This is a series of simple, quick checks on bytes in the $FFF8-$FFF9 region |
| + | ; of the cartridge ROM to see if it looks like an official 7800 cart. | ||
| + | lda CartRegion ; Read signature byte at $FFF8. | ||
| + | ora #ConsoleRegion ; OR with the console's region byte ($FE). | ||
| + | cmp #$FF ; | ||
bne SwitchTo2600Mode2 ; If low-bit of CartRegion=0, then go 2600. | bne SwitchTo2600Mode2 ; If low-bit of CartRegion=0, then go 2600. | ||
lda CartRegion ; | lda CartRegion ; | ||
| − | eor #$F0 ; Invert high nibble | + | eor #$F0 ; Invert high nibble. |
| − | and #$F0 ; | + | and #$F0 ; Isolate the high nibble. |
| − | bne SwitchTo2600Mode2 ; If high nibble was not F, then go 2600. | + | bne SwitchTo2600Mode2 ; If high nibble was not originally $F, then go 2600. |
| − | lda CartKeyStartPage ; | + | lda CartKeyStartPage ; Read signature byte at $FFF9. |
| − | and #$0B ; | + | and #$0B ; |
| − | cmp #$03 ; | + | cmp #$03 ; |
| − | bne SwitchTo2600Mode2 ; If low nibble FFF9 was not 3 or 7, go 2600. | + | bne SwitchTo2600Mode2 ; If low nibble of $FFF9 was not 3 or 7, go 2600. |
| + | |||
| + | ; --- Cartridge Header Processing --- | ||
| + | ; If the quick checks pass, the BIOS proceeds with the full authentication. | ||
lda CartKeyStartPage ; | lda CartKeyStartPage ; | ||
| − | and #$F0 ; Extract ROM start from high nibble | + | and #$F0 ; Extract ROM start page from high nibble of $FFF9. |
| − | sta $EE ; Store it | + | sta $EE ; Store it in zero page. |
| − | sta $2406 ; Store it | + | sta $2406 ; Store it in RAM for the authentication routine. |
cmp #$40 ; | cmp #$40 ; | ||
| − | bcc SwitchTo2600Mode2 ; If ROM start < | + | bcc SwitchTo2600Mode2 ; If ROM start page < $40 (i.e., address < $4000), fail. |
| − | sbc #$01 ; | + | sbc #$01 ; |
| − | cmp CartResetVectorHi ; | + | cmp CartResetVectorHi ; Compare ROM start page with high byte of reset vector. |
| − | bcs SwitchTo2600Mode2 ; If | + | bcs SwitchTo2600Mode2 ; If start page is >= reset vector high byte, fail. |
| − | jsr $2536 ; | + | jsr $2536 ; If all checks pass, start the full authentication process. |
| + | |||
| + | ; --- Obfuscated Authentication Code --- | ||
| + | ; The following code is part of the digital signature verification. It involves | ||
| + | ; complex mixing, rotating, and lookup operations on the cartridge's security | ||
| + | ; key and internal BIOS data tables. Its purpose is to be difficult to analyze | ||
| + | ; and replicate. The high-level goal is to compute a hash from the cart key | ||
| + | ; and compare it against an expected result. | ||
lda #$00 | lda #$00 | ||
| − | sta $F0 | + | sta $F0 |
| − | jsr $241B | + | jsr $241B |
| − | lda #$16 | + | lda #$16 |
| − | sta INPTCTRL | + | sta INPTCTRL |
| − | ldx #$00 | + | ldx #$00 |
| − | txa | + | txa |
| − | + | Auth_RamFillLoop1: | |
| − | dex | + | sta $1800,X |
| − | bne | + | dex |
| − | pha | + | bne Auth_RamFillLoop1 |
| − | ldy #$7F | + | pha |
| − | + | ldy #$7F | |
| − | sta $1800,Y | + | Auth_CopyRomToRamLoop: |
| − | dey | + | lda LFF00,Y |
| − | cpy #$F8 | + | sta $1800,Y |
| − | bne | + | dey |
| − | lda #$2E | + | cpy #$F8 |
| − | sta $2409 | + | bne Auth_CopyRomToRamLoop |
| − | lda #$24 | + | lda #$2E |
| − | sta $240A | + | sta $2409 |
| + | lda #$24 | ||
| + | sta $240A | ||
| − | + | Auth_MainLoop1: | |
| − | jsr $241B | + | jsr $241B |
| − | pla | + | pla |
| − | jsr $23FF | + | jsr $23FF |
| − | pha | + | pha |
| − | inc $2406 | + | inc $2406 |
| − | lda $2406 | + | lda $2406 |
| − | cmp #$FF | + | cmp #$FF |
| − | + | bne Auth_MainLoop1 | |
| + | |||
| + | jsr $241B | ||
| + | jsr $2412 | ||
| + | jsr $2412 | ||
| + | lda #$36 | ||
| + | sta $2409 | ||
| + | lda #$24 | ||
| + | sta $240A | ||
| + | dec $2406 | ||
| + | Auth_MainLoop2: | ||
| + | jsr $241B | ||
| + | pla | ||
| + | jsr $23FF | ||
| + | pha | ||
| + | dec $2406 | ||
| + | lda $2406 | ||
| + | cmp $EE | ||
| + | bcs Auth_MainLoop2 | ||
| + | lda #$60 | ||
| + | sta CTRL | ||
| + | ldx #$77 | ||
| + | Auth_XorAndStoreLoop: | ||
| + | lda $1800,X | ||
| + | eor $1850,X | ||
| + | eor $1888,X | ||
| + | sta $1A00,X | ||
| + | dex | ||
| + | bpl Auth_XorAndStoreLoop | ||
| + | lda $1A00 | ||
| + | and #$07 | ||
| + | sta $1A00 | ||
| + | lda #$00 | ||
| + | ldx #$04 | ||
| + | sta $1A00,X | ||
| + | sta $2000,X | ||
| − | + | ; --- Final Authentication Check --- | |
| − | + | ; After all the cryptographic hashing, this is the final comparison. | |
| − | + | ; It compares two blocks of memory in RAM. If they match, the cart is authentic. | |
| − | + | ldx #$77 | |
| − | + | Auth_FinalCompareLoop: | |
| − | + | lda $2000,X ; Read from one workspace area. | |
| − | + | cmp $1A00,X ; Compare with the computed hash. | |
| − | + | bne Auth_FailAndSwitchTo2600; If they don't match, authentication fails. | |
| − | + | dex | |
| − | + | bpl Auth_FinalCompareLoop ; Loop until all bytes are compared. | |
| − | + | jmp Enter7800Mode ; AUTHENTICATION PASSED: Jump to 7800 mode init. | |
| − | + | Auth_FailAndSwitchTo2600: | |
| − | + | jmp Enter2600Mode ; AUTHENTICATION FAILED: Jump to 2600 mode init. | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | ldx #$77 | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | lda | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | cmp $1A00,X | ||
| − | bne | ||
| − | dex | ||
| − | bpl | ||
| − | jmp | ||
| − | |||
| − | + | ; --- Authentication Subroutines --- | |
| − | ldx #$00 | + | ; More opaque subroutines used by the main authentication algorithm. |
| − | + | Auth_Sub_MixAndLookup_Equate = $F500 | |
| − | adc LFF00,X | + | ldx #$00 |
| − | tay | + | Auth_Sub_MixAndLookup_Loop: |
| − | lda $2DD5,Y | + | adc $1800,X |
| − | sta $1800,X | + | adc LFF00,X |
| − | inx | + | tay |
| − | bne | + | lda $2DD5,Y |
| − | rts | + | sta $1800,X |
| + | inx | ||
| + | bne Auth_Sub_MixAndLookup_Loop | ||
| + | rts | ||
| − | ldx #$00 | + | ldx #$00 |
| − | + | Auth_Sub_RotateRam_Loop: | |
| − | inx | + | rol $1800,X |
| − | bne | + | inx |
| − | rts | + | bne Auth_Sub_RotateRam_Loop |
| + | rts | ||
| − | ;$241B | + | ; Subroutine at $241B - This routine briefly disables the cartridge to access BIOS ROM/RAM. |
| − | php | + | php |
| − | dec $F0 | + | dec $F0 |
| − | bpl | + | bpl Auth_Sub_AccessBios_Done |
| − | lda #$02 | + | lda #$02 |
| − | sta INPTCTRL | + | sta INPTCTRL |
| − | + | Auth_Sub_AccessBios_WaitLoop: | |
| − | bmi | + | lda $F0 |
| − | lda #$16 | + | bmi Auth_Sub_AccessBios_WaitLoop |
| − | sta INPTCTRL | + | lda #$16 |
| − | + | sta INPTCTRL | |
| − | rts | + | Auth_Sub_AccessBios_Done: |
| + | plp | ||
| + | rts | ||
| − | + | ; --- Authentication Signature Data Tables --- | |
| − | + | ; These large blocks of byte data are part of the digital signature scheme. | |
| + | ; They are used as lookup tables and keys during the verification process. | ||
| + | AuthData_Table1: .byte $C7,$65,$AB,$CA,$EE,$F7,$83,$09 | ||
| + | AuthData_Table2: .byte $E1,$D0,$92,$67,$62,$B6,$72,$55,$8E,$91,$DC,$C5,$81,$BE,$78,$20 | ||
.byte $59,$B7,$E6,$3D,$06,$45,$AF,$C8,$08,$31,$38,$D1,$FB,$73,$84,$A9 | .byte $59,$B7,$E6,$3D,$06,$45,$AF,$C8,$08,$31,$38,$D1,$FB,$73,$84,$A9 | ||
.byte $17,$FC,$34,$87,$A3,$94,$FA,$90,$B8,$ED,$CE,$3B,$5B,$0A,$43,$D9 | .byte $17,$FC,$34,$87,$A3,$94,$FA,$90,$B8,$ED,$CE,$3B,$5B,$0A,$43,$D9 | ||
.byte $F3,$53,$82,$B3,$0D,$6D,$5A,$60,$9D,$51,$A7,$B9 | .byte $F3,$53,$82,$B3,$0D,$6D,$5A,$60,$9D,$51,$A7,$B9 | ||
| − | + | AuthData_Table3: .byte $11,$10,$BC,$E4,$7F,$80,$41,$E7,$E3 | |
| − | + | AuthData_Table4: .byte $F6,$56,$26,$35,$EC,$D6,$DF,$0C,$7F,$F4,$9E,$AC,$52,$46,$EF,$CF | |
.byte $BF,$A2,$3F,$A4,$13,$15,$97,$4A,$1C,$B0,$42,$8C,$B1,$05,$58,$80 | .byte $BF,$A2,$3F,$A4,$13,$15,$97,$4A,$1C,$B0,$42,$8C,$B1,$05,$58,$80 | ||
.byte $18,$77,$2B,$02,$3E,$A8,$49,$1A,$6A | .byte $18,$77,$2B,$02,$3E,$A8,$49,$1A,$6A | ||
| − | + | AuthData_Table5: .byte $CB,$6E,$0B,$8A,$EB,$F1,$4F,$14,$79,$8B,$D8,$9F,$9B,$57,$19,$F8 | |
.byte $2A,$2D,$76,$0E,$E8,$2E,$4B,$F9,$07,$03,$DE,$93,$16,$7E,$D4,$E5 | .byte $2A,$2D,$76,$0E,$E8,$2E,$4B,$F9,$07,$03,$DE,$93,$16,$7E,$D4,$E5 | ||
.byte $B2,$F0,$7D,$7A,$DA,$D2,$A1,$CC,$1D,$E0,$5E,$23,$A0,$95,$22,$1E | .byte $B2,$F0,$7D,$7A,$DA,$D2,$A1,$CC,$1D,$E0,$5E,$23,$A0,$95,$22,$1E | ||
.byte $36,$85,$FE,$1F,$39 | .byte $36,$85,$FE,$1F,$39 | ||
| − | + | AuthData_Table6: .byte $AA,$89,$96,$AD,$0F,$2F,$C0,$47 | |
| − | + | AuthData_Table7: .byte $27,$5D,$24,$EA,$C3,$A5,$F5,$21,$5F,$1B,$40,$8F,$AE,$74,$25,$DD | |
.byte $C1,$7C,$CD,$A6,$70,$D7,$33,$7B,$2C,$75,$BB,$86,$99,$BD,$54 | .byte $C1,$7C,$CD,$A6,$70,$D7,$33,$7B,$2C,$75,$BB,$86,$99,$BD,$54 | ||
| − | + | AuthData_Table8: .byte $9A,$6C,$63,$32,$48,$4C,$8D,$BA | |
| − | + | AuthData_Table9: .byte $5C,$61,$C4,$4E,$29,$37,$12,$C6,$98,$9C,$D5,$69,$6B,$E2,$04,$4D | |
.byte $E9,$C2,$88,$3A,$DB,$64,$01,$44,$6F,$B5,$F2,$30,$28,$FD,$50,$71 | .byte $E9,$C2,$88,$3A,$DB,$64,$01,$44,$6F,$B5,$F2,$30,$28,$FD,$50,$71 | ||
.byte $3C,$B4,$66,$68,$C9,$D3,$CA,$83,$C7,$AB,$F7,$65,$09,$EE | .byte $3C,$B4,$66,$68,$C9,$D3,$CA,$83,$C7,$AB,$F7,$65,$09,$EE | ||
| − | ldx #$77 ;($2536) | + | ; --- Main Authentication Routine ($2536) --- |
| − | stx $E4 | + | ; This routine is the entry point for the signature verification process. |
| − | stx $E5 | + | ; It copies the 128-byte security key from the cartridge ROM into RAM. |
| − | + | ldx #$77 ; ($2536) Start of routine, X=119 | |
| − | sta $1901,X ;Store it | + | stx $E4 |
| − | sta $2000,X ;Store it | + | stx $E5 |
| − | dex ;next byte of key | + | Auth_CopyCartKey_Loop: |
| − | bpl | + | lda CartKey,X ; Read a byte from the cart security key area ($FF80-FFFF). |
| + | sta $1901,X ; Store it in RAM work area 1. | ||
| + | sta $2000,X ; Store it in RAM work area 2. | ||
| + | dex ; next byte of key | ||
| + | bpl Auth_CopyCartKey_Loop ; Loop until all 128 bytes are copied. | ||
lda #$02 ; | lda #$02 ; | ||
| − | sta INPTCTRL ;Disable cart | + | sta INPTCTRL ; Disable cart ROM to access BIOS routines. |
| − | jsr | + | jsr Display_InitLogo ; Initialize Maria for the Fuji logo display. |
| − | jsr $257B | + | jsr $257B |
| − | dec $F2 | + | dec $F2 |
| − | ldx #$77 | + | ldx #$77 |
| − | stx $E4 | + | stx $E4 |
| − | + | Auth_CopyInternalKey_Loop: | |
| − | sta $1901,X | + | lda LFED5,X |
| − | dex | + | sta $1901,X |
| − | bpl | + | dex |
| − | lda $E1 | + | bpl Auth_CopyInternalKey_Loop |
| − | sta $E3 | + | lda $E1 |
| − | jsr $25E1 | + | sta $E3 |
| − | dec $F2 | + | jsr $25E1 |
| − | + | dec $F2 | |
| − | sta $2572 | + | Auth_CopyWorkspace_Start: |
| − | ldx #$77 | + | lda $E0 |
| − | + | sta $2572 | |
| − | + | ldx #$77 | |
| − | dex | + | Auth_CopyWorkspace_Loop: |
| − | bpl | + | lda $1800,X |
| − | rts | + | Auth_CopyWorkspace_Store: |
| − | + | sta $2000,X | |
| − | jsr $2639 ;($257B) | + | dex |
| − | ldy $E5 | + | bpl Auth_CopyWorkspace_Loop |
| − | iny | + | rts |
| − | + | ||
| − | TYA | + | ; --- More Authentication Subroutines --- |
| − | clc | + | jsr $2639 ; ($257B) |
| − | adc $E2 | + | ldy $E5 |
| − | pha | + | iny |
| − | + | Auth_Sub_KeyProcess_OuterLoop: | |
| − | lda #$00 | + | STY $E1 |
| − | sta $2671 | + | TYA |
| − | + | clc | |
| − | dex | + | adc $E2 |
| − | + | pha | |
| − | sta $1800 | + | Auth_Sub_KeyProcess_ClearRam: |
| − | + | tax | |
| − | STY $266E | + | lda #$00 |
| − | STY $2674 | + | sta $2671 |
| − | STY $267C | + | Auth_Sub_KeyProcess_ClearRam_Loop: |
| − | STY $2681 | + | sta $1800,X |
| − | ldx #$00 | + | dex |
| − | + | Auth_Sub_KeyProcess_ClearRam_Check: | |
| − | + | bne Auth_Sub_KeyProcess_ClearRam_Loop | |
| − | + | sta $1800 | |
| − | dec $2681 | + | Auth_Sub_KeyProcess_SetupCounters: |
| − | dec $E1 | + | iny |
| − | bmi | + | STY $266E |
| − | + | STY $2674 | |
| − | lda $2000,Y | + | STY $267C |
| − | and $25D9,X | + | STY $2681 |
| − | beq | + | ldx #$00 |
| − | lda $2662,X | + | Auth_Sub_KeyProcess_InnerLoop: |
| − | sta $2672 | + | dec $266E |
| − | jsr $266A | + | dec $2674 |
| − | + | dec $267C | |
| − | cpx #$08 | + | dec $2681 |
| − | bmi | + | dec $E1 |
| − | jmp $25A4 | + | bmi Auth_Sub_KeyProcess_Done |
| − | + | Auth_Sub_KeyProcess_BitCheck: | |
| − | sta $E1 | + | ldy $E1 |
| − | lda #$01 | + | lda $2000,Y |
| − | sta $E0 | + | and $25D9,X |
| − | rts | + | beq Auth_Sub_KeyProcess_NextBit |
| + | lda $2662,X | ||
| + | sta $2672 | ||
| + | jsr $266A | ||
| + | Auth_Sub_KeyProcess_NextBit: | ||
| + | inx | ||
| + | cpx #$08 | ||
| + | bmi Auth_Sub_KeyProcess_BitCheck | ||
| + | jmp $25A4 | ||
| + | Auth_Sub_KeyProcess_Done: | ||
| + | pla | ||
| + | sta $E1 | ||
| + | lda #$01 | ||
| + | sta $E0 | ||
| + | rts | ||
| − | + | AuthData_Bitmasks: .byte $01,$02,$04,$08,$10,$20,$40,$80 | |
jsr $2639 | jsr $2639 | ||
| − | lda $E3 | + | lda $E3 |
| − | sec | + | sec |
| − | sbc $E4 | + | sbc $E4 |
| − | sta $E0 | + | sta $E0 |
| − | sta $E1 | + | sta $E1 |
| − | ldx #$00 | + | ldx #$00 |
| − | stx $1800 | + | stx $1800 |
| − | stx $268F | + | stx $268F |
| − | stx $26AC | + | stx $26AC |
| − | dex | + | dex |
| − | stx $26A9 | + | stx $26A9 |
| − | stx $268C | + | stx $268C |
| − | stx $2692 | + | stx $2692 |
| − | stx $269A | + | stx $269A |
| − | stx $269F | + | stx $269F |
| − | ldx #$07 | + | ldx #$07 |
| − | inc $26A9 | + | inc $26A9 |
| − | inc $268C | + | inc $268C |
| − | inc $2692 | + | inc $2692 |
| − | inc $269A | + | inc $269A |
| − | inc $269F | + | inc $269F |
| − | dec $E1 | + | dec $E1 |
| − | bmi | + | bmi Auth_Sub_Op_Done |
| − | + | Auth_Sub_Op_Loop: | |
| − | sta $2690 | + | lda $2662,X |
| − | sta $26AD | + | sta $2690 |
| − | jsr $26A6 | + | sta $26AD |
| − | bcc | + | jsr $26A6 |
| − | jsr $2688 | + | bcc Auth_Sub_Op_Next |
| − | + | jsr $2688 | |
| − | bpl | + | Auth_Sub_Op_Next: |
| − | jmp $2608 | + | dex |
| − | + | bpl Auth_Sub_Op_Loop | |
| − | sta $E1 | + | jmp $2608 |
| − | rts | + | Auth_Sub_Op_Done: |
| + | lda $E3 | ||
| + | sta $E1 | ||
| + | rts | ||
| − | ldx $E4 ;($2639) | + | ldx $E4 ; ($2639) |
| − | inx | + | inx |
| − | stx $E2 | + | stx $E2 |
| − | ldy #$00 | + | ldy #$00 |
| − | STY $1900 | + | STY $1900 |
| − | + | Auth_Sub_Rotate_OuterLoop: | |
| − | sta $2655 | + | lda $2662,Y |
| − | iny | + | sta $2655 |
| − | lda $2662,Y | + | iny |
| − | sta $2659 | + | lda $2662,Y |
| − | ldx $E2 | + | sta $2659 |
| − | clc | + | ldx $E2 |
| − | + | clc | |
| + | Auth_Sub_Rotate_InnerLoop: | ||
| + | lda $1900,X | ||
rol | rol | ||
| − | sta $1900,X | + | sta $1900,X |
| − | dex | + | dex |
| − | bpl | + | bpl Auth_Sub_Rotate_InnerLoop |
| − | cpy #$07 | + | cpy #$07 |
| − | bmi | + | bmi Auth_Sub_Rotate_OuterLoop |
| − | rts | + | rts |
| − | + | AuthData_Indices: .byte $19,$1A,$1B,$1C,$1D,$1E,$1F,$21 | |
ldy $E2 | ldy $E2 | ||
| − | clc | + | clc |
| − | + | Auth_Sub_Add_Loop: | |
| − | adc $1900,Y | + | lda $1800,Y |
| − | sta $1800,Y | + | adc $1900,Y |
| − | dey | + | sta $1800,Y |
| − | bpl | + | dey |
| − | bcc | + | bpl Auth_Sub_Add_Loop |
| − | lda $1700,Y | + | bcc Auth_Sub_Add_NoCarry |
| − | adc #$00 | + | lda $1700,Y |
| − | sta $1700,Y | + | adc #$00 |
| − | dey | + | sta $1700,Y |
| − | jmp $2679 | + | dey |
| − | + | jmp $2679 | |
| + | Auth_Sub_Add_NoCarry: | ||
| + | rts | ||
| − | ldy $E2 | + | ldy $E2 |
| − | sec | + | sec |
| − | + | Auth_Sub_Subtract_Loop: | |
| − | sbc $1900,Y | + | lda $1800,Y |
| − | sta $1800,Y | + | sbc $1900,Y |
| − | dey | + | sta $1800,Y |
| − | bpl | + | dey |
| − | bcs | + | bpl Auth_Sub_Subtract_Loop |
| − | lda $1700,Y | + | bcs Auth_Sub_Subtract_NoBorrow |
| − | sbc #$00 | + | lda $1700,Y |
| − | sta $1700,Y | + | sbc #$00 |
| − | dey | + | sta $1700,Y |
| − | jmp $2697 | + | dey |
| − | + | jmp $2697 | |
| + | Auth_Sub_Subtract_NoBorrow: | ||
| + | rts | ||
| − | ldy #$00 | + | ldy #$00 |
| − | lda $1800,Y | + | lda $1800,Y |
| − | cmp $1900,Y | + | cmp $1900,Y |
| − | beq | + | beq Auth_Sub_Compare_Equal |
| − | + | Auth_Sub_Compare_NotEqual: | |
| + | rts | ||
| − | + | Auth_Sub_Compare_Equal: | |
| − | beq | + | cpy $E2 |
| − | iny | + | beq Auth_Sub_Compare_NotEqual |
| − | jmp $26A8 | + | iny |
| + | jmp $26A8 | ||
| − | + | ; --- 7800 Mode Initialization --- | |
| − | ldx #$16 ; | + | ORG $F7B9 |
| − | stx INPTCTRL ; | + | ; This label is an equate, not a location. It calculates the correct RAM address |
| − | txs ; | + | ; for the routine, which is needed by the code executing from RAM. |
| − | SED | + | ; ROM address $F7B9 -> RAM address $26B9. Offset is $D100. |
| − | jmp (CartResetVectorLo) | + | Enter7800Mode = . - $D100 |
| + | Routine_Enter7800Mode: | ||
| + | ldx #$16 ; Enable Maria and Cartridge ROM. | ||
| + | stx INPTCTRL ; Lock out BIOS, enable cartridge. | ||
| + | txs ; Set up stack pointer. | ||
| + | SED ; Set Decimal Mode (a 7800 convention, often cleared by games). | ||
| + | jmp (CartResetVectorLo) ; Jump to the game's starting address on the cartridge. | ||
| − | ; | + | ; --- 2600 Mode Initialization --- |
| − | + | ORG $F7C2 | |
| − | lda #$02 ; Enable | + | ; This label is also an equate to calculate the correct RAM address. |
| − | sta INPTCTRL ; | + | ; ROM address $F7C2 -> RAM address $26C2. Offset is $D100. |
| + | Enter2600Mode = . - $D100 | ||
| + | Routine_Enter2600Mode: | ||
| + | lda #$02 ; Enable 7800 RAM and BIOS ROM. | ||
| + | sta INPTCTRL ; | ||
ldx #$7F | ldx #$7F | ||
| − | + | ; Copy the 2600 TIA setup code from BIOS ROM into RIOT RAM ($0480). | |
| − | sta $0480,X | + | CopyTiaInitToRam_Loop: |
| − | dex | + | lda TiaInitCode,X |
| − | bpl | + | sta $0480,X |
| − | jmp $0480 ; execute it | + | dex |
| + | bpl CopyTiaInitToRam_Loop | ||
| + | jmp $0480 ; Jump to the newly copied code in RIOT RAM to execute it. | ||
| − | ; | + | ; --- 2600 TIA Setup Code (Executed from RIOT RAM at $0480) --- |
| − | + | TiaInitCode: | |
| − | + | lda #$00 ; | |
| − | tax ; | + | tax ; |
| − | sta INPTCTRL ; Disable 7800 RAM | + | sta INPTCTRL ; Disable 7800 RAM/BIOS, giving full control to cart. |
| − | + | ; Clear all TIA registers to put it in a known state. | |
| + | TiaInit_ClearLoop: | ||
| + | sta RSYNC,X | ||
inx ; | inx ; | ||
cpx #$2A ; | cpx #$2A ; | ||
| − | bne | + | bne TiaInit_ClearLoop |
| − | sta WSYNC ; | + | sta WSYNC ; Wait for horizontal sync. |
lda #$04 ; | lda #$04 ; | ||
nop ; | nop ; | ||
| − | bmi | + | bmi TiaInit_SetColors1 |
| − | ldx #$04 | + | ldx #$04 |
| − | + | TiaInit_DelayLoop: | |
| − | bpl | + | dex |
| − | txs | + | bpl TiaInit_DelayLoop |
| − | sta $0110 | + | txs |
| − | jsr $04CB | + | sta $0110 |
| − | jsr $04CB | + | jsr $04CB |
| − | sta RESP1 | + | jsr $04CB |
| − | sta GRP0 | + | sta RESP1 |
| − | sta GRP1 | + | sta GRP0 |
| − | sta PF2 | + | sta GRP1 |
| − | nop | + | sta PF2 |
| − | sta WSYNC | + | nop |
| − | lda #$00 | + | sta WSYNC |
| − | nop | + | lda #$00 |
| − | bmi | + | nop |
| − | bit RSYNC | + | bmi TiaInit_SetColors1 |
| − | bmi | + | bit RSYNC |
| − | + | bmi TiaInit_SetColors2 | |
| − | sta COLUBK | + | TiaInit_SetColors1: |
| − | sta LF112 | + | lda #$02 |
| − | bne | + | sta COLUBK |
| − | + | sta LF112 | |
| − | bmi | + | bne TiaInit_Finalize |
| − | lda #$02 | + | TiaInit_SetColors2: |
| − | sta COLUP0 | + | bit WSYNC |
| − | sta LF118 | + | bmi TiaInit_SetColors3 |
| − | sta LF460 | + | lda #$02 |
| − | bne | + | sta COLUP0 |
| − | + | sta LF118 | |
| − | lda #$08 | + | sta LF460 |
| − | sta GRP0 | + | bne TiaInit_Finalize |
| − | jsr $04CB | + | TiaInit_SetColors3: |
| − | nop | + | sta DPPH |
| − | bit WSYNC | + | lda #$08 |
| − | bmi | + | sta GRP0 |
| − | + | jsr $04CB | |
| − | sta COLUPF | + | nop |
| − | jmp (CartResetVectorLo) | + | bit WSYNC |
| − | nop | + | bmi TiaInit_SetColors1 |
| + | TiaInit_Finalize: | ||
| + | lda #$FD | ||
| + | sta COLUPF | ||
| + | jmp (CartResetVectorLo) ; Jump to the 2600 game's starting address on the cartridge. | ||
| + | nop | ||
| + | |||
| + | ;============================================================================= | ||
| + | ; | ||
| + | ; III. BIOS ENTRY, POWER-ON SELF TEST (POST), AND SYSTEM INIT | ||
| + | ; | ||
| + | ;============================================================================= | ||
ORG $F880 | ORG $F880 | ||
| − | + | ; --- Self-Test Failure Handler --- | |
| − | sta INPTCTRL ; | + | ; This code is jumped to whenever a POST routine fails. It writes an error |
| + | ; code to INPTCTRL, which can be read by external diagnostic hardware. | ||
| + | Post_FailHandler: | ||
| + | lda #$1D | ||
| + | sta INPTCTRL ; Enable TIA/Cart/lock out INPTCTRL. | ||
| − | START: ; $F884 | + | ; --- BIOS Entry Point / Cold Start --- |
| − | SEI ; | + | START: ; $F884 |
| − | CLD ; Clear decimal flag | + | SEI ; Disable interrupts on power-on. |
| + | CLD ; Clear decimal flag. | ||
lda #$02 ; | lda #$02 ; | ||
| − | + | Bios_SetRamAndBios: | |
| − | lda #$FB ; | + | sta INPTCTRL ; Enable 7800 RAM and map BIOS into memory. |
| + | lda #$FB ; Set high byte of NMI vector to $FB. | ||
sta $F5 ; | sta $F5 ; | ||
| − | lda #$12 ; | + | lda #$12 ; Set low byte of NMI vector to $12. |
| − | sta $F4 ; | + | sta $F4 ; NMI vector is now $FB12 (points to IRQ cleanup). |
lda #$7F ; | lda #$7F ; | ||
| − | sta CTRL ; Turn off DMA | + | sta CTRL ; Turn off Maria DMA. |
lda #$00 ; | lda #$00 ; | ||
| − | sta BACKGRND ; | + | sta BACKGRND ; Set background color to black. |
| − | ldx #$05 ; | + | |
| − | + | ; --- POST: RAM Test 1 (Basic) --- | |
| − | ldy #$00 ; | + | ; This is a quick test of the two 2KB RAM chips ($2000-$27FF). |
| − | + | ; It writes several patterns to each byte and reads them back. | |
| − | cmp $2000,Y ; | + | ldx #$05 ; Loop counter for the 6 test patterns. |
| − | bne | + | Post_RamTest1_PatternLoop: |
| − | sta $2100,Y ; | + | lda Post_RamTest_Data,X ; Load a test pattern ($00, $FF, $55, $AA, $69, $0F). |
| − | cmp $2100,Y ; | + | ldy #$00 ; Inner loop counter for 256 bytes. |
| − | bne | + | Post_RamTest1_PageLoop: |
| + | sta $2000,Y ; Write pattern to first RAM chip. | ||
| + | cmp $2000,Y ; Read it back and compare. | ||
| + | bne Post_RamTest_Fail1 ; If it fails, branch to error handler. | ||
| + | sta $2100,Y ; Write pattern to second RAM chip. | ||
| + | cmp $2100,Y ; Read it back and compare. | ||
| + | bne Post_RamTest_Fail1 ; If it fails, branch to error handler. | ||
dey ; | dey ; | ||
| − | bne | + | bne Post_RamTest1_PageLoop ; Loop through all 256 bytes of a page. |
dex ; | dex ; | ||
| − | bpl | + | bpl Post_RamTest1_PatternLoop ; Loop to the next test pattern. |
| − | lda #$43 ; Check RAM 0 mirror | + | |
| − | sta $2080 ; | + | ; --- POST: RAM Mirror Test --- |
| − | cmp $80 ; | + | ; Checks that the RAM is correctly mirrored every 256 bytes. |
| − | bne | + | lda #$43 ; Check RAM 0 mirror. |
| + | sta $2080 ; Write to $2080. | ||
| + | cmp $80 ; Compare with $0080 (a mirror). | ||
| + | bne Post_RamMirror_Fail ; make sure they match. If not, fail selftest. | ||
sta $2180 ; Check RAM 1 mirror | sta $2180 ; Check RAM 1 mirror | ||
| − | cmp $0180 ; | + | cmp $0180 ; |
| − | bne | + | bne Post_RamMirror_Fail ; make sure they match. If not, fail selftest. |
| − | jmp | + | jmp Post_CpuTest_Start ; continue selftest |
| − | + | Post_RamMirror_Fail: | |
| − | jmp | + | ldy #$04 ; |
| + | jmp Post_FailHandler ; selftest fail. | ||
| − | + | ; --- POST: RAM Failure Handlers --- | |
| − | cmp $1800 | + | ; These routines are jumped to from the RAM tests to indicate failure. |
| − | bne | + | Post_RamTest_Fail1: |
| − | + | sta $1800 ; test store and compare | |
| − | jmp | + | cmp $1800 |
| + | bne Post_RamTest_Fail3 ; some kind of error code is being determined | ||
| + | Post_RamTest_Fail2: | ||
| + | ldy #$01 | ||
| + | jmp Post_FailHandler | ||
| − | + | Post_RamTest_Fail_Indirect: | |
| + | ldy #$02 | ||
jmp $F880 | jmp $F880 | ||
| − | + | Post_RamTest_Fail3: | |
| − | jmp | + | ldy #$03 |
| + | jmp Post_FailHandler | ||
| − | + | ; --- POST: RAM Test 2 (Comprehensive) --- | |
| − | sta $F0 ; | + | ; This is a more thorough RAM test called later in the boot process. It uses |
| − | sta $F2 ; | + | ; indirect addressing to test all 8 pages of the 4KB RAM. |
| − | ldy #$07 | + | Post_RamTest2_Start: |
| − | STY $F4 | + | lda #$00 |
| − | + | sta $F0 ; Base address low byte pointer. | |
| − | sta $F1 | + | sta $F2 ; |
| − | lda | + | ldy #$07 ; Loop counter for 8 pages. |
| − | sta $F3 | + | STY $F4 |
| − | ldx #$05 | + | Post_RamTest2_OuterLoop: |
| − | + | lda Post_RamTest_AddrHi1,Y ; Load high byte of address for RAM chip 1. | |
| − | + | sta $F1 | |
| − | + | lda Post_RamTest_AddrHi2,Y ; Load high byte of address for RAM chip 2. | |
| − | cmp ($F0),Y | + | sta $F3 |
| − | bne | + | ldx #$05 ; Loop counter for 6 test patterns. |
| − | sta ($F2),Y | + | Post_RamTest2_PatternLoop: |
| − | cmp ($F2),Y | + | lda Post_RamTest_Data,X ; Load a test pattern. |
| − | bne | + | Post_RamTest2_InnerLoop: |
| − | dey | + | ldy #$00 ; Inner loop counter for 256 bytes. |
| − | bne | + | Post_RamTest2_ByteLoop: |
| − | dex | + | sta ($F0),Y ; Write to RAM 1 via indirect pointer ($F0/$F1). |
| − | bpl | + | cmp ($F0),Y ; Read back and compare. |
| − | dec $F4 | + | bne Post_RamTest_Fail2 ; Fail. |
| − | ldy $F4 | + | sta ($F2),Y ; Write to RAM 2 via indirect pointer ($F2/$F3). |
| − | bpl | + | cmp ($F2),Y ; Read back and compare. |
| − | jmp | + | bne Post_RamTest_Fail_Indirect ; Fail. |
| − | + | dey | |
| − | + | bne Post_RamTest2_ByteLoop ; Loop through page. | |
| − | + | dex | |
| − | + | bpl Post_RamTest2_PatternLoop ; Loop through patterns. | |
| − | + | dec $F4 | |
| + | ldy $F4 | ||
| + | bpl Post_RamTest2_OuterLoop ; Loop through pages. | ||
| + | jmp Bios_InitSystem ; ram test passed, so jump in here. | ||
| − | + | ; --- RAM Test Data --- | |
| − | + | Post_RamTest_Data: .byte $00,$FF,$55,$AA,$69,$0F | |
| + | Post_RamTest_AddrHi1: .byte $22,$23,$24,$25,$26,$27,$22,$23 | ||
| + | Post_RamTest_AddrHi2: .byte $18,$19,$1A,$1B,$1C,$1D,$1E,$1F | ||
| − | ; | + | Post_CpuTest_Fail: |
| + | ldy #$00 ; local place for selftest fail branch target | ||
| + | jmp Post_FailHandler | ||
| − | + | ; --- POST: 6502 CPU Test --- | |
| − | beq | + | ; This section exhaustively tests the 6502's instruction set, flags, |
| − | bpl | + | ; and addressing modes to ensure the CPU is functioning correctly. |
| − | bmi | + | Post_CpuTest_Start: |
| − | jmp | + | lda #$AA ; test some flags and branches |
| − | + | beq Post_CpuTest_Fail ; test failed | |
| − | jmp | + | bpl Post_CpuTest_Fail ; test failed |
| − | + | bmi CpuTest_CheckBne ; test passed | |
| + | jmp Post_CpuTest_Fail ; test failed | ||
| + | CpuTest_CheckBne: | ||
| + | bne CpuTest_CheckStore ; test passed | ||
| + | jmp Post_CpuTest_Fail ; test failed | ||
| + | CpuTest_CheckStore: | ||
| + | sta $AA ; store AA to 00AA | ||
cmp $AA ; compare it back | cmp $AA ; compare it back | ||
| − | bne | + | bne Post_CpuTest_Fail ; if it doesn't match, selftest fail |
lda #$00 ; do some more flag tests | lda #$00 ; do some more flag tests | ||
| − | bne | + | bne Post_CpuTest_Fail ; |
| − | bmi | + | bmi Post_CpuTest_Fail ; |
| − | bpl | + | bpl CpuTest_CheckBeq ; test passed |
| − | jmp | + | jmp Post_CpuTest_Fail ; |
| − | + | CpuTest_CheckBeq: | |
| − | jmp | + | beq CpuTest_CheckCmp ; test passed |
| − | + | jmp Post_CpuTest_Fail ; | |
| − | bne | + | CpuTest_CheckCmp: |
| − | bcc | + | cmp #$00 ; test the compare instruction |
| − | bcs | + | bne Post_CpuTest_Fail ; |
| − | jmp | + | bcc Post_CpuTest_Fail ; |
| − | + | bcs CpuTest_CheckCmpBcs ; test passed, since they're equal | |
| − | bcs | + | jmp Post_CpuTest_Fail ; |
| − | bcc | + | CpuTest_CheckCmpBcs: |
| − | jmp | + | cmp #$01 ; compare it to 01 |
| − | + | bcs Post_CpuTest_Fail ; | |
| + | bcc CpuTest_CheckCpx ; A < 01, so carry is clear | ||
| + | jmp Post_CpuTest_Fail ; | ||
| + | CpuTest_CheckCpx: | ||
| + | ldx #$55 ; test comparisons with the X register | ||
cpx #$56 ; | cpx #$56 ; | ||
| − | beq | + | beq Post_CpuTest_Fail ; |
stx $01AA ; | stx $01AA ; | ||
cpx $01AA ; | cpx $01AA ; | ||
| − | bne | + | bne Post_CpuTest_Fail ; |
ldy $AA ; and with the Y register. | ldy $AA ; and with the Y register. | ||
cpy #$AB ; | cpy #$AB ; | ||
| − | + | CpuTest_CheckCpy: | |
| + | beq Post_CpuTest_Fail ; | ||
STY $0155 ; put some stuff in the stack area to test stack | STY $0155 ; put some stuff in the stack area to test stack | ||
| − | cpy $0155 ; and then access this data in many | + | cpy $0155 ; and then access this data in many different ways |
| − | bne | + | bne Post_CpuTest_Fail ; |
dex ; | dex ; | ||
txs ; | txs ; | ||
| Line 636: | Line 802: | ||
pla ; | pla ; | ||
cmp #$AA ; | cmp #$AA ; | ||
| − | bne | + | bne Post_CpuTest_Fail_Central ; |
txa ; | txa ; | ||
pha ; | pha ; | ||
cpx $0155 ; | cpx $0155 ; | ||
| − | bne | + | bne Post_CpuTest_Fail_Central ; |
TYA ; | TYA ; | ||
cmp #$AA ; | cmp #$AA ; | ||
| − | bne | + | bne Post_CpuTest_Fail_Central ; |
tax ; | tax ; | ||
lda $0100,X ; | lda $0100,X ; | ||
tay ; | tay ; | ||
cpy #$55 ; | cpy #$55 ; | ||
| − | bne | + | bne Post_CpuTest_Fail_Central ; |
lda VSYNC,X ; | lda VSYNC,X ; | ||
cmp $AA ; | cmp $AA ; | ||
| − | bne | + | bne Post_CpuTest_Fail_Central ; |
cmp #$AA ; | cmp #$AA ; | ||
| − | bne | + | bne Post_CpuTest_Fail_Central ; |
eor #$FF ; | eor #$FF ; | ||
sta $0000,Y ; | sta $0000,Y ; | ||
cmp $55 ; | cmp $55 ; | ||
| − | bne | + | bne Post_CpuTest_Fail_Central ; |
cmp $0100,Y ; | cmp $0100,Y ; | ||
| − | bne | + | bne Post_CpuTest_Fail_Central ; |
cmp $20AB,X ; | cmp $20AB,X ; | ||
| − | bne | + | bne Post_CpuTest_Fail_Central ; |
lda #$20 ; | lda #$20 ; | ||
sta $F1 ; | sta $F1 ; | ||
| Line 668: | Line 834: | ||
sta ($46,X) ; | sta ($46,X) ; | ||
cmp $CC ; | cmp $CC ; | ||
| − | bne | + | bne Post_CpuTest_Fail_Central ; |
sta ($F0),Y ; | sta ($F0),Y ; | ||
cmp $2121 ; | cmp $2121 ; | ||
| − | bne | + | bne Post_CpuTest_Fail_Central ; |
lda #$EE ; test the indirect jump by setting up a jump | lda #$EE ; test the indirect jump by setting up a jump | ||
| − | sta $F0 ; to | + | sta $F0 ; to Post_CpuMath_Start |
lda #$F9 ; | lda #$F9 ; | ||
sta $F1 ; | sta $F1 ; | ||
jmp ($00F0) ; and do it. | jmp ($00F0) ; and do it. | ||
| − | + | CpuTest_ShouldNotBeReached: | |
| + | jmp Post_CpuTest_Fail_Central | ||
| − | + | Post_CpuTest_Fail_Central: | |
| + | jmp Post_CpuTest_Fail | ||
| − | + | ; --- POST: CPU Math and Logic Tests --- | |
| − | clc | + | Post_CpuMath_Start: |
| − | adc #$55 | + | lda #$55 ; now test out the math functions. |
| − | nop | + | clc |
| − | bcs | + | adc #$55 |
| − | bpl | + | nop |
| − | beq | + | bcs Post_CpuTest_Fail_Central ; test addition. |
| + | bpl Post_CpuTest_Fail_Central | ||
| + | beq Post_CpuTest_Fail_Central | ||
cmp #$AA ; make sure it worked | cmp #$AA ; make sure it worked | ||
| − | bne | + | bne Post_CpuTest_Fail_Central |
adc #$55 ; test addition again. | adc #$55 ; test addition again. | ||
| − | + | CpuMath_TestAdcCarry: | |
| − | bcc | + | nop |
| − | bmi | + | bcc Post_CpuTest_Fail_Central |
| − | bne | + | bmi Post_CpuTest_Fail_Central |
| + | bne Post_CpuTest_Fail_Central | ||
sbc #$55 ; test subtraction | sbc #$55 ; test subtraction | ||
| − | bcs | + | bcs Post_CpuTest_Fail_Central |
| − | bpl | + | bpl Post_CpuTest_Fail_Central |
| − | beq | + | beq Post_CpuTest_Fail_Central |
cmp #$AB ; make sure it worked | cmp #$AB ; make sure it worked | ||
| − | bne | + | bne Post_CpuTest_Fail_Central |
| − | clc | + | clc |
sbc #$AA ; test subtraction again | sbc #$AA ; test subtraction again | ||
| − | bcc | + | bcc Post_CpuTest_Fail_Central |
| − | bmi | + | bmi Post_CpuTest_Fail_Central |
| − | bne | + | bne Post_CpuTest_Fail_Central |
lda #$FF ; set up a stack | lda #$FF ; set up a stack | ||
tax ; and do all kinds of stuff in it for tests | tax ; and do all kinds of stuff in it for tests | ||
| − | inx | + | inx |
| − | bne | + | bne CpuMath_Fail |
| − | dex | + | dex |
| − | beq | + | beq CpuMath_Fail |
| − | bpl | + | bpl CpuMath_Fail |
| − | cpx #$FF | + | cpx #$FF |
| − | bne | + | bne CpuMath_Fail |
| − | tay | + | tay |
| − | iny | + | iny |
| − | bne | + | bne CpuMath_Fail |
| − | dey | + | dey |
| − | beq | + | beq CpuMath_Fail |
| − | iny | + | iny |
| − | bne | + | bne CpuMath_Fail |
| − | sta $F0 | + | sta $F0 |
| − | inc $F0 | + | inc $F0 |
| − | bne | + | bne CpuMath_Fail |
| − | cpy $F0 | + | cpy $F0 |
| − | bne | + | bne CpuMath_Fail |
| − | dec $F0 | + | dec $F0 |
| − | beq | + | beq CpuMath_Fail |
| − | cmp $F0 | + | cmp $F0 |
| − | bne | + | bne CpuMath_Fail |
| − | lda #$AA | + | lda #$AA |
| − | clc | + | clc |
rol ; now we get onto the more complex math instrs | rol ; now we get onto the more complex math instrs | ||
rol | rol | ||
rol | rol | ||
| − | cmp #$52 | + | cmp #$52 |
| − | bne | + | bne CpuMath_Fail ; make sure rotate left works. |
ror | ror | ||
ror | ror | ||
ror | ror | ||
| − | cmp #$AA | + | cmp #$AA |
| − | beq | + | beq CpuMath_TestAsl ; test rotate right |
| − | + | CpuMath_Fail: | |
| + | jmp Post_CpuTest_Fail ; fail! | ||
| − | + | CpuMath_TestAsl: | |
| − | bcc | + | asl ; test arithmetic shift left |
| − | asl | + | bcc CpuMath_Fail |
| − | bcs | + | asl |
| − | asl | + | bcs CpuMath_Fail |
| − | cmp #$50 | + | asl |
| − | bne | + | cmp #$50 |
| − | eor #$05 | + | bne CpuMath_Fail |
| + | eor #$05 | ||
lsr ; and logical shift right | lsr ; and logical shift right | ||
| − | bcc | + | bcc CpuMath_Fail |
| − | lsr | + | lsr |
| − | bcs | + | bcs CpuMath_Fail |
| − | lsr | + | lsr |
| − | cmp #$0A | + | cmp #$0A |
| − | bne | + | bne CpuMath_Fail |
lda #$55 ; now test the ands and ors. | lda #$55 ; now test the ands and ors. | ||
| − | ora #$1B | + | ora #$1B |
| − | cmp #$5F | + | cmp #$5F |
| − | bne | + | bne CpuMath_Fail |
| − | and #$55 | + | and #$55 |
| − | and #$1B | + | and #$1B |
| − | cmp #$11 | + | cmp #$11 |
| − | bne | + | bne CpuMath_Fail |
| − | ora #$55 | + | ora #$55 |
eor #$1B ; and the eors | eor #$1B ; and the eors | ||
| − | cmp #$4E | + | cmp #$4E |
| − | bne | + | bne CpuMath_Fail |
| − | jsr | + | jsr Cpu_TestJsr ; test jump subroutine instruction |
| − | jmp | + | jmp CpuMath_Fail ; if we return, fail |
| − | + | Cpu_TestJsr: | |
| + | tsx | ||
cpx #$52 ; check stack pointer | cpx #$52 ; check stack pointer | ||
| − | bne | + | bne CpuMath_Fail ; fail if not right |
| − | pla | + | pla |
| − | cmp #$8D | + | cmp #$8D |
| − | bne | + | bne CpuMath_Fail |
| − | pla | + | pla |
| − | cmp #$FA | + | cmp #$FA |
| − | bne | + | bne CpuMath_Fail ; get the old return address off the stack |
| − | lda #$F8 | + | lda #$F8 |
| − | pha | + | pha |
| − | lda #$E6 | + | lda #$E6 |
pha ; and make our own | pha ; and make our own | ||
| − | rts ; and 'return' to | + | rts ; and 'return' to Post_RamTest2_Start |
| − | jmp | + | jmp CpuMath_Fail ; another jump to catch a failure |
| + | |||
| + | ;============================================================================= | ||
| + | ; | ||
| + | ; IV. NMI HANDLER (FUJI LOGO DISPLAY) | ||
| + | ; | ||
| + | ;============================================================================= | ||
| − | ; | + | ; This NMI routine is responsible for drawing the "Fuji" mountain logo and |
| + | ; the "ATARI" text on the screen during the boot-up/authentication process. | ||
| + | ; It is called on every VBLANK. Its main job is to manipulate Maria's palette | ||
| + | ; registers to create the signature color-cycling effect on the Fuji logo. | ||
| − | + | Nmi_DrawFujiAndAtari: | |
| − | pha ; | + | txa ;Save A |
| − | lda #$43 ;Setup control register | + | pha ; |
| − | sta CTRL ; | + | lda #$43 ;Setup control register |
| − | ldx #$0F ; Handle the color scrolling in the FUJI | + | sta CTRL ; |
| − | lda $EF | + | ldx #$0F ; Handle the color scrolling in the FUJI |
| + | lda $EF | ||
sta P0C2 | sta P0C2 | ||
| − | bit $F3 | + | bit $F3 |
| − | bvc | + | bvc Nmi_Sync3 |
| − | bpl | + | bpl Nmi_Sync2 |
| − | + | Nmi_Sync1: | |
| − | + | sta MWSYNC ;Wait for 3 scanlines | |
| − | + | Nmi_Sync2: | |
| − | sec | + | sta MWSYNC ; |
| − | sbc #$10 | + | Nmi_Sync3: |
| − | cmp #$10 | + | sta MWSYNC ; |
| − | bcs | + | sec |
| − | sbc #$0F | + | sbc #$10 |
| − | + | cmp #$10 | |
| − | dex | + | bcs Nmi_StoreColor |
| − | bpl | + | sbc #$0F |
| + | Nmi_StoreColor: | ||
| + | sta P0C2 | ||
| + | dex | ||
| + | bpl Nmi_Sync1 ;Branch to do next section of fuji | ||
ldx #$40 ;set 160x2/160x4 mode | ldx #$40 ;set 160x2/160x4 mode | ||
| − | stx CTRL ; | + | stx CTRL ; |
and #$F0 ; | and #$F0 ; | ||
ora #$0E ;set Palette 1 color 3 | ora #$0E ;set Palette 1 color 3 | ||
| Line 826: | Line 1,014: | ||
ora #$06 ;set Palette 1 color 1 | ora #$06 ;set Palette 1 color 1 | ||
sta P1C1 ; | sta P1C1 ; | ||
| − | and #$F0 | + | and #$F0 |
| − | clc | + | clc |
| − | adc #$40 | + | adc #$40 |
| − | bcc | + | bcc Nmi_SetPalette1Color2 |
| − | adc #$0F | + | adc #$0F |
| − | + | Nmi_SetPalette1Color2: | |
| − | sta P1C2 | + | ora #$03 ;set Palette 1 color 2 |
| − | dec $F1 | + | sta P1C2 |
| − | bpl | + | dec $F1 |
| − | lda $F3 | + | bpl Nmi_RestoreRegs |
| − | adc #$60 | + | lda $F3 |
| − | bcc | + | adc #$60 |
| − | lda $EF | + | bcc Nmi_UpdateFrameCounter |
| − | clc | + | lda $EF |
| − | adc #$10 | + | clc |
| − | bcc | + | adc #$10 |
| − | adc #$0F | + | bcc Nmi_StoreNewBaseColor |
| − | + | adc #$0F | |
| − | lda $F2 | + | Nmi_StoreNewBaseColor: |
| − | sta $F1 | + | sta $EF |
| − | lda #$00 | + | lda $F2 |
| − | + | sta $F1 | |
| − | + | lda #$00 | |
| − | sta $F0 | + | Nmi_UpdateFrameCounter: |
| − | pla | + | sta $F3 |
| − | tax | + | Nmi_RestoreRegs: |
| − | pla | + | lda #$02 |
| − | rti | + | sta $F0 |
| + | pla | ||
| + | tax | ||
| + | pla | ||
| + | rti | ||
| + | |||
| + | Trap_InfiniteLoop: | ||
| + | jmp Trap_InfiniteLoop | ||
| − | + | ;============================================================================= | |
| + | ; | ||
| + | ; V. SYSTEM INITIALIZATION (POST-TEST) | ||
| + | ; | ||
| + | ;============================================================================= | ||
| − | + | ; This routine is executed after all Power-On Self Tests have passed. | |
| + | Bios_InitSystem: | ||
| + | ldx #$FF ; selftest has passed, start system init | ||
txs ; set up a stack | txs ; set up a stack | ||
lda #$00 ; Clear TIA/Maria registers | lda #$00 ; Clear TIA/Maria registers | ||
tax ; | tax ; | ||
| − | + | Bios_ClearRegsLoop: | |
| + | sta $01,X ; | ||
inx ; | inx ; | ||
cpx #$2C ; | cpx #$2C ; | ||
| − | bne | + | bne Bios_ClearRegsLoop |
lda #$02 ; | lda #$02 ; | ||
| − | sta INPTCTRL ; Enable 7800 RAM | + | sta INPTCTRL ; Enable 7800 RAM |
ldx #$00 ; | ldx #$00 ; | ||
| − | stx BACKGRND ; Set background color | + | stx BACKGRND ; Set background color |
| − | + | ||
| + | ; --- Copy BIOS Routines and Data to RAM --- | ||
| + | ; This is a critical step. It copies the cartridge authentication code, | ||
| + | ; graphics display routines, and associated data from the BIOS ROM | ||
| + | ; into the main system RAM. This allows the BIOS to run its complex | ||
| + | ; authentication logic from RAM, freeing up the cartridge bus. | ||
| + | Bios_CopyCodeToRam_Loop: | ||
| + | lda RamExec_Start,X ; copy the authentication and title screen to | ||
sta $2300,X ; ram | sta $2300,X ; ram | ||
| − | lda | + | lda Auth_Sub_MixAndLookup_Equate,X ; |
sta $2400,X ; | sta $2400,X ; | ||
| − | lda | + | lda AuthData_Table8,X ; |
sta $2500,X ; | sta $2500,X ; | ||
lda LF700,X ; | lda LF700,X ; | ||
| Line 879: | Line 1,088: | ||
lda LF800,X ; | lda LF800,X ; | ||
sta $2700,X ; | sta $2700,X ; | ||
| − | lda | + | lda DisplayList_Defs,X ; |
sta $2200,X ; | sta $2200,X ; | ||
cpx #$00 ; | cpx #$00 ; | ||
| − | bmi | + | bmi Bios_CopyCodeToRam_Done ; |
| − | lda | + | lda DisplayList_Master,X ; |
sta $1F84,X ; | sta $1F84,X ; | ||
| − | lda | + | lda GraphicsData_FujiAtari_1,X ; |
sta $1984,X ; | sta $1984,X ; | ||
lda LFD3D,X ; | lda LFD3D,X ; | ||
| Line 897: | Line 1,106: | ||
lda LFE96,X ; | lda LFE96,X ; | ||
sta $1E84,X ; | sta $1E84,X ; | ||
| − | + | Bios_CopyCodeToRam_Done: | |
| − | bne | + | dex ; |
| − | jmp $2306 ; and execute it | + | bne Bios_CopyCodeToRam_Loop ; |
| + | jmp $2306 ; and execute it (CartTest) | ||
;Start display of Atari logo | ;Start display of Atari logo | ||
| − | + | Display_InitLogo: | |
| − | + | lda CartKeyStartPage ; Read ROM start byte from cart | |
and #$04 ; Is rom start greater then $4000? | and #$04 ; Is rom start greater then $4000? | ||
| − | beq | + | beq Display_InitLogo_Done ; Branch if not |
| − | lda #$03 | + | lda #$03 |
| − | sta $F1 | + | sta $F1 |
| − | sta $F2 | + | sta $F2 |
| − | lda #$49 | + | lda #$49 |
| − | sta $EF | + | sta $EF |
lda #$66 ;Palette 1 Color 1 | lda #$66 ;Palette 1 Color 1 | ||
sta P1C1 ; | sta P1C1 ; | ||
| Line 917: | Line 1,127: | ||
lda #$2E ;Palette 1 Color 3 | lda #$2E ;Palette 1 Color 3 | ||
sta P1C3 ; | sta P1C3 ; | ||
| − | lda #$AA ;Set NMI vector to | + | lda #$AA ;Set NMI vector to Nmi_DrawFujiAndAtari |
sta $F4 ; | sta $F4 ; | ||
lda #$FA ; | lda #$FA ; | ||
sta $F5 ; | sta $F5 ; | ||
| − | + | Display_WaitForVblankEnd: | |
| − | bmi | + | bit MSTAT ;Check VBLANK status |
| − | + | bmi Display_WaitForVblankEnd ;Wait till end of VBLANK | |
| − | bpl | + | Display_WaitForVblankStart: |
| + | bit MSTAT ;Check BLANK status | ||
| + | bpl Display_WaitForVblankStart ;Wait for start of next VBLANK | ||
lda #$84 ;Set Display list pointer to $1f84 | lda #$84 ;Set Display list pointer to $1f84 | ||
sta DPPL ; | sta DPPL ; | ||
| Line 931: | Line 1,143: | ||
lda #$43 ;Maria mode = DMA on/320A or 320C | lda #$43 ;Maria mode = DMA on/320A or 320C | ||
sta CTRL ; | sta CTRL ; | ||
| − | + | Display_InitLogo_Done: | |
| + | rts ; | ||
| − | ; | + | ;============================================================================= |
| + | ; | ||
| + | ; VI. GRAPHICS DATA AND DISPLAY LISTS | ||
| + | ; | ||
| + | ;============================================================================= | ||
| − | + | ; --- Display List Definitions for Fuji/Atari Logo --- | |
| + | ; Each entry defines a block of scanlines, pointing to the graphics data | ||
| + | ; to be displayed. Copied to $2200 in RAM. | ||
| + | DisplayList_Defs: ;$2200 | ||
.byte $84,$1F,$19,$BB ;$2200 Blank space | .byte $84,$1F,$19,$BB ;$2200 Blank space | ||
| Line 969: | Line 1,189: | ||
.byte $AF,$2C,$1D,$00 ;$2241 End of Atari | .byte $AF,$2C,$1D,$00 ;$2241 End of Atari | ||
| − | .byte $AF,$2C,$1D,$50 | + | .byte $AF,$2C,$1D,$50 |
.byte $00,$00 | .byte $00,$00 | ||
| Line 980: | Line 1,200: | ||
.byte $D5,$2D,$19,$28 ;$2257 Atari line 3 | .byte $D5,$2D,$19,$28 ;$2257 Atari line 3 | ||
.byte $00,$00 | .byte $00,$00 | ||
| − | + | ||
.byte $E8,$2D,$19,$28 ;$225D Atari line 4 | .byte $E8,$2D,$19,$28 ;$225D Atari line 4 | ||
.byte $00,$00 | .byte $00,$00 | ||
| Line 1,005: | Line 1,225: | ||
.byte $00,$00 | .byte $00,$00 | ||
| − | ; | + | ; --- Master Display List for Boot Screen --- |
| − | + | ; This is the main display list that Maria reads. It is a list of pointers | |
| − | + | ; to the display list definitions above. Copied to $1F84 in RAM. | |
| + | DisplayList_Master: | ||
| + | .byte $0F,$22,$06 ;Blank space | ||
.byte $0F,$22,$00 ; | .byte $0F,$22,$00 ; | ||
.byte $0F,$22,$00 ; | .byte $0F,$22,$00 ; | ||
.byte $0F,$22,$00 ; | .byte $0F,$22,$00 ; | ||
.byte $03,$22,$00 ; | .byte $03,$22,$00 ; | ||
| − | .byte $85,$22,$0D ;DLI | + | .byte $85,$22,$0D ;DLI - Triggers NMI for color cycling |
.byte $05,$22,$13 ;Draw Fuji | .byte $05,$22,$13 ;Draw Fuji | ||
.byte $05,$22,$19 ; | .byte $05,$22,$19 ; | ||
| Line 1,021: | Line 1,243: | ||
.byte $0F,$22,$00 ;Blank area | .byte $0F,$22,$00 ;Blank area | ||
.byte $01,$22,$37 ;Draw "ATARI" | .byte $01,$22,$37 ;Draw "ATARI" | ||
| − | .byte $00,$22,$4B ;Line 1 | + | .byte $00,$22,$4B ;Line 1 |
.byte $02,$22,$37 | .byte $02,$22,$37 | ||
.byte $00,$22,$51 ;Line 2 | .byte $00,$22,$51 ;Line 2 | ||
| Line 1,049: | Line 1,271: | ||
.byte $0F,$22,$00 ; | .byte $0F,$22,$00 ; | ||
| − | + | ; --- Fuji/Atari Logo Graphics Data --- | |
| + | ; These are the raw bitmap graphics for the logo, copied to RAM. | ||
| + | GraphicsData_FujiAtari_1: | ||
.byte $00,$7c,$7f,$8f,$80,$fc,$7f,$8f,$c0,$1f,$87,$f8,$7e,$0f,$e0,$7f | .byte $00,$7c,$7f,$8f,$80,$fc,$7f,$8f,$c0,$1f,$87,$f8,$7e,$0f,$e0,$7f | ||
| − | + | GraphicsData_FujiAtari_2: | |
.byte $81,$fc,$07,$ff,$80,$7f,$80,$7f,$f8,$1f,$ff,$f0,$00,$7f,$80,$03 | .byte $81,$fc,$07,$ff,$80,$7f,$80,$7f,$f8,$1f,$ff,$f0,$00,$7f,$80,$03 | ||
| − | + | GraphicsData_FujiAtari_3: | |
.byte $ff,$fe,$1f,$00,$00,$00,$7f,$80,$00,$00,$3e,$00,$00,$0c,$00,$3f | .byte $ff,$fe,$1f,$00,$00,$00,$7f,$80,$00,$00,$3e,$00,$00,$0c,$00,$3f | ||
| − | + | GraphicsData_FujiAtari_4: | |
.byte $ff,$ff,$ff,$f0,$00,$c0,$00,$00,$3f,$ff,$ff,$00,$03,$fc,$00,$00 | .byte $ff,$ff,$ff,$f0,$00,$c0,$00,$00,$3f,$ff,$ff,$00,$03,$fc,$00,$00 | ||
| − | + | GraphicsData_FujiAtari_5: | |
.byte $3f,$00,$3f,$ff,$ff,$ff,$f0,$03,$f0,$00,$00,$3f,$ff,$ff,$fc,$03 | .byte $3f,$00,$3f,$ff,$ff,$ff,$f0,$03,$f0,$00,$00,$3f,$ff,$ff,$fc,$03 | ||
| − | + | GraphicsData_FujiAtari_6: | |
.byte $fc,$00,$00,$ff,$c0,$00,$03,$ff,$00,$00,$0f,$fc,$00,$00,$3f,$f0 | .byte $fc,$00,$00,$ff,$c0,$00,$03,$ff,$00,$00,$0f,$fc,$00,$00,$3f,$f0 | ||
| − | + | GraphicsData_FujiAtari_7: | |
.byte $03,$ff,$c3,$fc,$00,$03,$ff,$f0,$00,$03,$ff,$00,$00,$3f,$ff,$00 | .byte $03,$ff,$c3,$fc,$00,$03,$ff,$f0,$00,$03,$ff,$00,$00,$3f,$ff,$00 | ||
| − | LFD36 | + | LFD36: |
.byte $00,$3f,$f0,$00,$3f,$c3,$fc,$00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80 | .byte $00,$3f,$f0,$00,$3f,$c3,$fc,$00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80 | ||
| − | LFD46 | + | LFD46: |
.byte $1f,$87,$f8,$7e,$0f,$f0,$7f,$83,$fc,$01,$ff,$80,$7f,$80,$7f,$e0 | .byte $1f,$87,$f8,$7e,$0f,$f0,$7f,$83,$fc,$01,$ff,$80,$7f,$80,$7f,$e0 | ||
| − | LFD56 | + | LFD56: |
.byte $1f,$ff,$f8,$00,$7f,$80,$07,$ff,$fe,$1f,$f0,$00,$00,$7f,$80,$00 | .byte $1f,$ff,$f8,$00,$7f,$80,$07,$ff,$fe,$1f,$f0,$00,$00,$7f,$80,$00 | ||
| − | LFD66 | + | LFD66: |
.byte $03,$fe,$00,$0f,$f3,$fc,$00,$03,$ff,$00,$00,$ff,$3f,$c0,$00,$3f | .byte $03,$fe,$00,$0f,$f3,$fc,$00,$03,$ff,$00,$00,$ff,$3f,$c0,$00,$3f | ||
| − | LFD76 | + | LFD76: |
.byte $f0,$00,$ff,$c3,$fc,$00,$3f,$c0,$ff,$00,$03,$ff,$00,$03,$fc,$0f | .byte $f0,$00,$ff,$c3,$fc,$00,$3f,$c0,$ff,$00,$03,$ff,$00,$03,$fc,$0f | ||
| − | LFD86 | + | LFD86: |
.byte $f0,$00,$3f,$f0,$3f,$fc,$03,$fc,$00,$ff,$00,$3f,$c0,$03,$ff,$00 | .byte $f0,$00,$3f,$f0,$3f,$fc,$03,$fc,$00,$ff,$00,$3f,$c0,$03,$ff,$00 | ||
| − | LFD96 | + | LFD96: |
.byte $0f,$f0,$03,$fc,$00,$3f,$f0,$ff,$c0,$03,$fc,$03,$ff,$ff,$ff,$f0 | .byte $0f,$f0,$03,$fc,$00,$3f,$f0,$ff,$c0,$03,$fc,$03,$ff,$ff,$ff,$f0 | ||
| − | LFDA6 | + | LFDA6: |
.byte $03,$ff,$00,$3f,$ff,$ff,$ff,$00,$3f,$f0,$3f,$f0,$03,$fc,$00,$7c | .byte $03,$ff,$00,$3f,$ff,$ff,$ff,$00,$3f,$f0,$3f,$f0,$03,$fc,$00,$7c | ||
| − | LFDB6 | + | LFDB6: |
.byte $7f,$8f,$80,$7c,$7f,$8f,$80,$1f,$87,$f8,$7e,$07,$f0,$7f,$83,$f8 | .byte $7f,$8f,$80,$7c,$7f,$8f,$80,$1f,$87,$f8,$7e,$07,$f0,$7f,$83,$f8 | ||
| − | LFDC6 | + | LFDC6: |
.byte $00,$ff,$c0,$7f,$80,$ff,$c0,$1f,$ff,$fc,$00,$7f,$80,$0f,$ff,$fe | .byte $00,$ff,$c0,$7f,$80,$ff,$c0,$1f,$ff,$fc,$00,$7f,$80,$0f,$ff,$fe | ||
| − | LFDD6 | + | LFDD6: |
.byte $1f,$fc,$00,$00,$7f,$80,$00,$0f,$fe,$0f,$ff,$ff,$ff,$fc,$03,$ff | .byte $1f,$fc,$00,$00,$7f,$80,$00,$0f,$fe,$0f,$ff,$ff,$ff,$fc,$03,$ff | ||
| − | LFDE6 | + | LFDE6: |
.byte $00,$ff,$ff,$ff,$ff,$c0,$3f,$f0,$0f,$fc,$03,$fc,$3f,$f0,$00,$03 | .byte $00,$ff,$ff,$ff,$ff,$c0,$3f,$f0,$0f,$fc,$03,$fc,$3f,$f0,$00,$03 | ||
| − | LFDF6 | + | LFDF6: |
.byte $ff,$03,$ff,$03,$ff,$00,$00,$3f,$f0,$3f | .byte $ff,$03,$ff,$03,$ff,$00,$00,$3f,$f0,$3f | ||
| − | LFE00 | + | LFE00: |
.byte $f0,$03,$ff,$03,$fc,$ff | .byte $f0,$03,$ff,$03,$fc,$ff | ||
| − | LFE06 | + | LFE06: |
.byte $c0,$00,$00,$ff,$c3,$ff,$0f,$fc,$00,$00,$0f,$fc,$3f,$f0,$00,$ff | .byte $c0,$00,$00,$ff,$c3,$ff,$0f,$fc,$00,$00,$0f,$fc,$3f,$f0,$00,$ff | ||
| − | LFE16 | + | LFE16: |
.byte $c3,$fc,$00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80,$0f,$87,$f8,$7c,$07 | .byte $c3,$fc,$00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80,$0f,$87,$f8,$7c,$07 | ||
| − | LFE26 | + | LFE26: |
.byte $f0,$7f,$83,$f8,$00,$7f,$c0,$7f,$80,$ff,$80,$1f,$ff,$fe,$00,$7f | .byte $f0,$7f,$83,$f8,$00,$7f,$c0,$7f,$80,$ff,$80,$1f,$ff,$fe,$00,$7f | ||
| − | LFE36 | + | LFE36: |
.byte $80,$1f,$ff,$fe,$1f,$ff,$00,$00,$7f,$80,$00,$3f,$fe,$55,$55,$55 | .byte $80,$1f,$ff,$fe,$1f,$ff,$00,$00,$7f,$80,$00,$3f,$fe,$55,$55,$55 | ||
| − | LFE46 | + | LFE46: |
.byte $55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55 | .byte $55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55 | ||
| − | LFE56 | + | LFE56: |
.byte $55,$00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80,$0f,$c7,$f8,$fc,$03,$f0 | .byte $55,$00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80,$0f,$c7,$f8,$fc,$03,$f0 | ||
| − | LFE66 | + | LFE66: |
.byte $7f,$83,$f0,$00,$3f,$e0,$7f,$81,$ff,$00,$01,$ff,$fe,$00,$7f,$80 | .byte $7f,$83,$f0,$00,$3f,$e0,$7f,$81,$ff,$00,$01,$ff,$fe,$00,$7f,$80 | ||
| − | LFE76 | + | LFE76: |
.byte $1f,$ff,$e0,$1f,$ff,$c0,$00,$7f,$80,$00,$ff,$fe,$aa,$aa,$aa,$aa | .byte $1f,$ff,$e0,$1f,$ff,$c0,$00,$7f,$80,$00,$ff,$fe,$aa,$aa,$aa,$aa | ||
| − | LFE86 | + | LFE86: |
.byte $aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa | .byte $aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa | ||
| − | LFE96 | + | LFE96: |
.byte $00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80,$0f,$c7,$f8,$fc,$03,$f8,$7f | .byte $00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80,$0f,$c7,$f8,$fc,$03,$f8,$7f | ||
| − | LFEA6 | + | LFEA6: |
.byte $87,$f0,$00,$1f,$e0,$7f,$81,$fe,$00,$00,$1f,$ff,$00,$7f,$80,$3f | .byte $87,$f0,$00,$1f,$e0,$7f,$81,$fe,$00,$00,$1f,$ff,$00,$7f,$80,$3f | ||
| − | LFEB6 | + | LFEB6: |
.byte $fe,$00,$1f,$ff,$e0,$00,$7f,$80,$01,$ff,$fe,$55,$55,$55,$55,$55 | .byte $fe,$00,$1f,$ff,$e0,$00,$7f,$80,$01,$ff,$fe,$55,$55,$55,$55,$55 | ||
| − | LFEC6 | + | LFEC6: |
.byte $55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$09 | .byte $55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$09 | ||
| − | LFED6 | + | LFED6: |
.byte $ca,$c9,$c6,$b4,$12,$08,$1b,$60,$58,$81,$4b,$86,$01,$d8,$bf,$d9 | .byte $ca,$c9,$c6,$b4,$12,$08,$1b,$60,$58,$81,$4b,$86,$01,$d8,$bf,$d9 | ||
| − | LFEE6 | + | LFEE6: |
.byte $25,$a0,$7b,$dc,$32,$79,$84,$3b,$7c,$bc,$2f,$e2,$e2,$fa,$8d,$0a | .byte $25,$a0,$7b,$dc,$32,$79,$84,$3b,$7c,$bc,$2f,$e2,$e2,$fa,$8d,$0a | ||
| − | LFEF6 | + | LFEF6: |
.byte $00,$3b,$c5,$ec,$af,$2d,$8a,$cd,$06,$93 | .byte $00,$3b,$c5,$ec,$af,$2d,$8a,$cd,$06,$93 | ||
| − | LFF00 | + | LFF00: |
.byte $6a,$a5,$14,$46,$77,$c4 | .byte $6a,$a5,$14,$46,$77,$c4 | ||
| − | LFF06 | + | LFF06: |
.byte $6a,$b2,$53,$36,$ef,$8c,$ce,$0c,$a2,$68,$71,$d3,$73,$e8,$f7,$6d | .byte $6a,$b2,$53,$36,$ef,$8c,$ce,$0c,$a2,$68,$71,$d3,$73,$e8,$f7,$6d | ||
| − | LFF16 | + | LFF16: |
.byte $06,$b5,$20,$ef,$23,$47,$0c,$51,$55,$c8,$fe,$f4,$58,$c4,$3f,$20 | .byte $06,$b5,$20,$ef,$23,$47,$0c,$51,$55,$c8,$fe,$f4,$58,$c4,$3f,$20 | ||
| − | LFF26 | + | LFF26: |
.byte $a7,$67,$38,$b0,$76,$e2,$c4,$d8,$05,$63,$f8,$3c,$58,$3b,$2d,$22 | .byte $a7,$67,$38,$b0,$76,$e2,$c4,$d8,$05,$63,$f8,$3c,$58,$3b,$2d,$22 | ||
| − | LFF36 | + | LFF36: |
.byte $cc,$88,$b3,$71,$8f,$1d,$80,$0a,$87,$bd,$a1,$59,$23,$e9,$70,$e2 | .byte $cc,$88,$b3,$71,$8f,$1d,$80,$0a,$87,$bd,$a1,$59,$23,$e9,$70,$e2 | ||
| − | LFF46 | + | LFF46: |
.byte $d3,$ec,$46,$68,$80,$42,$39,$ea,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | .byte $d3,$ec,$46,$68,$80,$42,$39,$ea,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | ||
| − | LFF56 | + | LFF56: |
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | ||
| − | LFF66 | + | LFF66: |
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | ||
| − | LFF76 | + | LFF76: |
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | ||
| − | LFF86 | + | LFF86: |
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | ||
| − | LFF96 | + | LFF96: |
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | ||
| − | LFFA6 | + | LFFA6: |
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | ||
| − | LFFB6 | + | LFFB6: |
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | ||
| − | LFFC6 | + | LFFC6: |
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | ||
| − | LFFD6 | + | LFFD6: |
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | ||
| − | LFFE6 | + | LFFE6: |
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff | ||
| Line 1,162: | Line 1,386: | ||
LFDD5 = $FDD5 | LFDD5 = $FDD5 | ||
LFE18 = $FE18 | LFE18 = $FE18 | ||
| − | LFE57 = $FE57 | + | LFE57 = $FE57 |
LFE84 = $FE84 | LFE84 = $FE84 | ||
LFE96 = $FE96 | LFE96 = $FE96 | ||
| Line 1,168: | Line 1,392: | ||
LFF00 = $FF00 | LFF00 = $FF00 | ||
| − | GccCopyright | + | GccCopyright: |
.byte $47,$43,$43,$28,$43,$29 ; 'GCC(C)' | .byte $47,$43,$43,$28,$43,$29 ; 'GCC(C)' | ||
.byte $31,$39,$38,$34,$2D,$F7 ; '1984-' | .byte $31,$39,$38,$34,$2D,$F7 ; '1984-' | ||
| − | ; | + | ;============================================================================= |
| − | ; | + | ; |
| + | ; VII. 6502 HARDWARE VECTORS | ||
| + | ; | ||
| + | ;============================================================================= | ||
| − | + | ; These are the processor's hard-wired vectors at the top of memory. | |
| − | + | ; The BIOS uses these to direct the CPU on NMI, Reset, and IRQ events. | |
| − | + | ; When a cartridge is mapped in, its own vectors at these locations are used. | |
| − | |||
| − | |||
| − | |||
| + | CartNMIVectorLo: | ||
| + | .byte $00,$F0 ; System NMI vector points to NmiVector_Rom. | ||
| + | CartResetVectorLo: | ||
| + | .word START ; System RESET vector points to $F884. | ||
| + | CartIRQVectorLo: | ||
| + | .byte $33,$F9 ; System IRQ vector. | ||
</pre> | </pre> | ||
Latest revision as of 20:27, 20 July 2025
7800 NTSC BIOS
This reverse engineered 7800 NTSC BIOS code contains helpful context-comments and builds a 1:1 with the official BIOS ROM. In DASM syntax.
; Disassembly of the Atari 7800 NTSC BIOS (bios7800.bin)
; Originally disassembled by DiStella v2.0 on Sun Mar 08 20:30:38 1998
;
; This version has been heavily commented and refactored for maximum clarity,
; with generic labels (e.g., LF400) renamed to descriptive ones.
; The goal is to explain the inner workings of the BIOS for educational
; and development purposes.
;
; Original comments by:
; - Keith Henrickson <flipper@phin.com>
; - Daniel Boris <dboris@home.com>
; - Mike Saarna (dasm fixups, additional comments)
; - Gemini 2.5 Pro (semantic meaningful label names, comments galore)
;
; The code must be assembled with DASM.
;
processor 6502
;=============================================================================
;
; I. HARDWARE AND MEMORY EQUATES
;
;=============================================================================
; --- Misc System Equates ---
LFD80 = $FD80
CartKey = $FF80 ; Start address of the 8-byte cartridge security key
CartRegion = $FFF8 ; Cartridge region/signature byte
CartKeyStartPage = $FFF9 ; Cartridge ROM start page and signature byte
CartResetVectorHi = $FFFD ; High byte of the cartridge's RESET vector address
LF112 = $F112
LF118 = $F118
LF460 = $F460
LF700 = $F700
LF800 = $F800
; --- 7800 System Control ---
INPTCTRL = $01 ; Input Control Register. Controls memory mapping (BIOS, RAM, Cart) and TIA access.
; --- TIA (Television Interface Adapter) Registers (for 2600 Mode) ---
VSYNC = $00 ; Vertical Sync
VBLANK = $01 ; Vertical Blank
WSYNC = $02 ; Wait for Horizontal Sync
RSYNC = $03 ; Reset Horizontal Sync Counter
COLUP0 = $06 ; Color-Luminance for Player 0
COLUPF = $08 ; Color-Luminance for Playfield
COLUBK = $09 ; Color-Luminance for Background
PF2 = $0F ; Playfield Register 2
RESP1 = $11 ; Reset Player 1
GRP0 = $1B ; Player 0 Graphics
GRP1 = $1C ; Player 1 Graphics
; --- Maria (Custom Graphics Chip) Registers ---
BACKGRND = $20 ; Background Color
POC1 = $21 ; Palette 0, Color 1
P0C2 = $22 ; Palette 0, Color 2
POC3 = $23 ; Palette 0, Color 3
MWSYNC = $24 ; Maria Wait for Sync
P1C1 = $25 ; Palette 1, Color 1
P1C2 = $26 ; Palette 1, Color 2
P1C3 = $27 ; Palette 1, Color 3
MSTAT = $28 ; Maria Status Register (VBLANK, etc.)
DPPH = $2C ; Display List Pointer High Byte
DPPL = $30 ; Display List Pointer Low Byte
CTRL = $3C ; Maria Control Register (DMA, graphics mode)
; --- Zero Page RAM Vectors/Variables ---
DLIAddr = $F4 ; RAM vector for Display List Interrupts (NMI)
ConsoleRegion = $FE ; Console region byte (used in cart verification)
;=============================================================================
;
; II. CARTRIDGE AUTHENTICATION & MODE SWITCHING LOGIC
;
; This entire block of code from $F000 to $F880 is not executed from ROM.
; Instead, it is copied to RAM ($2300-$2700) by the main BIOS init routine
; at Bios_InitSystem. The jump to $2306 then begins execution from RAM.
;
;=============================================================================
ORG $F000
; --- NMI Vector (ROM) ---
; This is the hardware NMI vector. It immediately jumps to a RAM vector at $F4,
; which allows the running program (including the BIOS itself) to define its
; own NMI handler.
NmiVector_Rom:
pha ; This is the NMI routine.
jmp (DLIAddr) ; Jump to RAM NMI vector.
ORG $F400
; --- RAM Routine: Cartridge Detection and Authentication ---
; This code is designed to be executed from RAM starting at address $2300.
; Its primary job is to determine if a valid, signed 7800 cartridge is
; present. If it is, the system boots in 7800 mode. If not, it attempts
; to boot in 2600 mode.
RamExec_Start:
SwitchTo2600Mode1:
jmp Enter2600Mode ; Branch to the 2600 mode initialization routine.
SwitchTo2600Mode2:
jmp Enter2600Mode ; Branch to the 2600 mode initialization routine.
CartTest:
lda #$16 ; Value to enable Maria and map in the cartridge ROM.
sta INPTCTRL ; Switch in cart + enable Maria.
ldy #$FF ; Initialize Y to $FF.
ldx #$7F ; Initialize X to $7F.
; --- Cartridge Presence Test ---
; This loop compares two different ROM regions ($FD80-$FDFF and $FE00-$FE7F).
; If a cartridge is not present, the data bus "floats", and reading any address
; might return the last value seen on the bus (or garbage). By comparing two
; regions that should contain different data, the BIOS can detect this state.
; If the data is the same, it's likely a floating bus, so no cart is present.
CartPresenceCheck_Loop:
lda LFE00,X ; Read from the $FExx range.
cmp LFD80,Y ; Compare with the $FDxx range.
bne SwitchTo2600Mode1 ; If they differ, the test passes (so far). Branch if they are the same (fail).
dey ;
dex ;
bpl CartPresenceCheck_Loop ; Loop for 128 bytes.
; --- Cartridge Reset Vector Test ---
; A valid 7800 or 2600 cart must have a valid reset vector. A vector of $FFFF
; or $0000 typically indicates an empty socket or a problem.
lda CartResetVectorLo ;
and CartResetVectorHi ;
cmp #$FF ;
beq SwitchTo2600Mode1 ; If RESET vector is $FFFF, then go 2600.
lda CartResetVectorLo ;
ora CartResetVectorHi ;
beq SwitchTo2600Mode1 ; If RESET vector is $0000, then go 2600.
; --- Cartridge Signature/Region Verification ---
; This is a series of simple, quick checks on bytes in the $FFF8-$FFF9 region
; of the cartridge ROM to see if it looks like an official 7800 cart.
lda CartRegion ; Read signature byte at $FFF8.
ora #ConsoleRegion ; OR with the console's region byte ($FE).
cmp #$FF ;
bne SwitchTo2600Mode2 ; If low-bit of CartRegion=0, then go 2600.
lda CartRegion ;
eor #$F0 ; Invert high nibble.
and #$F0 ; Isolate the high nibble.
bne SwitchTo2600Mode2 ; If high nibble was not originally $F, then go 2600.
lda CartKeyStartPage ; Read signature byte at $FFF9.
and #$0B ;
cmp #$03 ;
bne SwitchTo2600Mode2 ; If low nibble of $FFF9 was not 3 or 7, go 2600.
; --- Cartridge Header Processing ---
; If the quick checks pass, the BIOS proceeds with the full authentication.
lda CartKeyStartPage ;
and #$F0 ; Extract ROM start page from high nibble of $FFF9.
sta $EE ; Store it in zero page.
sta $2406 ; Store it in RAM for the authentication routine.
cmp #$40 ;
bcc SwitchTo2600Mode2 ; If ROM start page < $40 (i.e., address < $4000), fail.
sbc #$01 ;
cmp CartResetVectorHi ; Compare ROM start page with high byte of reset vector.
bcs SwitchTo2600Mode2 ; If start page is >= reset vector high byte, fail.
jsr $2536 ; If all checks pass, start the full authentication process.
; --- Obfuscated Authentication Code ---
; The following code is part of the digital signature verification. It involves
; complex mixing, rotating, and lookup operations on the cartridge's security
; key and internal BIOS data tables. Its purpose is to be difficult to analyze
; and replicate. The high-level goal is to compute a hash from the cart key
; and compare it against an expected result.
lda #$00
sta $F0
jsr $241B
lda #$16
sta INPTCTRL
ldx #$00
txa
Auth_RamFillLoop1:
sta $1800,X
dex
bne Auth_RamFillLoop1
pha
ldy #$7F
Auth_CopyRomToRamLoop:
lda LFF00,Y
sta $1800,Y
dey
cpy #$F8
bne Auth_CopyRomToRamLoop
lda #$2E
sta $2409
lda #$24
sta $240A
Auth_MainLoop1:
jsr $241B
pla
jsr $23FF
pha
inc $2406
lda $2406
cmp #$FF
bne Auth_MainLoop1
jsr $241B
jsr $2412
jsr $2412
lda #$36
sta $2409
lda #$24
sta $240A
dec $2406
Auth_MainLoop2:
jsr $241B
pla
jsr $23FF
pha
dec $2406
lda $2406
cmp $EE
bcs Auth_MainLoop2
lda #$60
sta CTRL
ldx #$77
Auth_XorAndStoreLoop:
lda $1800,X
eor $1850,X
eor $1888,X
sta $1A00,X
dex
bpl Auth_XorAndStoreLoop
lda $1A00
and #$07
sta $1A00
lda #$00
ldx #$04
sta $1A00,X
sta $2000,X
; --- Final Authentication Check ---
; After all the cryptographic hashing, this is the final comparison.
; It compares two blocks of memory in RAM. If they match, the cart is authentic.
ldx #$77
Auth_FinalCompareLoop:
lda $2000,X ; Read from one workspace area.
cmp $1A00,X ; Compare with the computed hash.
bne Auth_FailAndSwitchTo2600; If they don't match, authentication fails.
dex
bpl Auth_FinalCompareLoop ; Loop until all bytes are compared.
jmp Enter7800Mode ; AUTHENTICATION PASSED: Jump to 7800 mode init.
Auth_FailAndSwitchTo2600:
jmp Enter2600Mode ; AUTHENTICATION FAILED: Jump to 2600 mode init.
; --- Authentication Subroutines ---
; More opaque subroutines used by the main authentication algorithm.
Auth_Sub_MixAndLookup_Equate = $F500
ldx #$00
Auth_Sub_MixAndLookup_Loop:
adc $1800,X
adc LFF00,X
tay
lda $2DD5,Y
sta $1800,X
inx
bne Auth_Sub_MixAndLookup_Loop
rts
ldx #$00
Auth_Sub_RotateRam_Loop:
rol $1800,X
inx
bne Auth_Sub_RotateRam_Loop
rts
; Subroutine at $241B - This routine briefly disables the cartridge to access BIOS ROM/RAM.
php
dec $F0
bpl Auth_Sub_AccessBios_Done
lda #$02
sta INPTCTRL
Auth_Sub_AccessBios_WaitLoop:
lda $F0
bmi Auth_Sub_AccessBios_WaitLoop
lda #$16
sta INPTCTRL
Auth_Sub_AccessBios_Done:
plp
rts
; --- Authentication Signature Data Tables ---
; These large blocks of byte data are part of the digital signature scheme.
; They are used as lookup tables and keys during the verification process.
AuthData_Table1: .byte $C7,$65,$AB,$CA,$EE,$F7,$83,$09
AuthData_Table2: .byte $E1,$D0,$92,$67,$62,$B6,$72,$55,$8E,$91,$DC,$C5,$81,$BE,$78,$20
.byte $59,$B7,$E6,$3D,$06,$45,$AF,$C8,$08,$31,$38,$D1,$FB,$73,$84,$A9
.byte $17,$FC,$34,$87,$A3,$94,$FA,$90,$B8,$ED,$CE,$3B,$5B,$0A,$43,$D9
.byte $F3,$53,$82,$B3,$0D,$6D,$5A,$60,$9D,$51,$A7,$B9
AuthData_Table3: .byte $11,$10,$BC,$E4,$7F,$80,$41,$E7,$E3
AuthData_Table4: .byte $F6,$56,$26,$35,$EC,$D6,$DF,$0C,$7F,$F4,$9E,$AC,$52,$46,$EF,$CF
.byte $BF,$A2,$3F,$A4,$13,$15,$97,$4A,$1C,$B0,$42,$8C,$B1,$05,$58,$80
.byte $18,$77,$2B,$02,$3E,$A8,$49,$1A,$6A
AuthData_Table5: .byte $CB,$6E,$0B,$8A,$EB,$F1,$4F,$14,$79,$8B,$D8,$9F,$9B,$57,$19,$F8
.byte $2A,$2D,$76,$0E,$E8,$2E,$4B,$F9,$07,$03,$DE,$93,$16,$7E,$D4,$E5
.byte $B2,$F0,$7D,$7A,$DA,$D2,$A1,$CC,$1D,$E0,$5E,$23,$A0,$95,$22,$1E
.byte $36,$85,$FE,$1F,$39
AuthData_Table6: .byte $AA,$89,$96,$AD,$0F,$2F,$C0,$47
AuthData_Table7: .byte $27,$5D,$24,$EA,$C3,$A5,$F5,$21,$5F,$1B,$40,$8F,$AE,$74,$25,$DD
.byte $C1,$7C,$CD,$A6,$70,$D7,$33,$7B,$2C,$75,$BB,$86,$99,$BD,$54
AuthData_Table8: .byte $9A,$6C,$63,$32,$48,$4C,$8D,$BA
AuthData_Table9: .byte $5C,$61,$C4,$4E,$29,$37,$12,$C6,$98,$9C,$D5,$69,$6B,$E2,$04,$4D
.byte $E9,$C2,$88,$3A,$DB,$64,$01,$44,$6F,$B5,$F2,$30,$28,$FD,$50,$71
.byte $3C,$B4,$66,$68,$C9,$D3,$CA,$83,$C7,$AB,$F7,$65,$09,$EE
; --- Main Authentication Routine ($2536) ---
; This routine is the entry point for the signature verification process.
; It copies the 128-byte security key from the cartridge ROM into RAM.
ldx #$77 ; ($2536) Start of routine, X=119
stx $E4
stx $E5
Auth_CopyCartKey_Loop:
lda CartKey,X ; Read a byte from the cart security key area ($FF80-FFFF).
sta $1901,X ; Store it in RAM work area 1.
sta $2000,X ; Store it in RAM work area 2.
dex ; next byte of key
bpl Auth_CopyCartKey_Loop ; Loop until all 128 bytes are copied.
lda #$02 ;
sta INPTCTRL ; Disable cart ROM to access BIOS routines.
jsr Display_InitLogo ; Initialize Maria for the Fuji logo display.
jsr $257B
dec $F2
ldx #$77
stx $E4
Auth_CopyInternalKey_Loop:
lda LFED5,X
sta $1901,X
dex
bpl Auth_CopyInternalKey_Loop
lda $E1
sta $E3
jsr $25E1
dec $F2
Auth_CopyWorkspace_Start:
lda $E0
sta $2572
ldx #$77
Auth_CopyWorkspace_Loop:
lda $1800,X
Auth_CopyWorkspace_Store:
sta $2000,X
dex
bpl Auth_CopyWorkspace_Loop
rts
; --- More Authentication Subroutines ---
jsr $2639 ; ($257B)
ldy $E5
iny
Auth_Sub_KeyProcess_OuterLoop:
STY $E1
TYA
clc
adc $E2
pha
Auth_Sub_KeyProcess_ClearRam:
tax
lda #$00
sta $2671
Auth_Sub_KeyProcess_ClearRam_Loop:
sta $1800,X
dex
Auth_Sub_KeyProcess_ClearRam_Check:
bne Auth_Sub_KeyProcess_ClearRam_Loop
sta $1800
Auth_Sub_KeyProcess_SetupCounters:
iny
STY $266E
STY $2674
STY $267C
STY $2681
ldx #$00
Auth_Sub_KeyProcess_InnerLoop:
dec $266E
dec $2674
dec $267C
dec $2681
dec $E1
bmi Auth_Sub_KeyProcess_Done
Auth_Sub_KeyProcess_BitCheck:
ldy $E1
lda $2000,Y
and $25D9,X
beq Auth_Sub_KeyProcess_NextBit
lda $2662,X
sta $2672
jsr $266A
Auth_Sub_KeyProcess_NextBit:
inx
cpx #$08
bmi Auth_Sub_KeyProcess_BitCheck
jmp $25A4
Auth_Sub_KeyProcess_Done:
pla
sta $E1
lda #$01
sta $E0
rts
AuthData_Bitmasks: .byte $01,$02,$04,$08,$10,$20,$40,$80
jsr $2639
lda $E3
sec
sbc $E4
sta $E0
sta $E1
ldx #$00
stx $1800
stx $268F
stx $26AC
dex
stx $26A9
stx $268C
stx $2692
stx $269A
stx $269F
ldx #$07
inc $26A9
inc $268C
inc $2692
inc $269A
inc $269F
dec $E1
bmi Auth_Sub_Op_Done
Auth_Sub_Op_Loop:
lda $2662,X
sta $2690
sta $26AD
jsr $26A6
bcc Auth_Sub_Op_Next
jsr $2688
Auth_Sub_Op_Next:
dex
bpl Auth_Sub_Op_Loop
jmp $2608
Auth_Sub_Op_Done:
lda $E3
sta $E1
rts
ldx $E4 ; ($2639)
inx
stx $E2
ldy #$00
STY $1900
Auth_Sub_Rotate_OuterLoop:
lda $2662,Y
sta $2655
iny
lda $2662,Y
sta $2659
ldx $E2
clc
Auth_Sub_Rotate_InnerLoop:
lda $1900,X
rol
sta $1900,X
dex
bpl Auth_Sub_Rotate_InnerLoop
cpy #$07
bmi Auth_Sub_Rotate_OuterLoop
rts
AuthData_Indices: .byte $19,$1A,$1B,$1C,$1D,$1E,$1F,$21
ldy $E2
clc
Auth_Sub_Add_Loop:
lda $1800,Y
adc $1900,Y
sta $1800,Y
dey
bpl Auth_Sub_Add_Loop
bcc Auth_Sub_Add_NoCarry
lda $1700,Y
adc #$00
sta $1700,Y
dey
jmp $2679
Auth_Sub_Add_NoCarry:
rts
ldy $E2
sec
Auth_Sub_Subtract_Loop:
lda $1800,Y
sbc $1900,Y
sta $1800,Y
dey
bpl Auth_Sub_Subtract_Loop
bcs Auth_Sub_Subtract_NoBorrow
lda $1700,Y
sbc #$00
sta $1700,Y
dey
jmp $2697
Auth_Sub_Subtract_NoBorrow:
rts
ldy #$00
lda $1800,Y
cmp $1900,Y
beq Auth_Sub_Compare_Equal
Auth_Sub_Compare_NotEqual:
rts
Auth_Sub_Compare_Equal:
cpy $E2
beq Auth_Sub_Compare_NotEqual
iny
jmp $26A8
; --- 7800 Mode Initialization ---
ORG $F7B9
; This label is an equate, not a location. It calculates the correct RAM address
; for the routine, which is needed by the code executing from RAM.
; ROM address $F7B9 -> RAM address $26B9. Offset is $D100.
Enter7800Mode = . - $D100
Routine_Enter7800Mode:
ldx #$16 ; Enable Maria and Cartridge ROM.
stx INPTCTRL ; Lock out BIOS, enable cartridge.
txs ; Set up stack pointer.
SED ; Set Decimal Mode (a 7800 convention, often cleared by games).
jmp (CartResetVectorLo) ; Jump to the game's starting address on the cartridge.
; --- 2600 Mode Initialization ---
ORG $F7C2
; This label is also an equate to calculate the correct RAM address.
; ROM address $F7C2 -> RAM address $26C2. Offset is $D100.
Enter2600Mode = . - $D100
Routine_Enter2600Mode:
lda #$02 ; Enable 7800 RAM and BIOS ROM.
sta INPTCTRL ;
ldx #$7F
; Copy the 2600 TIA setup code from BIOS ROM into RIOT RAM ($0480).
CopyTiaInitToRam_Loop:
lda TiaInitCode,X
sta $0480,X
dex
bpl CopyTiaInitToRam_Loop
jmp $0480 ; Jump to the newly copied code in RIOT RAM to execute it.
; --- 2600 TIA Setup Code (Executed from RIOT RAM at $0480) ---
TiaInitCode:
lda #$00 ;
tax ;
sta INPTCTRL ; Disable 7800 RAM/BIOS, giving full control to cart.
; Clear all TIA registers to put it in a known state.
TiaInit_ClearLoop:
sta RSYNC,X
inx ;
cpx #$2A ;
bne TiaInit_ClearLoop
sta WSYNC ; Wait for horizontal sync.
lda #$04 ;
nop ;
bmi TiaInit_SetColors1
ldx #$04
TiaInit_DelayLoop:
dex
bpl TiaInit_DelayLoop
txs
sta $0110
jsr $04CB
jsr $04CB
sta RESP1
sta GRP0
sta GRP1
sta PF2
nop
sta WSYNC
lda #$00
nop
bmi TiaInit_SetColors1
bit RSYNC
bmi TiaInit_SetColors2
TiaInit_SetColors1:
lda #$02
sta COLUBK
sta LF112
bne TiaInit_Finalize
TiaInit_SetColors2:
bit WSYNC
bmi TiaInit_SetColors3
lda #$02
sta COLUP0
sta LF118
sta LF460
bne TiaInit_Finalize
TiaInit_SetColors3:
sta DPPH
lda #$08
sta GRP0
jsr $04CB
nop
bit WSYNC
bmi TiaInit_SetColors1
TiaInit_Finalize:
lda #$FD
sta COLUPF
jmp (CartResetVectorLo) ; Jump to the 2600 game's starting address on the cartridge.
nop
;=============================================================================
;
; III. BIOS ENTRY, POWER-ON SELF TEST (POST), AND SYSTEM INIT
;
;=============================================================================
ORG $F880
; --- Self-Test Failure Handler ---
; This code is jumped to whenever a POST routine fails. It writes an error
; code to INPTCTRL, which can be read by external diagnostic hardware.
Post_FailHandler:
lda #$1D
sta INPTCTRL ; Enable TIA/Cart/lock out INPTCTRL.
; --- BIOS Entry Point / Cold Start ---
START: ; $F884
SEI ; Disable interrupts on power-on.
CLD ; Clear decimal flag.
lda #$02 ;
Bios_SetRamAndBios:
sta INPTCTRL ; Enable 7800 RAM and map BIOS into memory.
lda #$FB ; Set high byte of NMI vector to $FB.
sta $F5 ;
lda #$12 ; Set low byte of NMI vector to $12.
sta $F4 ; NMI vector is now $FB12 (points to IRQ cleanup).
lda #$7F ;
sta CTRL ; Turn off Maria DMA.
lda #$00 ;
sta BACKGRND ; Set background color to black.
; --- POST: RAM Test 1 (Basic) ---
; This is a quick test of the two 2KB RAM chips ($2000-$27FF).
; It writes several patterns to each byte and reads them back.
ldx #$05 ; Loop counter for the 6 test patterns.
Post_RamTest1_PatternLoop:
lda Post_RamTest_Data,X ; Load a test pattern ($00, $FF, $55, $AA, $69, $0F).
ldy #$00 ; Inner loop counter for 256 bytes.
Post_RamTest1_PageLoop:
sta $2000,Y ; Write pattern to first RAM chip.
cmp $2000,Y ; Read it back and compare.
bne Post_RamTest_Fail1 ; If it fails, branch to error handler.
sta $2100,Y ; Write pattern to second RAM chip.
cmp $2100,Y ; Read it back and compare.
bne Post_RamTest_Fail1 ; If it fails, branch to error handler.
dey ;
bne Post_RamTest1_PageLoop ; Loop through all 256 bytes of a page.
dex ;
bpl Post_RamTest1_PatternLoop ; Loop to the next test pattern.
; --- POST: RAM Mirror Test ---
; Checks that the RAM is correctly mirrored every 256 bytes.
lda #$43 ; Check RAM 0 mirror.
sta $2080 ; Write to $2080.
cmp $80 ; Compare with $0080 (a mirror).
bne Post_RamMirror_Fail ; make sure they match. If not, fail selftest.
sta $2180 ; Check RAM 1 mirror
cmp $0180 ;
bne Post_RamMirror_Fail ; make sure they match. If not, fail selftest.
jmp Post_CpuTest_Start ; continue selftest
Post_RamMirror_Fail:
ldy #$04 ;
jmp Post_FailHandler ; selftest fail.
; --- POST: RAM Failure Handlers ---
; These routines are jumped to from the RAM tests to indicate failure.
Post_RamTest_Fail1:
sta $1800 ; test store and compare
cmp $1800
bne Post_RamTest_Fail3 ; some kind of error code is being determined
Post_RamTest_Fail2:
ldy #$01
jmp Post_FailHandler
Post_RamTest_Fail_Indirect:
ldy #$02
jmp $F880
Post_RamTest_Fail3:
ldy #$03
jmp Post_FailHandler
; --- POST: RAM Test 2 (Comprehensive) ---
; This is a more thorough RAM test called later in the boot process. It uses
; indirect addressing to test all 8 pages of the 4KB RAM.
Post_RamTest2_Start:
lda #$00
sta $F0 ; Base address low byte pointer.
sta $F2 ;
ldy #$07 ; Loop counter for 8 pages.
STY $F4
Post_RamTest2_OuterLoop:
lda Post_RamTest_AddrHi1,Y ; Load high byte of address for RAM chip 1.
sta $F1
lda Post_RamTest_AddrHi2,Y ; Load high byte of address for RAM chip 2.
sta $F3
ldx #$05 ; Loop counter for 6 test patterns.
Post_RamTest2_PatternLoop:
lda Post_RamTest_Data,X ; Load a test pattern.
Post_RamTest2_InnerLoop:
ldy #$00 ; Inner loop counter for 256 bytes.
Post_RamTest2_ByteLoop:
sta ($F0),Y ; Write to RAM 1 via indirect pointer ($F0/$F1).
cmp ($F0),Y ; Read back and compare.
bne Post_RamTest_Fail2 ; Fail.
sta ($F2),Y ; Write to RAM 2 via indirect pointer ($F2/$F3).
cmp ($F2),Y ; Read back and compare.
bne Post_RamTest_Fail_Indirect ; Fail.
dey
bne Post_RamTest2_ByteLoop ; Loop through page.
dex
bpl Post_RamTest2_PatternLoop ; Loop through patterns.
dec $F4
ldy $F4
bpl Post_RamTest2_OuterLoop ; Loop through pages.
jmp Bios_InitSystem ; ram test passed, so jump in here.
; --- RAM Test Data ---
Post_RamTest_Data: .byte $00,$FF,$55,$AA,$69,$0F
Post_RamTest_AddrHi1: .byte $22,$23,$24,$25,$26,$27,$22,$23
Post_RamTest_AddrHi2: .byte $18,$19,$1A,$1B,$1C,$1D,$1E,$1F
Post_CpuTest_Fail:
ldy #$00 ; local place for selftest fail branch target
jmp Post_FailHandler
; --- POST: 6502 CPU Test ---
; This section exhaustively tests the 6502's instruction set, flags,
; and addressing modes to ensure the CPU is functioning correctly.
Post_CpuTest_Start:
lda #$AA ; test some flags and branches
beq Post_CpuTest_Fail ; test failed
bpl Post_CpuTest_Fail ; test failed
bmi CpuTest_CheckBne ; test passed
jmp Post_CpuTest_Fail ; test failed
CpuTest_CheckBne:
bne CpuTest_CheckStore ; test passed
jmp Post_CpuTest_Fail ; test failed
CpuTest_CheckStore:
sta $AA ; store AA to 00AA
cmp $AA ; compare it back
bne Post_CpuTest_Fail ; if it doesn't match, selftest fail
lda #$00 ; do some more flag tests
bne Post_CpuTest_Fail ;
bmi Post_CpuTest_Fail ;
bpl CpuTest_CheckBeq ; test passed
jmp Post_CpuTest_Fail ;
CpuTest_CheckBeq:
beq CpuTest_CheckCmp ; test passed
jmp Post_CpuTest_Fail ;
CpuTest_CheckCmp:
cmp #$00 ; test the compare instruction
bne Post_CpuTest_Fail ;
bcc Post_CpuTest_Fail ;
bcs CpuTest_CheckCmpBcs ; test passed, since they're equal
jmp Post_CpuTest_Fail ;
CpuTest_CheckCmpBcs:
cmp #$01 ; compare it to 01
bcs Post_CpuTest_Fail ;
bcc CpuTest_CheckCpx ; A < 01, so carry is clear
jmp Post_CpuTest_Fail ;
CpuTest_CheckCpx:
ldx #$55 ; test comparisons with the X register
cpx #$56 ;
beq Post_CpuTest_Fail ;
stx $01AA ;
cpx $01AA ;
bne Post_CpuTest_Fail ;
ldy $AA ; and with the Y register.
cpy #$AB ;
CpuTest_CheckCpy:
beq Post_CpuTest_Fail ;
STY $0155 ; put some stuff in the stack area to test stack
cpy $0155 ; and then access this data in many different ways
bne Post_CpuTest_Fail ;
dex ;
txs ;
inx ;
pla ;
cmp #$AA ;
bne Post_CpuTest_Fail_Central ;
txa ;
pha ;
cpx $0155 ;
bne Post_CpuTest_Fail_Central ;
TYA ;
cmp #$AA ;
bne Post_CpuTest_Fail_Central ;
tax ;
lda $0100,X ;
tay ;
cpy #$55 ;
bne Post_CpuTest_Fail_Central ;
lda VSYNC,X ;
cmp $AA ;
bne Post_CpuTest_Fail_Central ;
cmp #$AA ;
bne Post_CpuTest_Fail_Central ;
eor #$FF ;
sta $0000,Y ;
cmp $55 ;
bne Post_CpuTest_Fail_Central ;
cmp $0100,Y ;
bne Post_CpuTest_Fail_Central ;
cmp $20AB,X ;
bne Post_CpuTest_Fail_Central ;
lda #$20 ;
sta $F1 ;
lda #$CC ;
sta $F0 ;
sta ($46,X) ;
cmp $CC ;
bne Post_CpuTest_Fail_Central ;
sta ($F0),Y ;
cmp $2121 ;
bne Post_CpuTest_Fail_Central ;
lda #$EE ; test the indirect jump by setting up a jump
sta $F0 ; to Post_CpuMath_Start
lda #$F9 ;
sta $F1 ;
jmp ($00F0) ; and do it.
CpuTest_ShouldNotBeReached:
jmp Post_CpuTest_Fail_Central
Post_CpuTest_Fail_Central:
jmp Post_CpuTest_Fail
; --- POST: CPU Math and Logic Tests ---
Post_CpuMath_Start:
lda #$55 ; now test out the math functions.
clc
adc #$55
nop
bcs Post_CpuTest_Fail_Central ; test addition.
bpl Post_CpuTest_Fail_Central
beq Post_CpuTest_Fail_Central
cmp #$AA ; make sure it worked
bne Post_CpuTest_Fail_Central
adc #$55 ; test addition again.
CpuMath_TestAdcCarry:
nop
bcc Post_CpuTest_Fail_Central
bmi Post_CpuTest_Fail_Central
bne Post_CpuTest_Fail_Central
sbc #$55 ; test subtraction
bcs Post_CpuTest_Fail_Central
bpl Post_CpuTest_Fail_Central
beq Post_CpuTest_Fail_Central
cmp #$AB ; make sure it worked
bne Post_CpuTest_Fail_Central
clc
sbc #$AA ; test subtraction again
bcc Post_CpuTest_Fail_Central
bmi Post_CpuTest_Fail_Central
bne Post_CpuTest_Fail_Central
lda #$FF ; set up a stack
tax ; and do all kinds of stuff in it for tests
inx
bne CpuMath_Fail
dex
beq CpuMath_Fail
bpl CpuMath_Fail
cpx #$FF
bne CpuMath_Fail
tay
iny
bne CpuMath_Fail
dey
beq CpuMath_Fail
iny
bne CpuMath_Fail
sta $F0
inc $F0
bne CpuMath_Fail
cpy $F0
bne CpuMath_Fail
dec $F0
beq CpuMath_Fail
cmp $F0
bne CpuMath_Fail
lda #$AA
clc
rol ; now we get onto the more complex math instrs
rol
rol
cmp #$52
bne CpuMath_Fail ; make sure rotate left works.
ror
ror
ror
cmp #$AA
beq CpuMath_TestAsl ; test rotate right
CpuMath_Fail:
jmp Post_CpuTest_Fail ; fail!
CpuMath_TestAsl:
asl ; test arithmetic shift left
bcc CpuMath_Fail
asl
bcs CpuMath_Fail
asl
cmp #$50
bne CpuMath_Fail
eor #$05
lsr ; and logical shift right
bcc CpuMath_Fail
lsr
bcs CpuMath_Fail
lsr
cmp #$0A
bne CpuMath_Fail
lda #$55 ; now test the ands and ors.
ora #$1B
cmp #$5F
bne CpuMath_Fail
and #$55
and #$1B
cmp #$11
bne CpuMath_Fail
ora #$55
eor #$1B ; and the eors
cmp #$4E
bne CpuMath_Fail
jsr Cpu_TestJsr ; test jump subroutine instruction
jmp CpuMath_Fail ; if we return, fail
Cpu_TestJsr:
tsx
cpx #$52 ; check stack pointer
bne CpuMath_Fail ; fail if not right
pla
cmp #$8D
bne CpuMath_Fail
pla
cmp #$FA
bne CpuMath_Fail ; get the old return address off the stack
lda #$F8
pha
lda #$E6
pha ; and make our own
rts ; and 'return' to Post_RamTest2_Start
jmp CpuMath_Fail ; another jump to catch a failure
;=============================================================================
;
; IV. NMI HANDLER (FUJI LOGO DISPLAY)
;
;=============================================================================
; This NMI routine is responsible for drawing the "Fuji" mountain logo and
; the "ATARI" text on the screen during the boot-up/authentication process.
; It is called on every VBLANK. Its main job is to manipulate Maria's palette
; registers to create the signature color-cycling effect on the Fuji logo.
Nmi_DrawFujiAndAtari:
txa ;Save A
pha ;
lda #$43 ;Setup control register
sta CTRL ;
ldx #$0F ; Handle the color scrolling in the FUJI
lda $EF
sta P0C2
bit $F3
bvc Nmi_Sync3
bpl Nmi_Sync2
Nmi_Sync1:
sta MWSYNC ;Wait for 3 scanlines
Nmi_Sync2:
sta MWSYNC ;
Nmi_Sync3:
sta MWSYNC ;
sec
sbc #$10
cmp #$10
bcs Nmi_StoreColor
sbc #$0F
Nmi_StoreColor:
sta P0C2
dex
bpl Nmi_Sync1 ;Branch to do next section of fuji
ldx #$40 ;set 160x2/160x4 mode
stx CTRL ;
and #$F0 ;
ora #$0E ;set Palette 1 color 3
sta P1C3 ;
lda $EF ;
and #$F0 ;
ora #$06 ;set Palette 1 color 1
sta P1C1 ;
and #$F0
clc
adc #$40
bcc Nmi_SetPalette1Color2
adc #$0F
Nmi_SetPalette1Color2:
ora #$03 ;set Palette 1 color 2
sta P1C2
dec $F1
bpl Nmi_RestoreRegs
lda $F3
adc #$60
bcc Nmi_UpdateFrameCounter
lda $EF
clc
adc #$10
bcc Nmi_StoreNewBaseColor
adc #$0F
Nmi_StoreNewBaseColor:
sta $EF
lda $F2
sta $F1
lda #$00
Nmi_UpdateFrameCounter:
sta $F3
Nmi_RestoreRegs:
lda #$02
sta $F0
pla
tax
pla
rti
Trap_InfiniteLoop:
jmp Trap_InfiniteLoop
;=============================================================================
;
; V. SYSTEM INITIALIZATION (POST-TEST)
;
;=============================================================================
; This routine is executed after all Power-On Self Tests have passed.
Bios_InitSystem:
ldx #$FF ; selftest has passed, start system init
txs ; set up a stack
lda #$00 ; Clear TIA/Maria registers
tax ;
Bios_ClearRegsLoop:
sta $01,X ;
inx ;
cpx #$2C ;
bne Bios_ClearRegsLoop
lda #$02 ;
sta INPTCTRL ; Enable 7800 RAM
ldx #$00 ;
stx BACKGRND ; Set background color
; --- Copy BIOS Routines and Data to RAM ---
; This is a critical step. It copies the cartridge authentication code,
; graphics display routines, and associated data from the BIOS ROM
; into the main system RAM. This allows the BIOS to run its complex
; authentication logic from RAM, freeing up the cartridge bus.
Bios_CopyCodeToRam_Loop:
lda RamExec_Start,X ; copy the authentication and title screen to
sta $2300,X ; ram
lda Auth_Sub_MixAndLookup_Equate,X ;
sta $2400,X ;
lda AuthData_Table8,X ;
sta $2500,X ;
lda LF700,X ;
sta $2600,X ;
lda LF800,X ;
sta $2700,X ;
lda DisplayList_Defs,X ;
sta $2200,X ;
cpx #$00 ;
bmi Bios_CopyCodeToRam_Done ;
lda DisplayList_Master,X ;
sta $1F84,X ;
lda GraphicsData_FujiAtari_1,X ;
sta $1984,X ;
lda LFD3D,X ;
sta $1A84,X ;
lda LFDB4,X ;
sta $1B84,X ;
lda LFE18,X ;
sta $1C84,X ;
lda LFE57,X ;
sta $1D84,X ;
lda LFE96,X ;
sta $1E84,X ;
Bios_CopyCodeToRam_Done:
dex ;
bne Bios_CopyCodeToRam_Loop ;
jmp $2306 ; and execute it (CartTest)
;Start display of Atari logo
Display_InitLogo:
lda CartKeyStartPage ; Read ROM start byte from cart
and #$04 ; Is rom start greater then $4000?
beq Display_InitLogo_Done ; Branch if not
lda #$03
sta $F1
sta $F2
lda #$49
sta $EF
lda #$66 ;Palette 1 Color 1
sta P1C1 ;
lda #$56 ;Palette 1 Color 2
sta P1C2 ;
lda #$2E ;Palette 1 Color 3
sta P1C3 ;
lda #$AA ;Set NMI vector to Nmi_DrawFujiAndAtari
sta $F4 ;
lda #$FA ;
sta $F5 ;
Display_WaitForVblankEnd:
bit MSTAT ;Check VBLANK status
bmi Display_WaitForVblankEnd ;Wait till end of VBLANK
Display_WaitForVblankStart:
bit MSTAT ;Check BLANK status
bpl Display_WaitForVblankStart ;Wait for start of next VBLANK
lda #$84 ;Set Display list pointer to $1f84
sta DPPL ;
lda #$1F ;
sta DPPH ;
lda #$43 ;Maria mode = DMA on/320A or 320C
sta CTRL ;
Display_InitLogo_Done:
rts ;
;=============================================================================
;
; VI. GRAPHICS DATA AND DISPLAY LISTS
;
;=============================================================================
; --- Display List Definitions for Fuji/Atari Logo ---
; Each entry defines a block of scanlines, pointing to the graphics data
; to be displayed. Copied to $2200 in RAM.
DisplayList_Defs: ;$2200
.byte $84,$1F,$19,$BB ;$2200 Blank space
.byte $00,$00
.byte $84,$40,$19,$1F,$BB ;$2206 First DL on screen
.byte $00,$00
.byte $85,$1C,$19,$4A ;$220D Blank space before Fuji
.byte $00,$00
.byte $89,$1C,$19,$4A ;$2213 Fuji line 1
.byte $00,$00
.byte $8D,$1C,$19,$48 ;$2219 Fuji line 2
.byte $00,$00
.byte $91,$1B,$19,$46 ;$221F Fuji line 3
.byte $00,$00
.byte $96,$19,$19,$42 ;$2225 Fuji line 4
.byte $00,$00
.byte $9D,$17,$19,$3E ;$222B Fuji line 5
.byte $00,$00
.byte $A6,$17,$19,$3E ;$2231 Fuji line 6
.byte $00,$00
.byte $AF,$2C,$1C,$00 ;$2237 Start of Atari
.byte $AF,$2C,$1C,$50 ; and between lines
.byte $00,$00
.byte $AF,$2C,$1D,$00 ;$2241 End of Atari
.byte $AF,$2C,$1D,$50
.byte $00,$00
.byte $AF,$2D,$19,$28 ;$224B Atari line 1
.byte $00,$00
.byte $C2,$2D,$19,$28 ;$2251 Atari line 2
.byte $00,$00
.byte $D5,$2D,$19,$28 ;$2257 Atari line 3
.byte $00,$00
.byte $E8,$2D,$19,$28 ;$225D Atari line 4
.byte $00,$00
.byte $AF,$2D,$1A,$28 ;$2263 Atari line 5
.byte $00,$00
.byte $C2,$2D,$1A,$28 ;$2269 Atari line 6
.byte $00,$00
.byte $D5,$2D,$1A,$28 ;$226F Atari line 7
.byte $00,$00
.byte $E8,$2D,$1A,$28 ;$2275 Atari line 8
.byte $00,$00
.byte $AF,$2D,$1B,$28 ;$227B Atari line 9
.byte $00,$00
.byte $C2,$2D,$1B,$28 ;$2281 Atari line 10
.byte $00,$00
.byte $D5,$2D,$1B,$28 ;$2287 Atari line 11
.byte $00,$00
; --- Master Display List for Boot Screen ---
; This is the main display list that Maria reads. It is a list of pointers
; to the display list definitions above. Copied to $1F84 in RAM.
DisplayList_Master:
.byte $0F,$22,$06 ;Blank space
.byte $0F,$22,$00 ;
.byte $0F,$22,$00 ;
.byte $0F,$22,$00 ;
.byte $03,$22,$00 ;
.byte $85,$22,$0D ;DLI - Triggers NMI for color cycling
.byte $05,$22,$13 ;Draw Fuji
.byte $05,$22,$19 ;
.byte $05,$22,$1F ;
.byte $05,$22,$25 ;
.byte $05,$22,$2B ;
.byte $05,$22,$31 ;
.byte $0F,$22,$00 ;Blank area
.byte $01,$22,$37 ;Draw "ATARI"
.byte $00,$22,$4B ;Line 1
.byte $02,$22,$37
.byte $00,$22,$51 ;Line 2
.byte $02,$22,$37
.byte $00,$22,$57 ;Line 3
.byte $02,$22,$37
.byte $00,$22,$5D ;Line 4
.byte $02,$22,$37
.byte $00,$22,$63 ;Line 5
.byte $02,$22,$37
.byte $00,$22,$69 ;Line 6
.byte $02,$22,$37
.byte $00,$22,$6F ;Line 7
.byte $02,$22,$37
.byte $00,$22,$75 ;Line 8
.byte $02,$22,$37
.byte $00,$22,$7B ;Line 9
.byte $02,$22,$37
.byte $00,$22,$81 ;Line 10
.byte $02,$22,$37
.byte $00,$22,$87 ;Line 11
.byte $01,$22,$41
.byte $0F,$22,$00 ;Blank Space
.byte $0F,$22,$00 ;
.byte $0F,$22,$00 ;
.byte $0F,$22,$00 ;
.byte $0F,$22,$00 ;
; --- Fuji/Atari Logo Graphics Data ---
; These are the raw bitmap graphics for the logo, copied to RAM.
GraphicsData_FujiAtari_1:
.byte $00,$7c,$7f,$8f,$80,$fc,$7f,$8f,$c0,$1f,$87,$f8,$7e,$0f,$e0,$7f
GraphicsData_FujiAtari_2:
.byte $81,$fc,$07,$ff,$80,$7f,$80,$7f,$f8,$1f,$ff,$f0,$00,$7f,$80,$03
GraphicsData_FujiAtari_3:
.byte $ff,$fe,$1f,$00,$00,$00,$7f,$80,$00,$00,$3e,$00,$00,$0c,$00,$3f
GraphicsData_FujiAtari_4:
.byte $ff,$ff,$ff,$f0,$00,$c0,$00,$00,$3f,$ff,$ff,$00,$03,$fc,$00,$00
GraphicsData_FujiAtari_5:
.byte $3f,$00,$3f,$ff,$ff,$ff,$f0,$03,$f0,$00,$00,$3f,$ff,$ff,$fc,$03
GraphicsData_FujiAtari_6:
.byte $fc,$00,$00,$ff,$c0,$00,$03,$ff,$00,$00,$0f,$fc,$00,$00,$3f,$f0
GraphicsData_FujiAtari_7:
.byte $03,$ff,$c3,$fc,$00,$03,$ff,$f0,$00,$03,$ff,$00,$00,$3f,$ff,$00
LFD36:
.byte $00,$3f,$f0,$00,$3f,$c3,$fc,$00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80
LFD46:
.byte $1f,$87,$f8,$7e,$0f,$f0,$7f,$83,$fc,$01,$ff,$80,$7f,$80,$7f,$e0
LFD56:
.byte $1f,$ff,$f8,$00,$7f,$80,$07,$ff,$fe,$1f,$f0,$00,$00,$7f,$80,$00
LFD66:
.byte $03,$fe,$00,$0f,$f3,$fc,$00,$03,$ff,$00,$00,$ff,$3f,$c0,$00,$3f
LFD76:
.byte $f0,$00,$ff,$c3,$fc,$00,$3f,$c0,$ff,$00,$03,$ff,$00,$03,$fc,$0f
LFD86:
.byte $f0,$00,$3f,$f0,$3f,$fc,$03,$fc,$00,$ff,$00,$3f,$c0,$03,$ff,$00
LFD96:
.byte $0f,$f0,$03,$fc,$00,$3f,$f0,$ff,$c0,$03,$fc,$03,$ff,$ff,$ff,$f0
LFDA6:
.byte $03,$ff,$00,$3f,$ff,$ff,$ff,$00,$3f,$f0,$3f,$f0,$03,$fc,$00,$7c
LFDB6:
.byte $7f,$8f,$80,$7c,$7f,$8f,$80,$1f,$87,$f8,$7e,$07,$f0,$7f,$83,$f8
LFDC6:
.byte $00,$ff,$c0,$7f,$80,$ff,$c0,$1f,$ff,$fc,$00,$7f,$80,$0f,$ff,$fe
LFDD6:
.byte $1f,$fc,$00,$00,$7f,$80,$00,$0f,$fe,$0f,$ff,$ff,$ff,$fc,$03,$ff
LFDE6:
.byte $00,$ff,$ff,$ff,$ff,$c0,$3f,$f0,$0f,$fc,$03,$fc,$3f,$f0,$00,$03
LFDF6:
.byte $ff,$03,$ff,$03,$ff,$00,$00,$3f,$f0,$3f
LFE00:
.byte $f0,$03,$ff,$03,$fc,$ff
LFE06:
.byte $c0,$00,$00,$ff,$c3,$ff,$0f,$fc,$00,$00,$0f,$fc,$3f,$f0,$00,$ff
LFE16:
.byte $c3,$fc,$00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80,$0f,$87,$f8,$7c,$07
LFE26:
.byte $f0,$7f,$83,$f8,$00,$7f,$c0,$7f,$80,$ff,$80,$1f,$ff,$fe,$00,$7f
LFE36:
.byte $80,$1f,$ff,$fe,$1f,$ff,$00,$00,$7f,$80,$00,$3f,$fe,$55,$55,$55
LFE46:
.byte $55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55
LFE56:
.byte $55,$00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80,$0f,$c7,$f8,$fc,$03,$f0
LFE66:
.byte $7f,$83,$f0,$00,$3f,$e0,$7f,$81,$ff,$00,$01,$ff,$fe,$00,$7f,$80
LFE76:
.byte $1f,$ff,$e0,$1f,$ff,$c0,$00,$7f,$80,$00,$ff,$fe,$aa,$aa,$aa,$aa
LFE86:
.byte $aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa
LFE96:
.byte $00,$7c,$7f,$8f,$80,$7c,$7f,$8f,$80,$0f,$c7,$f8,$fc,$03,$f8,$7f
LFEA6:
.byte $87,$f0,$00,$1f,$e0,$7f,$81,$fe,$00,$00,$1f,$ff,$00,$7f,$80,$3f
LFEB6:
.byte $fe,$00,$1f,$ff,$e0,$00,$7f,$80,$01,$ff,$fe,$55,$55,$55,$55,$55
LFEC6:
.byte $55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$09
LFED6:
.byte $ca,$c9,$c6,$b4,$12,$08,$1b,$60,$58,$81,$4b,$86,$01,$d8,$bf,$d9
LFEE6:
.byte $25,$a0,$7b,$dc,$32,$79,$84,$3b,$7c,$bc,$2f,$e2,$e2,$fa,$8d,$0a
LFEF6:
.byte $00,$3b,$c5,$ec,$af,$2d,$8a,$cd,$06,$93
LFF00:
.byte $6a,$a5,$14,$46,$77,$c4
LFF06:
.byte $6a,$b2,$53,$36,$ef,$8c,$ce,$0c,$a2,$68,$71,$d3,$73,$e8,$f7,$6d
LFF16:
.byte $06,$b5,$20,$ef,$23,$47,$0c,$51,$55,$c8,$fe,$f4,$58,$c4,$3f,$20
LFF26:
.byte $a7,$67,$38,$b0,$76,$e2,$c4,$d8,$05,$63,$f8,$3c,$58,$3b,$2d,$22
LFF36:
.byte $cc,$88,$b3,$71,$8f,$1d,$80,$0a,$87,$bd,$a1,$59,$23,$e9,$70,$e2
LFF46:
.byte $d3,$ec,$46,$68,$80,$42,$39,$ea,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
LFF56:
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
LFF66:
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
LFF76:
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
LFF86:
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
LFF96:
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
LFFA6:
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
LFFB6:
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
LFFC6:
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
LFFD6:
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
LFFE6:
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
LFD3D = $FD3D
LFDB4 = $FDB4
LFDD5 = $FDD5
LFE18 = $FE18
LFE57 = $FE57
LFE84 = $FE84
LFE96 = $FE96
LFED5 = $FED5
LFF00 = $FF00
GccCopyright:
.byte $47,$43,$43,$28,$43,$29 ; 'GCC(C)'
.byte $31,$39,$38,$34,$2D,$F7 ; '1984-'
;=============================================================================
;
; VII. 6502 HARDWARE VECTORS
;
;=============================================================================
; These are the processor's hard-wired vectors at the top of memory.
; The BIOS uses these to direct the CPU on NMI, Reset, and IRQ events.
; When a cartridge is mapped in, its own vectors at these locations are used.
CartNMIVectorLo:
.byte $00,$F0 ; System NMI vector points to NmiVector_Rom.
CartResetVectorLo:
.word START ; System RESET vector points to $F884.
CartIRQVectorLo:
.byte $33,$F9 ; System IRQ vector.