3.8 串操作指令

2024-09-11 09:13:57 浏览数 (2)

3.8 串操作指令

基础知识

源串存放在数据段 (DS)
  • 用SI提供源串的偏移地址。****DS:SI为寻址源串
目的串存放在附加段 (ES)
  • 用DI提供目的串的偏移地址。****ES:DI为寻址目的串
方向标志 (DF)
  • DF=0:SI、DI寄存器自动增加(加1用于字节串,加2用于字串),实现从低地址到高地址的操作。
  • DF=1:SI、DI寄存器自动减少(减1用于字节串,减2用于字串),实现从高地址到低地址的操作。
重复前缀 (REP/REPE/REPNE)
REP:****无条件重复
  • CX寄存器存储重复次数,每执行一次串操作后,CX减1,直到CX为0。
REPE/REPZ ****当相等/为零时重复串操作
REPNE/REPNZ 当不相等/不为零时重复串操作

执行步骤如下:

①先判断CX的内容,如(CX)=0或ZF=1,则串操作停止,否则执行第②步;

②(CX)-1→CX ;

③执行其后的串操作指令,转第①步。

数据字节串/字串传送指令 (MOVSB/MOVSW)
  • <font style="color:#DF2A3F;">MOVSB</font>(Move String Byte): 逐字节移动数据。
  • <font style="color:#DF2A3F;">MOVSW</font>(Move String Word): 逐字移动数据(16位)。
  • 功能:将源段 DS:SI 指向的字节或字串传送到目标段 ES:DI,同时根据方向标志 DF 修改 SI 和 DI 的值,指向下一个字节或字。
示例:将内存单元首地址3100H起的100个字节传送到首地址2800H的内存单元
代码语言:javascript复制
CLD                ; 清除方向标志DF,使传送方向为从低地址到高地址
MOV SI, 3100H      ; 将源串的起始地址3100H加载到SI
MOV DI, 2800H      ; 将目的串的起始地址2800H加载到DI
MOV CX, 100        ; 将传送字节数100加载到CX
REP MOVSB          ; 重复执行MOVSB指令,传送100个字节

这段代码用于将内存中从地址3100H开始的100个字节复制到从地址2800H开始的位置。以下是每个步骤的详细解析,包括数据移动的方式:

  1. CLD (Clear Direction Flag):
    • 清除方向标志DF,使其等于0。方向标志DF决定了字符串操作指令(如MOVSB)在每次操作后地址指针是增加还是减少。DF = 0 表示指针会递增,也就是说数据从低地址向高地址传输。
  2. MOV SI, 3100H:
    • 将源数据的起始地址3100H加载到SI寄存器。SI(Source Index)用于指向源字符串的当前字节地址。
  3. MOV DI, 2800H:
    • 将目标数据的起始地址2800H加载到DI寄存器。DI(Destination Index)用于指向目标字符串的当前字节地址。
  4. MOV CX, 100:
    • 将传送的字节数100加载到CX寄存器。CX寄存器作为计数器,用来指示需要传输的字节数量。在每次传输后,CX会自动减1,直到CX减为0,表示传输完成。
  5. REP MOVSB:
    • REP指令前缀与MOVSB(Move String Byte)指令结合使用。MOVSB指令从SI指向的源地址复制一个字节到DI指向的目标地址,然后SI和DI各自递增(因为DF = 0)。REP指令会重复执行MOVSB指令,直到CX寄存器的值减为0为止。
数据传输过程:
  • 第一次传输
    • SI = 3100H,DI = 2800H。
    • 从地址3100H读取一个字节,存放到地址2800H。
    • SI递增为3101H,DI递增为2801H,CX减1。
  • 第二次传输
    • SI = 3101H,DI = 2801H。
    • 从地址3101H读取一个字节,存放到地址2801H。
    • SI递增为3102H,DI递增为2802H,CX再减1。
  • 重复上述过程
    • 这个过程会持续进行,直到传输了100个字节(CX = 0)。最后一次传输时:
    • SI = 3199H,DI = 2899H。
    • 从地址3199H读取一个字节,存放到地址2899H。
    • SI递增为319AH,DI递增为289AH,CX减为0,传输结束。

