本程式是由三個.ASM檔所構成。除了有最基本的將鍵盤按鍵轉換成PC喇叭發音的功能外,還能將按鍵顯示在螢幕上琴鍵的相對位置上。並且能夠升高降低八度音,儲存以及讀取先前的按鍵…等。
程式的方塊圖如下:
當使用者按下鍵盤後,piano.asm先做判斷,如果不是特殊功能的按鍵,則呼叫transf.asm將按鍵轉換成8253的計數值,再由transf.asm呼叫sound.asm將計數值寫到CNT2中並開啟喇叭發音。在發音0.3秒後,在呼叫sound.asm將喇叭關閉。若是按鍵為+/-,則將計數值除/乘以2,以升高或降低八度音。若是按下1鍵則將buffer中的按鍵存入磁碟中,按下2則由磁碟中讀取按鍵到buffer裡,並重播之。
暫停0.3秒,是利用系統每秒發生18.2次中斷時,於0000:046Ch及0000:046Eh的值會被increase,因此當此值增加6時,即約為0.3秒,這就是wait3s這個副程式運作的原理。
以下便是piano.asm, transf.asm, sound.asm的原始碼。
INCLUDE \MASM61\INCLUDE\DOS.INC
INCLUDE \MASM61\INCLUDE\BIOS.INC
buf_size EQU 50
.MODEL SMALL
.DATA
logo DB 0Dh,0Ah,0Dh,0Ah
DB '========================',0Dh,0Ah
DB '*** Electric Piano ***',0Dh,0Ah
DB '========================',0Dh,0Ah,0Dh,0Ah,'$'
pia0 DB 196,220,196,220,196,194,196,220,196,220,196,220,196,194,196,220,196,220,196,194,196,220,196,220,196,220,196,194,196,220,196,0dh,0ah,'$'
pia1 DB 32,219,32,219,32,179,32,219,32,219,32,219,32,179,32,219,32,219,32,179,32,219,32,219,32,219,32,179,32,219,0dh,0ah,'$'
pia2 DB 32,179,32,179,32,179,32,179,32,179,32,179,32,179,32,179,32,179,32,179,32,179,32,179,32,179,32,179,32,179,0dh,0ah,'$'
pia3 DB 196,193,196,193,196,193,196,193,196,193,196,193,196,193,196,193,196,193,196,193,196,193,196,193,196,193,196,193,196,193,196,0dh,0ah,'$'
; a w s e d f t g y h u j A W S E D F T G Y H U J K
KEY DW 0
buffer DB buf_size DUP(0)
buf_off DB 0
handle DW ?
file DB 'notes.dat',0
.CODE
EXTRN transf:NEAR, nosound:NEAR ; External Lib.
begin: mov ax,@DATA
mov ds,ax
@SetCsrPos 0,0,0
mov dx,OFFSET logo ; print logo
mov ah,9
int 21h
show: ; print piano
@SetCsrPos 0,10,0
mov dx,OFFSET pia0
mov ah,9
int 21h
mov cx,3
show1: mov dx,OFFSET pia1
int 21h
loop show1
mov cx,3
show2: mov dx,OFFSET pia2
int 21h
loop show2
mov dx,OFFSET pia3
int 21h
wait_key:
mov ah,7 ; Resd Key
int 21h
cmp al,'*' ; "*" to quit
jz exit
.IF al=='-' && KEY >0
sub KEY,1
.ELSEIF al=='+'
add KEY,1
.ENDIF
.IF al=='1' ;save notes
@MakeFile file,00100000b
mov handle,ax
@Write buffer,buf_size,handle
@CloseFile handle
.ELSEIF al=='2' ;load'n'play
@OpenFile file
mov handle,ax
@Read buffer,buf_size,handle
@CloseFile handle
mov cx,buf_size
mov bx,0
playb: mov al,buffer[bx]
mov si,KEY ; the rising key
push cx
push bx
call transf ; sound
call wait3s ; pause for 0.3s
call nosound ; turn off sound
pop bx
pop cx
inc bx
loop playb
mov buf_off,0
.ENDIF
mov bh,0
mov bl,buf_off
mov buffer[bx],al
add buf_off,1
mov si,KEY ; rising key
call transf ; sound
call wait3s ; pause 0.3s
call nosound
jmp show ; refresh window
exit: call nosound
mov ah,4Ch
int 21h
;---------------------------------------------------------
wait3s PROC NEAR ; wait for 0.3 sec
mov dx,6
mov bx,0
mov es,bx
add dx,es:[46Ch]
adc bx,es:[46Eh]
wait1: mov bp,es:[46Ch]
mov ax,es:[46Eh]
sub bp,dx
sbb ax,bx
jc wait1
ret
wait3s ENDP
;----------------------------------------------------------
.STACK
END begin
;====================================
; transf -- transfer key to sound freq.
; parameter:AL= input key
; SI= rising KEY
; return:none
;====================================
INCLUDE \MASM61\INCLUDE\DOS.INC
INCLUDE \MASM61\INCLUDE\BIOS.INC
NOTES EQU 28 ; # of notes
.MODEL SMALL
.DATA
key_map DB 'awsedrftgyhujkAWSEDRFTGYHUJK' ; key mapping
count DW 1138/2,1205/2,1277/2,1353/2 ; to freq. counts
DW 1433/2,1519/2,1609/2,1705/2,1 ; reversed
DW 1806/2,1913/2,2027/2,2148/2,2275/2
DW 1138,1205,1277,1353
DW 1433,1519,1609,1705
DW 1,1806,1913,2027,2148,2275
.CODE
PUBLIC transf
EXTERN sound:NEAR, nosound:NEAR
transf PROC ; transf
mov bx,ds
mov es,bx
mov di,OFFSET key_map ; es:di->key_map
mov cx,NOTES ;
repnz scasb ;
jz tt ; if found call sound
call nosound ; else turn off
jmp exit
tt: push cx
mov bl,NOTES
mov bh,0
sub bl,cl
dec bl
mov dl,bl
@SetCsrPos ,17,0
@PutChar key_map[bx],,0,1 ;show key
pop cx
shl cx,1 ; x2 for 2 Byte
mov bx,cx ; bx as index
mov ax,count[bx] ; get count
mov cx,si
shr ax,cl ; rise sound key...
call sound
exit: ret
transf ENDP
.STACK
END
;==============================
; sound -- turn on PC speaker
; parameter:AX= count
; return:none
; nosound -- turn off
; parameter:none
; return:none
;==============================
CNT2 EQU 42h
CNT_CTR EQU 43h
ENA_CTR EQU 61h
.MODEL SMALL
.CODE
PUBLIC sound,nosound ; declare as public
sound PROC
push ax
mov al,10110110b
out CNT_CTR,al ; set mode
pop ax
push ax
out CNT2,al ; low byte
mov al,ah
out CNT2,al ; high byte
in al,ENA_CTR
or al,00000011b
out ENA_CTR,al ; turn on
pop ax
ret
sound ENDP
nosound PROC
push ax
in al,ENA_CTR
and al,11111100b
out ENA_CTR,al ; turn off
pop ax
ret
nosound ENDP
.STACK
END