movsb可以理解为 move string byte,即字节传送指令。
来看一个示例,汇编代码如下:
mov ax,0x0050
mov es,ax
mov ax,0x07C0
mov ds,ax
jmp near Code
;把这5个数据复制到起始地址0x00500的位置
Data:
db 0xAA,0x11,0x22,0x33,0x44
Code:
mov al,byte[ds:Data+0]
mov byte[es:0],al
mov al,byte[ds:Data+1]
mov byte[es:1],al
mov al,byte[ds:Data+2]
mov byte[es:2],al
mov al,byte[ds:Data+3]
mov byte[es:3],al
mov al,byte[ds:Data+4]
mov byte[es:4],al
End:
jmp near End
times 510-($-$$) db 0x00
db 0x55,0xAA
如果你已经看完了前面的文章,那么这个程序对你来说很容易理解,上面这段代码的作用是将0xAA,0x11,0x22,0x33,0x44这几个数据复制到0x00500内存地址的位置,但是这个程序有很多重复性的代码,有同学可能会说可以用loop指令来代替做这些重复的工作
mov ax,0x07C0
mov ds,ax
jmp near Code
;把这5个数据复制到起始地址0x00500的位置
Data:
db 0xAA,0x11,0x22,0x33,0x44
Code:
mov ax,0x0050
mov es,ax ;目标地址段寄存器初始化
mov di,0+4 ;目标地址段偏移寄存器初始化
mov ax,0x07c0
mov ds,ax ;源地址段寄存器初始化
mov si,Data+4 ;源地址段偏移寄存器初始化
mov cx,Code-Data ;循环次数(移动次数)
StartMove:
mov al, byte[ds:si]
mov byte[es:di],al
dec si
dec di
loop StartMove ;循环体定义了移动的方向和每次移动的字节
End:
jmp near End
times 510-($-$$) db 0x00
db 0x55,0xAA
这段代码可以理解为:
即便是使用loop指令,上面的代码还是没有简化很多。在我们汇编指令中有一个指令,它的作用就是专业负责,把一个地方的内存数据,复制到另外一个地方,那就是movsb指令。
给出的汇编代码如下:
mov ax,0x07C0
mov ds,ax
jmp near Code
;把这5个数据复制到起始地址0x00500的位置
Data:
db 0xAA,0x11,0x22,0x33,0x44
Code:
mov ax,0x0050
mov es,ax ;目标地址段寄存器初始化
mov di,0 ;目标地址段偏移寄存器初始化
mov ax,0x07c0
mov ds,ax ;源地址段寄存器初始化
mov si,Data ;源地址段偏移寄存器初始化
movsb ;通过movsb指令复制
End:
jmp near End
times 510-($-$$) db 0x00
db 0x55,0xAA
执行结果:
movsb这条指令编译后,在内部中实际上变成了movsb byte ptr es:[di], byte ptr ds:[si]了,这条指令的意思就是从源地址中复制一个字节数据到目的地址,其中es:[di]表示目的内存地址,而ds:[si]表示源内存地址,寄存器ES和寄存器DS就说明了这一点。
需要注意的是movsb指令每次执行完成之后,di和si都会加1或者减1,也就是说你可以多次执行movsb指令复制数据,例如更改代码为:
movsb ;执行5次movsb指令
movsb
movsb
movsb
movsb
执行结果如下:
那有同学可能会问了,什么时候movsb指令会让di和si都减一呢?,不知道大家是否还记得我们之前学过的标志寄存器EFL,在这个标志寄存器中有一个DF标志位,如下图所示:
DF标志位是一个方向标志,顾名思义,当DF标志位=0的时候,movsb指令执行完成之后, di和si就会加1。当DF标志位=1的时候,movsb指令执行完成之后, di和si就会减1。可以使用cld和std指令来设置DF标志寄存器的值,cld会设置DF标志位的值为0,std会设置DF标志位的值为1。
修改代码:
std ;设置DF标志位为1
movsb ;通过movsb指令复制
movsb
movsb
movsb
movsb
执行结果如下:
当执行std指令时,就会把DF标志位的值修改为1。
以上的代码还是可以继续优化的,例如当有大量的数据时每次都要重复执行movsb指令,如果还是按照之前的方法的话,就会出现大量的重复代码,因此我们可以使用rep movsb指令,这条指令的作用是,只要寄存器CX不为0,就会重复执行movsb指令,直到CX等于0为止。
最终的汇编代码如下:
mov ax,0x07C0
mov ds,ax
jmp near Code
;把这5个数据复制到起始地址0x00500的位置
Data:
db 0xAA,0x11,0x22,0x33,0x44
Code:
mov ax,0x0050
mov es,ax ;目标地址段寄存器初始化
mov di,0+4 ;目标地址段偏移寄存器初始化
mov ax,0x07c0
mov ds,ax ;源地址段寄存器初始化
mov si,Data+4 ;源地址段偏移寄存器初始化
mov cx,Code-Data
std ;设置DF标志位的值为1
rep movsb ;如果CX不为0就重复执行movsb指令
End:
jmp near End
times 510-($-$$) db 0x00
db 0x55,0xAA
学会了movsb指令,那么对于movsw指令就可以举一反三了,movsw的意思是mov string word ,字传送指令。配合cx寄存器和rep movsw这个指令,也可以实现批量复制。执行完movsw或者rep movsw指令,si和di的值会增加2,或者减少2,这取决于DF标志位的值是1还是0。
下面直接给出代码:
jmp near Code
Data:
db 0xAA,0x11,0x22,0x33,0x44,0x55
Code:
mov ax,0x0050
mov es,ax ;目标地址段寄存器初始化
mov di,0 ;目标地址偏移寄存器初始化
mov ax,0x07C0
mov ds,ax ;原始地址段寄存器初始化
mov si,Data ;原始地址偏移寄存器初始化
mov cx,(Code-Data)/2
rep movsw ;一次性复制一个字,即两个字节的数据
End:
jmp near End
times 510-($-$$) db 0x00
db 0x55,0xAA