May 1983 © BYTE Publications Inc
Thomas W. Starnes
Motorola Inc., Microprocessor Division
3501 Ed Bluestein Blvd.
Austin, TX 78721
Last month, in part 1, I discussed the design philosophy behind the Motorola MC68000, a powerful 16-bit processor with multiple 32-bit registers. This month I'll describe the data-movement, arithmetic, and logic instructions of the MC68000. A thorough reading of the MC68000's user's guide (available from many computer bookstores and Motorola distributors) will give you all the details of each instruction's operation, but a look at the general categories of instructions, a discussion of why certain design decisions were made, and mention of some special capabilities of the instructions will give you insight into the power of this instruction set.
Photo 1: The MC68000 microprocessor chip, which contains more than 68,000 transistors, is 246 by 281 mils (6.24 by 7.14 mm) in size. This photo shows the location of the major functions of the chip. "Int. Log." stands for "Interrupt Logic"; "A0 Mux Control", for "Microcode A0 Multiplexer Control"; and "FC Log.", for "Function Code Logic." The labels "µROM" and "NROM" indicate two areas of microcode. "Trap and Ill. Inst. PLA" stands for "Trap and Illegal Instruction Programmable Logic Array"; "IRD Reg.", for "Instruction Register Decode Register"; and "ALU Control", for "Arithmetic and Logic Unit Control." The Data Execution Unit houses the main functions of the arithmetic and logic unit, while the two Address Execution Units perform the arithmetic associated with the calculation of an address.
Before I get into the instruction groups, let's first look at how assembly-language instructions are written. Table 1 illustrates a common instruction format and the choices that can be made within it. First, of course, you can pick one of several microprocessor instructions -- for example, an addition (ADD), comparison (CMP), arithmetic shift left (ASL), or data move (MOV). If the instruction is one that handles data, you can, with the MC68000, select one of three data sizes: 8, 16, or 32 bits. This selection is made by following the mnemonic with a period and either a "B", "W", or "L", for byte, word, or long word; if no size is specified, the assembler will assume a 16-bit operation.
Table 1: General format for MC68000 instructions.
Instruction format is: mnemonic.size source,destination Examples: ADD.L D1,D2 MOVE.B #15,-1(A0) ADD D1,D2 (size assumed to be ".W") BGE LOOP1 (only one argument) RTS (no arguments) Explanations: mnemonic = instruction abbreviation (ADD, CMP, MULS, etc.) size (optional) = operand size: .B means byte data (8 bits) .W means word data (16 bits; default size) .L means long word data (32 bits) source (optional) = source operand addressing mode destination (optional) = destination operand addressing mode
On a data operation, you need to make one or two more decisions, i.e., which addressing mode to use for the one or two operands the instruction requires. (See the text box on data organization on page 354 for more details.) Typically, you can select one of 14 modes; within most of these modes, one of eight address registers is selected. On many operations, you need to select a second addressing mode; this usually involves selection of one of eight data registers, but for the data-movement instruction, any addressing mode can be selected.
All MC68000 instructions are fully defined with 16 bits of op code. (Op code is short for operation code; it is the pattern of bits that a microprocessor interprets as a specific machine-language instruction executable by it.) Depending on the instruction or the addressing mode(s) selected, additional 16-bit extension words may follow the op code. These extension words provide additional addressing information and may make the total instruction length as long as 10 bytes. Because the instruction is always lengthened by multiples of 16 bits, you can ensure that instructions always begin on even-byte boundaries; because of the way the MC68000 fetches 16-bit quantities from memory, this placement of instructions increases the speed of program execution.
By far, the most common operation in any processor application is the movement of data. Other microprocessors move data with LOAD, STORE, PUSH, PULL, POP, and input/output (I/O) instructions. When you boil it all down, each instruction simply moves data from one location to another. So why not call them all MOVE? Simplicity of expression is a fundamental theme throughout the MC68000's instruction set: all similar operations should perform similarly in a number of respects. For example, if you can use an ADD operation with two 32-bit quantities, you should be able to use an add-with-carry operation with two 32-bit quantities. If you can select from 14 addressing modes to use an ADD operation, you should be able to select from 14 addressing modes to use SUB (subtract). If certain status register codes are modified to reflect the results of an ADD, the same codes should also be modified when a SUB or NEG (negate) instruction is performed.
With this philosophy in mind, all of the old LOAD, STORE, PUSH, PULL, POP, and I/O instructions from other microprocessors were rolled into one very powerful and flexible MOVE instruction in the MC68000. Let's look at just what this one instruction can do.
The MOVE instruction can move 8-, 16-, or 32-bit data from practically any location to practically any other. And a wide selection of addressing modes and registers for both the source and the destination should cover about any way you want to find the operands. Table 2 lists the different combinations of addressing modes available on both the MC68000 and the Intel 8086 families. Let's look at what some of the MC68000 addressing-mode combinations allow you to do.
Table 2: Addressing modes available to the Motorola MC68000 and the Intel 8086. The information at the intersection of a row and a column indicates the availability of that source/destination addressing-mode combination for each microprocessor.
destination Dn An (An) (An)+ -(An) d16(An) d8(An,Xn) Abs.W Abs.L ------------------------------------------------------------------------- #options 8 8 8 8 8 8 128 N/A N/A ==================================================================================== source | #options | Dn | 8 | MI MI MI M M MI MI MI M An | 8 | MI MI MI M M MI MI MI M (An) | 8 | MI MI MI M M M M M M (An)+ | 8 | M M M M M M M M M -(An) | 8 | M M M M M M M M M d16(An) | 8 | MI MI M M M M M M M d8(An,Xn | 128 | MI MI M M M M M M M Abs.W | N/A | MI MI M M M M M M M Abs.L | N/A | M M M M M M M M M d16(PC) | 1 | M M M M M M M M M d8(PC,Xn) | 16 | M M M M M M M M M Immediate | 1 | M M M M M M M M M
Notes:
Certainly, you can copy data between registers, but you can also copy data to or from a register to memory using any of the memory-addressing modes. Most microprocessors allow the programmer to transfer data on the top of a stack to or from a register only. What if the data is really needed elsewhere in memory? You must run a second instruction to make the second move and use a register for a temporary holding space. The MC68000 allows you to move top-of-stack data to or from any register, another stack, a queue, any memory location, or any I/O location, all in one smooth motion. And why shouldn't you be able to? An added advantage of the MC68000 comes from its ability to use any one of the eight address registers as a stack pointer; this allows you to build as many as eight different stacks without having to swap out registers.
You can also do direct memory-to-memory moves. There are 10 different memory-addressing modes to select from for the source operand and seven for the destination. Also, keep in mind that each addressing mode can use any of the eight address registers, further increasing the versatility of these move instructions. (Actually, in one mode, you have 16 registers to choose from -- eight address and eight data registers.)
Just how many different ways are there to move general data in the MC68000? When you couple all of the combinations allowed with the selection of registers available, there are 34,888 different ways, and each one can be used for 8-, 16-, or 32-bit data. That ought to solve most programmers' data-shuffling problems!
The remaining data-movement instructions include SWAP, for instance, which exchanges the contents of any two data and/or address registers. You can read or modify the status register codes with a MOVE SR (Status Register) instruction.
The MC68000 was designed to interface directly to the MC6800 line of 8-bit peripherals so that all the existing peripheral circuits could easily be used on the MC68000. (Many of the 8-bit peripherals provide very useful functions that would need to be included in a 16-bit system.) To bring the best of the 8-bit peripheral world into the universe of 16-bit software, designers included a special MOVEP (Move Peripheral) instruction in the MC68000. Here's how and why it works.
Frequently, you must set up registers to ready a peripheral for operation. You need to connect an 8-bit peripheral to either the upper or lower half of the 16-bit-wide data bus. This means that the registers connected to a peripheral appear within the MC68000 memory address space as successive-even or successive-odd addresses. The MOVEP instruction will move either 16 or 32 bits of a given data register out to memory in 8-bit chunks, starting at a given location (see figure 1); addresses for each successive byte are incremented by two, not by the one that the normal MOVE uses. This allows the 2 or 4 bytes being transferred to be loaded into the proper peripheral port addresses. Thus, you can load as many as four 8-bit registers in one simple instruction. The MOVEP instruction is bidirectional, so that the registers can be either loaded or read.
Figure 1: Moving data from a 32-bit register to memory using the MOVEP instruction. Bytes from the register are stored in every other memory byte. The instruction takes 24 clock cycles to execute.
LONG TRANSFER TO/FROM AN EVEN ADDRESS. 31 24 23 16 15 8 7 0 ----------------------------------------------- REGISTER: | HIGH-ORDER| MID-UPPER | MID-LOWER | LOW-ORDER | ----------------------------------------------- BYTE BYTE ADDRESS 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ADDRESS ----------------------------------------------- MEMORY 2N | HIGH-ORDER | | 2N+1 ----------------------------------------------- 2N+2 | MID-UPPER | | 2N+3 ----------------------------------------------- 2N+4 | MID-LOWER | | 2N+5 ----------------------------------------------- 2N+6 | LOW-ORDER | | 2N+7 -----------------------------------------------
Two special types of the MOVE instruction are the MOVEQ (Move Quick) and the MOVEM (Move Multiple Register). Often a register is used as a counter or a constant, with values that are typically rather small. The MOVEQ instruction makes it fast and easy to initialize a register to such values. MOVEQ will take any signed 8-bit immediate value between -128 and 127, extend its sign bit so that it will be correctly interpreted as a 32-bit number, and load it into one of the data registers. The op code for MOVEQ includes the 8-bit immediate value; this means the microprocessor can perform the operation very quickly. Because the small immediate value is part of the MOVEQ op code itself, the instruction is classified as a separate addressing mode of the MC68000 called the "quick immediate" addressing mode.
It is common in machine-language programming to have to save the contents of various on-chip registers, use the registers for some other purpose, and then restore their former contents. This happens when you are beginning or ending a subroutine, executing an interrupt handler, changing tasks, or calling the operating system. The MC68000 has a very handy instruction that makes this a fast, efficient operation. The MOVEM instruction will take any combination (or all) of the 16 data and address registers and move them either to or from memory in an organized manner. These registers can be transferred to or from any stack or to a specific location in memory. They are put in memory and taken from memory in reverse order to ensure that each register receives its proper contents. An option of the MOVEM instruction is that either the lower 16 bits of the registers or the entire 32-bit registers can be transferred. An example of this instruction is:
MOVEM.L D0/D4-D7/A4/A5,40(A6)
which would save the registers as shown in figure 2. (The instruction will save registers D0, D4 through D7, A4, and A5 into memory starting at the location pointed to by the value in register A6 plus the value 28 hexadecimal.) The list of registers to be transferred is compactly encoded in a 16-bit value that follows the MOVEM op-code word -- an "on" bit indicates the associated register is to be transferred. Not only is the MOVEM instruction both compact and useful, it is also as fast as possible for the number of bytes of information that must be transferred.
Figure 2: Pushing multiple registers to memory with the MOVEM instruction. This instruction offers a very fast way to store selected registers in memory and, later, restore them properly. This figure shows the storage of registers D0, D4 through D7, A4, and A5 to location 91C028 hexadecimal. The instruction itself is MOVEM.L D0/D4-D7/A4/A5,40(A6). (Remember that 40 decimal equals 28 hexadecimal.) The instruction takes 58 clock cycles to execute.
----------------- REGISTER A6 | 0 0 9 1 C 0 0 0 | ----------------- ADDRESS MEMORY CONTENTS OF A6 91C000 (HEXADECIMAL) (ORGANIZED + DISPLACEMENT + 28 AS WORDS) ---------------- ------ --------- STARTING ADDRESS 91C028 -----------> 91C028 | D0-HIGH | |- -| 2A | D0-LOW | |---------| 2C | D4-HIGH | |- -| 2E | D4-LOW | |---------| 30 | D5-HIGH | |- -| 32 | D5-LOW | |---------| 34 | D6-HIGH | |- -| 36 | D6-LOW | |---------| 38 | D7-HIGH | |- -| 3A | D7-LOW | |---------| 3C | A4-HIGH | |- -| 3E | A4-LOW | |---------| 40 | A5-HIGH | |- -| 91C042 | A5-LOW | ---------
Arithmetic operations are key instructions in a microprocessor because they tend to be the ones that do the bulk of the work. The arithmetic and logic instructions allow programmers to write code exactly as they desire without having to rearrange data, gather more data, or do things in an unnatural order. As with so many other characteristics of the MC68000, the design of the arithmetic and logic instructions is very orthogonal -- more so than for any previous microprocessor. Orthogonality can be defined as the ability of any allowed operation to use any resource in any way that any other operation may.
The arithmetic and logic instructions are very similar in the way they function, the way the condition codes are affected as a result, and the selection of addressing modes, registers, and operands available to them. The advantage of this is that, when coding, the programmer has only one uniform set of rules to remember. Older microprocessor designs forced programmers to have different sets of rules for even similar instructions, which decreased programmers' productivity by forcing them to recall and use correctly large amounts of essentially arbitrary information.
All of the dual-operand arithmetic instructions are true "one-and-a-half" address operations (i.e., one operand can be specified as a memory address, but the other must be an internal register -- the result overwrites one operand). Thus, you can add any register to any register, a constant to any register, the top of a stack to any register, a value buried in a stack to any register, a table entry to any register, an input from an I/O device to any register, or any memory location to any register. Or, because the order may be reversed, you can add any register to any of the same (except that you can't add to a constant, and you can't use the program-counter relative addressing mode to specify a destination). Also, remember that any of these instructions can occur with 8-, 16-, or 32-bit data.
Let's look at the types of arithmetic instructions that are available. Add (ADD), subtract (SUB), and compare (CMP) instructions are general two-operand instructions. ADDX and SUBX are used to work on numbers longer than 32 bits (the X condition bit in the MC68000 performs a function similar to the one the carry bit performs in most microprocessors). Two multiply and divide instructions are available: signed (MULS and DIVS) for single-precision instructions and unsigned (MULU and DIVU) for multiple-precision instructions.
The negate (NEG) and clear (CLR) instructions require only a single operand, and you can use a NEGX to negate multiple-precision values. To blend mixed sizes of data, the MC68000 provides a sign-extend instruction (EXT), while a TST (test) instruction is used to check for positive, negative, or zero conditions. A special instruction, the indivisible test-and-set (TAS), provides software synchronization in multimicroprocessor operations.
One variation on the ADD instruction enables the MC68000 to overcome a common limitation of other microprocessors. The normal "one-and-a-half" address design of most processors makes it difficult to use constant (immediate) values with anything but registers. The MC68000 overcomes this with the ADDI instruction, which allows a byte, word, or long-word immediate value to also be added to an operand in memory using any legal destination-memory addressing mode.
The MC68000 has no increment or decrement instructions. Why? Remember, the idea is to treat all similar instructions the same. An increment instruction adds 1 to a quantity and is often used to step to the next element in a table of byte-wide values. But the MC68000 programmer will often be manipulating 16- and 32-bit data, which require increments of 2 and 4, respectively, to the table address. The design team wanted to generalize the increment and decrement instructions to make them useful with all data sizes yet still retain the speed associated with an instruction that does not have to fetch an immediate argument. To solve these problems, and then some, the designers gave the MC68000 "add quick" (ADDQ) and "subtract quick" (SUBQ) instructions, which allow any number from 1 to 8 to be added to or subtracted from any register or any memory location. The instructions accomplish this in the shortest possible time by using 3 bits within the 16-bit op code to hold the increment or decrement amount (in this scheme, the bits 000 indicate an operand value of 8, not 0). Then, not only can you quickly and easily change an address pointer by 1, 2, or 4 for 8-, 16-, or 32-bit data, but you can also change counters by 3, 5, 6, 7, or 8. And the effect is identical to that using the standard ADDI instruction -- even the status register codes are all the same.
Some odds and ends of arithmetic instructions include sign-extend (EXT), clear (CLR), and test (TST) instructions. Since three different sizes of data can be used in the MC68000, there should be a convenient way of changing size. If you want to move only part of a datum (for example, the bottom 16 bits of a 32-bit register), you need only use a MOVE instruction of the proper data size. If, however, you want to convert a datum to a larger-sized two's-complement value (for example, making a 16-bit value into a 32-bit expression), you need a special instruction. The EXT instruction will take an 8-bit or a 16-bit datum and duplicate its uppermost bit position through the higher portions of any data register in order to convert the datum to 16 or 32 bits wide, respectively. CLR simply loads a set of 0s into the destination. TST sets the negative and zero condition bits (discussed in the next section) according to the nature of the given operand.
What if you are dealing with binary integers that require more than 32 bits for expression? Say you want to add two 128-bit (16-byte) numbers. If both of these numbers were in the MC68000 registers, all eight data registers would be in use. More likely, the two values would be in 16 consecutive bytes of memory, starting with the most significant byte of data. The normal procedure to add two such numbers is to add the two least significant bytes, remember the carry, go to the previous bytes and add them, remember the carry, and so on. This sequence of operations is handled neatly in the MC68000 by the predecrement address-register deferred mode, which uses the notation "-(An)". Use two address registers to point to the byte just past each operand. Each execution of an ADDX -(Am),-(An) instruction will decrement the values in the Am and An registers (m and n stand for numbers between 0 and 7), then add the two numbers pointed to by those registers. By putting this single instruction in a loop, you can quickly create the code needed to operate on multiple-precision numbers.
Let's detour for a second to discuss the status register of the M68000 (see figure 3). It contains the standard carry (C), overflow (V), zero (Z), and negative (N) bits found in other microprocessors. It also has a status-register bit not found on other microprocessors, the X (or extend) bit. This bit was created to eliminate confusion caused by traditional overuse of the carry bit.
Figure 3: The bits of the MC68000 status register (SR).
MC68000 STATUS REGISTER SYSTEM BYTE USER BYTE ________|_______ ______|______ / \ / \ 15 13 10 8 7 4 0 ---------------------------------- |T| |S| | |I2|I1|I0| | | |X|N|Z|V|C| ---------------------------------- | | \______/ | | | | | TRACE MODE ---- | | | | | | | SUPERVISORY -------- | | | | | | INTERRUPT MASK ----------------- | | | | | EXTEND ----------------------------- | | | | NEGATIVE ------------------------------- | | | ZERO --------------------------------- | | OVERFLOW ----------------------------------- | CARRY -------------------------------------
To explain the extend bit, I should describe the carry bit. In most microprocessors, the carry bit is overused. It is changed by (among other things) an addition instruction, but it is used in two different ways. Sometimes it is used in a later addition, such as in multiple-precision additions; sometimes a program tests the bit and branches according to the carry bit's state. So programmers use the carry bit for two different purposes: for extended-precision arithmetic and for program control.
The MC68000 has a bit for each purpose. Both the carry and the extend bits are changed according to the results of an addition instruction. However, the carry bit is used by the microprocessor during testing for program control purposes, while the extend bit is used as an input for multiple-precision arithmetic operations. For ADD, SUB, NEG, and specified shift and rotate instructions, both the carry and extend bits are updated. Other instructions -- MOVE, AND, OR, TST, CLR, MUL, and DIV -- change only the carry bit. This design helps prevent inadvertent changes to either bit.
Because of the extend bit, the familiar "add with carry" operation in the MC68000 becomes ADDX or "add with extend bit." Look at why this is important. Once you start a multiple-precision arithmetic operation and get a partial result, the integrity of the extend bit will be maintained even if you have to suspend the addition to do some data movement with the MOVE instruction. Programming becomes easier because you don't have to save the status register codes when interrupting a multiple-precision operation.
I should mention one other thing about multiple-precision arithmetic. When you have finished the multiple-precision operation, what does the negative bit mean? It correctly indicates that the result was positive or negative. In most microprocessors, though, the zero bit indicates only that the most significant portion of the result is 0, not that the entire result is 0. The multiple-precision arithmetic instructions in the MC68000 are designed so that the zero bit will accurately depict the status of the entire result. This is done by allowing multiple-precision instructions to reset the zero bit (denoting a nonzero result) but not to set it (denoting a zero result). With this scheme, the programmer's only responsibility is to set the zero bit before beginning the multiple-precision operation.
One final issue can come up in the middle of arithmetic operations, and the MC68000's handling of the problem illustrates another fundamental difference between it and so many other microprocessors. How many times have you interrupted a series of arithmetic operations to modify some memory pointers and later discovered that your completed arithmetic operation gave a wrong result because you inadvertently modified certain status register code bits? When you get right down to it, when you add 12 to a memory address, who cares if a carry was generated or if the result was negative? In fact, the negative bit has no meaning in relation to addresses. We as programmers are hurt by the senseless changing of status register code bits when address-related operations are run. Why don't we leave these bits alone when changing memory addresses?
As you can imagine, the designers of the MC68000 have addressed this problem. One of the primary distinctions between the data and address registers in the MC68000 is that instructions with an address register as the destination do not modify the status register code bits. They are not changed by moving a new pointer value into an address register, incrementing or decrementing an address register, or by adding any value to an address register. This means that you should never run into a problem with memory-pointer modifications affecting your ongoing data arithmetic operations.
Another interesting note is that all operations to any address register affect the entire address register. Because all MC68000 addresses are 32 bits wide, any operations with an address register as destination must perform in a way that keeps the result valid as a 32-bit address. One solution, to require all inputs to address-register operations to be full 32-bit quantities, would be wasteful of memory space. So either word (16-bit) or long-word (32-bit) operations may take place in any of the address registers A0 through A7. If a word operation is performed, the 16-bit quantity is first sign-extended to 32 bits before it is used.
Motorola designed the MC68000 to offer a versatile set of data sizes and addressing modes for the assembly-language programmer. To understand fully the power of this machine, you must first understand how the MC68000 organizes and moves data.
Although the MC68000 "sees" its memory space as a collection of 8-bit bytes, it works on several different data sizes. It can address memory in the following sizes: byte (8 bits), word (16 bits), and long word (32 bits). In addition, it can manipulate individual bits -- a bit is specified by the byte it is in and its bit number (between 0 and 7) within the byte.
The MC68000 has a definite preference for addressing 16- and 32-bit quantities that start at even addresses. In particular, it was designed to access quickly 16-bit quantities that start on an even address. Even though this speed comes at the expense of words and long words that begin at odd addresses (which would take two, not one, fetch operations to be accessed), this is not a serious disadvantage. MC68000 op codes are always 16 bits wide, and any arguments the MC68000 requires are always stored as one or more 16-bit words (even if the argument is only a byte quantity). Because of this, code that starts on an even-byte boundary will stay on an even-byte boundary and thus will be accessed at the fastest rate possible.
The MC68000 register set (see the figure) indicates the microprocessor's commitment to long-word data: even though this is a 16-bit machine, all the internal registers are 32 bits wide. The dotted lines in the data registers denote those registers' ability to handle 8-, 16-, or 32-bit-wide data. The address registers can handle either 16- or 32-bit-wide data.
31 16 15 8 7 0 ----------------------------------------------- | | | | D0 ----------------------------------------------- | | | | D1 ----------------------------------------------- | | | | D2 ----------------------------------------------- EIGHT | | | | D3 DATA ----------------------------------------------- REGISTERS | | | | D4 ----------------------------------------------- | | | | D5 ----------------------------------------------- | | | | D6 ----------------------------------------------- | | | | D7 ----------------------------------------------- 31 16 15 8 7 0 ----------------------------------------------- | | | | A0 ----------------------------------------------- | | | | A1 ----------------------------------------------- | | | | A2 ----------------------------------------------- SEVEN | | | | A3 ADDRESS ----------------------------------------------- REGISTERS | | | | A4 ----------------------------------------------- | | | | A5 ----------------------------------------------- | | | | A6 ----------------------------------------------- ----------------------------------------------- | USER STACK POINTER | TWO ----------------------------------------------- A7 STACK POINTERS | SUPERVISOR STACK POINTER | ----------------------------------------------- 31 9 ----------------------------------------------- | | | | PROGRAM COUNTER ----------------------------------------------- 15 8 7 0 ----------------------- |SYSTEM BYTE| USER BYTE | STATUS REGISTER -----------------------
The MC68000 supports six major addressing modes; although I will not go into the variations available in each mode, a short description of each will show you the basic ways the micro processor can get its operands.
How fast does the MC68000 execute instructions? Because of the consistency of the microprocessor, the answer for addition instructions will serve as a guide for all arithmetic and logic instructions. A prefetching mechanism in the MC68000 keeps decoded instructions waiting to be executed. So while the timing information given refers only to the time it takes to pass through the adder, recall that the prefetcher will have fetched the next op code while the current op code is being executed.
The minimum time it takes the MC68000 microprocessor to access memory (to read or write) is 4 clock cycles. With a clock frequency of 8 MHz (the frequency used in the standard MC68000 microprocessor), this bus cycle will take 500 ns (nanoseconds). (All subsequent timings will be given in clock cycles, which is a meaningful measurement for all the MC68000-family microprocessors, regardless of the speed of their system clocks -- 8, 10, or 12.5 MHz.) Every instruction will take at least 4 clock cycles to complete because this is the time it takes to fetch the next op code.
The MC68000 has only one 16-bit arithmetic and logic unit (ALU) for data operations. Therefore, 8- or 16-bit operations can be performed in a single pass through this unit; this takes 4 clock cycles. A 32-bit operation will require a second pass. Memory-addressing modes increase the time needed for an operation because the microprocessor requires more time to calculate the addresses, and a bus cycle is required for each 16 bits of addressing information or actual data that needs to be transferred. An indexed addressing mode, or anything with a displacement, for instance, will require 1 additional bus cycle for the address extension word and another to get the data (2 if the data is a long word); add about 8 more clock cycles (12 if the data is a long word) to the execution time of a given instruction that uses this mode. Some sample worst-case clock timings for various addition instructions are given in table 3.
Table 3: Examples of MC68000 addition instructions. The clock times given are worst-case times for the instruction.
Instruction Operation ADD.B D6,D2 adds the lower 8 bits of D6 to D2 (takes 4 clock cycles) ADD.L 52(A1,D7.W),D6 the effective address is the sum of the constant 52, the contents of register A1, and the lower 16 bits of register D7; the long word at the effective address is added to the contents of register D6 (20 clock cycles) ADD.W D3,(A7) adds the lower 16 bits of D3 to the element on top of stack pointed to by A7 (12 clock cycles) ADDI.L #$400,D1 adds 400 hexadecimal to the 32-bit contents of D1 (16 clock cycles) ADDI.B #$A9,$30B(A6) the effective destination address is the sum of the 30B hexadecimal and the contents of register A6; A9 hexadecimal is added to the byte at the effective address (20 clock cycles) ADDA.W -(A5),A2 decrement register A5 by 2, then add the word pointed to by register A5 to register A2 (14 clock cycles) ADDA.W #100,A5 add the value 100 to the contents of register A5 (12 clock cycles) ADDQ.W #1,(A4)+ add 1 to the word pointed to by register A4, then increment register A4 by 2 (12 clock cycles) ADDQ.B #3,D7 add 3 to the contents of register D7 (4 clock cycles) ADDX.L -(A2),-(A5) after decrementing both registers A2 and A5 by 4, add together the X bit and the two long words pointed to by A2 and A5 (30 clock cycles)
Like the ADD instruction, other MC68000 arithmetic instructions come in several forms. The subtract instructions have forms analogous to the add instructions -- SUB, SUBA, SUBI, SUBQ, and SUBX. Instructions for compare operations that are all similar (CMP, CMPA, CMPI) perform the subtractions without storing a result (the net effect is to set the appropriate status register bits). A memory-compare instruction (CMPM) allows two strings of binary integers in memory to be compared by sequencing through them to higher memory. Two versions of the single-operand negate instruction, NEG and NEGX, ignore and include, respectively, the state of the X bit.
Two versions of multiply and divide instructions make fast work of more complex arithmetic. The two versions are unsigned (MULU and DIVU) and signed (MULS and DIVS) instructions; these versions interpret their operands as one's-complement and two's-complement numbers, respectively. All of these instructions can include immediate values as the multiplier or divisor so that variables can be operated on by constants.
The multiply instructions take two 16-bit operands (one from any memory location by any addressing mode or any data register, and the other from the lower 16 bits of any data register), multiply them, and place the resulting product into the full 32 bits of the same data register. The divide instructions take the dividend from any 32-bit data register and divide it by a 16-bit divisor, which may come from memory using any addressing mode or any data register. The quotient is placed in the lower 16 bits of the same 32-bit data register, while the 16-bit remainder is placed in the upper 16 bits of the same register.
The divide instruction has two characteristics that may be undesirable and so are specially handled. All of us remember from high school and college that there just isn't any good way to divide by zero. The result is infinite if it's defined at all. Well, Motorola's designers didn't think they knew any better than the mathematicians, so if a zero divisor is detected, the divide instructions do not execute, and a special "trap" procedure is entered. Since the trap operation will be covered in part 3 of this article, let's just say that a "zero-divide trap" specially calls the operating system to decide what to do.
The other thing that could happen is that the divisor could be just too small for the dividend and the quotient could require more than 16 bits in which to be expressed. When this overflow condition is detected, the division is halted, the overflow (V) status register code bit is set, and the instruction is concluded without overwriting either of the original operands. Thus, following any divide instruction, you should check the overflow bit and act accordingly.
For a number of reasons, there are no instructions to multiply two 32-bit numbers or to divide a 64-bit number by a 32-bit number. First, the need for such instructions is very infrequent in most applications. Second, there are no other facilities in the machine to handle 64-bit quantities. Finally, because such instructions would take a lot of time to execute, the MC68000 would occasionally take much longer to respond to an interrupt -- a situation the designers did not want to create.
The multiply instructions take fewer than 70 clock cycles to execute using register operands, and the divide instructions require fewer than 140 clock cycles for an unsigned operation (158 cycles for a signed operation); however, different combinations of 1s and 0s in the operands can make these operations take less than these times to execute. A short MC68000 routine that performs a 32-bit by 32-bit multiplication is shown in listing 1. It executes in about 60 microseconds, which is less time than that taken by the dedicated instruction that does the same thing in the Z8000.
Listing 1: A short MC68000 assembly-language routine to multiply two 32-bit numbers.
Input: register D0 contains 32-bit multiplicand register D1 contains 32-bit multiplier Output: registers D0 and D1 contain the 64-bit result, with the most significant byte in D0 SUBQ #4,A7 initialize product area CLR.L -(A7) MOVE.L D0,-(A7) save copy of multiplicand MULU D1,D0 multiply low-order parts MOVE.L D0,8(A7) MOVE.W (A7),D0 high-order multiplicand MULU D1,D0 times low-order multiplier ADD.L D0,6(A7) SWAP D1 now use high-order multiplier MOVE.W 2(A7),D0 low-order multiplicand MULU D1,D0 times high-order multiplier ADD.L D0,6(A7) BCC MUL32A carry into high-order ADDQ.W #1,4(A7) word of product MUL32A MOVE.L (A7)+,D0 high-order multiplicand SWAP D0 MULU D1,D0 times high-order multiplier ADD.L (A7)+,D0 ADDQ #4,A7 MOVE.L (A7)+,D1 load low-order product
The final type of arithmetic instructions handles decimal digits. The most common form of human-interface data comes as binary-coded decimal or BCD data. This method of encoding numeric information as a string of bits stores each decimal digit of the number as a 4-bit binary number. Numbers are easily encoded into BCD form; once inside the computer, they are easily printable in human-readable form (much more so than numbers encoded in signed floating-point binary form). Because the BCD format is so useful, most microprocessors include instructions that operate on BCD numbers. To allow these BCD types of data to be manipulated, the MC68000 has three instructions that add (ABCD), subtract (SBCD), and negate (NBCD) packed digits. Each of these instructions works on two BCD digits packed into a byte.
Because BCD numbers may be many digits wide, the BCD instructions work as multiple-precision operations, which means they have the characteristics of the other multiple-precision instructions. The operands can be in data registers or in memory (in which case, they are operated on using the predecrement addressing mode). The value of the X status register code bit is included in the BCD operations, and the Z status register bit is handled so that it properly reflects the state of the entire result, not just the final portion.
Once again, the best thing about these instructions is the simplicity with which they operate, especially when compared with the often mysterious code a programmer had to write to do BCD arithmetic on most older microprocessors. A glance at MC68000 code performing BCD functions (see figure 4) shows how simple such code is. Here, two 6-digit numbers need to be added. While a short loop might make the routine more generally useful, inline code is fastest and illustrates the point best. First, we must load the two address registers to be used as memory pointers with the correct values. The next instruction (SUB D1,D1) is a quick way of both setting the Z bit and clearing the X bit, though a MOVE #$04,CCR would do virtually the same thing.
Figure 4: An example of multiple-precision binany-coded decimal (BCD) arithmetic. Because the predecrement addressing mode used ("ABCD -(A1),-(A2)") decrements the register pointers before performing the BCD addition, registers A1 and A2 must be loaded with a value that points to the byte immediately after the least significant byte of the number to be worked on.
X = X X X X X X ADDRESS MEMORY BCD 5 4 3 2 1 0 (HEXADECIMAL) (ORGANIZED AS WORDS) Y = Y Y Y Y Y Y BCD 5 4 3 2 1 0 |-------------| FE | X X | | 5 4 | |-------------| 100 | X X X X | | 3 2 1 0 | |-------------| 102 | | | | |-------------| ~ ~ ~ ~ |-------------| 1FE | Y Y | | 5 4 | |-------------| 200 | Y Y Y Y | | 3 2 1 0 | |-------------| 202 | | | | |-------------| | | CODE TO ADD X TO Y BCD BCD MOVE.L #$102,A1 LOAD ADDRESS JUST PAST X IN A1 MOVE.L #$202,A2 LOAD ADDRESS JUST PAST Y IN A2 SUB D1,D1 CLEAR X STATUS BIT AND SET Z STATUS BIT ABCD -(A1),-(A2) BCD ADDITION OF BOTTOM TWO DIGITS ABCD -(A1),-(A2) BCD ADDITION OF MIDDLE TWO DIGITS ABCD -(A1),-(A2) BCD ADDITION OF TOP TWO DIGITS
The three ABCD (add binary-coded-decimal) instructions begin at the least significant two digits and move toward the most significant; this must be done to get accurate results from use of the extend bit. The result replaces the BCD number pointed to by A2; when the routine has finished, A2 points to the first byte of the BCD result (which is stored in order of most to least significant digit). Similar subtraction and negation operations can be built in the same way.
The MC68000 logic instructions are simple but powerful. The AND, OR, exclusive-or (EOR), and NOT instructions, like the arithmetic instructions, allow 8-, 16-, and 32-bit quantities in data registers or in memory to be operated on with any data register or an immediate constant, or to be inverted. These instructions are just as fast as the arithmetic instructions. Additionally, ANDI, ORI, and EORI instructions are used to clear, set, and toggle individual status register code bits.
A serial shifter in the MC68000 can be moved any number of bits to allow for shifting of 8-, 16-, and 32-bit data. The arithmetic-shift-right instruction (ASR) shifts the least significant bit to the X and C status bits while duplicating the most significant bit before moving it to the right. In the arithmetic shift left (ASL), the logical shift right (LSR), and the logical shift left (LSL), the bit shifted out of the data area goes into the X and C bits, while the bit into which no bit is being shifted is filled with a 0.
The rotate instructions shift bits around in a circular manner so that bits shifted out of one end of an operand are shifted in the other end, with the bit being shifted out of the data area also being copied into the C status register code bit and, optionally, the X bit. The rotate instructions are rotate right and rotate left (ROR and ROL); the ROXR and ROXL instructions are used when you want to update both the X and C bits.
One single shift or rotate instruction can move register data as many as 31 bit positions in the selected direction. You can specify this count value either statically (as a value between 1 and 8 encoded into the instruction op code) when the instruction is written or dynamically (as a value between 0 and 63 stored in a specified data register) when the instruction is executed. For simplicity, memory operands to be shifted or rotated are limited to displacements of 1 bit and operations on word-sized data only. Table 4 illustrates some shift and rotate instructions, their timing, and their effects.
Table 4: Examples of shift and rotate instructions and their effect on registers and memory.
Status Status Register Register Instruction Register Codes ---------------------------------------------------------------------------- C X V ---------------------------------------------------------------------------- ASR.B #3,D3 (D3 before) 10111010 01011111 01100101 10101100 x x x (12 clock cycles) (D3 after) 10111010 01011111 01100101 11110101 1 1 0 ---------------------------------------------------------------------------- ASL.L #5,D1 (D1 before) 11101100 10100010 11011101 00101111 x x x (18 clock cycles) (D1 after) 10010100 01011011 10100101 11100000 1 1 1 ---------------------------------------------------------------------------- LSL.W D5,D7 (D5 before) 00101000 10001100 11101001 00101001 x x x (D7 before) 10111010 01011111 01100101 00010101 x x x (24 clock cycles) (D7 after) 10111010 01011111 00101010 00000000 0 0 0 ---------------------------------------------------------------------------- ROL.L D2,D1 (D2 before) 01100101 00101010 10111110 01110100 x x x (D1 before) 10010101 00101000 01000101 10010100 x x x (48 clock cycles) (D1 after) 01011001 01001001 01010010 10000100 0 x 0 ---------------------------------------------------------------------------- ROXR.W #4,D6 (D6 before) 10111010 01011111 01100101 00010101 x P x (14 clock cycles) (D6 after) 10111010 01011111 101P0110 01010001 0 0 0 ---------------------------------------------------------------------------- ROR $A0000 (word A0000 before) 10011100 10101001 x x x (word A0000 after) 11001110 01010100 1 x 0
Notes:
An important aspect of programming that until the MC68000 was quite limited is that of individual bit manipulation, the ability to single out bits of memory, test them, set them, and clear them. Such operations are useful; in I/O, for instance, you frequently need to sense the state of a single input line, drive a particular output line high, or turn a servo-mechanism off. These operations involve only a single bit associated with a latch, peripheral, or memory location.
In the past, most of us have done the best we could by executing AND, OR, and EOR instructions to the desired location. But the difficulty with these operations is their crudeness. Sure, they allow us to change more than 1 bit at a time, but it turns out that much more secure code can be written when single events or conditions affect single outputs. Also, because it is impossible to sense the state of more than one input at a time, nothing is gained by the ability of such instructions to work on multiple bits.
Four powerful MC68000 instructions make all bit-manipulation functions far simpler. They are the bit test (BTST), bit test and set (BSET), bit test and clear (BCLR), and bit test and change (BCHG) instructions. How will you specify the target bit? The MC68000 uses two methods, similar to those used for shift and rotate instructions. Either a data register or a series of bits in the bit-instruction op code names the bit to be affected; in this case, however, the bit number can be from 0 to 31 if a register is affected, or from 0 to 7 if the area affected is a memory location. (In the MC68000, bits in memory are identified by the bit number of the byte in which they reside.)
With true bit-manipulation instructions, not crude logic instructions, bit-manipulation operations -- sensing the state of inputs, driving outputs, setting register bits, setting attribute bits, transposing bit matrices, or just building special data types -- are straightforward tasks, not the chores they usually are with other microprocessors. The MC68000 makes it very easy to specify precisely the bit to be changed.
The computation and data-movement instructions that perform the major work in any MC68000 program are numerous, comprehensive, and, perhaps most important, straightforward and easy to use. The versatile MOVE instruction on the MC68000 replaces a confusing variety of data-movement instructions on other microprocessors. Flexible add, subtract, compare, negate, multiply, and divide instructions operate on any register, with constants, on stacks, and in memory using any addressing mode. For digital data rather than binary data, pairs of BCD numbers can be added, subtracted, and negated. The common logic operations of AND, OR, exclusive-or, and NOT can similarly operate on data registers and constants, and in memory.
When data needs to be shifted about, it can be arithmetic-shifted,. logic-shifted, or rotated left or right. It can also be shifted or rotated multiple bit positions, with the count of the movement either predetermined and constant, or variable and dependent upon other data.
Individual bits in data or I/O can be separately tested to determine their state; they can also be set, reset, or toggled. The bit to be worked on can be chosen either when the instruction is written or, based on other data, when it is run.
All the above instructions can operate on 8-, 16-, or 32-bit data, with a uniform yet flexible set of addressing modes. This combination of good instruction set design, computational power, and ease of use make the MC68000 microprocessor an excellent one for assembly-language programming. Next month, I'll discuss program-control instructions and several advanced instruction groups.
About the Author
Thomas Starnes is an electrical engineer who has spent the last five years helping to plan the direction of the MC68000 family of processor products for Motorola.