第五章:[BX]和loop指令05
让编程改变世界
Change the world by program
loop和[bx]的联合应用
考虑这样一个问题:
计算ffff:0~ffff:b单元中的数据的和,结果存储在dx中。分析:
(1)运算后的结果是否会超出 dx 所能存储的范围? ffff:0~ffff:b内存单元中的数据是字节型数据,范围在0~255之间,12个这样的数据相加,结果不会大于 65535 ,可以在dx中存放下。 (2)我们是否将 ffff:0~ffff:b中的数据直接累加到dx中? 当然不行,因为ffff:0~ffff:b中的数据是8位的,不能直接加到16位寄存器dx中。 (3)我们能否将ffff:0~ffff:b中的数据累加到dl中,并设置(dh=0,从而实现累加到dx中的目标? 这也不行,因为dl是8位寄存器,能容纳的数据的范围在小 255 之间,ffff : 0~ffff:b中的数据也都是 8 位,如果仅向dl中累加12个 8 位数据,很有可能造成进位丢失。 (4)我们到底怎样将用ffff:0~ffff:b中的8位数据,累加到16位寄存器dx中? 从上面的分析中,我们可以看到,这里面有两个问题:类型的匹配和结果的不超界。 具体的说,就是在做加法的时候,我们有两种方法:(dx)=(dx)+内存中的8位数据
(dl)=(dl)+内存中的8 位数据
第一种方法中的问题是两个运算对象的类型不匹配,第二种方法中的问题是结果有可能超界。 怎样解决这两个看似矛盾的问题? 目前的方法(在后面的课程中我们还有别的方法)就是我们得用一个16位寄存器来做中介。 我们将内存单元中的 8 位数据赋值到一个16位寄存器ax中,再将ax中的数据加到dx上,从而使两个运算对象的类型匹配并且结果不会超界。 想清楚以上的问题之后,我们编写程序代码。 [codesyntax lang="asm"]assume cs:code code segment mov ax,0ffffh mov ds,ax ;设置(ds)=0ffffh mov dx,0 ;初始化累加寄存器,(dx)=0 mov al,ds:[0] mov ah,0 ;(ax)=((ds)*16+0)=(0ffff0h) add dx,ax ;向dx中加上0ffffh:0单元的数值 mov al,ds:[1] mov ah,0 ;(ax)=((ds)*16+1)=(0ffff1h) add dx,ax ;向dx中加上0ffffh:1单元的数值 mov al,ds:[2] mov ah,0 ;(ax)=((ds)*16+2)=(0ffff2h) add dx,ax ;向dx中加上0ffffh:2单元的数值 mov al,ds:[3] mov ah,0 ;(ax)=((ds)*16+3)=(0ffff3h) add dx,ax ;向dx中加上0ffffh:3单元的数值 mov al,ds:[4] mov ah,0 ;(ax)=((ds)*16+4)=(0ffff4h) add dx,ax ;向dx中加上0ffffh:4单元的数值 mov al,ds:[5] mov ah,0 ;(ax)=((ds)*16+5)=(0ffff5h) add dx,ax ;向dx中加上0ffffh:5单元的数值 mov al,ds:[6] mov ah,0 ;(ax)=((ds)*16+6)=(0ffff6h) add dx,ax ;向dx中加上0ffffh:6单元的数值 mov al,ds:[7] mov ah,0 ;(ax)=((ds)*16+7)=(0ffff7h) add dx,ax ;向dx中加上0ffffh:7单元的数值 mov al,ds:[8] mov ah,0 ;(ax)=((ds)*16+8)=(0ffff8h) add dx,ax ;向dx中加上0ffffh:8单元的数值 mov al,ds:[9] mov ah,0 ;(ax)=((ds)*16+9)=(0ffff9h) add dx,ax ;向dx中加上0ffffh:9单元的数值 mov al,ds:[0ah] mov ah,0 ;(ax)=((ds)*16+0ah)=(0ffffah) add dx,ax ;向dx中加上0ffffh:0ah单元的数值 mov al,ds:[0bh] mov ah,0 ;(ax)=((ds)*16+0bh)=(0ffffbh) add dx,ax ;向dx中加上0ffffh:0bh单元的数值 mov ax,4c00h ;程序返回 int 21h code ends end[/codesyntax]
问题5.4
应用loop 指令,改进程序5.5 ,使它的指令行数让人能够接受。 请先思考后再看分析。 我们可以看出,在程序中,有12个相似的程序段,我们将它们一般化地描述为:mov al,ds:[x]
mov ah,0
add dx,ax
我们可以看到,12个相似的程序段中,只有mov al,ds:[X]指令中的内存单元的偏移地址是不同的,其他都一样。 而这些不同的偏移地址是可在0≤X≤0bH的范围内递增变化的。 我们可以用数学语言来描述这个累加的运算 [caption id="attachment_322" align="aligncenter" width="213"] 用数学语言来描述这个累加的运算[/caption] 从程序实现上,我们将循环做:(al)=((ds)*16+X)
(ah)=0
(dx)=(dx)+(ax)
一共循环12次,在循环开始前(dx)=0ffffh,X=0,ds:X指向第一个内存单元。每次循环后,X递增,ds:X指向下一个内存单元。完整的算法描述
初始化:(ds)=0ffffh
X=0
(dx)=0
循环12次:(al) =((ds)*16+X)
(ah) = 0
(dx)=(dx)+(ax)
X=X+1
可见,表示内存单元偏移地址的X应该是一个变量 ,因为在循环的过程中,偏移地址必须能够递增。 这样,在指令中,我们就不能用常量来表示偏移地址。我们可以将偏移地址放到 bx中,用[bx]的方式访问内存单元。 在循环开始前设(bx)=0,每次循环,将bx中的内容加1即可。最后一个问题是,如何实现循环12次?
我们的loop指令该发挥作用了。 更详细的算法描述。更详细的算法描述初始化
(ds)=0ffffh
(bx)=0
(dx)=0
(cx)=12
循环12 次:s: (al)=((ds)*16+(bx))
(ah)=0
(dx)=(dx)+(ax)
(bx)=(bx)+1
loops
我们的最终程序
[codesyntax lang="asm"]assume cs:code code segment mov ax,0ffffh mov ds,ax mov bx,0 mov dx,0 mov cx,12 s: mov al,[bx] mov ah,0 add dx,ax inc bx loop s mov ax,4c00h int 21h code ends end[/codesyntax] 在实际编程中,经常会遇到,用同一种方法处理地址连续的内存单元中的数据的问题。 我们需要用循环来解决这类问题,同时我们必须能够在每次循环的时候按照同一种方法来改变要访问的内存单元的地址。 这时,我们就不能用常量来给出内存单元的地址(比如[0]、[1]、[2]中,0、1、2是常量),而应用变量。 “mov al,[bx]”中的 bx就可以看作一个代表内存单元地址的变量,我们可以不写新的指令,仅通过改变bx中的数值,改变指令访问的内存单元。