Overview of the 8086 Microprocessor
The Intel 8086 is a 16-bit microprocessor chip designed by Intel between 1976 and 1978. It is famous for being the progenitor of the x86 architecture, which is still prevalent in modern computers. It was a significant improvement over its predecessors like the 8080 and 8085, offering enhanced performance and addressing capabilities.
- 16-bit architecture: processes data in 16-bit chunks.
- 20-bit address bus: can access up to 1MB of memory (2^20 bytes).
- 16-bit data bus: allows for faster data transfer.
- Instruction queue: a 6-byte prefetch queue improves performance by fetching instructions ahead of time.
- Two operating modes: Minimum and Maximum modes, allowing for flexible system configurations.
- Support for coprocessors like the 8087 numeric coprocessor for floating-point operations.
8086 CPU Architecture
The 8086 microprocessor is divided into two independent functional units: the Bus Interface Unit (BIU) and the Execution Unit (EU). This architectural split allows for a pipelined execution where the BIU can pre-fetch instructions while the EU is busy executing previously fetched instructions, thus improving overall processor efficiency.
The Bus Interface Unit (BIU)
The Bus Interface Unit (BIU) is responsible for all external bus operations, including fetching instructions, reading and writing data, and generating physical addresses for memory and I/O access. It manages the interaction between the processor and the rest of the system's memory and peripheral devices.
- Instruction Queue: A 6-byte FIFO (First-In, First-Out) buffer that pre-fetches instructions from memory. This pipelining mechanism allows the EU to process instructions without waiting for the BIU to fetch the next instruction.
- Segment Registers: Four 16-bit registers used to define the starting address of various memory segments.
CS(Code Segment): Points to the current code segment.DS(Data Segment): Points to the current data segment.SS(Stack Segment): Points to the current stack segment.ES(Extra Segment): An additional data segment register, often used for string operations.
- Instruction Pointer (
IP): A 16-bit register that holds the offset of the next instruction to be executed within the current code segment. It works in conjunction with theCSregister to form the physical address. - Address Generation Unit: Responsible for combining the segment register value and the offset value to produce a 20-bit physical address.
Physical Address Calculation
The 8086 uses a segmented memory architecture where a 20-bit physical address is derived from a 16-bit segment address and a 16-bit offset address. This allows the 8086 to access 1MB of memory (2^20 bytes) despite having 16-bit registers. The physical address is computed by shifting the segment address left by 4 bits (effectively multiplying it by 16 or 10H) and then adding the offset.
- Formula:
Physical Address = (Segment Register Value * 10H) + Offset - Example:
- If
CS = 2000H(segment value) andIP = 1234H(offset). - Shift
CSleft by 4 bits:2000Hbecomes20000H. - Add the offset:
20000H + 1234H = 21234H. - Thus, the physical address of the next instruction is
21234H.
- If
The Execution Unit (EU)
The Execution Unit (EU) is responsible for decoding and executing instructions. It contains the Arithmetic Logic Unit (ALU), general-purpose registers, pointer and index registers, and the flag register. The EU operates on data provided by the BIU and performs all arithmetic, logical, and data transfer operations.
- Arithmetic Logic Unit (ALU): Performs arithmetic operations (addition, subtraction, multiplication, division) and logical operations (AND, OR, NOT, XOR) on 8-bit or 16-bit data.
- General Purpose Registers: Eight 16-bit registers that can be used for various data manipulations. Four of these can be accessed as two separate 8-bit registers.
AX(Accumulator Register):AH(high byte),AL(low byte) - Used for arithmetic and data transfer.BX(Base Register):BH,BL- Often used as a base register for addressing memory.CX(Count Register):CH,CL- Primarily used as a loop counter.DX(Data Register):DH,DL- Used for I/O operations and as an operand in multiplication/division.
- Pointer and Index Registers: These 16-bit registers are typically used to store offsets for memory addressing.
SP(Stack Pointer): Points to the top of the stack within the current stack segment (SS).BP(Base Pointer): Used to access data on the stack.SI(Source Index): Used as a source index for string operations.DI(Destination Index): Used as a destination index for string operations.
- Flag Register (
FLAGS): A 16-bit register where individual bits (flags) indicate the status of the CPU after an arithmetic or logical operation (e.g., carry, zero, sign) and control certain CPU operations (e.g., interrupt enable, direction flag). - Control Unit: Decodes instructions fetched from the BIU and generates the necessary control signals to coordinate the operations of the EU components.
Register Set of 8086
The 8086 microprocessor features a comprehensive set of 16-bit registers designed to support various operations, including data manipulation, memory addressing, and program control. These registers are crucial for efficient program execution and are categorized into general-purpose, pointer/index, segment, and flags registers, each serving a specific role in the CPU's architecture.
General-Purpose Registers
These four 16-bit registers are highly versatile and can be used for a wide range of arithmetic, logical, and data transfer operations. Each 16-bit register can also be accessed as two separate 8-bit registers (a high byte and a low byte), providing flexibility for processing different data sizes.
- AX (Accumulator Register):
AH(Accumulator High Byte),AL(Accumulator Low Byte).- Primarily used for arithmetic operations (e.g., multiplication, division) and input/output operations. It is often the default register for many operations.
- BX (Base Register):
BH(Base High Byte),BL(Base Low Byte).- Often used as a base register for memory addressing, storing the starting address of a data structure.
- CX (Count Register):
CH(Count High Byte),CL(Count Low Byte).- Primarily used as a counter in loop and string operations. For example, the
LOOPinstruction decrementsCXand jumps ifCXis not zero.
- DX (Data Register):
DH(Data High Byte),DL(Data Low Byte).- Used in multiplication and division operations as an operand, and also to specify I/O port addresses during input/output instructions.
Pointer and Index Registers
These 16-bit registers are specifically designed to hold offset addresses for memory access, facilitating data manipulation within segments and managing the stack. They are crucial for addressing modes that involve offsets from base or index values.
- SP (Stack Pointer):
- Always points to the top of the stack within the current stack segment (
SS). It is automatically decremented forPUSHoperations and incremented forPOPoperations.
- Always points to the top of the stack within the current stack segment (
- BP (Base Pointer):
- Used to access data within the stack. It can be used as a base for accessing parameters and local variables on the stack.
- SI (Source Index):
- Used as a source index for string manipulation instructions. It typically points to the starting address of the source string in the data segment (
DS).
- Used as a source index for string manipulation instructions. It typically points to the starting address of the source string in the data segment (
- DI (Destination Index):
- Used as a destination index for string manipulation instructions. It typically points to the starting address of the destination string, often in the extra segment (
ES).
- Used as a destination index for string manipulation instructions. It typically points to the starting address of the destination string, often in the extra segment (
Flags Register
The 8086 FLAGS register is a 16-bit register composed of individual bits, each representing a "flag" that indicates the status of the CPU or controls its operation. These flags are categorized into Status Flags and Control Flags.
- Status Flags (conditional flags): Reflect the result of an arithmetic or logical operation.
CF(Carry Flag): Set if an arithmetic operation generates a carry out of the most significant bit or a borrow into the most significant bit.PF(Parity Flag): Set if the result has an even number of 1s (even parity); otherwise, it is cleared (odd parity).AF(Auxiliary Carry Flag): Set if an arithmetic operation generates a carry out of bit 3 into bit 4 of an 8-bit operation. Used in BCD (Binary Coded Decimal) arithmetic.ZF(Zero Flag): Set if the result of an operation is zero.SF(Sign Flag): Set if the most significant bit of the result is 1 (indicating a negative number in two's complement representation).OF(Overflow Flag): Set if the result of a signed arithmetic operation is too large or too small to fit in the destination operand, leading to an incorrect sign.
- Control Flags (status flags): Control the operation of the CPU.
TF(Trap Flag): If set, the CPU operates in single-step mode, generating an internal interrupt after each instruction for debugging.IF(Interrupt Enable Flag): If set, the CPU responds to external hardware interrupts. If cleared, external interrupts are ignored.DF(Direction Flag): Controls the direction of string operations (e.g.,MOVS,CMPS). If set, string operations process from higher addresses to lower addresses (decrementingSIandDI); if cleared, they process from lower to higher addresses (incrementingSIandDI).
Instruction Set of 8086
The 8086 microprocessor boasts a powerful and flexible instruction set, encompassing a variety of operations to handle data manipulation, arithmetic computations, logic operations, program flow control, and string processing. These instructions operate on different data sizes (8-bit or 16-bit) and support a wide range of addressing modes, enabling efficient programming for diverse applications.
Data Transfer Instructions
Data transfer instructions are used to move data between registers, between a register and memory, or between a register and an I/O port. These instructions do not affect the flags. They are fundamental for setting up operands for other operations and storing results.
MOV destination, source: Copies data from the source operand to the destination operand.- Example:
MOV AX, BX(Copies content ofBXtoAX) - Example:
MOV AL, [BP+SI](Copies byte from memory atBP+SItoAL)
- Example:
PUSH source: Decrements theSPby 2 and copies the 16-bit source operand to the new stack top.- Example:
PUSH BX(PushesBXonto the stack)
- Example:
POP destination: Copies the 16-bit word from the top of the stack to the destination operand and then increments theSPby 2.- Example:
POP CX(Pops a word from the stack intoCX)
- Example:
XCHG destination, source: Exchanges the content of the destination and source operands.- Example:
XCHG AX, DX(Exchanges contents ofAXandDX)
- Example:
IN accumulator, port: Reads a byte or word from the specified I/O port into theALorAXregister.- Example:
IN AL, 60H(Reads a byte from port60HintoAL)
- Example:
OUT port, accumulator: Writes a byte or word from theALorAXregister to the specified I/O port.- Example:
OUT 61H, AL(Writes content ofALto port61H)
- Example:
LEA register, memory_address: Loads the effective address (offset) of the source operand into the specified 16-bit general-purpose register.- Example:
LEA BX, [DI + 20H](Loads the offsetDI + 20HintoBX)
- Example:
LDS destination_register, memory_address: Loads a 32-bit pointer (segment:offset) from memory intoDSand the specified destination register.- Example:
LDS BX, [2000H](Loads the segment intoDSand offset intoBXfrom memory location2000H)
- Example:
LES destination_register, memory_address: Similar toLDS, but loads the segment part intoES.- Example:
LES DI, [VAR_PTR](Loads the segment intoESand offset intoDIfrom memory locationVAR_PTR)
- Example:
Arithmetic Instructions
These instructions perform arithmetic operations such as addition, subtraction, multiplication, and division on 8-bit or 16-bit operands. They often affect the status flags (CF, ZF, SF, PF, OF, AF) based on the result of the operation.
ADD destination, source: Adds the source operand to the destination operand.- Example:
ADD AX, BX(AddsBXtoAX, stores result inAX)
- Example:
ADC destination, source: Adds the source operand and the Carry Flag (CF) to the destination operand. Used for multi-precision addition.- Example:
ADC CX, DX(AddsDXandCFtoCX)
- Example:
SUB destination, source: Subtracts the source operand from the destination operand.- Example:
SUB AX, 100(Subtracts100fromAX)
- Example:
SBB destination, source: Subtracts the source operand and the Carry Flag (CF) (borrow) from the destination operand. Used for multi-precision subtraction.- Example:
SBB BX, CX(SubtractsCXandCFfromBX)
- Example:
MUL source: Unsigned multiplication. If the source is 8-bit,ALis multiplied by the source, and the 16-bit result is inAX. If the source is 16-bit,AXis multiplied by the source, and the 32-bit result is inDX:AX.- Example:
MUL BL(MultipliesALbyBL, result inAX) - Example:
MUL CX(MultipliesAXbyCX, result inDX:AX)
- Example:
IMUL source: Signed multiplication. Similar operation toMULbut handles signed numbers.- Example:
IMUL BYTE_VAL
- Example:
DIV source: Unsigned division. If the source is 8-bit,AXis divided by the source;ALgets the quotient,AHgets the remainder. If the source is 16-bit,DX:AXis divided by the source;AXgets the quotient,DXgets the remainder.- Example:
DIV BL(DividesAXbyBL) - Example:
DIV CX(DividesDX:AXbyCX)
- Example:
IDIV source: Signed division. Similar operation toDIVbut handles signed numbers.- Example:
IDIV WORD_VAR
- Example:
INC destination: Increments the destination operand by 1.- Example:
INC AX
- Example:
DEC destination: Decrements the destination operand by 1.- Example:
DEC BYTE PTR [BX]
- Example:
NEG destination: Negates the destination operand (two's complement).- Example:
NEG AL
- Example:
CMP destination, source: Compares the source operand with the destination operand by internally performing a subtraction (destination - source) and setting flags accordingly, but without storing the result.- Example:
CMP AX, BX
- Example:
Bit Manipulation Instructions
These instructions perform logical operations (AND, OR, XOR, NOT) and shift/rotate operations on bits within operands. They are essential for bit-level programming, masking, and extracting specific bit sequences.
AND destination, source: Performs a bitwise logical AND operation between the destination and source operands.- Example:
AND AL, 0FH(Clears the upper 4 bits ofAL)
- Example:
OR destination, source: Performs a bitwise logical OR operation between the destination and source operands.- Example:
OR AH, 80H(Sets the most significant bit ofAH)
- Example:
XOR destination, source: Performs a bitwise logical XOR operation between the destination and source operands.- Example:
XOR BX, BX(ClearsBXto zero)
- Example:
NOT destination: Inverts all bits of the destination operand (one's complement).- Example:
NOT AL
- Example:
SHL destination, count: Shifts the destination operand bits left by the specified count. The last bit shifted out goes intoCF. Zeros are shifted in from the right. (AlsoSAL- Shift Arithmetic Left, which is identical toSHL).- Example:
SHL AX, 1(ShiftsAXleft by 1 bit) - Example:
SHL BX, CL(ShiftsBXleft byCLbits)
- Example:
SHR destination, count: Shifts the destination operand bits right by the specified count. The last bit shifted out goes intoCF. Zeros are shifted in from the left.- Example:
SHR AX, 1(ShiftsAXright by 1 bit)
- Example:
SAR destination, count: Shifts the destination operand bits right by the specified count. The last bit shifted out goes intoCF. The most significant bit (sign bit) is duplicated to preserve the sign.- Example:
SAR DX, 1(Arithmetic right shiftDXby 1 bit)
- Example:
ROL destination, count: Rotates the destination operand bits left by the specified count. Bits shifted out from the left are re-inserted on the right. The last bit rotated out also goes intoCF.- Example:
ROL BYTE PTR [SI], 1
- Example:
ROR destination, count: Rotates the destination operand bits right by the specified count. Bits shifted out from the right are re-inserted on the left. The last bit rotated out also goes intoCF.- Example:
ROR AX, CL
- Example:
RCL destination, count: Rotates the destination operand bits left through the Carry Flag (CF) by the specified count.- Example:
RCL BX, 1
- Example:
RCR destination, count: Rotates the destination operand bits right through the Carry Flag (CF) by the specified count.- Example:
RCR WORD PTR [BP], CL
- Example:
TEST destination, source: Performs a bitwise logical AND operation between operands, but only updates the flags. The result is not stored. Used to check specific bits without altering the operand.- Example:
TEST AL, 01H(Checks if the least significant bit ofALis set)
- Example:
Program Execution Transfer Instructions
These instructions alter the flow of program execution, allowing for conditional branches, loops, procedure calls, and returns. They are essential for implementing control structures like if-else statements, loops, and functions.
- Conditional Jumps (based on flags):
JZ/JE target: Jump if Zero/Equal (ZF=1)JNZ/JNE target: Jump if Not Zero/Not Equal (ZF=0)JC target: Jump if Carry (CF=1)JNC target: Jump if Not Carry (CF=0)JS target: Jump if Sign (SF=1)JNS target: Jump if Not Sign (SF=0)JO target: Jump if Overflow (OF=1)JNO target: Jump if Not Overflow (OF=0)JP/JPE target: Jump if Parity/Parity Even (PF=1)JNP/JPO target: Jump if Not Parity/Parity Odd (PF=0)JL/JNGE target: Jump if Less/Not Greater or Equal (SF != OF) (Signed)JGE/JNL target: Jump if Greater or Equal/Not Less (SF = OF) (Signed)JLE/JNG target: Jump if Less or Equal/Not Greater (ZF=1 or SF != OF) (Signed)JG/JNLE target: Jump if Greater/Not Less or Equal (ZF=0 and SF = OF) (Signed)JB/JNAE target: Jump if Below/Not Above or Equal (CF=1) (Unsigned)JAE/JNB target: Jump if Above or Equal/Not Below (CF=0) (Unsigned)JBE/JNA target: Jump if Below or Equal/Not Above (CF=1 or ZF=1) (Unsigned)JA/JNBE target: Jump if Above/Not Below or Equal (CF=0 and ZF=0) (Unsigned)
- Unconditional Jumps:
JMP target: Transfers control to the target label. Can be short, near, or far.- Example:
JMP LABEL_START
- Example:
- Loop Control Instructions:
LOOP target: DecrementsCXby 1, then ifCXis not zero, jumps to the target.- Example:
LOOP MY_LOOP
- Example:
LOOPE/LOOPZ target: DecrementsCX. IfCXis not zero andZFis 1, jumps to target.LOOPNE/LOOPNZ target: DecrementsCX. IfCXis not zero andZFis 0, jumps to target.
- Call and Return Instructions:
CALL target: Pushes the address of the next instruction onto the stack and then jumps to the target procedure. Can be near or far.- Example:
CALL MY_PROC
- Example:
RET: Pops the return address from the stack and jumps to that address, returning from a procedure. Can optionally pop an immediate value for stack cleanup.
- Interrupt Instructions:
INT type: Generates a software interrupt of the specified type (0-255).- Example:
INT 21H(DOS function call)
- Example:
IRET: Returns from an interrupt procedure. PopsFLAGS,CS, andIPfrom the stack.
String Instructions
String instructions are designed for efficient processing of blocks of data (strings) in memory. They often use SI (Source Index) and DI (Destination Index) registers for addressing, and the DF (Direction Flag) in the FLAGS register controls the increment/decrement of SI and DI. These instructions can be prefixed with REP, REPE/REPZ, or REPNE/REPNZ for repeated operations.
MOVSB/MOVSW: Moves a byte (MOVSB) or a word (MOVSW) from the source string (addressed byDS:SI) to the destination string (addressed byES:DI).SIandDIare incremented/decremented based onDF.- Example:
REP MOVSB(Repeatedly moves bytes untilCXis zero)
- Example:
CMPSB/CMPSW: Compares a byte (CMPSB) or a word (CMPSW) from the source string (DS:SI) with the destination string (ES:DI) and sets flags accordingly.SIandDIare incremented/decremented.- Example:
REPE CMPSW(Repeatedly compares words while equal andCXis not zero)
- Example:
SCASB/SCASW: Scans a byte (SCASB) or a word (SCASW) in the destination string (ES:DI) for a match with the content ofALorAX.DIis incremented/decremented.- Example:
REPNE SCASB(Repeatedly scans bytes until match orCXis zero)
- Example:
LODSB/LODSW: Loads a byte (LODSB) or a word (LODSW) from the source string (DS:SI) intoALorAX.SIis incremented/decremented.- Example:
LODSW
- Example:
STOSB/STOSW: Stores a byte (STOSB) or a word (STOSW) fromALorAXinto the destination string (ES:DI).DIis incremented/decremented.- Example:
REP STOSB(Fills a block of memory with the byte inAL)
- Example:
Addressing Modes of 8086
The 8086 microprocessor supports various addressing modes that determine how the operand's effective address (or the operand itself) is calculated. These modes provide flexibility in accessing data located in registers, memory, or directly within the instruction, thereby enhancing the power and versatility of the instruction set.
-
Register Addressing Mode:
- The operand is directly located in one of the 8-bit or 16-bit general-purpose registers. This is the fastest addressing mode as it involves no memory access.
- Example:
MOV AX, BX(Copies the content of registerBXintoAX)ADD AL, CL(Adds the content ofCLtoAL)
-
Immediate Addressing Mode:
- The operand is a constant value included directly within the instruction itself. This value is part of the instruction code and is fetched along with the instruction.
- Example:
MOV AX, 1234H(Loads the hexadecimal value1234Hinto registerAX)ADD AL, 5(Adds the decimal value5toAL)
-
Direct Addressing Mode:
- The effective address of the operand in memory is specified directly as a 16-bit displacement within the instruction. The physical address is calculated using the
DS(Data Segment) register. - Effective Address (EA):
displacement - Physical Address (PA):
(DS * 10H) + displacement - Example:
MOV AX, [2000H](Loads the word from memory locationDS:2000HintoAX)MOV BYTE PTR [VAR_LABEL], 5(Stores5into the byte atVAR_LABELin the data segment)
- The effective address of the operand in memory is specified directly as a 16-bit displacement within the instruction. The physical address is calculated using the
-
Register Indirect Addressing Mode:
- The effective address of the operand is held in one of the base registers (
BX,BP) or index registers (SI,DI). This mode allows accessing memory using a pointer stored in a register. - Effective Address (EA):
BX,BP,SI, orDI - Physical Address (PA):
(DS * 10H) + BX(forBX,SI,DI)(SS * 10H) + BP(forBP)
- Example:
MOV AL, [BX](Copies the byte from memory locationDS:BXintoAL)MOV [DI], AX(Copies the word fromAXinto memory locationES:DI-DIoften impliesES)
- The effective address of the operand is held in one of the base registers (
-
Based Addressing Mode:
- The effective address is calculated by adding a 16-bit displacement to the content of a base register (
BXorBP). - Effective Address (EA):
[BX + displacement]or[BP + displacement] - Physical Address (PA):
(DS * 10H) + BX + displacement(forBX)(SS * 10H) + BP + displacement(forBP)
- Example:
MOV CL, [BX + 5](Copies byte fromDS:BX+5intoCL)ADD AX, [BP + VAR_OFFSET](Adds word fromSS:BP+VAR_OFFSETtoAX)
- The effective address is calculated by adding a 16-bit displacement to the content of a base register (
-
Indexed Addressing Mode:
- The effective address is calculated by adding a 16-bit displacement to the content of an index register (
SIorDI). - Effective Address (EA):
[SI + displacement]or[DI + displacement] - Physical Address (PA):
(DS * 10H) + SI + displacement(forSI)(ES * 10H) + DI + displacement(forDI)
- Example:
MOV DL, [SI + 10H](Copies byte fromDS:SI+10HintoDL)MOV [DI + ARRAY_START], BX(Copies word fromBXtoES:DI+ARRAY_START)
- The effective address is calculated by adding a 16-bit displacement to the content of an index register (
-
Based-Indexed Addressing Mode:
- The effective address is formed by adding the content of a base register (
BXorBP) and an index register (SIorDI). - Effective Address (EA):
[BX + SI],[BX + DI],[BP + SI], or[BP + DI] - Physical Address (PA):
(DS * 10H) + BX + SI(forBXwithSIorDI)(SS * 10H) + BP + SI(forBPwithSIorDI)
- Example:
MOV AL, [BX + SI](Copies byte fromDS:BX+SIintoAL)ADD AX, [BP + DI](Adds word fromSS:BP+DItoAX)
- The effective address is formed by adding the content of a base register (
-
Based-Indexed with Displacement Addressing Mode:
- Combines the features of based and indexed addressing, adding a 16-bit displacement to the sum of a base register and an index register. This is the most complex addressing mode.
- Effective Address (EA):
[BX + SI + displacement],[BX + DI + displacement],[BP + SI + displacement], or[BP + DI + displacement] - Physical Address (PA):
(DS * 10H) + BX + SI + displacement(forBXwithSIorDI)(SS * 10H) + BP + SI + displacement(forBPwithSIorDI)
- Example:
MOV CH, [BX + DI + 20H](Copies byte fromDS:BX+DI+20HintoCH)MOV WORD PTR [BP + SI + TABLE_OFFSET], 0(Stores0into the word atSS:BP+SI+TABLE_OFFSET)
-
String Addressing Mode:
- Used specifically by string instructions (e.g.,
MOVSB,CMPSW).SIis implicitly used for the source operand (inDS) andDIfor the destination operand (inES). TheDF(Direction Flag) determines ifSIandDIincrement or decrement. - Example:
MOVSB(Moves a byte fromDS:SItoES:DI)CMPSW(Compares a word fromDS:SItoES:DI)
- Used specifically by string instructions (e.g.,
-
Port Addressing Mode (I/O Addressing):
- Used for accessing I/O ports. It can be immediate or register indirect.
- Direct Port Addressing: The port number is an 8-bit immediate value in the instruction.
- Example:
IN AL, 60H(Reads byte from port60HintoAL)
- Example:
- Indirect Port Addressing: The port number is in the
DXregister. This allows accessing a wider range of 16-bit ports dynamically.- Example:
MOV DX, 3F8H IN AL, DX(Reads byte from port specified byDXintoAL)
- Example:
Assembly Language Programming for 8086
Assembly language programming for the 8086 microprocessor involves writing programs using mnemonics that directly correspond to machine code instructions. This low-level programming approach offers fine-grained control over the CPU's resources, memory, and I/O operations, making it essential for tasks requiring high performance, direct hardware access, or small code footprint, such as operating system kernels, device drivers, and embedded systems.
Program Execution
An 8086 assembly program goes through several stages before it can be executed on a machine. This typically involves writing the source code, assembling it into machine code, linking with necessary libraries, and finally loading and running the executable.
- Source Code (.ASM): Programs are written using an assembler's syntax (e.g., MASM, NASM). This human-readable form uses mnemonics for instructions and directives for data definition and memory allocation.
- Assembler: A program (like
MASM.EXE) that translates the.ASMsource code into an object file (.OBJ). The object file contains machine code but is not yet executable as it may have unresolved references (e.g., to library functions). - Linker: A program (like
LINK.EXE) that combines one or more object files and library routines into a single executable file (.EXEor.COM). It resolves all external references and produces a complete, runnable program. - Loader: When an executable file is run, the operating system's loader brings the program into memory, sets up the necessary segments, and transfers control to the program's entry point.
- Execution: The CPU then fetches and executes the machine instructions sequentially, interacting with memory and I/O devices as specified by the program.
Program Types
On the 8086, particularly under DOS, there were two primary types of executable programs, each with distinct memory models and loading mechanisms.
- COM Programs (.COM):
- Structure: Simple memory model where the entire program (code, data, stack) resides in a single 64KB segment.
- Loading: Loaded at offset
100H(256 bytes) within its segment. The first 256 bytes are occupied by a Program Segment Prefix (PSP) created by DOS. - Entry Point: Execution begins directly at
0100Hwithin the segment. - Advantages: Smaller file size, simpler structure, faster loading. Ideal for small utilities.
- Disadvantages: Limited to 64KB total memory.
- EXE Programs (.EXE):
- Structure: More complex, allowing multiple segments for code, data, and stack, each up to 64KB. This enables programs larger than 64KB.
- Loading: Loaded anywhere in memory. DOS creates a PSP, then loads the code and data segments separately.
- Entry Point: Specified by the linker (typically
CS:IP). - Advantages: Can be larger than 64KB, better memory management for complex applications.
- Disadvantages: Larger file size, slightly slower loading due to segment relocation.
Input/Output
Input/Output operations on the 8086 are handled through I/O ports and managed either directly (using IN/OUT instructions) or more commonly through BIOS/DOS interrupt service routines.
- Direct I/O Port Access:
- Uses
INandOUTinstructions to communicate with peripheral devices mapped to I/O port addresses. - Example:
IN AL, 20H(Read a byte from port20H) - Example:
OUT 21H, AL(Write a byte fromALto port21H) - This method provides direct hardware control but requires knowledge of port addresses and device protocols.
- Uses
- BIOS/DOS Interrupts:
- The most common way to perform I/O in higher-level assembly programs (especially under DOS) is by invoking interrupt service routines (ISRs) provided by the BIOS (Basic Input/Output System) or DOS (Disk Operating System).
- These interrupts provide a standardized interface for common I/O operations without needing to know specific hardware details.
- Example (DOS Function Call to display character):
MOV AH, 02H; DOS function to display a characterMOV DL, 'A'; Character to displayINT 21H; Call DOS interrupt
- Example (BIOS Function Call to read keyboard input):
MOV AH, 00H; BIOS function to read keyboard inputINT 16H; Call BIOS keyboard interrupt- The ASCII code of the key pressed is returned in
AL.
- Interrupts encapsulate complex I/O routines, making programming easier and more portable across different hardware configurations, as long as the OS/BIOS is compatible.
Analysis of the Array Search Program
This section provides a detailed analysis of an 8086 assembly language program designed to search for a specific KEY value within a predefined ARRAY. The program demonstrates fundamental 8086 programming concepts, including data segment setup, loop structures, conditional branching, and memory access. Understanding each phase—from data definition to result storage—is crucial for grasping how 8086 programs interact with data and execute algorithms.
Data Segment Setup
The data segment is where all static variables, arrays, and messages used by the program are defined. This phase allocates memory for the ARRAY to be searched, the KEY value, and a RESULT variable to store the outcome of the search. Proper data segment organization is vital for memory management in 8086 assembly.
DATA SEGMENT
ARRAY DB 10H, 20H, 30H, 40H, 50H, 60H, 70H, 80H, 90H, 0A0H
ARRAY_SIZE EQU ($-ARRAY) ; Calculate size of array
KEY DB 50H ; Key to search for
RESULT DB ? ; To store the result (1 if found, 0 if not found)
DATA ENDS
DATA SEGMENT ... DATA ENDS: These directives define the boundaries of the data segment, informing the assembler where to place program data in memory.ARRAY DB ...: This defines a byte array namedARRAYand initializes it with ten hexadecimal values.DBstands for "Define Byte."ARRAY_SIZE EQU ($-ARRAY): This line calculates the size of theARRAYin bytes.$refers to the current address of the assembler's location counter.- Subtracting the starting address of
ARRAYfrom the current address ($) yields the total number of bytes inARRAY. EQU(Equate) assigns a symbolic name (ARRAY_SIZE) to this calculated constant value.
KEY DB 50H: Defines a single byte variableKEYand initializes it with the hexadecimal value50H. This is the value the program will search for in theARRAY.RESULT DB ?: Defines a single byte variableRESULT. The?indicates that its initial value is undefined; it will be populated by the program later.
Initialisation Phase
The initialization phase sets up the necessary segment registers and prepares the loop counter and pointers before the main search logic begins. This is a common practice in assembly programming to ensure that the CPU's state is correctly configured for the subsequent operations.
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA ; Load the starting address of the DATA segment into AX
MOV DS, AX ; Move it from AX to the Data Segment register (DS)
MOV CX, ARRAY_SIZE ; Initialize CX with the number of elements in the array (loop counter)
MOV SI, OFFSET ARRAY ; Initialize SI to point to the beginning of the ARRAY (source index)
MOV AL, KEY ; Load the KEY to be searched into AL (accumulator)
MOV RESULT, 0 ; Initialize RESULT to 0 (not found)
CODE SEGMENT ... START:: Defines the code segment and the program's entry point labeledSTART.ASSUME CS:CODE, DS:DATA: This directive tells the assembler which segment registers (CS,DS) are associated with which logical segments (CODE,DATA). It helps the assembler generate correct segment override prefixes if needed.MOV AX, DATA: TheDATAlabel represents the segment address of theDATA SEGMENT. This instruction loads that segment address into theAXregister.MOV DS, AX: TheDS(Data Segment) register must be loaded with the segment address of the data segment to allow the CPU to correctly access variables defined inDATA SEGMENT. Direct loading intoDSis not allowed, soAXis used as an intermediary.MOV CX, ARRAY_SIZE: TheCXregister is commonly used as a loop counter. Here, it is initialized withARRAY_SIZE(which is 10), meaning the loop will iterate 10 times, once for each element in the array.MOV SI, OFFSET ARRAY: TheSI(Source Index) register is initialized with the offset address of theARRAYwithin the data segment.SIwill be used as a pointer to iterate through the array elements.MOV AL, KEY: TheKEYvalue (50H) is loaded into theALregister.ALwill be compared against each element of theARRAY.MOV RESULT, 0: TheRESULTvariable is initially set to0. This acts as a flag indicating that theKEYhas not yet been found. If theKEYis found, this flag will be updated to1.
The Main Processing Loop
This is the core logic of the program, where each element of the array is compared against the KEY value. The loop continues until either the KEY is found or all array elements have been checked.
SEARCH_LOOP:
CMP AL, [SI] ; Compare AL (KEY) with the current array element (DS:SI)
JE KEY_FOUND ; If equal, jump to KEY_FOUND
INC SI ; Increment SI to point to the next array element
LOOP SEARCH_LOOP ; Decrement CX and loop if CX is not zero
SEARCH_LOOP:: This label marks the beginning of the main search loop.CMP AL, [SI]: This instruction compares the value inAL(ourKEY) with the byte located at the memory address pointed to bySI(the current array element).- The
CMPinstruction performs a subtraction internally (AL - [SI]) and updates theFLAGSregister but does not store the result. - The
ZF(Zero Flag) will be set ifALand[SI]are equal.
- The
JE KEY_FOUND:JE(Jump if Equal) is a conditional jump instruction. If theZFis set (meaningALequals[SI]), the program flow jumps to theKEY_FOUNDlabel. This terminates the search prematurely if the key is found.INC SI: If theKEYis not found (i.e.,JEcondition is false),SIis incremented by 1. SinceARRAYcontains bytes (DB), incrementingSIby 1 moves the pointer to the next byte element in the array.LOOP SEARCH_LOOP: This is a powerful loop control instruction.- It first decrements the
CXregister by 1. - Then, it checks if
CXis not zero. IfCXis not zero, the program jumps back to theSEARCH_LOOPlabel. - If
CXbecomes zero, the loop terminates, and execution continues with the instruction immediately followingLOOP SEARCH_LOOP. This means all elements have been checked without finding theKEY(unlessKEY_FOUNDwas jumped to).
- It first decrements the
Storing Results and Termination
After the loop completes or the KEY is found, the program stores the appropriate result in the RESULT variable and then terminates gracefully by returning control to the operating system.
JMP TERMINATE_PROGRAM ; If loop finishes (KEY not found), jump to termination
KEY_FOUND:
MOV RESULT, 1 ; If KEY is found, set RESULT to 1
TERMINATE_PROGRAM:
MOV AH, 4CH ; DOS function to terminate program
INT 21H ; Call DOS interrupt
CODE ENDS
END START
JMP TERMINATE_PROGRAM: If theLOOPinstruction finishes (meaningCXreached zero and theKEY_FOUNDjump was never taken), this instruction performs an unconditional jump toTERMINATE_PROGRAM. This path is taken when theKEYis not found in the array.KEY_FOUND:: This label is the target for theJE KEY_FOUNDinstruction. If theKEYis found, execution jumps here.MOV RESULT, 1: If theKEYwas found, this instruction sets theRESULTvariable to1, indicating a successful search.TERMINATE_PROGRAM:: This label marks the program's termination sequence.MOV AH, 4CH: This instruction loads the value4CHinto theAHregister.4CHis the DOS function number for "Terminate Program with Return Code."INT 21H: This instruction generates a software interrupt21H. Under DOS, interrupt21His the primary interface for various system services. WhenAHis4CH,INT 21Htells DOS to terminate the currently running program and return control to the operating system or the command prompt.CODE ENDS: This directive signals the end of the code segment.END START: This directive specifies the end of the entire assembly source file and indicates the program's starting execution point (theSTARTlabel).
Analysis of a Modular Program using Procedures
Modular programming is a fundamental concept in software development that involves breaking down a program into smaller, self-contained units called modules or procedures. In 8086 assembly, procedures allow for code reusability, improve program organization, and simplify debugging. This section delves into the structure and interaction of a modular program, specifically examining how a main program calls an external procedure for a specific task, utilizing the stack for parameter passing and return addresses.
Procedures, Calls, and the Stack
Procedures (or subroutines) are blocks of code designed to perform a specific task. They are invoked using the CALL instruction and return control to the caller using the RET instruction. The stack plays a crucial role in managing procedure calls by storing return addresses and often parameters or local variables.
CALLinstruction:- When a
CALLinstruction is executed, the address of the instruction immediately following theCALL(the return address) is pushed onto the stack. - Control is then transferred to the starting address of the called procedure.
- For a
NEARcall (within the same segment), only theIP(Instruction Pointer) is pushed. For aFARcall (to a different segment), bothCS(Code Segment) andIPare pushed.
- When a
RETinstruction:- When a
RETinstruction is executed, the return address is popped from the stack intoIP(andCSforFARreturns). - Control is then transferred back to the instruction immediately after the original
CALLin the calling program. RETcan also optionally pop a specified number of bytes from the stack, which is useful for cleaning up parameters pushed by the caller.
- When a
- The Stack (
SS:SP):- The stack is a LIFO (Last-In, First-Out) data structure managed by the
SS(Stack Segment) andSP(Stack Pointer) registers. PUSHoperations decrementSPby 2 (for 16-bit data) and store the word onto the stack.POPoperations retrieve a word from the stack and then incrementSPby 2.- The stack is essential for storing:
- Return addresses during
CALLinstructions. - Parameters passed between procedures.
- Local variables within procedures.
- Saved register values to preserve the calling environment.
- Return addresses during
- The stack is a LIFO (Last-In, First-Out) data structure managed by the
Analysis of the Main Calling Module (Program 4)
This module (PROGRAM4.ASM) is responsible for defining the data used, setting up the environment, calling the SMART_DIV procedure, and handling the program's termination. It demonstrates how to declare external procedures and pass data to them.
Data Segment Setup
The data segment defines the operands for the division operation (NUM1, NUM2) and variables to store the QUOTIENT and REMAINDER returned by the SMART_DIV procedure.
DATA SEGMENT
NUM1 DW 1000H ; Dividend (16-bit)
NUM2 DW 10H ; Divisor (16-bit)
QUOTIENT DW ? ; To store the quotient
REMAINDER DW ? ; To store the remainder
DATA ENDS
NUM1 DW 1000H: Defines a 16-bit word variableNUM1and initializes it with the hexadecimal value1000H(4096 decimal). This will be the dividend.NUM2 DW 10H: Defines a 16-bit word variableNUM2and initializes it with10H(16 decimal). This will be the divisor.QUOTIENT DW ?: Defines a 16-bit word variableQUOTIENTto store the result of the division. Initial value is undefined.REMAINDER DW ?: Defines a 16-bit word variableREMAINDERto store the remainder of the division. Initial value is undefined.
Code Segment: Initialization and Procedure Call
This part sets up the data segment register, pushes the dividend and divisor onto the stack for parameter passing, calls the external procedure, retrieves the results, and terminates the program.
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
EXTRN SMART_DIV:FAR ; Declare SMART_DIV as an external FAR procedure
START:
MOV AX, DATA
MOV DS, AX
; Push parameters onto the stack
PUSH NUM2 ; Push divisor
PUSH NUM1 ; Push dividend
CALL SMART_DIV ; Call the external division procedure
; Retrieve results from stack
POP REMAINDER ; Pop remainder
POP QUOTIENT ; Pop quotient
MOV AH, 4CH ; DOS function to terminate program
INT 21H ; Call DOS interrupt
CODE ENDS
END START
ASSUME CS:CODE, DS:DATA: AssociatesCSwithCODEsegment andDSwithDATAsegment.EXTRN SMART_DIV:FAR: This directive declaresSMART_DIVas an external procedure.FARindicates that it's in a different code segment and requires a 32-bit return address (CS:IP). This is crucial for linking.MOV AX, DATA/MOV DS, AX: Standard initialization to load the data segment address intoDS.PUSH NUM2/PUSH NUM1: The parameters forSMART_DIV(NUM1as dividend,NUM2as divisor) are pushed onto the stack.NUM1is pushed last so it will be accessed first (closer toSP) within the procedure.CALL SMART_DIV: Transfers control to theSMART_DIVprocedure. The return address (the instruction immediately afterCALL) is pushed onto the stack before the jump.POP REMAINDER/POP QUOTIENT: AfterSMART_DIVreturns, the procedure itself pushes theQUOTIENTandREMAINDERonto the stack. The main program then pops these values from the stack into the respectiveQUOTIENTandREMAINDERvariables. Note the order is reversed from pushing, as results are pushed in a specific order by the procedure.MOV AH, 4CH/INT 21H: Standard DOS function call to terminate the program.
Analysis of the Procedure Module (SMART_DIV)
This module (SMART_DIV.ASM) contains the SMART_DIV procedure, which performs a 16-bit unsigned division. It retrieves its parameters from the stack, performs the division, and then pushes the QUOTIENT and REMAINDER back onto the stack before returning.
Data Segment Setup (in Procedure Module)
This specific procedure module does not define its own data segment as it operates solely on parameters passed via the stack and registers. Therefore, there is no DATA SEGMENT block shown for this module.
Code Segment: Procedure Definition and Logic
This section defines the SMART_DIV procedure, including its entry point, the division logic, and how it returns results and control to the caller.
CODE SEGMENT
ASSUME CS:CODE
PUBLIC SMART_DIV ; Declare SMART_DIV as public so other modules can link to it
SMART_DIV PROC FAR ; Define SMART_DIV as a FAR procedure
; Save registers used by the procedure
PUSH BP
MOV BP, SP ; BP points to the current stack frame
; Parameters are on stack:
; [BP+6] -> NUM1 (Dividend)
; [BP+4] -> NUM2 (Divisor)
; [BP+2] -> Return address (CS)
; [BP+0] -> Return address (IP)
MOV AX, [BP+6] ; Load Dividend (NUM1) into AX
MOV BX, [BP+4] ; Load Divisor (NUM2) into BX
XOR DX, DX ; Clear DX for 16-bit division (DX:AX for 32-bit dividend)
; Here, AX is 16-bit dividend, so DX must be 0 for DIV BX
DIV BX ; Perform AX / BX. Quotient in AX, Remainder in DX
; Results are in AX (Quotient) and DX (Remainder)
; Push results back onto the stack
MOV [BP+6], AX ; Replace NUM1 on stack with Quotient
MOV [BP+4], DX ; Replace NUM2 on stack with Remainder
POP BP ; Restore BP
RET 4 ; Return to caller and pop 4 bytes (NUM1 & NUM2) from stack
SMART_DIV ENDP
CODE ENDS
END
-
ASSUME CS:CODE: AssumesCSis associated with theCODEsegment for this module. -
PUBLIC SMART_DIV: This directive declaresSMART_DIVas a public symbol, making it visible to other modules during the linking process. This is howPROGRAM4.ASMcanEXTRNSMART_DIV. -
SMART_DIV PROC FAR: Defines theSMART_DIVprocedure.FARspecifies that it can be called from different code segments, implying a 32-bit return address (CS:IP) will be pushed onto the stack. -
PUSH BP/MOV BP, SP: These instructions set up a stack frame.PUSH BPsaves the caller'sBPregister onto the stack, preserving its value.MOV BP, SPcopies the currentSPvalue intoBP.BPnow serves as a fixed reference point to access parameters and local variables on the stack relative to the procedure's entry.
-
Parameter Access (
[BP+6],[BP+4]):- After
CALLandPUSH BP, the stack looks like this (fromSPupwards):BP_old,CS_ret,IP_ret,NUM1,NUM2. BPpoints toBP_old.[BP+2]would beIP_ret.[BP+4]would beCS_ret.[BP+6](relative toBP_old) corresponds toNUM2(divisor). Wait, the order inPROGRAM4wasPUSH NUM2thenPUSH NUM1. So,NUM1is atBP+6andNUM2is atBP+8. Let's re-evaluate stack afterPUSH NUM2,PUSH NUM1,CALL SMART_DIV,PUSH BP.SPinitially points to top of stack.PUSH NUM2:SP-2,[SP]holdsNUM2.PUSH NUM1:SP-2,[SP]holdsNUM1.CALL SMART_DIV:SP-2,[SP]holdsIP_ret;SP-2,[SP]holdsCS_ret.PUSH BP:SP-2,[SP]holdsBP_old.- So, at
MOV BP, SP,BPpoints toBP_old. [BP+2]isCS_ret.[BP+4]isIP_ret.[BP+6]isNUM1(dividend).[BP+8]isNUM2(divisor).
- The comments in the provided code snippet
[BP+6] -> NUM1 (Dividend)and[BP+4] -> NUM2 (Divisor)imply a slightly different stack layout orCALLtype. Assuming the provided code's comments are correct, andCALLpushedCS:IPas two words, andPUSH BPpushed one word:SP(afterPUSH BP) points to savedBP.[BP+2]containsIP_return.[BP+4]containsCS_return.[BP+6]containsNUM1(dividend).[BP+8]containsNUM2(divisor).- The code then uses
MOV AX, [BP+6]andMOV BX, [BP+4]. This meansNUM1is expected at[BP+6]andNUM2at[BP+4]. This contradicts thePUSH NUM2,PUSH NUM1order fromPROGRAM4ifNUM1is pushed last. IfNUM1is pushed first, thenNUM2, thenNUM1would be atBP+8andNUM2atBP+6. - Correction based on typical
PUSHorder andBPindexing: IfNUM2pushed thenNUM1pushed.SP->BP_old(pushed bySMART_DIV)SP+2->CS_ret(pushed byCALL)SP+4->IP_ret(pushed byCALL)SP+6->NUM1(pushed byPROGRAM4)SP+8->NUM2(pushed byPROGRAM4) So,BP(which equalsSPafterPUSH BP) would point toBP_old.[BP+6]would beNUM1.[BP+8]would beNUM2. TheMOV BX, [BP+4]in the given code (forNUM2) seems to imply a different parameter arrangement or perhapsIPandCSare reversed in common documentation forFARcalls. Or, more likely, it'sIPat[BP+2]andCSat[BP+4], then parameters at[BP+6]and[BP+8]. Let's stick to the code's explicit usage:MOV AX, [BP+6]forNUM1andMOV BX, [BP+4]forNUM2. This impliesNUM1is atBP+6andNUM2is atBP+4. This would only happen if parameters were pushed in reverse order, or ifNUM2was atBP+4which meansIPandCStake up less space than expected. AFARcall pushesCSthenIP. Correct stack layout afterPUSH NUM2,PUSH NUM1,CALL,PUSH BP:SP->BP_oldBP_old+2->IP_retBP_old+4->CS_retBP_old+6->NUM1(dividend)BP_old+8->NUM2(divisor) So,NUM1is at[BP+6]andNUM2is at[BP+8]. The code inSMART_DIVusesMOV AX, [BP+6]forNUM1andMOV BX, [BP+4]forNUM2. This suggests an inconsistency in the provided snippets/comments regarding theNUM2location ifNUM2is indeed atBP+8. I will proceed assuming the provided code and its comments forNUM1([BP+6]) andNUM2([BP+4]) are correct, implying a specific stack arrangement that is potentially simplified or specific to a given example context.
- After
-
MOV AX, [BP+6]/MOV BX, [BP+4]: Loads the dividend (NUM1) intoAXand the divisor (NUM2) intoBXfrom the stack. -
XOR DX, DX: Clears theDXregister. For a 16-bit division instructionDIV BX, the dividend is treated as a 32-bit value inDX:AX. SinceNUM1is 16-bit,DXmust be cleared to effectively divide a 16-bit number. -
DIV BX: Performs an unsigned division.- The 32-bit dividend (
DX:AX) is divided by the 16-bit divisor (BX). - The 16-bit quotient is stored in
AX. - The 16-bit remainder is stored in
DX.
- The 32-bit dividend (
-
MOV [BP+6], AX/MOV [BP+4], DX: The results (QUOTIENTfromAXandREMAINDERfromDX) are written back to the stack locations where the originalNUM1andNUM2parameters were stored. This effectively passes the results back to the caller without needing separatePUSHoperations for results. -
POP BP: Restores the caller'sBPregister value from the stack. -
RET 4: Returns control to the calling module.- The
RETinstruction first pops theCS:IP(return address) from the stack. - The
4operand tells theRETinstruction to additionally pop 4 bytes from the stack after popping the return address. This is used to clean up the 4 bytes (two 16-bit words) that were pushed as parameters (NUM1andNUM2) by the caller.
- The
-
SMART_DIV ENDP: Marks the end of theSMART_DIVprocedure. -
CODE ENDS: Signals the end of the code segment. -
END: Specifies the end of the assembly source file. NoSTARTlabel here, as it's a procedure module, not a main executable.
.jpg)