ITEEDU

解决了字符串的输入与输出,现在来考虑这样一个问题:如何在一个字符串中查找一个给定的字符?下面给出了一般的解决方法:

SEARCH.ASM
        data  segment
              assume    ds:data
        mess  db        'Price: $1.00',0dh,0ah,0	;定义一个含有'$'的字符串
       ascii  db        '0123456789ABCDEF'	;十六进制数字对应的ASCII码
      errmsg  db        07h,'Character not found!',	0dh,0ah,'$'
        data  ends
        code  segment
              assume    cs:code
        main  proc      far
              mov       ax,data	;初始化DS寄存器指向数据段
              mov       ds,ax
              mov       si,offset mess	;SI寄存器指向字符串MESS
              mov       cx,0	;CX寄存器清0,准备计字符数
    outchar:
              mov       al,byte ptr [si]	;取一个字符送入AL寄存器
              cmp       al,'$'	;是所要找的'$'符吗?
              jz        out_cx	;若是则转OUT_CX输出CX寄存器中的值
              cmp       al,0	;到字符串结尾了吗?
              jz        not_fd	;显示出错信息并退出
              inc       cx	;CX寄存器加1
     out_cx:  inc       si	;SI寄存器指向下一个字符
              jmp       outchar	;判断下一个字符
              mov       ax,cx	;CX寄存器存入AX寄存器
    outloop:  mov       bx,offset ascii	;BX寄存器指向ASCII表
              mov       cx,4	;处理4个数位
			
              rol       ax,1	;循环左移4位,将高4位移至低4位
              rol       ax,1
              rol       ax,1
              rol       ax,1
              push      ax	;暂存AX寄存器
              and       ax,000fh	;保留低4位
              xlat      ;取得对应的ASCII码
              mov       ah,0eh	;利用10H中断的0EH功能
              int       10h	;输出AL中的字符
              pop       ax	;取回AX寄存器
              loop      outloop	;处理下一个数位
       exit:  mov       ah,4ch	;结束程序
              int       21h
     not_fd:  mov       dx,offset errmsg
              mov       ah,09h
              int       21h
              jmp       exit
        main  endp
        code  ends
              end       main

程序中并没有使用LODS指令,而是把SI寄存器作为一个可以作间接寻址的一般寄存器应用。如何使用串处理指令解决这个问题呢?请看下面的程序:

        data  segment
              assume    ds:data
        mess  db        'Price: $1.00',0dh,0ah,0
       ascii  db        '0123456789ABCDEF'
      errmsg  db        07h,'Character not found!',0dh,0ah,'$'	 ;字符未找到
        data  ends
        code  segment
              assume    cs:code
        main  proc      far
              mov       ax,data	;初始化DS、ES寄存器指向数据段
              mov       ds,ax
              mov       es,ax
              mov       di,offset mess	;DI寄存器指向字符串MESS
              mov       cx,14	;在14个字符中查找'$'
              mov       al,'$'	;要查找的字符是'$'
       repnz  scasb     ;扫描字符串
              jz        out_cx	;找到'$'后转至OUT_CX
              jmp       not_found	;未找到'$'则转至NOT_FOUND
     out_cx:  sub       di,offset mess+1	;计算'$'符的偏移位置
              mov       ax,di	;偏移位置送入AX寄存器
              mov       bx,offset ascii	;BX寄存器指向ASCII表
              mov       cx,4	;处理4个数位
    outloop:  rol       ax,1	;AX寄存器向左循环移4位
              rol       ax,1
              rol       ax,1
              rol       ax,1
              push      ax	;暂存AX寄存器
              and       ax,000fh	;保留AX寄存器的低4位
              xlat      ;取得对应的ASCII码
              mov       ah,0eh	;利用10H中断的0EH功能
              int       10h	;显示AL寄存器中的字符
              pop       ax	;取回AX寄存器
              loop      outloop	;处理下一个数位
              jmp       exit	;转至EXIT结束程序
			
  not_found:  mov       dx,offset errmsg	;显示出错信息
              mov       ah,9
              int       21h
       exit:  mov       ah,4ch	;结束程序
              int       21h
        main  endp
        code  ends
              end       main

程序中新出现了一个寄存器--DI(Destination Index)。

DI称为目的地址索引寄存器,它和SI寄存器一样用于保存串处理指令需要用到的地址,但这个寄存器指示的是"目的地址",也就是说它和LODS这样的指令没有任何关系。只有那些具有"写"数据操作的串指令或串扫描、串比较指令才会使用DI寄存器中的地址。

当然DI寄存器也可以作间接寻址用,这和SI寄存器是一样的。还有一点需要注意的是在使用串处理指令时这个寄存器是和附加段寄存器ES相配合给出逻辑地址,只有在不用于串指令而作一般的间接寻址用时,才默认DS为段地址。

回顾一下上面的程序,我们在设定DS寄存器指向DATA段后又用MOV ES,AX指令将ES寄存器设为DATA段,目的就在于为后面的SCAS指令建立正确的字符串地址。那么SCAS指令又具有什么作用呢?

助记符:SCAS(Scan string)
用 途:在"目的串"中搜索给定的数据
格 式:SCASB(搜索一字节)
SCASW(搜索一个字)
执 行:将位于ES:DI处的数据(字节或字)与累加器中的数据进行比较,并根据结果设定标志位,同时DI寄存器根据DF标志变化。

和LODS一样,指令末尾的"B"和"W"表示所要搜索的数据位宽,DI寄存器究竟变化1还是2就由这两个字母决定。不过这个指令有个易搞混的问题,就是单独使用这个指令时是否会将字符串中所有数据都处理一次?

大家不要被它的名字蒙住,它虽然叫串扫描指令,但如果我们单独使用这个指令,它只会将ES:DI指向的数据与累加器相比,而后改变DI寄存器,并不像我们所想的那样将整个串中所有数据都扫一遍。而我们所需要的恰好是扫描整个串,这又该如何应用SCAS指令呢?

幸好SCAS指令可以配合一个前缀,这个前缀就是:

助记符:REP(Repeat)
用 途:作为前缀使后面的串指令重复执行,重复次数在CX中设定。
格 式:REP 串处理指令
执 行:判断CX,若CX不为0,则CX寄存器减1,执行后面的指令,重复这三个步骤直至CX=0