Design and Implementation of a Simple 8-Bit CPU

Copyright © 1997, 2000 by Rex N. Fisher


3.0 Instruction Set

3.1 Overview

There are 16 different P8 instructions. Research on instruction set usage was the basis for instruction selection. Each instruction has at least two addressing modes, with most of them having four. The instruction set is orthogonal, i.e., each instruction implements every relevant addressing mode.

3.2 Design Rationale

One of the requirements of a good instruction set is completeness. A complete instruction set can be used to evaluate any mathematical or logic function. The instruction set of an educational CPU should be complete, and should illustrate the types of instructions normally supported by actual CPUs.

The instruction set should also be simple. There is research that indicates that even very simple instruction sets can be complete. As early as 1956 a simple 1-instruction computer was developed that met the requirement of completeness (Hayes, 1988, p. 210). Of course, an excessively simple instruction set requires very complicated programs to perform even the simplest tasks.

The P8 instruction set is both complete and reasonably simple. Its completeness can be informally proven by writing a sequence of instructions to implement common operations that have not been included. For example, multiplication can be performed, even though there is no multiply instruction, with a sequence of adds and shifts. Similarly, the nonexistent AND function can be implemented because both OR and NOT are available.

Another desirable characteristic of an instruction set is regularity. A regular instruction set includes normally expected operations. In this case, some commonly encountered instructions have been omitted. This was done primarily for two reasons:

1. The 74181 4-bit ALU used for this CPU has a limited repertoire of functions.  For example, it implements a shift left and decrement, but not a shift right or increment. While this can lead to awkward code composition, and may sometimes require a little creative programming, it is possible to work around the missing functions.

2. This CPU is a teaching tool. A powerful instruction set is not a requirement.  In fact, a simple instruction set, that implements only the most common instruction types is easier to understand. In the interest of simplicity, the instruction set has been limited to 16 different operations.

Regularity also implies orthogonality. An orthogonal instructions set is one where each instruction can use every relevant addressing mode. This simplifies compiler design by making the rules for operand address specification consistent. While this is not an important consideration for the P8 CPU, it demonstrates the principle.

Research on instruction mixes for several types of processors has been compiled over the years. Of the various processors that have been benchmarked, the Intel 8086 most closely resembles the P8 CPU for this project (Hennessy & Patterson, 1990, p. 178). The frequency of instructions used by the 8086 is shown in Figure 3.1. This information is a reasonable guide for determining the most appropriate instruction mix for a simple, accumulator-based CPU like this one. Instructions that are used often should be included, if possible.
   

       8086 Instruction              Average Frequency of Use

    Move                          27%
    Conditional Jump              10%
    Compare                        7%
    Push                           7%
    Pop                            5%
    Shift Left, Shift Right        5%
    Loop                           4%
    Call                           4%
    Return                         4%
    Increment, Decrement           3%
    Or, Xor                        3%
    Add                            3%
    Subtract                       2%
    Jump                           2%

       All other instructions had a frequency of use less than 2%.


Figure 3.1: Distribution of Instruction Frequencies on the Intel 8086.


Most instructions with a usage-frequency less than 2% have not been implemented. Additionally, there are several common instructions that cannot be fully implemented on the P8 CPU:

Conditional Jump: The only condition flag is zero. Conditional jumps can only be based on whether the zero flag is set. This illustrates the principle while maintaining a simpler hardware implementation.

Compare: Only equality can be evaluated.

Push & Pop: Eliminating the stack pointer simplified the hardware significantly, but precluded the use of these instructions.

Call & Return: Again, the lack of a stack pointer makes these difficult to implement. The functions could be performed by jumping to predetermined addresses, but the programming would not be straightforward.

Loop: This operation must be implemented with a sequence of simpler instructions.

Increment: This function is not available on the 74181 ALU. The additional circuitry required to implement this would not contribute to the goal of simplicity. If an increment is required, it can be done by the add instruction with immediate data of 01h.

Operations chosen for inclusion in this instruction set represent those used nearly two-thirds of the time in the 8086 benchmarks.

An educational CPU should also implement common addressing modes. Benchmark results of commonly used 8086 addressing modes for various application programs (Hennessy & Patterson, 1990, p. 177) can be seen in Figure 3.2.


        8086 Addressing Mode                       Frequency of Use

        Memory
            Absolute                               12%
            Indirect                                5%
            Displacement                           24%
        Register                                   51%
        Immediate                                   8%

