目录
一、x86汇编指令
1.1 指令简介
指令通常由操作码和操作数两部分构成。操作码定义了计算机需要执行的具体操作类型,例如数据传送、算术运算、位移操作或条件跳转等,它是构成指令的核心要素。操作数则是参与指令执行的实体,即操作所针对的数据对象。
并非所有指令都需要操作数;一些指令可能不需要任何操作数。常见的指令可能包含一个或两个操作数,而少数指令可能包含三个甚至四个操作数。
1.2 操作码和操作数
每种指令的操作码:
操作码通过一个唯一的助记符来表示,这个助记符通常是该指令功能的英文缩写,它对应着机器指令中的一个特定的二进制编码。
指令中的操作数:
操作数可以是一个具体的数值,也可以是数据存放的寄存器,或者是指向主存储器中数据位置的地址。
以指令 `MOV AX, 12` 为例:
操作码是 `MOV`,它表示移动操作。
目标操作数是 `AX`,这是一个寄存器,表示数据将被移动到的位置。
源操作数是 `12`,这是一个具体的数值,表示将要被移动的数据。
1.3 指令的助记符格式
指令的助记符格式通常表示为:
操作码 操作数1, 操作数2; 注释
操作数2,通常称为源操作数(src),它是指令操作中的一个参与对象。
操作数1,通常称为目的操作数(dest),它不仅作为指令操作的一个对象,还用于存放指令操作的结果。
例如:
`MOV dest, src` 这条指令的注释解释为 "dest = src",表示将源操作数src的值传送到目的操作数dest。
`MOV AL, 05` 这条指令的注释解释为 "AL = 05",表示将数值05传送到寄存器AL中。
`MOV BX, AX` 这条指令的注释解释为 "BX = AX",表示将寄存器AX的值传送到寄存器BX中。
1.4 指令操作数表达方式
指令操作数的表达方式如下:
- `r8` 表示任意一个8位通用寄存器,包括 AH, AL, BH, BL, CH, CL, DH, DL。
- `r16` 表示任意一个16位通用寄存器,包括 AX, BX, CX, DX, SI, DI, BP, SP。
- `reg` 代表 `r8` 或 `r16`,即可以是任意一个8位或16位通用寄存器。
- `seg` 表示段寄存器,包括 CS, DS, ES, SS。
- `m8` 表示一个8位存储器操作数单元,可以使用所有主存寻址方式。
- `m16` 表示一个16位存储器操作数单元,同样可以使用所有主存寻址方式。
- `mem` 代表 `m8` 或 `m16`,即可以是任意一个8位或16位存储器操作数单元。
- `i8` 表示一个8位立即数。
- `i16` 表示一个16位立即数。
- `imm` 代表 `i8` 或 `i16`,即可以是任意一个8位或16位立即数。
- `dest` 表示目的操作数,即指令操作结果存放的位置。
- `src` 表示源操作数,即指令操作中参与操作的数据来源。
1.5 汇编语言的多种表达方式
汇编语言中的寻址方式具有多种表达形式,以下是一些例子:
`MOV AX, [BX][SI]` 这种写法与 `MOV AX, [BX+SI]` 是等价的,都表示将 BX 和 SI 寄存器中的值相加作为内存地址,然后从该地址读取数据到 AX 寄存器。
`MOV AX, 12[SI]` 这种写法与 `MOV AX, [SI+12]` 是等价的,都表示将 SI 寄存器的值加上 12 作为内存地址,然后从该地址读取数据到 AX 寄存器。
`MOV AX, 12[BX][SI]` 这种写法与 `MOV AX, 12[BX+SI]` 或 `MOV AX, [BX+SI+12]` 是等价的,都表示将 BX 和 SI 寄存器的值相加后再加 12 作为内存地址,然后从该地址读取数据到 AX 寄存器。
这些不同的表达方式在功能上是相同的,只是语法上有所不同,它们都遵循相同的寻址规则。
二、寻址方式
从8086处理器的机器代码格式出发,我们可以深入探讨以下几种寻址方式:
1) **立即数寻址方式**
2) **寄存器寻址方式**
3) **存储器寻址方式**
通过研究这些寻址方式,我们可以更好地理解8086汇编语言指令的格式,特别是操作数的表达方法。这将为我们深入学习8086指令系统打下坚实的基础,使我们能够编写和理解更复杂的汇编语言程序。
2.1 立即数寻址
立即数寻址方式是指在指令中,操作数直接以数值的形式存放在机器代码中,紧随操作码之后。这个操作数被称为立即数(imm),它可以是一个8位数值(i8,范围从00到FF),也可以是一个16位数值(i16,范围从0000到FFFF)。立即数寻址方式通常用于给寄存器赋值,例如在指令 `MOV AX, 1234h` 中,`1234h` 就是一个立即数,它直接被赋值给寄存器AX。
2.2 寄存器寻址
寄存器寻址方式是指操作数存放在CPU的内部寄存器中。这些寄存器可以是:
8位寄存器(r8),包括 AH、AL、BH、BL、CH、CL、DH、DL。
16位寄存器(r16),包括 AX、BX、CX、DX、SI、DI、BP、SP。
4个段寄存器(seg),包括 CS、DS、SS、ES。
在寄存器寻址方式中,指令直接引用这些寄存器作为操作数。例如,在指令 `MOV BX, AX` 中,`AX` 和 `BX` 都是寄存器操作数。
2.3 存储器寻址
存储器寻址方式是指在指令中提供操作数在主存储器中的地址信息,这个地址称为有效地址(EA,Effective Address)。段地址则默认在某个段寄存器中,或者可以通过段超越前缀来指定。
8086处理器设计了多种存储器寻址方式,包括:
1) **直接寻址方式**:指令中直接给出操作数的偏移地址。
2) **寄存器间接寻址方式**:操作数的偏移地址存放在某个寄存器中。
3) **寄存器相对寻址方式**:操作数的偏移地址是某个寄存器的内容加上一个立即数。
4) **基址变址寻址方式**:操作数的偏移地址是两个寄存器内容的和。
5) **相对基址变址寻址方式**:操作数的偏移地址是两个寄存器内容的和再加上一个立即数。
这些寻址方式提供了灵活的方法来访问存储器中的数据,使得程序能够根据需要动态地计算操作数的地址。
2.4 直接寻址
直接寻址方式是指在指令中直接给出操作数的有效地址。默认情况下,段地址位于数据段寄存器(DS)中,但可以通过使用段超越前缀来改变默认的段寄存器。
例如:
- `MOV AX, [2000H]` 这条指令中,有效地址是 `2000H`,默认段地址在 DS 段寄存器中,因此操作数的实际地址是 DS:[2000H]。对应的机器指令操作码可能是 `A1 00 20`。
- `MOV AX, ES:[2000H]` 这条指令中,使用了段超越前缀 ES,因此操作数的实际地址是 ES:[2000H]。对应的机器指令操作码可能是 `26 A1 00 20`,其中 `26` 是 ES 段寄存器的段超越前缀。
2.5 寄存器间接寻址
寄存器间接寻址方式是指操作数的有效地址存放在基址寄存器(BX)或变址寄存器(SI、DI)中。默认情况下,段地址位于数据段寄存器(DS)中,但可以通过使用段超越前缀来改变默认的段寄存器。
例如:
`MOV AX, [SI]` 这条指令中,有效地址存放在变址寄存器 SI 中,默认段地址在 DS 段寄存器中,因此操作数的实际地址是 DS:[SI]。
2.6 寄存器相对寻址
寄存器相对寻址方式是指操作数的有效地址是寄存器内容与一个有符号的8位或16位位移量之和。参与计算的寄存器可以是 BX、BP、SI 或 DI。有效地址的计算公式为:有效地址 = BX/BP/SI/DI + 8/16位位移量。
段地址的默认设置如下:
当使用 BX、SI 或 DI 寄存器时,默认段地址在 DS 段寄存器中。
当使用 BP 寄存器时,默认段地址在 SS 段寄存器中。
可以通过使用段超越前缀来改变默认的段寄存器。
例如:
`MOV AX, [DI+06]` 这条指令中,有效地址是 DI 寄存器的内容加上位移量 06,默认段地址在 DS 段寄存器中,因此操作数的实际地址是 DS:[DI+06]。
`MOV AX, [BP+06]` 这条指令中,有效地址是 BP 寄存器的内容加上位移量 06,默认段地址在 SS 段寄存器中,因此操作数的实际地址是 SS:[BP+06]。
2.7 基址变址寻址
基址变址寻址方式是指操作数的有效地址由基址寄存器(BX 或 BP)的内容加上变址寄存器(SI 或 DI)的内容构成。有效地址的计算公式为:有效地址 = BX/BP + SI/DI。
段地址的默认设置如下:
当使用 BX 作为基址寄存器时,默认段地址在 DS 段寄存器中。
当使用 BP 作为基址寄存器时,默认段地址在 SS 段寄存器中。
可以通过使用段超越前缀来改变默认的段寄存器。
例子:
`MOV AX, [BX+SI]` 这条指令中,有效地址是 BX 寄存器的内容加上 SI 寄存器的内容,默认段地址在 DS 段寄存器中,因此操作数的实际地址是 DS:[BX+SI]。
`MOV AX, [BP+DI]` 这条指令中,有效地址是 BP 寄存器的内容加上 DI 寄存器的内容,默认段地址在 SS 段寄存器中,因此操作数的实际地址是 SS:[BP+DI]。
`MOV AX, DS:[BP+DI]` 这条指令中,使用了段超越前缀 DS,因此操作数的实际地址是 DS:[BP+DI]。
2.8 相对基址变址寻址
相对基址变址寻址方式是指操作数的有效地址是基址寄存器(BX 或 BP)、变址寄存器(SI 或 DI)与一个8位或16位位移量之和。有效地址的计算公式为:有效地址 = BX/BP + SI/DI + 8/16位位移量。
段地址的默认设置如下:
当使用 BX 作为基址寄存器时,默认段地址在 DS 段寄存器中。
当使用 BP 作为基址寄存器时,默认段地址在 SS 段寄存器中。
可以通过使用段超越前缀来改变默认的段寄存器。
例子:
`MOV AX, [BX+SI+06H]` 这条指令中,有效地址是 BX 寄存器的内容加上 SI 寄存器的内容再加上位移量 06H,默认段地址在 DS 段寄存器中,因此操作数的实际地址是 DS:[BX+SI+06H]。
三、汇编中的栈
堆栈是一种遵循“后进先出”(LIFO,Last In First Out)或“先进后出”(FILO,First In Last Out)原则的主存储区域,它位于堆栈段中,由堆栈段寄存器(SS)记录其段地址。
堆栈只有一个出口,即当前的栈顶,这个位置由堆栈指针寄存器(SP)来指定。在堆栈中,栈顶是地址较小的一端(低端),而栈底则保持不变。数据项被压入堆栈时,SP 的值会减小,指向新的栈顶;数据项被弹出堆栈时,SP 的值会增加,指向新的栈顶位置。
3.1 栈的基本操作
堆栈的基本操作非常简单,主要包括两种操作:进栈(PUSH)和出栈(POP),它们分别对应于两条指令。
**PUSH**:进栈指令首先将堆栈指针(SP)减去2(因为8086处理器是16位的,所以每次操作一个字,即两个字节),然后将一个字操作数存入堆栈的顶部。
**POP**:出栈指令则是将堆栈顶部的字操作数传送至指定的目的操作数,随后将堆栈指针(SP)增加2,以反映堆栈顶部的变化。
这两种操作确保了堆栈的“后进先出”特性,使得最后压入堆栈的数据项会最先被弹出。
3.2 进栈操作
进栈操作是通过进栈指令(PUSH)来实现的。进栈指令的原型可以是对16位寄存器(r16)、16位存储器操作数(m16)或段寄存器(seg)的操作。
例子:
`PUSH AX`:这条指令将寄存器AX的内容压入堆栈。
`PUSH [2000]`:这条指令将存储器地址2000处的16位数据压入堆栈。
在执行进栈操作时,堆栈指针(SP)会先减2,以指向新的栈顶位置,然后将指定的操作数存入这个新位置。
3.3 出栈操作
出栈操作是通过出栈指令(POP)来实现的。出栈指令的原型可以是对16位寄存器(r16)、16位存储器操作数(m16)或段寄存器(seg)的操作。
例子:
`POP AX`:这条指令将堆栈顶部的16位数据弹出,并存入寄存器AX中。
`POP [2000h]`:这条指令将堆栈顶部的16位数据弹出,并存入存储器地址2000h处。
在执行出栈操作时,首先将堆栈顶部的数据传送到指定的目的操作数,然后将堆栈指针(SP)增加2,以反映堆栈顶部的变化。
3.4 堆栈操作的特点
堆栈操作的特点如下:
1)堆栈操作的单位是字(16位),无论是进栈还是出栈,都只对字量数据进行操作。
2)当字量数据从栈顶压入或弹出时,遵循低地址字节送低字节,高地址字节送高字节的规则。
3)堆栈操作遵循“先进后出”原则,但由于存储器寻址方式的存在,可以随机存取堆栈中的数据。
4)堆栈常用于临时存放数据、传递参数以及保存和恢复寄存器状态。
这些特点使得堆栈在程序执行过程中扮演着重要的角色,尤其是在函数调用、中断处理和局部变量管理等方面。