Assembly Language Programming | Generated by AI

Home PDF

Below is a comprehensive tutorial for Part 2: Assembly Language Programming, covering the topics outlined: 8086 Instruction Set (Data Transfer, Arithmetic, Logic, and Control Flow Instructions), Assembly Language Programming (Sequential, Branch, and Loop Structures), and Interrupt Service Routines. This tutorial is designed to be thorough, accessible, and practical, building on the fundamentals of microcomputers (e.g., 8086/8088 architecture). It assumes basic knowledge of CPU registers and memory addressing.


Part 2: Assembly Language Programming

Assembly language is a low-level programming language that provides direct control over a microprocessor’s operations. For the Intel 8086/8088, assembly language allows programmers to write instructions that map closely to machine code, offering fine-grained control over hardware resources like registers, memory, and I/O devices.

1. 8086 Instruction Set

The 8086 instruction set is a collection of commands that the CPU understands, categorized by their function: data transfer, arithmetic, logic, and control flow. Each instruction operates on registers, memory, or immediate values, using the 8086’s addressing modes (e.g., register, direct, indirect).

a. Data Transfer Instructions

These instructions move data between registers, memory, and immediate values.

b. Arithmetic Instructions

These perform mathematical operations, updating flags (e.g., ZF, CF, SF, OF) based on results.

c. Logic Instructions

These perform bitwise operations and manipulate binary data.

d. Control Flow Instructions

These alter the program’s execution sequence, enabling jumps, loops, and subroutines.


2. Assembly Language Programming

Assembly language programs are written as human-readable instructions that are assembled into machine code. The 8086 uses a segmented memory model, with code, data, and stack segments defined explicitly.

a. Program Structure

A typical 8086 assembly program includes:

Example Program Structure (MASM syntax):

.model small
.stack 100h
.data
    message db 'Hello, World!$'
.code
main proc
    mov ax, @data    ; Initialize DS
    mov ds, ax
    mov dx, offset message ; Load message address
    mov ah, 09h      ; DOS print string function
    int 21h          ; Call DOS interrupt
    mov ah, 4Ch      ; Exit program
    int 21h
main endp
end main

b. Sequential Structures

Sequential code executes instructions in order, without jumps or loops.

Example: Adding Two Numbers

mov ax, 5        ; AX = 5
mov bx, 10       ; BX = 10
add ax, bx       ; AX = AX + BX (15)
mov [result], ax ; Store result in memory

c. Branch Structures

Branching uses conditional/unconditional jumps to alter program flow based on conditions.

Example: Compare and Branch

mov ax, 10       ; AX = 10
cmp ax, 15       ; Compare AX with 15
je equal         ; Jump if AX == 15
mov bx, 1        ; Else, BX = 1
jmp done
equal:
    mov bx, 0    ; BX = 0 if equal
done:
    ; Continue program

d. Loop Structures

Loops repeat instructions until a condition is met, often using LOOP or conditional jumps.

Example: Sum Numbers 1 to 10

mov cx, 10       ; Loop counter = 10
mov ax, 0        ; Sum = 0
sum_loop:
    add ax, cx   ; Add CX to sum
    loop sum_loop ; Decrement CX, loop if CX ≠ 0
    ; AX = 55 (1 + 2 + ... + 10)

Example with Conditional Loop

mov ax, 0        ; Counter
mov bx, 100      ; Limit
count_up:
    inc ax       ; AX++
    cmp ax, bx   ; Compare with 100
    jle count_up ; Jump if AX <= 100

e. Subroutines

Subroutines modularize code, allowing reuse via CALL and RET.

Example: Square a Number

main:
    mov ax, 4    ; Input
    call square  ; Call subroutine
    ; AX = 16
    jmp exit
square:
    push bx      ; Save BX
    mov bx, ax   ; Copy AX
    mul bx       ; AX = AX * BX
    pop bx       ; Restore BX
    ret          ; Return
exit:
    ; End program

3. Interrupt Service Routines (ISRs)

Interrupts allow the CPU to respond to external or internal events (e.g., keyboard input, timer ticks) by pausing the current program and executing an ISR.

Interrupt Mechanism

Writing an ISR

ISRs must:

Example: Custom Timer ISR

.data
old_vec dw 2 dup(0) ; Store old interrupt vector
.code
install_isr:
    cli             ; Disable interrupts
    mov ax, 0
    mov es, ax      ; ES = 0 (IVT segment)
    mov bx, 1Ch*4   ; Timer interrupt (1Ch)
    mov ax, es:[bx] ; Save old vector
    mov old_vec, ax
    mov ax, es:[bx+2]
    mov old_vec+2, ax
    mov ax, offset my_isr ; Set new vector
    mov es:[bx], ax
    mov ax, cs
    mov es:[bx+2], ax
    sti             ; Enable interrupts
    ret
my_isr:
    push ax
    inc word ptr [counter] ; Increment counter
    pop ax
    iret            ; Return from interrupt

Example: DOS Interrupt (INT 21h)

mov ah, 09h      ; Print string function
mov dx, offset msg ; Address of '$'-terminated string
int 21h          ; Call DOS

Practical Notes


Example Program: Factorial Calculation

This program calculates the factorial of a number (e.g., 5! = 120) using a loop and subroutine.

.model small
.stack 100h
.data
    num dw 5        ; Input number
    result dw ?     ; Store result
.code
main proc
    mov ax, @data
    mov ds, ax      ; Initialize DS
    mov ax, num     ; Load number
    call factorial  ; Compute factorial
    mov result, ax  ; Store result
    mov ah, 4Ch     ; Exit
    int 21h
main endp
factorial proc
    push bx
    mov bx, ax      ; BX = n
    mov ax, 1       ; AX = result
fact_loop:
    cmp bx, 1
    jle done        ; If BX <= 1, exit
    mul bx          ; AX = AX * BX
    dec bx          ; BX--
    jmp fact_loop
done:
    pop bx
    ret
factorial endp
end main

Best Practices

  1. Comment Code: Assembly is cryptic; explain every step.
  2. Minimize Register Use: Avoid unnecessary overwrites.
  3. Test Incrementally: Use debuggers to trace execution.
  4. Handle Edge Cases: Check for overflow, zero, or negative inputs.
  5. Optimize: Reduce instructions (e.g., use XOR AX, AX instead of MOV AX, 0).

Summary


Practice Questions

  1. Write a program to reverse a string in memory.
  2. Implement a subroutine to check if a number is prime.
  3. Create an ISR for the keyboard interrupt (INT 09h) to count keypresses.
  4. Explain the difference between JMP and CALL.
  5. Optimize this code: MOV AX, 0; MOV BX, AX; ADD BX, 5.

This tutorial covers the essentials of 8086 assembly programming, with examples and explanations to build practical skills. If you’d like to explore specific instructions, advanced techniques (e.g., string operations), or emulation tools, let me know!


Back 2025.04.15 Donate