汇编语言程序设计 | AI生成和翻译

Home 2025.04

以下是 第二部分:汇编语言程序设计 的全面教程,涵盖以下主题:8086 指令集(数据传送、算术、逻辑和控制流指令)、汇编语言程序设计(顺序、分支和循环结构)以及中断服务程序。本教程内容详尽、易于理解且注重实践,建立在微型计算机(例如 8086/8088 架构)的基础知识之上。它假定读者已具备 CPU 寄存器和内存寻址的基本知识。


第二部分:汇编语言程序设计

汇编语言是一种低级编程语言,它提供了对微处理器操作的直接控制。对于 Intel 8086/8088,汇编语言允许程序员编写与机器代码紧密映射的指令,从而对寄存器、内存和 I/O 设备等硬件资源进行细粒度控制。

1. 8086 指令集

8086 指令集是 CPU 能够理解的一系列命令,按其功能可分为:数据传送算术逻辑控制流。每条指令都作用于寄存器、内存或立即数,并使用 8086 的寻址方式(例如,寄存器寻址、直接寻址、间接寻址)。

a. 数据传送指令

这些指令在寄存器、内存和立即数之间移动数据。

b. 算术指令

这些指令执行数学运算,并根据结果更新标志位(例如 ZF、CF、SF、OF)。

c. 逻辑指令

这些指令执行位操作并处理二进制数据。

d. 控制流指令

这些指令改变程序的执行顺序,实现跳转、循环和子程序。


2. 汇编语言程序设计

汇编语言程序以人类可读的指令编写,然后被汇编成机器代码。8086 使用 分段内存模型,代码段、数据段和堆栈段需显式定义。

a. 程序结构

典型的 8086 汇编程序包括:

程序结构示例(MASM 语法)

.model small
.stack 100h
.data
    message db 'Hello, World!$'
.code
main proc
    mov ax, @data    ; 初始化 DS
    mov ds, ax
    mov dx, offset message ; 加载消息地址
    mov ah, 09h      ; DOS 打印字符串功能
    int 21h          ; 调用 DOS 中断
    mov ah, 4Ch      ; 退出程序
    int 21h
main endp
end main

b. 顺序结构

顺序代码按顺序执行指令,没有跳转或循环。

示例:两个数相加

mov ax, 5        ; AX = 5
mov bx, 10       ; BX = 10
add ax, bx       ; AX = AX + BX (15)
mov [result], ax ; 将结果存储到内存

c. 分支结构

分支使用条件/无条件跳转,根据条件改变程序流程。

示例:比较和分支

mov ax, 10       ; AX = 10
cmp ax, 15       ; 比较 AX 和 15
je equal         ; 如果 AX == 15 则跳转
mov bx, 1        ; 否则,BX = 1
jmp done
equal:
    mov bx, 0    ; 如果相等,BX = 0
done:
    ; 继续程序

d. 循环结构

循环重复执行指令直到满足条件,通常使用 LOOP 或条件跳转。

示例:求 1 到 10 的和

mov cx, 10       ; 循环计数器 = 10
mov ax, 0        ; 和 = 0
sum_loop:
    add ax, cx   ; 将 CX 加到和
    loop sum_loop ; CX 减 1,如果 CX ≠ 0 则循环
    ; AX = 55 (1 + 2 + ... + 10)

带条件循环的示例

mov ax, 0        ; 计数器
mov bx, 100      ; 限制
count_up:
    inc ax       ; AX++
    cmp ax, bx   ; 与 100 比较
    jle count_up ; 如果 AX <= 100 则跳转

e. 子程序

子程序通过 CALLRET 模块化代码,实现代码复用。

示例:计算数字的平方

main:
    mov ax, 4    ; 输入
    call square  ; 调用子程序
    ; AX = 16
    jmp exit
square:
    push bx      ; 保存 BX
    mov bx, ax   ; 复制 AX
    mul bx       ; AX = AX * BX
    pop bx       ; 恢复 BX
    ret          ; 返回
exit:
    ; 结束程序

3. 中断服务程序(ISR)

中断允许 CPU 响应外部或内部事件(例如键盘输入、定时器滴答),暂停当前程序并执行 ISR。

中断机制

编写 ISR

ISR 必须:

示例:自定义定时器 ISR

.data
old_vec dw 2 dup(0) ; 存储旧的中断向量
.code
install_isr:
    cli             ; 禁用中断
    mov ax, 0
    mov es, ax      ; ES = 0(IVT 段)
    mov bx, 1Ch*4   ; 定时器中断 (1Ch)
    mov ax, es:[bx] ; 保存旧向量
    mov old_vec, ax
    mov ax, es:[bx+2]
    mov old_vec+2, ax
    mov ax, offset my_isr ; 设置新向量
    mov es:[bx], ax
    mov ax, cs
    mov es:[bx+2], ax
    sti             ; 启用中断
    ret
my_isr:
    push ax
    inc word ptr [counter] ; 递增计数器
    pop ax
    iret            ; 从中断返回

示例:DOS 中断(INT 21h)

mov ah, 09h      ; 打印字符串功能
mov dx, offset msg ; 以 '$' 结尾的字符串地址
int 21h          ; 调用 DOS

实践注意事项


示例程序:阶乘计算

此程序使用循环和子程序计算一个数的阶乘(例如 5! = 120)。

.model small
.stack 100h
.data
    num dw 5        ; 输入数字
    result dw ?     ; 存储结果
.code
main proc
    mov ax, @data
    mov ds, ax      ; 初始化 DS
    mov ax, num     ; 加载数字
    call factorial  ; 计算阶乘
    mov result, ax  ; 存储结果
    mov ah, 4Ch     ; 退出
    int 21h
main endp
factorial proc
    push bx
    mov bx, ax      ; BX = n
    mov ax, 1       ; AX = 结果
fact_loop:
    cmp bx, 1
    jle done        ; 如果 BX <= 1,退出
    mul bx          ; AX = AX * BX
    dec bx          ; BX--
    jmp fact_loop
done:
    pop bx
    ret
factorial endp
end main

最佳实践

  1. 注释代码:汇编语言晦涩难懂,需解释每一步。
  2. 最小化寄存器使用:避免不必要的覆盖。
  3. 增量测试:使用调试器跟踪执行。
  4. 处理边界情况:检查溢出、零或负输入。
  5. 优化:减少指令数(例如,使用 XOR AX, AX 代替 MOV AX, 0)。

总结


练习题

  1. 编写一个程序,反转内存中的字符串。
  2. 实现一个子程序,检查一个数是否为质数。
  3. 为键盘中断(INT 09h)创建一个 ISR,用于统计按键次数。
  4. 解释 JMPCALL 的区别。
  5. 优化这段代码:MOV AX, 0; MOV BX, AX; ADD BX, 5

本教程涵盖了 8086 汇编程序设计的基础知识,通过示例和解释帮助您构建实践技能。如果您想探索特定指令、高级技术(例如字符串操作)或模拟工具,请告诉我!


Back Donate