前面学习了 8086 CPU 通过从 CS:IP 指向的内存取指令并执行,内存中数据的方法为 DS:[xxx],以及栈的相关知识。从这里开始,我们正式开始编写汇编代码,来体验下真正和 CPU 寄存器打交道的乐趣。
第一个程序
程序如下:
assume cs:codesg |
汇编语言中包含汇编指令和伪指令,汇编指令是最终要编译成机器码的,而伪指令没有对应的机器码,是由编译器执行的指令,编译器根据伪指令来进行相关的编译工作。
上面的程序中出现了三种伪指令分别是:
- assume cs:codesg
xxx segment
……
xxx ends
end
assume 伪指令的含义是假设,可以将有特定用途的段和相关寄存器关联起来
segment 和 ends 是成对使用的伪指令,segment说明一个段的开始
end 是汇编程序结束标志
mov ax,4c00h 和 int 21H 是程序的返回,就像高级语言中的 return。
计算 2 的 3 次方,代码如下:
assume cs:abc |
[bx]
mov ax [bx] 命令的含义为将一个内存单元的内容送入 ax 中,这个内存单元的长度为 2 字节,存放一个字,偏移地址在 bx 中,段地址在 ds 中。
loop
loop 指令的格式是:loop 标号,其中需要循环的次数通常放在 cx 中,CPU 在执行 loop 指令的分为 2 步:
- (cx)= (cx)- 1
- 判断 cx 中的值,不为零则跳转到标号处执行程序,如果为零则向下执行
用 loop 指令计算 2 * 3,代码如下
assume cs:codesg |
在汇编程序中,数据不能以字母开头。例如 1234H 在汇编源程序中可以直接写 1234H,而 A000H 在汇编源程序中要写成 0A000H。
[bx] 和 loop 综合应用
计算 ffff:0 ~ ffff:b 单元中的数据的和,代码如下:
assume cs:code |
其中 inc bx 表示 bx 中的内容加 1 。
段前缀及其应用
我们知道 mov ax,[bx] 表示把一个内存单元的数据的放入 ax 中,偏移地址由 bx 给出,段地址默认在 ds 中。也可以显示的给出段地址,例如:mov ax,ds:[bx]。8086 有 4 个 16 位段寄存器,分别为 CS、DS、SS、ES,那么很显然 mov ax,[bx] 默认的 ds 段地址也可以由 CS 或 SS 或 ES 显示的给出,如以下所示的都是合法的:
- mov ax,cs:[bx]
- mov ax,ss:[bx]
- mov ax,es:[bx]
段前缀应用举例:将内存 ffff:0 ~ ffff:b 中的内容复制到 0020:0 ~ 0020:b 中,代码如下:
;将内存 ffff:0 ~ ffff:b 中的内容复制到 0020:0 ~ 0020:b 中 |
编译执行完成后用 debug 查看内存情况,发现程序正确执行,把 ffff:0 ~ ffff:b 中的内容复制到了 0020:0 ~ 0020:b 中。

在程序中使用数据
之前的列子我们用到的数据都是在寄存器或者在内存中,比如计算 2 的 3 次方,数据直接放在寄存器中,把某段内存中的数值累加等。比如要计算已知的 n 个数的和该怎么做呢?当然可以把这个 n 个数放到内存中,累加计算其和,但是把这 n 个数放在内存中的什么地方呢?随便拿一块内存来存放数据操作系统允许你这样干吗?其实我们有两种方法取得一块合理的内存来使用,一是在操作系统装载程序的时候为程序分配内存,另一种方法是在程序中向操作系统申请内存。这里我们只讨论在操作系统装载程序为程序分配内存的情况。就像高级语言定义变量一样,汇编也可以定义一个段来存储数据。我们可以使用 db、dw、dd 来定义,它们的具体意义如下:
- db 定义字节类型变量,一个字节数据占1个字节单元,读完一个,偏移量加 1
- dw 定义字类型变量,一个字数据占2个字节单元,读完一个,偏移量加 2
- dd 定义双字类型变量,一个双字数据占4个字节单元,读完一个,偏移量加 4
例如我们要计算 0123H,0456H,0789H,0abcH,0defH,0001H,0002H,0003H 这 8 个数据的和,结果存在 ax 中,代码可如下所示:
assume cs:code |
上面的代码编译链接后用 debug 查看,显示如下:

发现第一条指令并不是我们真正需要 CPU 执行的指令,真的指令从第 17 个字节开始,前面的 16 个字节其实是我们定义的 8 个数据。要想让 CPU 执行指令,在上面的代码中可以加入 start 标号,代码修改为下面的样子:
assume cs:code |
这时候发现 IP 为 10H 才是我们希望 CPU 执行的指令

总结
通过本次学习,我们编写了第一个汇编程序,知道了常用的伪指令:assume、segment ends、end,明白了 [bx] 和 loop 的用法,学习了段前缀及其用法,掌握了在程序中使用数据的方法,并且清楚了 start 标号的用途,收获颇丰,继续努力!