最终,地址3100H到3199H的100个字节的数据被完整复制到了地址2800H到2899H的位置。

数据字节串/字串比较指令 (CMPSB/CMPSW)
  • 功能:比较源段 DS:SI 和目标段 ES:DI 指向的字节或字串,不返回结果,仅根据比较结果修改标志位,同时根据方向标志 DF 修改 SI 和 DI 的值,指向下一个字节或字。
示例:检查内存单元首地址2200H起的50个字节与首地址3200H起的50个字节是否相等
代码语言:javascript复制
CLD                ; 清除方向标志DF,使比较方向为从低地址到高地址
MOV SI, 2200H      ; 将源串的起始地址2200H加载到SI
MOV DI, 3200H      ; 将目的串的起始地址3200H加载到DI
MOV CX, 50         ; 将比较字节数50加载到CX
REPE CMPSB         ; 如果相等且ZF=1,继续比较,直到CX=0或发现不相等
JZ LP1             ; 如果ZF=1,则跳转到标签LP1,表示所有字节都相等
DEC SI             ; 如果不相等,将SI指针减1,指向第一个不相等的字节
MOV BX, SI         ; 将第一个不相等字节的地址存入BX
MOV AL, [SI]       ; 将源串中第一个不相等的字节内容存入AL
JMP LP2            ; 跳转到标签LP2

LP1: MOV BX, 0     ; 如果所有字节相等,则将BX置为0
LP2:               ; 继续执行后续代码
数据字节串/字串检索指令(SCASB/SCASW)

功能:

  • SCASB(Scan String Byte):逐字节比较 <font style="color:#DF2A3F;">AL</font>** 寄存器中的值与 [ES:DI] 段内存中的值。
  • SCASW(Scan String Word):逐字比较 <font style="color:#DF2A3F;">AX</font>** 寄存器中的值与 [ES:DI] 段内存中的值。

关键点:

  • 比较的结果不会返回到寄存器中,而是影响标志位,如ZF(Zero Flag),CF(Carry Flag)等。
  • 在比较完成后,DI 指针会自动递增(或递减)指向下一个字节(字),这取决于CLDSTD指令设置的方向标志。
示例: 在内存附加段(ES段)4300H地址开始的100个字节中查找字符 *。如果找到,偏移地址存入BX寄存器;如果未找到,BX = 0。
代码语言:javascript复制
CLD                   ; 清除方向标志,设置为向前搜索
MOV DI, 4300H         ; 初始化DI为4300H,指向内存中的第一个字节
MOV AL, '*'           ; 在AL中加载要查找的字符 '*'
MOV CX, 100           ; 设置搜索范围为100个字节

REPNZ SCASB           ; 逐字节比较,直到找到'*' 或 CX=0
JNZ LP1               ; 如果未找到(ZF=0),跳转到LP1

DEC DI                ; 减小 DI,因为 SCASB 会多移动一个字节
MOV BX, DI            ; 将找到的位置偏移地址存入BX
JMP LP2               ; 跳转到 LP2

LP1:
MOV BX, 0             ; 未找到 '*',将 BX 设为 0
LP2:
; 继续其他代码
总结(CMPSB/CMPSW)和(SCASB/SCASW)的区别
  • CMPSB/CMPSW:
    • 比较操作: 两个字符串的内容逐字节或逐字进行比较。
    • 应用场景: 用于比较两个字符串,通常用于验证它们是否相等或查找不同之处。
  • SCASB/SCASW:
    • 检索操作: 搜索单个字符串中的特定值。
    • 应用场景: 用于在字符串中查找特定的字节或字,并根据标志位确定是否找到了目标值。

简而言之,CMPSB/CMPSW 是比较两个字符串中的数据,而 SCASB/SCASW 是在一个字符串中搜索特定的数据。

只能操纵指定的寄存器

