我们在前面编制的程序只能发出频率一定的"纯音",但有时我们希望喇叭里能发出"噪音",即将许多种不同频率的声音无规律地混杂在一起发出,这样我们可以使喇叭产生"音响效果",如枪炮声、爆炸声等。下面的程序可以产生"白噪声"。
code segment
assume cs:code
org 100h
main proc far
mov dx,140h ;设置随机数的种子
mov bx,0fffh ;设置外循环数
in al,61h ;取61H端口当前值
and al,11111100b ;低2位置0
sound: xor al,2 ;改变Bit1位
out 61h,al ;输出至61H端口
add dx,9248h ;随机数种子加9248H
mov cl,3 ;准备右循环移位数
ror dx,cl ;随机数种子右循环移位
mov cx,dx ;所得随机数置入CX寄存器
and cx,0fffh ;保证CX中的随机数不大于0FFFH
or cx,10h ;保证CX中的随机数不小于10H
delay: loop delay ;以随机数为循环计数循环
dec bx ;外循环计数减1
jnz sound ;循环发声
and al,11111100b ;关闭定时器
out 61h,al
int 20h ;结束程序
main endp
code ends
end main
① “白噪声”这个词源自光学名词“白光”,白光是由多种光组成的,相应的,由多种频率的声音组成的噪声就称为“白噪声”。
程序中多了一条指令--ROR,这是一个移位指令,但它和前面讲的SHL不同,它可以完成循环移位:
助记符:ROR(Rotate right)
用 途:将寄存器或内存中的数据循环右移
格 式:ROR 寄存器,1
ROR 寄存器,CL
ROR 存储单元,1
ROR 存储单元,CL
执 行:寄存器或内存中的数据各个位顺次右移,移出去的数位返回到寄存器或存储单元的最左端
图4-5(a)表示了这条指令的动作情况。与这条指令功能相反的指令是:
助记符:ROL(Rotate left)
用 途:将寄存器或内存中的数据循环左移
格 式:ROL 寄存器,1
ROL 寄存器,CL
ROL 存储单元,1
ROL 存储单元,CL
执 行:寄存器或内存中的数据各个位顺次左移,移遇去的数位返回到寄存器或存储单元的最右端
图4-5(b)表示了此指令的动作情况。

WHITE程序的原理十分简单,我们在前面编制过通过61H端口的bit1位发声的程序,那些程序都是用LOOP指令控制声音频率,其循环次数是固定的。如果用一个随机数作循环计数,则产生的声音频率也是随机的,这就相当于将许多频率无规律地混合,从而产生噪音。
如何产生一个随机数?WHITE程序采用的是"种子"算法。即首先在DX寄存器中放入初值140H,这个初值称为"种子"(SEED),而后每次需要一个随机数时就将"种子"加上9248H,并循环右移三位,这样就产生了一个随机数,保留这个随机数作为新的"种子"以备后用。至于指令AND CX,0FFFH和OR CX,10H是用于限制随机数的取值范围,从而使声音频率在一定范围内变化。程序开始设定在BX中的循环计数值用于控制噪音的长短,适当减小此值可以听到一声爆炸声,下面的程序产生连续的爆炸声,也就是枪声。
GUN.ASM
code segment
assume cs:code
org 100h
gun proc far
mov cx,20 ;发出20声枪声
shot: call shoot ;调用噪声子过程
push cx ;枪声数存入堆栈
mov cx,0c000h ;设置循环次数
silent: loop silent ;空循环,产生枪声之间的间隔
pop cx ;取回枪声计数
loop shot ;发下一声枪声
int 20h ;结束程序
gun endp
shoot proc near ;噪声子程序
push cx ;保存CX寄存器
mov dx,140h ;设置随机数的种子
mov bx,0ffh ;设置每声枪响的长度
in al,61h ;取61H端口当前值
and al,11111100b ;低2位置0
sound: xor al,2 ;改变Bit1位
out 61h,al ;输出至61H端口
add dx,9248h ;随机数种子加9248H
mov cl,3 ;准备右循环移位数
ror dx,cl ;随机数种子右循环移位
mov cx,dx ;所得随机数置入CX寄存器
delay: and cx,0fffh ;保证CX中的随机数不大于0FFFH
or cx,10h ;保证CX中的随机数不小于10H
loop delay ;以随机数为循环计数循环
dec bx ;外循环计数减1
jnz sound ;循环发声
and al,11111100b ;关闭定时器
out 61h,al
pop cx ;恢复CX寄存器
ret ;子过程返回
shoot endp
code ends gun
end
注意这两个程序中的循环都是用LOOP指令完成的,因此在不同的机器上循环的计数值要用不同的值。笔者使用一台装有80MHzCyrix486DX2CPU的电脑,如果使用其它的CPU,需要修改计数值才能获得最好的效果。
在这一章中,我们详细地谈了精确控制频率和时间的方法,至此我们已经将常用的发声程序设计技术介绍完了,希望读者能很好地掌握讲过的指令、硬件知识和程序设计技巧。不过直到现在止我们还没有掌握最高境界的发声程序设计技术,有一个名为《DARKSEED》的电子游戏,这个游戏可以从喇叭里发出很清楚的语言。设计这样的程序还需要其它一些知识,我们现在就不再详细介绍了。如果有兴趣可以找一些资料自己研究。