程序输入完毕,我们首先使用"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原理,所以这个问题只能留到后面再研究了。