Skip to content(if available)orjump to list(if available)

An invalid 68030 instruction accidentally allowed the Mac Classic II to boot

adrianmonk

> I’ve discovered an undocumented MC68030 instruction that performs a read-modify-write bus cycle and also changes the value of the A1 register.

Rather than a "real" instruction that CPU designers consciously created and which was meant to do something useful but wasn't documented, it could just be that this is an illegal instruction and the logic in the CPU is doing whatever it happens to do when given don't-care inputs. (Maybe this is what the author meant, and I'm just catching up.)

Normally the CPU would detect illegal instructions and cause an exception. This would mean there are certain situations where it doesn't.

I found a manual at https://www.nxp.com/docs/en/reference-manual/MC68030UM.pdf. On page 8-9 of the manual (which is page 276 in the PDF file), it says:

> An illegal instruction is an instruction that contains any bit pattern in its first word that does not correspond to the bit pattern of the first word of a valid MC68030 instruction or is a MOVEC instruction with an undefined register specification field in the first extension word.

Note "in its first word". According to the write-up, the instruction is 3 words long. The first word is normal, and the weird bits occur in the second word. So quite possibly the 68030 doesn't validate this second word, just plows forward with the logic that implements the CAS instruction, and lets whatever happens happen.

(Great write-up and amazing dedication, by the way!)

sowbug

In college, I took a computer architecture course that briefly went into CPU microcode. The fake CPU we studied divided opcodes into bit fields where a certain bit caused a certain primitive operation, like maybe triggering an adder. From this you could see that a specific opcode was just a particular composition of the right primitives. "Valid" opcodes were simply the combinations that were useful enough to document, and "invalid" ones were superfluous or meaningless combinations. Once you understand this, illegal/invalid/mystery opcodes are a lot less surprising; rather, they're inevitable.

I was also taking a logic course at the time and dreaming in terms of truth tables, so it was quite a formative time in my early understanding of processor architecture.

rep_lodsb

It looks to me (not being an 68k expert) that only the first word is considered the "opcode": the second word just selects what "D" registers are used for the CAS operation. Normally one would expect the zero bits to be completely ignored in that case, since they don't have any role in the instruction.

But maybe on the 68030 in this case, the bits must be zero even if they have no documented use, because there is hardwired logic for another instruction that is activated by those bits being set, somewhat like the 6502 illegal opcodes?

userbinator

This is the closest I could find to what the 68000 opcode map looks like:

http://goldencrystal.free.fr/M68kOpcodes-v2.3.pdf

It's reminiscent of ARM, but the relevant part is that the CAS instruction's second word bits 5:0 look like a "modrm" (to use the x86 terminology) where the officially documented values select only Dn, but the undocumented variant would correspond to (d16,An). At least, that's my theory for why A1 gets modified.

userbinator

Update: from the linked thread at https://68kmla.org/bb/index.php?threads/classic-ii-possible-... there is this quote that supports my theory:

This seems to put something into A1 [...]If you clear bit 3 from the second word of the instruction this stops happening.

kstenerud

CAS has always been a bitch. I think I've received more bug reports about that instruction's emulation than any other instruction.

Incidentally, I remember another old "bug" in King of Fighters that "incorrectly" checked the carry flag of the SBCD instruction, which it used to decrement the round timer and end the current round. Completely undocumented of course, but if you don't emulate the arithmetic status flags when doing binary coded decimal operations, the round timer in KOF will just keep on going forever, cycling from 00 to 99 :P

SNK were really the gods of the 68000 chip.

mrandish

> SNK were really the gods of the 68000 chip.

As a fan of retro arcade machines and the 68k, I'd love to hear more about why SNK were godly in how they maximized the 68K.

azinman2

Yes - were they doing something that Apple, Amiga and the Unix vendors weren’t?

ChuckMcM

That is quite the journey. I find I don't have the patience these days to go quite so far down the rabbit hole as the author does, but I resonate with that feeling of accomplishment in knowing something versus just thinking you know something.

dougg3

Author here. Yeah, I have a tendency to go into pretty big deep dives when I find stuff like this. It's so rewarding at the end, even if it does take a lot of time!

mrandish

Finding a previously unknown undocumented instruction at this late date in a line of processors as prevalent and historically significant as the 68k is surprising. Congrats on your achievement! If someone does dive into fully characterizing the undocumented instruction so it can be properly supported in emulators (as you suggested), please post about it on HN. I suspect, like many undocumented CPU instructions, it was probably to help the original designers test or verify something during development but it would be interesting to know.

While obviously a subjective judgement, a lot of people who hand coded assembler on 68k processors regard the ISA as especially elegant, powerful and fun to develop for. In many ways I think of it as peak CISC, thanks to its orthogonal instruction set and wildly flexible addressing modes. And of course the platforms which used it are legendary, from consumer (Mac/Lisa, Amiga, Atari ST, Sinclair QL) to workstations (SUN, Apollo, Quantel) to gaming (Sega Genesis, Neo Geo, Capcom, Atari, Namco, Sega, Taito, Konami) to embedded (automation, print/network controllers, synthesizers, appliances). I'm certainly biased but to this day the 68k (and its 8-bit little brother the 6809) are the only CPUs I still enjoy writing assembler on.

userbinator

I suspect, like many undocumented CPU instructions, it was probably to help the original designers test or verify something during development but it would be interesting to know.

Or simply be an emergent but unintended behaviour of the implementation, as is the case for most of the undocumented 6502, Z80, and x86 instructions I know of.

dougg3

Thank you! Yes, I will definitely make another post if and when someone figures out what the instruction does.

bell-cot

> a lot of people who hand coded assembler on 68k processors regard the ISA as especially elegant, powerful and fun to develop for. In many ways I think of it as peak CISC, thanks to its orthogonal instruction set and wildly flexible addressing modes.

I definitely agree...but I'd say Motorola really got carried away with those wildly flexible addressing modes. Which lead them into implementation, power draw, and gate-delay hells by the late 1980's and the 68040. The future was ever-rising transistor counts and clock speeds - and their 68k architecture just couldn't go there.

submeta

I had an Amiga 2000 with an 68000 processor when I was a kid. How I got excited when I heared about the 68020, or even 68030! And then even RISC architecture. Those were things that got me excited back in those days. I could let my Amiga say a sentence like „Hello, how are you?“, in a robotic tone, and my friends were baffled as if they‘d been to the moon and back. I couldn’t have imagined that less then four decades later I‘d be talking to my computer in natural language using llms. And with Python, VS Code, and LLM in my toolbelt I can automate almost anything I wish to. Crazy times!

userbinator

Nearly all CPUs have undocumented instructions, and the 68k is no exception; it's just that the vast majority of people with enough interest and low-level knowledge at the time were focused on the x86/PC instead, which was arguably a far more open and stable architecture than Apple's. The 8088 and 8086 microcode was disassembled and studied extensively a few years ago, and I believe there's been some attempts at simulating it at the transistor level already. Even before that, the structure of the x86 opcode space was also explored in detail by many, with documents like these resulting from such effort:

http://ref.x86asm.net/geek.html

https://gist.github.com/seanjensengrey/f971c20d05d4d0efc0781...

We don’t really know the exact details of what this instruction does. With some limited testing, I believe I’ve observed that the resulting value of A1 depends on the original A1 value, the value of A7, and the program counter. But I’m not sure. Maybe someone can make a program that tries out a bunch of different register values and memory contents, and attempt to deduce what exactly the instruction does so that it can be emulated accurately. Until someone decides that it’s worth trying to figure out, MAME is patching this bug out of the ROM in order to allow the Classic II to boot.

IMHO this is definitely worth figuring out for accurate emulation. I'm not familiar with 68k but the bits in the instruction offer a good clue - my theory is that bits 5:3 of the 2nd word seem like another mode field, and instead of selecting one of the Dn registers via mode 000, 101 is selecting (d16, An) again and the Dc field, containing 001, is being interpreted as A1.

fulafel

nitpick: 68k of course wasn't Apple's architecture. The '030 was the nicest GP CPU around in its day, used in the Amiga 3000, Atari Falcon, Sun 3, NeXT Cube etc.

pjmlp

At the time Mac Classic was relevant, PC still wasn't thar great in home computing, no one was bothering with this stuff in x86/PC.

We were still bothering with demoscene stuff in 8 bit home computers, and those of us busy with 16 bit home systems were focused on Atari and Amiga systems.

PC and x86 at home only took off, meany really taking off among demoscene and other home users, was when VGA and sound cards became part of a standard PC.

userbinator

The Mac Classic was released in 1990, the Mac Classic II that is the subject of this article was released in 1991. At that time PCs with 286s and 386s were already common, and the 486 was just starting to gain marketshare at the high end. Most of the undocumented 8086 instructions had already been known for almost a decade; and the majority of those who knew were not demosceners. Many developers used Asm exclusively, and the "classic hacker mindset" was very much alive among them.

pjmlp

Depends on where in the globe one were and in my demoscene circles PCs only took off as interesting after Windows 95, folks using MS-DOS or Windows 3.x were mostly due to their parents family computer.

TazeTSchnitzel

I think non-x86/non-PC-compatible home computers did remain relevant for a bit longer in Europe and Japan than in the US but by 1992 the writing must have been on the wall.

zargon

This is 1992 we're talking about. You might be confusing the Mac Classic II with mid-80s Macs due to the form factor.

pjmlp

Where Amiga and Atari were still calling the shots among European demoscene.

khazhoux

30+ years later, I'm still always amazed at how effective the Mac debugger UI could be with such a tiny screen resolution. It's really quite masterful.

hajile

Was this copy protection to keep it from running on systems or does this happen on all 68030?

kevingadd

The contents of the post make me pretty confident that this wasn't copy protection, the jump table involved here is just missing an entry for the machine because (most likely) everyone involved in working on the ROM forgot to add a new entry to the jump table and it happened to work without a table entry by pure chance.

snvzz

Apple was already dabbling in planned obsolesce.

1over137

This seems like it will be impossible in the future with today’s Macs. Apple’s technical documentation is rubbish these days.

grishka

IMO the reason Apple doesn't provide this level of hardware documentation is because modern Macs don't have comparable expansion capabilities. The kind that expose system buses on connectors that users are supposed to plug cards into, and third-party developers to interact directly with hardware to make those cards work. On a modern Mac, you've got USB and Thunderbolt that you can interact with from a userspace program.

Though I'm not denying that some of the newer macOS APIs are very poorly documented. As in, you know you've stumbled upon the cool shit when you end up on one of those old pages with a blue gradient in the header that says "Apple documentation archive".

kevingadd

I think in modern environments the odds of this sort of bug slipping into released firmware/software are much lower. Address spaces are much bigger and the vast majority of addresses aren't mapped so doing a memory operation on a garbage address is going to fail most of the time, and invalid instructions will probably fail too.

Reading from a jump table with an index that's too big is a realistic sort of bug to have, so I could see that part making it into modern shipped software. But I would expect the process to fall over when it happens, not keep on trucking like it did here.

FWIW, WebAssembly is an environment where bugs of this sort are more possible, since it has a single linear address space where every address is both readable and writable. So if your garbage address is within range you can do an erroneous read, write or CAS and get away with it. But then invalid instructions like in the post will cause the WASM module to fail to load, so it's still not 1:1 comparable with this issue in the mac's ROM.

1over137

Sorry, the “this” I was referring to was the ability to consult docs, reverse engineer to this extent, etc.

saagarjha

I think you’re underestimating people who do this.

snvzz

Fortunately, today we've got pointer masking.

(ARM and RISC-V do, anyway)

basementcat

Do the '040/060 also support this "undocumented instruction"?

dougg3

On the 040, it seems to do something that actually involves D1. Definitely doesn't touch A1 at all. I didn't test further, but it's possible it just handles the instruction as a normal CAS.

It did cause a system error the first time I stepped through the instruction with MacsBug on my LC 475, but then it was fine after that.

mras0

Have an Amiga w/ 060, and that instruction doesn't seem to modify any A registers. (Only did a very quick test of those exact instruction words)

basementcat

I appreciate that I can ask an esoteric question about the behavior of a 30 year old microprocessor and multiple people respond with test results on actual hardware within a few hours. Can y'all also post the mask revision (if known) and whether it is an EC or LC device? (In case it impacts behavior)

mras0

Rev5 "full" 060 (not EC/LC). Quick capture of crappy methodology: https://imgur.com/a/XwQ1Tnp (PCR with revision number is in d0)

dougg3

My test of an 040 (no A1 change, D1 changed) was on a chip with the following markings:

XC68LC040RC25B

02E23G QEDP9348D MALAYSIA

cluckindan

Time to fuzz the rest of the processor.

ptek

Damn this instruction won’t speed up Amiga chunky to planar conversion.

fredoralive

Re Egret and Command-Power, I’m pretty sure the key combination (and the command-control-power hard reset combo) is always active, and not a Macsbug thing. ISTR you can trigger sad Macs on boot with it, which would be before Macsbug is loaded (I think), and also access the mini debugger if Macsbug isn’t loaded. At least from what I remember about my old LCII (would have to dig it out to double check though).

dougg3

I’ve been trying it out a bunch lately. From what I’ve seen, machines with Egret don’t have it enabled by default, but machines with the newer Cuda do.