01/09/2017 at 18:12 •
I grew up in suburban Norristown, PA. My mother, a single parent, raised my brother and me on a shoestring budget; we had little room for niceties. So in 1984, when my grandfather decided to buy me a computer, I was ecstatic. Computers were cool. I had been using the Apple ][+ since 6th grade at school; we had a Commodore PET lab there as well. Some of my friends had Commodore VIC-20s and C-64s. One had a TI-99/4a. Another loaned me her TRS-80 MC-10 for a while. I never thought I'd have a computer of my own, and here it was - an opportunity to buy whatever kind of computer I wanted!
I spent months preparing - deciding what kind of computer I wanted, based on what I wanted to do with it. The TI-99/4a was fun, but an outlier. C-64s were for playing games, and that's not what I wanted. I wanted to be able to fiddle. Like I did with the Apple computers at school. CoCos were very fiddly, but had a feel that I didn't quite like. And so it happened that one day - after a lengthy discussion with him about what I wanted and why - my grandfather drove me down to Bundy Typewriter & Computer in Northeast Philly to buy an Apple.
Now, as I recall it, the school year was coming to a close - which means it was early 1985. I would have just finished 7th grade (for the second time!) and was entering the 8th grade "gifted" classes (the district had dropped me out of the program after I flunked 7th the first time - note, it is not possible to pass 7th grade in 1983-4 if you do absolutely no homework all year, despite your test scores).
I suspect there was some condition attached to the computer - probably related to my getting back on the academic straight-and-narrow. I'd probably aced that year. The district dropped me in to the "average" classes, which were a breeze. I accidentally got myself in to the advanced Algebra class for 8th grade. I stumbled in to helping the Title I Math class with their Apple ][s, which paved the way to helping to teach the computer classes with Commodore PETs. All of this led toward my 8th grade year of gaming the system: I found the things that I liked and was good at (orchestra, Commodore PETs, Algebra, managing the Apple ][s, stage crew) and then things I hated and was bad at (umm... art, which is a sign of a crappy teacher, since I consider myself an actual artist) and balanced my life accordingly (read: found an excuse to skip every art class in 8th grade with a signed teacher's note for one of the aforementioned fun activities).
One of the biggest reasons I wanted the Apple //e (released in early 1983! New! Shiny!) was so that I would have a set of tools to work with. Yes, my friends had all those other computers; but I wanted to be able to work with the same software I'd been using in the Apple ][ lab at school. The games, I suppose. But more: the utilities. Disk editors. Programming languages like Logo and Applesoft BASIC. Program listings out of BYTE magazine. These were what really intrigued me. I wanted to be able to understand how things worked. Change them. Make my own things.
(I'm sure that will have surprised absolutely nobody that knows me today.)
So when we walked in to Bundy and they tried to upsell us to the brand new shinier Macintosh 128 (this is 1984, remember!), I was having none of it. It was clearly infantile in its capabilities. Nascent. Perhaps it had promise, but in that moment, it was the wrong machine. It had no programming languages. It didn't even boot in to Applesoft BASIC. I wouldn't be able to share any of it at school. Or run it in the school labs in the mornings and evenings, then head back home to work on it some more. Nope. No Mac, No How.
We actually went back home to deliberate (certainly not my choice) and went back a few weeks later. Walking in the second time, I knew *exactly* what we were getting. An Apple //e, 128k extended graphics card, color monitor. Which is exactly what we bought, and exactly what I used pretty much nonstop for the next 5 years. (Including - to much of my family's chagrin I'm sure - on vacations.)
In high school, I wound up working for Babbage's Software. Software sales were also nascent. People didn't know what they were buying or why. Software sales folks didn't know what to tell their customers about what to buy. But there were a large (and increasing) number of software titles, many competing in the same space; Babbage's was a pre-Internet way to put shrink-wrapped boxes of software in front of curious eyes, free from the car-dealership feel of computer hardware sellers. And one of the ways that they managed to keep the staff informed was to let the staff take software home and test it out.
I amassed quite a pile of software for my Apple //e. There are some utilities that I remember copying from my middle school. Some that I got from a friend with an Apple //c in early high school. And many, many that I bought while I worked at Babbage's. (I'm not sure I actually made any money there; I think I just funneled it all back in to software!)
So that I can wrap this up and get on with my day, I'll fast forward over the rest of the fun times. I wound up at Drexel University in 1990, where I worked with my roommate's Mac SE (we couldn't afford to buy me a new Mac of my own). The utilities I wanted now existed, and I was wholly ready to move on. My Apple //e went in to a box until about 1994, when a friend - a writer - was looking to buy a word processor. I spent a weekend dumping disk images out a serial port at a friend's house (he had the serial card), and then my Apple //e became a full-time word processor.
Most of the Apple ][ disks I had couldn't be imaged (copy protection was rampant at the time, and those diskettes were getting long in the tooth anyway). The remaining disk images went in to a CVS archive, later converted to git, and eventually supplemented with images found on the 'Net of software that I hadn't been able to copy (but someone had cracked, copied, and uploaded now that they're all abandonware).
Occasionally I'll pull out an Apple ][ emulator and play a game I remember. And one that I remember vividly from my early middle school years is Three Mile Island - a nuclear reactor simulator.
I never owned a copy of this disk, and I must have played it in 6th and 7th grades. I found the image online (probably in the early 2000s) and spent some time playing it. The only problem with it is that it crashes if you press '7'. Which is problematic, because the number keys switch between screens in the game. '7' is supposed to let you save and restore. Instead... instant boom. Bummer for a game that takes hours to play.
Now here I am, in 2016 - and I've got a hankering to play again. Only this time, I don't want to play TMI; I want to fix it.
01/09/2017 at 18:39 •
The problem is multivariate.
This is a disk image that I got online. I have no idea what its provenance is. I don't know if someone had to crack it (there's no crack notice, which was a point of pride back in the day, so I suspect not).
The software is old. Not just 36-years-old-from-today. It's also from 1980, which was early days. The Apple ][+ had just been released; words like "language card" were on the way out. When the Apple //e was released the Apple world took big steps forward again. Everything I know about the internals of Apple ][s comes from 1982 and later. This is, to me, from Apple pre-history.
And then I'm also running this in emulators. No emulator is perfect. I expect glitches.
So when 36-year-old "old" software in an emulator crashes, where do you look for the problem? Well, for 14 years (I'm guessing I downloaded this along with many other titles around 2002), the answer is "nowhere." When you forget that '7' is the keystroke of death and accidentally push it, you stop playing.
What makes now different, then?
I found a copy of Merlin Pro 2.58 - a ProDOS version of the assembler that I used through High School.
This was the first piece of serious software that I bought for myself. It was expensive, IIRC. But it opened so many doors. I spent a lot of high school programming nonsense in my free time - mucking around with the internals of ProDOS, creating new commands; mucking around with the internals of floppy drives, learning what prologs and epilogs were and how bits were encoded on the disk; generally breaking things and learning how they worked. And a lot of the most advanced work I did was powered by Merlin Pro 2.58.
I had occasionally looked for this online before. Merlin 2.4* (2.43, maybe?) has been available as an image for a long time. But this is DOS-only, and my work disks were all ProDOS (unreadable from DOS 3.3).
So when I found Merlin Pro 2.58, I jumped at the chance to go look at the assembly that I wrote back in the late 1980s. And wow, such nostalgia! I remember vividly the printouts of fan-fold continuous feed sprocket dot matrix assembly. Marking them up during math class. Trying out new things in our Apple lab in my high school (they didn't have assemblers; the classes were all in BASIC, but that's another story). Running home to code changes after school, print it back out in the morning, and repeat. I loved those days.
After perusing the code I wrote as a teenager, I looked at some of the other disk images in my archive. I played Ali Baba and the Forty Thieves with my 11-year-old son (who is now addicted to it, mwahahaha). I found Temple of Apshai images online and spent a few minutes remembering how I wanted to love this game but hated its controls. And I found my image of Three Mile Island, in its semi-defunct state.
Curious, I thought I'd troubleshoot it a bit.
Was it perhaps designed for a specific ROM? Virtual ][+, my emulator of choice, comes with ROMs for the ][ and the ][+. A quick test showed that it failed the same way there.
Is this a bad disk image? I downloaded every copy of TMI that I could find - some with different names - just to find that they're all exactly the same disk image. So either this is exactly the original image, or they're all copies of the same single disk.
Which leaves little to do except to poke in the binary itself and see what the heck it's doing.
01/09/2017 at 20:05 •
My great aunt used to say that she had a great Forgettery. I think I inherited it. My Remembery is faulty.
I can remember really arcane and worthless trivia, like "CALL -151" enters the machine monitor on an Apple ][ and that the HERO-1 robot had a built-in speech synthesizer with the phrase "I don't do windows". Useful everyday stuff, like where my wife said the various knives belong in the knife drawer? Not a chance.
This works out pretty well for this project, though! I still remember enough of how to get around the Apple //. The assembly language looks familiar. I can poke at executables in memory. I can boot various kinds of DOS and transfer files between them. With ProDOS and Virtual ][, I can copy files back out to my Mac and use all of the tools I've got there, too. But I need a bit of a refresher - I haven't looked at the internals of the Apple //e since 1989. I don't remember what $C010 is, but I do remember that $C000 is the beginning of ROM. But it's a fast study: I quick remember zero-page memory, Applesoft BASIC tricks with magic values therein, and find references for various ROM entry points. The TMI disk is a DOS 3.3 disk; I use disk Copy to copy it to a ProDOS disk, boot to ProDOS, and then use Virtual ][ to copy it out to my Mac. Lovely.
Do I know where in memory this binary even loads, so that I can poke at it in the virtualizer? Nope. But a little poking around memory, and I find it at $1000.
Do I have a disassembler for 6502? Well, no. But several exist. I download a few and find them all lacking.
The 6502, being an 8-bit processor, has at most 256 opcodes. It can't be that hard to write a disassembler. When faced with the same problem for the PIC, I wrote pic-disassemble, a (wait for it) disassembler for (ahem) the PIC. (Bet you didn't see that coming.)
So an hour later, and I've got a perl script that disassembles. Just like all the other disassemblers that I found and didn't like. But from here, I can augment it to do anything I want!
I want it to be able to label memory points. I want it to be able to mark regions of memory as data, so they're not disassembled. But mostly I want it to be able to draw call flow graphs. Like perhaps this one!
(The full-size copy is here, on my web server, if you really want to look at it.)
From the call flow graph - which initially just had nameless labels, no descriptive ones like "MAINLOOP" - I was able to guess where the highlights are. MAINLOOP was my first target. Then I named ROM entry points in the assembly listing, followed by a lot of reset-to-monitor; call subroutine manually; see what happens.
Eventually I got tired of pure exploration and went in for the kill.
What does this program *do*?
0x1000 ORG 0x1000 0x1000 JSR ASKINIT 0x1003 L1003 JSR F8ROM:INIT 0x1006 JSR F8ROM:HOME 0x1009 JSR PRNTMENU 0x100C MAINLOOP LDA #$FF 0x100E STA CURSCR 0x1011 JSR PRNTTIME 0x1014 JSR RUNTILL 0x1017 CMP #$84 0x1019 BNE L1022 0x101B LDA #$2 0x101D STA $5B28 0x1020 BNE MAINLOOPWell, there's the beginning of it. Simple enough. The ASKINIT function asks if you want to initialize the reactor core, and then returns. The F8ROM functions are part of the machine's rom - in this case, clearing the screen and returning the cursor to the top-left. The PRNTMENU function prints out the list of screens in the game. And so on. Boring.
I spent some time looking at code bits that read from the hardware KBD register, and that cleared the KBDSTROBE register (telling the machine that you're ready for the next keypress). Which lead me to this piece of the program:
0x543F GETKEY LDA KBD 0x5442 BMI L5446 0x5444 SEC 0x5445 RTS 0x5446 L5446 BIT KBDSTRB 0x5449 STA $18 0x544B CLC 0x544C RTSRead from the keyboard; if the high bit isn't set, then there's no input, and we'll set the Carry flag and return. If the high bit *is* set, then we'll clear it, store the result in zero-page location 0x18, clear the carry, and return. Clearly this is a utility function to get a keypress and return (via the Carry flag) whether or not something was read.
What calls GETKEY? I want to find the thing that responds when you press '7'. Should be easy enough.
Except that it isn't. The code is sprawling; it's clearly compiled (not hand-coded) because there are pieces of code that look like they're doing parts of the same thing, in very different parts of memory. I'd only found a couple places that call GETKEY; from there, I suspect everything is picking up the key code from $18? Hmm. Well, I find code snippits like this that are looking for specific keys:
0x12F4 L12F4 JSR GETKEY ; wait until we get a keypress; S/R go to 1303, others return 0x12F7 BCS L12F4 0x12F9 CMP #$D3 ; 'S' 0x12FB BEQ L1303 0x12FD CMP #$D2 ; 'R' 0x12FF BEQ L1303 0x1301 L1301 CLC 0x1302 RTS 0x1303 L1303 STA $0 ; keypress was 'S' or 'R' from L12F4... only no part of the game I know of uses S or R. But there are pieces like this:
; This is reached normally for every tick 0x18A5 L18A5 LDA KBD ; read from keyboard 0x18A8 BMI L18EF ; this may be "if key not pressed"? 0x18AA LDY $5B0D 0x18AD CPY #$7 0x18AF BCC L18B3 ; branch if screen < 7 0x18B1 LDY #$7 0x18B3 L18B3 LDA L5B04 ; wait out the rest of the cycle? 0x18B6 BEQ L18BB 0x18B8 JSR $FCA8 ; F8ROM:WAIT 0x18BB L18BB LDA $5B0D 0x18BE CMP #$0 ; on screen #0? 0x18C0 BEQ L18C6 ; yes: go to L18C6 0x18C2 CMP #$2 ; on screen #2? 0x18C4 BNE L18EA ; no: go to 18ea 0x18C6 L18C6 LDA #$12 ; this is screen 2 work... 0x18C8 JSR L5511 0x18CB TAX 0x18CC INX... which are identifiably "if you hit key 0-7, then we do something". I dug through a lot of code like this, which is repeated various places, that is so tangled up that I'd need a lot of tea to decipher its intent.
I spent a lot of time changing pieces of the program to BRK statements (0x00) so that I could tell whether or not that bit of code was being executed, and when. None of them were executed when I pressed '7'. Here's another one:
0x1039 L1039 CMP #$B0 ; 0? 0x103B BCC L1003 ; branch if A <'0' 0x103D CMP #$B8 ; 8? 0x103F BCS L1003 ; branch if A >= '8' 0x1041 PHA ; start of handling keys 0-7 0x1042 SEC 0x1043 SBC #$B0 0x1045 STA $5B0D ; 5b0d gets the current screen we're on 0x1048 PLA 0x1049 JSR L564F 0x104C BCS L10B5 0x104E BPL L1001 ; WTF? can't be right. 0x1050 DEC $10 0x1052 ??? 0x1053 ROL $11,X 0x1055 ??? 0x1056 SEI 0x1057 ORA ($B4),Y 0x1059 DEC $11,X 0x105B LDA CV,X 0x105D ??? 0x105E LDX L124F 0x1060 ??? 0x1061 ??? 0x1062 ??? 0x1063 ???This is just after I've identified that $5B0D is what I call CURSCR. When the screen changes, a number from 0 to 7 (presumably) is put in $5B0D. When the game starts, it puts $FF in there (presumably meaning "on the menu"). But hours of poring through code paths that mention $5B0D, and I'm no closer. So I zeroed in on something odd in this particular listing.
It's those question marks.
Not all 256 values are opcodes. Those ??? values are things that don't disassemble properly. And that instruction at 0x104E -- "Branch on Plus to L1001" makes no sense! Back in the first listing, you'll see that there's an instruction at 0x1000 and then one at 0x1003, because the first instruction is three bytes long. How can we be jumping back to 0x1001? Is this really a program that's sophisticated enough to be able to take advantage of gadgets embedded at offset code locations?
I doubt it. More likely, I'm looking at something that's not code.
The Apple ][, by virtue of that "if the high bit is set, then there's a key that's been pressed" feature, winds up setting the high bit on ASCII characters. So you can't see things like text from a straight hex dump. But if you XOR the whole thing by 0x80, then you might see something like...
Now we're talking. I'll just update the disassembler to dump those as strings rather than trying to disassemble them, and then...
0x1271 L1271 CLC 0x1272 RTS 0x1273 JSR F8ROM:INIT 0x1276 JSR F8ROM:HOME 0x1279 JSR $A7AD 0x127C LDA #$0 0x127E STA $A9A1 0x1281 JSR L5479 0x12B9 STR '\cM SAVE / RESET STATE\cM\cM\cM\cMCATALOG (Y OR N) _\0' 0x12B9 BIT KBDSTRB 0x12BC L12BC JSR GETKEY 0x12BF BCS L12BC 0x12C1 CMP #$D9 0x12C3 BNE L12D3 0x12C5 JSR L5479 0x12D3 L12D3 STR '\cM\cDCATALOG\cM\0' 0x12D3 L12D3 JSR L5479 0x12EF STR '\cM\cMSAVE OR RESET (S OR R) '... setting aside some of the minor formatting bugs in the disassembler: this is looking really interesting. Not only do I now see something that's talking about 'S' and 'R', but I also see a clear entry point to the function that is definitely responsible for saving and restoring! Since I never see these messages, I'm assuming that the magic memory regions $A7AD and $A9A1 are implicated in the crash.
Region $A000 is DOS. So this makes sense - we're about to try to invoke some DOS actions. The string at 0x12D3 is chr(4)CATALOG -- where control-D (character 4) is a magic way to tell DOS that you want it to execute a command. And CATALOG is the DOS way to 'ls' or 'dir'. Yes yes yes. This is it, folks. If I were playing hot/cold, we'd be boiling right about now.
The only trouble is that I have no idea what $A7AD is supposed to do. It's not familiar to me at all. None of my reference books (now PDFs; I've long since gotten rid of the paper copies, so thank you whomever scanned these things and put them on the Internet!) mention that vector. So I'm stumped until some Google-Fu lands me here, back in 1980, reading Micro, the 6502 Journal.
As was the fashion at the time, clever people were always coming up with little programs to do this-or-that better than before. They'd publish the listings in publications like BYTE Magazine. Or, apparently, Micro. And on page 9 is "A Little Plus for your Apple II", by Craig Peterson. Thank you Craig. And thank you Micro, for publishing him. Because right there, on the right side of the page, Craig says:
Also, this example is setup for use with 3.2 DOS on a 48K system. If you have 3.1 DOS and 48K memory, use DOS addresses $A7AD and $A99E in place of $A851 and $AA5B in lines 200, 210, 400, 640, and 690.
That's the Rosetta Stone. $A851 I know! That's the vector to re-initialize the DOS hooks so that you can make DOS calls from inside a binary program. Wow, that tells me a lot. Combined with the history I know about the TMI game (that I've read online), I can now reconstruct how this image came to be, and I think I know how to fix it.
TMI was purportedly originally an Integer BASIC game. I never saw it in that form; it was rewritten in machine language in 1980 - compiled, as far as I can tell, by a fairly inefficient compiler that stomps all over zero page and redoes work and whatnot - and must have been originally distributed, in that form, for Apple DOS 3.1.
It had to be for DOS 3.1. That's the DOS 3.1 entry point for the thing I know of in DOS 3.3, that my new bud Craig says was in DOS 3.2.
But this copy is on a DOS 3.3 diskette. How did that happen? And can we just put it back on a DOS 3.1 disk and run it? Well, no, we can't just put it back.
DOS 3.1 and 3.2 used 13 sectors per track on the disk. When Apple were working on 3.3, they found a sneaky way to increase that to 16 sectors per track, which gave them more storage on the media. But it's not backwards-compatible. People had to lug their Apple ][ back to the dealer for an EPROM replacement on the disk drive controllers in order to use DOS 3.3. Which means that my emulators would need a copy of the 13-sector ROM for DOS 3.1 in order to use it. Which I don't have, and have little interest in looking for; I'd have trouble figuring out how to transfer the binary back over to that disk, and then I'd still have some strange nonstandard emulator configuration that I couldn't run easily. Nor could I fix this problem for the world without crazy instructions about replacing ROMs (because, y'know, the world obviously wants Three Mile Island fixed so that they can all play it without it crashing - so that they can melt down a nuclear reactor in peace and all).
My guess is that all of the copies of this disk out on the Internet came from the same person. I'm guessing that someone used MUFFIN or some other Apple sector converter tool (hey, that's how I could get the binary on a 13-sector disk! Still doesn't fix the "need a special ROM to use it" problem, but anyway) - ahem, or some other Apple sector converter tool to copy it on to a 16-sector DOS 3.3 disk. And that one plays the game. Mostly. With a little crashy DOS 3.1 incompatibility left behind.
I doubt this is a copy from the manufacturer, though. I remember playing this game in middle school, and I don't recall it crashing. And I know we were running DOS 3.3, which means 16-sector disks, which means there was a DOS 3.3 compatible version of TMI. It had a well-printed label, as I recall. I'm reasonably sure it was an authentic original. So the manufacturer *had* a copy that was 3.3-clean; not this abomination of a 3.1 copy on a 3.3 disk.
But then again, we don't need to put it back on a 3.1 disk to make it work again.
All we have to do is change which DOS entry point it's using. It's just this one place in the game, these two little bytes (little-endian order), which need to read:
Save. Reboot to disk copy. Copy from ProDOS-mounted Mac volume on to virtual disk. Swap disks, reboot. Aaaannd...
HELLZ YEAH! No crashie. Time to stick my name in here for the old-timey "I done cracked a warez" feeling, but better, since I've just fixed a bug:
That's one 37-ish-year-old bug squashed.
If you want to grab a copy of the disk image for yourself, here's a copy on my webserver or you can now get a copy from the Asimov archive. Or you can grab the original from just about any Apple ][ disk archive and edit the binary yourself. You're welcome, world.
And now if you'll forgive me, I have a nuclear reactor that I have to go melt down.