第五章 [BX]和loop指令
5.1 [bx]
[bx]是什么
和 [0] 有些类似,[0] 表示内存单元,它的偏移地址是 0。
例如:
mov ax, [0]
内存以字节为单位;ax以字(16bit = 2Byte)为单位;al以字节为单位。所以,mov ax, [0]
解读为把偏移地址为 0 的内存单元处的一个字对应的内容复制到 ax 寄存器中。mov al, [0]
解读为把偏移地址为 0 的内存单元处的一个字节对应的内容复制到 ax 寄存器中,因为 al 寄存器的长度为一个字节。
[bx]和内存单元的描述
我们要完整地描述一个内存单元,需要两个信息:
内存单元的地址;
内存单元的的长度(类型)。
我们用[0]表示一个内存单元时,0表示单元的偏移地址,段地址默认在ds中,单元的长度(类型)可以由具体指令中的其它操作对象(比如说寄存器)指出,如前边的 ax、al。若是ax,则以字单位传输;al,则以字节单位传输。
针对在MASM中的情况,[bx]同样也表示一个内存单元,它的偏移地址在bx中,比如下面的指令:
mov ax, [bx]
在MASM情况下,要实现“把偏移地址为 0 对应的内存单元的内容传给 ax 寄存器”,只能这么写:
mov bx, 0 mov ax, [bx]
题外话
在MASM中,例如下面一段汇编代码:
xxx.asm 文件内容为:
assume cs:codesg codesg segment fishcc: mov ax, 2000H mov ds, ax mov al, [0] mov bl, [1] mov cl, [2] mov dl, [3] mov ax, 4C00H int 21H codesg ends end fishcc
注:在 MASM 下折腾汇编语言时,代码
mov bl, [1]
不能解读为把偏移地址为 1 处的内存单元的内容复制到 bl 寄存器,而应该解读为把 1 赋值给 bl 寄存器。在 Debug 中我们若用
-a
输入汇编指令mov bl, [1]
则按正常解读。解读为把偏移地址为 1 处的内存单元的内容复制到 bl 寄存器。
也就是说,当在以后缀为 .asm 文件内写入汇编指令时,才特殊对待。其它情况按汇编语法解释理解即可。
5.2 Loop指令
英文单词“loop”有循环的含义,显然这个指令和循环有关。
描述性符号 “()”
为了描述上的简洁,在以后的课程中,我们将使用一个描述性的符号 “()” 来表示一个寄存器或一个内存单元中的内容。也就是说括号的作用是取内容。比如:
ax 中的内容为 0010H,我们可以这样来描述:(ax) = 0010H
2000:1000 处的内容为 0010H,我们可以这样来描述:(21000) = 0010H
对于
mov ax,[2]
的功能,我们可以这样来描述:(ax) = ((ds) * 16 + 2)对于
mov [2],ax
的功能,我们可以这样来描述:((ds) * 16 + 2) = (ax)对于
add ax,2
的功能,我们可以这样来描述:(ax) = (ax) + 2对于
add ax,bx
的功能,我们可以这样来描述:(ax) = (ax) + (bx)对于
push ax
的功能,我们可以这样来描述(push,入栈,先修改栈顶指针然后存入数据;pull,出栈,先取出数据后修改栈顶指针。):(sp) = (sp) - 2
((ss) * 16 + (sp)) = (ax)
对于
pop ax
的功能,我们可以这样来描述:(ax) = ((ss) * 16 + (sp))
(sp) = (sp) + 2
约定符号 idata 表示常量
我们在 Debug 中写过类似的指令:mov ax,[0]
表示将 ds:0
处的数据送入 ax 中。指令中,在 “[...]” 里用一个常量 0 表示内存单元的偏移地址。以后,我们用 idata 表示常量。例如:
mov ax,[idata]
就代表mov ax,[1]
、mov ax,[2]
、mov ax,[3]
等。mov bx,idata
就代表mov bx,1
、mov bx,2
、mov bx,3
等。mov ds,idata
就代表mov ds,1
、mov ds,2
等,它们都是非法指令(因为段寄存器不能直接操作)。
5.1 [bx]
我们来看一看下面指令的功能:
mov ax,[bx]
功能:bx 中存放的数据作为一个偏移地址 EA,段地址 SA 默认在 ds 中,将 SA:EA 处的数据送入 ax 中。
即:(ax) = ((ds) * 16 + (bx))
mov [bx],ax
功能:bx 中存放的数据作为一个偏移地址 EA,段地址 SA 默认在 ds 中,将 ax 中的数据送入内存 SA:EA 处。
即:((ds) * 16 + (bx)) = (ax)
问题 5.1
准备工作:
我们首先在 masm 根目录下创建 test.asm 文件,并写入内容:
assume cs:codesg
codesg segment
fishcc:
mov ax, 2000H
mov ds, ax
mov bx, 1000H
mov ax, [bx]
inc bx
inc bx
mov [bx], ax
inc bx
inc bx
mov [bx], ax
inc bx
mov [bx], al
inc bx
mov [bx], al
codesg ends
end fishcc
然后 Debug 生成的 test.exe 这个程序,单步调试来看一看。以上过程如下图:
然后修改内存 2000:1000 处的内容为 be 00
查看是否修改成功:
问题 5.1 分析
先看一下程序的前三条指令:
mov ax, 2000H mov ds, ax mov bx, 1000H
这三条指令执行后:
ds = 2000H
bx = 1000H
附图:
再看第4条指令:
mov ax, [bx]
。执行前:ds = 2000H, bx = 1000H,则mov ax, [bx]
将把内存 2000:1000 处的字型数据送入 ax 中。该指令执行后,ax = 00beH。附图:
再看5、6条指令:
inc bx inc bx
指令执行前:bx = 1000H
执行后:bx = 1002H
附图:
再看第7条指令:
mov [bx], ax
指令执行前:ds = 2000H, bx = 1002H,则
mov [bx], ax
将把 ax 中的数据送入内存 2000:1002 处。指令执行后:2000:1002 单元的内容为 BE,2000:1003 单元的内容为 00。
附图:
接下来,第8、9条指令:
inc bx inc bx
这两条指令执行前 bx = 1002H,执行后 bx = 1004H。
附图:
接下来,第10条指令:
mov [bx], ax
指令执行前:ds = 2000H,bx = 1004H,则
mov [bx], ax
将把 ax 中的数据送入内存 2000:1004 处。指令执行后,2000:1004 单元的内容为 BE,2000:1005 单元的内容为 00。
接下来,第11条指令:
inc bx
。这条指令执行前 bx = 1004H,执行后 bx = 1005H。附图:
接下来,第12条指令:
mov [bx], al
指令执行前:ds = 2000H,bx = 1005H,则
mov [bx], al
将把 al 中的数据送入内存 2000:1005 处。指令执行后,2000:1005 单元的内容为 BE。附图:
接下来,第13条指令:
inc bx
。这条指令执行前 bx = 1005H,执行后 bx = 1006H。附图:
接下来,第14条指令:
mov [bx], al
指令执行前:ds = 2000H,bx = 1006H,则 mov [bx], al
将把 al 中的数据送入内存 2000:1006 处。指令执行后,2000:1006 单元的内容为 BE。
附图:
### Loop 指令
- 格式:loop 标号
- CPU执行loop指令的时候,要进行两步操作:
- (cx) = (cx) - 1;
- 判断 cx 中的值,不为零则转至标号处执行程序,如果为零则向下执行。
- 从上面的描述中,我们可以看到,cx 中的值影响着loop指令的执行结果。通常(注意,我们说的是通常情况)我们用loop指令来实现循环功能,cx 中存放循环次数。下面我们结合一个例子来具体看一看loop指令执行流程。
### 程序 5.1
在MASM环境的根目录下创建待编译文件loop.asm,在其中写入内容:
assume cs:code
code segment
addrs:
mov ax,2
mov cx,11
s:add ax, ax
loop s
mov ax,4c00h
int 21h
code ends
end addrs
注意:微软的MASM默认是十进制的,所以指令 mov ax,4c00h
中的 4c00
要加上 h/H
。Debug默认是十六进制的,所以我们在debug中则用加 h/H
。
然后 Debug 生成的 loop.exe 这个程序,单步调试来看一看。以上过程如下图:
首先我们把ax寄存器的值赋值为2,cx原本默认是存储这个程序的大小,在这里表面程序的大小为000FH,即15个字节。
附图:
然后指令 mov cx,000b
将 11 赋值给 cx 寄存器。
附图:
观察 cx 寄存器的值,直到 cx 减为 0。此时循环结束,ax 寄存器的值为 1000H,转换成十进制正好是 4096 = \(2^{12}\) 。
附图:
然后用debug的 t 命令继续执行循环后边的语句 mov ax,4c00h
。int 21
表示程序结束,键入debug的 p 命令结束程序;接着键入debug的q命令退出Debug。键入 exit 退出终端。
附图:
小结
从上边的课程中,我们可以总结出用 cx 和 loop 指令相配合实现循环功能的三个要点:
- 在 cx 中存放循环次数;
- loop指令中的标号所标识地址要在前面;
- 循环执行的程序段,要写在标号和loop指令的中间。
用 cx 和 loop 指令相配合实现循环功能的程序框架如下:
mov cx,循环次数
s:
循环执行的程序段
loop s
指令 loop s
会做三件事:
- (cx) = (cx) - 1;
- 判断 cx 中的值,不为零则转至标号(s)处执行程序;
- 如果为零则向下执行。
温故而知新
- [bx] 的作用:作为偏移地址与DS配合
- loop 和 cx 合作
- debug 的 -g 偏移地址命令和 -p 命令
5.3 在Debug中跟踪用loop指令实现的循环程序
5.4 Debug和汇编编译器Masm对指令的不同处理
正是由于它俩对汇编语法的不同处理,诞生了 [bx] 的应用。
我们在Debug中写过类似的指令:
mov ax,[0]
表示将 ds:0 处的数据送入 al 中。因为内存单元是一个字节,ax 是两个字节。所以,该语句使 al 内容为 ds:0 处的数据;ah 默认置为 0。
但是在汇编元程序中(即:Masm),指令“mov ax,[0]”被编译器当作指令“mov ax,0”处理。
示例:
将内存2000:0、2000:1、2000:2、2000:3单元中的数据送入al、bl、cl、dl中。
- 在Debug中编程实现
- 汇编程序实现
两种实现的实际实施情况
在Debug中编程实现
回车
debug的 -r 命令:查看当前寄存器的值;
debug的 -t 命令:向前执行一条指令;
debug的 -d 命令:查看内存单元内容(比如查看内存从地址1000:0处开始,8个字节的内容);
debug的 -e 命令:修改内存单元内容(比如修改内存地址1000:0单元的内容);
debug的 -u 命令:将内存中的机器码翻译为汇编代码;
debug的 -a 命令:以汇编语言的形式在内存中写入一段程序。
我们现在用 -r 命令查看当前 ds 寄存器内容(即当前地址),在此处写入一段汇编代码:
接着设置 CS:IP 的值为 0b2a:0000 ,CS 的值已经是 0b2a,只要把 IP 指向 0000 即可。
附图:
注:在debug中默认的单位是十六进制;在Masm中默认的单位是十进制。
接着就可以用debug的 -t 命令单步跟踪观察刚刚输入的汇编代码对寄存器操作的情况了。
附图:
对比汇编程序实现
在Masm环境的根目录创建测试文件 test.asm ,写入下面代码:
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
注意:
- Masm只认十进制,所以第二行的 h/H 不要忘记写。
- 还有就是 Masm 不认中括号,类似于
mov al,[0]
这样的命令,实现的效果是把 0 赋值给 al 寄存器,而不是把内存偏移地址 0 处的内容复制到 al 寄存器。mov al,[0]
和mov al,0
在 Masm 是等价的。
附图:
注:如上边注意中提到一样。我们在 test.asm 中写的汇编命令是 mov al,[0]
,Masm 只会把 0 赋值给 al 寄存器。
mov bl,[1]
mov cl,[2]
mov dl,[3]
也是一样的道理:
若我们想把内存偏移地址 0 处的内容复制到 al 寄存器,怎么实现呢?这时候需要借助 bx 寄存器。
修改上边 test.asm 中的汇编代码如下:
assume cs:code
code segment
mov ax,2000h
mov ds,ax
mov bx,0
mov al,[bx]
mov bx,1
mov bl,[bx]
mov bx,2
mov cl,[bx]
mov bx,3
mov dl,[bx]
mov ax,4c00h
int 21h
code ends
end
重新编译 test.asm
,调试 test.exe
。即可实现我们想要的把内存偏移地址 0 处的内容复制到 al 寄存器。因为内存地址 2000:0000 单元的内容此例正好为0,我们不好看出效果。我们直接看把内存偏移地址 1 处的内容复制到 bl 寄存器的情况。
附图:
注:内存单元 2000:0 和 2000:1 的内容都为0。
如果一定要像DEBUG那样
在MASM中 mov ax,[2]
是解释为 mov ax,2
的。一般我们是通过 bx 来代替,像前边我们先 mov bx,2
再通过 mov ax,[bx]
来实现。但是我们要像DEBUG一样直接用 [2]
可以吗?答案是可以的,不过要加上段地址。
assume cs:code
code segment
mov ax,2000h
mov ds,ax
mov al,ds:[0]
mov bl,ds:[1]
mov cl,ds:[2]
mov dl,ds:[3]
mov ax,4c00h
int 21h
code ends
end
5.5 loop和[bx]的联合应用
5.6 段前缀
- 指令
mov ax,[bx]
中,内存单元的偏移地址由 bx 给出,而段地址默认在 ds 中。
5.7 一段安全的空间
在一般的PC机中,DOS方式下(实模式下),DOS和其他合法的程序一般都不会使用 0:200~0:2FF (0:200h~0:2FFh) 的256个字节的空间。所以,我们使用这段空间是安全的。
5.8 段前缀的使用
第五章 [BX]和loop指令的更多相关文章
- [汇编学习笔记][第五章[BX]和loop指令]
第五章[BX]和loop指令 前言 定义描述性符号“()”来表示一个寄存器或一个内存单元的内容,比如: (ax)表示ax中的内容,(al)表示al的内容. 约定符号ideta表示常量. 5.1 [BX ...
- 王爽汇编第五章,[bx]和loop指令
目录 王爽汇编第五章,[bx]和loop指令 [bx]和loop指令 例子: 王爽汇编第五章,[bx]和loop指令 [bx]和loop指令 [bx]之前我们介绍寄存器的时候,已经很详细的说明过了,b ...
- [汇编语言]-第五章[bx]和loop指令
1- [bx]和内存单元的描述 [0]表示内存单元, 他的偏移地址为0 mov ax,[0] 将一个内存单元的内容送入到ax.这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为0,段地址在d ...
- 汇编语言-[BX]和loop指令
汇编语言-[BX]和loop指令 [BX]指令介绍 mov ax,[bx] 功能: bx为偏移地址,段地址默认为ds.因此,上面指令作用就是将ax中的数据送入内存ds:bx处,即:((ds)*16 + ...
- 80806汇编(5)——[BX]和Loop指令
80806汇编(5)--[BX]和Loop指令 已经好久没写点东西了,国庆节就一直想弄个个人网站,这段时间一直在弄那个,虽然有现成的框架(Hexo),但是总想弄出自己的效果来,但是最后还是有些差强人意 ...
- 小甲鱼零基础汇编语言学习笔记第五章之[BX]和loop指令
这一章主要介绍什么是[BX]以及loop(循环)指令怎么使用,loop和[BX]又怎么样相结合,段前缀又是什么鬼,以及如何使用段前缀. 1.[BX]的概念 [BX]和[0]类似 ...
- [bx]和loop指令
body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...
- 汇编语言-[bx]和loop指令和多个段
5.1 [BX]和内存单元的描述 要完成描述一个内存单元,需要两种信息: 内存单元的地址: 可以用 [0] 表示一个内存单元, 0 表示单元的偏移地址,段地址默认在 ds 中: 同样也可以用 [bx] ...
- 实验4 [BX]和loop指令
实验内容: 1.综合使用loop,[bx],编写完整汇编程序,实现向内存b800:07b8开始的连续16个字单元重复填充字数据0441H. 实验结果: 若填充的数据为:0403h,则实验结果转变为: ...
随机推荐
- python xml childNodes,childNodes[1].childNodes[0].data例子
xml: <?xml version='1.0' encoding='utf-8'?><!--this is a test about xml--><booklist t ...
- Linux基础命令---sum,cksum
cksum 检查文件的crc是否正确,统计文件的字节数. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fedora. 1.语法 cks ...
- (五)使用GitHub的前期准备
创建账户 创建账号成功后将得到一个个人的公开页面URL:https://github.com/xkfx. 设置头像 设置SSH Key SSH 为 Secure Shell 的缩写. from bai ...
- Tomcat上发布webservices的war工程,访问异常404
Tomcat上发布webservices的war工程,访问异常404 Tomcat部署正常.war导出工程正常.Tomcat自带的工程可以正常访问: 问题: webservices工程访问异常404 ...
- Arduino使用HC05蓝牙模块与手机连接
Arduino使用HC05蓝牙模块与手机连接 一切都是最好的选择 首先是线路连接,一定不要接错了 Arduino 代码 #include <SoftwareSerial.h> // Pin ...
- 05: MySQL高级查询
MySQL其他篇 目录: 参考网站 1.1 GROUP BY分组使用 1.2 mysql中NOW(),CURDATE(),CURTIME()的使用 1.3 DATEDIFF() 函数 1.4 DATE ...
- 03: centos中配置使用svn
1.1 centos7.3源码搭建svn----安装各种依赖包 1.安装zlib-1.2.8.tar.xz xz -d zlib-1.2.8.tar.xz tar xvf zlib-1.2.8.tar ...
- mysql引擎问题
今天遇到需要修改数据库引擎问题 /*查看支持的引擎*/ show engines; /*默认引擎*/ show variables like '%storage_engine%'; /*看某个表用了什 ...
- 20145101《Java程序设计》第9周学习总结
20145101<Java程序设计>第9周学习总结 教材学习内容总结 第十六章 整合数据库 数据库本身是个独立运行的应用程序 撰写应用程序是利用通信协议对数据库进行指令交换,以进行数据的增 ...
- 格式化输出%与format
一.%的用法 1.1整数输出 %o —— oct 八进制 : %d —— dec 十进制 : %x —— hex 十六进制 >>> print('%o' % 20) 24 >& ...