ITEEDU

 程序输入完毕,我们首先使用"R"命令将所有寄存器的当前值列出,重点观察一下SP寄存器与AX寄存器的值。

-r AX寄存器的初值 SP寄存器的指向
AX=0000 BX=0000 CX=000A DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0B01 ES=0B01 SS=0B01 CS=0B01 IP=0100 NV UP EI PL NZ NA PO NC
0B01:0100 B83412 MOV AX,1234 第一条待执行的指令

现在我们输入一个"T"命令,CPU就会将第一条指令"MOV AX,1234"执行,执行之后DEBUG会将执行结果显示出来。

-t 注意此寄存器的变化 指令指针的增加
AX=1234 BX=0000 CX=000A DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0B01 ES=0B01 SS=0B01 CS=0B01 IP=0103 NV UP EI PL NZ NA PO NC
0B01:0103 BBCDAB MOV BX,ABCD第二条待执行的指令

以上显示的内容就是执行了指令"MOV AX,1234"之后的各寄存器的状态,可以看到AX寄存器中确实存入了1234。另外一点,请注意IP寄存器的变化情况。
再次输入"T"命令,则指令"MOV BX,ABCD"也会被执行。

-tBX 寄存器存入ABCD 指令指针继续增长
AX=1234 BX=ABCD CX=000A DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0B01 ES=0B01 SS=0B01 CS=0B01 IP=0106 NV UP EI PL NZ NA PO NC
0B01:0106  50 PUSH AX需要重点观察的指令

"PUSH"指令的执行情况是我们要点要观察的,单步执行此指令之后请注意SP寄存器的变化。

-t SP寄存器减量
AX=1234 BX=ABCD CX=000A DX=0000 SP=FFEC BP=0000 SI=0000 DI=0000
DS=0B01 ES=0B01 SS=0B01 CS=0B01 IP=0107 NV UP EI PL NZ NA PO NC
0B01:010753PUSH BX

这是第一次执行PUSH指令,可以观察到SP寄存器由原来的FFEE变成了FFEC,恰好是减了2。

-t SP寄存器继续减量
AX=1234 BX=ABCD CX=000A DX=0000 SP=FFEA BP=0000 SI=0000 DI=0000
DS=0B01 ES=0B01 SS=0B01 CS=0B01 IP=0108 NV UP EI PL NZ NA PO NC
0B01:0108 58 POP AX      POP指令执行与PUSH恰好相反

执行了"PUSH BX"之后SP寄存器继续减2。

-t注意AX寄存器的改变执行POP指令后SP寄存器增量
AX=ABCD BX=ABCD CX=000A DX=0000 SP=FFEC BP=0000 SI=0000 DI=0000
DS=0B01 ES=0B01 SS=0B01 CS=0B01 IP=0109 NV UP EI PL NZ NA PO NC
0B01:0109 5B POP BX

现在指令"POP AX"执行完华,注意SP寄存器的变化,它加了2。另外看一看AX寄存器与BX的关系,究竟那个数从堆栈中跳出了呢?

-t   AX、BX寄存器内容交换   SP寄存器复员
AX=ABCD BX=1234 CX=000A DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0B01 ES=0B01 SS=0B01 CS=0B01 IP=010A NV UP EI PL NZ NA PO NC
0B01:010A 0883745B OR [BP+DI+5B74],AL

最后一条指令"POP BX"执行完了,我们可以很清楚地看到SP寄存器的变化情况,同时也会发现,AX寄存器和BX寄存器中的数据换了位置,这就是堆栈中数据"后进先出"的结果。

应用PUSH和POP指令时必须记住使用16位数据,PUSH AH、POP DL这样的用法都是不对的。

新增了两条指令,"救活"了一个循环,现在这个程序应该可以结束了吧?不过现在的回答仍然是否定的。

很多高级语言都不要求程序结尾必须有表示结束的语句,然而汇编语言可不行。除非你输入了要求返回DOS的指令,否则CPU是不会自行结束执行程序的。因此我们需要再学一条指令:

助记符:RET(Return)
用 途:从子程序返回到主程序
格 式:RET
执 行:结束子程序的执行,返回主程序

PROG2 
0B1D:0100 MOV CX,0800
0B1D:0103 PUSH CX
0B1D:0104 IN AL,61
0B1D:0106 XOR AL,02
0B1D:0108 OUT 61,AL
0B1D:010A MOV CX,0500
0B1D:010D LOOP 010D
0B1D:010F POP CX
0B1D:0110 LOOP 0103
0B1D:0112 RET

关于这条指令的使用还有很多更深入的内容,但凭我们现在这点功底还不宜讲得过深,现在只需记住这条指令能够让我们的程序正常的结束就行了。PROG2就是程序最终改成的样子。

运行PROG2,喇叭里会发出一声鸣叫,然后屏幕上会重新出现DEBUG的提示符。改变外循环的计数值,就可以任意改变声音的长短。

-g=100 
Program terminated   normally(程序正常结束)

这个程序看来已经接近完美了,然而新的问题又出现了:想要听到0.5秒钟的声音,外循环的计数值应是多少?

非常遗憾,用LOOP指令精确控制时间十分困难,这和我们前面所讨论的控制频率的问题一样。而要想解决这两个问题,我们还需了解更多的PC原理,所以这个问题只能留到后面再研究了。