📄 llsnd.asm
字号:
TITLE LLSND - multivoice music and sound interface
;***
; LLSND - multivoice music and sound interface
;
; Copyright <C> 1986, Microsoft Corporation
;
;Purpose:
;
; This module has the following support routines :
;
; 1. B$DONOTE
; 2. B$TICTOC.... The TIMER interrupt vectors here before
; updating the time of the day. Normal
; frequency of the timer interrupt is 18.2
; times per second. This frequency is changed
; so that it interrupts at 572.4 times per
; second (exactly 32 times faster). This increase
; in speed gives better music performance. The
; basic philosophy in handling music is described
; below :
;
; Music is composed of notes and a note consists of:
; 1. Frequency
; 2. Duration
; The frequency is loaded into the 8253 timer
; chip. The duration is saved in some memory location and each
; time the timer interrupts, we decrement the duration by 1 and
; when the duration becomes zero we are all done. It is
; important to note that the 8253 operates in the mode
; wherein it reloads itself each time it counts down to
; zero.
; We have to remember one important thing, since we
; change the frequency of the TIMER interrupt whenever
; music is playing, we should see to it that the actual
; timer interrupt (one which updates the time of the day)
; gets called once every 32 times our timer interrupt
; gets called. The figure 32 comes from the fact that
; the TIMER is 32 times as fast as what it used to be.
; Also when music stops playing we SHOULD change the
; timer frequency back to its original rate.
; The figure below illustrates how the TIMER interrupt
; ( INT 8H ) is handled :
;
; Timer INT
; vectors
; ------------ IF MUSIC --------------
; | -------|------------------->| B$TICTOC |
; ------------ | ACTIVE | Interrupt |
; | CS | | | service |
; ------------ | | routine for|
; | | handling |
; | | music |
; | | _________|______
; | ELSE IF -------------- |
; | MUSIC NOT |
; | ACTIVE |
; | |
; | ------------ |
; --------------->| ROM Timer | |
; | interrupt |<-------
; | service |
; | routine |
; | updates |
; | time of |
; | day. |
; | |
; | (F000:FEA5)|
; | | INT 1CH
; | -------------------
; | | |
; ------------ |
; |
; |
; ------------ |
; | CLOCK_INT | |
; | This |<-------
; | routine |
; | checks for|
; | timer, pen|
; | and TRIG |
; | traps. |
; | | JUMP TO
; | --------|------------->
; ------------ OLD 1CH
; VECTOR
;
; The routine B$TICTOC calls on the following routines:
;
; 1. B$NXTSND.... This looks to see if any more entries
; are present in the sound queue and if so
; gets the frequency and duration information,
; and starts that note. It also check if any play
; event occured and if so sets the PLYFLG
; and the EVTFLG.
;
;******************************************************************************
INCLUDE switch.inc ;switch file
INCLUDE rmacros.inc
UseSeg _BSS
UseSeg _DATA
UseSeg EV_TEXT
INCLUDE seg.inc
INCLUDE idmac.inc
INCLUDE ibmunv.inc ; include control block & DONOTE input values
INCLUDE intmac.inc
INCLUDE event.inc
CLK_DELTA EQU 8 ; 8 = 256/32
QUE_BYTES EQU 1+2+2 ; Note + Duration + Frequency
sBegin _BSS
globalB b$PLAFLG,,1 ;play event flag
globalB b$PLENBL,,1 ;play trapping enabled or not flag
globalW b$PLYCNT,,1 ;# of notes specified in ON PLAY(n)
globalW b$NOTES,,1 ;keeps running total of the music notes
globalW b$SNDTIM,,1
PUBLIC b$SNDQCB ; Sound control block
b$SNDQCB QUE_CTRL_BLOCK <>
externB b$MUSIC ; defined in LLSCNIO.ASM
externW b$SNQueSeg ;SNINIT - sound queue buffer segment
staticB b$SNDFLG,,1 ;flag set if music started
staticB CLK_TICS,8,1 ;Clock tic modulo counter at any time
externW b$pTrapEvent ; pointer to B$TrapEvent if event code
; present
sEnd _BSS
assumes CS,EV_TEXT
sBegin EV_TEXT
externW b$BASDSG
externNP B$INIQUE
externNP B$QUE
externNP B$DQUE
POPFF MACRO ;; macro to insure that POPF instruction
LOCAL L1,L2 ;; is executed correctly on 286. It seems
JMP SHORT L2 ;;to enable interrupts after POPF instruction
L1: IRET ;; even if the stack image says 'disabled'
L2: PUSH CS
CALL L1
ENDM
;***
;B$DONOTE - Support for sound
;OEM-interface routine
;
;Purpose:
; This routine supports speaker activity. The support is subdivided
; into several subfunctions as follows:
;
; a. Queue a note:
; This function is passed a frequency and a duration. The note is
; queued, if the frequency is legal, otherwise nothing it put in
; in the queue. No attempt to start the speaker is made.
;
; The definition of a legal frequency are machine dependent. For
; an IBM, a frequency must be within the range 37 <= f <= 32767.
;
; b. Queue a rest:
; This function is passed a duration. Stacatto and Normal mode notes
; are built from a note and a short pause. This function is used
; to play the fraction of a note that defines a short pause in case
; of Stacatto and Normal notes.
;
; c. Query if voice is active:
; This function is used to test the current activity of the
; sound queue. If queued items are currently being processed
; in the sound queue, this function code should return
; FF in [AL], else it should return 0 in [AL].
;
; d. Start Music:
; This function requests that notes that have been queued are
; to start playing. Notes should be played until the queue is
; empty. This function can be called if notes are currently
; playing, in which case it simply returns. Notes may be added
; to the queue while it is being played. No event other than
; this subfunction should start music.
;
; e. Stop Music and flush the queue:
; This function requests that the voice be stopped and that
; queue for the voice be flushed.
;
;
; Certain of the above functions must add queue entries. If no room
; remains in the queue then the routine must return immediately to
; specify that the queue is full. This event must not cause speaker
; activity to begin.
;
;Entry:
; [AL] = Function number
; 0: Queue rest
; [DX] = Duration ( 1 = 2.5 milliseconds )
; 1: Queue note
; [CX] = Frequency in Hz
; [DX] = Duration ( 1 = 2.5 milliseconds )
; 2: Query if voice is active
; Return in [AL]
; 0 - voice is not currently active
; FF - voice is active
; FE: Start music
; FF: Stop music and flush queue
;
;Exit:
; PSW.C set indicates that [AL] contains return information
; [AL] = 1 - No room in the queue for this request
; 2 - Illegal function request
; 3 - Unsupported frequency
; PSW.C reset indicates successful completion.
;
;
;Uses:
; Per convention
;
;Preserves:
; BX, CX, DX
;
;Exceptions:
; None.
;****
;
;ALGORITHM:
; case [AL] of
; 0,1 : begin
; B$QNOTE (AX,CX,DX)
; /* queues either the note or the rest */
; end
; 2 : B$QUERY
; /* checks if the voice is active */
; 3 : begin /* in practice, this is ignored */
; B$QSYNC()
; end
; 4,5 : ENVELOPE SHAPE/DURATION
; /* These functions are not supported */
; 6 : $MKNSE /* this function is not supported */
; FC : SOUND ON/OFF /* this function is not supported */
; FD : BEEP ON/OFF /* this function is not supported */
; FE : B$QSTART
; /* starts music */
; FF : B$QFLUSH
; /* stops music and flushes the queues */
; endcase
;
DONOTE_TABLE LABEL WORD ; Dispatch table
DW B$QSTART ; start the music
DW B$QFLUSH ; stop the music & flush the queue
DW B$QNOTE ; queue a rest
DW B$QNOTE ; queue a note
DW B$QUERY ; query if voice is active or not
MAX_DONOTE EQU ($ - DONOTE_TABLE)/2 ; Permissible entry value in AL
cProc B$DONOTE,<PUBLIC,NEAR>,<BX,CX,DX,SI>
cBegin
MOV BX,OFFSET DGROUP:b$SNDQCB ; get addr of music queue in BX
CMP b$SNDFLG,0 ;initialization been done?
JNZ DONOT1 ;jump if already initialized
INC b$SNDFLG ;mark as initialized
PUSH AX
XOR AX,AX ;music queue starts at zero
MOV [BX].QUEBOT,AX ; set the bottom
cCALL B$INIQUE ; set put, get, num, note..
MOV AX,QLENTH ; get length
MOV [BX].QUELEN,AX ; and set it in the block
MOV [BX].QUETOP,AX ; to set the top of queue.
POP AX
DONOT1:
CBW ; AH = 0/-1
MOV SI,AX ; SI will be used to point to the dispatcher
INC SI ; Add 2 to SI to make it 0-relative
INC SI
CMP SI,MAX_DONOTE ; Check if arg within range
JA DONOTE_ERR ; Brif no - issue error
SHL SI,1 ; Make it a word index
CLD
cCALL DONOTE_TABLE[SI] ; Invoke respective handler
JNC DONOTE_EXIT ; No error - else error code is in DL
XCHG AX,DX ; Else get error code in AL
DONOTE_EXIT: ; Common exit point
cEnd ; End of B$DONOTE
DONOTE_ERR: ; Error case
MOV AL,2 ; Issue voice-id invalid
STC ; CF = 1 indicates error
JMP SHORT DONOTE_EXIT ; Exit...
PAGE
;***
;B$QNOTE
;
;PURPOSE:
; This routine queues either a note or a rest. By rest we mean
; the short pause that goes to make Stacatto and Normal notes.
; This routine calls a number of device dependent routines. These
; device dependent routines check for the validity of frequency and
; duration. They also output these quantities to the 8253 timer chip.
; It either queues in the whole note or does'nt queue at all.
;
;ENTRY:
; [BX] = QUEUE BLOCK BASE ADDR
; [CX] = Frequeuncy
; [DX] = Duration
; [AL] = 0 OR 1 depending on whether it is a rest or
; a music note respectively.
;
;EXIT:
; [DL] = Error codes as specified by the routine B$DONOTE
;
;MODIFIED:
; None
;
;****
cProc B$QNOTE,<NEAR>,<AX,ES>
localV QBLK,QUE_BYTES ; This block is used to temporarily
; queue the note as it is processed
cBegin
OR AL,AL ; was it a note ?
JNZ QNOTE ; brif so
SUB CX,CX ; Else queue in very high frequency
QNOTE:
CMP [BX].QUENUM,QLENTH-QUE_BYTES ; Is there enough space?
JA QUE_ERR1 ; Brif not
LEA SI,QBLK ; [si] = address of qblk
MOV BYTE PTR [SI],AL; temporarily queue the attribute
CHECK_DURATION:
; [DX] = duration (1 = 2.5 millisecs)
; [AX] = [AX] * 1.5
MOV AX,DX ;copy duration for adjustment
SHR AX,1 ;divide by 2
ADD AX,DX ;duration = 1.5 * original duration
JNC DUROK ;Brif no overflow
SBB AX,AX ; else use maximum duration
DUROK:
MOV WORD PTR [SI+1],AX ; temporarily queue the duration
CHECK_FREQ:
; check for valid frequency and convert it to tics
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -