[BX] 和 loop 指令
- [bx]和内存单元的描述
- loop
- 我们定义的描述性的符号:“()”,“()”中的元素可以有3中类型:寄存器名、段寄存器名、内存单元的物理地址。
- 约定符号idata表示常量
5.1 [BX]
具体实例:
mov ax,[bx]
功能:bx中存放的数据作为一个偏移地址EA,段地址SA默认在ds中,将SA:EA处的数据送入ax中。即:(ax) = ((ds)*16+(bx))。
问题 5.1
程序和内存中的情况如下图所示,写出程序执行后,21000H~21007H单元中的内容。
程序代码和结果如下:
mov ax,2000h ;ax = 2000h
mov ds,ax ;ds = 2000h
mov bx,1000h ;bx = 1000h
mov ax,[bx] ;ax = 00BEh
inc bx ;bx = 1001h inc是将后面的内容+1
inc bx ;bx = 1002h
mov [bx],ax ;2000:1002 = 00BEh
inc bx ;bx = 1003h
inc bx ;bx = 1004h
mov [bx],ax ;2000:1004 = 00BEh
inc bx ;bx = 1005h
mov [bx],al ;2000:1005 = BEh
inc bx ;bx = 1006h
mov [bx],al ;2000:1006 = BEh
5.2 Loop指令
loop指令格式:loop 标号
loop指令执行的时候,进行两步操作:(cx) = (cx) - 1 和 判断cx中的值,若不为0则转至标号处执行程序,若为0则向下继续执行程序(通俗的说,就是如果不为0继续循环,为0就退出)。
任务:
- 编程计算2^2
- 编程计算2^3
- 编程计算2^12
对于上面3个问题,如果我们进行重复输入指令去计算的话,会发现如果我们要计算一百次,就需要写几百行代码。所以我们引入了loop这个工具。
程序用loop实现:
assume cs:code
code segment
mov ax,2
mov cx,n ;这里的n代表n+1次方
s: add ax,ax ;第一次add时,ax = 4 = 2^2
loop s
mov ax,4c00h
int 21h
code ends
end
问题 5.2
编程,用加法计算 123*236,结果存在ax中。
assume cs:code
code segment
mov ax,0
mov cx,123
m: add ax,236
loop m
mov ax,4c00h
int 21h
code ends
end
此时,效率达到最高。
5.3 在Debug中跟踪用loop指令实现的循环程序
考虑这样一个问题,计算ffff:0006单元中的数乘以3,结果存储在dx中。
我们分析一下:
- 运算后的结果是否会超出dx所能存储的范围?
ffff:0006单元中的数是一个字节型的数据,范围0~255之间,则用它和3相乘结果不会大于65535,可以在dx中存放下。 - 用循环累加实现乘法,用哪个寄存器进行累加?
将ffff:0006单元中的数赋值给ax,用dx进行累加。先设(dx) = 0,然后做3次(dx) = (dx) + (ax)。 - ffff:6单元是一个字节单元,ax是一个16位寄存器,数据的长度不一样,如何赋值?
ax的低8位与ffff:6对其。
编写程序:
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov bx,6
mov al,[bx]
mov ah,0
mov dx,0
mov cx,3
m: add dx,ax
loop m
mov ax,4c00h
int 21h
code ends
end
注意:在汇编源程序中,数据不能以字母开头。
自行Debug调试。这里还有个g指令,可以直接跳到某个语句。格式为:g IP的地址。
Debug和汇编编译器masm对指令的不同处理
在Debug中编程实现:
汇编源程序实现:
assume cs:code
code segment
mov ax,2000h
mov ds,ax
mov al,[0]
mov bl,[1]
mov cl,[2]
mov dl,[3]
mov ax,4c00h
int 21h
code ends
end
在Debug加载后:
我们发现,Debug将它解释为[idata],而编译器将[idata]解释为idata(也就是说,mov al ,[0] 等效于 (ax) = 0。[idata]表示常量,[bx]表示ds:[bx]。)
loop 和 [bx] 的联合应用
计算ffff:0~b单元中的数据的和,结果存储在dx中。
分析:
- 运算后的结果是否会超出dx所能存储的范围?
ffff:0~b内存单元中的数据是字节型数据,范围在0~255之间,12个这样的数据相加,结果不会大于65535,可以在dx中存放下。 - 我们能否将ffff:0~b中的数据直接累加到dx中?
当然不行,因为ffff:0~b中的数据是8位的,不能直接加到16位寄存器dx中。 - 我们能否将ffff:0~b中的数据累加到dl中,并设置(dh)=0,从而实现累加到dx中?
不行,dl是8位寄存器,只能容纳0~255之间,可能会造成进位丢失。 - 我们到底怎样将ffff:0~b中的8位数据,累加到16位寄存器dx中?
将8位数据赋值到一个16位寄存器ax中,再将ax中的数据加到dx上。
程序代码如下:
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
这就是bx与loop配合使用的一个典型例子。
5.6 段前缀
我们可以使用段地址:[偏移地址]的形式:
- mov ax,ds:[bx]
- mov ax,cs:[bx]
- mov ax,ss:[bx]
- mov ax,es:[bx]
- mov ax,ss:[0]
- mov ax,cs:[0]
以上的“ds:”、“cs:”、“ss:”、“es:”,在汇编语言中称为段前缀。
5.7 一段安全的空间
我们执行以下程序:
assume cs:code
code segment
mov ax,0
mov ds,ax
mov ds:[26h],ax
mov ax,4c00h
int 21h
code ends
end
运行后,发现报错:
这是由于0000:0026处存放的重要系统数据被修改而造成的。
一般,合法的程序不会使用0000:0200~0000:02ff这段空间,所以对于我们来说,这段空间是安全的。
5.8 段前缀的使用
将内存ffff:0~b单元中的数据复制到0:200~0:20b单元中。
分析:
(1)0:200~0:20b单元等同于0020:0~0020:b单元,它们描述的是同一段内存空间,
(2)复制的过程应用循环实现。
(3)在循环中,源始单元ffff:x和目标单元0200:x的偏移地址x是变量。用bx存放。
(4)将0:200~0:20b用0020:0~0020:b描述。
程序如下:
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov ax,0020h
mov es,ax
mov bx,0h
mov cx,12
m: mov dl,[bx]
mov es:[bx],dl
inc bx
loop m
mov ax,4c00h
int 21h
code ends
end
实验4 [bx]和loop的使用
(1)编程,向内存0:200~0:23F依次传送数据0~63(3FH)。
assume cs:code
code segment
mov ax,20h
mov ds,ax
mov cx,40h
mov bx,0
s: mov [bx],bx
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end
(2)编程,向内存0:200~0:23F一次传送数据0~63(3FH),程序中只能使用9条指令,9条指令中包括“mov ax,4c00h”和“int 21h”。
答案同上。
(3)下面的程序的功能是将“mov ax,4c00h”之前的指令复制到内存0:200处,补全程序。上机调试,跟踪运行结果。
assume cs:code
code segment
mov ax,076A
mov ds,ax
mov ax,0020h
mov es,ax
mov bx,0
mov cx,1Bh
s: mov al,[bx]
mov es:[bx],al
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end
提示:
(1)复制的是什么?从哪里到哪里?
复制的是指令,从076A:0开始到0020:0。
(2)复制的是什么?有多少个字节?你如何知道要复制的字节的数量?
有1B个字节。