汇编学习Day5

第五章 [BX]和loop指令

  • [BX]和内存单元的描述
    [BX]是什么,和[0]有点类似,[0]表示内存单元,偏移地址是0。比如下面的指令中
mov ax,[0]

将一个内存单元送入ax,这个内存单元的长度为2字节,存放一个字,偏移地址为0,段地址在ds中

mov al,[0]

将一个内存单元的内容送入al,这个内存单元的长度为1字节,存放一个字节,偏移地址为0,段地址在ds中

  • 要完整描述一个内存单元,需要两种信息:1.内存单元的地址;2.内存单元的长度(类型)
    用[0] 表示一个内存单元时,0表示单元的偏移地址,段地址默认在ds中,单元的长度(类型)
    用[0]表示一个内存单元时,0表示单元的偏移地址段地址默认在ds中,单元的长度可以由具体指令中的其它操作对象指出
    [bx]同样也表示一个内存单元,它的内存偏移地址在bx中

  • loop

  • “loop”本身就有循环的意思,这个指令和循环有关

  • 定义的描述性的符号:“()”

  • 用描述性的符号“()”来表示一个寄存器或一个内存单元的内容
    (ax)表示ax的内容
    (20000H)表示20000H单元的内容(()中的内存单元的地址为物理地址)
    ((ds)19+(bx))表示:
    ds中的内容为ADR1,bx中内容为ADR2,内存ADR1
    16+ADR2的内容
    也可以说ds中ADR1作为段地址,bx中的ADR2作为偏移地址,内存ADR1:ADR2单元的内容
    ()中的元素可以有3种类型:1寄存器名 2段寄存器名 3内存单元的物理地址(一个20位的数据)

我们看一下(X)应用

  1. ax中的内容为0010H,可以这样来描述: (ax)=0010H;
  2. 2000:1000处的内容为0010H,可以这样来描述:(21000H)=0010H;
  3. 对于mov ax,[2]的功能,可以这样来描述:(ax)=((ds)*16+2);
  4. 对于mov [2],ax的功能,可以这样来描述: ((ds)*16+2)=(ax);
  5. 对于add ax,2的功能,可以这样来描述:(ax)=(ax)+2;
  6. 对于add ax,bx的功能,可以这样来描述:(ax)=(ax)+(bx);
  7. 对于push ax 的功能,可以这样来描述:
    (sp)=(sp)-2
    ((ss)*16+(sp))=(ax)
  8. 对于pop ax的功能,可以这样来描述:
    (ax)=((ss)*16+(sp))
    (sp)=(sp)+2

“(X)”所表示的数据有两种:1 字节 2 字

5.1 [BX]

写出程序执行后,21000H~21007H单元中的内容
在这里插入图片描述

提示:

mov bx,1
inc bx 
这两条指令执行后bx为2 

分析:

1.movax,2000H
2.mov ds,ax
3.mov bx,1000H
三条指令执行完后 ds=2000H,bx=1000H
4.mov ax,[bx]
执行前:ds=2000H,bx=1000H,则mov ax,[bx]将内存2000:1000处的字型数据送入ax中。该指令执行后,ax=00beH。
5.inc bx
6.inc bx
执行后bx=1002H
7.mov [bx],ax

执行前:ds=2000H,bx=1002H,则mov [bx],ax将把ax中的数据送入内存2000:1002处。
执行指令后,2000:1002单元的内容为BE,2000:1003单元的内容为00
8.inc bx
9.inc bx
执行完后bx=1004H
10.mov [bx],ax
11.inc bx
10执行前,ds=2000H,bx=1004H,则mov [bx],ax将ax中的数据送入内存2000:1004中
执行后20001004的内容为BE,2000:1005单元内容为00
11inc bx执行后bx=1005H
12.mov [bx],al
13.inc bx
14.mov [bx],al
12指令执行前:ds=2000H,bx=1005H,则 mov [bx],al将把al 中的数据送入内存2000:1005处。
指令执行后,2000:1005单元的内容为BE。
13执行后bx=1006H
14指令执行前:ds=2000H,bx=1006H,则 mov [bx],al将把al 中的数据送入内存2000:1006处。指令执行后,2000:1005单元的内容为BE。

