(Written in 1987)
Among the high-end microprocessors currently available, two of the most widely used are the Intel 8088 and the Motorola MC68000 (usually abbreviated to just 68000). Both are members of small families of microprocessors. The 8088 family includes the 8086 and the more powerful 80286 and 80386; it can trace its lineage through the earlier 8080 and 8008 all the way back to the first microprocessor, the 4004. Since the 8088 is identical to the 8086 in most respects, and since the 8086 is considered to be the “parent” of the 8088 chip’s microprocessor family, I will refer to the 8086 in this paper; however, everything said about it also applies to the more widely-used 8088. The 68000 family contains, besides the 68000 itself, the 68008, the 68010, the 68012, and the 68020. These processors generally have additional registers and operation codes beyond what the 68000 has; however, I will not be dealing with them.
The rivalry between the 8088-based IBM PC family and the 68000-based Apple Macintosh series sets one wondering how the microprocessors they use perform. I will be examining these two microprocessors from a programmer’s point of view; the only references to hardware characteristics will be those that are important to most assembly-language programmers using the chips.
II. User Registers
The registers that are available to the assembly-language programmer are where some of the most obvious major differences between the 8086 and the 68000 can be found. The 8086 contains 14 16-bit registers, four of which can also be used as two eight-bit registers. All of the 8086’s registers are used for specific functions, although several can be used for general storage as well.
The four “general purpose” registers in the 8086 are called AX, BX, CX, and DX to indicate their “eXtended” (16-bit) length; their two halves can also be referenced as eight-bit registers called AH and AL, BH and BL, CH and CL, and DH and DL, for the High and Low halves of the 16-bit value. The AX register is the primary accumulator; many instructions, such as MOV and AND, have special short forms which specifically deal with the AX register (or the AL register, for eight-bit operations). The BX, CX, and DX registers can also be used as eight- or 16-bit accumulators, but they have unique functions to which they are dedicated in some situations: BX is used as a base pointer in certain addressing modes, CX is used as a loop counter, and DX is used to hold I/O port addresses.
Besides the four “general purpose” registers, the 8086 has four 16-bit index registers, called SP (the stack pointer), BP (the base pointer, used similarly to BX in some addressing modes), SI (source index for string operations), and DI (destination index for string operations). It also has a 16-bit PC register (the program counter) and four 16-bit “segment” registers, called CS (code segment), DS (data segment), SS (stack segment), and ES (extra segment). I will deal more fully with the segment registers in the Addressing Modes section of the paper. Finally, the 8086 has a 16-bit Status register, also called the Flags register or the Program Status Word (PSW). This register contains nine one-bit flags which keep track of the results of arithmetic operations and certain operating modes; some of the flags will be discussed in detail later on.
The 68000 contains 18 32-bit registers and one 16-bit register, giving it considerably more internal storage space than the 8086. In addition, the 68000 registers come much closer to being “general purpose” than do those of the 8086. The 68000 has eight 32-bit “data” registers, named D0 through D7. They can be used to hold 32-, 16-, or 8-bit values, depending on the instructions used to manipulate them. The 16- and 8-bit instructions affect only the appropriate number of lower order bits in the registers. The data registers can all be used for all arithmetic operations, and most other operations as well.
The 68000 also has seven 32-bit general-purpose “address” registers, named A0 through A6. These registers can also be used for 16-, but not 8-bit operations. They can be used in some arithmetic operations, but they are usually used to hold the addresses of operands which will be manipulated.
The 68000 also has a 32-bit program counter (PC) and two 32-bit stack pointers, called USP (user stack pointer) or A7, used in “user mode,” and SSP (system stack pointer) or A7′, used in “supervisor mode” (see the Other Differences section below for a more complete description of the 68000’s operation modes). However, the USP register can be copied to or from any of the address registers during supervisor mode by using the MOVE instruction. Finally, the 68000 has a 16-bit SR (Status Register) register, of which the lower eight bits are available in user mode and are called the CCR (Condition Code Register). The CCR essentially holds the results of arithmetic calculations, as with the 8086 Flags register; the rest of the 68000 SR holds other operation mode flags.
In summary, while some of the registers in these two processors share similar functions, e.g. the stack pointers, program counters, and status registers, their “general purpose” registers differ greatly. The 68000 has more of them (15 vs. six, including SI and DI), and they can hold larger values and be used for a greater variety of purposes than those on the 8086.
III. Addressing Modes
The 8086 and the 68000 also differ in how they find the operands for their machine language opcodes, although their addressing modes look less similar at first glance than they actually are. The 8086, as mentioned before, has a 16-bit program counter and four 16-bit “segment registers.” Depending on the operation being performed, the value of one of the segment registers, shifted left by four bits (in effect multiplying it by 16), is added to an “effective address,” calculated in one of the ways described below, resulting in a 20-bit absolute address. This means that the 8086 can directly address one megabyte of memory. For fetching instructions, the Code Segment register is used; for stack operations, the Stack Segment register; for certain string operations, the Extra segment register; and for most other operations, the Data Segment register. The Data Segment default can be overridden to use another segment register in most cases.
The 8086 can access its operands in one of twelve general ways, called addressing modes. They are:
1. Operand is in opcode
2. Operand is in register specified in opcode
3. Address of operand is in DS+16 bit signed displacement following opcode
Direct, Indexed Mode:
4. Address of operand is in DS+DI or SI+8 or 16 bit signed displacement
5. Address of operand is in DS+DI or SI
Base-Relative, Direct Mode:
6. Address of operand is in DS+BX
7. Address of operand is in DS+BX+8 or 16 bit signed displacement
Base-Relative, Direct, Indexed Mode:
8. Address of operand is in DS+BX+DI or SI+8 or 16 bit signed displacement
Base-Relative, Implied Mode:
9. Address of operand is in DS+BX+DI or SI
Base-Relative, Direct Stack Mode:
10. Address of operand is in SS+BP+8 or 16 bit signed displacement
Base-Relative, Direct, Indexed, Stack Mode:
11. Address of operand is in SS+BP+DI or SI+8 or 16 bit signed displacement
Base-Relative, Implied Stack Mode:
12. Address of operand is in SS+BP+DI or SI
Note that there are actually more than twelve combinations of registers used for addressing, because of the many places where either DI or SI can be used. However, the above are the twelve patterns that the 8086 uses to compute effective addresses. Note the dedicated use of specific registers for each addressing mode.
The 68000, in contrast, has no need for the segment registers because of its 32-bit program counter. In fact, the program counter actually allows larger addresses than the chip’s hardware can address: the 68000’s address bus has only 24 bits. Still, that is enough to handle 16 megabytes, compared with the 8086’s one megabyte address space. In addition, the specialized register uses of the 8086 are largely missing in the 68000; the main distinction is between the seven address registers and the eight data registers. Any data register can be used for any purpose that any other data register can; likewise for the address registers.
Here are the available addressing modes in the 68000:
1. Operand is in opcode
2. Operand is in register specified in opcode
3. Address of operand is in opcode
Address Register Indirect Mode:
4. Address of operand is in An (n=0-6)
5. Address of operand is in An; predecrement An (n=0-6)
6. Address of operand is in An; postincrement An (n=0-6)
Address Register Indirect Mode/Program Counter Indirect Mode:
7. Address of operand is in An or PC+16 bit signed displacement (n=0-6)
8. Address of operand is in An or PC+Xm+16 or 32 bit signed displacement (n=0-6; Xm=A0-A6 or D0-D7)
Again, there are actually many more combinations of registers than there are access patterns, i.e. addressing modes. The 68000’s addressing modes tend to each use fewer registers than the 8086’s do; however, most of that difference is due to the 8086’s use of segment registers. Once they are taken into account, the addressing modes of the two processors are remarkably similar; the largest difference between them is that the 68000 modes can be used with many more registers than can the 8086 modes. Aside from that, the 8086 lacks predecrement and postincrement modes, a minor omission.
IV. Instruction Sets
Many of the differences between the instruction sets of the 8086 and the 68000 have already been dealt with in the section on User Registers and the section on Addressing Modes. Differences in those areas lead directly to readily evident differences in the instruction sets, for example the sizes of the values saved on the stack by the CALL/JSR instructions. Below I will discuss notable differences not covered earlier.
The standard assembler mnemonics for the 8086 and the 68000 differ considerably. Although both use capital letters, the 8086 uses the order “destination, source” in its operand field while the 68000 uses the reverse. Also, instructions which perform essentially the same function are given different names by the two assemblers. For example, the 8086 CALL (subroutine) performs analogously to the 68000 JSR (Jump to SubRoutine), and the 8086 SBB (SuBtract with Borrow) performs the same function as the 68000 SUBX (SUBtract with eXtend). Other mnemonics have smaller differences, for example: MOV on the 8086 is MOVE on the 68000 (and as noted above, the order of operands is reversed).
“String operations” means the instructions used to implement operations on consecutive memory elements, usually with some kind of automation or semi-automation. The 8086 has special instructions included for just this purpose. They automatically perform an operation (moving or comparing data), then increment or decrement (depending on the state of the Direction flag in the Flags register) index registers (SI and/or DI) to point to the next element. If they are preceded by the REP instruction, they also decrement and test a counter (CX); if it is not zero, they keep looping. Perhaps the weakest aspect of these instructions, called MOVS, LODS, STOS, SCAS and CMPS, is that they inflexibly use specific registers for each aspect of their operation. Otherwise, they are faster and shorter than the corresponding normal loops would be.
The 68000 does not have dedicated string operation instructions. Instead, it has the predecrement and postincrement addressing modes, mentioned earlier, which can be used with any of the address registers. These are used with the standard conditional branch instructions. Thus although the 68000 is more flexible in its implementation of string functions, it cannot have loops which are as tight as those the 8086 can.
Stack operations are an area where the 68000 is clearly more versatile than the 8086. The 8086 uses PUSH and POP instructions which can only move 16-bit values onto or off of the system stack. To implement more than one stack, the programmer would need to use MOV along with DEC (DECrement value) and INC (INCrement value) on the register or memory location chosen as the second stack’s pointer. The 68000, by contrast, handles stack operations by using the predecrement (for push) and postincrement (for pop) addressing modes on anyone of the address registers, including A7, the USP register. The 68000 can therefore not only move 16-bit values onto or off of the stack, but it can perform other operations such as AND and CLR on stack values, and the MOVEM (MOVE Multiple registers) instruction can be used to good advantage to quickly push or pop many registers at once to or from the stack. The 68000 can also do 32- and 8-bit stack operations. However, register A7 won’t accept 8-bit operations, to guarantee that words it contains will start on even addresses (see the section on Other Differences below). The 68000 also has two instructions, LINK and UNLK, which allocate and deallocate, respectively, a stack frame for temporary storage. The 8086 has a form of the RET instruction which adds a given value to the stack pointer before pulling the address off of the stack, but it is most useful for cleaning up at the end of subroutines which are passed values on the stack.
In the area of integer arithmetic, the 8086 is slightly more versatile than the 68000. It can add and subtract 8 or 16 bit values, with or without adding in the carry; it can multiply two 8-bit or two 16-bit values, signed or unsigned; and it can divide 16 by 8 bit or 32 by 16 bit values, signed or unsigned. The 68000 can add and subtract 32 bit values in addition to the operations that the 8086 can do, but it can’t do the 8*8 bit multiplication or the 16/8 bit division that the 8086 can. Of course, those operations are easy to do with the multiplication and division instructions that it does have, simply by clearing the upper parts of the affected registers beforehand.
Besides the regular binary arithmetic operations, the 8086 has instructions to adjust the result so that it can do arithmetic on BCD and ASCII values, as well. The 68000 has special instructions for doing BCD arithmetic, but it has nothing equivalent to the 8086’s ASCII adjustment instructions.
Although the interrupt handling facilities of the 8086 and the 68000 differ considerably, if we limit ourselves to looking at the software interrupt instructions we find that they are very similar. The 8086 INT instruction causes a jump to any of 224 addresses in low memory; there are actually 256 available, but 32 of them are reserved for other purposes. INTO uses one of those reserved addresses to cause an interrupt if the overflow flag in the Flags register is set. Both INT and INTO push the Flags, CS and PC registers onto the stack before jumping. The IRET instruction returns from an interrupt, restoring the values of Flags, CS and PC from the stack.
The 68000 has similar instructions with different names: TRAP causes a jump to any of 192 addresses in low memory (the other 64 are reserved) and TRAPV causes an interrupt if the overflow flag is set, both of them saving the values of PC and SR on the stack. RETE is the 68000 equivalent of IRET. Note that although the 68000 has 8 priority levels for interrupts, compared to the 8086’s one level, software interrupts on the 68000 always operate at the same priority, so software interrupts are really no different from those on the 8086 in that respect. There is one small difference, however: interrupts on the 68000 cause the processor to enter supervisor mode, which the 8086 does not have.
In looking over the instruction sets of the 8086 and the 68000, I found a few other oddities I would like to discuss. The first instructions in the alphabetical lists for both processors made me wonder if someone was playing a joke on me: AAA and ABCD on the 8086 and 68000, respectively. The abbreviations stand for Adjust result of ASCII Addition and Add Binary Coded Decimal, but they sure look to me like they were contrived to be cute. That’s okay. It’s fun to see something like that sneak into general production.
The 8086’s assorted odd instructions include LAHF and SAHF, which load and save the lower byte of the Flags register into the AH register. That seems rather specialized to me. There is also JCXZ, which Jumps if the CX register is Zero. It is the only instruction in the set which tests a full register instead of just a bit or two, and it reinforces the specialized use of CX as a counter. Last, there is the XLAT instruction, which moves the value pointed to by AL + BX into AL. It is intended for use in table lookups, but it seems very limited and specialized to me: there is no choice as to which registers may be used. However, in that respect, it is consistent with what seems to be the general register use philosophy of the 8086.
The 68000 also contains some unusual instructions. In addition to the standard bit test instruction, BTST, it has BCHG, BCLR, and BSET, which combine two operations: they test a bit and then complement, clear, or set it respectively. It also has instructions for dealing with variable-length bit fields, with the mind-numbing mnemonics BFEXTU (Bit Field EXTract Unsigned), BFFFO (Bit Field Find First One), BFINS (Bit Field INSert), BFSET (Bit Field SET), and BFTST (Bit Field TeST). I never thought I’d see three F’s in a row in an assembly language instruction!
Another 68000 instruction which combines two operations is CAS2, which Compares And Swaps two values. It is specifically useful in a multiprocessor environment, where if the operation was done in several steps another processor could grab control of the bus in the middle of the process and corrupt the operation. CHK is an unusual instruction which checks if the value in a data register is between 0 and a given boundary. Unlike most comparison instructions, if the value is outside of those limits, it causes an interrupt (a trap) rather than setting a flag. Finally, there is RTR, which pulls not only the PC but also the CCR from the stack. It is like RTE, which can only be used in privileged mode, except that it does not restore the high byte of the SR.
Overall, in the majority of areas the 68000 has a more powerful and flexible instruction set than the 8086. That difference is due largely to its having more and larger registers and allowing more versatility in their use.
V. Other Differences
There are a few other differences which will be important to most assembly language programmers on these machines but which do not fit into any of the above categories. For example, the 8086 allows words (16-bits) to start at any address, while the 68000 requires that words and long words (32-bits) start at even addresses. Also, the two processors use opposite orders of storage for multibyte numbers: the 8086 stores the lowest valued byte first, the 68000 the highest. And lastly, the 8086, like most microprocessors, has only one operating mode. The 68000, designed more with multi-user environments in mind, has two operating modes: user mode, which is the normal one, and supervisor mode, which is able to use certain privileged instructions affecting the Status Register and I/O devices, like RTE and RESET. As mentioned above, interrupts cause the 68000 to enter supervisor mode. Having two different modes is useful in preventing ordinary users from changing data belonging to the system or to other users, or even crashing the system.
Both the 8086 and the 68000, and their related families of microprocessors, contain a powerful assortment of registers, addressing modes, and instructions. Both are able to perform high-precision arithmetic, allow large address spaces, and can access their operands in many different ways. The 68000, however, has more and larger registers, and allows much greater versatility in their use. In addition, it has a privileged operating mode, which the 8086 lacks, to protect the system from disaster in complex operating environments. From the programmer’s point of view, then, the 68000 seems to be a more desirable chip to use than the 8086.
All technical data on the 8086 is from:
Rector, Russell, and Alexy, George. The 8086 Book. Berkeley, California: Osborne/McGraw-Hill, 1980.
All technical data on the 68000 is from:
Leventhal, Lance A.; Hawkins, Doug; Kane, Gerry; and Cramer, William D. 68000 Assembly Language Programming, Second Edition. Berkeley, California: Osborne/McGraw-Hill, 1986.