汇编语言学习笔记-3

0x00 其他的转移指令

ret与retf

ret使用栈中的数据,来修改IP的内容,来实现近转移(段内转移)。操作如下所示:

1
2
(ip)=((ss)*16+(sp))
(sp)=(sp)+2

执行ret指令时,相当于进行:pop ip

retf则是修改cs和ip的内容,实现远转移(段间转移)。操作如下所示:

1
2
3
4
(ip)=((ss)*16+(sp))
(sp)=(sp)+2
(cs)=((ss)*16+(sp))
(sp)=(sp)+2

当执行retf指令时,相当于进行:pop ip; pop cs

Q&A

​ 补全程序,实现从内存1000:0000处开始执行指令。

image-20230223142420118

1
2
3
mov ax, 1000H
push ax
mov ax, 0000H

call 指令

​ call指令的执行流程:(1)将当前IP或CS:IP压入栈中。(2)转移。

注意,call指令不能实现短转移(8位长),除此之外,call与jmp指令原理相同。

  • call 标号(段内转移)

​ 功能:将当前ip压入栈中,再转到标号处执行指令。相当于:

1
2
3
(sp)=(sp)-2
((ss)*16+(sp))=(ip)
(ip)=(ip)+16位位移(相对于当前ip的位置转移)

​ 用汇编语言,相当于进行:

1
2
push ip
jmp near ptr 标号; 近转移

​ 举例:

image-20230223143556294

1
ax=6
  • call的段间转移为:call far ptr 标号,如下所示:
1
2
3
4
(sp)=(sp)-2
((ss)*16+(sp))=(cs)
(sp)=(sp)-2
((ss)*16+(sp))=(ip)

​ 用汇编语言,相当于:

1
2
3
push cs
push ip
jmp far ptr 标号

Q&A

image-20230223144221512

1
ax=1010H
  • call word ptr 内存单元地址

​ 汇编相当于:

1
2
push ip
jmp word ptr 内存单元地址

image-20230223145240793

1
2
(ip)=0123H
(sp)=0EH
  • call dword ptr 内存单元地址

​ 汇编相当于:

1
2
3
push cs
push ip
jmp dword ptr 内存单元地址

image-20230223145538515

1
2
3
(cs)=0H
(ip)=0123H
(sp)=0CH

image-20230223182824504

image-20230223182840131

1
2
3
4
(1)注:需要说明的是,第一个inc ax相对于start偏移为11h
最终,ax=3,自己思考咯。
(2)注:offset s=1ah
ax=1,bx=0

call与ret的配合使用

image-20230223185542169

1
bx=8

image-20230223190532302

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
; 假设程序从内存1000:0装入
assume cs:code, ss:stack
stack segment
db 8 dup(0)
db 8 dup(0)
stack ends

code segment
start:
mov ax, stack ; ax = 1000, cs = 1001
mov ss, ax ; ss = 1000
mov sp, 16 ; sp = 16
mov ax, 1000 ; ax = 1000
call s ; sp = 14, ss:[sp] = 0EH, ss:[sp+1] = 00H

mov ax, 4c00H
int 21H
s:
add ax, ax ; ax = 2000
ret ; sp = 16, ip = 0EH
code ends
end start

0x01 mul指令

image-20230223192157361

​ 例如,mul byte ptr ds:[0]意思就是(ax)=(al)*(ds*16+0)mul word ptr [bx+si+8]意思就是(dx)=(ax)*((ds)*16+bx+si+8)的高16位(ax)=(ax)*((ds)*16+bx+si+8)的低16位

Q&A

image-20230223192611226

1
(ax)=1000

image-20230223192641772

1
2
(dx)=0FH
(ax)=4240H

0x02 函数参数与返回值的传递

​ 用寄存器存储参数与结果。

Q&A

image-20230223193859935

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
assume cs:code, ds:data
data segment
dw 1,2,3,4,5,6,7,8
dd 0,0,0,0,0,0,0,0
data ends
code segment
cube:
mov si, ax
mul si
mul si
ret
start:
mov ax, data
mov ds, ax
mov cx, 8
mov bx, 0
s:
mov ax, ds:[bx]
call cube
mov ds:[bx+16], ax
mov ds:[bx+17], dx
add bx, 2
loop s

