有一点必须说明,定时器具有多种工作状态,并非每种工作状态都能产生声音,所以当我们想通过定时器产生声音时,我们应首先"初始化"定时器,为其建立正确的工作状态。初始 化定时器并不复杂,向端口43H输出数据0B6H即可。这个数据的二进制形式是10110110,有些书籍把这个数称为"幻数"(MAGIC BYTE)。用这样一个数的原因笔者不作过多讨论,大家可以查阅别的参考书。
C:\ASM\>DEBUG[Enter] -O61 3[Enter] -O42 0[Enter] -O42 3[Enter]
有了上面介绍的这些知识,我们就可以编程控制定时器发出给定频率的声音。程序PROG6可以使喇叭发出1000Hz的声音,这个程序中有一个新的逻辑操作指令:
助记符:OR(or)
用 途:将两个数据作"或"逻辑操作
格 式:OR 寄存器,立即数
OR 寄存器,寄存器
OR 寄存器,存储器
OR 存储单元,寄存器
OR 存储单元,立即数
执 行:两个操作数进行"或"计算,结果保存在左边的寄存器或存储单元中
-a100[Enter] 0A3E:0100 MOV AL,B6 ;AL寄存器装入定时器初始化设置码 0A3E:0102 OUT 43,AL ;将设置码输出到43H端口 0A3E:0104 MOV AX,04A9 ;AX寄存器置入N值 0A3E:0107 OUT 42,AL ;将N值分两次输出到42H端口 0A3E:0109 MOV AL,AH 0A3E:010B OUT 42,AL 0A3E:010D IN AL,61 ;取得61H端口的当前状态 0A3E:010F PUSH AX ;在堆栈中保存61H端口的当前状态 0A3E:0110 OR AL,03 ;准备将03H输出到61H端口 0A3E:0112 OUT 61,AL ;打开定时器及电子开关 0A3E:0114 MOV AH,01 ;选择DOS API的01H功能 0A3E:0116 INT 21 ;调用21H中断等待键盘输入 0A3E:0118 POP AX ;从堆栈中取回61H端口的原状态 0A3E:0119 OUT 61,AL ;恢复61H端口的原状态 0A3E:011B RET ;结束程序运行 0A3E:011C
我们已经用DEBUG编制了一些程序,相信大家对DEBUG已有了较深的了解。DEBUG虽然小巧,但它存在很多弱点。最明显的,当我们用"JMP"指令向高地址方向转移时,我们无法知道目的地址究竟是多少,只能先假设一个地址,等到全部程序输入完后在修改转移指令的目的地址。这个问题在DEBUG中无法解决,毕竟它不是一个真正的编译系统。那么如何应用汇编语言的编译系统?这就是我们现在要讨论的问题。先来看这样一个程序——PROG7.ASM:
CODE SEGMENT ;定义代码段 ORG 100H ;设定偏移地址为0100H MAIN PROC NEAR ;定义主过程开始 MOV AL,10110110B ;AL寄存器置入定时器初始化设置码 OUT 43H,AL ;初始化定时器 MOV AX,4A9H ;设置N值为04A9H OUT 42H,AL ;输出N值 MOV AL,AH OUT 42H,AL IN AL,61H ;取得61H端口当前状态 PUSH AX ;将61H端口的当前状态存入堆栈 OR AL,3 ;将61H端口的低2位改为1 OUT 61H,AL ;打开定时器及与门 MOV AH,1 ;选择DOS API的01号功能 INT 21H ;调用21H中断等待键盘输入 POP AX ;取回61H端口的原状态 OUT 61H,AL ;关闭定时器及与门 RET MAIN ENDP ;主过程结束 CODE ENDS ;代码段结束 END MAIN ;进程结束
这个程序看上去很眼熟吧,它就是PROG6的源程序形式,不过和PROG6相比,它有点面目全非了。我们先来讨论一个简单的问题:源程序中的数。
采用DEBUG编程时所用的数都是16进制的,如果需要使用其它进制的数,程序员就必须自己将其转换为16进制。而编制汇编源程序则没有这个烦琐的问题,我们可以在源程序中使用任意进制的数,只需在数的末尾给出进制标识即可。