汇编语言程序实际架构。
基本知识篇 常用DEBUG指令 -r
显示 CPU 寄存器值
-t
单步执行,同时显示新寄存器值
-d xxxx
查看内存地址
-u
显示接下来的指令
-g xxxx
指令跳转
寄存器显示中,各标志位代码对应值如下:
汇编语言源程序框架 MASM 6.x 版本简化段定义的源程序框架 :
1 2 3 4 5 6 7 8 9 10 .model small ; 定义程序存储模式 (small 表示小型模式) .stack ; 定义堆栈段 (默认 1KB 空间) .data ; 定义数据段 ... ; 数据定义 .code ; 定义代码段 .startup ; 程序起始点,并设置DS和SS内容 ... ; 主程序代码 .exit 0 ; 程序终止点,返回DOS ... ; 子程序代码 end ; 汇编结束
以输出 “Hello, world!” 为例展示完整代码:
1 2 3 4 5 6 7 8 9 10 11 .model small ; 定义程序存储模式 (small 表示小型模式) .stack ; 定义堆栈段 (默认 1KB 空间) .data ; 定义数据段 string db 'Hello, world!', 0dh, 0ah, '$' .code ; 定义代码段 .startup ; 程序起始点,并设置DS和SS内容 mov dx, offset string mov ah, 9 int 21h .exit 0 ; 程序终止点,返回DOS end ; 汇编结束
任何版本都能使用的完整段定义的源程序框架 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 stack segment stack ; 定义堆栈段 ... ; 分配堆栈段大小 stack ends ; 堆栈段结束 data segment ; 定义数据段 ... ; 定义数据 data ends ; 数据段结束 code segment 'code' ; 定义代码段 assume cs:code, ds:data, ss:stack ; 确定cs/ds/ss指向的逻辑段 start: mov ax, data ; 定义数据段的段地址ds mov ds, ax ... ; 主程序代码 mov ax, 4cc00h int 21h ; 程序执行终止,返回DOS ... ; 子程序代码 code ends ; 代码段结束 end start ; 汇编结束,程序起始点为start
上述输出 “Hello, world!” 的代码改写为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 stack segment stack ; 定义堆栈段 db 1024 dup(?) stack ends ; 堆栈段结束 data segment ; 定义数据段 string db 'Hello, world!', 0dh, 0ah, '$' data ends ; 数据段结束 code segment 'code' ; 定义代码段 assume cs:code, ds:data, ss:stack ; 确定cs/ds/ss指向的逻辑段 start: mov ax, data ; 定义数据段的段地址ds mov ds, ax mov dx, offset string mov ah, 9 int 21h mov ax, 4c00h int 21h ; 程序执行终止,返回DOS code ends ; 代码段结束 end start ; 汇编结束,程序起始点为start
DOS 系统功能调用
在AH寄存器中设置系统功能调用号
在指定寄存器中设置入口参数
调用指令 int 21h
执行功能调用
根据出口参数分析功能调用情况
子功能号
功能
入口参数
出口参数
AH=01H
输入一个字符
AL = 输入字符的ASCII码
AH=02H
输出一个字符
DL = 输出字符的ASCII码
AH=09H
输出一个字符串
DS:DX = 字符串地址
AH=0AH
输入一个字符串
DS:DX = 缓冲区地址
AH=0BH
判断是否有键按下
AL = 0,无;AL = FFH, 有
AH=4CH
程序执行终止
AL = 返回代码
数值表达式
个人把常数理解为宏定义,在汇编过程中完成替换与计算,不占用实际存储空间。
指由运算符连接的各种常数所构成的表达式,会在汇编过程中得到结果。
运算符类型
运算符号
算术运算符
+,-,*,/,MOD
逻辑运算符
and,or,xor,not
移位运算符
shl,shr
关系运算符
eq,ne,gt,lt,ge,le
高低分离符
high,low,highword,lowword
变量定义格式 定义伪指令
伪指令有 db
dw
dd
(以及不常用的df
dq
dt
)
初值表可以是直接由 ,
分隔的参数,也可以由 dup
定义重复参数:
定位伪指令 org
参数,使其后面的数据或指令从参数指定地址开始。
1 2 org 100h ; 从100h处开始安排数据或程序 org $+10 ; 使偏移地址+10
even
参数,使其后面的数据或指令从偶地址开始。
若原地址为偶地址,则不变,否则将地址指针+1。
align n
参数,使其后面的数据或指令从 n 的整数倍开始。
处理类似 even
。
这三个指令都可以在代码段使用,用于指定随后指令的偏移地址。
地址操作符 1 2 3 4 [] ; 表示将括起来的表达式作为存储器地址指针 $ ; 表示当前偏移地址 offset 名字/标号 ; 返回名字或标号的偏移地址 seg 名字/标号 ; 返回名字或标号的段地址
类型操作符 1 2 3 类型名 ptr 名字/标号 ; 使名字或标号具有指定类型 this 类型名 ; 创建采用当前地址,但为指定类型的操作数 type 名字/标号 ; 返回一个字量数值,表明名字或标号的类型
逻辑控制篇 分支结构 1 2 3 4 5 6 if (condition){ } else { }
在汇编中可以用如下结构实现:
1 2 3 4 5 6 7 8 cmp ..., ... jcc satisfied ; action2 jmp continue; satisfied: ; action1 continue: ...
例:判断方程 $ax^2+bx+c=0$ 是否有实根,若有实根,将tag置1,否则置0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 .model small .stack .data _a db ? _b db ? _c db ? tag db ? .code .startup mov al, _b imul al mov bx, ax ; 计算b^2放入bx中 mov al, _a imul _c mov cx, 4 imul cx ; 计算4ac放入ax中 cmp bx, ax jge yes mov tag, 0 jmp done yes: mov tag, 1 done: .exit 0 end
循环结构 计数控制循环
例:计算1~100数字和,将结果存入字变量sum中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .model small .stack .data sum dw ? .code .startup xor ax, ax mov cx, 100 again: add ax, cx ; 从100, 99, ..., 1 倒序相加 loop again mov sum, ax .exit 0 end
例:确定字变量wordX中1的最低位数(0~15),并将结果存于变量byteY中,若wordX没有为1的位,将-1存入byteY中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 .model small .stack .data wordx dw 56 bytey db ? .code .startup mov ax, wordx mov cx, 16 mov dl, -1 again: inc dl test ax, 1 ror ax, 1 loope again ; 运算结果为0, 即ax最低位非1, 继续循环 je notfound mov bytey, dl jmp done notfound: mov bytey, -1 done: .exit 0 end
条件控制循环
例:将一个字符串中的所有大写字母改为小写字母,该字符串以 0
结尾
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 .model small .stack .data string db 'Hello, Everybody!', 0 .code .startup mov bx, offset string again: mov al, [bx] or al, al jz done cmp al, 'A' jb next cmp al, 'Z' ja next or al, 20h mov [bx], al next: inc bx jmp again done: .exit 0 end
多重循环
例:冒泡排序将长度已知的数组升序排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 .model small .stack .data array db 56h, 23h, 37h, 78h, 0ffh, 0, 12h count equ ($ - array) / type array .code .startup mov cx, count dec cx ; cx为外层循环次数 outlp: mov dx, cx ; dx为内层循环次数 mov bx, offset array ; bx获取偏移地址 inlp: mov al, [bx] cmp al, [bx + 1] jna next ; 不大于就跳过,实现每次将最大的数放在队尾 xchg al, [bx + 1] mov [bx], al next: inc bx dec dx jnz inlp loop outlp .exit 0 end
子程序设计 1 2 3 过程名 proc[near\far] 过程体 过程名 endp
例:实现回车、换行功能的子程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 dpcref proc push ax push dx mov dl, 0hd mov ah, 2 int 21h mov dl, 0ah mov ah, 2 int 21h pop dx pop ax ret dpcref endp
参数传递的实现:
用寄存器传递参数:将参数存于约定的寄存器中。
用变量传递参数:直接用同一个变量名访问传递的参数。
用堆栈传递参数:主程序将入口参数压入堆栈,子程序弹出数据。
(堆栈传递参数)递归实现阶乘计算:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 .model small .stack .data n dw 3 result dw ? .code .startup mov bx, n push bx call fact pop result .exit 0 fact proc push ax push bp mov bp, sp mov ax, [bp+6] cmp ax, 0 jne fact1 inc ax jmp fact2 fact1: dec ax push ax call fact pop ax mul word ptr[bp+6] fact2: mov [bp+6], ax pop bp pop ax ret fact endp end