AX -The accumulator register BX -The base address register CX -The count register DX -The data registerBesides the above registers, which are visible to the programmer, the x86 processors also have an instruction pointer register which contains the address of the next instruction to execute. There is also a flags register that holds the result of a comparison. The flags register remembers if one value was less than, equal to, or greater than another value.
Of course, a major difficulty with this scheme is that the number of
possible instructions is severely limited by the number of sockets one could
physically place on each row. However, CPU designers quickly discovered
that with a small amount of additional logic circuitry, they could reduce
the number of sockets required from n holes for n instructions to lg(n)
[log base 2] holes for n instructions. They did this by assigning a numeric
code to each instruction and then encode that instruction as a binary number
using lg(n) holes:
This addition requires eight logic functions to decode the A, B, and
C bits from the patch panel, but the extra circuitry is well worth the cost
because it reduces the number of sockets that must be repeated for each
instruction.
Of course, many CPU instructions are not stand-alone. For example, the move
instruction is a command that moves data from one location in the computer
to another (e.g., from one register to another). Therefore, the move
instruction requires two operands: a source operand and a destination
operand. The CPU's designer usually encodes these source and destination
operands as part of the machine instruction, certain sockets correspond
to the source operand and certain sockets correspond to the destination
operand. The figure below shows one possible combination of sockets to handle
this. The move
instruction would move data from the source
register to the destination register, the add
instruction would
add the value of the source register to the destination register, etc.
One of the primary advances in computer design that the VNA provides
is the concept of a stored program. One big problem with the patch panel
programming method is that the number of program steps (machine instructions)
is limited by the number of rows of sockets available on the machine. John
Von Neumann and others recognized a relationship between the sockets on
the patch panel and bits in memory; they figured they could store the binary
equivalents of a machine program in main memory and fetch each program from
memory, load it into a special decoding register that connected directly
to the instruction decoding circuitry of the CPU.
The trick, of course, was to add yet more circuitry to the CPU. This circuitry,
the control unit (CU), fetches instruction codes (also known as operation
codes or opcodes) from memory and moves them to the instruction decoding
register. The control unit contains a special registers, the instruction
pointer that contains the address of an executable instruction. The control
unit fetches this instruction's code from memory and places it in the decoding
register for execution. After executing the instruction, the control unit
increments the instruction pointer and fetches the next instruction from
memory for execution, and so on.
When designing an instruction set, the CPU's designers generally choose
opcodes that are a multiple of eight bits long so the CPU can easily fetch
complete instructions from memory. The goal of the CPU's designer is to
assign an appropriate number of bits to the instruction class field (move,
add, subtract, etc.) and to the operand fields. Choosing more bits for the
instruction field lets you have more instructions, choosing additional bits
for the operand fields lets you select a larger number of operands (e.g.,
memory locations or registers). There are additional complications. Some
instructions have only one operand or, perhaps, they don't have any operands
at all. Rather than waste the bits associated with these fields, the CPU
designers often reuse these fields to encode additional opcodes, once again
with some additional circuitry. The Intel 80x86 CPU family takes this to
an extreme with instructions ranging from one to about ten bytes long. Since
this is a little too difficult to deal with at this early stage, the x86
CPUs will use a different, much simpler, encoding scheme.
mov
(two forms), add, sub, cmp, and, or, not, je, jne, jb, jbe, ja, jae,
jmp, brk, iret, halt, get,
and put
. The following paragraphs
describe how each of these work.mov
instruction is actually two instruction classes merged
into the same instruction. The two forms of the mov
instruction
take the following forms:
mov reg, reg/memory/constant mov memory, regwhere
reg
is any of ax, bx, cx,
or dx
;
constant
is a numeric constant (using hexadecimal notation),
and memory
is an operand specifying a memory location. The
next section describes the possible forms the memory operand can take. The
"reg/memory/constant" operand tells you that this particular operand
may be a register, memory location, or a constant.add reg, reg/memory/constant sub reg, reg/memory/constant cmp reg, reg/memory/constant and reg, reg/memory/constant or reg, reg/memory/constant not reg/memoryThe
add
instruction adds the value of the second operand to
the first (register) operand, leaving the sum in the first operand. The
sub
instruction subtracts the value of the second operand from
the first, leaving the difference in the first operand. The cmp
instruction compares the first operand against the second and saves the
result of this comparison for use with one of the conditional jump instructions
(described in a moment). The and
and or
instructions
compute the corresponding bitwise logical operation on the two operands
and store the result into the first operand. The not
instruction
inverts the bits in the single memory or register operand.cmp
instruction. These instructions include the following:
ja dest -- Jump if above jae dest -- Jump if above or equal jb dest -- Jump if below jbe dest -- Jump if below or equal je dest -- Jump if equal jne dest -- Jump if not equal jmp dest -- Unconditional jump iret -- Return from an interruptThe first six instructions in this class let you check the result of the previous cmp instruction for greater than, greater or equal, less than, less or equal, equality, or inequality. For example, if you compare the
ax
and bx
registers with the cmp
instruction and execute the ja
instruction, the x86 CPU will
jump to the specified destination location if ax
was greater
than bx
. If ax
is not greater than bx
,
control will fall through to the next instruction in the program. The jmp
instruction unconditionally transfers control to the instruction at the
destination address. The iret instruction returns control from an interrupt
service routine, which we will discuss later.get
and put
instructions let you read and
write integer values. Get
will stop and prompt the user for
a hexadecimal value and then store that value into the ax
register.
Put
displays (in hexadecimal) the value of the ax
register.halt
and brk
. Halt
terminates program execution and
brk
stops the program in a state that it can be restarted.mov
instruction:
mov ax, ax mov ax, bx mov ax, cx mov ax, dxThe first instruction accomplishes absolutely nothing. It copies the value from the
ax
register back into the ax
register.
The remaining three instructions copy the value of bx
, cx
and dx
into ax
. Note that the original values
of bx
, cx
, and dx
remain the same.
The first operand (the destination) is not limited to ax
;
you can move values to any of these registers.mov ax, 25 mov bx, 195 mov cx, 2056 mov dx, 1000These instructions are all pretty straightforward; they load their respective registers with the specified hexadecimal constant.
mov ax, [1000] mov ax, [bx] mov ax, [1000+bx]The first instruction above uses the direct addressing mode to load
ax
with the 16 bit value stored in memory starting at location 1000 hex. ax
from the memory location
specified by the contents of the bx
register. This is an indirect
addressing mode. Rather than using the value in bx
, this
instruction accesses to the memory location whose address appears in bx
.
Note that the following two instructions:
mov bx, 1000 mov ax, [bx]are equivalent to the single instruction:
mov ax, [1000]Of course, the second sequence is preferable. However, there are many cases where the use of indirection is faster, shorter, and better. We'll see some examples of this when we look at the individual processors in the x86 family a little later.
mov ax, [1000+bx]This instruction adds the contents of
bx
with 1000 to produce
the address of the memory value to fetch. This instruction is useful for
accessing elements of arrays, records, and other data structures.The basic instruction is either one or three bytes long. The instruction
opcode consists of a single byte that contains three fields. The first field,
the H.O. three bits, defines the instruction class. This provides eight
combinations. As you may recall, there are 20 instruction classes; we cannot
encode 20 instruction classes with three bits, so we'll have to pull some
tricks to handle the other classes. As you can see above, the basic opcode
encodes the mov instructions (two classes, one where the rr field specifies
the destination, one where the mmm field specifies the destination), the
add, sub, cmp, and,
and or
instructions. There
is one additional class: special. The special instruction class provides
a mechanism that allows us to expand the number of available instruction
classes, we will return to this class shortly.
To determine a particular instruction's opcode, you need only select the
appropriate bits for the iii, rr, and mmm fields. For example, to encode
the mov ax, bx
instruction you would select iii=110 (mov reg,
reg), rr=00 (ax), and mmm=001 (bx). This produces the one-byte instruction
11000001 or 0C0h.
Some x86 instructions require more than one byte. For example, the instruction
mov ax, [1000]
loads the ax register from memory location 1000.
The encoding for the opcode is 11000110 or 0C6h. However, the encoding for
mov ax,[2000]
's opcode is also 0C6h. Clearly these two instructions
do different things, one loads the ax
register from memory
location 1000h while the other loads the ax
register from memory
location 2000. To encode an address for the [xxxx] or [xxxx+bx] addressing
modes, or to encode the constant for the immediate addressing mode, you
must follow the opcode with the 16-bit address or constant, with the L.O.
byte immediately following the opcode in memory and the H.O. byte after
that. So the three byte encoding for mov ax, [1000]
would be
0C6h, 00h, 10h and the three byte encoding for mov ax, [2000]
would be 0C6h, 00h, 20h.
The special opcode allows the x86 CPU to expand the set of available instructions.
This opcode handles several zero and one-operand instructions as shown in
the following two figures:
There are four one-operand instruction classes. The first encoding (00)
further expands the instruction set with a set of zero-operand instructions.
The second opcode is also an expansion opcode that provides all the x86
jump instructions:
The third opcode is the not
instruction. This is the bitwise
logical not operation that inverts all the bits in the destination register
or memory operand. The fourth single-operand opcode is currently unassigned.
Any attempt to execute this opcode will halt the processor with an illegal
instruction error. CPU designers often reserve unassigned opcodes like this
one to extend the instruction set at a future date (as Intel did when moving
from the 80286 processor to the 80386).
There are seven jump instructions in the x86 instruction set. They all take
the following form:
jxx addressThe
jmp
instruction copies the 16-bit immediate value (address)
following the opcode into the IP register. Therefore, the CPU will fetch
the next instruction from this target address; effectively, the program
"jumps" from the point of the jmp
instruction to
the instruction at the target address. jmp
instruction is an example of an unconditional jump
instruction. It always transfers control to the target address. The remaining
six instructions are conditional jump instructions. They test some condition
and jump if the condition is true; they fall through to the next instruction
if the condition is false. These six instructions, ja, jae, jb, jbe,
je,
and jne
let you test for greater than, greater than
or equal, less than, less than or equal, equality, and inequality. You would
normally execute these instructions immediately after a cmp
instruction since it sets the less than and equality flags that the conditional
jump instructions test. Note that there are eight possible jump opcodes,
but the x86 uses only seven of them. The eighth opcode is another illegal
opcode.brk
(break) instruction pauses the CPU until the user manually
restarts it. This is useful for pausing a program during execution to observe
results. The iret
(interrupt return) instruction returns control
from an interrupt service routine. We will discuss interrupt service routines
later. The halt
program terminates program execution. The get
instruction reads a hexadecimal value from the user and returns this value
in the ax
register; the put
instruction outputs
the value in the ax
register.mov
reg, reg/memory/constant
instruction:
ip
register to point at the next byte.ip
to point beyond the operand.ip
register to the address bus and
reads the byte at that address. This will take one clock cycle.ip
so
that it points at the next byte in the instruction stream. If the current
instruction is a multibyte instruction, ip
will now point at
the operand for the instruction. If the current instruction is a single
byte instruction, ip
would be left pointing at the next instruction.
This takes one clock cycle.ip
by two so that it points at the next byte following the operand. This operation
takes zero or one clock cycles. Zero clock cycles if there is no operand;
one if an operand is present.bx
register and the 16 bit constant; this requires two cycles, one cycle to
fetch bx
's value, the other to computer the sum of bx
and xxxx. If the mmm field contains 100, then the CPU fetches the value
in bx
for the memory address, this requires one cycle. If the
mmm field does not contain 100 or 101, then this step takes zero cycles.mov
instruction is to store the value
into the destination location. Since the destination of the load
instruction is always a register, this operation takes a single cycle.mov
instruction takes between five and eleven
cycles, depending on its operands and their alignment (starting address)
in memory.mov memory, reg
instruction:
ip
to point at the next byte (one clock cycle).ip
to point beyond the operand (zero
cycles if no such operand, one clock cycle if the operand is present).the other mov
because that instruction
can read data from memory;
this version of mov
instruction "loads" its data
from a register. This instruction takes five to eleven clock cycles to execute.add, sub, cmp, and,
and or
instructions do
the following:
ip
to point at the next byte (one clock cycle).ip
to point beyond the constant operand
(zero or one clock cycles).ip
to point at the next byte (one clock cycle).ip
to point beyond the constant operand
(zero or one clock cycles).not
instruction takes six to fifteen cycles to execute.ip
to point at the next byte (one clock cycle).ip
to point beyond the address (one clock cycle).ip
register (zero
cycles if no branch, one clock cycle if branch occurs).
mov
reg, xxxx
instruction except the destination register is the x86's
ip register rather than ax, bx, cx,
or dx
.halt
, put
, and get
instructions are of no interest to us here. They appear in the instruction
set mainly for programs and experiments. We can't very well give them "cycle"
counts since they may take an indefinite amount of time to complete their
task.