第五章 【bx】和loop指令
5.1 [bx]
因为在debug中使用mov ax,[0]:代表的是偏移地址,而在asm文件中写的编译后却变成了数字 mov ax,0 相当与把0给了ax ,而不是 ds:0,所以出现了bx。要写成这样才能实现:mov ax,ds:[0]
- 和[0]类似,[0]表示内存单元,它的偏移地址是0; [bx]同样也表示一个内存单元,它的段地址在DS中
- 它的偏移地址在bx中,至于是取字还是取字节,要看他放入的寄存器是8位还是16位
- 补充:inc指令:相当于C语言中的++运算符
5.2 Loop指令
这个指令和循环有关
- 指令格式:loop 标号
CPU执行loop指令的时候,要进行两步操作
1.(cx)=(cx)-1;
2.判断cx中的值,若不为零,则转至标号处执行程序
若为零,则向下执行。 - 通常,loop指令实现循环,cx中存放循环的次数
- 标号:在汇编语言中,标号代表了一个地址,标号标识了一个地址
- 使用cx和loop指令相配合实现循环功能的三个要点
1.在cx中存放循环次数
2.loop指令中的标号所标识地址要在前面
3.要循环执行的程序段,要写在标号和loop指令的中间 - 用cx和loop指令相配合实现循环功能的程序框架
mov cx,循环次数
S:循环执行的程序段
loop s
assume cs:code
code segment
mov ax,2
mov cx,11
s: add ax,ax
loop s
mov ax,4c00h
int 21h
code ends
end
debug一下,可以看到cx存放的是000bh,也就是11了。loop 0006也就是上一句的ip。
一直运行到最后,cx变成0,ax变成1000h,也就是2^12了。
5.3 在Debug中跟踪供loop指令实现的循环程序
注意:在汇编程序中,数据不能以字母开头,如果要输入像FFFFH这样的数,则要在前面添加一个0,否则编译会报错。
在debug程序中引入G命令和P命令(避免循环次数过多一直t)
- G命令
G命令如果后面不带参数,则一直执行程序,直到程序结束
G命令后面如果带参数,则执行到ip为那个参数地址停止
- P命令
T命令相当于单步进入(step into)
P命令相当于单步通过(step over)
如果在循环loop那一句直接p就会一次循环完。
5.4 Debug和汇编编译器Masm对指令的不同处理
- 在debug中,可以直接用指令 mov ax,[0] 将偏移地址为0号单元的内容赋值给ax
- 但通过masm编译器,mov ax,[0] 会被编译成 mov ax,0
1.要写成这样才能实现:mov ax,ds:[0]
2.也可以写成这样:
mov bx,0
mov ax,[bx] ;或者mov ax,ds:[bx]
5.5 loop和[bx]的联合应用
- 计算ffff:0-ffff:b单元中的数据的和,结果存储在dx中
注意两个问题
1.12个8位数据加在一起,最后的结果可能会超出8位(越界),故要用16位寄存器存放结果
2.将一个8位的数据加入到16位寄存器中,类型不匹配,8位的数据不能与16位相加 - 【解决办法】
把原来8位的数据,先通过通用寄存器ax,将它们转化成16位的 - 代码如下
assume cs:codesg
codesg segment
start: mov ax,0ffffh
mov ds,ax;初始化
mov ax,0 ;相当于 ah,0 ah一直是0
mov dx,0
mov bx,0
mov cx,0ch ;指定循环次数,12次
circ: mov al,[bx] ;把8位数据存入al中,即ax中存放的是[bx]转化之后的16位数据,前8位都是0
add dx,ax ;进行累加
inc bx ;bx自增,变化内存的偏移地址
loop circ ;程序返回
mov ax,4c00h
int 21H
codesg ends
end start
5.6 段前缀
- 指令“mov ax,[bx]”中,内存单元的偏移地址由bx给出,而段地址默认在ds中
- 我们可以在访问内存单元的指令中显式地给出内存单元的段地址所在的段寄存器,比如 mov ax,ds:[0]或mov ax,ds:[bx]。这里的ds就叫做【段前缀】
5.7 一段安全的空间
-
8086模式中,随意向一段内存空间写入内容是很危险的
因为这段空间中可能存放着【重要的系统数据或代码】
-
在一般的PC机中,DOS方式下,DOS和其他合法的程序一般都不会使用【0:200h~0:2FFh】 的256个字节的空间。所以,我们使用这段空间是安全的,全是0,没有存放东西。 如果要向内存写东西,可以使用这端安全空间。
5.8 段前缀的使用
我们考虑一个问题,将内存ffff:0-ffff:b单元中的数据复制到0:200-0:20b单元中。
assume cs:code ;程序5.8
code segment
mov bx,0 ;(bx)=0,偏移地址从О开始
mov cx,12 ; (cx)=12,循环12次
s : mov ax,0ffffh
mov ds ,ax ; (ds)=0ffffh
mov dl,[bx] ; (dl)= ( (ds)* 16+(bx)),将ffff:bx中的数据送入dl
mov ax,0020h
mov ds , ax ; (ds)=0020h
mov [bx] ,dl ; ( (ds)* 16+(bx) ) = (dl),将中dl的数据送入0020:bx
inc bx ; (bx)= (bx)+1
loop s
mov ax, 4c00h
int 21h
code ends
end
因源始单元ffff:X和目标单元0020:X 相距大于64KB,在不同的64KB段里,程序5.8 中,每次循环要设置两次ds。这样做是正确的,但是效率不高。我们可以使用两个段寄存器分别存放源始单元ffff:X和目标单元0020:X的段地址,这样就可以省略循环中需要重复做12次的设置ds的程序段。
assume cs : code;程序5.9优化5.8
code segment
mov ax,0ffffh
mov ds , ax ; (ds)=0ffffh
mov ax ,0020h
mov es,ax ; (es)=0020h
mov bx,0 ; (bx)=0,此时ds:bx指向ffff:0,es:bx指向0020:0
mov cx,12 ; (cx)=12,循环12次
s : mov dl,[bx] ; (dl)= ( (ds)*16+(bx)),将ffff:bx中的数据送入dl
mov es : [bx] ,dl ; ( (es)*16+(bx) )=(dl),将dl中的数据送入0020:bx
inc bx ; (bx)=(bx)+1
loop s
mov ax,4c00h
int 21h
code ends
end
程序5.9中,使用es 存放目标空间0020:0-0020:b 的段地址,用ds 存放源始空间ffff:0-ffff:b 的段地址。在访问内存单元的指令“mov es:[bx],al”中,显式地用段前缀“es:”给出单元的段地址,这样就不必在循环中重复设置ds.