这些字符串操作指令在 x86 汇编中只能操纵特定的寄存器,这是指令设计的一个特点。具体来说:

  1. CMPSB/CMPSW: 源寄存器: SI(Source Index),指向源字符串的内存地址,使用DS(Data Segment)段。 目标寄存器: DI(Destination Index),指向目标字符串的内存地址,使用ES(Extra Segment)段。 比较寄存器:比较操作隐式地使用 AL 或 AX 寄存器(实际比较操作由 CMPSB 或 CMPSW 执行的内存位置与内存位置之间进行)。
  2. SCASB/SCASW: 目标寄存器: DI,指向字符串的内存地址,使用 ES 段。 比较寄存器: AL(对于 SCASB)或 AX(对于 SCASW)是隐式指定的寄存器。指令将 AL 或 AX 中的值与 ES:DI 指向的内存内容进行比较。CMPSB/CMPSWSCASB/SCASW 指令只能操纵指定的寄存器,即 SIDIALAX。这些寄存器在指令中是隐式使用的,不能被其他寄存器替代。

CMPSB/CMPSWSCASB/SCASW 指令只能操纵指定的寄存器,即 SIDIALAX。这些寄存器在指令中是隐式使用的,不能被其他寄存器替代。

数据字节串/字串读出指令(LODSB/LODSW)

功能:

  • LODSB(Load String Byte):将[DS:SI]的字节数据加载到AL寄存器中,并将SI指针指向下一个字节。
  • LODSW(Load String Word):将[DS:SI]的字数据加载到AX寄存器中,并将SI指针指向下一个字(16位)。
示例 :使用 LODSB 读取数据
代码语言:javascript复制
CLD               ; 清除方向标志DF,使操作从低地址到高地址
MOV SI, 2200H     ; 将源串的起始地址2200H加载到SI
MOV CX, 10        ; 准备读取10个字节的数据

READ_LOOP:
    LODSB         ; 将[DS:SI]中的字节数据加载到AL寄存器中,并将SI指针加1
    ; 这里可以对AL中的数据进行处理,例如存入其他寄存器或执行运算
    DEC CX        ; 将CX减1
    JNZ READ_LOOP ; 如果CX不为0,继续循环

; 循环结束后,AL中保存了最后一个读取的字节数据,SI指向最后一个字节之后的地址
数据和指针的移动
  • 数据存入AL:每次 LODSB 指令执行时,当前 SI 地址的字节数据被加载到 AL 寄存器中。处理完后,AL 寄存器中存储了当前读取的数据。
  • SI 寄存器的移动**:每次执行 LODSB 后,SI 的值增加 1。假设初始值为 2200H,经过一次 LODSB 操作后,SI 会变为 2201H,然后是 2202H,如此继续直到读取完 10 个字节。因此,SI 最终会指向最后一个字节之后的位置,即 2200H 10 = 220AH
  • 最终状态
    • AL:保存了最后读取的字节数据,即第 10 个字节。
    • SI:指向最后一个读取字节的下一个地址,即 220AH
总结
  • LODSB 通过从当前 SI 指向的地址读取一个字节数据到 AL 寄存器,并将 SI 自动增加 1,从而实现从低地址到高地址的遍历。
  • 经过 10 次 LODSB 操作后,SI 指向的是最后一个读取字节之后的位置(220AH),AL 中则保存了最后一个读取的字节数据。
数据字节串/字串写入指令(STOSB/STOSW)

功能:

  • STOSB(Store String Byte):将AL寄存器的内容存入[ES:DI],并将DI指针指向下一个字节。
  • STOSW(Store String Word):将AX寄存器的内容存入[ES:DI],并将DI指针指向下一个字(16位)。
示例 :使用 STOSB 写入数据
代码语言:javascript复制
CLD               ; 清除方向标志DF,使操作从低地址到高地址
MOV DI, 3200H     ; 将目的串的起始地址3200H加载到DI
MOV CX, 10        ; 准备写入10个字节的数据
MOV AL, 42H       ; 将字节42H加载到AL(假设需要写入的值是42H)

WRITE_LOOP:
    STOSB         ; 将AL寄存器中的值存入[ES:DI],并将DI指针加1
    DEC CX        ; 将CX减1
    JNZ WRITE_LOOP ; 如果CX不为0,继续循环

; 循环结束后,内存地址[3200H]到[3209H]中存储的都是42H,DI指向下一个位置320AH

0 人点赞