Figure 3.2: Distribution of Addressing Mode Frequencies on the 8086.

Even though displacement addressing is very powerful, and used frequently on the 8086, it has not been implemented because hardware complexity would be increased.

3.3 Programmer’s Model

The P8 CPU consists of three 8-bit registers and one 1-bit condition register.  See Figure 3.3. It is an accumulator-based design. This means that the number in the accumulator (A register) is an operand for most ALU operations. Additionally, all ALU results are placed in the A register, and overwrite its previous contents. It is also used for reading input ports and writing output ports. The R register may be used to store general data or a second operand. It also contains the memory address of operands for instructions using indirect addressing.

Because the instruction pointer is an 8-bit register, only 256 bytes of memory may be addressed. Similarly, there are 256 available I/O ports, which do not share the memory space.

The Z register is a 1-bit register that stores the results of the last compare operation. If the result was zero, the Z register contains a "1", otherwise it is "0".


    CPU Registers
+----------------+
|                | Instruction Pointer (Program Counter)
+----------------+
|                | A Register (Accumulator)
+----------------+
|                | R Register (Data/Address Register)
+-+--------------+
| |                Z Register (Zero Flag Register)
+-+

   Memory Space                     I/O Space
+----------------+             +----------------+
|                | Address 00h |                |
|                |             |                |
|                |             |                |
|                |             |                |
|                | Address FFh |                |
+----------------+             +----------------+

                 
Figure 3.3: Programmer’s Model of MP8 CPU.


3.4 Instruction Types

MP8 CPU instructions fall into five major instruction categories:

1. Input / Output. These instructions transfer data between the accumulator and external I/O devices.

    IN = Read Input Port
    OUT = Write Output Port

2. Program Control. These instructions change the sequence of program execution. They are often called branch instructions.

    JMP = Unconditional Jump
    JNZ = Jump If Not Zero (Conditional Jump)
    JZ = Jump If Zero (Conditional Jump)
    CMP = Compare (Sets / Resets Zero Bit For Conditional Jumps)

3. Data Transfer. These instructions cause data in one location (either the internal registers or external memory) to be copied to another location.

    LDA = Load A Register
    LDR = Load R Register
    STA = Store A Register
    STR = Store R Register

4. Arithmetic. These instructions perform numerical operations on data. (Floating point operations are not supported.)

    ADD = Add To A Register
    SUB = Subtract From A Register
    DEC = Decrement

5. Logical. These instructions perform Boolean operations on data, including bit shifting.

    OR = Or With A Register
    INV = Invert & Move To A Register
    SHL = Shift Left & Move To A Register

3.5 Addressing Modes

Four common addressing modes have been selected for this instruction set.  They account for those used two-thirds of the time in Intel 8086 benchmarks. They are:

1. Direct.  This is the same as absolute addressing. The address of the required data is part of the instruction. In this case, it will be the second byte of the instruction.

2. Indirect.  The address containing the address of the required data is specified. There are normally two types of indirect modes: 1) memory-indirect; and 2) register-indirect.  An instruction with memory-indirect addressing specifies the memory address in which the address of the required data is stored. A specified register contains the address of the data when register-indirect addressing is used. The indirect mode for this instruction set will be limited to register-indirect, and the register containing the address will always be the R Register.

3. Register. This is sometimes called inherent addressing. The required data is in a register.

4. Immediate. The required data is part of the instruction. For this architecture, it is the second byte of the instruction.

3.6 Instruction Format

Each instruction has an 8-bit opcode.  The eight bits are divided into two fields: 1) the operation; and 2) the addressing mode (Figure 3.4).

All instructions that use register addressing or indirect addressing require only one byte. A second byte is required for the other two addressing modes. The second byte of a direct instruction contains the 8-bit address, and the second byte of an immediate instruction specifies the immediate data.


        -----------------
        |X|X|X|X|X|Y|Y|Y| 8-Bit Opcode
        -----------------
        -----------------
        |Z|Z|Z|Z|Z|Z|Z|Z| 8-Bit Address or Immediate Data
        -----------------

Figure 3.4: Instruction Format for 1-Byte & 2-Byte Instructions.


The operation is encoded in the 5-bit field labeled ‘XXXXX’.

   5-Bit Operation Code                Instruction Type

