第五章 [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]和内存单元的描述

  • 我们要完整地描述一个内存单元,需要两个信息:

    1. 内存单元的地址;

    2. 内存单元的的长度(类型)。

    我们用[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,1mov bx,2mov bx,3 等。
  • mov ds,idata 就代表 mov ds,1mov 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

屏幕快照 2018-08-14 15.46.22

准备工作:

我们首先在 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 这个程序,单步调试来看一看。以上过程如下图:

屏幕快照 2018-08-14 16.06.27

屏幕快照 2018-08-14 16.07.27

然后修改内存 2000:1000 处的内容为 be 00

屏幕快照 2018-08-14 16.10.31

查看是否修改成功:

屏幕快照 2018-08-14 16.11.42

问题 5.1 分析

  1. 先看一下程序的前三条指令:

    mov ax, 2000H
    mov ds, ax
    mov bx, 1000H
    

    这三条指令执行后:

    ds = 2000H

    bx = 1000H

    附图:

    屏幕快照 2018-08-14 16.22.51

  2. 再看第4条指令:mov ax, [bx] 。执行前:ds = 2000H, bx = 1000H,则 mov ax, [bx] 将把内存 2000:1000 处的字型数据送入 ax 中。该指令执行后,ax = 00beH。

    附图:

    屏幕快照 2018-08-14 16.34.35

  3. 再看5、6条指令:

    inc bx
    inc bx
    

    指令执行前:bx = 1000H

    执行后:bx = 1002H

    附图:

    屏幕快照 2018-08-14 16.39.20

  4. 再看第7条指令:mov [bx], ax

    指令执行前:ds = 2000H, bx = 1002H,则 mov [bx], ax 将把 ax 中的数据送入内存 2000:1002 处。

    指令执行后:2000:1002 单元的内容为 BE,2000:1003 单元的内容为 00。

    附图:

    屏幕快照 2018-08-14 16.49.10

  5. 接下来,第8、9条指令:

    inc bx
    inc bx
    

    这两条指令执行前 bx = 1002H,执行后 bx = 1004H。

    附图:

    屏幕快照 2018-08-14 16.57.19

  6. 接下来,第10条指令:mov [bx], ax

    指令执行前:ds = 2000H,bx = 1004H,则 mov [bx], ax 将把 ax 中的数据送入内存 2000:1004 处。

    指令执行后,2000:1004 单元的内容为 BE,2000:1005 单元的内容为 00。

    屏幕快照 2018-08-14 17.02.21

  7. 接下来,第11条指令:inc bx。这条指令执行前 bx = 1004H,执行后 bx = 1005H。

    附图:

    屏幕快照 2018-08-14 17.06.11

  8. 接下来,第12条指令:mov [bx], al

    指令执行前:ds = 2000H,bx = 1005H,则 mov [bx], al 将把 al 中的数据送入内存 2000:1005 处。指令执行后,2000:1005 单元的内容为 BE。

    附图:

    屏幕快照 2018-08-14 17.09.46

  9. 接下来,第13条指令:inc bx。这条指令执行前 bx = 1005H,执行后 bx = 1006H。

    附图:

    屏幕快照 2018-08-14 17.12.48

  10. 接下来,第14条指令:mov [bx], al

指令执行前:ds = 2000H,bx = 1006H,则 mov [bx], al 将把 al 中的数据送入内存 2000:1006 处。指令执行后,2000:1006 单元的内容为 BE。

附图:

屏幕快照 2018-08-14 17.16.24

Loop 指令

  • 格式:loop 标号
  • CPU执行loop指令的时候,要进行两步操作:
    1. (cx) = (cx) - 1;
    2. 判断 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 这个程序,单步调试来看一看。以上过程如下图:

屏幕快照 2018-08-15 10.51.50

首先我们把ax寄存器的值赋值为2,cx原本默认是存储这个程序的大小,在这里表面程序的大小为000FH,即15个字节。

附图:

屏幕快照 2018-08-15 10.56.03

然后指令 mov cx,000b 将 11 赋值给 cx 寄存器。

附图:

屏幕快照 2018-08-15 11.05.58

观察 cx 寄存器的值,直到 cx 减为 0。此时循环结束,ax 寄存器的值为 1000H,转换成十进制正好是 4096 = \(2^{12}\)

附图:

屏幕快照 2018-08-15 11.08.14

然后用debug的 t 命令继续执行循环后边的语句 mov ax,4c00hint 21 表示程序结束,键入debug的 p 命令结束程序;接着键入debug的q命令退出Debug。键入 exit 退出终端。

附图:

屏幕快照 2018-08-15 11.16.41

小结

从上边的课程中,我们可以总结出用 cx 和 loop 指令相配合实现循环功能的三个要点:

  1. 在 cx 中存放循环次数;
  2. loop指令中的标号所标识地址要在前面;
  3. 循环执行的程序段,要写在标号和loop指令的中间。

用 cx 和 loop 指令相配合实现循环功能的程序框架如下:

mov cx,循环次数
s:
循环执行的程序段
loop s

指令 loop s 会做三件事:

  1. (cx) = (cx) - 1;
  2. 判断 cx 中的值,不为零则转至标号(s)处执行程序;
  3. 如果为零则向下执行。

温故而知新

  • [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中。

  1. 在Debug中编程实现
  2. 汇编程序实现

两种实现的实际实施情况

在Debug中编程实现

屏幕快照 2018-08-15 15.24.21

回车

debug的 -r 命令:查看当前寄存器的值;

debug的 -t 命令:向前执行一条指令;

debug的 -d 命令:查看内存单元内容(比如查看内存从地址1000:0处开始,8个字节的内容);

debug的 -e 命令:修改内存单元内容(比如修改内存地址1000:0单元的内容);

debug的 -u 命令:将内存中的机器码翻译为汇编代码;

debug的 -a 命令:以汇编语言的形式在内存中写入一段程序。

屏幕快照 2018-08-15 15.33.11

屏幕快照 2018-08-15 15.35.22

我们现在用 -r 命令查看当前 ds 寄存器内容(即当前地址),在此处写入一段汇编代码:

接着设置 CS:IP 的值为 0b2a:0000 ,CS 的值已经是 0b2a,只要把 IP 指向 0000 即可。

附图:

屏幕快照 2018-08-15 15.45.09

注:在debug中默认的单位是十六进制;在Masm中默认的单位是十进制。

接着就可以用debug的 -t 命令单步跟踪观察刚刚输入的汇编代码对寄存器操作的情况了。

附图:

屏幕快照 2018-08-15 15.52.15

对比汇编程序实现

在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 是等价的。

附图:

屏幕快照 2018-08-15 16.03.26

屏幕快照 2018-08-15 16.06.45

屏幕快照 2018-08-15 16.10.23

注:如上边注意中提到一样。我们在 test.asm 中写的汇编命令是 mov al,[0] ,Masm 只会把 0 赋值给 al 寄存器。

mov bl,[1]
mov cl,[2]
mov dl,[3]

也是一样的道理:

屏幕快照 2018-08-15 16.17.08

若我们想把内存偏移地址 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 寄存器的情况。

附图:

屏幕快照 2018-08-15 16.38.20

屏幕快照 2018-08-15 16.45.12

注:内存单元 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 段前缀的使用

posted @ 2018-09-17 20:22  houhaibushihai  阅读(439)  评论(0编辑  收藏  举报