Intel汇编语言实现的演奏音乐程序

本程序通过Intel汇编语言来实现一个简单的奏乐。程序中提供一个音乐菜单,用键盘数字键选择相应的音乐后,便开始播放原先编制好的音乐。程序将帮助你进一步的了解Intel汇编语言编程的一些原理。(程序原本是汇编语言课程设计的作业,这里将自己的程序整理并贴出来与大家共享。)
演奏音乐程序: 编写用键盘选择计算机演奏乐曲的程序,要求程序运行首先在屏幕上显示出乐曲菜单.例:
                1:Mary Had a little Lamb
                2:欢乐颂
                3:pretty boy
                …
      当从键盘上输入乐曲的序号时,计算机根据输入的序号演奏相应的乐曲,如果输入的是“Q”,则结束演奏,退出程序.

在看下面的代码之前,先来说明一下操作方法。另外,有关的实现原理可参看本文最后的参考资料。
[操作说明]

以下程序中,提供了四首乐曲(未能找到以上相应乐曲的乐谱,故用以下取代之,读者也可以自行编制自己喜欢的乐谱。):
1:Mary Had a little Lamb         ;玛丽有只小羊羔
2:Christmas ring                 ;圣诞铃声
3:Little Starlet                 ;小星星
4:Little bee                     ;小蜜蜂
键盘输入各音乐对应的数字,便可插放音乐。输入q,则退出。
输入除1到4以外的字符,提示输入出错,提示再次输入。
播放完音乐,再次提供乐曲清单。可再次选择乐曲。
注:推荐在win平台上用Masm编译。
[程序源代码]

;music.asm
;
;YuHongzhou DEC.30.2005
;
dispmsg macro message       ;用来调用显示字符串的宏
     mov dx,offset message
     mov ah,9
     int 21h
     endm

.model small
.stack
.data
;显示音乐菜单的字符串
msgmenu      db'************************************** ',0dh,0ah
     db'* The music menu is that:            * ',0dh,0ah
     db'*                                    * ',0dh,0ah
     db'* 1:Mary Had a little Lamb           * ',0dh,0ah        ;玛丽有只小羊羔
     db'* 2:Christmas ring                   * ',0dh,0ah        ;圣诞铃声
     db'* 3:Little Starlet                   * ',0dh,0ah        ;小星星
     db'* 4:Little bee                       * ',0dh,0ah        ;小蜜蜂
     db'************************************** ',0dh,0ah,'$'

;音乐播放时,提示的字符
msgdoing db 0dh,0ah,0dh,0ah
     db 0dh,0ah,'        Now,playing the music which you chose     ^_^      please waiting...'
     db 0dh,0ah,0dh,0ah,'$'
;提示选择乐曲
msgchoose      db 0dh,0ah,'Input number(1~4) to choose the music you want[q to exit!],input:','$'
;提示输入字符错误
msgerror       db 0dh,0ah,0dh,0ah,0dh,0ah,'!!! -_- you input a wrong number!!!',0dh,0ah
     db 'please try to choose from 1 to 4 again!,[q to exit],input:','$'
;以下对应为各乐曲的频率表和节拍时间表
merry_frequency dw 330,294,262,294,3 dup(330),3 dup(294),330,392,392    
     dw 330,294,262,294,4 dup(330),294,294,330,294,262,0       ;最后一个0控制结束
;设置节拍时间时,采用了25*400=10000表示4/4拍的音节,即0.25秒(四分音符),0.5秒用50*400表示
merry_time         dw 6 dup(25*400),50*400,2 dup(25*400,25*400,50*400),12 dup(25*400),100*400
christ_frequency dw 7 dup(330),392,262,294,330,4 dup(349),2 dup(330),330,294,294,262,294,392
     dw 7 dup(330),392,262,294,330,4 dup(349),2 dup(330),392,392,349,294,262,0   
christ_time dw 2 dup(25*400,25*400,50*400),4 dup(25*400),100*400,2 dup(25*400,25*400,50*400)
     dw 4 dup(25*400),2 dup(50*400),2 dup(25*400,25*400,50*400)
     dw 4 dup(25*400),100*400,2 dup(25*400,25*400,50*400),4 dup(25*400),100*400
