The original IBM PC used the 8088 CPU which is a scaled down 8086 with an 8-bit data bus. The 8088 executes the same instruction set as the 8086. Higher performance CPUs based on the 8086 include the 80186, 80188, and the 80286. These have additional capabilities and execute a superset of the 8086 instructions.
The 80386 is a high speed 32-bit multi-mode CPU. In real address mode, it emulates an 8086 with some additional instructions. In virtual 8086 mode, it emulates an 8086 and can also switch quickly to protected mode to realize the full power of the 32-bit CPU. Switching back to virtual 8086 mode can also be done quickly.
Since the MS-DOS Small C compiler uses only the instructions of the basic 8086 CPU, it is compatible with all of these processors. The following material describes the basic architecture of the 8086 family of CPUs.
Figure B-1 is a diagram of the 8086 central processing unit and its view of memory. Memory is simply an array of bytes. Each byte consists of eight bits and has a unique address. The first byte has an address of 0, the second 1, and so on through 1,048,575. In hexadecimal the highest address is FFFFF, a 20-bit value.
The 8086 family CPUs employ a memory segmentation scheme which permits 16 bit values to address more than 65,536 bytes. The CPU derives absolute addresses from two components--a 16-bit segment address and a 16-bit offset. The segment component comes from one of the four segment registers CS, DS, SS, or ES. The offset component comes from one of the other registers or the instruction being executed. These are added together with the segment address shifted left four bits; i.e., multiplied by 16. The r esult is the requisite 20-bit address.
The CPU is designed around the assumption that programs will usually consist of at least three segments--a code segment containing the program's instructions, a data segment containing global data, and a stack segment containing the program's stack. The extra segment register (ES) allows a program to have an additional data segment. Of course, a program could have more than one of each type of segment if it changed the values in the segment registers appropriately. On the other hand, all four segment r egisters could contain the same value, so that all of the program resides in a single segment.
When the CPU fetches an instruction for execution, the instruction is derived from the code segment (CS) and instruction pointer (IP) registers, as shown in Figure B-2.
When the CPU performs stack operations, it derives the stack address from the stack segment register (SS) and the stack pointer (SP) or the base pointer (BP). Figure B-3 illustrates the case for push and pop instructions. On the left is the situation before an operand is pushed onto the stack. On the right is the situation after the push. If a pop finds the situation on the right, it will leave it as on the left. The operand is not placed on the stack until the push instruction executes.
The remainder of the addressing modes require the CPU to compose a physical memory address for the operand. In the direct mode, the instruction contains an offset which is added to DS to derive the physical address of the operand. In the register indirect mode, a register contains an offset which is added to DS to produce the physical address of the operand. In the based mode, a base register is added to an offset contained in the instruction (a displacement) and to DS to produce the physical address o f the operand. The instruction displacement is optional. In the indexed mode, an index register is added to a displacement in the instruction and to DS to produce the physical address of the operand. The instruction displacement is optional. In the based and indexed mode, a base register is added to an index register, a displacement in the instruction, and to DS to produce the physical address of the operand. The instruction displacement is optional. Figure B-4 illustrates an example of based and in dexed addressing.
IP holds the offset of the next instruction to be fetched for execution. Normally, when an instruction is fetched, IP is incremented by the instruction's length, causing a sequential execution of instructions. However, jump, call, and return instructions break this sequence by placing a new value into IP.
SP and BP are used as offsets into the stack segment. BP establishes the base of a stack frame, whereas SP always points to the item at the top of the stack. BP is only changed by moving new values into it, whereas SP is also altered by push and pop instructions.
The index registers--source index (SI) and destination index (DI)--are used by certain string instructions as offsets into data segments. By default SI is associated with DS and DI with ES.
Sixteen bit values (words) are stored in memory as consecutive bytes with the low-order byte first. Thirty-two-bit values (double words) are stored as consecutive 16-bit values with the low-order word first. Addresses that are a multiple of 2 and 4 are referred to as word and double-word boundaries. Depending on the width of the data bus, significant improvements in speed can be realized by placing word items on word boundaries, and double-word items on word or double-word boundaries.
Four data registers are included in the CPU. They are AX, BX, CX, and DX. Each of these 16-bit registers consists of a pair of 8-bit registers--a high register and a low register. Thus, AX consists of AH and AL, and so on. Instructions can reference the data registers as four 16-bit registers or eight 8-bit registers.
The flag register is a collection of condition flags which are set or cleared by conditions resulting from the execution of arithmetic and logical instructions. The flag bits can be tested individually. The 8086 CPU uses nine flag bits.
The carry flag (CF) is set by a carry into the high-order bit or a borrow from the high- order bit. The auxiliary flag (AF) is set by a carry out of the lowest nibble (4 bits) or a borrow into the lowest nibble. The overflow flag (OF) is set when an arithmetic overflow occurs. The sign flag (SF) is set when the high-order bit of the result is set to one. The parity flag (PF) is set when the result contains an even number of bits. The zero flag (ZF) is set when the result is zero.
Three of the flags control the CPU rather than reflect conditions resulting from an arithmetic or logical instruction. The direction flag (DF) determines the direction in which string operations are performed. When clear, string operations proceed from left to right, and vice versa. The interrupt-enable flag (IF) determines whether or not external (maskable) interrupts can be recognized. When clear, these interrupts are disabled, and vice versa. The trap flag (TF), when set, places the CPU in single step mode. In that mode, the CPU generates an interrupt after each instruction. This enables a debugger to receive control after each instruction.