您的位置:首页 > 移动开发 > IOS开发

戏说BIOS之Beep

2009-06-18 15:43 225 查看
戏说BIOSBeep

1. Introduction

大凡用过电脑的朋友都应该听到过BIOS的报警声,有时 PC开机的时候就会听到嘀的一声,有过修理PC经验的话就更清楚了“一短内存刷新失败,二短内存校验错误,一长三短内存错误,一长八短显示错误”等等诸如此类,可能各家的BIOS定的规则不同,但目的都是通过报警音获悉系统运行状况,找出病灶对症下药(有点像中医诊断中“望闻问切”中的闻:))。

2. 8253/8254

Beep声我们都听过,但是有没有想过这是怎么实现的呢?带着疑惑随我开始探索之旅。提到这个Beep,它可算是历史悠久了,追溯到IBM的第一台PC, 那时工程师们可能觉得pc功能太过单调枯燥,于是他们就祈求上帝给我点声音吧,于是声音就有了J。他们将一个简单的扬声器加入了最初的pc硬件之中。光有speaker肯定是不足以产生音乐的,因为音乐得有音调和节奏才能组成。虽然我不懂音乐,但是我知道一点就是声音的高低和频率有关,所以还要有能制造频率的东东这就是8253/8254。既然提到我就大概的讲一讲8253/8254,8253/8254是可编程的定时器,8254是8253的增强版本差异主要在可以外接clock频率不同,其实使用上无差了。8253有三个独立工作16位的计数器t0、t1、t2分别使用40h、41h、42h port去操纵,除此之外还有一个43h port用于设定控制字。三个计数器分别编程,但是在使用之前必须先配置控制字,控制字主要用于选定哪一个计数器,选择计数器的工作模式等。控制字的格式如下表1所示:

Bit 0
计数值格式 0表示binary;1表bcd
Bit1~3
模式选择
Bit4~5
读写指示
Bit6~7
选择计数器
表 1

其实这三个计数器在PC内部已经规划好了功能,基本上不需要用户参与了J
t0:用于系统时钟提供定时基准,它的输出端与中断控制器的IRQ0相连。
t1:用于DRAM更新的信号,每隔15.2us刷新一次。
t2:用于控制扬声器发声,作为speaker的音频频率。
所以我们知道t2用于提供speaker的音频频率,驱动speaker发生。这个部分早期驱动电路如下图1所示:



由上图1我们可以看到61h PB0控制T2的gate2,也就说只有将PB0 pull high T2才能工作。另外PB1与T2的输出端OUT2经过一个与门运算然后再驱动speaker,所以PB1也要pull high这样T2的输出就可以操纵speaker的频率了。图中的61h是没有介绍过的,那就再来聊聊61h port。61h在XT系统中集成在8255之中,8255是一颗可编程的外围接口芯片,61h对应8255的port B,它是一个8 bit的 IO port,每一个bit代表的意义如下表2所示:

Bit0
t2 gate2 控制位
Bit1
Speaker 控制位
Bit2
DIP相关
Bit3
录音马达
Bit4
RAM同步更新检查位
Bit5
I/0通道检查
Bit6
Keyboard 电平控制
Bit7
Keyboard 始能控制
表 2
AT以后8255已不再使用,port61h也使用别的IC代替了但是它的主要的bit功能还是保留了下来,所以仍然可以使用I/O指令读写61h port。

3. Beep~~~~~~

知道了以上的知识,我们就来写一个“一长三短的内存错误”的报警声玩玩咯J。需要做的工作有三个:a.通过操作61h port始能speaker input和t2 gate2;b.操作8253控制beep音的音调;c. 音调保持一定的时间(也就是声音的长短)。我们逐个的实现上述功能。
a最简单只要将61h port的bit0&1 pull high/low即可始能或者禁能,代码如下所示:
;----------------------------------------------------------------------------
; speak_set
; en/dis speaker input control&t2 gate2 control
; called with: cx
; used registers: ax
;-----------------------------------------------------------------------------
speak_set proc near

push ax

in al,61h
jcxz se_d
jmp se_e
ss_d:
and al,0fch
jmp se_done
ss_e:
or al,03h
ss_done:
out 61h,al

pop ax
ret

speak_set endp

b就需要设置8253计数器2的模式工作频率,操纵8253的步骤为先向43h port选择所要使用的计数器以及工作模式参数类型等,然后再向42h port装入t2的计数初始值,代码如下所示:
;----------------------------------------------------------------------------
; t2_set
; enable t2 & set work mode & out 2 frequency
; called with: di(frequency demanded)
; used registers: ax,dx
;-----------------------------------------------------------------------------
t2_set proc near

push dx
push ax

mov al,0b6h ;t2 lsb,msb,mode 3,binary
out 42h,al
mov dx,12h
mov ax,348ch
div di
out 42h,al
mov al,ah
out 42h,al

pop ax
pop dx
ret

t2_set endp

c可以通过执行loop达到延时的目的,可是loop延时和处理器的类型频率有关,不同种类的cpu执行同样指令所需的时钟周期不同,就算相同种类但是主频不同的cpu要达到同样的延时效果计数的基准也会不同。那么有没有精确延时的方法呢?书上给出的答案是通过检测61h port的bit4 ram刷新检查位,每隔15us该bit会发生一次变化,所以检测它可以获得比较精确的时间(我猜测这个bit会和8253 t1同步变化,因为t1的输出脉冲用作DRAM的刷新定时信号,而该信号要求15us刷新一次)。延时的代码如下所示:
;----------------------------------------------------------------------------
; delay
; delay time base on 15us unit
; called with: cx (counts of time unit)
; used registers: ax
;-----------------------------------------------------------------------------
delay proc near

push ax

dloop:
in al,61h
and al,10h
cmp al,ah
je dloop
mov ah,al
loop dloop

pop ax
ret
delay endp

以上就是beep的主要代码了,最后开放完整的source code供有兴趣的朋友参考。

REFF:
PC硬體元件控制詳解

《IBM-PC汇编语言程序设计》

Enjoy it!

That’s all!

Peter
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: