Boot tracing in the 21st century

In a previous post, I went over the contents of The Electronic Mailbag program, but I want to also run through how I managed to get it into a usable format, since it was an interesting foray back into the world of boot tracing.

The disk itself is fairly resistant to copying, throwing lots of errors in most every program I tried. I had hoped just a nibble copy would suffice, but it didn’t.

When you start the program, it clearly is just booting DOS 3.3 (or, actually, as I discovered later on, Diversi-DOS, which is a modified and optimized version of DOS 3.3). It looks just like any standard-issue disk when booting, it makes the normal sounds, shows the Applesoft prompt, and runs a startup program. So it should be relatively easy. Nevertheless, the bit copies I made would grind and grind and then restart when I tried to use them.

So, step one was to grab the nibbles, which I accomplished using SST (Saltine’s Super-Transcopy), a modified version of the EDD (Essential Data Duplicator) copy program that allows to save the nibbles it collects during a copy operation onto two data disks, that can be later used to reconstitute the original disk. I saved the half tracks as well just in case, though they didn’t turn out to be necessary. Using the CFFA3000, I was able to put these data images on a USB stick that I could then move to my Mac for the rest of the work.

Mailbag sst

Once the images are on the Mac, running SST on an emulator allows you to reconstitute the nibbles onto a nibble image. I’m using Virtual II on the Mac, which has a bunch of very useful features for making this boot tracing adventure easier. Running SST in reverse and saving onto a .nib format disk image, I wound up with a mostly-faithful copy of the disk. What is not preserved is any kind of special timing information.

Inserting the disk into Virtual II and booting it just resulted in a bunch of disk grinding and rebooting. So, I decided to start watching what it was doing to see if I could find where it was going wrong.

The “normal” procedure for boot tracing is to copy the Disk II controller boot ROM into RAM somewhere and modify it so that when it is about to jump to the next stage, it instead stops, so you can examine memory. The ROM loads track 0, sector 0 into $0800 and then jumps to $0801, so normally you just change the jump to a return (60) and then look to see what it loaded.

This is where modern technology comes in, though. Now that the machine is virtual, it is no longer necessary to do all of that in quite such a step by step way. Instead, I just set a breakpoint at the jump to $0801 and used Virtual II’s fantastic “Inspector” panel to see what was happening.

Mailbag inspector 0801

Then just restart the machine and boot the disk, and it halts. The code it executes at $0801 loads the next stage of the boot process, and it’s pretty standard DOS 3.3 stuff. It loads the first 10 sectors of the disk into $B700-BFFF (which provides the basic RWTS [Read/Write Track/Sector] routines) and then jumps into it to load the rest of DOS in. Usually, this jumps to $B800, but here it is jumping instead to $BB00. So, set a breakpoint there (and best to clear the breakpoint at $C6F8, since that gets called repeatedly until RWTS is loaded).

Mailbag jmp bb00

Resuming on, it loads RWTS and stops at the breakpoint so we can investigate.

Mailbag bb00

It turns out that what happens at $BB00 is a little patch. This sets the reset vector so that pressing Reset won’t let the user out of the program (it will jump to $B75D if you hit reset and tells the machine it was just powered up), it stores $AA in zero page address $31, and then resumes the normal booting procedure at $B700. So this is a little bit of protection.

The code at $B700 is pretty much standard, there’s nothing of interest here that’s different from normal DOS 3.3. So, it loads all of the rest of the sectors to bring in DOS and then jumps to the DOS coldstart entry point at $9D84. However, it is clear from booting the disk that it never makes it to the DOS coldstart call. When it tries to read the disk, it just gets drive errors and fails. So there must be something tricky in the read part of RWTS. Let’s see.

The part of RWTS that reads the address marks is at $B944 (and this is all made much easier of course by the fact that this is just a slightly modified DOS 3.3 being used here, so all of the known entry points still seem to be valid). What this does on a normal DOS disk is this: it reads things on the disk until it encounters the nibbles D5 AA 96, after which it expects to find address information that tells it where it is on the disk, consisting of a track number, sector number, volume number, and checksum. After those, it expects to find DE AA, and then it is ready to start reading the sector data. It is actually a fairly common copy protection trick to alter these signatures, since normal DOS won’t be able to find any data if it can’t find the address marks. Looking at this, I see that it does something a little bit unusual here.

