文章目录
前言
📌
汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优化方面有重要作用。此外,它在逆向工程和安全领域不可或缺,帮助分析软件运行机制并增强漏洞修复能力。
本专栏的汇编语言学习章节主要是依据王爽老师的《汇编语言》来写的,和书中一样为了使学习的过程容易展开,我们采用以8086CPU为中央处理器的PC机来进行学习。
1. DF 标志和串传送指令
1.1 DF 标志
flag的第10位是DF,方向标志位。
在串处理指令中,控制每次操作后si,di的增减。
-
DF = 0:每次操作后si,di递增;
-
DF = 1:每次操作后si,di递减。
1.2 串传送指令
我们再来看下面的一个串传送指令。
汇编语言中的串传送指令用于一次性处理多个数据单元,如MOVS
指令可直接完成源和目标之间的数据传输。这类指令简化了对连续数据的操作,尤其适合字符串或数组的处理,提高了代码的简洁性和执行效率。
1.2.1 字节串传送
-
格式:movsb
-
功能:执行 movsb 指令相当于进行下面几步操作(以字节为单位传送)。
- (1)((es)×16 + (di)) = ((ds) ×16 + (si))
- (2)如果DF = 0则:(si) = (si) + 1,(di) = (di) + 1
- (3)如果DF = 1则: (si) = (si) - 1,(di) = (di) - 1
用汇编语法描述 movsb 的功能如下。
mov es:[di],byte ptr ds:[si] ;8086并不支持这样的指令,这里只是个描述
如果 df=0:
inc si
inc di
如果 df=1:
dec si
dec di
可以看出,movsb 的功能是将 ds:si 指向的内存单元中的字节送入 es:di中,然后根据标志寄存器DF位的值,将 si和di递增或递减。
1.2.2 字串传送
当然,也可以传送一个字,指令如下。
-
格式:movsw
-
功能:将 ds:si指向的内存字单元中word送入es:di中,然后根据标志寄存器DF位的值,将si和di递增2或递减2(以字为单位传送)。
用汇编语法描述 movsw 的功能如下。
mov es:[di],word ptr ds:[si] ;8086并不支持这样的指令,这里只是个描述
如果 df=0:
add si,2
add di,2
如果 df=1:
sub si,2
sub di,2
1.3 与rep配合使用的串传送
movsb和movsw进行的是串传送操作中的一个步骤,一般来说,movsb 和 movsw 都和rep配合使用。
1.3.1 rep movsb
格式如下:
rep movsb
用汇编语法来描述rep movsb
的功能就是:
s: movsb
loop s
可见,rep的作用是根据cx的值,重复执行后面的串传送指令。
由于每执行一次movsb指令,si和di都会递增或递减指向后一个单元或前个单元,则rep movsb
就可以循环实现(cx)个字符的传送。
1.3.2 rep movsw
同理,也可以使用这样的指令:rep movsw
。
相当于:
s: movsw
loop s
1.4 设置DF位
由于flag的DF位决定着串传送指令执行后,si和di改变的方向,所以CPU应该提供相应的指令来对DF位进行设置,从而使程序员能够决定传送的方向。
8086CPU提供下而两条指令对DF位进行设置:
-
cld指令:将标志寄存器的DF位置0
-
std指令:将标志寄存器的DF位置1
1.5 例题演示与巩固
1.5.1 问题一
编程:用串传送指令,将data段中的第一个字符串复制到它后面的空间中。
data segment
db ‘Welcome to masm!’
db 16 dup (0)
data ends
1.5.2 问题一的分析与程序实现
分析:
我们分析一下,使用串传送指令进行数据的传送,需要给它提供一些必要的信息,它们是:
-
① 传送的原始位置:ds:si;
-
② 传送的目的位置:es:di;
-
③ 传送的长度:cx;
-
④ 传送的方向:DF。
在这个问题中,这些信息如下:
-
① 传送的原始位置:data:0;
-
② 传送的目的位置:data:16;
-
③ 传送的长度:16;
-
④ 传送的方向: 因为正向传送(每次串传送指令执行后,si和di 递增)比较方便,所以设置DF=0。
编写程序:
好了,明确了这些信息之后,我们来编写程序,如下。
mov ax,data
mov ds,ax
mov si,0 ;ds:si指向data:0
mov es,ax
mov di,16 ;es:di指向data:16
mov cx,16 ;(cx)=16,rep循环16次
cld ;设置DF=0,正向传送
rep movsb
1.5.3 问题二
编程:用串传送指令,将F000H段中的最后16个字符复制到data段中。
data segment
db 16 dup (0)
data ends
1.5.4 问题二的分析与程序实现
分析:
我们还是先来看一下应该为串传送指令提供什么样的信息。
要传送的字符串位于F000H段的最后16个单元中,那么它的最后一个字符的位置:F000:FFFF,是显而易见的。我们可以将ds:si指向 F000H段的最后一个单元,将es:di指向data段中的最后一个单元,然后逆向(即从高地址向低地址)传送16个字节即可。
相关信息如下:
-
① 传送的原始位置:F000:FFFF;
-
② 传送的目的位置:data:000F;
-
③ 传送的长度:16;
-
④ 传送的方向:因为逆向传送(每次串传送指令执行后,si 和 di 递减)比较方便,所以设置 DF=1。
编写程序:
mov ax,0f000h
mov ds,ax
mov si,0ffffh ;ds:si指向f000:ffff
mov ax,data
mov es,ax
mov di,15 ;es:di指向data:000F
mov cx,16 ;(cx)=16,rep循环16次
std ;设置DF=1,逆向传送
rep movsb
2. pushf 和 popf
pushf:将标志寄存器的值压栈;
popf:从栈中弹出数据,送入标志寄存器中。
pushf 和 popf,为直接访问标志寄存器提供了一种方法。
3. 标志寄存器在 Debug 中的表示
在Debug中,标志寄存器是按照有意义的各个标志位单独表示的。在Debug中,我们可以看到下面的信息。
下面列出 Debug 对我们已知的标志位的表示。
结语
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。
也可以点点关注,避免以后找不到我哦!
Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!