adrianmonk 6 days ago

> 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 5 days ago

    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 5 days ago

    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 5 days ago

      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.

kstenerud 6 days ago

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 5 days ago

    > 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 5 days ago

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

      • kstenerud 4 days ago

        They were game devs, so they weren't so much interested in how the chip was SUPPOSED to behave, but rather in how it ACTUALLY behaved. If you look at their code, you'll see instructions being used in ways that they weren't intended, or for their hidden side effects.

        You saw similar kinds of shenanigans with the 6502 chip and its "hidden opcodes" that eventually became semi-official.

        The mere idea of using binary coded decimal to implement a time counter that can trivially be converted to decimal numeric sprite indices is brilliant. If SBCD had operated as described in the manual, it would have been completely inappropriate due to the extra time SBCD takes to run. But since the instruction already alters the status register, there's no need for an additional CMP, and so SBCD is faster overall, and then a simple shift-then-index is wayyyy faster than a full binary-to-decimal conversion when displaying the timer on screen (DIV performance was abysmal on the 68000).

        This is how they managed to squeeze so much juice out of the chip, as evidenced in the Metal Slug series, and SNK vs Capcom https://en.wikipedia.org/wiki/SNK_vs._Capcom:_SVC_Chaos

        An OS vendor wouldn't do these things, because there'd be no guarantee that a future chip (68010, 68020, 68030 etc) mightn't actually behave as the manual states, and then the code would break.

        • mrandish 4 days ago

          Interesting stuff. Thanks!

          > An OS vendor wouldn't do these things...

          Good observation. Writing for a closed, custom-built platform had its advantages.

ChuckMcM 6 days ago

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 6 days ago

    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 6 days ago

      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 6 days ago

        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.

      • bell-cot 5 days ago

        > 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.

        • mrandish 5 days ago

          > I'd say Motorola really got carried away with those wildly flexible addressing modes.

          Yeah, while they could certainly be extremely powerful, I'll admit the edges of my 68000 programmer's reference card quickly got dog-eared from how often I'd need to remind myself exactly how some program-counter relative indexed redirection+offset instruction worked. Almost made me miss the days of simple 8-bit loads, stores, compares and branches being all we had.

          > The future was ever-rising transistor counts and clock speeds - and their 68k architecture just couldn't go there

          I've always wanted to understand more about why Motorola abandoned the 68k architecture. I understand the broad factors cited in the Wikipedia article and on RetroStackExchange but I don't recall anyone citing supporting the addressing modes specifically (though it makes sense). I never programmed x86 assembler but my sense was that ISA also had its own oddball complexities. I never understood if there was some fundamental conceptual difference between the 68k and x86 ISAs that prevented one from being able to scale into the future while the other could. Would love any more info or links if you have them handy.

          • bell-cot 5 days ago

            Not seriously detailed, but try this:

            https://userpages.umbc.edu/~vijay/mashey.on.risc.html

            Another way to view it: Motorola did not have a senior 68k implementation engineer, who could look down the road and push back against cool- or easy-sounding ideas for making 68k programmers happy.

            (I once heard that, with virtual memory, a single 68040 instruction could generate 16 page faults. No, that'll never happen in the real world - but once the spec' is final, the CPU implementation team has to lay out a chip that can handle every situation correctly. And if you're pipelining a sequence of "tough" instructions against corner-case data - yeah, that can be factorial hell.)

            • mrandish 5 days ago

              Thanks for the link to that John Mashey post. It's meaty, so I'll need some time to chew through it :-)

              > Motorola did not have a senior 68k implementation engineer, who could look down the road...

              Yeah, this makes sense. Having started out as a complete newbie user and fanboy on 8-bit micros and then leaping to the brand new Amiga 1000 as my first 68k (because it was just so awesome), I've realized the perspective I had back then on Moto and the 68k line wasn't very complete. Reading some of the first-hand oral history from insiders in recent years shows that Motorola management made key strategic mistakes like not realizing the potential of what they had at various inflection points. The 68k was created by a new team with very little experience but which had some unusually brilliant people on it. That yielded a bold and expansive design with lots of deeply powerful aspects (like that addressing) but it may have been "too expansive" (or maybe over-complete) for what would be the first part in a long product line.

              Moto also didn't seem to realize they were in an all-out, high-stakes drag race to advance silicon fabrication faster and farther than their competitors. Moto was quite the laggard both in leading edge process technology and in perfecting their leading edge manufacturing reliability/predictability. This may have just been due to Moto being a huge conglomerate with lots of divergent businesses, like selling millions of 8-bit 6803 derivatives to General Motors every year. Whereas in the 1980s, Intel could still adopt a startup mindset and singular focus when they decided it was crucial. Maybe that's the over-arching meta here. Intel bet the company on figuring out some way to scale the x86 into the future and Moto treated the 68k CPUs like they were just another line of business.

              Personally, I now think of the 68k line as a beautiful swan born to distracted, mediocre parents who never really understood its potential, while the x86 was a bit of an ugly duckling born to committed, passionate, smart parents who were determined to find a way to make it successful. Maybe things would have turned out differently if the Moto board of directors knew that in 30 years the most valuable corporations in the world would all be based around silicon fabrication and IP. :-)

      • dougg3 6 days ago

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

