⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 llsnd.asm

📁 [随书类]Dos6.0源代码
💻 ASM
📖 第 1 页 / 共 2 页
字号:
	CMP	CX,37D		;check for valid frequency (37 <= CX <= 32767)
	JGE	FRQOK		; Brif in range
	MOV	DL,3		; Error code in AL (if needed)
	INC	CX		; If zero, then set it to some high value
	LOOP	QUE_ERR2	; Brif not zero - Error in freq value

FRQ_0:
	NOT	CX		; CX = -1 ; Play an inaudible frequency

FRQOK:				; Convert frequency to tics
	MOV	AX,34DCH	; 1.193180 MHz
	MOV	DX,12H		
	DIV	CX		; [AX] = COUNT = CLOCK/FREQUENCY
	CLC			; CF = 0

	MOV	WORD PTR [SI+3],AX ; temporarily queue the frequency
	MOV	ES,b$SNQueSeg	;music queue has its own buffer segment
	MOV	CX,QUE_BYTES	; 5 bytes to be queued
	CLI			;CLI,bcos this is an indivisable operation

QUETHEM:			;[si] = address of QBLK

.erre	ID_SSEQDS		; Assert SS = DS

	LODSB			; [al] = byte to be queued
	cCALL	B$QUE		; queue the byte
	LOOP	QUETHEM

	MOV	AL,BYTE PTR QBLK ; get the note/rest (1/0)
	CBW			; AH = 0
				; For rest, the following additions are NOPs
	ADD	[BX].QUNOTE,AX	; Suitably update # of notes in queue
	ADD	b$NOTES,AX	  ; and total note count
	CLC			; Clear carry to indicate no error

QUERET:
	STI			;restore interrupts

cEnd				; End of B$QNOTE

QUE_ERR1:
	MOV	DL,1		; error code in [al]
QUE_ERR2:
	STC			; indicate error
	JMP	SHORT QUERET

	PAGE

;***
;B$QUERY
;
;PURPOSE:
;       Checks to see if any voices are active
;
;ALGORITHM:
;	If the timer is turned on of off frequently, then an irritating
;	'click' sound is heard between two play statements. In order to
;	avoid this 'click', when (b$NOTES | b$SNDTIM) > 2 then say that
;	music is inactive. This lets Hi-Level to queue the next note
;	and the speaker is not turned off in-between.
;
;ENTRY:
;       None
;
;EXIT:
;       [AL] = 0 if voice is not currently active
;              FF if voice is active
;
;MODIFIED:
;       None
;
;****
cProc	B$QUERY,<NEAR>	
cBegin				

	MOV	AX,b$NOTES	; Get number of notes (may be zero)
	OR	AX,b$SNDTIM	; Add any music-ticks still left
	CMP	AX,2		; If zero or 1 then zero else -1
	CMC			; CF =1 if AX > 2
	SBB	AL,AL		; AL = -1 if speaker active
	CLC			; No error for this call


QUERY_RET:			; Just exit...

cEnd				; End of B$QUERY


	PAGE

;***
;B$TICTOC
;
;PURPOSE:
;       The timer interrupt, vectors here BEFORE updating the
;       time of the day. This routine helps support the
;       Music routines. B$TICTOC keeps decrementing the duration
;       count until it becomes zero at which point it
;       either gets the next sound from the sound
;       queue or else it turns off the voice by calling B$QFLUSH.
;ENTRY:
;       None
;
;EXIT:
;       None
;
;MODIFIED:
;       None
;
;****

cProc	B$TICTOC,<NEAR>	
cBegin				

	STI			; Enable interrupts now itself - there
				; is no chance of another timer intterupt
				; occurring since EOI is sent only later.
	PUSH	AX
	PUSH	BX
	PUSH	CX		; Save temp registers AX,BX,CX
	PUSH	DX		; Save this also
	PUSH	DS
	PUSH	ES

	MOV	DS,CS:b$BASDSG ;get BASIC's data seg
	MOV	ES,b$SNQueSeg	;point to music buffer

	XOR	AX,AX		; Get ready for some zero comparisons
	CMP	b$SNDTIM,AX	; has SND_TIM zeroed out?
	JZ	NXTONE		;Brif so to get next sound
	DEC	b$SNDTIM	; SND_TIM - 1
	JNZ	CLK_TIC
