王爽汇编学习(二)

tech2022-09-11  87

​ CPU可以把一段内存当做栈,提供了push和pop指令。push代表进栈,pop代表出栈。利用两个寄存器来指示栈的内存范围,ss寄存器存储着是段地址,sp寄存器存储着是栈顶地址,即偏移地址。

​ 寄存器数量不够的时候,一般使用栈用来暂时存储数据。

​ 一般栈顶是低地址,栈底是高地址,所以push的时候,sp存储的地址会减少,pop的时候,sp存储的地址会增加。可以把栈想象成一个桶,桶底是高地址,接触地面。往里面放东西,栈顶自然离桶底越来越远。

​ 因为CPU并没有指定栈顶和栈底范围,就会出现越界的情况。

编写程序

assume cs:codesg codesg segment mov ax,0123H mov bx,0456H add ax,bx add ax,ax mov ax,4c00H int 21H codesg ends end

分为伪指令和汇编指令,其中伪指令是由编译器执行的,汇编指令由CPU执行的。

通过编译,编译之后还是有伪指令,直到链接后生成exe,才没有伪指令。

可以由运行CS的指向看出,TEST1.exe中的b823是指令的开头。

伪指令

assume:假设’用于将代码段与寄存器关联。例如:assume cs:code,将code代码段关联到cs上,也就是cs指向code代码段。

segment:代码段。用来定义一个代码段。例如:code segment定义了一个code的代码段,然后以code ends结尾。

start:指定程序入口地址。start和end start成对出现

offset:取标号的偏移地址。

start: mov ax,offset start ;相当于mov ax, 0 s: mov ax,offset s ;相当于mov ax,3

寻址方式

idata 代表是十六进制数字常量,比如2000H、2200H等。

直接寻址

idata

寄存器间接寻址

[bx]

寄存器相对寻址

用于结构体:

[bx].idata

用于数组:

idata[si]

idata[di]

用于二维数组:

[bx] [idata]

基址变址寻址

用于二维数组:

[bx] [si]

相对基址变址寻址

用于表格(结构)中的数组项:

[bx].idata[si]

用于二维数组:

idata[bx] [si]

指令数据长度

由寄存器指出指令操作的长度

代表操作16位的数据长度,16位又可以叫做字

mov ax,1

代表操作8位的数据长度,8位又可以叫做字节

mov al,1

由操作符 X ptr 指明内存单元的长度,X在汇编指令中可以为word或者byte

用word ptr 指明了指令访问的内存单元是一个字单元

mov word ptr ds:[0],1

用byte ptr 指明了指令访问的内存单元是一个字节单元

mov byte ptr ds:[0],1

其他方法

有些指令默认了访问的是字单元还是字节单元,比如push指令只进行字操作

push [1000H]

div 指令

除数

在寄存器中获取内存单元中

被除数

如果被除数是32位,就在dx和ax中存放,dx放高位,ax放低位

如果被除数是16位,就放在ax中

结果

如果除数是8位,则al存储商,ah存储余数

如果除数是16位,则ax存储商,dx存储余数

mul 指令

两个相乘的数,要么都是8位,要么都是16位。

8位

一个默认放在al中,另一个放在8位的寄存器或者内存字节单元中

相乘的结果默认放在ax中

16位

一个默认放在ax中,另一个放在16位寄存器或者内存字单元中

相乘的结果高位放在dx中,低位放在ax中

转移指令

可以修改IP,或者同时修改CS和IP的指令统称为转移指令。

转移行为分类:

段内转移:只修改IP,比如 jmp ax

短转移

修改范围:-128~127

近转移

修改范围:-32768~32767

段间转移:同时修改CS和IP,比如jmp 1000:0

转移条件分类:

无条件转移指令

条件转移指令

循环指令

过程

中断

jmp 指令

根据位移进行转移

jmp short 标号,进行的是短转移,不包含具体的偏移地址,只有相对于当前指令偏移地址。

转移的目的地址在指令中

jmp far ptr 标号,进行的是段间转移。

assume cs:codesg codesg segment start: mov ax,0 mov bx,0 jmp far ptr s db 256 dup (0) s: add ax,1 inc ax codesg ends end start
转移地址在寄存器中

jmp ax

转移地址在内存中

jmp word ptr 内存单元地址(段内转移)

mov ax,0123H mov ds:[0],ax jmp word ptr ds:[0]

jmp dword ptr 内存单元地址(段间转移)

mov ax,0123H mov ds:[0],ax mov word ptr ds:[2],0 jmp dword ptr ds:[0]

jcxz 指令

jcxz是有条件转移指令,所有有条件转移指令都是短转移。

如果cs=0,转移到标号出执行,类似于if语句。

if( (cx) == 0 ) jmp short 标号;

loop 指令

loop指令用于循环执行,loop指令是循环指令,所有的循环指令都是短转移。

( cx ) --;

if( ( cx ) != 0 ) jmp short 标号;

ret 和retf 指令

ret 指令是用栈中的数据,修改IP的内容,从而实现近转移。

pop ip ; 相当于执行该语句

retf 指令是用栈中的数据,修改IP的内容,从而实现远转移。

; 相当于执行以下指令 pop ip pop cs

call 指令

CPU执行call指令时,进行两步操作:

将当前的IP或者CS和IP压入栈

转移

call指令,按照我的理解,就是保护好现场,然后再去转移,执行其他指令。

call指令不能实现短转移。

依据位移进行转移

call 标号

; 相当于以下指令 push ip jmp near ptr 标号
转移的目的地址在指令中

call far ptr 标号

; 相当于以下指令 push cs push ip jmp far ptr 标号
转移地址在寄存器中

call ax

; 相当于以下指令 push ip jmp ax
转移地址在内存中

call word ptr 内存单元地址

;相当于以下指令 push ip jmp word ptr 内存单元地址 ; 举例 mov sp,10h mov ax,0123h mov ds:[0],ax call word ptr ds:[0]

call dword ptr 内存单元地址

;相当于以下指令 push cs push ip jmp dword ptr ; 举例 mov sp,10h mov ax,0123h mov ds:[0],ax mov word ptr ds:[2],0 call dword ptr ds:[0]

call 和ret配合使用,可以实现高级语言的函数调用,举例

int fun() { return 1; // 相当于ret指令 } int main() { fun();// 相当于call这个函数 return 0; }
最新回复(0)