!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. ; ;------------------------------- ; 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 ; Zero-page addresses we use for variables !ifdef PASS2 { } else { nibsrcindex = $EC ; byte nibdestindex = $ED ; byte prbuf = $EE ; word unform = $F0 ; word nibcount = $F2 ; byte modtmp = $F3 ; byte modsrc = $F4 ; word moddest = $F6 ; word cmp1 = $F8 ; word cmp2 = $FA ; word counter = $FC ; byte tmp = $FC ; byte iunit = $FC ; byte tmpa = $FC ; byte tmpx = $FD ; byte tmpy = $FE ; byte flag = $FF ; byte } ; Application constants (not zero addresses) RELBASE = $5800 ; address to move Passport code ; so that it's out of the way LOWPOINT = $4300 ; lowest available address for code BASEPAGE = $10 ; Special Delivery tracer assumes ; this is $10, so don't change it! TRUE = $00 ; Lots of code assumes this is $00 ; so it can branch with BEQ, so ; don't change it either! FALSE = $01 ldx #$00 FM lda FirstMover + ((255 + LastMover - FirstMover) & $FF00),x sta $B500,x inx bne FM dec FM+2 dec FM+5 lda FM+2 cmp #$1F 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/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 "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 jsr CreateRAMFile lda #%11000000 ; note: execution falls through here Action sta gMode jsr ResetProgress jsr InitSectorMap jsr CopyUniversal lda #FALSE sta gTriedUniv sta gSaidWriting lda #$00 sta gTrack sta gSector sta gPatchCount sta callrwts+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 lda #$00 sta $b991 ldy #gRWTSParams ; if T00,S00 is readable jsr $BD00 ; (only care about the return code) bcc .checkt00s00 lda #s_fail jsr PrintByID lda #s_fatal0000 jsr PrintByID jmp TheEnd .checkt00s00 lda #$02 sta $b991 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 ; 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 ; current sector in sector map pha beq nextsector ; #$00 = skip this sector cmp #$FE ; #$FE = switch to built-in RWTS bne + ; to read this sector lda gTriedUniv beq + jsr SwitchToUniv + jsr ReadSector bcc nextsector pla pha ; ; Uh oh, we got a read error. But do we care? ; Maybe we marked this sector as optional based ; on markers in the bootloader. ; cmp #$80 beq .optional ; ; If we're in the middle of a track, 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.) ; lda gSector cmp #$0F bne .tryuniversal ; ; We just got to this track, so check for a variety ; of whole-track conditions that might indicate we should ; just skip the entire track jsr SkipTrack bcs .tryuniversal ; Skip this track (we already printed the reason) lda #$00 jsr ChangeSector lda checksector+1 sec sbc #$0F sta checksector+1 bcs .notrackdec dec checksector+2 .notrackdec jmp nextsector .tryuniversal 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 .skipram lda #s_writing jsr PrintByID jsr WriteRAMToDisk .skipram 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 #>universalrwts ldx #$B8 ldy #$08 jsr CopyMemory lda #$00 sta callrwts+1 lda #$BD sta callrwts+2 rts Cancel lda #s_canceled jsr PrintByID jmp TheEnd FatalError lda #s_fail jsr PrintByID lda gTrack cmp #$22 beq .failont22 jmp TheEnd .failont22 lda gSector cmp #$0F beq .failont22s0f jmp TheEnd .failont22s0f lda #s_fatal220f jsr PrintByID ; note: execution falls through here TheEnd lda $C0E8 lda #s_done jsr PrintByID jsr WaitForKey cmp #$9B beq CleanExit jmp MainMenu CleanExit jsr SwapProDOS lda gChangedPrefs bne .checkRAM jsr SavePrefs .checkRAM lda gRAMDiskRef beq .doquit jsr CloseFile ; leave it behind in case it's wanted .doquit jsr MLI !byte $65 !word .quitparm .quitparm !byte $04,$00,$00,$00,$00,$00,$00 FatalNoSlot6 lda #s_noslot6 jsr PrintByID jsr WaitForKey jmp CleanExit ResetProgress lda #$82 ; reset progress indicator sta progressind+1 lda #$05 sta progressind+2 rts IncProgress lda #$20 ; display minimal progress indicator progressind sta $FFFF ; set at runtime inc progressind+1 rts ;------------------------------- ; WriteTrack ;------------------------------- MLI_IOERR = $27 MLI_NODEV = $28 MLI_WRITEPROT = $2B WriteTrack jsr AnalyzeTrack WriteTrackNA ; entry point used by Special Delivery tracer ; to write track with "N"o "A"nalysis bit gMode bpl .done ; verify mode -> no write lda gSaidWriting beq .write lda #s_writeram ; only print "writing to" message once ldx gUsingRAMDisk beq + lda #s_writing ; only print "writing to" message once + jsr PrintByID lda #TRUE sta gSaidWriting .write jsr WriteTrackMLI bcc .done 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 .done rts ;------------------------------- ; ChangeTrack ; in: A = new track ;------------------------------- ChangeTrack pha jsr WriteTrack pla ; 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 as it finds and 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 beq _applyToT00 jmp _applyToAll _applyToT00 jsr AnalyzeT00 ; /src/id/inspect0 !source "patchers/sunburst.a" !source "patchers/jmpbcf0.a" !source "patchers/jmpbeb1.a" !source "patchers/jmpbeca.a" !source "patchers/jmpb660.a" !source "patchers/jmpb720.a" !source "patchers/bademu.a" !source "patchers/bademu2.a" !source "patchers/rwts.a" !source "patchers/rwtslog.a" !source "patchers/mecc1.a" !source "patchers/mecc2.a" !source "patchers/mecc3.a" !source "patchers/mecc4.a" !source "patchers/rol1e.a" !source "patchers/jmpb4bb.a" !source "patchers/jmpb4bbhi.a" !source "patchers/thunder.a" !source "patchers/jsrbb03.a" !source "patchers/davidbb03.a" !source "patchers/rwtsswap.a" !source "patchers/rwtsswap2.a" !source "patchers/border.a" !source "patchers/jmpae8e.a" !source "patchers/jmpbbfe.a" !source "patchers/datasoft.a" !source "patchers/nibtable.a" !source "patchers/diskvol.a" !source "patchers/c9ff.a" !source "patchers/milliken.a" !source "patchers/methods.a" !source "patchers/jsr8b3.a" !source "patchers/laureate.a" !source "patchers/pascalrwts.a" !source "patchers/micrograms.a" !source "patchers/dos32.a" _applyToAll !source "patchers/universale7.a" !source "patchers/a6bc95.a" ; gIsPascal only !source "patchers/a5count.a" ; gIsPascal only !source "patchers/d5d5f7.a" ; gIsPascal || (gIsBoot0 && gPossibleD5D5F7) only !source "patchers/prodosrwts.a" ; gIsProDOS only !source "patchers/prodos6a.a" ; gIsProDOS only !source "patchers/prodosmecc.a" ; gIsProDOS only !source "patchers/bbf9.a" ; gIsProDOS only !source "patchers/memory.config.a" ; gIsProDOS only !source "patchers/rwtsswapmecc.a" ; gIsMECCFastloader only !source "patchers/protecteddos.a" ; gIsProtDOS only !source "patchers/fbff.a" !source "patchers/sierra.a" ; gIsBoot0 only !source "patchers/corrupter.a" ; T13 only !source "patchers/ea.a" ; (gIsEA || gIsBoot0) only !source "patchers/gamco.a" ; gPossibleGamco only !source "patchers/optimum.a" ; gIsOptimum only !source "patchers/bootcounter.a" ; T01 only !source "patchers/jmpb412.a" ; T02 only !source "patchers/jmpb400.a" ; T02 && gIsBoot0 only !source "patchers/advint.a" ; gAdventureInternational only lda gPatchCount beq .nopatches clc !byte $24 ; hide next SEC .nopatches sec ; set carry if nothing happened rts ; ; Global variables ; gTriedUniv !byte FALSE ; 0=true, 1=false ; whether or not we've already tried the built-in RWTS ; (and therefore a read error is fatal) ; reset before each operation gMode !byte %00000000 ; bit 7 0=verify, 1=see bit 6 ; bit 6 0=demuffin, 1=crack ; set based on main menu choice gPatchCount !byte 00 ; int ; number of patches we've applied to this disk ; reset before each operation ; incremented in modify() gSaidWriting !byte FALSE ; 0=true, 1=false ; reset before each operation ; set to #TRUE after we print WRITING TO S%s,D%d gChangedPrefs !byte FALSE ; 0=true, 1=false ; whether we should try to write the configuration file ; when the program exits ; set to #TRUE when changing slots gLastTrack !byte 00 ; int ; the last track that we should try to read ; (assuming reading from T22 down to T00) ; reset to 0 before each operation, but some ; disks (like DOS3.3P) will change it because ; they write out the first few DOS tracks ; manually before seeking up to T22 to convert ; the rest of the disk gIsDOS32 !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in IDBootloader() after reading T00,S00 gIsBoot0 !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in IDBootloader() after reading T00,S00 gIsBoot1 !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in AnalyzeT00() after reading T00 gIsMaster !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in AnalyzeT00() after reading T00 gIsRWTS !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in AnalyzeT00() after reading T00 gIsProDOS !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in IDBootloader() after reading T00,S00 gIsPascal !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in IDBootloader() after reading T00,S00 gIsProtDOS !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in IDBootloader() after reading T00,S00 gIsDavidDOS !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in IDBootloader() after reading T00,S00 gIsEA !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in IDBootloader() after reading T00,S00 gPossibleGamco !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in SkipTrack() after reading T22 gIsOptimum !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in IDBootloader() after reading T00,S00 gIsMECCFastloader !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in IDBootloader() after reading T00,S00 gPossibleD5D5F7 !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in IDBootloader() after reading T00,S00 gAdventureInternational !byte FALSE ; 0=true, 1=false ; reset before each operation ; set in TraceDOS33c() after tracing DOS 3.3-shaped RWTS gOnAClearDayYouCanReadForever !byte FALSE ; 0=true, 1=false ; retry reads with a captured RWTS forever, ; instead of falling back to built-in RWTS ; compile-time flag, no way to change at runtime gUsingRAMDisk !byte FALSE ; 0=true, 1=false gRAMDiskRef !byte 00 ; handle of RAM disk ; non-zero if open !source "universalrwts.a" } LastMover !if ($B500 - ((255 + LastMover - FirstMover) & $FF00)) != RELBASE { !serious "RELBASE requires adjustment to ", ($B500 - ((255 + LastMover - FirstMover) & $FF00)) } !if ($B500 - ((255 + LastMover - FirstMover) & $FF00)) < LOWPOINT { !serious "code is too large to fit in available space!" }