userbinator 6 days ago

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 5 days ago

    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 5 days ago

    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 5 days ago

      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 5 days ago

        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 5 days ago

      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 5 days ago

      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 5 days ago

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

khazhoux 6 days ago

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.

submeta 5 days ago

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!

AnimalMuppet 6 days ago

WOW.

Amazing work. Thanks for the exposition.

(I miss the 68000 line. Those were such great chips...)

  • aruggirello 5 days ago

    I miss the assembly. It was so clear and logical, like great engineering should always be. You could browse through it and comprehend at a glance what was happening. By contrast x86 assembly looks like pure rubbish, with stuff that most of the time looks like clever hacks coming from a bad night's sleep, like subtracting a register from itself to get zero... c'mon!

    • snvzz 5 days ago

      Fortunately, sane assembly didn't die with 68k falling out of favor.

      MIPS carried the flag, and now RISC-V is even more readable.

      The dusk of x86 era is nigh.

    • jjuran 5 days ago

      I hate to disappoint you, but the canonical way to clear a 68K address register is `SUBA.L An,An`. My asm-coded replacement for the Metrowerks code resource runtime uses it:

      <https://github.com/jjuran/metamage_1/blob/master/mac/toolcha...>

      • aruggirello 3 days ago

        Oops, you're right. I forgot stuff like `MOVE.L #0,Dn` is restricted to data registers. So this must be the way to go, every time a NULL pointer is needed...

hajile 6 days ago

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

  • kevingadd 6 days ago

    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 5 days ago

    Apple was already dabbling in planned obsolesce.

1over137 6 days ago

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

  • grishka 5 days ago

    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 6 days ago

    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 6 days ago

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

      • saagarjha 5 days ago

        I think you’re underestimating people who do this.

    • snvzz 5 days ago

      Fortunately, today we've got pointer masking.

      (ARM and RISC-V do, anyway)

basementcat 6 days ago

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

  • dougg3 6 days ago

    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 6 days ago

    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 6 days ago

      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 6 days ago

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

        • ahefner 5 days ago

          Today by, way of your screenshot, I discover Asm-Pro. Just got into Amiga recently (by way of receiving one from my uncle's closet..) and have been meaning to backfill my shameful lack of 68k asm knowledge. Thanks!

          • mras0 4 days ago

            Good luck! As the sibling comment mentions, Asm-Pro is IMO a good choice these though it requires KSv2+ (so you can't use it on an stock A500). I mostly use vasm for "larger" stuff though (http://sun.hasenbraten.de/vasm/) and cross-compile (though it can run on Amiga as well) / test in an emulator.

            For a quick test/code loop nothing beats the Asm-* family - remember to save often and make backups though :)

          • snvzz 5 days ago

            Asm-Pro is a modern, maintained, feature-rich asmone derivative.

            Another modern/maintained one, focusing on low end 68000/68010 machines, is asmtwo.

            AsmOne was a very popular assembler at the time, and has seen a few derivatives. I remember an old one called trash'm'one.

        • mras0 4 days ago

          Update: a1=-1 is a bad choice of test value (0 is much better), as it won't show the issue if present! However, it doesn't change the outcome of the test on 060, but I would have noticed that 020 is affected as well (as also mentioned in the comments to the article).

      • dougg3 5 days ago

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

        XC68LC040RC25B

        02E23G QEDP9348D MALAYSIA

cluckindan 6 days ago

Time to fuzz the rest of the processor.

TapamN 6 days ago

Does Ken Shirriff have time to look at a 68030?

  • kens 5 days ago

    Unfortunately, not any time soon.

    • phibz 5 days ago

      That's great. Merely speak his name and he appears.

      Love the work you do Ken. Thank you.

fredoralive 5 days ago

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 5 days ago

    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.

ptek 5 days ago

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

Zardoz84 5 days ago

> By buying a Classic II and hacking the ROM...

I think that to be a perfect article, they should wrote :

By the magic of buying a Classic II and hacking the ROM...

Waterluvian 6 days ago

We’re confident this wasn’t some ROM DRM type feature?

  • rep_lodsb 5 days ago

    Wouln't have prevented hardware clones, since they would have used the same chip. It's fairly certain nobody at Apple knew about this instruction.

  • the-rc 6 days ago

    I was thinking maybe it's a way to save some bytes, like people used to do with undocumented opcodes in 6502 programs.

  • userbinator 6 days ago

    An "unintentional emulator trap" is how I would characterise it.

knuckleheadsmif 5 days ago

Perhaps it is ‘undocumented’ and is used as proof of someone copying the source code some portion of the source code.

Another possibility is that is a special institution in the chip specifically for Apple that again was used as a copy write detection or protection scheme.

kazinator 5 days ago

It's not miraculous. If the flaw had prevented the system from booting (in a way that has a high repro rate) they would have fixed it.

The system booted in spite of that undocumented instruction. When things work, you don't go looking for undocumented things that are contributing to the working state.

Millions of C programs work accidentally, in spite of undefined behavior. Nothing gets investigated until a compiler change triggers something.

  • dougg3 5 days ago

    Don't you think, as a Mac ROM developer, the chances of your software bug being accidentally fixed by the CPU through an undocumented instruction are pretty low? That's what I was getting at when I wrote that.

    Of course they would have fixed it if it had prevented the system from booting, I even said that in the article. I still think the odds of what happened here were pretty small. That's what I meant by miraculous.