个人头像

汇编语言--计算机钢琴

发表于2017-08-06 | 分类于

计算机钢琴程序——编写程序, 程序运行时使 PC 机成为一架可弹奏的钢琴,
当按下数字键 1-8 时, 依次发出 8 个音调。 按 CTRL-C 则退出钢琴状态
利用文件存储乐谱,可以播放之前弹奏的音乐

.8086
.MODEL small
.data
      dw 0,0
fun_table dw Quit,Play,History     ;代码的直接定址表,存放各功能代码块的偏移地址
music_table dw 494,524,588,660,698,784,880,950  ;数据的直接定址表,存放音符频率
menu  db 10,13,10,13,'*** MENU ***'
      db 10,13
      db 10,13,'  1. Play'
      db 10,13,'  2. History'
      db 10,13,'  0. Quit'
      db 10,13
      db 10,13,'please choose one of 0~2:','$'  
content db 100 dup(0)         ;存放输入按键的ASCII码
filename db 'history.txt',0    ;导出文件的文件名
handle dw ?                     ;导出文件的文件句柄
mess1  db 10,13,'please start your performance,exit with ctrl+c','$'
mess2  db 10,13,'Playing now......','$'
.stack 128
.code
;*****************************************************************
; 宏
; 功能:显示字符串
; 入口参数:字符串标号
; 出口参数:无
;*****************************************************************
show macro _Label
    lea dx,_Label  ;字符串标号
    mov ah,9
    int 21h
endm    

main proc
start: 
    mov ax,@data
    mov ds,ax
disp:
      show menu    
      mov ah,1
      int 21h             ;调用21h中断的第1号功能,从键盘读入字符,AL保存读入字符的ASCII码
      cmp al,30h
      jb no
      cmp al,32h
      ja no
      sub al,30h         ;将输入的ASCII码转为BCD码
      mov bl,al
      mov bh,0
      add bx,bx         ;数字乘2才是直接定址表中的标号的地址
      call word ptr fun_table[bx]  ;调用子程序
   no:jmp disp          ;重复显示菜单

;*****************************************************************
; 子程序:Play
; 功能:输入钢琴按键
; 入口参数:无
; 出口参数:无
;*****************************************************************
Play proc
    show mess1
    ;换行
    mov dl,0Dh
    mov ah,2
    int 21h
    mov dl,0Ah
    mov ah,2
    int 21h
    ;创建文件
    lea dx,filename
    mov cx,0
    mov ah,3ch
    int 21h              ;21号中断的3c号功能,用指定的文件名创建一个新文件
    mov handle,ax     ;保存文件句柄
    mov di,0
input:
     mov ah,00h
     int 16h
     cmp ah,2eh     ;按CTRL-C则退出钢琴状态,ah存储键盘扫描码
     je finish
     cmp al,'1'     ;al存储ascii码
     jb input
     cmp al,'8'
     ja input
     sub al,30h         ;将输入的ASCII码转为BCD码
     mov bl,al
     mov bh,0
     add bx,bx         ;数字乘2才是直接定址表中的标号的地址
     mov si,music_table[bx]
     call sound
     add di,2
     jmp input
finish: ;向文件中写入文本
    mov cx,100
    mov bx,handle
    lea dx,content        ;dx存储起始地址
    mov ah,40h            ;21号中断的40号功能
    int 21h
    ;关闭文件
    mov bx,handle
    mov ah,3eh
    int 21h
    jmp disp
Play endp

temp:     ;History里jmp的中转处
      jmp disp

;*****************************************************************
; 子程序:History
; 功能:播放之前弹奏的音乐
; 入口参数:无
; 出口参数:无
;*****************************************************************
History proc
    show mess2
    ;打开文件
    mov ah,3dh
    mov al,00h        ;只读 
    lea dx,filename
    mov handle,ax     ;保存文件句柄
    int 21h
    ;读取文件内容  
    mov ah,3fh  
    mov bx,handle  
    lea dx,content  
    mov cx,100  
    int 21h 
    ;关闭文件
    mov bx,handle
    mov ah,3eh
    int 21h     
    ;播放
    mov di,0
op: 
    mov al,content[di]
    cmp al,0
    je temp
    sub al,30h         ;将输入的ASCII码转为BCD码
    mov bl,al
    mov bh,0
    add bx,bx         ;数字乘2才是直接定址表中的标号的地址
    mov si,music_table[bx]
    call sound
    add di,2
    dec cx
    jmp op
    ret
History endp 

;*****************************************************************
; 子程序:sound
; 功能:产生音调
; 入口参数:si——音调频率 
; 出口参数: 
;*****************************************************************
sound proc
;演奏一个音符
;入口参数:si - 要演奏的音符的频率的地址
    push ax
    push dx
    push cx
    add al,30h
    mov content[di],al ;写入内存
    ;8253 芯片(定时/计数器)的设置
    mov al,0b6h    ;8253初始化
    out 43h,al     ;43H是8253芯片控制口的端口地址
    mov dx,12h
    mov ax,34dch
    div 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,12      ;保持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
sound endp   

;*****************************************************************
; 子程序:Quit
; 功能:退出程序
; 入口参数:无
; 出口参数:无
;*****************************************************************
Quit  proc near
      mov ah,4ch
      int 21h
Quit  endp


main endp
      end start