Mailbag address marks

Instead of looking for a nibble value of D5, it instead checks to see if the value it read, shifted one bit to the right, is 6A. As it happens, D5 shifted one bit to the right is 6A, so it would recognize normal D5 address mark nibbles, but it would also recognize D4 as well. A search of the nibble image does reveal a few D4AA96 headers (as well as D5AA96 headers), so that was one trick they used to keep the disk from being copyable. The second nibble in the header is normally AA, but here, rather than checking for AA, they check for whatever is in zero page address $31. Back a couple of steps ago, the patch at $BB00 put AA in $31, and as far as I can tell nothing ever changes that. Maybe this was a protection mechanism that they in the end opted not to use. So, the code is slightly different from standard DOS 3.3, but the effect is the same. Then, it wraps up by looking for 96 as usual. So, we now know that it is willing to accept address fields starting either with D5AA96 or D4AA96. While interesting, this hasn’t really gotten us any closer to making the disk boot, though, since the nibbles are recorded faithfully and the mixture of D5AA96 and D4AA96 headers were accordingly preserved as they were.

After reading the headers, it does the standard thing to bring in the track, sector, volume, and checksum information, and then it looks for a DE nibble to close the header, but then it does something strange.

Mailbag cmp 08

Normally, DOS would check for a DE, then check for an AA, and then it would be satisfied that the address has been read. What this does, however, is checks for a DE, and then immediately, without even waiting for the entire nibble to be read, pulls in some bits off the disk. If the bits it pulls in come too quickly, it reports an error. Specifically, it is reading the data latch (LDA $C08C,X) and if it has already gotten enough 1 bits to exceed 00001000, then it branches off to report a read error. So, what must have happened is that on the original disk, some extra zeros (or just a nonstandard nibble) were written after the DE nibble in the address field trailer. If those extra zeros are not there (and they wouldn’t be either in this nibble image or likely in a nibble copy), then the disk must not be the original.

So, that’s why it’s throwing all these read errors. But there is an easy fix. We just tell it not to react with an error even if it got the next nibble too quickly, by “commenting out” the branch to error. Specifically, replacing that instruction (BCS $B942) with NOP (no operation) commands. Fortunately, Virtual II also allows you to alter memory on the fly, so we can insert those NOPs now.

Mailbag nop nop

With that modification in place, resuming allows the disk to boot property.

Mailbag booted

If we were willing to set a breakpoint and modify the memory each time we booted the disk, we’d be done. But of course it would be nice to record those NOPs in the code it loads from the disk, so that it would just boot on its own.

This turned out to be a bigger challenge than I thought it would be. Ideally, I would have just copied the sectors over onto a normal DOS 3.3 disk and then used a disk editor to alter those bytes. But this address mark trickery was foiling almost every copy program I tried. This would still be the next step, since it is likely very feasible to turn this into a regular DOS 3.3 COPYAble disk. However, most copy programs are set up to handle altered address marks, and don’t allow you to completely ignore the closing marks, and after a while fiddling with Super IOB and modifications to RWTS, I decided I would just change the nibbles in the nibble image.

This is not an easy task. The form the data takes on the actual disk is actually quite far removed from the form it takes in memory. Each section is 256 bytes long, but due to the way the disk hardware works, it can only read 6 bits at a time. That’s what the nibbles are, 6 bit representations of parts of the bytes it is trying to read. So, when a sector is written, the bytes are all broken up into 6-bit chunks, with the most significant bits stored in one buffer, and then a mixture of the least significant bits, grouped together, in a second buffer. Once this is done, the 6-bit data is then run through a translation table to get to the actual nibbles that are written to the disk. Furthermore, the nibbles are not a straight translation through the table, but are also exclusive-ored with the previous nibble in order to generate a checksum at the end. So even though I knew very well that what I wanted to do was find the sequence C9 08 B0 A5 and replace it with C9 08 EA EA, finding that in the nibble stream is not at all easy.