NXTONE:
	MOV	BX,OFFSET DGROUP:b$SNDQCB ; BX = address of music queue	
	CMP	[BX].QUENUM,AX	; Is the queue empty
	JE	TURN_OFF	; If so, then turn music off
	cCALL	B$DQUE		; Get first entry in queue
	OR	AL,AL		; Check if it is note or rest
	JZ	GETSND1		; Brif rest : no need to check play trap
				; AL = 1 for note
	CMP	b$PLENBL,AL	; Check if play trapping is enabled
	JNE	GETS1		; Brif disabled
	MOV	AX,b$PLYCNT	; Get the number of notes set for trapping
	CMP	[BX].QUNOTE,AX	; and compare it with current # of notes
	JNE	GETS1		; Brif event has not occurred
	MOV	b$PLAFLG,1	; Else set the flag
	MOV	AL,PLAOFF	; AL = trap number for B$TrapEvent
	CALL	[b$pTrapEvent]	; set the global flag if events linked in

GETS1:
	DEC	[BX].QUNOTE	; One more note has been played
	DEC	b$NOTES	; update overall count also

GETSND1:
	cCALL	B$DQUE		; Get the duration LSB
	XCHG	AX,CX		; CL = LSB
	cCALL	B$DQUE		; Get the duration MSB
	MOV	CH,AL		; CH = MSB
	MOV	b$SNDTIM,CX	
	cCALL	B$DQUE		; Get the frequency LSB
	OUT	TIMER2,AL	; & set the timer counter Lo-byte
				; 8253 will wait until hi-byte is loaded.
	cCALL	B$DQUE		; Get the frequency MSB
	OUT	TIMER2,AL	; & set the timer counter Hi-byte
	JMP	SHORT CLK_TIC	; All done

TURN_OFF:			; Turn off the music
	PUSH	DS		; B$FLUSH requires ES = DS
	POP	ES		
	cCALL	B$QFLUSH	

CLK_TIC:
	SUB	CLK_TICS,CLK_DELTA ; clock tick  -= CLK_DELTA
				; wait for 32 interrupts to invoke ROMCLK
	POP	ES
	POP	DS
	POP	DX		
	POP	CX
	POP	BX
	JNZ	CLKTIX		;don't do ROM clock INT now
	POP	AX
	INT	ROMCLK		;to ROM clock INT service routine
	IRET			

CLKTIX:
	MOV	AL,EOI		; send End-of-Interrupt
	OUT	INTA0,AL	; to 8259
	POP	AX		
	IRET

cEnd	<nogen>			; End of B$TICTOC

	PAGE

;***
;B$QSTART
;
;PURPOSE:
;       This routine starts the music. It does the following
;       things:
;       1. Change the interrupt vector to point at our handler
;       2. Modify timer2 to interrupt 32 times faster
;       3. Turn on the speaker and start timer2 only if timer2
;          is active.
;
;ENTRY:
;       None
;
;EXIT:
;       None
;
;MODIFIED:
;       None
;
;****
cProc	B$QSTART,<NEAR>,<AX>	
cBegin				

	MOV	AH,01		; Comes useful
; DON'T reset the counter every time: only once at program load.  This is
; a modulo counter that should be retained.
;	MOV	CLK_TICS,CLK_DELTA	; Reset the counter
	PUSHF			; Save caller's IF
	CLI
	IN	AL,MSKREG	;get IMR into [AL]
	OR	AL,AH		;mask out timer interrupt
	PAUSE			;make sure instruction fetch has occurred
	OUT	MSKREG,AL	;write mask to IMR

	XCHG	b$MUSIC,AH	; get b$MUSIC flag and set it to 1
	OR	AH,AH		; Check if music is already on
	JNZ	STRTMXT		; Brif so - timer is already changed

	PUSH	DS
	PUSH	CS
	POP	DS		;[DS] := [CS]
	SETVEC	CLKINT/4,B$TICTOC ;modify timer2 interrupt vector
	POP	DS
	MOV	AL,0		;modify timer2 to interrupt
	OUT	TIMER0,AL	;at 32 times the
	MOV	AL,8H		;original
	PAUSE			;make sure instruction fetch has occurred
	OUT	TIMER0,AL	;rate
	MOV	AL,SQUARE	;else set timer2 in square
	PAUSE			;make sure instruction fetch has occurred
	OUT	TMRCMD,AL	;wave mode
	PAUSE			;make sure instruction fetch has occurred
	IN	AL,SPEAKER	;turn on the
	OR	AL,SPKRON	;speaker
	PAUSE			;make sure instruction fetch has occurred
	OUT	SPEAKER,AL
	PAUSE			;make sure instruction fetch has occurred

