决定一个声音的因素有两个:频率和持续时间

在这次示例中用 2 个芯片来分别控制这两个因素,分别为:**8253芯片 **和 8255芯片

8253芯片(定时/计数器)的设置

1
2
3
4
5
6
7
8
9
10
mov al, 0B6H ; 8253初始化,固定值
out 43h, al ; 43H是8253芯片控制口的端口地址
mov dx, 12h ; 被除数高位,固定值
mov ax, 34DCh ; 被除数低位,固定值

div word ptr [si] ; 计算分频值,赋值给ax,[si]存放声音的频率,变量

out 42h, al ; 先送低8位到计数器,42h是8253芯片通道2的端口地址
mov al, ah
out 42h, al ; 后送高8位,分两次传送

8255芯片(并行I/O)的设置,控制扬声器的开/关

1
2
3
4
5
6
7
in al, 61h ; 读取8255 B端口的原值
mov ah, al ; 移到高8位,保存原来端口的值
or al, 0011b ; 使低 2 位置1,以便打开开关
out 61h, al ; 开扬声器,发生
...... ; 延时,保持时间
mov al, ah ; 高8位移到低8位
out 61h, al; 恢复扬声器端口原值

音符和发音频率(Hz)的对应关系:

低音符 频率 中音符 频率 高音符 频率
1 138 1 262 1 524
2 147 2 294 2 587
3 165 3 330 3 659
4 175 4 349 4 698
5 196 5 392 5 784
6 220 6 440 6 880
7 247 7 494 7 988
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
assume cs:codeseg, ds:dataseg, ss:stackseg
dataseg segment
mus_freq dw 262,262,262,196,330,330,330,262
dw 262,330,392,392,349,330,294
dw 294,330,349,349,330,294,330,262
dw 262,330,294,196,247,294,262,-1
mus_time dw 3 dup(12,12,25,25),12,12,50
dw 3 dup(12,12,25,25),12,12,50
dataseg ends
stackseg segment
db 100h dup (0)
stackseg ends
codeseg segment
start:
mov ax, stackseg
mov ss, ax
mov sp, 100h
mov ax, dataseg
mov ds, ax
lea si, mus_freq
lea di, mus_time
play:
mov dx, [si]
cmp dx, -1
je end_play
call sound
add si, 2
add di, 2
jmp play
end_play:
mov ax, 4c00h
int 21h
sound:
push ax
push dx
push cx
mov al,0b6h
; 8253芯片设置
out 43h,al
mov dx,12h
mov ax,34dch
div word ptr [si]
out 42h, al
mov al, ah
out 42h, a
;设置8255芯片, 控制扬声器的开/关
in al,61h
mov ah,al
or al,3
;延时
mov dx, [di]
wait1:
mov cx, 28000
delay:
nop
loop delay
dec dx
jnz wait
;恢复扬声器端口原值
mov al, ah
out 61h, al

pop cx
pop dx
pop ax
ret
codeseg ends
end start