【汇编语言】第五章 [BX]和loop指令

  • Post author:
  • Post category:其他




前言

最近学了王爽教授写的《汇编语言》,整理一下学习笔记。



5.1 [BX]



[bx]和内存单元的描述

通过前面的学习我们知道。[···]表示内存单元,比如[2],它的偏移地址是2。[bx]同样表示一个内存单元,只不过它的

偏移地址在bx中,段地址在ds中

要完整的描述一个内存单元,需要两种信息:


(1)内存单元的地址;

(2)内存单元的长度;

以[bx]为例:

mov ax, [bx]表示将一个内存单元的内容送入ax,这个

内存单元的长度为2字节

;

mov al, [bx]表示将一个内存单元的内容送入al,这个

内存单元的长度为1字节

我们定义“()”来表示一个寄存器或一个内存单元中的内容。如:

(ax)表示ax中的内容,(bl)表示bl中的内容。

要注意的是,“()”中的元素可以有三种类型:


(1)寄存器名

(2)段寄存器名

(3)内存单元的物理地址(一个20位数据)


比如:(20000H)是正确的用法,但(2000: 0)是错误的用法。



idata常量


idata表示常量

,比如:

mov ax, [idata]就可以代表 mov ax, [1]、mov ax, [2]等;

mov bx, idata就可以代表mov bx, 1、mov bx, 2等。



inc指令


inc



算数运算指令

,表示

加1

,比如:

mov bx, 1

inc bx

执行后 bx的值为2。

类似于高级语言中的“

++运算符



5.2 loop指令


loop



循环控制指令

,Loop指令需要和cx寄存器配合使用, 用于循环操作, 类似于高级语言中的for, while等。

循环次数存储在cx寄存器中

,循环标号要自己定义,使用格式是:


mov cx, 循环次数

标号: (表明后面就是需要循环的循环体) 循环执行的程序代码

loop 标号

CPU在执行loop指令时,进行两步操作:


(1) (cx)=(cx)-1;

(2) 判断cx中的值,不为0则转至标号处执行程序,如果为0则向下执行。

比如:计算2^12,用add ax, ax指令的话要重复写11次,但是用loop指令就可以大大化简程序:

p2

现在来分析一下上面的程序:


(1)标号


汇编语言中,标号通常代表地址。上面程序中有一个标号

s

,它

标识了一个地址

,此地址处有一条指令:

add ax,ax




(2)loop s


CPU执行loop s时,要进行两步操作:


<1> (cx)=(cx)-1;

<2> 判断(cx)中的值,不为0则转至标号s处所标识的地址,如果为0,则执行下一条指令

(即mov ax, 4c00h)。



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

现在有一个问题:


计算 ffff:0006单元中的数乘以3,结果存储在dx中

让我们来分析一下这个问题:

(1)运算后的结果是否会超出dx的存储范围?


ffff:0006

中的数是一个字节型的数据,范围在

0~255

之间,则它和3相乘的结果不会大于

65535

,可以在

dx

中放下。

(2)用循环累加来实现乘法,用哪个寄存器进行累加?

我们可以将

ffff:0006

单元中的数赋值给

ax

,用dx进行累加。先设


(dx)=0

,然后做3次

(dx)=(dx)+(ax)



(3)

ffff:0006

单元是一个字节,

ax

十一个

16

位寄存器,数据长度不一样,如何赋值?

很显然,我们需要让

ax

的高八位为

0

,把

ffff:0006

赋值给

ax

的低八位,这样就可以解决这个问题了。

下面开始写代码:

p3


注意:

程序中第一条指令,

mov ax, 0ffffh

。我们知道,大于9FFFh的十六进制数据如

A000H、A001H

等等,在书写的时候都是以字母开头的。而在汇编源程序中,

数据不能以字母开头

,所以要在前面加

0

下面我们对程序的执行过程进行跟踪。首先将它编辑成源文件,文件名为

p3.asm

;编译连接之后生成

p3.exe

;然后用

Debug



p3.exe

中的程序进行跟踪。

Debug跟踪p3

从图中我们可以看到,

(ds)=075AH

,所以程序在

076A: 0000

处;

CS: IP

指向程序的第一条指令。

我们再用

U命令

看一下被

Debug

加载入内存的程序:

用u命令查看被Debug加载入内存的程序

我们可以看到,从

076A: 0000~076A: 001A

是我们的程序,

076A: 0014

处是源程序中的指令

loop s

,只是此时标号s已经变成了一个地址

0012h

。在执行循环指令时,如果

(cx)-1

后不为

0

,那么此时 “

loop 0012

” 就会把

IP

设置成

0012h

,从而使

CS: IP

指向

076A: 0012

处的

add dx, ax

,从而实现跳转。



g命令

由于循环程序从

CS: 0012

