----------The Spanish Hangman---------- A 4am crack 2022-01-03 --------------------------------------- Name: The Spanish Hangman Version: rev. 2 (*) Genre: educational Year: 1983 Credits: George Earl Platform: Apple ][ (32K) or later Media: 5.25-inch disk Sides: 2 OS: DOS 3.2 Previous cracks: none Identical cracks: #2181 The Latin Hangman #2272 The Spanish Hangman #2753 The German Hangman #2756 The French Hangman #2757 The Russian Hangman #2766 The German Hangman rev. 2 #2767 The Russian Hangman rev. 2 (*) Compared to 4am crack no. 2272, this disk has several spelling corrections, suggesting a later version. ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA immediate disk read error Locksmith Fast Disk Backup can't read anything beyond T00,S00 EDD 4 bit copy (no sync, no count) works Copy ][+ 8.4 nibble editor appears to be 13-sector, 5-3 encoded tracks 0-2 are standard but tracks 3+ use modified prologues and epilogues address prologue "D5 BB B5" epilogue "DE BB EB" data prologue "D5 BB AD" epilogue "DE BB EB" Copy ][+ 8.4 sector editor ["P" for sector editor patcher] ["DOS 3.2"] ["CUSTOM"] ["PROLOG" = "D5 BB B5" (address)] ["EPILOG" = "DE BB" (address)] ["PROLOG" = "D5 BB AD" (data)] ["EPILOG" = "DE BB" (data)] Success! T03+ readable T11 -> looks like disk catalog Why didn't COPYA work? not a 16-sector disk Why didn't Locksmith FDB work? not a 16-sector disk EDD worked. What does that tell us? no half or quarter tracks almost certainly no nibble check just structural changes to prologues/ epilogues Next steps: 1. Capture 13-sector RWTS with AUTOTRACE 2. Convert to 16-sector format with Advanced Demuffin 3. Copy files to freshly formatted DOS 3.3 disk 4. Declare victory(*) (*) do not go to the gym, stay the f--- home ~ Chapter 1 In Which It's The End of The World As We Know It, And I Feel Like The Music of My Childhood Instilled Unrealistic Expectations of How I Would Feel About It Passport has some ability to convert 13-sector disks, but in this case it decides the bootloader is too unusual and bails. So we're going to dust off my old work disk with my old AUTOTRACE program. [S6,D1=original disk] [S5,D1=my work disk] ]PR#5 CAPTURING BOOT0 ...reboots slot 6... ...reboots slot 5... SAVING BOOT0 /!\ BOOT0 RELOCATES TO $0200 CAPTURING BOOT0 STAGE 2 ...reboots slot 6... ...reboots slot 5... SAVING BOOT0 STAGE 2 CAPTURING BOOT1 ...reboots slot 6... ...reboots slot 5... SAVING BOOT1 SAVING RWTS /!\ 13-SECTOR RWTS Here's what it captured automatically. (This is the standard "boot a 13-sector disk on a 16-sector drive" bootloader. If you know how that works, you can skip to Chapter 2.) ]BLOAD BOOT0,A$800 ]CALL -151 *801L ; immediately move this code to the ; input buffer at $0200 0801- A0 0F LDY #$0F 0803- B9 00 08 LDA $0800,Y 0806- 99 00 02 STA $0200,Y 0809- C8 INY 080A- D0 F7 BNE $0803 080C- 4C 0F 02 JMP $020F *20F<80F.8FFM *20FL ; set up nibble translation table 020F- A0 AB LDY #$AB 0211- 98 TYA 0212- 85 3C STA $3C 0214- 4A LSR 0215- 05 3C ORA $3C 0217- C9 FF CMP #$FF 0219- D0 09 BNE $0224 021B- C0 D5 CPY #$D5 021D- F0 05 BEQ $0224 021F- 8A TXA 0220- 99 00 08 STA $0800,Y 0223- E8 INX 0224- C8 INY 0225- D0 EA BNE $0211 ; $00 into zero page $26 and $03 into ; $27 means we're probably going to be ; loading data into $0300..$03FF soon. 0227- 84 3D STY $3D 0229- 84 26 STY $26 022B- A9 03 LDA #$03 022D- 85 27 STA $27 022F- A6 2B LDX $2B 0231- 20 5D 02 JSR $025D *25DL ; read a sector from track $00 (this is ; similar to the disk controller ROM ; routine at $C65C, but it looks for an ; address prologue of "D5 AA B5" and ; uses the 5-3 encoding translation ; table we set up earlier at $020F 025D- 18 CLC 025E- 08 PHP 025F- BD 8C C0 LDA $C08C,X 0262- 10 FB BPL $025F 0264- 49 D5 EOR #$D5 0266- D0 F7 BNE $025F 0268- BD 8C C0 LDA $C08C,X 026B- 10 FB BPL $0268 026D- C9 AA CMP #$AA 026F- D0 F3 BNE $0264 0271- EA NOP 0272- BD 8C C0 LDA $C08C,X 0275- 10 FB BPL $0272 0277- C9 B5 CMP #$B5 <-- ! 0279- F0 09 BEQ $0284 027B- 28 PLP 027C- 90 DF BCC $025D 027E- 49 AD EOR #$AD 0280- F0 1F BEQ $02A1 0282- D0 D9 BNE $025D ...[snipped]... ; store partially decoded data at $0300 ; (target address set at $0229) 02C1- 91 26 STA ($26),Y 02C3- C8 INY 02C4- D0 EF BNE $02B5 02C6- BC 8C C0 LDY $C08C,X 02C9- 10 FB BPL $02C6 02CB- 59 00 08 EOR $0800,Y 02CE- D0 8D BNE $025D 02D0- 60 RTS *234L ; finish decoding nibbles (not shown) 0234- 20 D1 02 JSR $02D1 ; Weird. This code is modifying the ; code it just read from disk. Not sure ; why yet. 0237- A9 A9 LDA #$A9 0239- 8D 1F 03 STA $031F 023C- A9 02 LDA #$02 023E- 8D 20 03 STA $0320 ; jump to the code we just read 0241- 4C 01 03 JMP $0301 At this point, AUTOTRACE interrupts the boot and captures the sector we just read into $0300. Let's see what that looks like. *BLOAD BOOT0 0300-03FF,A$300 *301L ; data table fiddling 0301- B9 00 08 LDA $0800,Y 0304- 0A ASL 0305- 0A ASL 0306- 0A ASL 0307- 99 00 08 STA $0800,Y 030A- C8 INY 030B- D0 F4 BNE $0301 ; slot number (x16) 030D- A6 2B LDX $2B ; looks like we're loading the next ; phase into $0900 030F- A9 09 LDA #$09 0311- 85 27 STA $27 ; this should be the address into which ; we're loading the next phase of the ; boot 0313- AD CC 03 LDA $03CC 0316- 85 41 STA $41 *3CC 03CC- 76 Fantastic. (By the way, this is why Passport refused to continue tracing and capture this disk's RWTS -- because it would load at $7600 and overwrite Passport itself! Passport leaves room at $3600 and $B600 for capturing a disk's RWTS; this disk uses neither.) 0318- 84 40 STY $40 031A- 8A TXA 031B- 4A LSR 031C- 4A LSR 031D- 4A LSR 031E- 4A LSR ; This is the instruction that got ; replaced at $023B. Looking at the ; previous few instructions (which ; are useless because this modified ; instruction blows away the contents ; of the accumulator), it appears that ; this code can be used to re-use the ; disk controller ROM routine to read ; sectors, but has been modified to use ; the routine at $025D instead. 031F- A9 02 LDA #$02 0321- 85 3F STA $3F 0323- A9 5D LDA #$5D 0325- 85 3E STA $3E ; read a sector via ($3E) (so, $025D) 0327- 20 43 03 JSR $0343 ; decode nibble data 032A- 20 46 03 JSR $0346 *346L 0346- A2 32 LDX #$32 0348- A0 00 LDY #$00 034A- BD 00 08 LDA $0800,X 034D- 4A LSR 034E- 4A LSR 034F- 4A LSR 0350- 85 3C STA $3C 0352- 4A LSR 0353- 85 2A STA $2A 0355- 4A LSR ; Ah, I see what's going on now. The ; routine at $025D stores raw data in ; the target page ($0900), but doesn't ; decode it. This routine now takes ; that data and decodes it (using the ; data table we set up at $0800), then ; it stores the final data in ($40). ; ($40) was initialized as $00/<$03CC>, ; which is $76. So this is storing data ; at $7600 and up. 0356- 1D 00 09 ORA $0900,X 0359- 91 40 STA ($40),Y 035B- C8 INY 035C- BD 33 08 LDA $0833,X 035F- 4A LSR 0360- 4A LSR 0361- 4A LSR 0362- 4A LSR 0363- 26 3C ROL $3C 0365- 4A LSR 0366- 26 2A ROL $2A 0368- 1D 33 09 ORA $0933,X 036B- 91 40 STA ($40),Y 036D- C8 INY 036E- BD 66 08 LDA $0866,X 0371- 4A LSR 0372- 4A LSR 0373- 4A LSR 0374- 4A LSR 0375- 26 3C ROL $3C 0377- 4A LSR 0378- 26 2A ROL $2A 037A- 1D 66 09 ORA $0966,X 037D- 91 40 STA ($40),Y 037F- C8 INY 0380- A5 2A LDA $2A 0382- 29 07 AND #$07 0384- 1D 99 09 ORA $0999,X 0387- 91 40 STA ($40),Y 0389- C8 INY 038A- A5 3C LDA $3C 038C- 29 07 AND #$07 038E- 1D CC 09 ORA $09CC,X 0391- 91 40 STA ($40),Y 0393- C8 INY 0394- CA DEX 0395- 10 B3 BPL $034A 0397- AD 99 08 LDA $0899 039A- 4A LSR 039B- 4A LSR 039C- 4A LSR 039D- 0D FF 09 ORA $09FF 03A0- 91 40 STA ($40),Y 03A2- A6 2B LDX $2B 03A4- 60 RTS Continuing from $032D... *32DL ; Zero page $3D is a counter for the ; number of sectors read already. $03FF ; is the total number of sectors. 032D- A5 3D LDA $3D 032F- 4D FF 03 EOR $03FF ; if we've read enough sectors, break ; out of the loop 0332- F0 06 BEQ $033A *3FF 03FF- 09 We're reading $0A sectors. (The comparison happens before the count is incremented, so add 1.) ; increment target page (starts at $76) 0334- E6 41 INC $41 ; increment sector count 0336- E6 3D INC $3D ; this is really an unconditional jump 0338- D0 ED BNE $0327 ; Break out of loop here (from $0332). ; Accumulator is always zero at this ; point. 033A- 85 3E STA $3E ; still $76 033C- AD CC 03 LDA $03CC 033F- 85 3F STA $3F ; now $77 0341- E6 3F INC $3F ; jump to $7700 to continue the boot 0343- 6C 3E 00 JMP ($003E) At this point, AUTOTRACE interrupts the boot again by changing the instruction at $033C to jump to a callback that captures the new code at $7600..$7FFF, stored on my work disk as BOOT1. ~ Chapter 2 In Which Everything Is Off By 16K *BLOAD BOOT1,A$7600 *7700L ; well, would you look at that -- we've ; managed to load a more-or-less normal ; DOS-shaped bootloader in low memory 7700- 8E E9 77 STX $77E9 7703- 8E F7 77 STX $77F7 7706- A9 01 LDA #$01 7708- 8D F8 77 STA $77F8 770B- 8D EA 77 STA $77EA 770E- AD E0 77 LDA $77E0 7711- 8D E1 77 STA $77E1 7714- A9 00 LDA #$00 7716- 8D EC 77 STA $77EC 7719- AD E2 77 LDA $77E2 771C- 8D ED 77 STA $77ED 771F- AD E3 77 LDA $77E3 7722- 8D F1 77 STA $77F1 *77E2 77E2- 0B This code starts reading at track $00 (hard-coded), sector $0B (from $77E2). *77E3 77E3- 5D Into $5D00. *77E0 77E0- 19 $19 sectors, so $5D00..$75FF. Basically a full copy of DOS, just below the RWTS and bootloader at $7600. ; pretty standard stuff to set up DOS ; pointers 7725- A9 01 LDA #$01 7727- 8D F4 77 STA $77F4 772A- 8A TXA 772B- 4A LSR 772C- 4A LSR 772D- 4A LSR 772E- 4A LSR 772F- AA TAX 7730- A9 00 LDA #$00 7732- 9D F8 04 STA $04F8,X 7735- 9D 78 04 STA $0478,X ; read DOS into memory 7738- 20 93 77 JSR $7793 ; standard machine initialization stuff 773B- A2 FF LDX #$FF 773D- 9A TXS 773E- 8E EB 77 STX $77EB 7741- 20 93 FE JSR $FE93 7744- 20 89 FE JSR $FE89 ; jump to DOS cold start entry point 7747- 4C 84 5D JMP $5D84 Okay so far. But remember, tracks 3+ use modified prologues, so at some point it's going to switch those. Also, the original disk does not ever show the DOS prompt (which would happen during the DOS cold boot at $5D84), so there may be further shenanigans ahead. Looking at the RWTS entry point at $7D00, an unusual JSR jumps out at me. *7D00L 7D00- 84 48 STY $48 7D02- 85 49 STA $49 7D04- A0 01 LDY #$01 7D06- B1 48 LDA ($48),Y 7D08- AA TAX 7D09- 20 A1 7E JSR $7EA1 <-- ! *7EA1L ; instruction that I expected at $7D09 7EA1- 8C F8 04 STY $04F8 ; look at track 7EA4- AD EC 77 LDA $77EC ; tracks 0-2 treated one way 7EA7- F0 0C BEQ $7EB5 7EA9- C9 01 CMP #$01 7EAB- F0 08 BEQ $7EB5 7EAD- C9 02 CMP #$02 7EAF- F0 04 BEQ $7EB5 ; higher tracks treated another way -- ; by changing the address and data ; prologues for both read and write 7EB1- A9 BB LDA #$BB 7EB3- D0 02 BNE $7EB7 7EB5- A9 AA LDA #$AA 7EB7- 8D 98 78 STA $7898 7EBA- 8D E3 78 STA $78E3 7EBD- 8D 12 79 STA $7912 7EC0- 8D 60 79 STA $7960 7EC3- 8D 80 79 STA $7980 7EC6- 8D BC 79 STA $79BC 7EC9- 8A TXA 7ECA- 60 RTS That explains how the disk is able to read itself, despite having different prologues for lower and higher tracks. On every sector read, it looks at the track and changes the RWTS code accordingly. One thing of note (because this is going to trip me up later): standard RWTS code is designed to be agnostic about where the RWTS parameter table is. It takes an address in A & Y and stores it in zero page $48/$49, then uses indirect addressing to find values within the table. But this custom code at $7EA1 does not do that; it assumes the RWTS parameter table is $77E8 and the track number is at $77EC. ~ Chapter 3 In Which We Attempt To Use The Disk As A Weapon Against Itself And It Goes Extraordinarily Well Right Up Until It Doesn't Before I can use Advanced Demuffin with this captured RWTS, I will create an IOB file. (See the docs on my work disk for more about IOB files.) Advanced Demuffin assumes the RWTS entry point is at $BD00, but this one is at $7D00. Thus: ; Most of this is identical to the ; standard IOB module that comes with ; Advanced Demuffin 1400- 4A LSR 1401- 8D 22 0F STA $0F22 1404- 8C 23 0F STY $0F23 1407- 8E 27 0F STX $0F27 140A- A9 01 LDA #$01 140C- 8D 20 0F STA $0F20 140F- 8D 2A 0F STA $0F2A ; One problem with having an RWTS at ; $7800..$7FFF is that that range is ; normally used to store track data ; during the copy process. If we just ; let Advanced Demuffin run, it will ; overwrite the custom RWTS almost ; immediately and crash. In the ; ADVANCED DEMUFFIN 1.5 DOCS (also ; included on my work disk), it ; mentions that you can control how ; many sectors Advanced Demuffin reads ; at a time, and where it puts it in ; memory. Normally $1CF0 is $20 and ; $1CF1 is $90, meaning that it will ; copy 7 tracks worth of data at a time ; into $2000..$8FFF. Changing the end ; parameter to $76 will copy fewer ; tracks at a time, but it has the ; distinct advantage of not overwriting ; the RWTS and crashing. 1412- A9 76 LDA #$76 1414- 8D F1 1C STA $1CF1 ; The next problem, specific to this ; disk and its custom RWTS swapper at ; $7EA1, is that it assumes the RWTS ; parameter table is at $77E8. Since ; that is not the case, we get to copy ; Advanced Demuffin's RWTS parameter ; table into the place that the RWTS ; swapper expects it. Otherwise it ; will incorrectly determine which ; track we're reading and set the ; prologue bytes to the wrong values. 1417- A0 10 LDY #$10 1419- B9 1E 0F LDA $0F1E,Y 141C- 99 E8 77 STA $77E8,Y 141F- 88 DEY 1420- 10 F7 BPL $1419 ; Finally, get the address of the RWTS ; parameter table at $0F1E and call the ; RWTS entry point at $7D00 (instead of ; the usual $BD00) 1422- A9 0F LDA #$0F 1424- A0 1E LDY #$1E 1426- 4C 00 7D JMP $7D00 *BSAVE IOB,A$1400,L$FB Now I can use Advanced Demuffin to convert the disk to a standard format. It uses the disk's own RWTS to read the original (stored in the RWTS file, accessed via the IOB module), then a standard DOS 3.3-compatible RWTS to write out the data, sector by sector. [S6,D1=original disk] [S6,D2=blank disk] [S5,D1=my work disk] ]BRUN ADVANCED DEMUFFIN 1.5 [press "5" to switch to slot 5] [press "R" to load a new RWTS module] --> At $78, load "RWTS" from drive 1 [press "I" to load a new IOB module] --> load "IOB" from drive 1 [press "6" to switch to slot 6] [press "C" to convert disk] [press "Y" to change default values] --v-- ADVANCED DEMUFFIN 1.5 (C) 1983, 2014 ORIGINAL BY THE STACK UPDATES BY 4AM ======================================= INPUT ALL VALUES IN HEX SECTORS PER TRACK? (13/16) 13 <--change START TRACK: $00 START SECTOR: $00 END TRACK: $22 END SECTOR: $0C INCREMENT: 1 MAX # OF RETRIES: 0 COPY FROM DRIVE 1 TO DRIVE: 2 ======================================= 13SC $00,$00-$22,$0C BY$01 S6,D1->S6,D2 --^-- [press RETURN to start copy] --v-- ADVANCED DEMUFFIN 1.5 (C) 1983, 2014 ORIGINAL BY THE STACK UPDATES BY 4AM =======PRESS ANY KEY TO CONTINUE======= TRK:R.................................. +.5: 0123456789ABCDEF0123456789ABCDEF012 SC0:................................... SC1:................................... SC2:................................... SC3:................................... SC4:................................... SC5:................................... SC6:................................... SC7:................................... SC8:................................... SC9:................................... SCA:R.................................. SCB:................................... SCC:................................... ======================================= 13SC $00,$00-$22,$0C BY$01 S6,D1->S6,D2 --^-- The read error on T00,S0A is expected; this is where it stores a copy of the 16-sector boot sector on the otherwise 13-sector disk. Of course the demuffin'd disk is not bootable, since its bootloader is still expecting 5-and-3 encoded nibbles with custom prologues. We'll deal with that in a moment. But look, a disk catalog: ]PR#5 ... ]CATALOG,S6,D2 ]C C1983 DSR^C#254 136 FREE A 002 MENU I 057 APPLESOFT B 009 A B 009 C B 009 B B 009 D B 010 E B 010 F B 009 G B 009 H B 014 Z B 015 Y B 015 X B 015 W B 017 V B 016 U B 016 T B 016 S B 010 I ]RUN MENU --v-- COPYRIGHT 1978 BY GEORGE EARL 1302 S. GEN. MCMULLEN, SAN ANTONIO,TEXAS ALL RIGHTS RESERVED -- AS A COMPETENT PROGRAMMER, YOU SHOULD TRY TO RESPECT OTHER PROGRAMMER'S RIGHTS. --^-- Oh. ~ Chapter 4 In Which We Pretend To Be A Competent Programmer And It Goes Extraordinarily Well Right Up Until It Doesn't It appears that copying the files is not going to be sufficient, which means I get to trace the boot further to determine how it's loading the real program. Returning to my demuffin'd disk, I have a disk with a 13-sector RWTS and a custom RWTS swapper, none of which is useful anymore. I would like to replace it with a standard DOS 3.3 bootloader located at the same memory address. This is more difficult than it sounds. The bootloader loads at $7600..$7FFF, which is the top of memory for a 32K Apple ][. Aha! The DOS 3.3 master disk will load at $3600 then relocate itself to the top of memory. On a 32K machine, the top of memory would be $7FFF. Except... I don't actually own a 32K Apple ][. Looking around my office, I can see - Apple IIgs (min. 256K) - Pravetz 8C (min. 128K) - Apple //e (min. 64K) - Apple ][+ (min. 48K) Ultimately, I discovered that the Virtual ][ emulator can emulate an original Apple ][ with a selectable amount of memory, including 32K. So I set that up, booted the DOS 3.3 master disk, captured the relocated RWTS, and copied it to track 0 on my crack-in- progress. [S6,D1=DOS 3.3 master disk] [S5,D1=my work disk] ]PR#6 ... LOADING APPLESOFT BASIC INTO MEMORY (this is not relevant to the crack, but I have only ever seen this message once before in 40 years of using the Apple ][, and it was while cracking another George Earl disk) >FP [S6,D1=blank disk] ]INIT HELLO ...write write write... Turning to my trusty Disk Fixer sector editor, I copied sectors 0 and 2-9 of track 0 from this newly initialized DOS 3.3 disk to my crack-in-progress. Sector 1 has the code to load DOS from the first 13 sectors of each track, which is still where it is even though this is now a 16-sector disk. Now... will it be enough? [S6,D1=crack-in-progress] ]PR#6 ...boots, loads DOS, hangs... Sigh. ~ Chapter 5 In Which Competence Is Relative It sounds like my crack-in-progress is successfully loading DOS. To quickly verify that, I changed the "JMP $5D84" on sector 1 to "JMP $FF59". Sure enough, it breaks to the monitor. That means the RWTS is in place, and it's working, and it's loading DOS into the right place ($5D00+). So there is further protection code beyond that. *5D84L 5D84- AD E9 77 LDA $77E9 5D87- 4A LSR 5D88- 4A LSR 5D89- 4A LSR 5D8A- 4A LSR 5D8B- 8D 6A 6A STA $6A6A 5D8E- AD EA 77 LDA $77EA 5D91- 8D 68 6A STA $6A68 5D94- 20 34 63 JSR $6334 That is not normal. Let's see what's at $6334. *6334L 6334- 20 B1 7E JSR $7EB1 Uh oh. That's inside the RWTS. But we just replaced the RWTS... oh dear. The original disk has custom code inside the RWTS we just replaced. Rebooting my work disk, I can check the RWTS I originally captured to see what's at $7EB1. ]PR#5 ... ]BLOAD RWTS,A$7800 ]CALL -151 *7EB1L 7EB1- A9 BB LDA #$BB 7EB3- D0 02 BNE $7EB7 7EB5- A9 AA LDA #$AA 7EB7- 8D 98 78 STA $7898 7EBA- 8D E3 78 STA $78E3 7EBD- 8D 12 79 STA $7912 7EC0- 8D 60 79 STA $7960 7EC3- 8D 80 79 STA $7980 7EC6- 8D BC 79 STA $79BC 7EC9- 8A TXA 7ECA- 60 RTS Ah, this is the RWTS swapper that I discovered earlier. Not sure why we're calling it again though. ]PR#6 ...still breaks into monitor with DOS in memory... ]CALL -151 Continuing from $6337... ; copy something into place (more on ; this in a moment) 6337- A2 2C LDX #$2C 6339- BD 13 64 LDA $6413,X 633C- 9D A0 7E STA $7EA0,X 633F- CA DEX 6340- D0 F7 BNE $6339 ; restore code at $7D09 that previously ; called the RWTS swapper 6342- A9 8C LDA #$8C 6344- 8D 09 7D STA $7D09 6347- A9 F8 LDA #$F8 6349- 8D 0A 7D STA $7D0A ; also restore the code at $5D94 that ; called this routine 634C- 8D 95 5D STA $5D95 634F- A9 04 LDA #$04 6351- 8D 0B 7D STA $7D0B 6354- A9 AD LDA #$AD 6356- 8D 94 5D STA $5D94 And this is why we called the RWTS swapper one last time -- to make the changes permanent, then reuse the space in memory and remove the call to the RWTS swapper on every sector read. So what's at $7EA0 now? *6413L [...omitted for brevity, but it appears to be the actual 13-sector RWTS code that was supposed to be at $7EA0 in the first place...] To sum up, this subroutine does two things, neither of which are desirable: 1. call the RWTS swapper one last time (don't want that) 2. replace part of the RWTS with the "original" RWTS code, but of a 13- sector RWTS, not a 16-sector RWTS (don't want that either) Continuing from $6359, we have this: 6359- 20 BC 63 JSR $63BC 635C- 60 RTS *63BCL 63BC- 20 58 FC JSR $FC58 63BF- A2 15 LDX #$15 63C1- BD A2 63 LDA $63A2,X 63C4- 9D 30 04 STA $0430,X 63C7- CA DEX 63C8- D0 F7 BNE $63C1 63CA- A2 0D LDX #$0D 63CC- BD D5 63 LDA $63D5,X 63CF- 9D 34 07 STA $0734,X 63D2- CA DEX 63D3- D0 F7 BNE $63CC 63D5- 60 RTS *63BCG [...prints "THE SPANISH HANGMAN" loading screen...] OK, that part is actually useful. I can change the JSR at $5D94 to call that directly and skip over the other stuff. T00,S0B,$95: 34 -> BC (I also changed the JMP $FF59 for debugging back to JMP $5D84.) ]PR#6 ...boots, prints title screen, hangs... Progress! ~ Chapter 6 In Which All That Glitters Is Not Gold, All That Prints Is Not Specious, And Everything Is Actually Fine In The End Continuing from the first instruction after the JSR $63BC (which prints the text title screen)... ; normal 5D97- A9 00 LDA #$00 5D99- D0 11 BNE $5DAC 5D9B- 8D B6 6A STA $6AB6 5D9E- A2 0A LDX #$0A 5DA0- BD 61 5D LDA $5D61,X 5DA3- 9D 55 5D STA $5D55,X 5DA6- CA DEX 5DA7- D0 F7 BNE $5DA0 5DA9- 4C BC 5D JMP $5DBC ... ; normal 5DBC- 38 SEC 5DBD- B0 12 BCS $5DD1 ... ; normal 5DD1- 08 PHP ; hook I/O vectors at ($36) and ($38) ; (normal) 5DD2- 20 51 68 JSR $6851 5DD5- A9 00 LDA #$00 5DD7- 8D 5E 6A STA $6A5E 5DDA- 8D 52 6A STA $6A52 5DDD- 28 PLP 5DDE- 6A ROR 5DDF- 8D 51 6A STA $6A51 ; mormal (always branches) 5DE2- 30 03 BMI $5DE7 ... 5DE7- 6C 5C 5D JMP ($5D5C) $5D5C/$5D5D were just overwritten in the copy loop at $5D9E, with the values at $5D68/$5D69. *5D68.5D69 5D68- 81 62 *6281L ; pop return value (so we're not ; returning to the normal bootloader) 6281- 68 PLA 6282- 68 PLA ; overwrite all of zero page with ; values that (I am not making this up) ; were stored in the original RWTS 6283- A2 00 LDX #$00 6285- BD D0 7E LDA $7ED0,X 6288- 95 00 STA $00,X 628A- E8 INX 628B- D0 F8 BNE $6285 ; print a space, wait what? 628D- A9 A0 LDA #$A0 628F- 20 ED FD JSR $FDED Let's back up. We're overwriting the entire zero page, which means we're also overwriting the I/O vectors that $FDED routes through. I always think about $FDED as "the print function," but it's literally just JMP ($0036). $7ED0 + $0036 = $7F06. Let's see where that JMP $FDED is really going. ]PR#5 ... ]BLOAD RWTS,A$7800 ]CALL -151 *7F06.7F07 7F06- 81 5E Oh! This is actually fine, like non- sarcastically fine. $5E81 is the DOS keyboard intercept routine. On an unprotected DOS 3.3 disk, it would be called later by way of the I/O vectors being hooked and catching the printing of "]" and the subsequent attempt to read the keyboard after initializing BASIC. (This part of the DOS boot is extremely weird because of the way BASIC is initialized. It all seems super suspicious but is actually not.) The strange part is that, instead of being called via the input vector, this disk is setting it as the output vector (by way of overwriting the entire zero page, of which more in a moment) and "printing" a space character. But the end result is the same: the boot continues as normal. The only thing to actually worry about here is how to overwrite zero page. I have an idea for that: there is a scratch buffer (normally at $BB00, here at $7B00) that is read from track 0, sector 5, then overwritten during subsequent RWTS calls. This scratch buffer is $156 bytes long, which is more than enough for our purposes. Using my trusty sector editor, I copied the buffer that starts at T00,S08,$D0 on the original disk, into T00,S05,$00 of my crack-in-progress. It looks like this (in part): --v-- -------------- DISK EDIT -------------- TRACK $00/SECTOR $05/VOLUME $FE/BYTE$00 --------------------------------------- $00:>40<00 00 00 00 00 00 00 @@@@@@@@ $08: 00 00 00 00 00 00 00 00 @@@@@@@@ $10: 00 00 00 00 00 00 00 E3 @@@@@@@c $18: 40 04 44 00 00 00 00 00 @DD@@@@@ $20: 00 28 00 18 01 17 D0 05 @(@XAWPE $28: D0 07 D0 07 00 00 80 02 PGPG@@.B $30: 00 00 FF BE 05 00 81 5E @@.>E@.^ $38: BD 5E 40 02 40 02 40 02 =^@B@B@B $40: 40 02 A2 5B 82 58 EF D8 @B"[.XoX $48: 00 77 00 08 00 C0 51 BE @7@H@@Q> $50: 65 8C 09 C3 CF D0 D9 D2 %.ICOPYR $58: C9 C7 C8 D4 A0 B1 B9 B7 IGHT 197 $60: B8 A0 C2 D9 A0 C7 C5 60 8 BY GE $68: 62 00 60 54 01 B2 0D 94 "@ TA2M. $70: EC 07 ED ED EC EC ED ED lGmmllmm $78: 62 8C 0D 0D 0D 0D 0D 0D ".MMMMMM ... --^-- Oh cute, a copyright message from the developer (George Earl). Apparently this bootloader dates back to 1978. I wonder how many times he reused it. Anyway, the final task is to copy this from the scratch buffer to zero page. I want to copy it at the same time as the original disk (so nothing in zero page gets overwritten while loading DOS), but the act of loading DOS will overwrite my scratch buffer. Argh. So we will do it in two stages. Stage 1: immediately after loading the RWTS (and scratch buffer), copy the scratch buffer to $0800. --v-- T00,S00,$B3 (loaded at $76B3): ----------- DISASSEMBLY MODE ---------- ; note: this routine must preserve X ; because $7700 assumes it contains ; the boot slot x16 ; A & Y can be clobbered, no one cares 00B3:A0 00 LDY #$00 00B5:B9 00 7B LDA $7B00,Y 00B8:99 00 08 STA $0800,Y 00BB:C8 INY 00BC:D0 F7 BNE $00B5 00BE:4C 00 77 JMP $7700 --^-- Now to call this routine instead of jumping to $7700: T00,S00,$4A: 6C FD 08 -> 4C B3 76 Now to use the buffer at $0800 instead of $7ED0, later in the boot. That copy loop was at $6283, which is loaded from track 0, sector 3. T01,S03,$86: D0 7E -> 00 08 And that's it. I can leave the wacky "print a space" logic -- in fact, I must, because that's how we continue the boot after copying zero page. ]PR#6 ...boots and loads and it is glorous... After extensive testing, there does not appear to be any further protection. Quod erat liberandum. --------------------------------------- A 4am crack No. 2768 ------------------EOF------------------