-------------House-a-Fire!------------- A 4am crack 2014-09-23 --------------------------------------- "House-a-Fire!" is a 1988 educational program distributed by The Ellen Nelson Learning Library, a division of Decision Development Co. COPYA fails miserably and immediately. Locksmith Fast Disk Backup fares no better. EDD 4 bit copy gives no read errors, but the copy it creates just reboots endlessly. Turning to my trusty Disk Fixer sector editor, I go to "Input/Output Control" (press "O") and set CHECKSUM ENABLED= NO. (This ignores address and data epilogue bytes.) Now I can read every sector on every track. From a brief manual inspection, I'd say the RWTS on track 0 strongly resembles a DOS 3.3 RWTS. However, there is no VTOC (disk catalog) on track $11, and the original disk does not sound like it is loading a DOS from tracks 0-2 on boot. Given the (relatively) weak structural protection, I used to turn to the DOS 3.3 master disk, patch the RWTS to ignore checksums and epilogue bytes (changing $B942 from "SEC" to "CLC"), and run COPYA. Then, one fine day, and completely by accident, I came across an original disk with a bad sector. I suppose this shouldn't surprise me. These floppies are decades old by now; it's amazing any of them work at all. The point is, I shouldn't be using tools that ignore potentially serious read errors. There are other tools, like Super Demuffin, that can convert a disk like this (with non-standard epilogue bytes) into a standard format. It requires figuring out what the actual epilogue bytes are, but it has the advantage of surfacing a read error if the original disk actually has a read error. So... no more COPYA+B942:18 patch. From now on, it's Super Demuffin or Advanced Demuffin to convert disks to a standard format. I should be able to use my AUTOTRACE program to extract the RWTS from the original disk, then load that into Advanced Demuffin to convert it to a standard format. Time for boot tracing with AUTOTRACE. [S6,D1=original disk, side A] [S5,D1=my work disk] ]PR#5 ... CAPTURING BOOT0 ...reboots slot 6... ...reboots slot 5... SAVING BOOT0 CAPTURING BOOT1 ...reboots slot 6... ...reboots slot 5... SAVING BOOT1 SAVING RWTS /!\ NIBBLE CHECK AT $BB00 Well, that worked flawlessly. My work disk first runs AUTOTRACE0, which sets up a minimal boot trace to capture the first sector of track 0 and saves it to the file "BOOT0" (on my work disk, not the original disk). If the first sector looks reasonably normal, it runs AUTOTRACE1, which sets up a more involved boot trace to capture the rest of track 0 and save it to the file "BOOT1". If *that* looks reasonably normal, it saves the RWTS to a file, unimaginatively named "RWTS", which can be loaded with Advanced Demuffin to copy each track of the original disk to a duplicate with standard address/data prologue/epilogue sequences. Furthermore, my AUTOTRACE program detected a possible nibble check at $BB00. This is a relatively new feature that I added quite recently. I'm glad to see it's already paying dividends. (It scans the $BB00 page for the three-byte sequence "BD E9 C0", which is the instruction "LDA $C0E9,X", which turns on the drive motor manually. Even though there are tons of other code variations that would accomplish the same thing, 99% of the nibble checks I've ever seen have started with that exact instruction.) I'll come back to the nibble check. First, I want to take the RWTS I extracted and load it into Advanced Demuffin to convert this disk to a standard format. [S6,D1=original disk, side A] [S6,D2=blank disk] [S5,D1=my work disk] ]PR#5 ... ]BRUN ADVANCED DEMUFFIN 1.5 [press "5" to switch to slot 5] [press "R" to load a new RWTS module] --> At $B8, load "RWTS" from drive 1 [press "6" to switch to slot 6] [press "C" to convert disk] This disk is 16 sectors, and the default options (copy the entire disk, all tracks, all sectors) don't need to be changed unless something goes horribly wrong. --v-- ADVANCED DEMUFFIN 1.5 (C) 1983, 2014 ORIGINAL BY THE STACK UPDATES BY 4AM =======PRESS ANY KEY TO CONTINUE======= TRK:................................... +.5: 0123456789ABCDEF0123456789ABCDEF012 SC0:................................... SC1:................................... SC2:................................... SC3:................................... SC4:................................... SC5:................................... SC6:................................... SC7:................................... SC8:................................... SC9:................................... SCA:................................... SCB:................................... SCC:................................... SCD:................................... SCE:................................... SCF:................................... ======================================= 16SC $00,$00-$22,$0F BY1.0 S6,D1->S6,D2 --^-- The disk's own RWTS gave no read errors on any track. (I repeated the procedure for side B with the same RWTS; it also converted with no errors.) This is the power and the genius of Advanced Demuffin. Every disk must be able to read itself. So, let it read itself, then capture the data and write it out in a standard format. However, booting the newly demuffin'd copy shows the same behavior as my failed bit copy: an endless loop of rebooting very early in the boot process. Let's go find that nibble check. ]PR#5 ... ]BLOAD BOOT1,A$2600 ]CALL -151 *FE89G FE93G B600<2600.2FFFM *B700L B700- 20 1A BC JSR $BC1A <-- ! B703- 8E F7 B7 STX $B7F7 B706- A9 01 LDA #$01 B708- 8D F8 B7 STA $B7F8 B70B- 8D EA B7 STA $B7EA B70E- AD E0 B7 LDA $B7E0 B711- 8D E1 B7 STA $B7E1 B714- A9 08 LDA #$08 B716- 8D EC B7 STA $B7EC B719- A9 0D LDA #$0D B71B- 8D ED B7 STA $B7ED Most of that looks relatively normal. I mean, it's reading from track $08, sector $0D, which is unusual, but it appears to be setting up a standard RWTS parameter table to do so. That first line is definitely unusual, though. That jumped out to me right away. *BC1AL ; this is the instruction I was ; expecting at $B700 (but it called ; this subroutine instead) BC1A- 8E E9 B7 STX $B7E9 ; OK BC1D- A9 00 LDA #$00 BC1F- 8D 78 04 STA $0478 BC22- 48 PHA BC23- A6 2B LDX $2B ; seek to track $00 (we're already ; there, but whatever) BC25- A9 00 LDA #$00 BC27- 20 A0 B9 JSR $B9A0 ; hello BC2A- 20 03 BB JSR $BB03 ; now clean up and restore everything BC2D- 68 PLA BC2E- 08 PHP BC2F- A6 2B LDX $2B BC31- 20 A0 B9 JSR $B9A0 BC34- 28 PLP BC35- 60 RTS Anything in the $BB00 range is automatically suspicious, because in any relatively normal RWTS, that entire page of memory gets overwritten with every disk read during denibblization. (That is totally not a real word.) *BB03L ; save some zero page state BB03- A2 00 LDX #$00 BB05- B5 00 LDA $00,X BB07- 9D 00 50 STA $5000,X BB0A- CA DEX BB0B- 10 F8 BPL $BB05 ; onward BB0D- 20 00 BC JSR $BC00 *BC00L ; set reset vector to reboot BC00- A5 2B LDA $2B BC02- 4A LSR BC03- 4A LSR BC04- 4A LSR BC05- 4A LSR BC06- 09 C0 ORA #$C0 BC08- 8D F3 03 STA $03F3 BC0B- 49 A5 EOR #$A5 BC0D- 8D F4 03 STA $03F4 BC10- A9 00 LDA #$00 BC12- 8D F2 03 STA $03F2 ; and continue elsewhere BC15- A6 2B LDX $2B BC17- 4C 30 BB JMP $BB30 *BB30L ; turn on the drive motor manually: ; always a sign of impending evil bits ; (see? it's always exactly "BD 89 C0") BB30- A6 2B LDX $2B BB32- BD 89 C0 LDA $C089,X BB35- BD 8E C0 LDA $C08E,X ; set up some pointers and counters BB38- A9 AE LDA #$AE BB3A- 85 1E STA $1E BB3C- A9 BB LDA #$BB BB3E- 85 1F STA $1F BB40- A9 0A LDA #$0A BB42- 85 09 STA $09 BB44- A9 80 LDA #$80 BB46- 85 08 STA $08 BB48- C6 08 DEC $08 ; if zero page $08 reaches 0, fail BB4A- F0 5C BEQ $BBA8 ; read next available address field BB4C- 20 44 B9 JSR $B944 ; if that failed, give up BB4F- B0 57 BCS $BBA8 ; check which sector we got BB51- A5 2D LDA $2D BB53- C9 0A CMP #$0A ; if we found the wrong sector, fail BB55- D0 F1 BNE $BB48 BB57- A0 00 LDY #$00 BB59- BD 8C C0 LDA $C08C,X BB5C- 10 FB BPL $BB59 BB5E- 88 DEY ; fail BB5F- F0 47 BEQ $BBA8 BB61- C9 D5 CMP #$D5 BB63- D0 F4 BNE $BB59 ; Search for a specific sequence of ; nibbles in the "dead zone" between ; the address field and data field. ; This area is normally not important, ; so COPYA didn't copy it precisely ; because normal disks don't care. ; (Actually, it's even more evil than ; that, because the original disk is ; written with timing bits in specific ; non-standard places between the ; nibbles in the dead zone. This code ; not only requires the right nibbles ; in the right order, it reads them ; just slightly slower than normal. So ; the timing bits need to be in the ; right places too, or else this code ; will read the wrong nibble values ; while it's out of sync. This will ; trip up even the best bit copiers. ; And you can forget about making a ; disk image for emulators -- those ; don't store timing bits at all.) BB65- A0 00 LDY #$00 BB67- BD 8C C0 LDA $C08C,X BB6A- 10 FB BPL $BB67 BB6C- 88 DEY ; fail BB6D- F0 39 BEQ $BBA8 BB6F- C9 E7 CMP #$E7 BB71- D0 F4 BNE $BB67 BB73- BD 8C C0 LDA $C08C,X BB76- 10 FB BPL $BB73 BB78- C9 E7 CMP #$E7 ; fail BB7A- D0 2C BNE $BBA8 BB7C- BD 8C C0 LDA $C08C,X BB7F- 10 FB BPL $BB7C BB81- C9 E7 CMP #$E7 ; fail BB83- D0 23 BNE $BBA8 ; kill some time to get out of sync ; with the "proper" start of nibbles) BB85- BD 8D C0 LDA $C08D,X BB88- A0 10 LDY #$10 BB8A- 24 FF BIT $FF ; now start looking for nibbles that ; don't really exist (except they do, ; because we're out of sync and reading ; timing bits as data) BB8C- BD 8C C0 LDA $C08C,X BB8F- 10 FB BPL $BB8C BB91- 88 DEY ; fail BB92- F0 14 BEQ $BBA8 BB94- C9 EE CMP #$EE BB96- D0 F4 BNE $BB8C ; check for nibble sequence stored ; in reverse order at $BBAE BB98- A0 07 LDY #$07 BB9A- BD 8C C0 LDA $C08C,X BB9D- 10 FB BPL $BB9A BB9F- D1 1E CMP ($1E),Y ; fail BBA1- D0 05 BNE $BBA8 BBA3- 88 DEY BBA4- 10 F4 BPL $BB9A BBA6- 18 CLC BBA7- 60 RTS ; all failures end up here -- try a few ; more times then give up completely BBA8- C6 09 DEC $09 BBAA- D0 98 BNE $BB44 ; out of chances, set carry and exit BBAC- 38 SEC BBAD- 60 RTS OK, so it's a nibble check that clears the carry if it works and sets the carry if it fails. Unwinding the calling stack, we left off here: BB0D- 20 00 BC JSR $BC00 ; save flags BB10- 08 PHP ; restore zero page state BB11- A2 00 LDX #$00 BB13- B9 00 50 LDA $5000,Y BB16- 95 00 STA $00,X BB18- CA DEX BB19- 10 F8 BPL $BB13 ; restore flags BB1B- 28 PLP ; if nibble check passed, continue BB1C- 90 0F BCC $BB2D ; if nibble check failed, push the ; $Cx00 address onto the stack and ; "return" to it (i.e. reboot) BB1E- A5 2B LDA $2B BB20- 4A LSR BB21- 4A LSR BB22- 4A LSR BB23- 4A LSR BB24- 09 C0 ORA #$C0 BB26- A8 TAY BB27- 88 DEY BB28- 98 TYA BB29- 48 PHA BB2A- A9 FF LDA #$FF BB2C- 48 PHA ; success path branches to here BB2D- A6 2B LDX $2B BB2F- 60 RTS Which is exactly the behavior I saw: endless rebooting. I can simply bypass this entire routine without adverse consequences. Unwinding the stack even further, this all started at $B700, which called $BC1A, which stored the X register in $B7E9 (useful) then did a nibble check (not useful). If I restore that original instruction at $B700, it should bypass all the protection-related code and continue with the normal boot. T00,S01,$00 change "20 1A BC" to "8E E9 B7" Success! The disk boots and loads its title screens and continues to the main menu. All options appear to work. The original disk's RWTS is liberal enough that it doesn't require any changes to read the unprotected copy. Quod erat liberandum. --------------------------------------- A 4am crack No. 144 ------------------EOF------------------