汇编语言(王爽)-[BX]和loop指令


[bx]和内存单元的描述

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

  1. 内存单元的地址

    [0]表示一个内存单元时,0表示单元的偏移地址,段地址默认在ds

    [bx]表示一个内存单元时,偏移地址在bx中,段地址在ds

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

    单元的长度(类型)可由具体指令中的其他操作对象(如寄存器)指出

约定描述性符号

  1. ()

    表示一个寄存器或一个内存单元中存放的内容

    ()中的元素有三种类型:

    1. 寄存器名

    2. 段寄存器名

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

    ()所表示的数据类型有两类:

    1. 字节

    2. 是哪种类型由寄存器名或具体的运算决定

  2. idata

    表示常量


[BX]

功能

  1. mov ax,[bx]

    bx中存放的数据为偏移地址EA,段地址SA默认在ds中,将SA:EA处的数据送入ax中,即:(ax)=((ds)*16+(bx))

  2. mov [bx],ax

    bx中存放的数据为偏移地址EA,段地址SA默认在ds中,将ax中的数据送入内存SA:EA处,即:((ds)*16+(bx))=(ax)


loop指令

格式:loop 标号

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

  1. (cx)=(cx)-1

  2. 判断cx中的值,不为零则转至标号处执行程序,为零则向下执行

通常,使用loop指令来实现循环功能,cx中存放循环次数

使用方法

计算2^12

1
2
3
4
5
6
7
8
9
10
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
  1. cx中存放循环次数

  2. loop指令中的标号标识要循环执行的语句,标号所标识地址要在前面

  3. 要循环执行的程序段,要写在标号和loop指令中间

cxloop指令相配合实现循环功能的程序框架:

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov bx,6 ;以上,设置ds:bx指向ffff:0006

mov al,[bx]
mov ah,0 ;以上,设置(al)=((ds*16)+(bx)),(ah)=0

mov dx,0 ;累加寄存器清0

mov cx,3 ;循环3次
s: add dx,ax
loop s ;以上累加计算(ax)*3

mov ax,4c00h
int 21h ;程序返回

code ends
end

程序中第一条指令mov ax,0ffffh在汇编源程序中,数据不能以字母开头,所以在前面加0

1.png

(ds)=0b3dh,则程序在0b3d:0处(程序内存前存在256个字节的程序段前缀)

(cs)=0b3dh,(IP)=0指向程序的第一条指令

2.png

使用u命令查看被Debug加载入内存的程序,此时,loop s已变为loop 0012h,用于实现跳转

3.png

4.png

5.png

6.png

7.png

8.png

程序执行结束

可以使用g命令跳过某些不想单步执行的代码段,g 0012将使Debug从当前的CS:IP指向的指令开始执行,一直到(IP)=0012h为止

9.png

可以使用p命令来让Debug自动重复执行循环中的指令,直到(cx)=0为止

使用g命令同样可以达到跳过循环过程的效果

10.png


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

对于类似mov ax,[0]的汇编指令

Debug中,表示将ds:0内存单元处的数据送入ax

masm中,被当作mov ax,0处理,表示将ax寄存器赋值为0

汇编源程序中以下指令的含义:

  1. mov al,[0](al)=0,将常量0送入al

  2. mov al,ds:[0](al)=((ds)*16+0),将内存单元中的数据送入al

  3. mov al,[bx](al)=((ds)*16+(bx)),将内存单元中的数据送入al

  4. mov al,ds:[bx]:与mov al,[bx]含义相同

如何实现在源程序中将内存单元中的数据送入寄存器呢?

  1. 使用bx,间接给出内存单元的偏移地址,则段地址默认存储在ds
    1
    2
    3
    4
    mov ax,2000h
    mov ds,ax
    mov bx,0
    mov al,[bx]
  1. 在汇编源程序中,若在[]中用一个常量直接给出内存单元的偏移地址,需在[]前显式的给出段地址所在的段寄存器,如:mov al,ds:[0]

loop与[bx]的联合应用

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

问题:

  1. 结果是否会超出dx范围

    一个存储单元存储一个字节数据,范围在0-255,12个字节型数据相加,结果不大于65535,不会超出存储范围

  2. 能否将数据直接累加到dx

    不行,内存单元数据为8位,不可直接累加到16位寄存器中

  3. 能否将内存单元中数据累加到dl中,设置(dh)=0,实现累加

    不行,会出现进位丢失

