The power of any instruction set is a function of both the types of instructions implemented and the number of addressing modes available. Suppose that a microprocessor could not directly manipulate data in a memory location. The data would have to be loaded into a processor register, manipulated, and written back into memory. If an addressing mode were available that could directly operate on data in memory, the task could be done more effectively. In this section we will examine the many different addressing modes available in the 80x86, and see how each is used. The examples presented make use of the MOV instruction, which has the following syntax: MOV DESTINATION,SOURCE. It will be obvious in the examples what is being accomplished with each MOV instruction, so a detailed description is not included here. Also, whenever the contents of a memory location or an address or data register is referred to, assume that the value or address is hexadecimal.
Creating an Effective Address
The 80x86 uses segmented memory, where a segment is a 64KB block of memory is accessed with the aid of one of the four segment registers.
Each of the 1 048 576 memory addresses of the 8086 represents a byte-wide location. Sixteen-bit words will be stored in two consecutive memory locations. If the first byte of a word is at an even address, the 8086 can read the entire word in one operation. If the first byte of the word is at an odd address, the 8086 will read the first byte with one bus operation and the second byte with another bus operation. The main point here is that if the first byte of a 16-bit word is at an even address. the 8086 can read the entire word in one operation.
The Intel 8088 has the same arithmetic logic unit, the same registers, and the same instruction set as the 8086. The 8086 has a 20-bit address bus, so it can address anyone of 1.048.576 bytes in memory. The 8088, has an 8-bit data bus. so it can only read data from or write data to memory and ports 8 bits at a time. The 8086 can read or write either 8 or 16 bits at a time.
To read a 16-bit word from two successive memory locations, the 8088 will always have to do two read operations. Since the 8086 and the 8088 are almost identical. any reference we make to the 8086 will also pertain to the 8088 unless we specifically indicate otherwise. The Intel 8088, incidentally, was used as the CPU in the original IBM Personal Computer, the IBM PC/XT, and several compatible personal computers.
The Intel 80186 is an improved version of the 8086, and the 80188 is an improved version of the 8088. In addition to a 16-bit CPU, the 80186 and 80188 each have programmable peripheral devices integrated in the same package. The instruction set of the 80186 and 80188 is a superset of the instruction set of the 8086. The term superset means that all the 8086 and 8088 instructions will execute properly on an 80186 or an 80188, but the 80186 and the 80188 have a few additional instructions. In other words, a program written for an 8086 or an 8088 is upward compatible to an 80186 or an 80188. but a program written for an 80186 or an 80188 may not execute correctly on an 8086 or an 8088.
The Intel 80286 is a 16-bit, advanced version of the 8086 that was specifically designed for use as the CPU in a multi-user or multitasking microcomputer. When operating in its real address mode, the 80286 functions mostly as a fast 8086. Most programs written for an 8086 can be run on an 80286 operating in its real address mode. When operating in its virtual address mode, an 80286 has features that make it easy to keep users' programs separate from one another and to protect the system program from destruction by users. programs.
The Intel 80386 is a 32-bit microprocessor, which can directly address up to 4 gigabytes of memory. The 80386 contains more sophisticated features than the 80286 for use in multi-user and multitasking microcomputer systems. Similar comments may be made of the 80486 and the entire Pentium family.
Whenever any address is generated by the 8088, the 20-bit value that appears on the processor's address bus is formed by some combination of segment register and an additional numerical offset. This address is referred to as the effective address. Many of the data transfer instructions use the data segment register by default when forming an effective address. Instruction fetches generate effective addresses via the addition of the code segment register and the instruction pointer. Stack operations automatically use the stack segment register and the stack pointer. Thus, from a programming standpoint, almost all instructions require proper initialization of these segment registers for correct execution and generation of effective addresses.
One way to initialize a segment register is to first place the desired segment address into the AX register and then copies the contents of AX into the corresponding segment register. For example, to initialize the data segment register to 1000H, we would use the following instructions:
MOV AX,1000H ;AX <= 1000H
MOV DS,AX ;DS <= AX
Remember that the direction of data transfer in the operand field is from right to left, so 1000H is MOVed (copied) into AX, and then the contents of AX are MOVed into DS. However, MOV DS,1000H is an illegal instruction since we are not allowed to move a numerical value directly into a segment register.
All of the examples used to explain the microprocessor's addressing modes assume that the corresponding segment register has already been initialized.
The operand field of an instruction in many cases will contain one or more of the CPU's internal registers. Register addressing is the name we use to refer to operands that contain one of these registers. The MOV DS,AX instruction is an example of register addressing, as we are only using processor registers in the operand field. Instructions of this form often execute very quickly, because they do not require any memory accesses beyond the initial instruction fetch cycles. The machine code corresponding to MOV DS,AX is 8E 08, a 2-byte instruction containing the information necessary to perform the desired operation. DEC DX (decrement the DX register) is another example of register addressing, and has 4A as its corresponding machine code.
If register AX contains the value 1000H and register BL contains 80H, what is the result of
Solution: The contents of the BL register are copied into the AL register, leaving AH undisturbed. The final value in AX is 1080H. Notice that the contents are copied during the MOV. MOV should also be interpreted as make a copy of. Since the source data is not destroyed when we move data from one place to another, we are merely making a copy of the source data.
The machine code for MOV AL,BL is 88 D8.
We often use immediate data in the operand field of an instruction. Immediate data represents a constant that we wish to place somewhere, by MOVing it into a register or a memory location. In MOV AX,1000H, we are placing the immediate value 1000H into register AX. Immediate data is represented by 8-bit or 16-bit numbers that are coded directly into the instruction. For example, MOV AX,1000H is coded as B8 00 10, and MOV AL,7 is coded as B0 07. Notice in the first instruction that the 16-bit value l000H has its lower byte (00) and its upper byte (10) reversed in the actual machine code. This technique is commonly referred to as Intel byte-swapping, and it is the way all 16-bit numbers are stored.
An important question at this time is “How did the assembler know that the 7 in MOV AL,7 should be coded as the single byte value 07, and not the 2-byte value 07 00 (in byte-swapped form)?” The answer is that the assembler looked at the size of the other operand in the instruction (AL). Because AL is the lower half of the AX register, the assembler knows that it may contain only 8 bits of data. Knowing this, we should see why MOV AL,9C8H would be an illegal instruction. If you cannot answer this question, think about how many bits are needed to represent the hexadecimal number 9C8. Because 12 bits are needed, we cannot possibly store 9C8H in the 8-bit AL register.
What is the result of
Solution: Because register CX is specified, the immediate value 7 is coded as the 2 byte number 00 07. One exception is when the operand was defined as an Equate. Equates are defined as a 16-bit value. Consider the following:
value equ 15
All the above are valid instructions. In the case of the first two instructions, only the low byte of the operand is copied into the register.
Had value been DB instead of EQU, the last instruction would be invalid.
In this addressing mode the effective address is formed by the addition of a segment register and a displacement value that is coded directly into the instruction. The displacement is usually the address of a memory location within a specific segment. One way to refer to a memory location in the operand field is to surround the memory address with square brackets. The instruction MOV [7000H],AX illustrates this concept. The 7000H is not thought of as immediate data here, but rather as address 7000H within the segment pointed to by the DS register. DS is the segment register used whenever SQUARE brackets [ ] are used in the operand field (unless we override the use of DS by specifying a different segment register). The effective address is DS:1000H
What is the result of
Assume that the DS register contains 1000H, and AX contains l234H.
Solution: Remember that when the CPU uses a segment register to form an effective address, it shifts the segment register 4 bits to the left (turning 1000H into 10000H) and then adds the specified 16-bit displacement (or offset). Thus, the effective address generated by the processor is 17000H. Because register AX is used in the operand field, two bytes will be written into memory, with the lower byte (34) going into the first location (17000H) and the upper byte (12) going into the second location (17001H). Once again, we can see that the processor has byte-swapped the data as it was written into memory.
The machine code for MOV [7000H],AX is A3 00 70.
Another form of direct addressing requires the use of a label in the operand field. The programmer can override the automatic use of the DS register for memory accesses by specifying a different segment register within the operand field. If we wish to use the extra segment register in the instruction, we would write MOV ES:[7000H],AX. The machine code required to allow the use of the ES register in this instruction is 26 A3 0070.
Register Indirect Addressing
In this addressing mode we have a choice of four registers to use within the square brackets to specify a memory location. The four registers to choose from are the two base registers (BX and BP) and the two index registers (SI and DI). The l6-bit contents of the specified register are added to the DS register in the usual fashion.
What is the effective address generated by the instruction
if register SI contains 2000H, and the DS register contains 800H?
Solution: Shifting the DS register four bits to the left and adding the contents of register SI produces an effective address of 0A000H.
The machine code for MOV DL,[SI] is 8A 14.
Using a register in the operand field to point to a memory location is a very common programming technique. It has the advantage of being able to generate any address within a specific segment simply by changing the value of the specified register.
This addressing mode uses one of the two base registers (EX or HP) as the pointer to the desired memory location. It is very similar to register indirect addressing, the difference being that an 8or 16-bit displacement may be included in the operand field. This displacement is also added to the appropriate segment register, along with the base register, to generate the effective address. The displacement is interpreted as a signed, 2's complement number. The 8-bit displacement gives a range of –128 to +127. The 16-bit displacement gives a range of -32768 to 32767. So, the signed displacement allows us to point forward and backward in memory, a handy tool when accessing data tables stored in memory.
What is the effective address generated by the instruction
MOV AX, [BX+4]
Assume that the DS register contains 100H and register BX contains 600H.
Solution: The DS register; the BX register, and the displacement value are added together to create the effective address 1604H. This address is then used to read the word out of locations 1604H and 1605H into the AX register. The machine code for MOV AX,[BX+4] is 8B 47 04
Like based addressing, indexed addressing a1lows the use of a signed displacement. The difference is that one of the index registers (SI or DI) must be used in the operand field.
What is the effective address generated by the instruction
Assume that the DS register contains 200H and that register DI contains 30H.
Solution: The negative displacement is combined with the DI register. Notice that although the DI register points to address 30H within the data segment, the actual location accessed is 8 bytes behind. The machine code for MOV [DI-8],BL is 88 SD F8. As in the previous example, the third byte in the machine code is the displacement value. In this case, the displacement of -8 is coded as F8 hexadecimal, which is the 2's complement of 8.
Based Indexed Addressing
This addressing mode combines the features of based and indexed addressing but does not allow the use of a displacement. Two registers must be used as the source or destination operand, one from BX or BP and the other from SI or DI. The contents of both registers are not interpreted as signed numbers; therefore, each register may have a range of values from 0 to 65535. What is the effective address generated by
Assume the DS register contains 2000H, register BP contains 4000H, and register SI contains 800H.
Solution: Shifting the DS register 4 bits to the left and adding the contents of BP and SI gives an effective address of 24800H. See Figure 3.9 for an illustration of this. Also note that a different form of the instruction is used in the figure. MOV [BP][SI],AH is a different way of saying MOV [BP+SI],AH. Both methods of specifying the operand are acceptable.
The machine code for MOV [BP+SI],AH is 88 22.
Based Indexed with Displacement Addressing
This addressing mode combines all of the features of the addressing modes we have been examining. As with all the others, it still accesses memory locations only within one 64KB segment. It does, however, give the programmer the option of using two registers to access stored information, and the addition of the signed displacement makes this addressing mode the most flexible of all. The machine code for
is 8A 89 80 20. In this case the last 2 bytes are the byte-swapped displacement.
Example 3.8: What memory location is accessed by MOV CL,[BX+DI+2080H]?
Assume that the DS register contains 300H, register BX contains 1000H, and register DI contains 10H. Solution: The three registers and the displacement are added to create the memory address 06090H. The byte stored at this location is copied into register CL.
The instructions that handle strings do not use the standard addressing modes covered in the previous examples. So, when we look at a string instruction we will not see any registers listed in the operand field. For example, consider the string instruction MOVSB (move byte string). No processor registers are shown in the instruction, but the CPU knows that it should use both SI and DI during execution of the instruction. All of the string-based instructions assume that the SI register points to the first element in the source string (which might be either a byte value or a word value) and that the DI register points to the first element of the destination string. The CPU will automatically adjust the contents of SI and DI during execution of the string instruction.
The Intel brand of microprocessors differ from other processors on the market in their implementation of the use of I/O ports for data communication between the processor and the outside world. One way to get information into the CPU is to read it from memory. Another way is to read an input port. When sending data out of the CPU, we can direct it into a memory location or send it to an output port. The CPU provides the programmer with up to 65,536 input and output ports (although many useful designs rarely use more than a handful of I/O ports). An I/O port is accessed in much the same way that memory is accessed, by placing the address of the I/O port onto the address bus and enabling certain control signals. The address of the I/O port can be coded directly into an instruction, as in:
In this case, the port address must be between 0H and 0FFH, a total of 256 different I/O ports. Notice that the AL register is used to receive the port information. We could also use AX to receive 16-bits of data from an input port.
A second method of addressing I/O ports requires that the port address be placed into the DX register. The corresponding instructions are:
Since we are now using the DX register to store the port address, our choices range from 0H to 0FFFFH, a total of 65,536 I/O locations. I/O ports are very useful for communication with peripherals connected to the CPU, such as serial and parallel I/O devices, video display chips, clock/calendar chips, and many others.