开始,在这前面的指令,我们不想再一步步地跟踪,想一次性执行完,然后从

CS: 0012

开始跟踪。可以用

g命令

来实现这样的操作:

g命令


Debug

执行 “

g 0012

” 后,我们可以通过各个相关寄存器的值,看出执行结果。



p命令

但是现在我又嫌一次次循环跟踪太麻烦,我希望它一次性就可以将循环执行完。可以用

p命令

来达到我们的目的。以后当我们遇到

loop

指令时,就可以用p命令来

自动重复执行循环中的指令

,直到

(cx)=0

为止。

用p命令执行loop指令

执行完循环,当前指令为

CS: 0016

处的 “

mov ax,4c00

”。当然,我们也可以直接用

g

命令,“

g 0016

”,来达到同样的目的。



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

举个例子:



Debug

中:

mov ax, [0]

表示将

ds: 0

处的数据送入

ax

中,这里[

0

]表示一个内存单元;

但是在汇编程序中,

masm

会将这句话解释为

mov ax, 0!!!

也就是说,对于[

idata

],Debug将它解释为

偏移地址为idata的内存单元

;而编译器将它解释为

常数idata



目前的

解决方法

是,可以将偏移地址送进一个寄存器

bx

,用[

bx

]的方式来访问内存单元就不会出错了,段地址默认在

ds

中。



5.5 loop和[bx]的联合应用

想一下这个问题:


计算 ffff: 0~ffff: b 单元中的数据和,结果存储在dx中

先来分析一下这个问题:

(1)运算后的结果是否会超过

dx

所能存储的范围?


ffff: 0~ffff: b

内存单元中的数据是字节型,范围在

0 ~ 225

之间,

12

个这样的数据相加,结果不会大于

65535

,可以在

dx

中存放下。

(2)可不可以直接将

ffff: 0~ffff: b

中的数据累加到

dx

中,或者

dl

中?


ffff: 0~ffff: b

中的数据是

8

位的,不能直接加到

16

位寄存器中。而且向

dl

中累加

12



8

位数据,很有可能造成进位丢失

(3)如何将

ffff: 0~ffff: b 中



8

位数据,累加到

16

位寄存器

dx

中?

没办法,只能找一个

16

位寄存器当中介。将内存中的8位数据赋值到一个

16

位寄存器

ax

中,再将

ax

中的数据加到

bx

上。

话不多说,上代码:

p6



5.6 段前缀

用于显示地指明内存单元的段地址的 “

ds:

”、“

cs:

”、“

ss:

”、“

es:

”,在汇编语言中称为段前缀。

比如 “

mov ax, ds: [bx]

”,然后来看一个问题:


将内存ffff: 0~ffff: b单元中的数据复制到0: 200 ~ 0: 20b单元中

浅浅分析一下:

(1)复制过程应用

循环

来实现,这样会方便很多,简单描述一下:


初始化:

X=0

循环12次:

将ffff: X单元中的数据送入0020: X (需要用一个寄存器中转)、

X++;


(2)在循环中,原始单元

ffff: X

和目标单元

0020: X

的偏移地址X是变量。我们用

bx

来存放。

(3)将

0: 200 ~ 0: 20b



0020: 0 ~ 0020: b

描述,使原始单元和目标单元的偏移地址都从同一数值0开始。

话不多说过,上代码:

p9

程序使用es访问目标空间

0020: 0 ~ 0020: b

的段地址,用ds存放原始空间

ffff: 0 ~ ffff: b

的段地址。在访问内存单元的指令 “

mov es: [bx], al

” 中,用段前缀 “

es:

” 给出单元的段地址,这样就不必在循环中重复设置ds。



5.7 一段安全的空间



8086模式

中,随意向一段内存空间中写入内容是很危险的,因为这段内存空间中可能存放着

重要的系统数据或代码

。比如,在

Windows 7



DOS方式

中,在

Debug

里运行 “

mov [0026], ax

”这条指令时,就会出错:

Debug运行



ax

赋值为

1

,然后运行指令,结果发现:

错误

产生这种结果的原因就是

0: 0026

处存放着

重要的系统数据

,而“mov [0026],ax ” 将其改写了。

可见,我们要向内存空间写入数据的话,要

使用操作系统给我们分配的空间

,而不应该直接用地址任意指定内存单元,向里面写入。

DOS方式下,一般情况,

0:200 ~ 0:2ff

空间中没有系统或其他程序的数据或代码,我们可以使用这段空间来向内存写入内容。



总结

以上为本人学习汇编语言时的摘录总结,主要内容来源于

汇编语言(第四版) 王爽 著

,大家若是感兴趣可以看看原书,很值得推荐,以上内容如果有什么错误的话,还请大家指正!



版权声明:本文为qq_62068476原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。