可使用一个16位寄存器做中介,将内存单元中的数据送入16位寄存器ax中,再将ax中数据加到dx中,实现运算对象类型匹配且结果不会超界

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
assume cs:code
code segment

mov ax,0ffffh
mov ds,ax

mov dx,0

mov al,ds:[0]
mov ah,0
add dx,ax

mov al,ds:[1]
mov ah,0
add dx,ax

mov al,ds:[2]
mov ah,0
add dx,ax

mov al,ds:[3]
mov ah,0
add dx,ax

mov al,ds:[4]
mov ah,0
add dx,ax

mov al,ds:[5]
mov ah,0
add dx,ax

mov al,ds:[6]
mov ah,0
add dx,ax

mov al,ds:[7]
mov ah,0
add dx,ax

mov al,ds:[8]
mov ah,0
add dx,ax

mov al,ds:[9]
mov ah,0
add dx,ax

mov al,ds:[0ah]
mov ah,0
add dx,ax

mov al,ds:[0bh]
mov ah,0
add dx,ax

mov ax,4c00h
int 21h

code ends
end

使用loop指令,使程序更简洁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
assume cs:code
code segment

mov ax,0ffffh
mov ds,ax
mov bx,0

mov dx,0

mov cx,12

s: mov al,[bx]
mov ah,0
add dx,ax
inc bx ;bx中的值自加1,使ds:bx指向下一个单元
loop s

mov ax,4c00h
int 21h

code ends
end

段前缀

如:mov ax,ds:[bx],出现在访问内存单元的指令中,用于显式指明内存单元的段地址的ds等,在汇编语言中称为段前缀


一段安全的空间

在8086模式中,随意向一段内存空间中写入内容是非常危险的,因为这段空间中可能存放着重要的系统数据或代码

在纯DOS方式(实模式)下,可直接使用汇编语言去操作真实的硬件,因为在CPU实模式下的DOS,没有能力对硬件系统进行全面、严格的管理

在运行于CPU保护模式下的操作系统中,无法用汇编语言去操作真实的硬件,硬件已被操作系统利用CPU保护模式所提供的功能全面而严格的管理了

总结

  1. 我们需要直接向一段内存中写入内容

  2. 这段内存空间不应该存放系统或其他程序的数据或代码,否则写入操作可能引发错误

  3. DOS方式下,一般情况,0:200-0:2ff空间中没有系统或其他程序的数据或代码

  4. 在使用内存空间时,可使用Debug查看内存空间是否被使用


段前缀的使用

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

0:200-0:20b单元等同于0020:0-0020:b单元,它们描述的是同一段内存空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
assume cs:code
code segment

mov bx,0 ;偏移地址从0开始
mov cx,12 ;循环次数12
s: mov ax,0ffffh ;循环开始,设置ax
mov ds,ax ;设置源段地址
mov dl,[bx] ;将源内存单元数据赋值到dl中

mov ax,0020h ;重新设置ax
mov ds,ax ;设置目的段地址
mov [bx],dl ;将dl中的数据赋值到目的内存单元中

inc bx ;bx自加1,用于下一单元数据复制
loop s

mov ax,4c00h ;程序返回
int 21h

code ends
end

程序中使用dl寄存器作为中介进行数据复制

每次循环需要设置两次ds,用于切换源段地址与目的段地址,源内存单元与目的内存单元相距大于64KB,效率不高,可以设置两个段寄存器,分别用于存放源段地址与目的段地址,即可省略每次循环需设置两次ds的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
assume cs:code
code segment

mov ax,0ffffh ;设置ax
mov ds,ax ;设置源段地址

mov ax,0020h ;设置ax
mov es,ax ;设置目的段地址

mov bx,0 ;设置逻辑地址bx

mov cx,12 ;设置循环次数

s: mov dl,[bx] ;将源内存单元数据赋值给dl,默认段地址在ds中
mov es:[bx],dl ;将dl中值赋值给目的内存单元
inc bx ;bx自加1,用于下一内存单元数据复制(源段地址与目的段地址共用一个逻辑地址)
loop s

mov ax,4c00h ;程序返回
int 21h

code ends
end

---------------The End---------------
0%