!cpu 6502 *=$2000 ;------------------------------- ; Passport ; a 4am hack ; (c) 2016-7 by 4am ; ; Permission is hereby granted, free of charge, to any ; person obtaining a copy of this software and associated ; documentation files (the "Software"), to deal in the ; Software without restriction, including without limitation ; the rights to use, copy, modify, merge, publish, ; distribute, sublicense, and/or sell copies of the ; furnished to do so, subject to the following conditions: ; ; The above copyright notice and this permission notice ; shall be included in all copies or substantial portions of ; the Software. ; ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY ; KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE ; WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR ; PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS ; OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR ; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR ; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ; ;------------------------------- !ifndef VERBOSE { VERBOSE = $00 ; set to $01 to display API label addresses } ; Supported languages ; (each has its own localized strings file ; and its own output filename) ENGLISH = 1 ; src/strings/en, PASSPORT.SYSTEM FRENCH = 2 ; src/strings/fr, PASSPORT.FR SPANISH = 3 ; src/strings/es, PASSPORT.ES ITALIAN = 4 ; src/strings/it, PASSPORT.IT FINNISH = 5 ; src/strings/fi, PASSPORT.FI ; Current language for this assembly LANG = ENGLISH !if LANG=ENGLISH { !to "../build/PASSPORT.SYSTEM",plain } !if LANG=FRENCH { !to "../build/PASSPORT.FR",plain } !if LANG=SPANISH { !to "../build/PASSPORT.SP",plain } !if LANG=ITALIAN { !to "../build/PASSPORT.IT",plain } !if LANG=FINNISH { !to "../build/PASSPORT.FI",plain } ;------------------------------- ; Addresses we read/call TEXTTOP = $22 VPOS = $25 MLI = $BF00 REBOOT = $FAA6 TEXT = $FB2F MACHINEID = $FBB3 HOME = $FC58 WAIT = $FCA8 PRBYTE = $FDDA COUT = $FDED PR0 = $FE89 IN0 = $FE93 ;Universal RWTS-specific values UNIV_A1 = $B956 ; must LSR before setting UNIV_A2 = $B95F UNIV_A3 = $B968 UNIV_D1 = $B8E7 UNIV_D2 = $B8F1 UNIV_D3 = $B8FC !source "apidefs.a" ldx #$00 FM lda LastMover - 256,x sta $B500,x inx bne FM dec FM+2 dec FM+5 lda FM+5 cmp #(>RELBASE)-((>(RELBASE+255))->RELBASE)-1 bne FM jmp OneTimeSetup FirstMover !pseudopc RELBASE { !zone ; use localized strings based on current language !if LANG=ENGLISH { !source "strings/en.a" } !if LANG=FRENCH { !source "strings/fr.a" } !if LANG=SPANISH { !source "strings/es.a" } !if LANG=ITALIAN { !source "strings/it.a" } !if LANG=FINNISH { !source "strings/fi.a" } !source "analyze.a" !source "id/inspect0.a" !source "id/trace.a" !source "id/trace33.a" !source "id/trace32.a" !source "id/trace8b3.a" !source "id/trace33p.a" !source "id/dos33.a" !source "id/prodos.a" !source "id/pascal.a" !source "id/rdos.a" !source "id/jsr8b3.a" !source "id/mecc.a" !source "id/datasoft.a" !source "id/protecteddos.a" !source "id/encode44.a" !source "id/encode53.a" !source "id/ea.a" !source "id/milliken.a" !source "id/daviddos.a" !source "id/quickdos.a" !source "id/diversidos.a" !source "id/prontodos.a" !source "id/d5d5f7.a" !source "id/laureate.a" !source "id/micrograms.a" !source "id/volumename.a" !source "id/dinkeydos.a" !source "print.a" !source "compare.a" !source "modify.a" !source "memory.a" !source "sectormap.a" !source "mli.a" !source "slots.a" !source "prefs.a" !source "keys.a" !source "cffa.a" !source "rwts.a" !source "standarddelivery.a" OneTimeSetup lda $C0E8 jsr SaveProDOS ldx MACHINEID cpx #$EA bne .slotscan lda #$DF sta kForceLower .slotscan jsr ScanForDiskII lda DiskIIArray+5 bne .founds6 jmp FatalNoSlot6 .founds6 jsr ScanForRAMDisk jsr LoadPrefs ; load preferences (if available) bcc ResetVector jsr SavePrefs ; save preferences (if possible) ResetVector lda #ResetVector sta $03F3 eor #$A5 sta $03F4 lda $C0E8 jsr PR0 jsr IN0 sta $C000 sta $C002 sta $C004 sta $C00C sta $C00E MainMenu ldx #$FF txs jsr ClearScreen lda #s_header jsr PrintByID lda #s_mainmenu jsr PrintByID .getkey jsr WaitForKey cmp #$80 bne + jsr EnterCFFAIfAvailable bcc ResetVector bcs .getkey + cmp #$9B ; esc beq .jmptoexit cmp #$91 ; ctrl-Q beq .jmptoexit cmp #k_quit bne + .jmptoexit jmp CleanExit + cmp #k_slot bne + jsr NextSlot lda #TRUE sta gChangedPrefs jmp MainMenu + cmp #k_verify bne + lda #%00000000 beq Action ; unconditional branch + cmp #k_demuffin bne + lda #%10000000 bne Action ; unconditional branch + cmp #k_crack bne .getkey lda #%11000000 ; note: execution falls through here Action sta gMode bpl + jsr CreateRAMFile + jsr ResetProgress jsr InitSectorMap lda #FALSE sta gTriedUniv sta gSaidWriting sta gIsProtDOS lda #$00 sta gTrack sta gSector sta gPatchCount sta jCallRWTS+1 lda #$08 sta gAddress+1 jsr ClearScreen lda #s_header jsr PrintByID lda #s_progbar jsr PrintByID lda VPOS sta TEXTTOP lda #s_reading jsr PrintByID jsr CopyUniversal jsr IgnoreAddressChecksum jsr ReadSector bcc + lda #s_fail jsr PrintByID lda #s_fatal0000 jsr PrintByID jmp TheEnd + jmp IDBootloader ; /src/id/inspect0 ; ; We are now fairly confident that the RWTS in memory ; is normal enough to call, Advanced Demuffin style. ; ADStyle jsr IncProgress lda #s_diskrwts jsr PrintByID ; ; Check for protections in early boot that ; might indicate intentional bad sectors ; elsewhere in the disk that we should skip, ; or changes we need to make to the RWTS ; before we start. ; jsr xHeredityDog bcs .noDog lda #s_bb00 jsr PrintByID .noDog jsr xSunburst bcs .noSun lda #s_sunburst jsr PrintByID .noSun jsr xOptimumRes bcs .noOptimum lda #TRUE sta gIsOptimum lda #s_optimum jsr PrintByID .noOptimum jsr xB660 jsr xB4BB jmp ReadWithRWTS UseUniversal jsr IncProgress jsr StartWithUniv lda gIsDinkeyDOS bne + ; ; On Dinkey-DOS disks (e.g. Ultima V, Times of Lore), we start with the ; universal RWTS then apply a patch in memory. ; These disks use a hybrid RWTS that alters the sector number in the ; address field based on the address epilogue. Once we normalize the ; sector numbers, no further patches are required, since the RWTS can also ; read unprotected disks. A separate patcher finds the actual code that ; alters the sector number to print it to the log. ; jsr PatchUniversalRWTSForOrigin bmi ReadWithRWTS ; always branches ; ; On other disks that use the universal RWTS for the entire disk, we use ; an adaptive RWTS that accepts any epilogue on the first sector but then ; enforces all other sectors to have the same epilogue. ; + jsr PatchUniversalRWTSForAdaptive ; ; Some disks have different epilogues on track 0, so we stop using the ; adaptive RWTS so we don't falsely accuse those disks of being damaged. ; lda #kSectorResetAdaptiveRWTS sta T00S0F ; ; Some disks have an intentionally invalid checksum on T00,S00 (read by ; the drive firmware, which doesn't verify it). ; lda #kSectorIgnoreAddressChecksum sta T00S00 ; ; note: execution falls through here ; ReadWithRWTS lda #$22 jsr ChangeTrackNW lda #$0F jsr ChangeSector lda #T22S0F sta checksector+2 .read lda KEY bpl checksector jmp Cancel checksector lda $FFFF ; status of current sector in sector map (modified above) pha cmp #kSectorCustomFirst ; call a custom routine before deciding what to do with this sector? bcc + cmp #kSectorCustomLast bcs + jsr PreReadSector tax pla txa pha ; replace status (on stack) with new status returned from PreReadSector + cmp #kSectorIgnore ; skip this sector? beq nextsector + cmp #kSectorSwitchToBuiltinRWTS ; switch to built-in RWTS before reading this sector? bne + lda gTriedUniv beq + jsr SwitchToUniv + jsr ReadSector bcc nextsector ; ; Uh oh, we got a read error. But do we care? ; If we just got to this track, check for whole-track protections. ; ldx #$0F ;16-sector lda gIsDOS32 bne + ldx #$0C ;13-sector + cpx gSector bne .checkoptional stx .sub+1 jsr SkipTrack bcs .checkoptional ; Skip this track (we already printed the reason) lda #$00 jsr ChangeSector lda checksector+1 sec .sub sbc #$0F ;self-modified according to sectors per track sta checksector+1 bcs + dec checksector+2 + jmp nextsector ; do this only *after* checking for track-skip ; to avoid fatal errors on unformatted tracks .checkoptional pla pha ; ; Maybe we marked this sector as optional based ; on markers in the bootloader. ; cmp #kSectorOptional beq .optional ; ; Otherwise we're in the middle of a track, so try switching to ; the universal RWTS and see if that helps. (Many disks contain ; an RWTS that can't read the early tracks or sectors that ; contain the RWTS code, since those are loaded by the ; disk controller firmware.) ; .tryuniversal lda gIsDOS32 ; is this a DOS 3.2 disk? beq .fatal ; yes, so read error is fatal lda gTriedUniv ; have we tried the universal RWTS? beq .fatal ; yes, so read error is fatal jsr SwitchToUniv ; no, switch it in now jmp .read ; and re-read this sector .fatal pla ; if we get to here, we've jmp FatalError ; decided the read error is fatal .optional lda #s_optbad ; say we're skipping this jsr PrintByID ; optional sector ; note: execution falls through here nextsector pla dec checksector+1 lda checksector+1 cmp #$FF bne .nodec dec checksector+2 .nodec lda gSector sec sbc #$01 jsr ChangeSector lda gSector bmi .prevtrack jmp .read .prevtrack lda #$0F jsr ChangeSector lda gTrack sec sbc #$01 jsr ChangeTrack jsr IncProgress lda gTrack bmi .pass cmp gLastTrack bcc .pass jmp .read .pass bit gMode bmi .passwrite lda #s_pass bne .passprint ; always branches .passwrite bvs .passcrack lda #s_passdemuf bne .passprint ; always branches .passcrack lda gUsingRAMDisk bne + lda #s_writing jsr PrintByID jsr WriteRAMToDisk bcc + jmp FatalWriteError + lda gPatchCount beq .passcrack0 lda #s_passcrack !byte $2C ; hide next LDA .passcrack0 lda #s_passcrack0 .passprint jsr PrintByID jmp TheEnd SwitchToUniv lda #s_switch !byte $2C ; hide next LDA StartWithUniv lda #s_builtin jsr PrintByID lda #TRUE sta gTriedUniv lda #FALSE sta gIsProtDOS ; note: execution falls through here CopyUniversal lda #$B8 CopyUniversalAnywhere sta universalrwts clc adc #$02 sta .cu+2 lda #>universalrwts sta _byte_hi lda # no write lda gSaidWriting beq .write lda #s_writeram ; only print "writing to RAM disk" message once ldx gUsingRAMDisk beq + lda #s_writing ; only print "writing to slot/drive" message once + jsr PrintByID lda #TRUE sta gSaidWriting .write jsr WriteTrackMLI bcs FatalWriteError .exit rts ;------------------------------- ; FatalWriteError ; in: A has MLI code (from WriteTrackMLI) ; out: exits via TheEnd ;------------------------------- FatalWriteError sta gDisplayBytes ; for use in error messages, if any cmp #MLI_IOERR beq .ioerr cmp #MLI_NODEV beq .nodev cmp #MLI_WRITEPROT beq .writeprot lda #s_othermli !byte $2C ; hide next LDA .ioerr lda #s_writeioerr !byte $2C ; hide next LDA .nodev lda #s_writenodev !byte $2C ; hide next LDA .writeprot lda #s_writeprot pha lda #s_writeerr jsr PrintByID pla jsr PrintByID jmp TheEnd ;------------------------------- ; ChangeTrack ; in: A = new track ;------------------------------- ChangeTrack sta .new+1 jsr WriteTrack .new lda #$d1 ; modified at runtime ; note: execution falls through here ChangeTrackNW ; "N"o "W"rite sta gTrack jsr ClearTSBuffer rts ;------------------------------- ; ChangeSector ; in: A = new sector ;------------------------------- ChangeSector sta gSector clc adc #BASEPAGE sta gAddress+1 rts ;------------------------------- ; AnalyzeTrack routine ; Looks at buffer in memory to detect known ; copy protections and disable/revert/modify them ; to work on standard disks. ; Prints through COUT ; Makes modifications in memory only. ; in: $BASEPAGE page contains one track worth of data ; out: if C set, no known protections were found and ; no modifications were made ; if C clear, at least one modification was made ;------------------------------- AnalyzeTrack lda gTrack bne _applyToAll _applyToT00 lda #>AnalyzeT00 sta _byte_hi lda #