在本章中,要在屏幕上显示的内容,连同它们的显示属性值,都集中声明在一起。想显示它们?那就要将它们“搬”到0xB800 段。有多种方法可以做到这一点,但8086 处理器提供了最好的方法,那就是使用movsb 或者movsw 指令。
这两个指令通常用于把数据从内存中的一个地方批量地传送(复制)到另一个地方,处理器把它们看成是数据串。但是,movsb 的传送是以字节为单位的,而movsw 的传送是以字为单位的。
movsb 和movsw 指令执行时,原始数据串的段地址由DS 指定,偏移地址由SI 指定,简写为DS:SI;要传送到的目的地址由ES:DI 指定;传送的字节数(movsb)或者字数(movsw)由CX指定。除此之外,还要指定是正向传送还是反向传送,正向传送是指传送操作的方向是从内存区域的低地址端到高地址端;反向传送则正好相反。正向传送时,每传送一个字节(movsb)或者一个字(movsw),SI 和DI 加1 或者加2;反向传送时,每传送一个字节(movsb)或者一个字(movsw)时,SI 和DI 减去1 或者减去2。不管是正向传送还是反向传送,也不管每次传送的是字节还是字,每传送一次,CX 的内容自动减一。
如图6-2 所示,在8086 处理器里,有一个特殊的寄存器,叫做标志寄存器FLAGS。作为一个例子,它的第6 位是ZF(Zero Flag),即零标志。当处理器执行一条算术或者逻辑运算指令后,算术逻辑部件送出的结果除了送到指令中指定位置(目的操作数指定的位置)外,还送到一个或非门。学过逻辑电路课程,或者看过《穿越计算机的迷雾》这本书的人都知道,或非门的输入全为0 时,输出为1;输入不全为0,或者全部为1 时,输出为0。或非门的输出送到一个触发器,这就是标志寄存器的ZF 位。这就是说,如果计算结果为0,这一位被置成1,表示计算结果为零是“真”的;否则清除此位(0)。
除此之外,它也允许通过指令设置一些标志,来改变处理器的运行状态。比如,第10 位是方向标志DF(Direction Flag),通过将这一位清零或者置1,就能控制movsb 和movsw 的传送方向。
源程序第19 行是方向标志清零指令cld。这是个无操作数指令,与其相反的是置方向标志指令std。cld 指令将DF 标志清零,以指示传送是正方向的。和cld 功能相反的是std 指令,它将DF标志置位(1)。此时,传送的方向是从高地址到低地址。
源程序第20 行,设置SI 寄存器的内容到源串的首地址,也就是标号mytext 处的汇编地址。
源程序第21 行,设置目的地的首地址到DI 寄存器。屏幕上第一个字符的位置对应着0xB800段的开始处,所以设置DI 的内容为0。
第22 行,设置要批量传送的字节数到CX 寄存器。因为数据串是在两个标号number 和mytext 之间声明的,而且标号代表的是汇编地址,所以,汇编语言允许将它们相减并除以2 来得到这个数值。需要说明的是,这个计算过程是在编译阶段进行的,而不是在指令执行的时候。除以2 的原因是每个要显示的字符实际上占两字节:ASCII 码和属性,而movsw 每次传送一个字。
图6-2 8086 处理器的标志寄存器
第23 行,是movsw 指令,操作码是0xA5,该指令没有操作数。使用movsw 而不是movsb的原因是每次需要传送一个字(ASCII 码和属性)。单纯的movsb 和movsw 只能执行一次,如果希望处理器自动地反复执行,需要加上指令前缀rep(repeat),意思是CX 不为零则重复。rep movsw 的操作码是0xF3 0xA5,它将重复执行movsw 直到CX 的内容为零。
检测点6.1
选择填空:MOVSW 指令每次传送一个( ),MOVSW 指令每次传送一个( )。原始数据在段内的偏移地址在寄存器( )中,要传送的目标位置的偏移地址在寄存器( )中。如果要连续传送多个字或字节,则需要( )前缀,在寄存器( )中设置传送的次数,并设置传送的方向。其中,( )指令指示正向传送,( )指令指示反向传送。反向传送时,每传送一次,SI 和DI 的内容将( )。
A.字节 B.字 C.di D.si E.cx F.rep G.减小 H.std I.cld J.增大