第十章聆听数字的声音.docx
- 文档编号:3864101
- 上传时间:2022-11-26
- 格式:DOCX
- 页数:32
- 大小:370.17KB
第十章聆听数字的声音.docx
《第十章聆听数字的声音.docx》由会员分享,可在线阅读,更多相关《第十章聆听数字的声音.docx(32页珍藏版)》请在冰豆网上搜索。
第十章聆听数字的声音
第十章聆听数字的声音
每次谈到计算机硬件的时候,人们无一例外地会说总线有多宽,速度有多快。
很难说什么样的“快”才叫快,特别是考虑到内存和外部设备在速度上和处理器相比,从来就不在一个数量级上。
数据通常在内存和外部设备之间,以及一个外部设备和另一个外部设备之间流动。
典型的例子包括从硬盘读数据到内存中,或者从内存中把数据传送到显存中。
如果屏幕的分辨率是1280×1024,在真彩色显示方式下,每个像素至少需要3个字节。
想想看,以每秒30帧的速度来刷新屏幕上的画面,这样的事,也只有现在的技术可以做到,在从前是不可想象的。
现在,让我们简单快速地回顾一下,以前是怎样的,现在又是怎样的。
10.1本章意图
10.1.1过气的直接存储器访问
数据传输毫无疑问地是在总线上进行的,因为不可能在众多的设备之间建立一对一的连线。
但是,总线的麻烦之处在于如何避免冲突,当两个家伙对话时,无关的人都应当关上门闭上嘴。
在这种情况下,必须有一个能够控制总线的设备,或者称为总线主控设备(busmaster),来管理这种事情。
在硬件技术不发达的早期,处理器是最重要的总线主控制设备,它有权决定谁参与总线数据传输。
考虑以下代码片断:
mov[0x2000],dx
在执行这条指令时,处理器不但发出地址信号,也发出控制信号,控制信号用来表明该地址是发给内存的,还是发给外部设备的。
所有设备都有译码电路,这些译码电路的输入就是地址和控制信号。
以上指令执行的时候,内存的译码结果是打开通向总线的数据通路,而外部设备则保持同总线的脱离状态。
相反地,下面的指令是发给端口的,内存当然不会工作。
而且,只有那个端口号相符的外部设备才会和数据总线连通,其它所有设备都保持同总线的脱离状态:
inal,0x70
在这种情况下,如果外部设备希望向内存传输数据,那么,必须由处理器首肯并介入其中:
movbx,0x2000
movcx,500
ttime:
inal,0x50
mov[bx],al
loopttime
在这个例子中,数据先从端口0x50读出,传送到寄存器AL,然后再传送到内存单元,视数据量的大小,这个过程需要重复多次。
当两个外部设备希望对话时,比如从一个设备传送200个字节的数据到另一个设备,情况也差不多:
movcx,200
ttime:
inal,0x30
out0x50,al
loopttime
在过去的岁月中先后出现过多种不同的总线类型,它们的典型代表就是工业标准结构总线(IndustrialStandardArchitecture:
ISA)。
总线不单单是数据线路,还包括地址线路的控制信号线路,规定和数据和地址的宽度,以及各种控制信号的规程和电气特性。
控制信号规定了设备之间互相交流的协同的方式,而不同的总线有不同的控制信号规程。
ISA是面向单用户和简单应用环境的总线,结构并不复杂。
所以,符合ISA信号规程的外部设备都很简单。
以今天的眼光看来,或者以数据传输的效能来看,对总线的控制能力有限。
尽管如此,它还是提供了一种解决方案,那就是直接存储器访问(DirectMemoryAccess:
DMA)。
直接存储器访问的核心器件是DMA控制器(DMAController:
DMAC),尽管不如处理器那么神通广大,但也是一个总线主控设备。
一台计算机内只有一个DMA控制器,由它负责所有外部设备的直接数据传输协调工作,而且赋予它的总线主控能力要高于处理器。
比如,当外部设备需要发起一次针对内存某个区域的数据传输时,应该向DMAC发出请求。
DMAC回应此请求,同时告诉处理器不要再使用总线。
注意,这是干预处理器的工作,命令它让出总线。
接着,由它主导,开始在该外部设备和内存之间直接传输数据。
如图10-1所示,在早期的计算机系统中,DMAC是独立于处理器和外部设备之外的第三方(Thridparty)总线主控器。
设备向DMAC发送DMA请求(DMAREQuest:
DREQ);如果总线空闲,可以占用,DMAC用DMA确认信号(DMAACKnology)回应。
此后,就可以正常开始DMA传送。
图10-1直接存储器访问控制逻辑
对大批量、高速数据传输的需求一直是存在着的,区别仅仅在于这种需求有多强烈。
所谓水涨船高,要求自然也跟着提高了,当我们的显卡能够支持高分辨率和真彩色显示模式时,大家就开始考虑让它能够播放高码率的高清电影。
DMA技术曾经很流行,但总线技术发展速度更快,所以,现在的情况是,DMA技术依然在大学的教材上非常流行,现实生活中,在实际的计算机系统中,它已经渐渐离我们远去。
在个人计算机中,总线发展的目标是智能、高速和并发地进行输入输出。
因此,为那些有批量数据传输需求的设备提供总线主控能力无疑是非常必要的,这就是现行的PCI和PCI-E总线所能为我们做的。
在这些新型的总线和总线设备上,DMA已经是一个历史名词,就仿佛它并没有存在过。
不得不说硬件的发展是非常快的,有很多曾经是主流的硬件慢慢地离我们而去,包括DMA技术。
尽管在现行的ICH芯片中还包括了DMA部件,但那是看在一些老式设备的面子上留下来的。
事实上,现在已经没有多少老式设备了,迟早有一天,这些老家伙,包括DMA,都会被清除出去。
10.1.2绝迹的SoundBlaster16声卡
讲解数据传输技术是很有趣的,但这需要设计一个典型的例子,还得结合最新的技术。
比如,我们可以设计一个例子程序,访问PCI(E)声卡,用它来播放MP3音乐。
在这个过程中,涉及到MP3文件的解码,这会让我们接触到处理器的多媒体指令;也涉及到PCI(E)声卡的数据传输控制,这会让我们见识如何访问PCI(E)设备,以及对它们编程的一般方法;数据的传输是处理器、声卡和应用程序三方参与的事情,这还涉及到彼此的通信和协调,这能让我们了解到中断机制是如何发挥作用的。
这真是一个复杂的例子,并不适合目前的学习进度,因此只能放在下册来做。
现在,比较实际的做法是先打一个简单的基础,看一看DMA是怎么工作的。
在本章,我们同样会写一个声音播放程序,但是,用到的硬件都非常古老,除了DMA,还有曾经的SoundBlaster16声卡,这种声卡已经绝迹了。
SoundBlaster的发音听起来很像“嗓子拨拉得疼”,所以又叫声霸卡就不值得奇怪了,毕竟都是谐音。
SoundBlaster16简称为SB-16,是新加坡创新公司的杰出产品,当年一统江湖,名气很大。
不像显卡,现在的显卡还能同以往保持兼容,所以我们的程序还能工作,但声卡不同,现在的声卡都不能按SoundBlaster16的方式工作。
所以,本章的程序只能在虚拟机上运行,在真实的计算机上,是发不出任何声音的。
VirtualBox虚拟机中有声卡类型选项,甚至可以选择“BoundBlaster16”,但是很明显,那是个虚拟的硬件。
虚拟机会把你针对硬件的访问和操作转化成对真实声卡的访问。
所以,这意味着,要想用本章的程序播放出声音来,宿主机,也就是安装虚拟机的真实机器,必须是有声卡和扬声器的。
启动VirtualBox,选择你创建的虚拟机(比如LEARN-ASM),单击“设置”,进入参数设置对话框。
如图10-2所示,选择“声音”项目。
图10-2为虚拟机设置声卡类型
首先要选择“启动声音”。
然后,选择声卡类型为“WindowsDirectSound”。
该选项的意思是,当你在虚拟机里针对声卡硬件进行操作时,虚拟机也会访问真实的物理声卡来做转换工作,而虚拟机访问物理声卡的方式是调用Windows操作系统的DirectSound驱动程序。
除了我们,现在谁还会直接针对硬件编程?
顺便说一句,DirectSound是DirectX的一部分,而DirectX解决了电子游戏程序在Windows上运行时慢如蜗牛的问题,它为游戏程序提供了一种绕过Windows图形用户接口的工具和方法,允许它们直接访问显卡和显存。
开发这套工具的三个核心人物,是微软公司内部的讨厌鬼。
那家伙们有着令人愤恨的怪僻,经常脚步沉重地提着一把塑料战斧穿过走廊,跟人说话的姿势夸张得好像正从自己的头脑中拿出一点智慧施舍给对方。
他们在微软帝国高层毫不知情的情况下开发出这套多媒体接口,开创了游戏行业的新天地。
“控制芯片”的意思是,你采用什么声卡。
声卡有自己的微处理器,即I/O处理器,通常称为数字信号处理器(DigitalSingalProcessor:
DSP)。
习惯上,人们喜欢用声卡芯片的型号来指代声卡的型号。
这里,应当选择“SoundBlaster16”。
谢天谢地,尽管有很多虚拟机可供选择,但能够支持SoundBlaster16的并不多,VirtualBox虚拟机算是其中的一个。
10.1.3代码清单10-1
1
;===============================================================================
2
SECTIONheadervstart=0;定义用户程序头部段
3
program_lengthddprogram_end;程序总长度[0x00]
4
5
;用户程序入口点
6
code_entrydwstart;偏移地址[0x04]
7
ddsection.code.start;段地址[0x06]
8
9
realloc_tbl_lendw(header_end-realloc_begin)/4
10
;段重定位表项个数[0x0a]
11
12
realloc_begin:
13
;段重定位表
14
code_segmentddsection.code.start
15
data_segmentddsection.data.start
16
stack_segmentddsection.stack.start
17
18
header_end:
19
20
;===============================================================================
21
SECTIONcodealign=16vstart=0;定义代码段(16字节对齐)
22
put_string:
;显示字符串(0结尾)
23
;输入:
DS:
BX=串地址
24
pushax
25
pushbx
26
pushsi
27
28
movah,0x0e;INT0x10第0x0e号功能
29
movsi,bx;字符串起始偏移地址
30
movbl,0x07;显示属性
31
32
.gchr:
33
moval,[si];逐个取要显示的字符
34
oral,al;如果AL内容为零,则
35
jz.rett;跳转到过程返回指令
36
int0x10;BIOS字符显示功能调用
37
incsi;下一个字符
38
jmp.gchr
39
40
.rett:
41
popsi
42
popbx
43
popax
44
45
ret
46
47
;-------------------------------------------------------------------------------
48
write_dsp:
49
pushdx
50
pushax
51
52
movdx,022ch
53
.@22c:
54
inal,dx
55
andal,1000_0000b
56
jnz.@22c
57
58
popax
59
outdx,al
60
popdx
61
62
ret
63
64
;-------------------------------------------------------------------------------
65
read_dsp:
66
pushdx
67
68
movdx,22eh
69
.@22e:
70
inal,dx
71
andal,1000_0000b;监视22e端口的位7,直到它变成1
72
jz.@22e
73
movdx,22ah
74
inal,dx;此时可以从22a端口读取数据
75
76
popdx
77
78
ret
79
80
;-------------------------------------------------------------------------------
81
start:
82
movax,[stack_segment]
83
movss,ax;修改SS时,处理器将在执行下一指
84
movsp,ss_pointer;令前禁止中断
85
86
movax,[data_segment]
87
movds,ax
88
89
movbx,init_msg
90
callput_string
91
92
;以下复位DSP芯片
93
movdx,0x226
94
moval,1;第一步,先写“1”到复位端口
95
outdx,al
96
97
xorax,ax
98
.@1:
99
decax
100
jnz.@1;一个硬件要求的延时(至少3ms)
101
102
outdx,al;第二步,写“0”到复位端口
103
104
callread_dsp
105
cmpal,0xaa;状态值0xaa表示初始化完成
106
jz.@4
107
108
movbx,err_msg;显示出错信息
109
callput_string
110
jmp.idle;直接停机
111
112
.@4:
113
movbx,done_msg
114
callput_string
115
116
;以下安装中断处理过程
117
movbx,intr_msg
118
callput_string
119
120
moval,0x0d;IR5引脚的中断号
121
movbl,4;每个中断向量占4个字节。
本段等效于:
122
mulbl;movbx,0x0d
123
movbx,ax;shlbx,2
124
125
cli;禁止在修改IVT期间发生中断
126
127
pushes;临时使用ES
128
xorax,ax
129
moves,ax;指向内存最低处的中断向量表
130
movword[es:
bx],dsp_interrupt
131
;偏移地址
132
incbx
133
incbx
134
movword[es:
bx],cs;当前代码段
135
popes
136
137
sti
138
139
;允许IRQ5
140
inal,0x21;8259主片的IMR
141
andal,1101_1111B;开放IR5
142
out0x21,al
143
144
movbx,done_msg
145
callput_string
146
147
movbx,dma_msg
148
callput_string
149
150
;对DMA控制器编程,设置其工作模式、缓冲区地址和传输长度
151
movdx,0x0a
152
moval,00000_1_01B
153
outdx,al
154
155
movax,ds;计算缓冲区物理地址
156
movbx,16
157
mulbx
158
addax,voice_data
159
adcdx,0
160
movbx,dx;bx:
ax=缓冲区20位地址
161
162
xoral,al
163
out0x0c,al;DMAC1高低触发器清零
164
165
movdx,0x02;写通道1基址与当前地址寄存器
166
outdx,al;低8位DMA地址
167
moval,ah
168
outdx,al;高8位DMA地址
169
170
movdx,0x83;写DMA通道1的页面寄存器
171
moval,bl
172
outdx,al
173
174
movdx,0x03;写通道1的基字计数与当前字计数器
175
movax,init_msg-voice_data;数据块(当缓冲区用)的大小
176
decax;DMA要求实际大小减一
177
outdx,al;缓冲区长度低8位
178
moval,ah
179
outdx,al;缓冲区长度高8位
180
181
moval,0101_1001b;设置DMAC1通道1工作方式:
单字节传送/
182
out0x0b,al;地址递增/自动预置/读传送/通道1
183
184
movdx,0x0a;DMAC1屏蔽寄存器
185
moval,1;允许通道1接受请求
186
outdx,al
187
188
moval,0x40;设置DSP采样率(播放)
189
callwrite_dsp
190
movax,65536-(256000000/(1*8000))
191
xchgah,al;只使用结果的高8位
192
callwrite_dsp
193
194
;显示信息
195
movbx,done_msg
196
callput_string
197
198
;编程设置DSP的DMA传输模式和数据长度,以启动音频播放
199
moval,0x48
200
callwrite_dsp
201
movax,init_msg-voice_data;数据块(当缓冲区用)的大小
202
shrax,1;长度设为DMA的一半
203
decax
204
callwrite_dsp;写低字节
205
xchgah,al
206
callwrite_dsp;写高字节
207
208
;打开喇叭输出
209
moval,0xd1
210
callwrite_dsp
211
212
;启动DSP的传输的播放
213
moval,0x1c
214
callwrite_dsp
215
216
movbx,play_msg
217
callput_string
218
219
.idle:
220
hlt
221
jmp.idle
222
223
;-------------------------------------------------------------------------------
224
dsp_interrupt:
;中断处理过程
225
pushax
226
pushbx
227
pushdx
228
229
;退出自动初始化模式
230
moval,0xda
231
callwrite_dsp
232
233
;关闭扬声器
234
moval,0xd3
235
callwrite_dsp
236
237
movbx,done_msg
238
callput_string
239
240
movbx,okay_msg
241
callput_string
242
243
movdx,0x22f;DSP中断应答
244
inal,dx
245
246
;发送EOI命令到中断控制器(主片)
247
moval,0x20;中断结束命令EOI
248
out0x20,al;发给主片
249
250
popdx
251
popbx
252
popax
253
254
iret
255
256
;-------------------------------------------------------------------------------
257
SECTIONdataalign=16vstart=0
258
259
voice_dataincbin"baby.wav",44
260
261
init_msgdb'Initializingsoundblastercard...',0
262
263
intr_msgdb'Installinginterruptvector...',0
264
265
dma_msgdb'SetupDMA...',0
266
267
done_msgdb'Done.',0x0d,0x0a,0
268
269
play_msgdb'Voiceisplayingnow...',0
270
271
okay_msgdb'Finished,stop.',0
272
273
err_msgdb'Soundcardinitfailed.',0
274
275
;===============================================================================
276
SECTIONstackalign=16vstart=0
277
278
resb256
279
ss_pointer:
280
281
;===============================================================================
282
SECTIONprogram_trail
283
program_end:
10.2声卡和声卡的初始化
10.2.1SoundBlaster16声卡简介
声卡是数字和声音的转换器件,录音的时候,声波可以推动磁场中的线圈,也可以使处于静电场中的两个电极间距改变,或者使碳精砂的疏密程度发生变化,又或者使压电陶瓷振动,从而产生音频电流。
音频电流是模拟信号(AnalogSignal),
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第十 聆听 数字 声音