程序说明:
这是一个音乐程序,按大写字母“A”,唱乐曲“玛丽有只小羔羊”;
按大写字母“B”,唱乐曲“太湖船”
按大写字母“C”,唱乐曲“祝福歌”
按"X"鍵,退出
函数图:
DATA SEGMENT PARA 'DATA' ;数据段
;数据对齐到para,1 para=16
;相当于段首址
;link过程中,同一程序的不同模块中, 相同组合名的段会组合到一起
INFO1 DB 0DH,0AH,'WELCOME YOU TO COME HERE!$' ;显示信息
INFO2 DB 0DH,0AH,'this is a music program!$'
INFO3 DB 0DH,0AH,'please select!$'
INFO4 DB 0DH,0AH,'INPUT ERROR!$'
INFO5 DB 0DH,0AH,'PLEASE INPUT AGAIN!$'
MUSLIST DB 0DH,0AH,'A MUSIC1'
DB 0DH,0AH,'B MUSIC2'
DB 0DH,0AH,'C MUSIC3'
DB 0DH,0AH,'Q EXIT'
db 0dh,0ah,'$'
MUS_FREG1 DW 330,294,262,294,3 DUP (330) ;音乐频率表
DW 3 DUP (294),330,392,392
DW 330,294,262,294,4 DUP (330)
DW 294,294,330,294,262,-1
MUS_TIME1 DW 6 DUP (25*8),50*8 ;音乐节拍表
DW 2 DUP (25*8,25*8,50*8)
DW 12 DUP (25*8),100*8
MUS_FREG2 DW 330,392,330,294,330,392,330,294,330
DW 330,392,330,294,262,294,330,392,294
DW 262,262,220,196,196,220,262,294,332,262,-1
MUS_TIME2 DW 3 DUP(50),25,25,50,25,25,100
DW 2 DUP(50,50,25,25),100
DW 3 DUP(50,25,25),100
MUS_FREG3 DW 262,262,294,262,349
DW 330,262,262,294,262
DW 392,349,262,262,523
DW 440,349,262,262,466
DW 466,440,262,392,349,-1
MUS_TIME3 DW 50,50,100,100,100
DW 100,100,50,50,100,100
DW 100,100,100,50,50,100
DW 100,100,100,100,100,50
DW 50,100,100,100,100,100
DATA ENDS
STACK SEGMENT PARA STACK 'STACK' ;堆栈段
DB 200 DUP ('STACK')
STACK ENDS
CODE SEGMENT ;代码段
ASSUME DS:DATA,SS:STACK,CS:CODE
MAIN PROC FAR ;跨间调用main
;系统把主程序当作DOS调用的一个子程序
;DOS内核与主程序不是在同一个段地址内
;所以主程序参数要用far
MOV AX,DATA
MOV DS,AX ;把data放到数据段中
MOV aH, 0 ;终止进程
MOV AL,4
INT 10H
MOV AH,0BH ;检测键盘状态
MOV BH,0
MOV BL,4
INT 10H
MOV AH,0BH ;检测键盘状态
MOV BH,01H
MOV BL,00
INT 10H
;*****定义一个宏*****
SHOW MACRO b ;输出字符串b,show为宏名
LEA DX,b
MOV AH,9
INT 21H
ENDM
;**********************
show INFO1
show INFO2
show INFO3
show MUSLIST
INPUT: MOV AH,01H
INT 21H
CMP AL,'Q'
JZ retu
CMP AL,'A'
JNZ B0
CALL MUSIC1 ;跳转子函数MUSIC1
JMP EXIT1
B0: CMP AL,'B'
JNZ C0
CALL MUSIC2 ;跳转子函数MUSIC2
JMP EXIT1
C0: CMP AL,'C'
JNZ exit
CALL MUSIC3 ;跳转子函数MUSIC3
EXIT1: show INFO5
JMP INPUT
EXIT:
call clear ;跳转子函数clear
show INFO4
show INFO5
show INFO1
show INFO2
show INFO3
show MUSLIST
jmp input
RETU: MOV AH,4CH
INT 21H
MAIN ENDP
MUSIC1 PROC NEAR
PUSH DS
SUB AX,AX
PUSH AX
LEA SI,MUS_FREG1 ;频率的地址赋给si
LEA BP,DS:MUS_TIME1 ;节拍的地址赋给bp
FREG1: MOV DI,[SI]
CMP DI,-1 ;-1是频率的结束标志
JE END_MUS1 ;zf=0时跳转
MOV DX,DS:[BP]
MOV BX,1400
CALL GENSOUND ;跳转gensound
ADD SI,2
ADD BP,2
JMP FREG1
END_MUS1:
RET
MUSIC1 ENDP
;实现音乐的播放函数
GENSOUND PROC NEAR
PUSH AX ;保护现场
PUSH BX
PUSH CX
PUSH DX
PUSH DI
MOV AL,0B6H ;初始化控制字
OUT 43H,AL ;将al写入端口43h,程序中断计时器
MOV DX,12H
MOV AX,533H*896 ;计算非896hz的声音的计数值。
;其它频率声音的时间计数值应为:
;533H×896÷给定频率=123280H÷给定频率
DIV DI
OUT 42H,AL ;将al写入端口42h,磁带或者扩音器的程序中断计时器
;8253中的42端口送一个控制字0B6H(10110110B)
;该控制字对定时器2进行初始化,使定时器2准备接收计数初值
MOV AL,AH
OUT 42H,AL ;传入时间计数值
IN AL,61H ;al从61h端口中读取,扬声器端口
MOV AH,AL
OR AL,3 ;逻辑或运算(3要进行二进制运算,也就是0011)把输出端口61H的PB0、PB1两位 置1,发出声音
OUT 61H,AL ;将al写入端口61h
WAIT1: MOV CX,8FF0H ;相当于不断分配cpu时钟周期,使播放能够完成
DELAY1: LOOP DELAY1 ;循环是bx不断减少
DEC BX
JNZ WAIT1 ;bx不为0就跳到wait1,等待bx*8ff0h*loop使用的cpu时钟周期
MOV AL,AH
OUT 61H,AL
POP DI ;恢复现场
POP DX
POP CX
POP BX
POP AX
RET
GENSOUND ENDP
MUSIC2 PROC NEAR
PUSH DS
SUB AX,AX
PUSH AX
LEA SI,MUS_FREG2
LEA BP,DS:MUS_TIME2
FREG2: MOV DI,[SI]
CMP DI,-1
JE END_MUS2
MOV DX,DS:[BP]
MOV BX,1400
CALL GENSOUND
ADD SI,2
ADD BP,2
JMP FREG1
END_MUS2:
RET
MUSIC2 ENDP
MUSIC3 PROC NEAR
PUSH DS
SUB AX,AX
PUSH AX
LEA SI,MUS_FREG3
LEA BP,DS:MUS_TIME3
FREG3: MOV DI,[SI]
CMP DI,-1
JE END_MUS3
MOV DX,DS:[BP]
MOV BX,1400
CALL GENSOUND
ADD SI,2
ADD BP,2
JMP FREG1
END_MUS3:
RET
MUSIC3 ENDP
;输错了的情况
clear proc near
push ax
push bx
push cx
push dx
mov ah,6 ;直接控制台i/o,输出dl中ascii码对应的字符
mov al,0 ;初始化各种参数
mov ch,0
mov cl,0
mov dh,24
mov dl,79
mov bh,7
int 10h
pop dx
pop cx
pop bx
pop ax
ret
clear endp
CODE ENDS
END MAIN
参数传递
本题无参数传递
中断向量计算
中断向量:
本题用到了10H、21H函数库的函数调用,入口地址为0:40h ,0:84h。
10H号函数库:CS=410h+2h=42h , IP=410h=40h 。中断向量为0:40H。
21H号函数库:CS=421h+2h=86h , IP=421h=84h 。中断向量为0:84h。