I did some math and worked through lookup tables, thought I’d gotten what I was looking for and then got foiled by the fact that the nibbles are all exclusive-ored with each other, and finally gave up and decided to take a possibly easier route. I extracted the RWTS from the protected disk by setting a breakpoint at $BB00, and then moved the program counter to $FF65 (reset), moved the RWTS into memory that would be safe across rebooting, booted another disk, and BSAVEd just the page with the C9 08 B0 A5 in it onto a newly formatted nibble-format disk image. Then I replaced B0 A5 with EAs (the desired change) and BSAVEd the same page. And then I went in with a hex editor, located the two sectors that had the “before” and “after” data, copied it out into a text editor, and just scanned the lines until I found the differences. There were two triples that needed to be changed. A D6BDEA that needed to change to DBB4D7, and a EB9EB7 that needed to change to F79696. This was at a cost of a bit of time and eyestrain, but ultimately seemed like the simplest way to do what I was after.

Mailbag nibble compare

So, in a hex editor, I made the changes to the Electronic Mailbag nibble image, so that the address mark trailer check was NOPed out, and that is the nibble image I posted before. And which I will repeat here. The data disk is a normal DOS 3.3 disk.

I almost thought I still had something left to do, because when I tried to use it and it asked for the original disk to be inserted, it wasn’t recognized. I again turned to Virtual II’s inspector and took a little tour through the program that was loaded, looking for the text of the message that asked for the original disk to be inserted. A closer look revealed what the problem was.

Insert master disk

This is the Applesoft code, and all of the Applesoft keywords are tokenized, so it’s a bit hard to read. But you can use a grid of Applesoft tokens to decode it. The line that prints the message starts at $3BFB. It points to the next line (at $3C53), it is line number $84DA=34010 and it says HOME (97):VTAB (A2) 12:PRINT (BA) ” INSERT THE MASTER PROGRAM DISK AND PRESS A KEY”;:GET (BE) A$:PRINT (BA). The next line references the following line ($3C5E), is line number $84E4=34020, and says GOTO (AB) 34004. So, where is 34004? That would be line $84D4, which starts at $3BD4. It says PRINT (BA):HOME (97), then the next line says PRINT CHR$(4);”VERIFY SYSTEM”:GOTO (AB) 281. Ok, now where is 281? That’s $0119, so back to the early part of the program… One of the great things about Virtual II’s inspector is that you can just search memory, so I searched for 19 01 and found it right away.

Mailbag line 281

Without parsing it out, clearly it is invoking DOS to delete a file, and rewrite it as a text file that runs whatever is stored in F$, possibly the program that was chosen from the menu selection. This gave me an idea, though. Maybe all that it was doing was presuming that it was not using the original disk if the disk was not write protected. So, I tried write protecting the disk image, and presto! No more problems recognizing the original disk. So, I didn’t need to change anything after all, this was just enough exploration to give me an idea of what to try.

There’s no check here exactly, but the most obvious way to make it behave this way is to use the ONERR GOTO command, which traps errors and sends program control off to whatever line is specified. From the Applesoft token table, this would mean we should find the tokens A5 AB somewhere (ONERR GOTO), and searching around in memory I did find a few places where this was used. I didn’t investigate all of them, but they all send program control into those sections at the end of the program in the 30000 range.

Mailbag onerr goto

Anyway, so that’s how I got The Electronic Mailbag to run properly in an emulator. It is in almost pristine form, the only thing I had to do was NOP out a single check in the RWTS on the address field trailer. It is not “cracked” insofar as if it were written back out to a floppy, it wouldn’t be much more copyable than it was before (though now a bit copier should be able to copy it successfully). It actually shouldn’t be all that difficult to get it into a more standardized DOS 3.3 format from here, since it is basically just in a slightly warped DOS 3.3 format already, but for archival purposes, the goal is already achieved, so I’ll probably direct my energy elsewhere.

Posted in Uncategorized and tagged , .