mov ax, 4c00H
int 21H
code ends
end start

但是,如果要传递特别特别多参数呢?或者传回的数据是一个字符串呢?

将参数放到内存中,然后将它们所在内存空间的首地址放到寄存器上,并传递给需要的子程序。

image-20230223201516411

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
assume cs:code, ds:data
data segment
db 'conversation'
data ends
code segment
capital:
mov al, ds:[si]
and al, 11011111B
mov ds:[si], al
inc si
loop capital
ret
start:
mov ax, data
mov ds, ax
mov cx, 12 ; ds存放数据串起始地址,cs存放长度
mov si, 0
call capital
mov ax, 4c00H
int 21H
code ends
end start

image-20230223202430300

注:用栈来传递参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
assume cs:code, ss:stack, ds:data
data segment
db 'conversion',0
data ends
stack segment
db 16 dup(0)
stack ends
code segment
capical:
mov cl, ds:[si]
mov ch, 0
jcxz short ok
and byte ptr cl, 11011111B
push cx
inc si
jmp short capital
ok:
ret
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 16
mov si, 0
call capital

mov ax, 4c00H
int 21H
code ends
end start

image-20230223204541189

​ 下面给出一个示例程序,看看有何问题:

image-20230223204653001

​ 问题就是captial会改变cx的值,因此会影响s循环,这就是寄存器冲突,如何解决呢?在子程序的开始将子程序中所有用到的寄存器中的内容都保存起来,在子程序返回前再恢复。我们可以用栈来保存寄存器中的内容。那么,子程序的标准框架就是:

image-20230223205144184

0x03 标志寄存器

8086CPU的标志寄存器有16位,其中存储的信息通常叫做程序状态字(PSW:Program Status Word)。

flag寄存器

image-20230223210421729

ZF标志(zero flag)

零标志位。ZF记录相关指令执行后,结果是否为0。结果如果为0,那么ZF=1,否则ZF=0。举例如下:

image-20230223214513698

注:运算指令如add|sub|mul|div|inc|or|and对标志寄存器有影响,而传送指令如mov|push|pop对标志寄存器没有影响,inc与loop不影响CF位

PF标志(partial flag)

奇偶标志位。PF记录指令执行后,结果二进制中1的个数。若PF=1,则为偶数,否则PF=0。举例如下:

image-20230223215410606

SF(sign flag)

符号标志位。进行有符号数计算时,执行指令后,结果为负,SF=1,否则SF=0。SF总会按照有符号数计算结果来影响SF标志位。

​ 有符号数与无符号数如下所示:

image-20230223215750054

CF(carry flag)

​ 进行无符号数计算时,记录了运算结果的最高有效位向更高位进位值,或从更高位的借位值。CF来记录这个进/借位信息。举例如下:

image-20230223221316458

OF(overflow flag)

有符号数运算时发生溢出会导致运算结果不正确,这记录在OF中,发生溢出时OF=1,否则OF=0。如下所示:

image-20230223223750925

Q&A

image-20230223224015467

1
2
3
4
5
6
7
8
9
10
CF| OF| SF| ZF| PF
0 | 0 | 0 | 1 | 1
0 | 0 | 0 | 1 | 1
0 | 0 | 1 | 0 | 1
0 | 0 | 1 | 0 | 1
1 | 1 | 0 | 1 | 1
1 | 1 | 0 | 1 | 1
1 | 0 | 0 | 0 | 0
1 | 0 | 0 | 0 | 0
0 | 1 | 1 | 0 | 1

补充

  • adc指令。表示带进位的加法指令,其利用了CF位上记录的进位值。例如adc ax, bx表示(ax)=(ax)+(bx)+CF举例如下:

image-20230224094924822

​ 可能会疑问,adc指令到底有什么用?

add ax, bx其实等价于add al, bl; adc ah, bh。那么,adc指令和add指令相配合就可以对更大(数位更宽)的数据进行加法运算(例如32位宽mov数据加法)。

image-20230224095303159

1
2
3
4
mov bx, 0F000H
add bx, 1000H
mov ax, 1EH
adc ax, 20H

