返回首页

汇编音乐程序的原理

125 2024-04-13 13:13 admin

早期的PC系列机中有一个专门用于定时的集成电路,型号是8253/8254。它有三个通道,第一个通道用于控制系统时钟正常运转;第二个通道用于存储器刷新;这两个通道与我们现在讨论的问题无关。第三个通道是最有意思的,它通过一组电路与喇叭相联。 图4-1所示即为PC机中完整的发声电路。定时器通道3的G端与61H端口的bit0位相联,如果将61H端口的bit0位置成1,那么定时器通道3就被启动,此时将有一组信号从OUT端输出,信号的频率可以用程序控制;若61H端口bit0位为0,则定时器被关闭,OUT端就会恒定为1 此电路用在这里相当一个可控开关,如果将61H端口的bit0、bit1位都置成1,则相当于既打开了定时器又打开了开关,这时候定时器产生的声音信号就会送到放大器推动喇叭发声;若将bit0位置0,则定时器关闭,此时OUT端为1,这时候如果连续改变bit1位的状态,也可以从喇叭中听到声音,这就是我们在第二章中所用的方法;若将bit1位置0,则开关关闭,此时即使打开定时器也不能听到声音。 这一点可以通过DEBUG加以验证:进入DEBUG,在-后打入O61 3,即可听到喇叭发出连续的叫声。(在纯DOS下实验) 向61H端口输出03,相当于打开定时器和开关,此时将有连续的声音发出,这个声音的频率约是896Hz,和我们刚开机时听到的蜂鸣音频率一样。 有趣的是声音一旦发出就不会停止,而且不干扰用户的任何操作。 停止这种声音的唯一方法就是进入DEBUG,打入命令O61 0(也可以是1或2)。之所以有这种现象是因为定时器的工作并不需CPU直接参与,CPU只要给定时设定好工作状态和频率值并打开定时器,此时定时器就会自主工作,CPU即可去做别的事情。这个特性十分有用,它是实现背景音乐的前提。 那么如何改变声音的频率呢?请注意定时器的通道3还有一个输入端CLK,这一端输入了一个固定的信号,频率是1193181.6Hz。输出信号与此信号具有如下关系: -------------------------------------------------------------------------------- F(OUT)=F(CLK)/N -------------------------------------------------------------------------------- 其中N是一个16bit数据,它的值可以由程序设定。方法很简单:将此16位数据分成高、低两个8位,先把低8位送至42H端口,紧接着再把高8位送至42H端口,输出信号的频率就会改变。我们可以试一下: C:\ASM\>DEBUG[Enter] -O61 3[Enter] -O42 0[Enter] -O42 3[Enter] 设定新的N值是300H,对应的F(OUT)是1193191.6/300H=1553Hz。声音马上变尖了。 有一点必须说明,定时器具有多种工作状态,并非每种工作状态都能产生声音,所以当我们想通过定时器产生声音时,我们应首先初始化定时器,为其建立正确的工作状态。初始化定时器并不复杂,向端口43H输出数据0B6H即可。这个数据的二进制形式是10110110,有些书籍把这个数称为幻数(MAGIC BYTE)。 有了上面介绍的这些知识,我们就可以编程控制定时器发出给定频率的声音。程序PROG6可以使喇叭发出1000Hz的声音 ------------------------------------------------ 0A3E:0100 MOV AL,B6 ;AL寄存器装入定时器初始化设置码 0A3E:0102 OUT 43,AL ;将设置码输出到43H端口 初始化 0A3E:0104 MOV AX,04A9 ;1193181.6Hz/1000=1193hz =04A9 hexadecimal AX寄存器置入N值 0A3E:0107 OUT 42,AL ;将N值分两次输出到42H端口 因为是8位 0A3E:0109 MOV AL,AH 0A3E:010B OUT 42,AL 0A3E:010D IN AL,61 ;取得61H端口的当前状态 0A3E:010F PUSH AX ;入栈 0A3E:0110 OR AL,03 ;0111 0A3E:0112 OUT 61,AL ;打开定时器及电子开关 0A3E:0114 MOV AH,01 ;AH = 01h Return: AL = character read 等待输入 ;character is echoed to standard output(回显) 0A3E:0116 INT 21 0A3E:0118 POP AX ;恢复61H 0A3E:0119 OUT 61,AL 0A3E:011B RET 0A3E:011C 我们已经讨论了如何通过定时器的通道3发出确定频率的声音,这一节我们要一起学习怎样精确地定时,这样才能解决演奏音乐的问题。 PC中的定时电路有三个通道,通道3用于发声,通道1用于控制系统内部的时钟。大家都十分清楚用DOS的TIME命令可以观察并修改系统内部的一个时钟,这个时钟之所以能连续运转主要依靠定时器的通道1。 通道1的工作方式和通道3一样,但是系统启动时设定其发出一个频率固定为18.2Hz的信号,这个信号直接送到系统中的中断控制器。每一个Hz都产生一个硬件中断,一般称这个硬中断为IRQ0,对应的中断号是08H。也就是说,当计算机启动后,我们的机器看上去十分平静,但实际上CPU非常忙碌。在定时器的控制下每隔55毫秒就要执行一个08H号中断,这个中断的主要工作就是连续地计数。 在内存0040H:006CH处有四字节的存储空间专门用于保存计数值,CPU每执行一次08H中断,这四字节的计数值就被加1,不难算出这个计数值每增加1091后时间恰好过了1分钟,每增加65454后时间恰好过了1小时。系统内部的时钟之所以能准确走时,靠得就是08H中断和这四字节的计数值。因此我们要想精确的定时,必须依靠时钟计数值才行 ;---一个能准确发出1000Hz声音的程序,声音持续时间为5秒钟--------------- PORTB equ 61H code segment assume cs:code,ds:code org 100h main proc near mov al,10110110b ;初始化定时器 out 43h,al mov ax,4a9h ;设置N值为04A9H out 42h,al mov al,ah out 42h,al in al,PORT_B ;打开定时器及与门 or al,3 out PORT_B,al ;------------------以下为定时部分--------------- mov ah,0 ;选择1AH中断的0号功能 int 1ah ;调用1AH中断取得当前时钟计数 add dx,91 ;在当前时钟计数上加91---5秒 mov bx,dx ;保存定时终了时的计数值 delay: int 1ah ;两次调用1AH中断取得时钟计数值 cmp dx,bx ;到达定时终了时的计数值了吗? jne delay ; 没有到达,则返回DELAY处继续 ;---------------------------------------------------- in al,PORT_B ;定时终止,关闭定时器及与门 and al,0fch ;1111 1100 out PORT_B,al int 20h ;结束程序 main endp code ends

顶一下
(0)
0%
踩一下
(0)
0%
相关评论
我要评论
用户名: 验证码:点击我更换图片