–
一、汇编语言
使用汇编语言编写一个完整的程序,步骤大致如下:
- 编写源代码,文件名拓展名为
.asm
- 编译、链接(可以使用微软的MASM编译器)
- 调试、运行
1.1. 语言组成
汇编语言由2类指令组成:
- 汇编指令:如
mov
、add
、sub
等,有对应的机器指令,可以被编译为机器指令,最终被CPU执行。 - 伪指令:如
assume
、segment
、ends
、end
等,没有对应的机器指令,由编译器解析,最终不被CPU执行,注释以分号开头。
1 | assume cs:code ; assume是声明一下code段是cs段(代码段),code是自己命名的 |
1.2. 中断
中断是由于软件的或硬件的信号,使得CPU暂停当前的任务,转而去执行另一段子程序。也就是说,在程序运行过程中,系统出现了一个必须由CPU立即处理的情况,此时,CPU暂时中止当前程序的执行转而处理这个新情况的过程就叫做中断。
中断的分类:
- 硬中断(外中断),由外部设备(比如网卡、硬盘)随机引发的,比如当网卡收到数据包的时候,就会发出一个中断。
- 软中断(内中断),由执行中断指令产生的,可以通过程序控制触发。
从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,跳到中断处理程序的入口点,进行中断处理。
可以通过指令int n
产生中断。n
是中断码,内存中有一张中断向量表,用来存放中断码对应中断处理程序的入口地址。CPU在接收到中断信号后,暂停当前正在执行的程序,跳转到中断码对应的中断向量表地址处,去执行中断处理程序。
常见中断指令:int 10h
:用于执行BIOS中断。int 3
:是“断点中断”,用于调试程序。int 21h
:用于执行DOS系统功能调用,AH寄存器存储功能号。
1.3. DOS系统功能调用
DOS系统功能调用由DOS提供的一组实现特殊功能的子程序供程序员在编写自己的程序时调用,以减轻编程的工作量。涉及屏幕显示、文件管理、I/O管理等等。
每个子程序都有一个功能号,所有的功能调用的格式都是一致的。调用的步骤大致如下:
系统功能号送到寄存器AH中;
入口参数送到指定的寄存器中;
用
INT 21H
指令执行功能调用;根据出口参数分析功能调用执行情况。
如果将代码、数据、栈都放到一个段里面会显得混乱,编程时要随时注意何处是数据、何处是栈、何处是代码。一个段的大小<=64KB,这样就会让数据、代码、栈的大小受到极大的限制。所以,一般会考虑使用多个段来存放数据、代码、栈。
1 | assume cs:code, ds:data |
1.4. 给数据起标号
1 | assume cs:code, ds:data |
1.5. 在代码段中存放数据
如下,计算1122h、3344h、5566h的和,结果存放在ax中。
1 | assume cs:code |
dw
(define word):定义了3个字型数据,数据之间用逗号隔开。类似的还有db
(define byte)、dd
(define double word)。start
和end start
是对应的,end start
标记程序的执行入口(start之前是定义的数据)。
对比高级语言的常量定义就可以发现,常量在编译的那一刻就已经确定了在内存中地址,因为常量最终是存放在数据段中的。
1.6. 段前缀
mov ax, [bx]
中bx的值是偏移地址,段地址默认在ds中。我们也可以明确地标明段地址,比如:
1 | mov ax, ds:[bx] |
上面的ds:
、cs:
、ss:
、es:
称为段前缀,由于cs、ss都有自己的特殊用途,一般就使用es作为附加段寄存器。
1.7. loop指令
loop指令和cx配合使用,用于循环执行重复的操作,类似于高级语言中的for、while循环。
使用格式:
1 | mov cx, 执行次数 |
执行流程:
- 让cx的值减一,即
cx = cx – 1
- 判断cx的值:
- 如果不为零转至标号处执行程序,然后重复1
- 如果为零则执行loop后面的代码
案例:计算$2^6$
1 | ; 不使用loop和cx |
1.8. 打印HelloWorld
第一种方式:
1 | assume cs:code, ds:data |
- 使用
int 21h
显示的字符串必须要以$
结尾。 - 字符串可以用双引号或者单引号括住。
第二种方式:
1 | assume cs:code, ds:data |
通过一个字节来显示字符的颜色。
1 | 0 000 0 000 |
二、寻址
8086指令能处理2种尺寸的数据:byte、word。
思考:mov [0], 20H
指令是否正确?mov byte ptr [0], 20H
:将20H放入0位置内存的字节单元,占用1个字节。mov word ptr [0], 20H
:将20H放入0位置内存的字单元,占用2个字节。
很多指令都可以通过byte ptr
或者word ptr
来指明所需要操作内存的数据长度。
1 | inc byte ptr [0] |
有些指令有默认的操作数据长度,比如push [0]
、pop [0]
的操作数据长度只能是2个字节。