Magic Architecture Overview - retrotruestory/M1DEV GitHub Wiki

Magic Architecture Overview

The basic architecture is one-address, with 8 and 16-bit operations. Each process will see up to 128K bytes of address space as 32 2K data pages and 32 2K code pages mapped into a 22-bit physical address space (actually, it is effectively a 23-bit physical address space when you consider device space). Each process has an associated page table consisting of 64 16-bit entries in dedicated page table memory, and located by a per-process page table base pointer. I/O is memory mapped. Support exists for external interrupts and DMA. Finally, bit and byte order are big-endian, because as all right-thinking people know, little-endianness is the mark of Satan.

An emphasis was placed on efficient addressing for traditional languages, so there is a fairly rich (though unfortunately non-orthogonal) set of addressing modes and address generation instructions. I also am a fan of atomic "compare and branch" and "test and branch" instructions, so I've devoted a large chunk of the opcode space to them. By limiting myself to 8-bit opcodes, I've pretty much had to abandon a clean encoding. This means that I'll be using a lot more microcode than most projects of this sort that I've seen. Oh well.

I should note that I wasn't shooting for elegence in this architectural design, but rather utility within my project goals and constraints. In particular, I designed with code generation output of a non-optimizing C compiler in mind. This is reflected in the addressing modes, as well as the way I strayed from a pure one-address model. My first version of Magic was a pure one-address accumulator design. However, when I began doing hand-compilation of C code into Magic assembly I eventually added the "B" register and "C" registers for efficiency. "B" is almost an accumulator, but not quite as capable as "A", and "C" is a count register for repeating operations.

One of the more interesting outcomes of my iterative Magic architecture design process is that I now have a much greater understanding of why the X86 architecture is the way it is (i.e. - covered with warts). For Magic, I was by intent not taking a long view. I designed for the first (and probably only) implementation and was very conscious of my immediate technology constraints (i.e. TTL, wire-wrap, limited amount of time my wife would let me play with this stuff, etc.). My goal was functionality of the first implementation. This, I imagine, was probably not unlike the goals of the early X86 designers battling in the marketplace with other microprocessor designs for survival.

What made this so clear to me was the number of times during Magic design that I realized that with just a few wires or gates, I could add something potentially useful. This "useful" feature, though, would be useful in the same sense that you could create useful storage in your house by nailing a few pieces of plywood together in the back yard. Useful space - but at the cost of degrading the overall appearance (and future resale value) of your home.

For example, at one point I realized that with just a trivial amount of extra hardware I could create a generic "repeat" instruction which would use the C (count) register to repeatedly execute the following instruction. X86 has such an instruction - the repeat prefix. When you consider the microprocessor landscape when the 8086 was introduced, the repeat prefix added real market value. Architecturally in the long run, though, it is a vile abomination that costs far more to retain backwards compatibility in modern X86 implementations than any value it might bring. On the other hand, in a competitive marketplace those ugly but useful useful features today may well be what allow you to survive to see tommorow. The key is finding the balance.