移位指令我们已经学习了4个,逻辑左右移位(SHL/SHR)与循环左右移位(ROL/ROR)。8086/88还有两组移位指令--算术左右移位和带进位标志的循环左右移位:
助记符:SAL/SAR(Shift Arithmetic Left/Shift Arithmetic Right)
用 途:完成向左/向右的算术移位
格 式:SAL/SAR 寄存器,1
SAL/SAR 寄存器,CL
SAL/SAR 存储单元,1
SAL/SAR 存储单元,CL
执 行:SAL执行结果与SHL相同,操作数向左移动指定位数,移出的数位进
入CF标志,同时0由操作数右侧移入;SAR执行时操作数向右移动指
定位数,移出的数位也进入CF标志,同时最高位(符号位)逐渐向
右扩展。
SAR指令主要用于除法运算,每向右移一位就相当于除以2。当然,逻辑右移指令SHL也能当作除法,不过SHL只适用于无符号数除以2,因为从左侧移入的0会使结果总为正数。而SAR指令就能同时适用于带符号数与无符号数的除2运算,因为此指令执行时从操作数左侧移入的不一定是0,而是与操作数的符号一致。
如果将80H向右移一位,采用逻辑移位指令SHR可以得到结果40H,这相当于将128除以2。而采用算术移位指令SAR就能得到结果0C0H,这才是-128除以2的结果。若把0C0H再除以2,则应用SAR指令可以得到结果0E0H,可以看出符号位是逐渐向右扩展的。
SAL指令与SHL指令的操作完全一致,这是因为左移操作是不可能保持符号位不变的。这也提醒我们负数是不能用左移位的方式完成乘2运算的。
指令RCL/RCR与指令ROL/ROR一样用于完成循环移位,不过有一点不同,RCL/RCR可以完成9位、17位的循环移位,而不是普通的8位、16位移位。那么多出的一位从何处而来呢?
助记符:RCL/RCR(Rotate Left/Right through Carry)
用 途:带进位循环左右移位
格 式:RCL/RCR 寄存器,1
RCL/RCR 寄存器,CL
RCL/RCR 存储单元,1
RCL/RCR 存储单元,CL
执 行:指令执行时使操作数向左(右)移动指定位数,移出的位进入CF标
志,同时原CF标志位由操作数右(左)侧进入操作数。
图10-1给出了这两个指令执行的情况,可以看出多出的一位就是CF标志。RCL/RCR指令的用途与ADC/SBB有些相似,前面我们说过ADC/SBB通常用于多位算术,RCL/RCR则通常用于多位移位。比如下面的程序可以将一个64Bit的数向右移动一位:
SHIFT64.ASM
data segment assume ds:data number db 88h,77h,66h,55h ;定义一个64Bit的数据 db 44h,33h,22h,11h data ends code segment assume cs:code main proc far push ds ;初始化堆栈 xor ax,ax push ax mov ax,data ;初始化DS寄存器 mov ds,ax mov cx,8 ;准备处理8个字节 mov si,offset number+7 ;SI寄存器指向64Bit数据的最高16位 std ;向低地址方向处理数据 loop1: lodsb ;取得一个字节 call bin8o ;按二进制形式显示 loop loop1 call crlf ;调用CRLF输出回车、换行符 mov cx,8 ;准备8个字节 mov si,offset number+7 ;SI寄存器指向64Bit数据的最高16位 clc ;预先清CF标志,完成逻辑移位 ; mov al,byte ptr [si] ;如用这两个指令代替CLC,可完成算术移位 ; shl al,1 loop2: lodsb ;取得一个字节 rcr al,1 ;带进位标志的右循环移位,最低位移入CF mov byte ptr [si+1],al ;将移位后的数据送回内存 loop loop2 ;返回LOOP2继续处理下一个字节 mov cx,8 ;再次显示移位后的64Bit数据 mov si,offset number+7 loop3: lodsb call bin8o loop loop3 ret ;结束进程 main endp bin8o proc near ;按二进制形式显示输出字程序 push cx ;保存CX寄存器 mov cx,8 ;显示8个数位 mov bl,al ;将待显示的数据送入BL寄存器 mov ah,0eh ;选择10H中断的0EH功能 loop4: shl bl,1 ;将待显示数据向左移1位,最高位移入CF mov al,31h ;准备显示"1" jc out_chr ;若移出的位为1,转OUT_CHR显示"1" mov al,30h ;若移出的位为0,准备显示"0" out_chr: int 10h ;调用10H中断显示AL寄存器中的字符 loop loop4 ;转LOOP4继续处理下一数位 pop cx ;恢复CX寄存器 ret ;返回主过程 bin8o endp crlf proc near ;输出回车、换行符的子程序 mov ax,0e0dh ;利用10H中断的0EH功能输出回车符 int 10h mov al,0ah ;输出换行符 int 10h ret ;返回主过程 crlf endp code ends end main
这段程序所完成的是64位逻辑右移,所用的一点技巧就是在处理第一个字节之前先用CLC指令将CF标志清零,这使得移位后最左端的数位是0,而不是什么未知的结果。如果想完成64位算术移位也很简单,可以考虑在处理第一个字节之前先把它的符号位送进CF标志就可以了。程序的修改很简单,只要去掉CLC指令改用它下面两条加有";"的指令即可。