00001                              IN
00010                              OUT
00100                              JMP
00101                              JNZ
00110                              JZ
00111                              CMP
01000                              LDA
01001                              LDR
01010                              STA
01011                              STR
01100                              ADD
01101                              SUB
01110                              DEC
10000                              OR
10001                              INV
10010                              SHL

The addressing mode is encoded in the 3-bit field labeled ‘YYY’.

   3-Bit Code      Addressing Mode       Data Location

000             Direct                Memory Address in Byte 2
001             Not Used              ---
010             Register              A Register
011             Register              R Register
100             Indirect              Memory Address in R Register
101             Not Used              ---
110             Immediate             Byte 2 of Instruction
111             Not Used              ---

3.7 Instruction Set Repertoire

See Appendix 7.1 for a more comprehensive description of the instruction set.

Instruction    Addressing Mode    Operation Performed                 # Bytes

ADD address    Direct             A <- A + MEM(address)               2
ADD A          Register           A <- A + A                          1
ADD R          Register           A <- A + R                          1
ADD M          Indirect           A <- A + MEM(R)                     1
ADD I, data    Immediate          A <- A + data                       2

CMP address    Direct             If A - MEM(address) = 0: Z <- 1     2
CMP A          Register           If A - A = 0: Z <- 1                1
CMP R          Register           If A - R = 0: Z <- 1                1
CMP M          Indirect           If A - MEM(R) = 0: Z <- 1           1
CMP I, data    Immediate          If A - data = 0: Z <- 1             2

DEC address    Direct             A <- MEM(address) -1                2
DEC A          Register           A <- A - 1                          1
DEC R          Register           A <- R - 1                          1
DEC M          Indirect           A <- MEM(R) - 1                     1
DEC I, data    Immediate          A <- data - 1                       2

IN address     Direct             A <- PORT(address)                  2
IN P           Indirect           A <- PORT(R)                        1    

INV address    Direct             A <- [MEM(address)]’                2
INV A          Register           A <- [A]’                           1
INV R          Register           A <- [R]’                           1
INV M          Indirect           A <- [MEM(R)]’                      1
INV I, data    Immediate          A <- [data]’                        2

JMP address    Direct             IP <- address                       2
JMP R          Register           IP <- R                             1

JNZ address    Direct             If Z = 0: JMP address               2
JNZ R          Register           If Z = 0: JMP R                     1

JZ address     Direct             If Z = 1: JMP address               2
JZ R           Register           If Z = 1: JMP R                     1

LDA address    Direct             A <- MEM(address)                   2
LDA A          Register           A <- A                              1
LDA R          Register           A <- R                              1
LDA M          Indirect           A <- MEM(R)                         1
LDA I, data    Immediate          A <- data                           2

LDR address    Direct             R <- MEM(address)                   2
LDR A          Register           R <- A                              1
LDR R          Register           R<- R                               1
LDR M          Indirect           R <- MEM(R)                         1
LDR I, data    Immediate          R <- data                           2

OR address     Direct             A <- A OR MEM(address)              2
OR A           Register           A <- A OR A                         1
OR R           Register           A <- A OR R                         1
OR M           Indirect           A <- A OR MEM(R)                    1
OR I, data     Immediate          A <- A OR data                      2

OUT address    Direct             PORT(address) <- A                  2
OUT P          Indirect           PORT(R) <- A                        1

SHL address    Direct             A <- [MEM(address)]6..0 ## 0         2
SHL A          Register           A <- [A]6..0 ## 0                    1
SHL R          Register           A <- [R]6..0 ## 0                    1
SHL M          Indirect           A <- [MEM(R)]6..0 ## 0               1
SHL I, data    Immediate          A <- [data]6..0 ## 0                 2

STA address    Direct             MEM(address) <- A                   2
STA M          Indirect           MEM(R) <- A                         1

STR address    Direct             MEM(address) <- R                   2
STR M          Indirect           MEM(R) <- R                         1

SUB address    Direct             A <- A - MEM(address)               2
SUB A          Register           A <- A - A                          1
SUB R          Register           A <- A - R                          1
SUB M          Indirect           A <- A - MEM(R)                     1
SUB I, data    Immediate          A <- A - data                       2


[Back to top][Back to Table of Contents][<== Previous Section][Next Section ==>]