用机器指令和汇编指令编程(三)
程序执行过程的跟踪
可以使用Debug
来跟踪一个程序的执行过程,示例程序1.asm
如下所示。
assume cs:codesg
codesg segment
mov ax,0123h
mov bx,0456h
add ax,bx
add ax,ax
mov ax,4c00h
int 21h
codesg ends
end
使用masm
编译1.asm
,生成1.obj
,然后将1.obj
使用连接器Link.exe
连接,生成1.exe
。输入debug 1.exe
,将程序载入内存,并使用R
命令查看各个寄存器的设置情况。
当程序载入内存后,cx
中存放的是程序的长度。1.exe
中程序的机器码共有15
字节,所以此时cx
的值为000FH
。
现在考虑程序被装载到内存的什么地方?从上图中可以得到如下信息。
- 程序加载后
ds
中存放着程序所在内存区的段地址,并且这个内存区的偏移地址为0
。因此,程序所在的内存区的地址为ds:0
。 - 这个内存区的前
256
字节存放的是PSP
,DOS
通过PSP
和程序进行通信。从256
字节处开始存放的是我们编写的程序。
所以,从ds
中可以得到PSP
的段地址SA
,且PSP
的偏移地址为0
,所以PSP
的物理地址为SA × 16 + 0
。因为PSP
占据了256(100H)
字节,所以程序的物理地址为SA × 16 + 100H = (SA + 16) × 16 + 0
。这个地址可以通过段地址和偏移地址表示:SA + 10H:0
。
从上图可知,此时ds
的值为075AH
,则PSP
的地址为075AH:0
,所以程序的地址应为076A:0
,注意此时CS:IP
的值正为076A:0
,进一步证明了CS:IP
指向的是程序执行的下一条指令(MOV AX,0123)
。
使用U
命令查看从076A:0
开始的其它命令。
[BX]和loop指令
[BX]
要完成的描述一个内存单元,需要两种信息:①内存单元的地址;②内存单元的长度。
mov ax,[0]
表示将一个内存单元的内容送入ax
,这个内存单元的长度为2
字节,偏移地址为0
,段地址存放在ds
中。mov al,[0]
表示将一个内存单元的内容送入al
,内存单元的长度为1
字节,偏移地址为0
,段地址存放在ds
中。mov ax,[bx]
表示将一个内存单元的内容送入ax
,内存单元的长度为2
字节,偏移地址存放在bx
中,段地址在ds
中。mov al,[bx]
表示将一个内存单元的内容送入al
,内存单元的长度为1
字节,偏移地址存放在bx
中,段地址在ds
中。
loop
loop
指令的格式是:loop 标号
,CPU
执行loop
指令的时候,执行两步操作,①(cx) = (cx) - 1
;②判断cx
中的值,不为0
则转至标号处指向,如果为0
则向下执行。注意到cx
中存放着循环次数,影响着loop
执行的执行结果。
使用loop
实现一个具体的任务:实现2^12
的计算。
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
分析这段程度的运行过程:当首次执行到add ax,ax
时,ax = ax + ax
,然后开始执行loop s
,先将cx
中的值减1
,然后判断cx
中的值是否为0
,若不为0
,则跳转到s
处执行add ax,ax
,如下图所示。
查看从CS:IP
处的指令,如下图所示。076A:0008
为loop s
指令的地址,并且标号s
已经被翻译成一个地址0006H
,如果在执行loop 0006
时,cx
减1
不为0
,那么就把IP
设置为0006H
,从而使CS:IP
再次指向076A:0006
,实现跳转。因此,loop
指令的本质就是检查cx
,修改IP
。
loop和[bx]的联合应用
考虑这样一个问题:计算FFFF:0~FFFF:B
单元中的数据的和,将结果存储在dx
中。
分析:
- 运算后的结果是否会超出
dx
所能存储的范围?FFFF:0~FFFF:B
内存单元中的数据是字节型的数据,范围在0~255
,12
个这样的整数相加不会超过65535
。 - 能否将
FFFF:0~FFFF:B
中的数据直接累加到dx
中? 不行,因为FFFF:0~FFFF:B
中的数据是8
位的,不能直接累加到16
位的寄存器中。 - 能否将
FFFF:0~FFFF:B
累加到dl
中,并设置(dh) = 0
,从而实现累加? 不能,因为dl
是8
位寄存器,累加会造成进位丢失。
解决办法:用一个16
位的寄存器作为中介,将内存单元中的8
位数据赋值到16
位的寄存器中,然后再将该寄存器中的数据加到dx
上。
使用ax
作为中介寄存器,并通过loop
循环将FFFF:0~FFFF:B
内存单元中的值赋值到ax
中,再进行累加。
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
loop s
mov ax,4c00H
int 21h
code ends
end
注意第一条指令mov ax,0FFFFH
,在汇编源程序中,数据不能以字母开头,所以要在前面加上0
。
总结
- 程序被加载进内存后,会创建一个
256
字节的PSP
,从这段内存区的256
字节处开始,才将程序装入。ds
中存放着PSP
的段地址,且此时偏移地址为0
。我们所编写的汇编指令是从(ds) + 10H:0
处开始,即CS = (ds + 10H)
,IP = 0
。 - ` loop s
实现程序循环的本质是修改
IP的值。在执行
loop s时,需要先将
(cx)减
1,然后检查
(cx)是否为
0,若不为
0则修改
IP`,再次执行循环体。