执行后如下表所示
在这里插入图片描述

5.2 Loop指令

loop指令格式是:loop标号,cpu执行loop指令的时候,进行两步操作:1.(cx)=(cx)-1 2.判断cx中的值,不为零则转到标号处执行程序,如果为零则向下执行。
从上面的描述中,可以看到,cx中的值影响着loop执行结果。通常我们用loop指令来实现循环功能,cx中存放循环次数。

  • loop指令的功能

编程计算2^2,结果存在ax中

分析:设(ax)=2,可计算(ax)=(ax)*2,最后(ax)中的值为2^2.N*2可用、N+N实现

assume cs:code
cod	segment
	mov ax,2
	add	ax,ax

	mov ax,4c00h
	int	21h
code ends
end 

计算2^3

分析:2^3=2*2*2,设(ax)=2,(ax)=(ax)*2*2,最后(ax)中为2^3的值。

assume cs:code
cod	segment
	mov ax,2
	add	ax,ax
	add ax,ax

	mov ax,4c00h
	int	21h
code ends
end 

计算2^12

分析:2^12=2*2*2*2*2*2*2*2*2*2*2*2,设(ax)=2,(ax)=(ax)*2*2*2*2*2*2*2*2*2*2*2

assume cs:code
cod	segment
	mov ax,2
	11次,add ax,ax
	

	mov ax,4c00h
	int	21h
code ends
end 
  • 计算2^12要重复11次add ax,ax,现在可以用loop简化程序
assume cs:code
code	segment
	mov ax,2
	mov	cx,11
  s:add	ax,ax
	loop s
	mov ax,4c00h
	int	21h
code ends
end 
  • 程序分析:
  1. 标号:标号代表一个地址,程序中有一个标号s。他实际标识了一个地址,这个地址有一条指令:add ax,ax。
  2. loop s:cpu执行loops的时候,要进行两步操作:
    (1)(cx)=(cx)-1
    (2) 判断cx中的值,部位0转至标号s所表示的地址处所执行(这里指令是add ax,ax),如果为零则执行下一条指令mov ax,4c00h)
	mov cx,11
  s:add ax,ax
  	loop s

执行loop s时,首先将(cx)-1,然后(cx)不为零,则向前转至s处执行add ax,ax。所以,可以利用cx来控制add ax,ax的执行次数,下面分析这段程序:

1.执行 mov cx,11,设置(cx)=11
2.执行 add ax,ax(第一次)
3.执行loop s将(cx)减1,(cx)=10,(cx)不为0,跳至s处
4.执行 add ax,ax(第二次)
5.执行loop s将(cx)减1,(cx)=9,(cx)不为0,跳至s处
6.执行 add ax,ax(第三次)
7.执行loop s将(cx)减1,(cx)=8,(cx)不为0,跳至s处
8.执行 add ax,ax(第四次)
9.执行loop s将(cx)减1,(cx)=7,(cx)不为0,跳至s处
  ………………
22. 执行 add ax,ax(第11次)
23.执行loop s将(cx)减1,(cx)=0,(cx)为0,向下执行(结束循环)

5.3 在Debug中跟踪用loop指令实现的循环程序

  • 考虑这样一个问题,计算ffff:0006单元中的数乘以3,结果存储在dx 中。
    分析一下
  1. 运算后的结果是否会超出dx所能存储的范围?
    ffff:0006单元中的数是一个字节型的数据,范围在0~255之间,则用它和3相乘结果不会大于65535,可以在dx中存放下。

  2. 用循环累加来实现乘法,用哪个寄存器进行累加?
    将ffff:0006单元中的数赋值给ax,用dx 进行累加。先设(dx)=O,然后做3次(dx)=(dx)+(ax)。

  3. ffff:6单元是一个字节单元,ax是一个16位寄存器,数据的长度不一样,如何赋值?
    我们说的是“赋值”,就是说,让 ax 中的数据的值(数据的大小)和flf:0006单元中的数据的值(数据的大小)相等。8位数据01H和 16位数据0001H的数据长度不一样,但它们的值是相等的。

