《汇编语言》- 读书笔记 - 第5章- [BX]和 loop 指令
- 5.1 [BX]
- 问题 5.1
- 5.2 Loop 指令
- 任务 1
- 任务 2
- 任务 3
- 程序 5.1
- 问题 5.2
- 问题 5.2
- 5.3 在 Debug 中跟踪用 loop 指令实现的循环程序
- 5.4 Debug 和汇编编译器 masm 对指令的不同处理
- Debug
- MASM
- 5.5 loop 和[bx]的联合应用
- 程序 5.5
- 问题 5.4
- 程序 5.6
- 5.6 段前缀
- 5.7 一段安全的空间
- 5.8 段前缀的使用
- 程序 5.8
- 程序 5.9
- 实验4 [bx]和 loop 的使用
- [bx]和内存单元的描述
指令 | 段地址 | 偏移地址 | 说明 |
---|---|---|---|
mov ax, [0] | ds | 0 | 将内存 ds:0 处的字 单元中的内容,送入 ax |
mov al, [0] | ds | 0 | 将内存 ds:0 处的字节 单元中的内容,送入 ax 的低8位 |
mov ax, [bx] | ds | bx中的值 | 将内存 ds:[bx] 处的字 单元中的内容,送入 ax |
mov al, [bx] | ds | bx中的值 | 将内存 ds:[bx] 处的字节 单元中的内容,送入 ax 的低8位 |
-
loop
loop 指令表示循环。 -
定义的描述性的符号:“()”
书中约定:()
括号用来表示寄存器
或内存
中的内容。
()
中可以入:寄存器名、段寄存器名、内存单元的物理地址。
(ax)表示 ax 中的内容;
(al)表示 al 中的内容;
(20000H)表示内存 20000H 单元的内容(()
中的内存单元的地址为物理地址);
((ds)*16+(bx))表示:内层()
从ds和bx取出值,算出结果后当用一个物理地址,外层()
从内存地址取出内容。
ax
中的内容为 0010H,可以这样来描述: (ax)=0010H;2000:1000
处的内容为 0010H,可以这样来描述: (21000H)=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
的功能,可以这样来描述:
(sp)=(sp)-2
((ss)*16+(sp))=(ax)
- 对于 pop ax 的功能,可以这样来描述:
(ax)=((ss)*16+(sp))
(sp)=(sp)+2
- 约定符号 idata 表示常量
书中约定: 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
程序和内存中的情况如图 5.1 所示,写出程序执行后,21000H~21007H 单元中的内容。
5.2 Loop 指令
通常使用 loop 实现循环功能, cx 中存放循环次数。指令的格式是: loop 标号
。
CPU 执行 loop 指令的时候,分两步:
(cx)=(cx) - 1
;- 判断 cx != 0 则:转至
标号
处执行,否则:向下继续执行。
任务 1
编程计算 2^2,结果存在 ax 中。
任务 2
编程计算 2^3
mov ax, 2
add ax, ax
add ax, ax
任务 3
编程计算 2^12
mov ax, 2
add ax, ax ; 重复 11 次
用于引出 loop 循环
程序 5.1
assume cs:abc
abc segment
mov ax, 2
mov cx, 11 ; 循环 11 次
s: add ax, ax
loop s ; cx-1 后如果不为 0,转到 s 处执行
mov ax, 4c00H
int 21H
abc ends
end
初始为 2 再 add ax, ax
11 次实现 2^12
问题 5.2
编程,用加法计算 123 * 236,结果存在 ax 中。
分析:可用循环完成,123 * 236 等于 123相加236次。
assume cs:abc
abc segment
mov ax, 123
mov cx, 236
s: add ax, 123
loop s
mov ax, 4c00H
int 21H
abc ends
end
问题 5.2
改进程序 5.2,提高 123 * 236 的计算速度。
分析:123 * 236 等于 123相加236次。也可以是 236相加123次。
assume cs:abc
abc segment
mov ax, 236
mov cx, 123
s: add ax, 236
loop s
mov ax, 4c00H
int 21H
abc ends
end
5.3 在 Debug 中跟踪用 loop 指令实现的循环程序
debug 中 t、p、g 的用法,不再赘述,详见:《汇编语言》- 读书笔记 - 实验1 查看 CPU 和内存,用机器指令和汇编指令编程
5.4 Debug 和汇编编译器 masm 对指令的不同处理
Debug
指令 | 说明 |
---|---|
mov ax, [0] | 将内存中 ds:0 的内容送进 ax |
MASM
指令 | 说明 |
---|---|
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] |
总结:在masm中如果偏移以常量表示,则要显示指定段地址。
5.5 loop 和[bx]的联合应用
计算 ffff:0~ffff:B
单元中的数据的和,结果存储在 dx
中。
分析:
dx
放不放的下?
答:0到b最多12个FF = 11EE,dx 有两字节最大存 FFFF,因此空间没问题。ffff:0~ffff:B
的数能否直接在dx
中累加?
答:不行。因为ffff:0~ffff:B
中是字节。dx
是字(占两个字节。)因为需要另一个寄存器做临时中转。- 能否直接用
dl
累加?
当然也不行。因为累加结果超过FF
,只用dl
会溢出。
用临时工ax
。比如 (al) = (ffff*16+0), (ah) = 0。把字节
放进字
,值不变。
程序 5.5
assume cs:code
code segment
mov ax, 0ffffh ;数值不能以字母开头,故前面加 0
mov ds, ax ;设置(ds)=ffffh
mov dx, 0 ;初始化累加寄存器,(dx)=0
mov al, ds:[0] ;从内存取值,给临时工 ax。
mov ah, 0 ;清空高位
add dx, ax ;执行累加
; 累计操作共重复12次。
mov ax, 4c00h
int 21h
code ends
end
问题 5.4
程序5.5的代码有问题,重复多次,应该使用 loop 实现循环。
程序 5.6
assume cs:code
code segment
mov ax, 0ffffh ;数值不能以字母开头,故前面加 0
mov ds, ax ;设置(ds)=ffffh
mov bx, 0 ;bx中存偏移地址,初始为 0
mov dx, 0 ;初始化累加寄存器,(dx)=0
mov cx, 12 ;设置循环次数
s: mov al, [bx] ;从内存取值,给临时工 ax
mov ah, 0 ;清空高位
add dx, ax ;执行累加
inc bx ;bx+1让偏移地址指向下一个字节
loop s ;检测循环条件,符合就循环,否则向下继续
mov ax, 4c00h
int 21h
code ends
end
5.6 段前缀
mov ax, ds:[bx]
mov ax, cs:[bx]
mov ax, ss:[bx]
mov ax, es:[bx]
mov ax, ss:[0]
mov ax, cs:[0]
这些出现在访问内存单元的指令中,用于显式地指明内存单元的段地址
的 ds:、cs:、ss、es 在汇编语言中称为段前缀
。
5.7 一段安全的空间
在一般的 PC 机中,DOS 方式下,DOS 和其他合法的程序一般都不会使用 0:200~0:2ff
(00200h~002ffh)这 256 个字节的空间。
为了谨慎起见,在进入 DOS 后,我们可以先用Debug 查看一下,如果 0:200~0:2ff
单元的内容都是 0
的话,则证明确实没有人使用这里。
5.8 段前缀的使用
程序 5.8
书先写了一个不合理的版本,在循环中更改段前缀。引出后话。
程序 5.9
这次用两个段寄存器,分别破防源与目标。省去了循环中捣腾段寄存器的烦恼。
assume cs:code
code segment
mov ax, 0ffffh
mov ds, ax ; 设置源段前缀
mov ax, 0020h
mov es, ax ; 设置目标源段前缀
mov bx, 0 ; 偏移量从0开始(源和目标皆从0开始,正好共用)
mov cx, 12 ; 循环12次
s:mov dl, ds:[bx] ; 从源内存取值,送入dl
mov es:[bx], dl ; 从 dl 取值送入目标内存
inc bx ; 偏移地址向前移动
loop s ; 判断cx不为0就循环,否则继续向下。
mov ax, 4c00h
int 21h
code ends
end
实验4 [bx]和 loop 的使用
《汇编语言》- 读书笔记 - 实验4 [bx]和 loop 的使用