王爽汇编学习(二)

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

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

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

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

编写程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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:取标号的偏移地址。

    1
    2
    3
    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 标号,进行的是段间转移。

1
2
3
4
5
6
7
8
9
10
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 内存单元地址(段内转移)

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

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

1
2
3
4
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的内容,从而实现近转移。

1
pop ip					; 相当于执行该语句

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

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

call 指令

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

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

  • 转移

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

call指令不能实现短转移。

依据位移进行转移

call 标号

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

call far ptr 标号

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

call ax

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

call word ptr 内存单元地址

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

call dword ptr 内存单元地址

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

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

1
2
3
4
5
6
7
8
9
int fun()
{
return 1; // 相当于ret指令
}
int main()
{
fun();// 相当于call这个函数
return 0;
}

评论