-------------Outdoor Safety------------ A 4am crack 2014-09-01 --------------------------------------- "Bingo Bugglebee Presents Outdoor Safety" is a 1985 educational game by Quest Learning Systems, Inc. [The copy protection is identical to "Home Alone," also distributed by Quest Learning Systems. This write-up is therefore quite similar to that one, with a few corrections.] COPYA fails miserably and immediately. EDD 4 bit copy gives no errors, but the copy just hangs on boot. Turning to my trusty Copy ][+ sector editor, I press "P" to get to the Sector Editor Patcher, and select "DOS 3.3 PATCHED". This option ignores checksum bytes and epilogue sequences -- as long as the address and data prologue are standard ("D5 AA 96" and "D5 AA AD", respectively), this will allow me to read each sector. With this option, I can read the data from every sector on track 0. But everything beyond that is still a black box. Time for boot tracing with AUTOTRACE. [S6,D1=original disk] [S5,D1=my work disk] ]PR#5 CAPTURING BOOT0 ...reboots slot 6... ...reboots slot 5... SAVING BOOT0 For those of you just tuning in, my work disk uses a custom program that I affectionately call "AUTOTRACE" to automate the process of boot tracing as far as possible. For some disks (like this one, apparently), it just captures track 0, sector 0 (saved in a file called "BOOT0") and stops. For other disks that load in the same way that an unprotected DOS 3.3 disk loads, it captures the next stage of the boot process as well (in a file called "BOOT1"). BOOT1 contains sectors 0-9 on track 0, which are loaded into memory at $B600..$BFFF. This generally contains the RWTS routines which the program uses to read the rest of the disk. If the RWTS is fairly normal as well (and my AUTOTRACE program just spot- checks a few memory locations to guess at its "normalcy"), AUTOTRACE extracts the RWTS routines (generally loaded from track 0, sectors 2-9 into $B800.. $BFFF) and saves *that* into a third file called "RWTS". There's a good chance I'll be able to load that "RWTS" file into a tool called Advanced Demuffin (written in 1983 by The Stack and updated by yours truly in 2014) to convert the disk into a standard disk readable by unprotected DOS 3.3 disks or any other third-party tools. That's the goal here: to extract the disk's RWTS and use it as a weapon against itself. Every disk, no matter how it's protected, needs to read itself. In this case, I'm still a few steps away from that, because my AUTOTRACE program stopped after capturing T00,S00. Now I need to look at that code and figure out why. ]CALL -151 *800<2800.28FFM *801L . . all normal, until... . 084A- 4C C0 B6 JMP $B6F0 A little something extra before the boot1 code. I don't like extra. Extra is bad. In a normal DOS 3.3 disk, the code on T00,S00 is actually loaded twice: once at $0800 and then again at $B600, where it remains in memory until you reboot or do something to intentionally wipe it out. So I can see what's going to be at $B6F0 by looking at $08F0. *8F0L ; odd 08F0- A9 AA LDA #$AA 08F2- 85 31 STA $31 ; odd x2 08F4- A9 AD LDA #$AD 08F6- 85 4E STA $4E ; suspicious (since this code is loaded at $B600, this will overwrite the $AA byte in the LDA instruction above) 08F8- 8D F1 B6 STA $B6F1 ; continue with boot1 08FB- 4C 00 B7 JMP $B700 This code is important, but it's not obvious why unless you've seen the technique before. Which I think I have. Let me see if I'm right. First, I'll need to let my AUTOTRACE program capture the rest of boot1. ]RUN 130 ...reboots slot 6... ...reboots slot 5... SAVING BOOT1 ]BLOAD BOOT1,A$2600 ]CALL -151 *FE89G FE93G ; disconnect DOS *B600<2600.2FFFM ; move RWTS into place *B92FL B92F- 0A ASL B930- 0A ASL B931- 0A ASL B932- 0A ASL B933- 0A ASL B934- 0A ASL B935- 0A ASL B936- 0A ASL B937- 0A ASL B938- 0A ASL B939- 0A ASL B93A- 0A ASL B93B- 0A ASL B93C- 0A ASL B93D- 0A ASL B93E- 0A ASL B93F- 0A ASL B940- 0A ASL B941- 0A ASL B942- 0A ASL Umm, that was not what I was expecting. At all. That's supposed to be the part of the RWTS code that checks for the address epilogue bytes ("DE AA"). But that's not even real code. I mean, technically it wouldn't crash, but what I was trying to show is that the RWTS uses those magic zero page values that are initialized at $B6F0. It probably does still do that, but I can't show it yet because this RWTS is... special. Let's back up. Boot1 starts at $B700. What's at $B700? *B700L B700- A0 1A LDY #$1A B702- B9 00 B7 LDA $B700,Y B705- 49 0A EOR #$0A B707- 99 00 B7 STA $B700,Y B70A- C8 INY B70B- D0 F5 BNE $B702 B70D- EE 04 B7 INC $B704 B710- EE 09 B7 INC $B709 B713- AD 09 B7 LDA $B709 B716- C9 C0 CMP #$C0 B718- D0 E8 BNE $B702 Well, that explains it. The first thing boot1 does is decrypt itself (well, everything after the first $1A bytes, which is how long the decryption loop is). Unfortunately, there's no room to insert a break after the decryption loop. Fortunately, I don't have to, because the decryption is simple enough that I can replicate it outside of the original boot process. *C500G ; because I overwrote DOS ... ]RENAME BOOT1,BOOT1 ENCRYPTED ]BLOAD BOOT1 ENCRYPTED,A$2600 ]CALL -151 0300- A0 1A LDY #$1A 0302- B9 00 27 LDA $2700,Y 0305- 49 0A EOR #$0A 0307- 99 00 27 STA $2700,Y 030A- C8 INY 030B- D0 F5 BNE $0302 030D- EE 04 03 INC $0304 0310- EE 09 03 INC $0309 0313- AD 09 03 LDA $0309 0316- C9 30 CMP #$30 0318- D0 E8 BNE $0302 031A- 60 RTS *BSAVE DECRYPT BOOT1,A$300,L$1B *300G *BSAVE BOOT1 DECRYPTED,A$2600,L$A00 *FE89G FE93G ; disconnect DOS *B600<2600.2FFFM ; move RWTS into place *B92FL B92F- 00 BRK B930- 00 BRK B931- 00 BRK B932- 00 BRK B933- 00 BRK B934- 00 BRK B935- 00 BRK This is not going well. Let's back up again. The decrypted code starts at $B71A. What's at $B71A? *B71AL ; RWTS parameter table setup (normal) B71A- 8E E9 B7 STX $B7E9 B71D- 8E F7 B7 STX $B7F7 ; unfriendly reset vector B720- A9 6B LDA #$6B B722- 8D F2 03 STA $03F2 B725- A9 B7 LDA #$B7 B727- 8D F3 03 STA $03F3 B72A- 49 A5 EOR #$A5 B72C- 8D F4 03 STA $03F4 B72F- EA NOP ; more RWTS parameters (normal) B730- A9 01 LDA #$01 B732- 8D F8 B7 STA $B7F8 B735- 8D EA B7 STA $B7EA B738- AD E0 B7 LDA $B7E0 B73B- 8D E1 B7 STA $B7E1 B73E- A9 02 LDA #$02 B740- 8D EC B7 STA $B7EC B743- A9 04 LDA #$04 B745- 8D ED B7 STA $B7ED B748- AC E7 B7 LDY $B7E7 B74B- 88 DEY B74C- 8C F1 B7 STY $B7F1 B74F- A9 01 LDA #$01 B751- 8D F4 B7 STA $B7F4 B754- 8A TXA B755- 4A LSR B756- 4A LSR B757- 4A LSR B758- 4A LSR B759- AA TAX B75A- A9 00 LDA #$00 B75C- 9D F8 04 STA $04F8,X B75F- 9D 78 04 STA $0478,X ; multi-sector read routine (normal) B762- 20 93 B7 JSR $B793 ; reset stack (normal) B765- A2 FF LDX #$FF B767- 9A TXS ; slightly odd (usually $9D84 is the ; boot2 entry point, but OK) B768- 4C 82 9D JMP $9D82 That all looks OK, and it proves that my manual decryption loop worked. But I still don't know why I'm not seeing the RWTS code I expected in the location I expected. Let's follow the white rabbit, starting at $B793, the entry point for the multi-sector read routine. *B793L ; this is not normal B793- 4C 00 B8 JMP $B800 ; but the rest of the loop looks ; entirely normal B796- AD E4 B7 LDA $B7E4 B799- 20 B5 B7 JSR $B7B5 B79C- AC ED B7 LDY $B7ED B79F- 88 DEY B7A0- 10 07 BPL $B7A9 B7A2- A0 0F LDY #$0F B7A4- EA NOP B7A5- EA NOP B7A6- CE EC B7 DEC $B7EC B7A9- 8C ED B7 STY $B7ED B7AC- CE F1 B7 DEC $B7F1 B7AF- CE E1 B7 DEC $B7E1 B7B2- D0 DF BNE $B793 B7B4- 60 RTS Down the rabbit hole we go... *B800L ; Hmm, the first thing this routine ; does is restore the code that should ; have been at $B793 (but wasn't, ; because it jumped here instead). ; Which tells me that this is designed ; to be run exactly once, during boot, ; the first time anything uses the ; multi-sector read routine at $B793. B800- A9 AC LDA #$AC B802- 8D 93 B7 STA $B793 B805- A9 E5 LDA #$E5 B807- 8D 94 B7 STA $B794 B80A- A9 B7 LDA #$B7 B80C- 8D 95 B7 STA $B795 B80F- A9 07 LDA #$07 B811- 85 4F STA $4F ; oh look, we're turning on the drive ; motor manually B813- AE E9 B7 LDX $B7E9 B816- BD 8D C0 LDA $C08D,X B819- BD 8E C0 LDA $C08E,X B81C- 10 12 BPL $B830 ; do something (below) B81E- 20 3E B8 JSR $B83E B821- 8D 00 02 STA $0200 ; do it again B824- 20 3E B8 JSR $B83E ; got the same result? B827- CD 00 02 CMP $0200 ; apparently "no" is the correct answer B82A- D0 0F BNE $B83B ; try again B82C- C6 4F DEC $4F B82E- D0 F4 BNE $B824 ; give up B830- A9 08 LDA #$08 B832- 8D 7A B7 STA $B77A B835- 8D F4 03 STA $03F4 ; jump to The Badlands B838- 4C 6B B7 JMP $B76B ; success path ($B82A branches here) -- ; continue to real multi-sector read ; routine B83B- 4C 93 B7 JMP $B793 ; main subroutine starts here -- looks ; for the standard address prologue B83E- AE E9 B7 LDX $B7E9 B841- BD 8C C0 LDA $C08C,X B844- 10 FB BPL $B841 B846- C9 D5 CMP #$D5 B848- D0 F7 BNE $B841 B84A- EA NOP B84B- EA NOP B84C- BD 8C C0 LDA $C08C,X B84F- 10 FB BPL $B84C B851- C9 AA CMP #$AA B853- D0 F1 BNE $B846 B855- EA NOP B856- EA NOP B857- BD 8C C0 LDA $C08C,X B85A- 10 FB BPL $B857 B85C- C9 96 CMP #$96 B85E- D0 E1 BNE $B841 B860- 48 PHA B861- 68 PLA ; skips over the first half of the ; address field B862- A0 04 LDY #$04 B864- BD 8C C0 LDA $C08C,X B867- 10 FB BPL $B864 B869- 48 PHA B86A- 68 PLA B86B- 88 DEY B86C- D0 F6 BNE $B864 ; look for track number 0 B86E- BD 8C C0 LDA $C08C,X B871- 10 FB BPL $B86E B873- C9 AA CMP #$AA B875- D0 CA BNE $B841 B877- 48 PHA B878- 68 PLA ; look for sector number 0 B879- BD 8C C0 LDA $C08C,X B87C- 10 FB BPL $B879 B87E- C9 AA CMP #$AA B880- D0 BF BNE $B841 ; skip the rest of the address field, ; then get the value of the raw nibble ; that follows B882- A0 05 LDY #$05 B884- BD 8C C0 LDA $C08C,X B887- 10 FB BPL $B884 B889- 48 PHA B88A- 68 PLA B88B- 88 DEY B88C- D0 F6 BNE $B884 B88E- 60 RTS The original disk has two address fields for T00,S00. One of them is the start of the actual sector data. The other one is a decoy that has an address field but no data field. The raw nibbles immediately following the two address prologues are different, and this routine checks to ensure that they are different. The routine in the disk controller ROM (usually at $C65C) that looks for track 0 sector 0 will ignore the decoy if it happens to find it before the real one. (Technically, it will look for the data field, not find it in a reasonable time frame, and start over, and eventually it will find the real address field as the disk continues to spin.) This decoy is apparently enough to fool bit copy programs. This is all very interesting -- and it explains why my bit copy would just hang during boot -- but it doesn't get me any closer to understanding this disk's custom RWTS. Let's back up. *B793L B793- 4C 00 B8 JMP $B800 B796- AD E4 B7 LDA $B7E4 B799- 20 B5 B7 JSR $B7B5 Ignoring the JMP for the moment, the multi-sector read routine calls the standard $B7B5 entry point to actually read a single sector. *B7B5L ; this is normal B7B5- 08 PHP B7B6- 78 SEI ; definitely not normal (usually $BD00) B7B7- 20 00 BA JSR $BA00 ; the rest is all normal B7BA- B0 03 BCS $B7BF B7BC- 28 PLP B7BD- 18 CLC B7BE- 60 RTS B7BF- 28 PLP B7C0- 38 SEC B7C1- 60 RTS That explains why I couldn't find the RWTS code I expected in the location I expected. This RWTS is laid out completely differently in memory than the standard DOS 3.3 RWTS. Even the entry point is different ($BA00 instead of $BD00). *BA00L BA00- 85 48 STA $48 BA02- 84 49 STY $49 BA04- A0 02 LDY #$02 BA06- 8C F8 06 STY $06F8 BA09- A0 04 LDY #$04 BA0B- 8C F8 04 STY $04F8 BA0E- A0 01 LDY #$01 BA10- B1 48 LDA ($48),Y BA12- AA TAX BA13- A0 0F LDY #$0F BA15- D1 48 CMP ($48),Y BA17- F0 1B BEQ $BA34 Yup, that looks like an RWTS entry point. After seconds of furious investigation, I found the RWTS code that looks for the data prologue: *BDE1L BDE1- BD 8C C0 LDA $C08C,X BDE4- 10 FB BPL $BDE1 BDE6- 49 D5 EOR #$D5 BDE8- D0 F4 BNE $BDDE BDEA- BD 8C C0 LDA $C08C,X BDED- 10 FB BPL $BDEA BDEF- C5 31 CMP $31 <-- ! BDF1- D0 F3 BNE $BDE6 BDF3- A0 56 LDY #$56 BDF5- BD 8C C0 LDA $C08C,X BDF8- 10 FB BPL $BDF5 BDFA- C5 4E CMP $4E <-- ! BDFC- D0 E8 BNE $BDE6 And there it is, in living color: this RWTS uses two magic zero page values to find the data prologue while it's reading a sector from disk. Why? Because f--- you, that's why. Because it makes the extracted RWTS useless without initializing the magic zero page location with the right magic number. Automated RWTS extraction programs wouldn't find this. If I load this RWTS into Advanced Demuffin, it will not be able to read the original disk, because the RWTS itself is not what initializes the magic zero page location. This calls for an IOB module. What's an IOB module? Well, the author of Advanced Demuffin anticipated that he couldn't anticipate everything, so he made the program extensible. Quoting from the Advanced Demuffin softdocs: --v-- An IOB module is an interface for the source RWTS. Advanced Demuffin uses the IOB module to set up the IOB table and jump to RWTS. The IOB module is stored from $1400-$14FB. When Advanced Demuffin loads in a IOB module, it reads the first sector of the file off the track-sector list and stores it at $13FC-$14FB. When Advanced Demuffin wants to read a sector it JSRs to the IOB module with the phase number, sector number, and the page number stored in the A, Y and X registers respectively. Since the source drive always has to be drive one, Advanced Demuffin can make the IOB module very compact. After it gets the page,track and sector Advanced Demuffin sets up the IOB for RWTS using this infor- mation, and JMPs to RWTS. (It jumps instead of JSRing, because it lets the RWTS do the RTS.) Here is a list of the IOB module that is built in to Advanced Demuffin: ; Convert phase # to track # 1400- 4A LSR ; Store track number 1401- 8D 22 0F STA $0F22 ; Store sector number 1404- 8C 23 0F STY $0F23 ; Store page number ; [note: original docs have incorrect ; hex opcode on this line] 1407- 8E 27 0F STX $0F27 140A- A9 01 LDA #$01 ; Store the drive number 140C- 8D 20 0F STA $0F20 ; Store the read code 140F- 8D 2A 0F STA $0F2A ; With high byte of IOB 1412- A9 0F LDA #$0F ; With low byte of IOB 1414- A0 1E LDY #$1E ; Goto RWTS 1416- 4C 00 BD JMP $BD00 --^-- Basically, Advanced Demuffin only knows how to call a custom RWTS if it 1. is loaded at $B800..$BFFF 2. uses a standard RWTS parameter table 3. has an entry point at $BD00 that takes the address of the parameter tables in A and Y 4. doesn't require initialization As it turns out, that covers a *lot* of copy protected disks, but it doesn't cover this one. This disk fails assumption #3 (the entry point is at $BA00, not $BD00) and #4 (the RWTS relies on the values of zero page $31 and $4E, which are initialized outside the RWTS). So, let's make an IOB module. *C500G ; because I overwrote DOS ... ]CALL -151 ; Most of this is identical to the ; standard IOB module that comes with ; Advanced Demuffin (explained above). 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 ; initialize the magic zero page values 1412- A9 AA LDA #$AA 1414- 85 31 STA $31 1416- A9 AD LDA #$AD 1418- 85 4E STA $4E ; get the address of the RWTS parameter ; table at $0F1E and call the RWTS at ; its non-standard entry point, $BA00 141A- A9 0F LDA #$0F 141C- A0 1E LDY #$1E 141E- 4C 00 BA JMP $BA00 Now let's tell Advanced Demuffin to use this custom IOB as well as the RWTS we captured from the original disk. [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 $B8, 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] I press RETURN to start the conversion process, and... Advanced Demuffin crashes. Wait, what? Specifically, it crashes the very first time it tries to read a sector from the original disk. That probably means that (a) the RWTS code I extracted from the original disk (and decrypted) is somehow corrupted or incomplete, or (b) my IOB module is calling that RWTS incorrectly Since decryption of executable code is generally an all-or-nothing affair, and I've already listed through much of it and confirmed that it contains real 6502 assembly language code (for example, to find the data prologue), I'm going to go with (b) my IOB module is incorrect. Let's back up. ]PR#5 ... ]BLOAD BOOT1 DECRYPTED,A$2600 ]CALL -151 *FE89G FE93G ; disconnect DOS *B600<2600.2FFFM ; move RWTS into place *B793L B793- 4C 00 B8 JMP $B800 B796- AD E4 B7 LDA $B7E4 B799- 20 B5 B7 JSR $B7B5 That "JMP $B800" instruction gets replaced immediately at $B800. B800- A9 AC LDA #$AC B802- 8D 93 B7 STA $B793 B805- A9 E5 LDA #$E5 B807- 8D 94 B7 STA $B794 B80A- A9 B7 LDA #$B7 B80C- 8D 95 B7 STA $B795 So, the routine at $B793 ends up looking like this: B793- AC E5 B7 LDY $B7E5 B796- AD E4 B7 LDA $B7E4 B799- 20 B5 B7 JSR $B7B5 Perfectly ordinary, no? Actually, no. Here's what it looks like on an ordinary (unprotected) DOS 3.3 disk. B793- AD E5 B7 LDA $B7E5 B796- AC E4 B7 LDY $B7E4 B799- 20 B5 B7 JSR $B7B5 Spot the difference. Go ahead, I'll wait. A and Y get passed through to the RWTS entry point, which is usually at $BD00 but on this disk is at $BA00. DOS 3.3 disk: *BD00L BD00- 84 48 STY $48 BD02- 85 49 STA $49 This disk: *BA00L BA00- 85 48 STA $48 BA02- 84 49 STY $49 Now do you see it? On a normal disk, the Y register holds the low byte of the RWTS parameter table address, and the accumulator holds the high byte. But on this disk, those are reversed; the accumulator holds the low byte, and the Y register holds the high byte. Why? Because f--- you, that's why. Of course, the IOB module I created to interface with this RWTS was still putting the low byte in Y and the high byte in A, so the RWTS was reading a completely bogus parameter table and God only knows what happened next. (Thank goodness the original disk was write-protected.) I need a new IOB module. *C500G ... ]BLOAD IOB,A$1400 ]CALL -151 *141A:A0 *141C:A9 *1400L 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 1412- A9 AA LDA #$AA 1414- 85 31 STA $31 1416- A9 AD LDA #$AD 1418- 85 4E STA $4E 141A- A0 0F LDY #$0F ; Y=high 141C- A9 1E LDA #$1E ; A=low 141E- 4C 00 BA JMP $BA00 *BSAVE IOB SWAPPED,A$1400,L$FB And here we go again. *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 "I" to load a new IOB module] --> load "IOB SWAPPED" from drive 1 [press "6" to switch to slot 6] [press "C" to convert disk] --v-- ADVANCED DEMUFFIN 1.5 (C) 1983, 2014 ORIGINAL BY THE STACK UPDATES BY 4AM ======================================= TRK:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR +.5: 0123456789ABCDEF0123456789ABCDEF012 SC0:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC1:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC2:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC3:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC4:...RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC5:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC6:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC7:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC8:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SC9:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCA:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCB:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCC:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCD:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCE:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR SCF:..RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR ======================================= 16SC $00,$00-$22,$0F BY1.0 S6,D1->S6,D2 --^-- Make no mistake: this is definitely progress. I have converted a little more than two tracks, which means that the RWTS I extracted *can* read (at least part of) the disk, and the IOB module I created *can* call the RWTS correctly. But this combination only up to T02,S04. That track/sector sounds suspiciously familiar. It's the last sector of DOS, and it's the first sector read by the boot1 code. ; relevant boot1 code B73E- A9 02 LDA #$02 B740- 8D EC B7 STA $B7EC B743- A9 04 LDA #$04 B745- 8D ED B7 STA $B7ED After DOS is loaded, I guess the RWTS is modified to look for a different data epilogue sequence. But remember, the third byte of the data epilogue is stored in zero page $4E (initially set up at $B6F0). So the DOS doesn't even need to modify the RWTS code directly; it just changes zero page $4E. Turning to the Copy ][+ nibble editor, it appears that every sector from T02,S05 to T22,S0F uses "D5 AA B5" as the data epilogue. --v-- COPY ][ PLUS BIT COPY PROGRAM 8.4 (C) 1982-9 CENTRAL POINT SOFTWARE, INC. --------------------------------------- TRACK: 03 START: 38A3 LENGTH: 015F 3880: B5 9A A6 B9 B6 D5 9A A6 VIEW 3888: FC DE AA EB DB DB DB DB ^^^^^^^^ data epilogue 3890: DB DB DB DB DB D7 AA 97 ^^^^^^^^ address prologue 3898: AA AA AB AB AF AB AE AA 38A0: AF FF FF FF FF FF FF FF <-38A3 ^^^^^^^^ address epilogue 38A8: FF D5 AA B5 CD F3 DF D6 ^^^^^^^^ data prologue 38B0: B4 F3 AE AE DF D6 CD ED 38B8: CD FC AE F3 F7 ED B4 96 38C0: D6 DF ED B9 9D 9D DB A7 --------------------------------------- A TO ANALYZE DATA ESC TO QUIT ? FOR HELP SCREEN / CHANGE PARMS Q FOR NEXT TRACK SPACE TO RE-READ --^-- It appears I need to make a *third* IOB module. ]PR#5 ... ]BLOAD IOB SWAPPED,A$1400 ]CALL -151 *1417:B5 *1400L 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 1412- A9 AA LDA #$AA 1414- 85 31 STA $31 1416- A9 B5 LDA #$B5 ; new 1418- 85 4E STA $4E 141A- A0 0F LDY #$0F 141C- A9 1E LDA #$1E 141E- 4C 00 BA JMP $BA00 *BSAVE IOB 3+,A$1400,L$FB [S6,D1=original disk] [S6,D2=partially demuffin'd 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 $B8, load "RWTS" from drive 1 [press "I" to load a new IOB module] --> load "IOB 3+" 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) 16 START TRACK: $02 ^^ important START SECTOR: $05 ^^ also important END TRACK: $22 END SECTOR: $0F INCREMENT: 1 MAX # OF RETRIES: 0 COPY FROM DRIVE 1 TO DRIVE: 2 ======================================= 16SC $02,$05-$22,$0F BY$01 S6,D1->S6,D2 --^-- And here we go... --v-- ADVANCED DEMUFFIN 1.5 (C) 1983, 2014 ORIGINAL BY THE STACK UPDATES BY 4AM ======================================= TRK: ................................. +.5: 0123456789ABCDEF0123456789ABCDEF012 SC0: ................................ SC1: ................................ SC2: ................................ SC3: ................................ SC4: ................................ SC5: ................................. SC6: ................................. SC7: ................................. SC8: ................................. SC9: ................................. SCA: ................................. SCB: ................................. SCC: ................................. SCD: ................................. SCE: ................................. SCF: ................................. ======================================= 16SC $02,$05-$22,$0F BY$01 S6,D1->S6,D2 --^-- 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. ]PR#5 ... ]CATALOG,S6,D2 C1983 DSR^C#254 319 FREE A 002 HELLO *B 002 LOMEM: T 081 QUESTIONS B 002 NEWMUSIC B 006 SMALL GUYS A 058 INVERTED NEWBEE12 B 026 NEWBEE.LO6 ]RUN HELLO Success! The game boots and loads with no complaint. All further disk access is done through standard DOS functions. (It even runs from drive 2!) Now to make the disk be able to read itself (remember, it still has the original RWTS on it)... Using Copy ][+, I can "copy DOS" from a freshly initialized DOS 3.3 disk onto the demuffin'd copy. This function of Copy ][+ just sector-copies tracks 0-2 from one disk to another, but it's easier than setting that up manually in some other copy program. Copy ][+ --> COPY --> DOS --> from slot 6, drive 2 --> to slot 6, drive 1 [S6,D1=demuffin'd copy] [S6,D2=newly formatted DOS 3.3 disk] ...read read read... ...write write write... Quod erat liberandum. --------------------------------------- A 4am crack No. 123 -------------------EOF-----------------