;这个是我以前应付上机的题目,放的是两只老虎,绝对是原创通用版本
;TITLE 发音程序
PAGE 50,130
;本程序可以通过两种方式演奏音乐
;第一种,通过键盘按键
;第二种,通过自编曲谱。
;曲谱的规定是,每三位数字一组,表达了一个音符的信息。
;第一个数字代表音阶,从1到7。
;第二个数字代表低音、中音和高音,分别为4,5,6。
;第三个数字代表音长,从0到5,对应32分、16分、8分、4分、2分和全音符。'-'代表延时
;曲谱结束以一个END_C为标记。
MYSTACK SEGMENT PARA STACK 'STACK'
DW 100 DUP(?)
TOP EQU THIS WORD
MYSTACK ENDS
DATA SEGMENT PARA 'DATA'
BUF_F DW 131,147,165,175,196,220,247,262,294,330,349,392,440,494,523,588,660,698,784,880,988 ;音符对应频率,从低音1开始到高音7
YC EQU ($-BUF_F)/2 ;音符的个数
BUF_C DW YC DUP(?) ;音符对应计数值
BUF_Q DB 3000 DUP(?) ;存放曲谱
BUF_A DB '1234567qwertyuasdfghj' ;键盘上音符对应键的ASCII码
END_C EQU 'm' ;曲谱、按键奏乐以字符END_C为标记
STATE_8255 DB ? ;保存程序运行前8255的初始状态(61H端口),待程序结束时恢复
BUF_TWOTIGERS DB 2 DUP('153253353153')
DB 2 DUP('353453554')
DB 2 DUP('552--1651552--1451353153')
DB 2 DUP('153543154'),END_C
PROMPT_MSG1 DB 'Please select function:',0AH,0DH
DB '1----Play Two Tigers',0AH,0DH
DB '2----Play music via the keyboard(BREAK BY STROKING THE KEY--',END_C,')',0AH,0DH
DB '3----Load and Play(press ESC back to MENU)',0AH,0DH
DB '4----Exit',0AH,0DH
DB 'Enter your choice(1,2,3,4):','$'
PROMPT_MSG2 DB 'Now playing Two Tigers...',0AH,0DH,'$'
PROMPT_MSG3 DB 'Please stroke the keyboard to play music.Enjoy!(BREAK BY STROKING THE KEY--',END_C,0AH,0DH,'$'
PROMPT_MSG4 DB 'Please input filename:$'
PROMPT_MSG5 DB 'File does NOT exist!!',0AH,0DH,'$'
TC_1_32 EQU 70 ;三十二分音符所耗的循环次数,经验值,须根据CPU速度的不同设定。
DATA ENDS
SAVE_8255 MACRO ;保存8255的信息
IN AL,61H
MOV STATE_8255,AL
ENDM
RESTORE_8255 MACRO ;恢复8255的信息
MOV AL,STATE_8255
OUT 61H,AL
ENDM
OPEN_SPEAKER MACRO ;打开扬声器
IN AL,61H
OR AL,3
OUT 61H,AL
ENDM
CLOSE_SPEAKER MACRO ;关闭扬声器
IN AL,61H
AND AL,0FCH
OUT 61H,AL
ENDM
SET_COUNTER MACRO ;设置好8253的定时器2,默认AX存放音符计数值的寄存器。
PUSH AX
MOV AL,0B6H ;设置2号定时器为方波输出
OUT 43H,AL
POP AX
OUT 42H,AL
MOV AL,AH
OUT 42H,AL
ENDM
DELAY_TIME MACRO
LOCAL L1,L2
MOV AX,TC_1_32 ;延时程序,CL中放三十二分音符的倍数,从0到5,对应32分、16分、8分、4分、2分和全音符。
SHL AX,CL
L1: MOV CX,0FFFFH
L2: NOP
NOP
NOP
NOP
LOOP L2
DEC AX
JNZ L1
ENDM
COMPUTE_SAVE_COUNT MACRO
LOCAL LP ;计算并保存计数值
MOV CX,YC
LEA SI,BUF_F
LEA DI,BUF_C
LP: MOV BX,[SI]
MOV DX,12H
MOV AX,34DCH
DIV BX
MOV [DI],AX
INC SI
INC SI
INC DI
INC DI
LOOP LP
ENDM
CODE SEGMENT PARA 'CODE'
BEGIN PROC FAR
ASSUME CS:CODE,DS:DATA,ES:DATA,SS:MYSTACK
MOV AX,DATA
MOV DS,AX
MOV ES,AX
SAVE_8255 ;保存8255的信息
COMPUTE_SAVE_COUNT ;计算并保存计数值
CALL FAR PTR PRINT_MENU ;打印系统菜单
;处理用户选择
L0: MOV AH,01H
INT 21H
CMP AL,'1' ;演奏内置音乐
JNZ L1
CALL FAR PTR PLAY_TWOTIGERS
JMP L0
L1: CMP AL,'2' ;通过键盘演奏
JNZ L2
CALL FAR PTR PLAY_KEYBOARD
JMP L0
L2: CMP AL,'3' ;通过调用曲谱文件演奏
JNZ L3
CALL FAR PTR PLAY_FILE
JMP L0
L3: CMP AL,'4' ;退出系统
JNZ L0
RESTORE_8255 ;恢复8255的信息
MOV AX,4C00H
INT 21H
BEGIN ENDP
PRINT_MENU PROC FAR ;打印系统菜单
MOV AH,09H
LEA DX,PROMPT_MSG1
INT 21H
RET
PRINT_MENU ENDP
PLAY_BUF PROC FAR ;演奏BUF_Q里面的曲谱
OPEN_SPEAKER
LEA SI,BUF_Q
LQ1: CMP BYTE PTR [SI],END_C ;测试结束标记
JZ LQ2
CMP BYTE PTR [SI],'-' ;测试延时标记
JZ LQ3
MOV AL,[SI+1] ;取计数值
SUB AL,34H
MOV BL,7
MUL BL
MOV BL,[SI]
SUB BL,31H
ADD AL,BL
SHL AL,1
XOR AH,AH
MOV BX,AX
MOV AX,BUF_C[BX]
SET_COUNTER ;设置好8253的定时器2,默认AX存放音符计数值的寄存器。
LQ3: MOV CL,[SI+2]
SUB CL,30H
DELAY_TIME ;延时程序,CL中放三十二分音符的倍数,从0到5,对应32分、16分、8分、4分、2分和全音符。
INC SI
INC SI
INC SI
JMP LQ1
LQ2: CLOSE_SPEAKER ;关闭扬声器
RET
PLAY_BUF ENDP
PLAY_TWOTIGERS PROC FAR ;演奏内置音乐
LEA SI,BUF_TWOTIGERS
LEA DI,BUF_Q
CLD
LT1: CMP BYTE PTR [SI],END_C ;测试结束标记
JZ LT2
MOVSB
JMP LT1
LT2: MOV BYTE PTR [DI],END_C
CALL PLAY_BUF
RET
PLAY_TWOTIGERS ENDP
PLAY_KEYBOARD PROC FAR ;通过键盘演奏
OPEN_SPEAKER
LK4: MOV AH,07H
INT 21H
CMP AL,END_C
JZ LK1
LEA SI,BUF_A
MOV CX,YC
LK3: CMP BYTE PTR [SI],AL
JZ LK2
INC SI
LOOP LK3
JMP LK4
LK2: SHL SI,1
MOV AX,BUF_C[SI]
SET_COUNTER ;设置好8253的定时器2,默认AX存放音符计数值的寄存器。
JMP LK4
LK1: CLOSE_SPEAKER
RET
PLAY_KEYBOARD ENDP
PLAY_FILE PROC FAR ;通过调用曲谱文件演奏
RET
PLAY_FILE ENDP
CODE ENDS
END BEGIN