目标程序
用汇编语言实现一个音乐播放器,并支持点歌
Overview
乐曲是按照一定的高低、长短和强弱关系组成的音调,在一首乐曲中,每个音符的音高和音长与频率和节拍有关,因此我们要分别为3首要演奏的乐曲定义一个频率表和一个节拍时间表。
利用8253/54定时器来驱动扬声器,在8253/54中的43端口送一个控制字0B6H,该控制字对定时器2进行初始化,使定时器2准备接收计数初值。在8253/54中的42H端口装入一个16位的计数值,以建立将要产生的发声频率。把输出端口61H的0、1两位置1,发出声音。
输入不同的数字,要播放相应的音乐。因此我们要利用输入的数字来获取歌曲的频率表地址和节拍时间表地址来播放对应音乐,利用直接定址表即可迎刃而解。
在CMOS RAM中存放着当前的时间,秒、分、时、日、月、年分别存放在0,2,4,7,8,9的单元中。这些数据以BCD码的方式存放,因此我们若要输出时间还需将他们转换为ASCII码才可正确输出。
更改界面颜色。在内存地址空间中,B8000H~BFFFFH共32KB的空间,为80*25彩色字符模式的显示缓冲区。一个字符占两个字节的存储空间,低位字节存储字符的ASCII码,高位字节存储字符的属性。属性字节的格式:7:BL闪烁 6、5、4:RGB背景色 3:I高亮 2、1、0:RGB前景色。更改属性字节即可更改界面颜色。
效果预览
初始界面
开始播放
结束播放
流程图
源码
;点歌系统:
;输入不同的数字,播放相应的音乐。
;其中“1”代表《刚好遇见你》音乐,“2”代表《成都》音乐,“3”代表《暧昧》音乐。
;输入数字“0”,则退出点歌系统。
;----------------------------------------------------
stack segment para stack 'stack'db 100 dup ('?')
stack ends
;----------------------------------------------------
data segment para 'data'
tip0 db '-----------------------------------','$'
tip1 db '| 1: just met you-- Fei Yuqing |','$'
tip2 db '| 2: ChenDu -- Zhao Lei |','$'
tip3 db '| 3: ambiguity -- Xue Zhiqian |','$'
tip4 db '| 0: exit |','$'
tip5 db ' --please input your choice: ','$'
tip dw tip0,tip1,tip2,tip3,tip4,tip0,tip5 ;界面
tip6 db 'music over!','$'
tip7 db 'begin:','$'
tip8 db 'end :','$'
tip9 db 'You have exited successfully.','$'
tip10 db 'input error!please input again:','$'
time db 'yy/mm/dd hh:mm:ss','$' ;输出时间的格式
table db 9,8,7,4,2,0 ;CMOS ROM中时间信息的存放单元;----------------------------------------------------
;《刚好遇见你》
mus_freq1 dw 262,262,262dw 262,220,524,440,440,440,392dw 392,330,330,262,294,262,262dw 262,220,524,587,524,524,440dw 440,392,330,392,294,262,247dw 262,220,524,440,440,440,392dw 392,392,330,392,294,262,294dw 262,247,262,262,262,294dw 330,294,262,262,247,262,262dw -1
mus_time1 dw 25,25,25dw 25,25,25,25,50,25,25dw 25,25,25,25,50,25,25dw 25,25,25,25,50,25,25dw 25,25,25,25,50,25,25dw 25,25,25,25,50,25,25dw 25,25,25,25,50,25,25dw 25,12,12,100,25,25dw 25,12,12,25,12,12,100
;---------------------------------------------------
;《成都》
mus_freq2 dw 196,262dw 262,294,330dw 392,330,330dw 330,196dw 262dw 294,262,220dw 196,196dw 262,262,294,330dw 440,330,392dw 330,294dw 262dw 294,392dw 330,294dw 330,392dw 392,330,392dw 440,524,440dw 330,294,262dw 294,330,330dw 196,392dw 330,330dw 294,262,262dw 196,294,262dw 330,294,262dw 262dw -1
mus_time2 dw 25,75dw 50,12,12dw 25,25,25dw 25,50dw 75dw 25,25,25dw 125,25dw 75,50,12,12dw 25,25,50dw 25,25dw 75dw 50,25dw 25,100dw 25,75dw 25,25,25dw 25,25,75dw 25,50,25dw 50,25,125dw 25,100dw 25,25dw 25,25,75dw 25,50,25dw 50,12,12dw 200
;-------------------------------------------------
;《暧昧》
mus_freq3 dw 262,262,262,294,262dw 392,440,330,330,294dw 247,247,247,262,247dw 330,392,247,294,262dw 220,247,220,247,220dw 330,349,262,220,247dw 196,262,294,294dw 349,330,330,294,262,294dw 330,262,262,262,294,262dw 392,440,330,330,294dw 294,247,247,247,262,247dw 330,392,247,294,262dw 220,247,220,247,220dw 330,349,220,262,247dw 247,330,392,294,262dw -1
mus_time3 dw 25,25,25,25,25dw 25,100,25,25,100dw 25,25,25,25,25dw 25,100,25,25,100dw 25,25,25,25,25dw 25,100,25,25,75dw 25,50,50,25dw 50,25,25,25,50,25dw 75,25,25,25,25,25dw 25,100,25,25,25dw 75,25,25,25,25,25dw 25,100,25,25,100dw 25,25,25,25,25dw 25,100,25,25,25dw 100,50,25,50,75
;---------------------------------------------------
;直接定址表
mus_time dw mus_time1,mus_time2,mus_time3
mus_freq dw mus_freq1,mus_freq2,mus_freq3
data ends
;----------------------------------------------------
code segment para 'code'assume cs:code,ss:stack,ds:data
music proc farmov ax,datamov ds,axcall tips;打印界面mov bh,00;第0页mov dh,06;第7行mov dl,42;第42列mov ah,02int 10h;设置光标位置mov ah,01int 21h;键盘输入并回显input: cmp al,30h;和'0'比较jb end_error;调用end_error处理错误输入cmp al,33h;和'3'比较ja end_error;调用end_error处理错误输入cmp al,30h;判断输入是否为'0'je end_exit;调用end_exit退出程序sub al,30h;ASCII码转换为对应数字dec al;减一,因为地址从0开始mov ah,0;ax寄存器高位置0shl ax,1;每个地址数据占两个字节,向左移位实现*2mov di,ax;相对位移赋值给变地寄存器实现寄存器相对寻址mov si,mus_freq[di]mov bp,mus_time[di]mov ah,02mov dh,08mov dl,20int 10h;设置光标位置mov ah,09lea dx,tip7int 21h;显示输出push dxmov dh,08mov dl,26call timer;调用timer子程序显示开始播放时间,dh设置显示行数,dl设置显示列数pop dxfreq:mov di,[si]cmp di,-1je end_mus;遇到-1时中止乐曲mov bx,ds:[bp]call soundf;调用发声子程序add si,2add bp,2jmp freq
end_mus:mov bh,00mov dh,10mov dl,26mov ah,2int 10h;设置光标位置mov ah,09lea dx,tip6int 21h;显示输出mov ah,02mov dh,11mov dl,20int 10h;设置光标位置mov ah,09lea dx,tip8int 21h;显示输出push dxmov dh,11mov dl,26call timer;调用timer程序显示结束时间pop dxjmp music_end;end_exit:mov bh,00mov dh,09mov dl,20mov ah,02int 10h;设置光标位置mov ah,09lea dx,tip9int 21h;显示输出jmp music_endend_error:mov ax,0b800hmov es,axmov si,7*160+44*2mov byte ptr es:[si],20h;将输入处清空,以处理第二次仍然是错误的情况mov bh,00mov dh,07mov dl,12mov ah,02int 10h;设置光标位置mov ah,09lea dx,tip10int 21h;显示输出mov bh,00;第0页mov dh,07;第7行mov dl,44;第44列mov ah,02int 10h;设置光标位置mov ah,01int 21h;键盘输入并回显jmp inputmusic_end:mov ax,4c00hint 21h
music endp
;-----------------------------------------------------
;发声程序
soundf proc farpush axpush bxpush cxpush dxpush dimov al,0b6hout 43h,al;对定时器2进行初始化mov dx,12hmov ax,348Ch;12348CH/DI中存放的给定频率得到声音计数值div diout 42h,almov al,ahout 42h,al;分两次先后装入低8位和高8位的声音计数值in al,61hmov ah,alor al,3out 61h,al;将61h端口的数据0、1位置1,发声WAIT1: mov cx,4971;4971*15.08=0.07scall waitf;调用延迟程序dec bxjnz WAIT1mov al,ahout 61h,al;恢复接口的值pop dipop dxpop cxpop bxpop axret
soundf endp
;----------------------------------------------------
;时间延迟
waitf proc farpush ax
waitf1:in al,61hand al,10hcmp al,ahje waitf1mov ah,alloop waitf1pop axret
waitf endp
;------------------------------------------------------
;打印界面 按预先设定的tip中的内容输出并更改背景颜色
tips proc farpush axpush dspush sipush cxmov si,offset tipsub si,2mov cx,7mov al,-1
tipsf:mov bh,0inc almov dh,almov dl,12mov ah,2int 10hpush axadd si,2mov dx,[si]mov ah,09int 21hpop axloop tipsfmov ax,0b800hmov es,axmov bp,0mov si,25
colorf:;改背景颜色mov byte ptr es:[bp+si],00110000B;颜色属性add si,2cmp si,95;控制改色区域的列的范围jb colorfadd bp,160mov si,25cmp bp,160*6;控制改色区域为1-6行jb colorfpop cxpop sipop dspop axrettips endp
;------------------------------------------------------
;显示当前时间,dh设置输出所在的行数,dl设置列数
timer proc farpush axpush dspush sipush dipush cxpush bxmov ax,datamov ds,ax
start: mov si,offset tablemov di,offset timemov cx,6
s1: push cxmov al,ds:[si]out 70h,al;向70h写入要访问的单元的地址in al,71h;从71h中得到指定单元的数据mov ah,almov cl,4shr ah,cl;十位数码值add ah,30h;转换位ASCII码and al,00001111b;个位数码值add al,30h;转换位ASCII码mov ds:[di],ahmov ds:[di+1],aladd di,3inc sipop cx loop s1mov bh,0mov ah,2int 10h;根据预先指定的dh,dl设定光标位置mov dx,offset timemov ah,9int 21h;显示输出pop bxpop cxpop dipop sipop dspop axret
timer endp
;------------------------------------------------------
code ends
;---------------------------------------------------------end music 11b;个位数码值add al,30h;转换位ASCII码mov ds:[di],ahmov ds:[di+1],aladd di,3inc sipop cx loop s1mov bh,0mov ah,2int 10h;根据预先指定的dh,dl设定光标位置mov dx,offset timemov ah,9int 21h;显示输出pop bxpop cxpop dipop sipop dspop axret
timer endp
;------------------------------------------------------
code ends
;---------------------------------------------------------end music