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.