那么我们如何赋值?设ffff:0006单元中的数据是XXH,若要ax 中的值和ffff:0006单元中的相等,ax中的数据应为00XXH。所以,若实现 ffff:0006单元向ax赋值,应该令(ah)=O,(al)=(ffff6H)。

编程如下:

assume cs:code
code segment
	mov ax,0ffffh
	mov dx,ax
	mov bx,6            ;以上,设置ds:bx指向ffff:6
	mov al,[bx]
	mov dx,0            ;以上,设置(al)=((ds*16+(bx)),(ah)=0
	mov dx,0
	mov cx,3            ;循环3次
	s:add dx,ax
	loop s               ;以上,累加计算(ax)*3
	mov ax,4c00h
	int 21h              ;程序返回
code ends
end

下面开始debug
用dosbox运行3.exe文件用r指令查看寄存器内容在这里插入图片描述
用u指令查看被debug加载入内存的程序
在这里插入图片描述
开始跟踪
在这里插入图片描述

  • 前3条指令执行后,(ds)=ffffh,(bx)=6,ds:bx 指向ffff:6单元。Debug 显示出当前要执行的指令“mov al,[bx]”,因为是读取内存的指令,所以Debug 将要访问的内存单元中的内容也显示出来,可以看到屏幕最右边显示的“ds:0006=32”,由此,我们可以方便地知道目标单元(ffff6)中的内容是32h。

继续执行,在这里插入图片描述
执行后(ax)=32h,完成了从ffff:6单元想ax的赋值
继续执行
在这里插入图片描述
这两条指令执行后,(dx)=0,完成对累加寄存器的初始化,(cx)=3,完成对循环寄存器的初始化

开始执行循环段
CPU执行0B3D:0012处的指令“add dx,ax”后,(IP)=0014h,CS:IP指向0B3D:0014 处的指令“loop 0012”。CPU执行“loop 0012”,第一步先将(cx)减1,(cx)=2;第二步因(cx)不等于0,将IP设为0012h。指令“loop 0012”执行后,(IP)=0012h,CS:IP再次指向0B3D:0012处的指令“add dx,ax”,这条指令将再次得到执行。注意,“loop 0012”执行后(cx)=2,也就是说,“loop 0012”还可以进行两次循环。
接着重复执行add dx,ax和loop 0012
直到(cx)=0在这里插入图片描述
最后一次执行“loop 0012”的结果,执行前(cx)=1,cpu执行“loop 0012”,第一部,(cx)=(cx)-1,(cx)=0;第二步,(cx)=0,所以loop指令不转跳,(ip)=0016h,cpu向下执行0B3D:0016处的指令“mov ax,4c00”

5.4Debug和汇编编译器masm对指令的不同处理

  • Debug和编译器 masm对形如“movax,[0]”这类指令在解释上的不同。我们在Debug中和源程序中写入同样形式的指令:“mov al,[0]”、“mov bl,[1]”、“mov cl,[2]”、“mov dl,[3]”,但 Debug 和编译器对这些指令中的“[idata]”却有不同的解释。Debug 将它解释为“[idata]”是一个内存单元,“idata”是内存单元的偏移地址;而编译器将“[idata]”解释为“idata”。
  • 怎么在源程序中实现将内存2000:0、2000:2、2000:3单元中的数据送入al、bl、cl、dl中?
    将偏移地址送入bx寄存器,用[bx]的方式访问内存单元。
    举例:
mov ax,2000H
mov ds,ax
mov bx,0
mov al,[bx]
  • 比较一下汇编源程序中以下指令的含义。

“mov al,[0]”含义:(al)=0,将常量0送入al中(想似于mov al,0)
"mov al,ds:[0]"含义:(al)=((ds)*16+0),将内存单元中的数据送入al中;
mov al,[bx],含义:(al)=((ds)*16+(bx)),将内存单元中的数据送入al中
mov al,ds:[bx].含义:与“mov al,[bx]”相同

  • 1)在汇编源程序中,如果用指令访问一个内存单元,则在指令中必须用“[.…]”来表示内存单元,如果在“[]”里用一个常量idata直接给出内存单元的偏移地址,就要在“”的前面显式地给出段地址所在的段寄存器。

