程序执行过程的跟踪

可以使用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命令查看各个寄存器的设置情况。

Alt text

当程序载入内存后,cx中存放的是程序的长度。1.exe中程序的机器码共有15字节,所以此时cx的值为000FH

现在考虑程序被装载到内存的什么地方?从上图中可以得到如下信息。

  • 程序加载后ds中存放着程序所在内存区的段地址,并且这个内存区的偏移地址为0。因此,程序所在的内存区的地址为ds:0
  • 这个内存区的前256字节存放的是PSPDOS通过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开始的其它命令。

Alt text

[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,如下图所示。

Alt text

查看从CS:IP处的指令,如下图所示。076A:0008loop s指令的地址,并且标号s已经被翻译成一个地址0006H,如果在执行loop 0006时,cx1不为0,那么就把IP设置为0006H,从而使CS:IP再次指向076A:0006,实现跳转。因此,loop指令的本质就是检查cx,修改IP

Alt text

loop和[bx]的联合应用

考虑这样一个问题:计算FFFF:0~FFFF:B单元中的数据的和,将结果存储在dx中。

分析:

  • 运算后的结果是否会超出dx所能存储的范围? FFFF:0~FFFF:B内存单元中的数据是字节型的数据,范围在0~25512个这样的整数相加不会超过65535
  • 能否将FFFF:0~FFFF:B中的数据直接累加到dx中? 不行,因为FFFF:0~FFFF:B中的数据是8位的,不能直接累加到16位的寄存器中。
  • 能否将FFFF:0~FFFF:B累加到dl中,并设置(dh) = 0,从而实现累加? 不能,因为dl8位寄存器,累加会造成进位丢失。

解决办法:用一个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,再次执行循环体。

参考