image-20230224095605975

1
2
3
4
5
6
mov cx, 1000H
mov bx, 0F000H
mov ax, 1EH
add cx, 1EF0H
adc bx, 1H
adc ax, 20H

image-20230224100219411

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
assume cs:code, ds:data
data segment
dw 88H,77H,66H,55H,44H,33H,22H,11H
dw 11H,ffH,eeH,ddH,ccH,bbH,aaH,99H
dw 10 dup(0)
data ends
code segment
add128:
push ax
push cx
push si
push di

sub ax, ax ; CF初始设置为0
mov cx, 8
s:
mov ax, ds:[si]
adc ax, ds:[di]
inc si ; inc与loop不会影响cf标志位,而add si, 2会影响cf标志位。
inc si
inc di
inc di
loop s
pop di
pop si
pop cx
pop ax
ret
start:
mov ax, data
mov ds, ax
mov si, 0H
mov di, 8H
call add128

mov ax, 4c00H
int 21H
code ends
end start
  • sbb指令。带借位(CF位)的减法指令。例如sbb ax, bx表示(ax)=(ax)-(bx)-CF

image-20230224102444215

1
2
3
4
5
sub ax, ax ; 设cf=0
mov bx, 1000H
mov ax, 3EH
sbb bx, 2000H
sbb ax, 20H
  • cmp指令。cmp 操作对象1, 操作对象2,计算操作对象1-操作对象2,并将结果设置标志寄存器。举例如下:

image-20230224103130182

​ 对于无符号数而言,有:

image-20230224103312153

​ 注:例如cmp ah, bh,SF=1不等价于(ah)<(bh)

​ 对于有符号数而言,有:

image-20230224105456322

0x04 条件转移指令

​ 对于无符号数而言:

image-20230224105605449

image-20230224105855619

1
2
3
4
5
6
7
8
    cmp ah, bh
je equal
add ah, bh
jmp ok
equal:
add ah, ah
ok:
ret

image-20230224110204924

1
(ax)=1

image-20230224110250206

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(1)
mov ax, data
mov ds, ax
mov ax, 0
mov cx, 8
mov bx, 0
s:
cmp byte ptr ds:[bx], 8H
je s1
jmp s2
s1:
inc ax
s2:
inc bx
loop s
(2) 将(1)中的je s1变为ja s1即可
(3) 将(1)中的je s1变为jb s1即可

​ 其他的条件转移指令:

image-20230224111140352

Q&A

image-20230224111304977

image-20230224111317564

1
2
jb s0
ja s0

0x05 DF标志与串传送指令

​ 是方向标志位。若DF=0,那么每次操作后si、di递增,若DF=1,那么si、di递减。

​ 与之配套的是movsb指令,其以字节为单位传送,即将ds:si指向的内存单元中的字节送入es:di中,然后根据标志寄存器DF位的值,将 si和di递增或递减1。可以描述为:

1
2
3
((es)*16+(di))_byte=((ds)*16+(si))_byte
若DF=0时,则(si)=(si)+1,(di)=(di)+1。
若DF=1时,则(si)=(si)-1,(di)=(di)-1。

​ 还有movsw指令,是以为单位传送。

​ movsw与movsb指令通常配合rep指令使用。rep指令的作用是根据cx的值,重复执行后面的串传送指令,如下所示:

image-20230224122312750

​ 看几个题:

image-20230224122627929

1
2
3
4
5
6
7
8
mov ax, data
mov ds, ax
mov es, ax
mov si, 0
mov di, 16
mov cx, 16
cld ; 设置DF=0,正向传送
rep movsb

image-20230224122930666

1
2
3
4
5
6
7
8
9
mov ax, data
mov es, ax
mov di, 15
mov ax, 0F000H
mov ds, ax
mov si, 0FFFFH
mov cx, 16
std ; DF=1,逆向传送
rep movsb

补充:

pushf :将标志寄存器的值压栈;popf :从栈中弹出数据,送入标志寄存器中。

​ pushf 和 popf为直接访问标志寄存器提供了一种方法。

Q&A

image-20230224123442278

1
(ax)=0000000001000101B

留言

2023-02-23

© 2024 wd-z711

⬆︎TOP