5.5 loop和[bx]的联合应用

  • 计算FFFF:0~FFFF:b单元中的数据的和放在dx中

  • 首先得先判断是否这么多数字的和超过了dx所能存储的最大范围

    • 分析:0~ b偏移量的内存单元中存放的都是8位的数字,8位表示的范围为0~255,而12个单元的数据相加最大也不会超过12 * 255 << 65535(16位所能存放的最大数据值),因此可以放得下
  • 怎样将ffff:0-ffff:b中的8位数据,累加到16位寄存器dx中

    • 分析:首先不能把0~b中数据累加到dx中,因为 0 ~b是8位数据不能直接加到16位寄存器。也不能将0 ~b累加到dl中设置dh=0,因为dl是8位寄存器,能容纳数据0 ~255之间,0 ~b数据也是8位,仅仅向12个8位数据,有可能造成进位丢失。所以应该让类型的匹配和结果的不超界。就是用一个16位寄存器来做中介,将内存单元中的8位数据赋值到一个16位寄存器ax中,再将ax中数据加到dx上,从而使两个运算对象的类型匹配并且不会超界

5.6 段前缀

  • 指令“mov ax,[bx]”中,内存地址的偏移地址有bx给出,段地址默认在ds中。我们可以在访问内存单元的指令中显示的给出内存单元的段地址所在的段寄存器。
    (1) mov ax,ds:[bx]
    将一个内存单元的内容送入ax,这个内存单元的长度为2字节,偏移地址在bx中,段地址在ds中
    (2)mov ax,cs:[bx]
    将一个内存单元的内容送入ax,这个内存单元的长度为2字节,偏移地址在[bx]中

  • 这些出现在访问内存单元的指令中,用于显式的指明内存单元的段地址的“ds:”“cs:”,在汇编语言里称为段前缀

5.7 一段安全的空间

  • 在8086模式中,随意向一段内存空间写入内容是很危险的 ,因为这段空间里面可能存放着很重要的系统数据或者代码
  • 在一般的PC机中,DOS方式下,DOS和其他合法的程序一般都不会使用0:2000:2ff(00200h002ffh)的256个字节的空间。所以,我们使用这段空间是安全的。不过为了谨慎起见,在进入DOS后,我们可以先用Debug 查看一下,如果0:200~0:2ff单元的内容都是0的话,则证明DOS 和其他合法的程序没有使用这里。
  • (1)我们需要直接向一段内存中写入内容;
  • (2)这段内存空间不应存放系统或其他程序的数据或代码,否则写入操作很可能引发错误;
  • (3) DOS方式下,一般情况,0:200~0:2ff 空间中没有系统或其他程序的数据或代码;
  • (4)以后,我们需要直接向一段内存中写入内容时,就使用0:200~0:2ff这段空间。

5.8 段前缀的使用

  • 将内存ffff:0 ~ ffff:b单元中数据复制到0:200~0:20b单元中。
    • 分析:0:200~0:20b单元等同于0020:0 ~0020:b单元,他们描述同一段内存空间
    • 循环过程复杂
    • 循环中,源始地址ffff:x和目标单元0020:x的偏移地址x是变量。用bx存放
    • 0:200 ~ 0:20b用0020:0~0020:b描述,就是为了使目标单元的偏移地址和原始单元偏移地址从同一0开始
      代码如下:
      在这里插入图片描述
      因源始单元ffff:X和目标单元0020:X相距大于64KB,在不同的64KB 段里,程序5.8中,每次循环要设置两次 ds。这样做是正确的,但是效率不高。我们可以使用两个段寄存器分别存放源始单元ffff:X和目标单元0020:X的段地址,这样就可以省略循环中需要重复做12次的设置ds的程序段。

改进如下:在这里插入图片描述
用es存放目标地址0020:0~0020:b段地址,用ds存放原始空间ffff:0 ~ffff:b的段地址。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Edison.W

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值