STRTMXT:
	IN	AL,MSKREG	;get IMR into [AL]
	AND	AL,0FEH		;unmask timer interrupt
				; CF = 0 to indicate no error
	PAUSE			;make sure instruction fetch has occurred
	OUT	MSKREG,AL	;write mask to IMR
	POPFF			; Restore caller's IF

cEnd				; End of B$QSTART

;***
;B$QFLUSH
;
;PURPOSE:
;	This routine stops music, initializes the music block, resets the timer
;	count, restores the original timer ISR and clears the flag b$SNDTIM
;
;ENTRY:
;	None
;
;EXIT:
;	None
;
;MODIFIED:
;	None
;
;****
;
cProc	B$QFLUSH,<NEAR>,<AX>	
cBegin				

	PUSHF			; Save caller's IF
	CLI
	IN	AL,MSKREG	; get IMR into [AL]
	OR	AL,01H		; mask out timer interrupt
	PAUSE			; make sure instruction fetch has occurred
	OUT	MSKREG,AL	; write mask to IMR
	cCALL	B$SNDOFF	
	JMP	STRTMXT		; Share code

cEnd	<nogen>			; End of B$QFLUSH

; B$SNDOFF moved here from LLQUE.ASM to increase /O modularity
; revision [6] applies to the entire routine:

;***
;B$SNDOFF - Turn off Sound and Clean Up
;OEM-interface routine
;
;Purpose:
;	This routine stops music and flushes the music queue(s).
;	It also performs all needed functions to disable sound.
;	B$SNDOFF is called before the RUN command is executed
;	and at program termination.
;
;	The sound queue is allocated and deallocated by the
;	runtime, so this routine need not worry about it.  The
;	queue will exist when this routine is called.
;
;Entry:
;	None
;
;Exit:
;	None
;
;Uses:
;	Per Convention
;
;Preserves:
;	AX, BX, CX, DX
;
;Exceptions:
;	None.
;***************************************************************************

DB	"<LEO>"			; This is used via a separate tool to
				; actually modify the .EXE file to stub
				; out B$SNDOFF. This is necessary for
				; profiling, as B$SNDOFF modifies the
				; timer hook, as does the profiler.
				; (chosen string pure vanity on Leo's part)

cProc	B$SNDOFF, <PUBLIC,NEAR>,<AX,BX>	
cBegin					
	XOR	AX,AX		;zero out [AX]
	MOV	b$NOTES,AX	; zero out b$NOTES
	MOV	b$SNDTIM,AX	; SND_TIM = 0
	MOV	b$MUSIC,AL	;music is currently OFF
	MOV	BX,OFFSET DGROUP:b$SNDQCB ; Get music block offset
	cCALL	B$INIQUE 	; init music queue at zero offset
	
	IN	AL,SPEAKER
	AND	AL,NOT SPKRON	;turn off speaker
	PAUSE			;ensure instruction fetch has occurred
	OUT	SPEAKER,AL
	XOR	BX,BX		; CF = 0 to indicate no error
	PUSH	DS
	MOV	DS,BX		; [DS] = interrupt vectors segment
	PUSHF			; Save flags
	CLI			;make sure interrupts are off
	SVINT	DS:CLKINT,DS:CLKVEC
	POPFF			; Restore flags
	POP	DS
	XCHG	AX,BX		; AX = 0
	OUT	TIMER0,AL	;restore timer2 count
	PAUSE			;ensure instruction fetch has occurred
	OUT	TIMER0,AL

cEnd				; End of B$SNDOFF

sEnd	EV_TEXT 		
	END

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -