串操作
- 串操作指令
- 串方向
- 重复前缀
;=================================||
- movsb
- 功能:
- mov string
- 将si地址的内容拷贝到di地址
- DF=0(up)
- si<-si+1
- di<-di+1
- DF=1(DN)
- si<-si-1
- di<-di-1
;=================================
- movsw
- 功能:
- mov string
- 将si地址的内容拷贝到di地址
- DF=0(up)
- si<-si+2
- di<-di+2
- DF=1(DN)
- si<-si-2
- di<-di-2
;=================================||
- stosb
- 功能
- store string
- 将al或者ax内存存储到di地址
- (dl)<-al
- DF=0(up)
- di<-di+1
- DF=1(DN)
- di<-di-1
;=================================
- stosw
- 功能
- store string
- 将al或者ax内存存储到di地址
- (di)<-ax
- DF=0(up)
- di<-di+2
- DF=1(DN)
- di<-di-2
;=================================||
- lodsb
- 功能
- load string
- 将si地址内容读入al或ax
- al<-(si)
- DF=0(up)
- si<-si+1
- DF=1(DN)
- si<-si-1
;=================================
- lodsw
- 功能
- load string
- 将si地址内容读入al或ax
- ax<-(si)
- DF=0(up)
- si<-si+2
- DF=1(DN)
- si<-si-2
;=================================||
- cmpsb
- 功能
- cmp string
- si地址内容减去di地址内容,不存储结果,影响标志位
- (si)-(di)
- DF=0(up)
- si<-si+1
- di<-di+1
- DF=1(DN)
- si<-si-1
- di<-di-1
;=================================
- cmpsw
- 功能
- cmp string
- si地址内容减去di地址内容,不存储结果,影响标志位
- (si)-(di)
- DF=0(up)
- si<-si+2
- di<-di+2
- DF=1(DN)
- si<-si-2
- di<-di-2
;=================================||
- scasb
- 功能
- scan string
- al减去di地址内容,不存储结果,影响标志位
- al-(di)或者ax-(di)
- DF=0(up)
- di<-di+1
- DF=1(DN)
- di<-di-1
;=================================
- scas2
- 功能
- scan string
- ax减去di地址内容,不存储结果,影响标志位
- al-(di)或者ax-(di)
- DF=0(up)
- di<-di+2
- DF=1(DN)
- di<-di-2
;=================================||
- 串方向
- CLD ;DF置0,自动加
- STD ;DF置1,自动减
cmpsb示例:
data segment
g_szSrc db "hello world$"
g_szDst db "heLL"
data ends
stack segment stack
dw 20h dup(?)
top label word
stack ends
code segment
assume ds:data,cs:code,ss:stack
p proc far
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
lea sp,top
;=====================================
;为es赋值
mov ax,ds
mov es,ax
lea si,g_szSrc;
lea di,g_szDst
CLD
cmpsb
cmpsb
cmpsb
cmpsb
p endp
code ends
end p
执行2次
下一次就是对比l和L了,再次执行
标志位改变,但是源数据不变
重复前缀
- 重复前缀
- 串操作指令一般都配合重复前缀使用,实现内存批量操作。
- 注意(下图的loads应该是lods)
- repne:重复执行器后面的指令,CX或ECX存放最多比较次数,DI或EDI存放查找表首地址,AL或AX或EAX存放想要查找的内容。当(CX或ECX)=0或ZF=1退出重复。否则,(CX或ECX)自动减一,执行其后面的串指令。CX或ECX为0结束是因为已经查表完毕,没有匹配到;ZF=1说明“比较的结果为0”,也就是查找到一样的内容,说明匹配到了想要查找的内容。
movs和rep的配对示例
data segment
g_szSrc db "hello world$"
g_szDst db 255 dup(00)
g_szDst0 db "heLL"
data ends
stack segment stack
dw 20h dup(?)
top label word
stack ends
code segment
assume ds:data,cs:code,ss:stack
p proc far
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
lea sp,top
;=====================================
;为es赋值
mov ax,ds
mov es,ax
lea si,g_szSrc;mov ax,offset g_szSrc
lea di,g_szDst
CLD
mov cx,offset g_szDst - offset g_szSrc
rep movsb
xor ax,ax
p endp
code ends
end p
流程转移指令
- 无条件跳转
- 条件跳转
- LOOP
- 无条件跳转
段内跳转示例:
data segment
g_szSrc db "hello world$"
g_szDst db 255 dup(00)
g_szDst0 db "heLL"
data ends
stack segment stack
dw 20h dup(?)
top label word
stack ends
code segment
assume ds:data,cs:code,ss:stack
p proc far
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
lea sp,top
;=====================================
jmp LABEL1
mov ax,ax
LABEL1:
jmp LABEL2
mov bx,bx
LABEL2:
mov ah,4ch
int 21h
p endp
code ends
end p
段间跳转示例:
data segment
g_szSrc db "hello world$"
g_szDst db 255 dup(00)
g_szDst0 db "heLL"
data ends
stack segment stack
dw 20h dup(?)
top label word
stack ends
code_seg2 segment
LABELT:
mov si,si
mov ah,4ch
int 21h
code_seg2 ends
code segment
assume ds:data,cs:code,ss:stack
p proc far
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
lea sp,top
;=====================================
jmp far ptr LABELT
mov ah,4ch
int 21h
p endp
code ends
end p
- 指令偏移计算
- 指令中的偏移值是下一条指令到目的地址的偏移
- 指令偏移计算练习
- 120h-102h-2h = 1c -> EB 1C (后面的-2h,是根据120h-102h可以很明显得到该跳转指令短跳就可以了,因此该jmp指令2字节的机器码就够了,因此是键2h,后面同理)
- 320h-102h=21Eh 很明显一个字节是存不下的,因此应当时3字节机器码的近跳转。21Eh-3h = 21B 所以机器码应该是 E9 1B 02
- 0020h-0102h=ff1eh,很明显再减去2h肯定不够一个字节的存放,因此应该是三字节的近跳 ,ff13h-3h=ff1bh E9 1B FF
- E9 FB FC
- …
- 使用寄存器间接转移
示例:
data segment
g_szSrc db "hello world$"
g_szDst db 255 dup(00)
g_szDst0 db "heLL"
data ends
stack segment stack
dw 20h dup(?)
top label word
stack ends
code segment
assume ds:data,cs:code,ss:stack
p proc far
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
lea sp,top
;=====================================
lea ax,LABEL2
jmp ax
mov ax,ax
LABEL2:
xor ax,ax
mov ah,4ch
int 21h
p endp
code ends
end p
- 使用EA的间接转移
条件跳转
- 条件跳转
- 依据标志位判断,条件成立则跳转,条件不成立则不跳。
- 单条件跳转
- 有符号数判断
- 无符号数判断
作业
1、SCASB指令的作用是什么?叙述指令REPE SCASB指令所完成的功能。
scasb:al减去di地址内容,不存储结果,影响标志位.al-(di)或者ax-(di).
- repen是重复执行指令。
- scas是用来搜索字符
- 一些标志位参数:
- DF:方向
- CX:索要搜索的串的长度
- AX:索要搜索的数据
- DI:要搜索的那条串
用法1:
- 计算字符串的长度
;需要将di指向所搜索的那条字符串
mov cx,FFFF;设置循环次数,这里用于计算字符串的长度所以设置为最大
xor ax,ax ;将ax置0 字符串以\0结尾时
cld ;将DF置0
repne scasb;开始搜索
neg cx ;得到cx所减的次数
dec cx ;长度不包括\0
- 计算字符串的长度
检测代码中的一些特殊指令(如断点,花指令,以及一些反调试代码)
2、指令REPNE SCASB结束执行的条件是什么?
CX=0或ZF=1;
3、REP前缀的作用是什么?能用指令REP LODSB读取DS:SI所指内存中的每个字符来进行处理吗?若不能,请说明原因。
重复执行后面的指令,不能,因为其只是重复执行将数据传送到al或ax而已,该指令并不能对字符的数据进行处理。
7、从键盘输入文件名,查找指定文件(不超过255)中是否含有字母B。
;======================================;======================================data segment
data segment
FileNameFuf db 100,?,100 dup(0)
TheFile db 100 dup(0)
file db 'test.txt',0
buf db 100 dup(?)
fh dw ?
error_msg db 0dh,0ah,'error!','$'
success_msg db 0dh,0ah,'done!','$'
FindSuccess_msg db 0dh,0ah,'FinhisAlpabet!','$'
FindFaile_msg db 0dh,0ah,'NotFinhisAlpabet!','$'
data ends
;======================================;======================================stack segment
stack segment stack
dw 20h dup(?)
top label word
stack ends
;======================================;======================================code segment
code segment
assume ds:data,cs:code,ss:stack
start:
;=========================================INIT
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
lea sp,top
;=========================================code todo
;输入文件名
lea dx,FileNameFuf
mov ah,0ah ;键盘输入字符串到缓冲区
int 21h
xor cx,cx
xor al,al
copy:
lea bx,FileNameFuf
add bx,2
add bx,cx
mov al,[bx]
cmp al,0dh
je endcopy
mov si,cx
lea bx,TheFile
mov ds:[bx+si],al
cmp cx,100
je endcopy
inc cx
jmp copy
endcopy:
;
lea dx,file
;open file
lea dx,TheFile
mov al,0
mov ah,3dh
int 21h
jc error
mov fh,ax
;read file
lea dx,buf
mov cx,100
mov bx,fh
mov ah,3fh
int 21h
jc error
;计算字符串的有效长度
lea ax,buf ;ax为传入参数,strlen_dollar
call strlen_dollar
;查找字符'B',传到ax返回数组下标,没有找到返回-1
mov al,'B'
lea dx,buf
call My_FindAlphabet_al
;开始判断是否找到字符
cmp ax,-1
je FaileFindAlphabet
jmp FindAlphabetIsSuccess
jmp exit
;====================================================
error:
lea dx,error_msg
mov ah,09h
int 21h
;=========================================
FindAlphabetIsSuccess:
lea dx,FindSuccess_msg
mov ah,09h
int 21h
jmp exit
;=========================================
FaileFindAlphabet:
lea dx,FindFaile_msg
mov ah,09h
int 21h
jmp exit
;=========================================
exit:
mov ah,4ch
int 21h
;====================================================My_strlen_dollar
;ax存放字符串长度带出子程序 以$结尾的字符串 di所指的那条字符串,
;注意!串操作di默认es 需要提前将ds指向需要操作的段,内部自动同步es为ds所指向的偏移地址
strlen_dollar:
push cx
;需要将es:di指向所搜索的那条字符串
mov di,ax
mov ax,ds
mov es,ax
mov cx,0FFFFh;设置循环次数,这里用于计算字符串的长度所以设置为最大
xor ax,ax ;将ax置0
mov al,'$'-1
add al,1h ;为了将ZF标志位置0
cld ;将DF置0
repne scasb;开始搜索
not cx ;得到cx所减的次数
dec cx ;长度不包括$
mov ax,cx
pop cx
ret
;====================================================
;My_Find_al al输入所要查找的字符,dx指向查找字符串的偏移地址,ax将结果带出
My_FindAlphabet_al:
push cx
;需要将es:di指向所搜索的那条字符串
mov di,dx
mov dx,ds
mov es,dx
mov cx,0FFFeh;设置循环次数,这里用于计算字符串的长度所以设置为最大
add cx,1h ;为了将ZF标志位置0
cld ;将DF置0
repne scasb;开始搜索
not cx ;得到cx所减的次数
dec cx ;长度不包括$
mov ax,cx
pop cx
ret
;====================================================
code ends
end start
9、程序中定义一个数组(不超过255),从键盘输入一个字符和长度,将数组中输入长度的部分设置成输入的字符。
10、使用条件转移,模拟c语言的if-else for do-while while
assume ds:data
data segment
g_a word 00
g_i word 00
data ends
//if-else
; int a = 8;
; if (a > 9) {
; a++;
; }
; else {
; a--;
; }
mov word ptr [g_a],8
cmp word ptr [g_a],9
jle labif
mov ax,word ptr [g_a]
add ax,1
mov word ptr [g_a],ax
jmp LabIfElseEnd
labNoif:
mov ax,word ptr [g_a]
sub ax,1
mov word ptr [g_a],ax
;==================================================================
; //while
; while (a>0)
; {
; a--;
; }
LabIfElseEnd:
LabWhileStart:
cmp word ptr [g_a],0
jle LabWhileEnd
mov ax,word ptr [g_a]
sub ax,1
mov word ptr [g_a],ax
jmp LabWhileStart
;==================================================================
; //do-while
; do
; {
; a ++;
; } while (a<9);
LabWhileEnd:
LabDoWhileStart:
mov ax,word ptr [g_a]
add ax,1
mov word ptr [g_a],ax
cmp word ptr [g_a],9
jl LabDoWhileStart
;==================================================================
; //for
; for (int i = 0; i < 5; i++) {
; a++;
; }
mov word ptr [g_i],0
jmp LabForInitEnd
mov ax,word ptr [g_i]
add ax,1
mov word ptr [g_i],ax
cmp word ptr [g_i],5
jge LabForEnd
mov ax,word ptr [g_a]
add ax,1
mov word ptr [g_a],eax
LabForEnd:
11、从键盘输入字符串,比较两个字符串是否相等
;======================================;======================================data segment
data segment
CmpBuf1 db 100,?,100 dup('$')
CmpBuf2 db 100,?,100 dup('$')
fh dw ?
error_msg db 0dh,0ah,'error!','$'
success_msg db 0dh,0ah,'done!','$'
StrEqual_mes db 0dh,0ah,'str1 and str2 is equal!','$'
StrNotEqual_msg db 0dh,0ah,'str1 and str2 is not equal!','$'
data ends
;======================================;======================================stack segment
stack segment stack
dw 20h dup(?)
top label word
stack ends
;======================================;======================================code segment
code segment
assume ds:data,cs:code,ss:stack
start:
;=========================================INIT
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
lea sp,top
;=========================================code todo
;输入字符串
lea dx,CmpBuf1
mov ah,0ah ;键盘输入字符串到缓冲区
int 21h
lea dx,CmpBuf2
mov ah,0ah ;键盘输入字符串到缓冲区
int 21h
;计算字符串的有效长度
lea ax,CmpBuf1 ;ax为传入参数,strlen_dollar
add ax,2
call strlen_dollar
push ax
lea ax,CmpBuf2
add ax,2
call strlen_dollar
pop dx
cmp ax,dx
jne strNotEqual ;两字符串有效长度相等才有比较意义
;============
xor ax,ax
mov ax,seg CmpBuf1
push ax ;076ch
mov ax,offset CmpBuf1
add ax,2
push ax ;2h
mov ax,seg CmpBuf2
push ax ;076ch
mov ax,offset CmpBuf2
add ax,2 ;0068h
push ax
;ds sp+ah ,si sp+8 ,es sp+6 ,di sp+4
;使用将对比字符串 ds:si es:di 进行入栈操作
call My_CmpStrFunction
cmp ax,1
je strEqual
jmp strNotEqual
;====================================================
error:
lea dx,error_msg
mov ah,09h
int 21h
;=========================================
strEqual:
lea dx,StrEqual_mes
mov ah,09h
jmp exit
strNotEqual:
lea dx,StrNotEqual_msg
mov ah,09h
jmp exit
;=========================================
exit:
mov ah,4ch
int 21h
;====================================================My_strlen_dollar
;ax存放字符串长度带出子程序 以$结尾的字符串 di所指的那条字符串,
;注意!串操作di默认es 需要提前将ds指向需要操作的段,内部自动同步es为ds所指向的偏移地址
strlen_dollar:
push cx
;需要将es:di指向所搜索的那条字符串
mov di,ax
mov ax,ds
mov es,ax
mov cx,0FFFFh;设置循环次数,这里用于计算字符串的长度所以设置为最大
xor ax,ax ;将ax置0
mov al,'$'-1
add al,1h ;为了将ZF标志位置0
cld ;将DF置0
repne scasb;开始搜索
not cx ;得到cx所减的次数
dec cx ;长度不包括$
mov ax,cx
pop cx
ret
;====================================================
;My_Find_al al输入所要查找的字符,dx指向查找字符串的偏移地址,ax将结果带出
My_FindAlphabet_al:
push cx
;需要将es:di指向所搜索的那条字符串
mov di,dx
mov dx,ds
mov es,dx
mov cx,0FFFeh;设置循环次数,这里用于计算字符串的长度所以设置为最大
add cx,1h ;为了将ZF标志位置0
cld ;将DF置0
repne scasb;开始搜索
not cx ;得到cx所减的次数
dec cx ;长度不包括$
mov ax,cx
pop cx
ret
;====================================================
;ds sp+ah ,si sp+8 ,es sp+6 ,di sp+4
;使用将对比字符串 ds:si es:di 进行入栈操作
My_CmpStrFunction:
mov bx,sp
mov ax,ss:[bx+2]
mov di,ax
mov ax,ss:[bx+4]
mov es,ax
mov ax,ss:[bx+6]
mov si,ax
mov ax,ss:[bx+8]
mov ds,ax
CLD
repz cmpsb ;当前字符相同则继续循环
je Equal
mov ax,0 ;ax=0,不相等
ret
Equal:
mov ax,1 ;ax=1,相等
ret
;====================================================
code ends
end start
12、从键盘输入一个字符串,用来填充程序内部一个255的缓冲区
13、从键盘输入字符串,对字符串中所有字符转小写,然后输出
14、从键盘输入字符串,将所有的字符正反颠倒,然后输出。