本文为《汇编语言程序设计》1704小节例程。点击链接…进课程主页。
任务:编程序,让计算机“唱”歌曲《新年好》
程序:
assume cs:codeseg, ds:dataseg, ss:stackseg
dataseg segment
mus_freq dw 262,262,262,196
dw 330,330,330,262
dw 262,330,392,392
dw 349,330,294
dw 294,330,349,349
dw 330,294,330,262
dw 262,330,294,196
dw 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
;演奏一个音符
;入口参数:si - 要演奏的音符的频率的地址
; di - 要演奏的音符的音长的地址
sound:
push ax
push dx
push cx
;8253 芯片(定时/计数器)的设置
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芯片, 控制扬声器的开/关
in al,61h ;读取8255 B端口原值
mov ah,al ;保存原值
or al,3 ;使低两位置1,以便打开开关
out 61h,al ;开扬声器, 发声
mov dx, [di] ;保持[di]时长
wait1:
mov cx, 28000
delay:
nop
loop delay
dec dx
jnz wait1
mov al, ah ;恢复扬声器端口原值
out 61h, al
pop cx
pop dx
pop ax
ret
codeseg ends
end start
附1:《新年好歌谱》
附2:音符和发音频率(Hz)的对应关系
附3:Mary had a little lamb数字化歌谱
dataseg segment
mus_freq dw 330, 294, 262, 294, 330, 330, 330
dw 294, 294, 294, 330, 392, 392, 392
dw 330, 294, 262, 294, 330, 330, 330, 330
dw 294, 294, 330, 294, 262, -1
mus_time dw 6 dup(25), 50
dw 2 dup(25, 25, 50)
dw 12 dup(25), 100
dataseg ends
附4:《九儿》数字化歌谱(部分)
dataseg segment
mus_freq dw 440, 587, 587, 523, 494, 440, 392
dw 440, 294
dw 440, 587, 587, 523, 494, 392, 440, 492, 440, -1
mus_time dw25, 50, 25, 25, 25, 25, 25
dw 50, 150
dw 25, 50, 25, 25, 25, 12, 12, 50, 175
dataseg ends