----------------Jumpman---------------- A 4am crack 2016-09-15 --------------------------------------- Name: Jumpman Genre: arcade Year: 1983 Author: Randy Glover (Atari version) Publisher: Epyx Platform: Apple ][+ or later Media: double-sided 5.25-inch floppy OS: custom Previous cracks: The Woz Similar cracks: #595 Addition Magician #476 Microzine 2 Both sides are bootable. I'll start with side A. ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA immediate disk read error Locksmith Fast Disk Backup unable to read any track EDD 4 bit copy (no sync, no count) read error on T22 copy displays a graphical title page then hangs with the drive motor on Copy ][+ nibble editor T00 -> standard prologues, modified epilogues (FF FF FF) T01,02 -> not full tracks? looks like they have some standard-ish sectors, but not 16 per track (also corrupted address fields) T03,04 -> corrupted address fields that claim to be track $00 T05-T21 -> uncorrupted address fields but still non-standard epilogues T22 unformatted When I say "corrupted address fields," this is what that looks like: --v-- COPY ][ PLUS BIT COPY PROGRAM 8.4 (C) 1982-9 CENTRAL POINT SOFTWARE, INC. --------------------------------------- TRACK: 01 START: 21DC LENGTH: 189D ^^ 21B8: FF FF FF FF FF FF FF FF VIEW 21C0: FF FF FF FF FF FF FF FF 21C8: FF FF FF FF FF FF FF FF 21D0: FF FF FF FF FF FF FF FF 21D8: FF FF FF FF FF D5 AA 96 <-21DD ^^^^^^^^ address prologue 21E0: AA AA AA AA AA AA AA AA ^^^^^ ^^^^^ ^^^^^ ^^^^^ V000 T00 S00 chksm 21E8: FF FF FF FF FF CF F3 FC ^^^^^^^^ address epilogue 21F0: FF FF D5 AA AD 9B DB B9 ^^^^^^^^ data prologue 21F8: B9 DB F2 DE B9 AE B3 BA --------------------------------------- A TO ANALYZE DATA ESC TO QUIT ? FOR HELP SCREEN / CHANGE PARMS Q FOR NEXT TRACK SPACE TO RE-READ --^-- The disk is lying to me. The address field claims to be track $00, but it's really track $01. Bad disk! Stop lying! Disk Fixer ["O" -> "Input/Output Control"] set Address Epilogue to "FF FF FF" set Data Epilogue to "FF FF FF" T00 readable T01-T04 unreadable (no option to ignore the corrupted address field) T05-T21 readable T22 unreadable (unformatted) Copy ][+ sector editor ["P" -> "Sector Editor Patcher"] set type to "CUSTOM" set Address Epilogue to "FF FF" set Data Epilogue to "FF FF FF" T00 readable T05-T21 readable ["P" -> "Sector Editor Patcher"] set CHECK TRACK to "NO" only parts of T01 and T02 readable: T01: S03,04,05,06,07,0A,0B,0C,0D,0E T02: S01,02,08,09,0F T03,04 readable! Why didn't COPYA work? modified epilogue bytes on track $00 (it never even got to the fun part) Why didn't Locksmith FDB work? ditto Why didn't my EDD copy work? I've seen similar disks, where the first N tracks have intentionally corrupted address fields. (N varies from disk to disk.) There's a custom loader that loads the data from those corrupted tracks, then transfers control to a standard RWTS for the rest of the program. Somewhere in the corrupted tracks, it will load data from consecutive half tracks. (These are devilishly difficult to copy, and I didn't even try.) That's just an educated guess; I could be surprised. Hey, I can actually validate that guess in the Copy ][+ nibble editor, which can read half tracks. --v-- COPY ][ PLUS BIT COPY PROGRAM 8.4 (C) 1982-9 CENTRAL POINT SOFTWARE, INC. --------------------------------------- TRACK: 01.50 START: 3700 LENGTH: 198E ^^^^^ 3BD0: FF FF FF FF FF FF FF FF VIEW 3BD8: FF FF FF FF FF FF FF FF 3BE0: FF FF FF FF FF FF FF FF 3BE8: FF FF C9 FF FF FF FF FF 3BF0: FF FF FF FF FF D5 AA 96 <-3BF5 ^^^^^^^^ address prologue 3BF8: AA AA AA AA AF AB AF AB ^^^^^ ^^^^^ ^^^^^ ^^^^^ V000 T00 S0B chksm 3C00: FF FF FF 9F E7 F9 FE FF ^^^^^^^^ address eplogue 3C08: D5 AA AD A7 B4 BD CD ED ^^^^^^^^ data prologue 3C10: ED 9B ED F2 E9 DF B6 AB --------------------------------------- A TO ANALYZE DATA ESC TO QUIT ? FOR HELP SCREEN / CHANGE PARMS Q FOR NEXT TRACK SPACE TO RE-READ --^-- Jackpot! (Note that it's still claiming to be track $00, though, just like the other whole tracks above and below it.) Next steps: 1. Trace the boot 2. ??? ~ Chapter 1 Boot Trace and Chill [S6,D1=original disk] [S5,D1=my work disk] ]PR#5 CAPTURING BOOT0 ...reboots slot 6... ...reboots slot 5... SAVING BOOT0 ]CALL -151 *800<2800.28FFM *801L ; set reset vector 0801- 8A TXA 0802- 4A LSR 0803- 4A LSR 0804- 4A LSR 0805- 4A LSR 0806- 09 C0 ORA #$C0 0808- 85 3F STA $3F 080A- 8D F3 03 STA $03F3 080D- 49 A5 EOR #$A5 080F- 8D F4 03 STA $03F4 0812- A9 00 LDA #$00 0814- 8D F2 03 STA $03F2 ; hmm 0817- A9 04 LDA #$04 0819- 48 PHA ; machine initialization (memory banks, ; TEXT, IN#0, PR#0, &c.) 081A- 8D 81 C0 STA $C081 081D- 20 2F FB JSR $FB2F 0820- 8D 52 C0 STA $C052 0823- 20 89 FE JSR $FE89 0826- 20 93 FE JSR $FE93 ; clear hi-res screen 1 0829- A2 20 LDX #$20 082B- A0 00 LDY #$00 082D- 84 06 STY $06 082F- A9 20 LDA #$20 0831- 85 07 STA $07 0833- 98 TYA 0834- 91 06 STA ($06),Y 0836- C8 INY 0837- D0 FB BNE $0834 0839- E6 07 INC $07 083B- CA DEX 083C- D0 F6 BNE $0834 ; switch to hi-res screen 1 (blank) 083E- 8D 57 C0 STA $C057 0841- 8D 50 C0 STA $C050 0844- 8D 54 C0 STA $C054 0847- 8D 52 C0 STA $C052 ; set up ($3E) vector to point to the ; sector read routine in the disk ; controller ROM 084A- A9 5C LDA #$5C 084C- 85 3E STA $3E ; the disk controller ROM always exits ; via $0801, so set that to an RTS so ; we can JSR and not have to set up a ; loop 084E- A9 60 LDA #$60 0850- 8D 01 08 STA $0801 ; hmm 0853- A9 72 LDA #$72 0855- 48 PHA OK, we've now pushed $04/$72 on the stack. That's probably important. ; multi-sector read ; Y = first logical sector ($01) ; X = last logical sector ($08) ; A = start address high byte ($B8) 0856- A0 00 LDY #$00 0858- 84 FC STY $FC 085A- C8 INY 085B- A9 B8 LDA #$B8 085D- A2 08 LDX #$08 ; call multi-sector read routine 085F- 20 6C 08 JSR $086C ; another sector read, this time just ; one sector, into $AF00 (X is already ; less than Y on entry, so loop will ; exit after one read) 0862- A9 AF LDA #$AF 0864- A2 01 LDX #$01 0866- 20 6C 08 JSR $086C ; final sector read into $0400 (!) 0869- A9 04 LDA #$04 086B- AA TAX ; falls through to multi-sector read ; entry point (was also called earlier) 086C- 85 27 STA $27 086E- E8 INX 086F- 86 49 STX $49 0871- 84 F9 STY $F9 ; map logical into physical sector and ; store it in zero page where the disk ; controller ROM will look for it 0873- B9 8A 08 LDA $088A,Y 0876- 85 3D STA $3D ; read sector via disk controller ROM 0878- 20 85 08 JSR $0885 ; loop until done 087B- A4 F9 LDY $F9 087D- C8 INY 087E- C4 49 CPY $49 0880- 90 EF BCC $0871 0882- A5 27 LDA $27 0884- 60 RTS ; subroutine to read a sector via ($3E) ; which points to $Cx5C, which exits ; via $0801, which is now an "RTS" ; (HOW F---ING ELEGANT IS THAT, RIGHT?) 0885- A6 2B LDX $2B 0887- 6C 3E 00 JMP ($003E) ; physical to logical sector map 088A- .. .. 00 03 05 07 09 0B 0890- 0D 0F 02 04 06 08 0A 0C 0898- 0E 01 That's it. Flexible but compact. It's a weird combination of reads, though. It loads a bunch of sectors at $B800, one at $AF00, then the last one at $0400. That's part of the text page, but it's hidden during boot because we cleared the entire hi-res graphics page and showed that instead. Of course, we manually pushed $04/$72 on the stack earlier, so once we fall through to the sector read routine, reads the last sector, and hits the RTS we put at $0801, it will "return" to $0472 + 1 = $0473. Let's interrupt the boot before it gets there. ~ Chapter 2 In Which Things Get Brilliantly Weird *9600physical sectors is at ; $0463) or a physical sector 241D- 24 4A BIT $4A 241F- 30 03 BMI $2424 2421- B9 63 04 LDA $0463,Y ; store physical sector in $3D (again, ; used by the disk controller ROM) 2424- 85 3D STA $3D ; read sector by jumping to ($003E), ; which points to $Cx5C (e.g. $C65C if ; booting from slot 6) and exit via ; $0801, which is an RTS by now, so ; this just continues to the next line 2426- 20 00 04 JSR $0400 ; increment sector index 2429- A4 F9 LDY $F9 242B- C8 INY ; are there more sectors to read? 242C- C4 49 CPY $49 ; yes, branch back and repeat 242E- 90 EA BCC $241A ; no, exit with last page (+1) in A ; (disk controller ROM increments this ; after storing sector data, so on exit ; this will be the first page that was ; NOT filled with data in this loop) 2430- A5 27 LDA $27 2432- 60 RTS But wait, it gets even better. ~ Chapter 3 Every Byte Is Sacred, Every Byte Is Great, If A Byte Gets Wasted, Woz Gets Quite Irate Continuing from $04B2... ; move the drive head one phase only, ; to the next HALF track 24B2- 20 36 04 JSR $0436 [now on track 1.5] ; read more sectors ($06..$0A) 24B5- A2 0A LDX #$0A 24B7- 20 15 04 JSR $0415 ; advance another half track 24BA- 20 36 04 JSR $0436 [now on track 2] ; read more sectors ($0B..$0F) 24BD- A2 0F LDX #$0F 24BF- 20 15 04 JSR $0415 ; fiddle with $4A again 24C2- 46 4A LSR $4A 24C4- 60 RTS So here's the deal with $4A: we initialized it at $0473 by a blind LSR, which clears the high bit. This tells the multi-sector read routine at $0415 to use logical sectors. Then we set the high bit at $04A6 with SEC + ROR, indicating we want $0415 to read physical sectors. Then we read a few sectors from track 1, a few from track 1.5, and a few from track 2. Then we reset $4A with another LSR, and we're back to using logical sectors. This explains why my EDD bit copy failed. This disk is storing data on half tracks. Worse, it's storing data on *adjacent* half tracks -- a few from track 1, a few from track 1.5, and a few from track 2. Due to limitations of the Disk II drive mechanism, that would be virtually impossible for a generic bit copier to reproduce on a blank floppy disk. Every part of this code is brilliant, AND it fits in a single sector on the text page, AND it's flexible enough to read from virtually uncopyable disks. Continuing from $0478... *2478L 2478- A9 80 LDA #$80 247A- 20 0E 04 JSR $040E This is very interesting. $0405 looks like this: *2405L 2405- 20 0E 04 JSR $040E 2408- 20 0E 04 JSR $040E 240B- 20 0E 04 JSR $040E 240E- 20 33 04 JSR $0433 2411- A2 0F LDX #$0F 2413- A0 00 LDY #$00 2415- 85 27 STA $27 . . multi-sector read routine (see above) . $0411 sets X and Y to read an entire track (sector $00 through $0F). Before that, $040E advances to the next whole track. And before *that*, we have three identical JSRs to $040E, each of which falls through to the next, and eventually to $040E again. Thus, calling $040E will advance one whole track and read one whole track. Calling $040B will do that twice, reading each track into consecutive memory (because the multi-sector read routine ends with next page in memory in the accumulator, so you can chain them and just fill up memory without having to reset the starting page). Calling $0408 will do it three times, and calling $0405 will do it four times (again, into consecutive memory). So these two lines of code... 2478- A9 80 LDA #$80 247A- 20 0E 04 JSR $040E ...will advance to track $03 and read it into $8000..$8FFF. Continuing from $047D... ; advance one whole track 247D- 20 33 04 JSR $0433 [now on track 4] ; read most of track 4 into the next ; consecutive address after the last ; read, so $9000+ 2480- A2 0C LDX #$0C 2482- 20 13 04 JSR $0413 By hook and by crook, we've managed to fill up $B800..$BFFF (from track 0) and most of $8000..$AFFF (from tracks 1, 1.5, 2, 3, and 4 -- oh, and one sector from track 0 just for good measure). ; turn off drive motor 2485- A6 2B LDX $2B 2487- BD 88 C0 LDA $C088,X ; set up DOS globals (tracking where ; the drive head is) 248A- 20 8E BE JSR $BE8E 248D- A5 FC LDA $FC 248F- 99 78 04 STA $0478,Y 2492- 4A LSR 2493- 8D 78 04 STA $0478 ; push $7F/$FF to the stack 2496- A9 7F LDA #$7F 2498- 48 PHA 2499- A9 FF LDA #$FF 249B- 48 PHA ; and exit via HOME routine 249C- 4C 58 FC JMP $FC58 And that's where I get to interrupt the boot. ~ Chapter 4 In Which We See The Light At The End Of The Tunnel, And It's A DOS-shaped RWTS Which Is A Weird Thing To See In A Tunnel, Honestly *9600 Some manual listing confirms that we have successfully captured $8000..$AFFF and $B800..$BFFF in memory. The part at $B800 is a DOS 3.3-shaped RWTS, likely used to read the rest of the disk after the custom bootloader zaps itself from memory by calling HOME (which wipes the text screen). ; copy used portions to lower memory so ; they survive a reboot *2000<8000.AFFFM *5800 A9 80 20 08 04 4C 85 04 which looks like this (using Disk Fixer's built-in disassembler): --v-- 0073:A9 80 LDA #$80 0075:20 08 04 JSR $0408 0078:4C 85 04 JMP $0485 --^-- Second, a new routine at $047B (now unused space) to increment the track number in zero page. T00,S0C,$7B -> E6 41 4C 36 04 --v-- 007B:E6 41 INC $41 007D:4C 36 04 JMP $0436 --^-- Third, the patch at $0433 to call the new routine at $047B so we increment the track number in zero page before advancing the drive arm. T00,S0C,$34: 36 -> 7B --v-- 0033:20 7B 04 JSR $047B --^-- And finally, several patches to the DOS 3.3-shaped RWTS that was loaded from track 0 and later used to read the rest of the disk (T05+). The sector ordering is weird because it's loading them in physical sector order, but I found all the usual patch points. T00,S02,$AE: FF -> DE T00,S02,$B3: FF -> AA T00,S05,$35: FF -> DE T00,S05,$3F: FF -> AA T00,S05,$91: FF -> DE T00,S05,$9B: FF -> AA T00,S06,$9E: FF -> DE T00,S06,$AE: FF -> AA ]PR#6 ...works, and it is glorious... Side B has identical protection. Quod erat liberandum. --------------------------------------- A 4am crack No. 840 ------------------EOF------------------