05.[BX]和loop指令
[BX]和loop指令
前言
[bx]和内存单元的概述
[bx] 是什么呢?和 [0] 有点类似,[0] 表示内存单元,段地址在 DS 中,偏移地址为 0
要完整的描述一个内存单元,需要两种信息:内存单元的地址;内存单元的长度
[bx]其实就是将偏移地址先存入bx寄存器中,就表示一个段地址在 DS 中,偏移地址在 bx 中内存单元
[bx]
mov ax,[bx]
功能:bx 中存放的数据作为一个偏移地址 EA,段地址 SA 默认在 ds 中,将 EA:SA 中的数据送入ax中
mov [bx],ax
功能:bx 中存放的数据作为一个偏移地址 EA,段地址 SA 默认在 ds 中,将 ax 中的数据送入EA:SA中
从程序来分析[bx]的使用, inc bx 指令的含义是 bx 的内容加1
assume codeseg
codeseg cs:segment
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
mov ax, 4c00H
int 21H
codeseg ends
end
内存变化图如下:

Loop指令
loop 指令的格式是:loop 标号
CPU 执行loop指令的时候,要进行两步操作:
- cx = cx -1
- 判断 cx 中的值,不为0则转至标号处执行程序,如果为0则向下执行
从上述描述可以看出,cx 中的值影响着 loop 指令的执行结果。通常我们用 loop 指令实现循环功能,cx 中存放循环次数
我们从具体程序进行分析:
- 编程计算 2^2, 结果存放在ax中
- 编程计算 2^3
- 编程计算 2^12
assume cs:codeseg
codeseg segment
mov ax, 2
mov cx, 11
s: add ax, ax
loop s
mov ax, 4c00H
int 21H
codeseg ends
end
从上面的代码中,我们可以总结出用 cx 和 loop 指令相配合实现循环功能的三个要点:
- 在cx中存放循环个数
- loop 指令中的标号所表示地址要在前面
- 要循环执行的字段,要写在标号和loop指令指令
用 cx 和 loop 指令相配合实现循环功能的程序框架如下:
mov cx,循环次数
标号:
循环执行的字段
loop 标号
在Debug中跟踪用 loop 指令实现的循环程序
计算 ffff:0006 单元中的数*3,结果存储在dx中
assume cs:codeseg
codeseg segment
mov ax, 0ffffH
mov ds, ax
mov bx, 6
mov ax, [bx]
mov ah, 0
mov dx, 0
mov cx, 3
s: add dx, ax
loop s
mov ax, 4c00H
int 21H
codeseg ends
end
代码可以以其他的形式编写,毕竟实现一个功能可以有很多种方式
代码中有几个关键点:
- 当我们获取一个字节数据时,可以先获取一个字的数据,然后将高位置0
- 汇编源程序中,数据不能以字母开头,例如 a445H 要写成 0a445h
- 存储寄存器一定要先初始化
当我们的循环次数变多时,我们不可能一步一步单步调试
- 可以使用 p 命令一次将循环执行完
- 也可以使用 g 命令跳到loop的下一条指令的地址 g + 偏移地址
Debug 和 汇编编译器 masm 对指令的不同处理
我们在 Debug 中写过类似的命令:mov ax,[0]
但是我们在汇编源程序中,指令 “mov ax,[0]” 被编译器当作指令 “mov ax,0” 处理
如果我们想在汇编源程序中使用 “mov ax,[0]” 类似的代码,有两种方式:
- 先将偏移地址存入bx中,再使用 mov ax,[bx]
- 显示的使用 mov ax,ds:[0] ,再前面加上段寄存器名
loop 和 [bx] 的联合应用
考虑这样一个问题,计算 ffff:0 ~ ffff:b 单元中的数据和,结果储存再dx中
assume cs:codeseg
codeseg segment
mov ax, 0ffffH
mov ds, ax
mov bx, 0
mov dx, 0
mov cx, 12
s: mov ax, [bx]
mov ah, 0
add bx, ax
inc bx
loop s
mov ax, 4c00H
int 21H
codeseg ends
end
注意几个点:
- 数据不能以字母开头
- 字节型数据存入16位寄存器时,可以存字型数据,然后将高位置0
段前缀
指令 “mov ax,[bx]” 中,内存单元的偏移地址由 bx 给出,而段地址默认再 ds 中,我们可以再访问内存单元的指令中显示地给出内存单元所在的寄存器。
- mov ax, ds:[bx]
- mov ax, cs:[bx]
- mov ax, ss:[bx]
- mov ax, es:[bx]
- mov ax, ss:[0]
- mov ax, cs:[0]
一段安全的空间
在 8086 模式下,随意的向一段内存空间写入内容是十分危险的,因为这段内存中可能存放着重要的数据或代码
那我们如何获取一份安全的内存空间呢?
不要忘记,我们是在操作系统的环境下工作的,操作系统管理所有的资源,也包括内存。如果我们需要向内存空间中写入数据时,要使用操作系统给我们分配的空间,而不应直接用地址任意指向内存单元,向里面写入
但是,我们同样不能忘记,我们正在学习是汇编语言,要通过它来获得底层的编程体验,理解计算机的底层原理。所以我们尽量直接对硬件编程,而不去理会操作系统
我们似乎面临一种选择,是在操作系统中安全,规矩的编程,还是自由,直接地用汇编语言去操作真实的硬件,理解那些早已被层层系统掩盖的真相?在大部分情况下,我们选择后者,除非我们在学习操作系统本身
注意,我们在纯 DOS 方式(实模式)下,可以不理会 DOS,直接用汇编语言去操作真实的硬件,因为运行在 CPU 实模式下的 DOS,没有能力对硬件系统进行全面的,严格的管控。但在 windows ,unix 这些运行在 CPU 保护模式下的操作系统,不理会操作系统,用汇编语言去操作真实的硬件,这是不可能的。
在一般的 PC 机上,DOS 模式下,DOS 和 其他合法程序一般不会使用 0:200 ~ 0:2ff 的256个字节的空间。为了谨慎起见,可以先使用 Debug 查看一下,是否全为 0
段前缀的使用
将内存 ffff:0 ~ ffff:b 单元中的数据复制到 0:200 ~ 0:20b 单元中
assume cs:codeseg
codeseg segment
mov ax, 0ffffH
mov ds, ax
mov ax, 0020H
mov es, ax
mov bx, 0
mov cx, 12
s: mov dl, [bx]
mov es:[bx], dl
inc bx
loop s
mov ax, 4c00H
int 21H
codeseg ends
end
有几个注意点:
- 不能使用 mov es:[bx], [bx] 这样的指令,这样的指令我们根本不知道有多大
- 一定要记得初始化
参考
<<汇编语言第四版>>-王爽
浙公网安备 33010602011771号