star_frequency dw 262,262,392,392,440,440,392,349,349,330,330,294,294,262
     dw 2 dup(392,392,349,349,330,330,294)
     dw 262,262,392,392,440,440,392,349,349,330,330,294,294,262,0
star_time dw 3 dup(6 dup(25*400),50*400,6 dup(25*400),50*400)
bee_frequency dw 392,330,330,349,294,294,262,294,330,349,4 dup(392),330,330,349,294,294
     dw 262,330,392,392,3 dup(330),5 dup(294),330,349,5 dup(330),349,392
     dw 392,330,330,349,294,294,262,330,392,392,262,0
bee_time dw 2 dup(25*400,25*400,50*400),4 dup(25*400),3 dup(25*400,25*400,50*400)
     dw 3 dup(6 dup(25*400),50*400)
     dw 2 dup(25*400,25*400,50*400),4 dup(25*400),100*400

table     dw music1,music2,music3,music4     ;取得各个标号的偏移地址
.code
.startup
;主程序
startmain: dispmsg msgmenu   
     dispmsg msgchoose        ;提示菜单及输入数字

inputagain: mov ah,1       ;等待按键
     int 21h
     cmp al,'q'      ;输入是否q,是则退出
     je game_over
     cmp al,'1'      ;数字<1?
     jb doagain
     cmp al,'4'      ;数字>4?
     ja doagain  
     and ax,000fh       ;将ASCII码转换成数字,以便后面指向table表里对应的地址
     dec ax             ;减1,对应地址
     shl ax,1           ;相当于ax*2,因为table里的相邻偏移地址是2
     mov bx,ax   
     jmp table[bx]      ;(段内)间接转移:IP<--[table+bx]跳到table里面对应的程序段
game_over: .exit 0
;以下为子程序、分支程序定义处
;***************输入错误时处理的程序[提示:再次输入]********************
doagain:dispmsg msgerror
jmp     inputagain

;**********************以下对应为各乐曲的处理程序************************
music1: dispmsg msgdoing       ;输入为1时,唱Mary Had a little Lamb的处理程序
lea si,offset merry_frequency
lea bp,offset merry_time
again1:     mov di,[si]
     cmp di,0        ;0用于判断是否结束歌曲
     je startmain
     mov bx,[bp]
     call speaker
     add si,2        ;取下一频率值
     add bp,2        ;取下一时间节拍值
     jmp again1
          ;输入为2时,唱'圣诞歌'的处理程序
music2: dispmsg msgdoing
lea si,offset christ_frequency
lea bp,offset christ_time
again2:     mov di,[si]
     cmp di,0
     je startmain
     mov bx,[bp]
     call speaker
     add si,2
     add bp,2
     jmp again2
          ;输入为3时,唱'小星星'的处理程序
music3: dispmsg msgdoing
lea si,offset star_frequency
lea bp,offset star_time
again3:     mov di,[si]
     cmp di,0
     je startmain
     mov bx,[bp]
     call speaker
     add si,2
     add bp,2
     jmp again3
          ;输入为4时,唱'小蜜蜂'的处理程序
music4:     dispmsg msgdoing
lea si,offset bee_frequency
lea bp,offset bee_time
again4:     mov di,[si]
     cmp di,0
     je startmain
     mov bx,[bp]
     call speaker
     add si,2
     add bp,2
     jmp again4

;***********************音乐处理子程序*************************
speaker     proc     
push ax      
push bx
push cx
push dx
push di
mov al,0b6h     ;向计数器写控制字   
out 43h,al      ;方式3、双字节写和二进制计数方式写到控制口
mov dx,12h      ;设置被除数  
mov ax,533h*896
div di          ;其商ax为预置值
out 42h,al      ;先送LSB
mov al,ah                  
out 42h,al      ;后送MSB
in al,61h       ;读端口原值  
mov ah,al    
or al,03h
out 61h,al      ;接通扬声器    
  
wait1: mov cx,28010     ;设循环次数为8ff0h    
delay1: loop delay1     
dec bx                 ;循环持续bx次,即传进来的节拍时间  
jnz wait1

mov al,ah              ;写回61h端口值,关闭扬声器  
out 61h,al    
pop di
pop dx
pop cx
pop bx
pop ax
ret
speaker endp
end

你可能感兴趣的:(ASM)