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

📄 gwplays.asm

📁 [随书类]Dos6.0源代码
💻 ASM
📖 第 1 页 / 共 2 页
字号:
	DB	"C"
	DW	OFFSET PLYNOT
	DB	"D"
	DW	OFFSET PLYNOT
	DB	"E"
	DW	OFFSET PLYNOT
	DB	"F"
	DW	OFFSET PLYNOT
	DB	"G"
	DW	OFFSET PLYNOT

	DB	"M"		;Music Meta Command
	DW	OFFSET B$PLYMET

;	DB	"Q"		;Envelope Subcommand Lead In
;	DW	OFFSET QCMNDS

	DB	"N"+128 	;PLAY NUMERIC NOTE
	DW	OFFSET PLYNUM
	DB	"O"+128 	;OCTAVE
	DW	OFFSET POCTAV
	DB	"P"+128 	;PAUSE
	DW	OFFSET PPAUSE
	DB	"T"+128 	;TEMPO
	DW	OFFSET PTEMPO
	DB	"L"+128 	;LENGTH
	DW	OFFSET PLYLEN

;	DB	"V"+128 	;Volume
;	DW	OFFSET PVOLUM

	DB	"X"		;EXECUTE STRING
;	DW	OFFSET B$MCLXEQ	; substring handler in MCLPRC
	DW	OFFSET B$PLYXEQ	; Substring handler is local

	DB	"<"		;Decrement Octave
	DW	OFFSET POCTAD
	DB	">"		;Increment Octave
	DW	OFFSET POCTAI
	DB	00		;END OF TABLE


; This table contains the allowed subcommands for the Q command
; in the Music Macro Language.

;	This table has been removed as part of PC Jr code removal

;B$PLYXEQ
;Purpose:
;	Reimplemented as part of revision [5]
;	This routine is dispatched to by the X command in the PLAY statement.
;	It is equivalent to a macro-language subroutine call, in that it
;	specifies a variable which is to be inserted in the Macro String.  It:
;
;	 1)  Calls B$GETSTKC to check for enought stack space.
;	 2)  Calls B$SCNVAR to get string descriptor in the FAC
;	 3)  Calls B$PUTSTR to stack the current string pointer & length,
;	 4)  Sets B$MCLPTR & B$MCLLEN to point to new nested string,
;	 5)  Returns to its caller (presumably PLAY (via B$MACCMD)

cProc	B$PLYXEQ,<NEAR>	
cBegin				

	MOV	CL,100		; Get size for stack check
	CALL	B$GETSTKC	; Check the stack for enough room
	CALL	B$SCNVAR	; Get the VARPTR$ descriptor offset in FAC
	CMP	[b$VTYP],VT_SD	; Test if type was a string
	JNE	PLYERR		; Brif not, signal an error
	cCALL	B$PUTSTR	; Put B$MCLPTR & B$MCLLEN on local stack
	MOV	BX,OFFSET DGROUP:B$AC	; Get FAC for descriptor
	CALL	B$SETMCL		; Set new values of B$MCLPTR & B$MCLLEN
	CLC			; To indicate to use new values

cEnd				; End of B$PLYXEQ

PLYERR:				; Indicate type mismatch error

	JMP	B$ERR_TM	

; Decrement the current octave number
POCTAD: CMP	B$OCTAVE,LOW 0	
	JZ	PLYRET
	DEC	B$OCTAVE		; octave -1
	RET

; Increment the current octave number
POCTAI: cmp	B$OCTAVE,low 6	
	JNB	PLYRET
	CLC
	INC	B$OCTAVE		; octave +1
	RET

; Set the volume level

; Volume support code has been deleted from here

; Set the note length
PLYLEN:
	JNB	PLGOFC		;ERROR IF NO ARG
	CMP	DL,LOW 65	;ALLOW ONLY UP TO 64
	JNB	PLGOFC		;FC ERROR IF TOO BIG
	OR	DL,DL		;DON'T ALLOW ZERO
	JZ	PLGOFC		;FC ERROR IF ZERO
	MOV	B$NOTELN,DL	; store note length
	RET

; Set the play tempo
PTEMPO:
	CMP	DL,LOW 32	;ALLOW ONLY 32 - 255
	JB	PLGOFC		;FC ERROR IF TOO SMALL
	mov	B$BEATS,dl	; store beats per minute
	CLC
	RET

; Play a rest (Pause command)
PPAUSE:
	JNB	PLGOFC		;ERROR IF NO ARG
	XOR	CX,CX		;PASS FREQ OF 0
	CMP	DL,LOW 65	;ALLOW ONLY 1-64
	JNB	PLGOFC		;FC ERROR IF TOO BIG
	OR	DL,DL		;SEE IF ZERO
	JZ	PLYRET		;RETURN IF SO - NO PAUSE
	JMP	PPAUS2		;[DX]=PAUSE LENGTH

; Set the current octave number
POCTAV:
	JNB	PLGOFC		;ERROR IF NO ARG
	CMP	DL,LOW 7	;ALLOW ONLY OCTAVES 0..6
	JNB	PLGOFC		;FC ERROR IF TO BIG
	mov	B$OCTAVE,dl	
	CLC
PLYRET: RET

; Play a particular note by note number
PLYNUM:
	JNB	PLGOFC		;ERROR IF NO ARG
	MOV	AL,DL		;GET NOTE NUMBER INTO [AL]
	OR	AL,AL		;SEE IF ZERO (PAUSE)
	JZ	PLYNO3		;DO THE PAUSE
	CMP	AL,LOW 85	;ALLOW ONLY 0..84
	JNB	PLGOFC		;FC ERROR IF TOO BIG
	CBW			;CLEAR HI BYTE FOR DIVIDE
	DEC	AX		;MAP TO 0..83
	MOV	DL,LOW 12	;DIVIDE BY 12
	DIV	DL
	MOV	DH,AL		;OCTAVE TO [DH]
	MOV	AL,AH		;NOTE NUMBER IS REMAINDER
	INC	AL		;ADD ONE
	ADD	AL,AL		;DOUBLE TO MAKE INDEX
	JMP	SHORT PLYNU3	;PLAY NOTE [AL], OCTAVE [DH]

PLGOFC: JMP	B$ERR_FC	; GIVE FUNCTION CALL ERROR

; Play a note by name
PLYNOT: SUB	CL,LOW "A"-1	;MAP TO 1..7
	ADD	CL,CL		;MAP TO 2..14 (THIS ASSUMES SHARP)
	CALL	B$FETCHR 	;GET NEXT CHARACTER
	JZ	PLYNO2		;END OF STRING - NO SHARP OR FLAT
	CMP	AL,LOW "#"	;CHECK FOR POSSIBLE SHARP
	JZ	PLYSHP		;SHARP IT THEN
	CMP	AL,LOW "+"	;"+" ALSO MEANS SHARP
	JZ	PLYSHP
	CMP	AL,LOW "-"	;"-" MEANS FLAT
	JZ	PLYFLT
	CALL	B$DECFET 	;PUT CHAR BACK IN STRING.
	JMP	SHORT PLYNO2	;TREAT AS UNMODIFIED NOTE.
PLYFLT: DEC	CL		;DECREMENT TWICE TO FLAT IT
PLYNO2: DEC	CL		;MAP BACK TO UNSHARPED
PLYSHP: MOV	AL,CL		;INTO [AL] FOR XLAT
	MOV	BX,OFFSET NOTXLT ;POINT TO TRANSLATE TABLE
	XLAT	BYTE PTR CS:[BX] ; TRANSLATE INTO NOTE TABLE INDEX
	OR	AL,AL		;SEE IF LEGAL NOTE
	JS	PLGOFC		;NOTE'S OK IF NOT .GT. 127

; ENTER HERE WITH NOTE TO PLAY IN [AL]
; NOTE 0 IS PAUSE, 2,4,6,8..10,12 ARE A-G AND FRIENDS.

PLYNO3:
	mov	dh,B$OCTAVE	; get B$OCTAVE into [dh] for later math
PLYNU3:
	PUSH	AX		;Save Note
	PUSH	DX		; Save Octave
	MOV	AL,B$NOTELN
	MOV	B$NOTE1L,AL	; one note duration = note length
	CALL	B$FETCHR
	JZ	PLYNU4		;Brif end of string
	CALL	B$VALSC2 	;See if possible number
	CMP	DL,LOW 65	;If was .gt. 64
	JNB	PLGOFC		; then error
	OR	DL,DL		;Any Length?
	JZ	PLYNU4		;Brif not, just do note
	MOV	B$NOTE1L,DL	; store duration for this note
PLYNU4:
	POP	DX		;Get Octave
	POP	AX		;Restore Note
	CBW			;FILL [AH] WITH ZEROS
	MOV	BX,AX		;TRANSFER TO BX FOR INDEXING
	OR	BX,BX		;SEE IF PAUSE (NOTE # 0)
	JZ	PLYNO4		;IF PAUSE, PASS [BX]=0
	MOV	BX,WORD PTR CS:NOTTAB-2[BX] ;FETCH FREQUENCY
	MOV	CL,LOW 6	;CALCULATE 6-OCTAVE
	SUB	CL,DH		;FOR # OF TIMES TO SHIFT FREQ.
	SHR	BX,CL		;DIVIDE BY 2^(6-OCTAVE)
	ADC	BX,0		;ADD IN CARRY TO ROUND UP
PLYNO4:
	MOV	CX,BX		;FREQUENCY INTO [CX] FOR DONOTE
	MOV	DL,B$NOTE1L	; get this note's length
PPAUS2:
	MOV	AL,B$BEATS	; GET BEATS PER UNIT TIME
	MUL	DL		;CALC NOTE LENGTH * B$BEATS
	PUSH	CX		;SAVE [CX] WHILE WE DIVIDE
	MOV	CX,AX		;CALC TIME CONST/(B$BEATS * NOTE LENGTH)
	MOV	DX,1		;96000 (4*60*400) is
	MOV	AX,73400O	;SPECIAL TIME CONSTANT
	DIV	CX
	POP	CX		;RESTORE FREQUENCY
	OR	AX,AX		;IF DURATION IS ZERO, GET OUT.
	JZ	PLYNO8
	PUSH	CX		;Save Freq

PLYDOT:
	MOV	CX,AX		; Copy of duration for doted notes
PLYDOT1:
	PUSH	AX		; Save duration
	PUSH	CX		; Save the current dot duration
	CALL	B$FETCHR
	JZ	PLYDOX		; Brif EOS
	CMP	AL,LOW "."	; Note duration extender?
	JNZ	PLYDO2		; Brif not
	POP	CX		; Get last dot duration
	POP	AX		; Get current duration
	SHR	CX,1		; This dot = previous dot / 2
	ADD	AX,CX		; Update the new duration
	JNB	PLYDOT1 	; Loop if not overflow
	JMP	B$ERR_FC	; else complain.... (wont return)

PLYDO2:
	CALL	B$DECFET 	; Put char back
PLYDOX:
	POP	AX		; Trash the dot duration
	POP	AX		; Duration
	POP	CX		; Get freq
	PUSH	AX		;Save Duration
	PUSH	CX		;Save Frequency
	JCXZ	PLYNO7		;Brif Pause
	CMP	B$MSCALE,LOW 1	
	JZ	PLYNO7		;Brif Legatto
	MOV	CL,B$MSCALE	; using scale for shift count
	MOV	BX,3		;Stacatto multiplier
	CMP	CL,LOW 2
	JZ	PLYNO6		;Brif Stacatto
	MOV	BX,7		; else Normal
PLYNO6:
	MUL	BX		;Duration * 7/8 or 3/4
	SHR	AX,CL
	OR	AX,AX
	JNZ	PLYNO7		;If zero
	INC	AX		; then make 1

; Have all of the parameters for this note.  Send the info to the OEM
; to queue the note.
; Because a note is sent via two separate commands (one for the first,
; sound generating, part of the note, and a second one for the inter-
; note pause) it is possible for the queue to overflow in the middle of
; the note.  It isn't possible to simply wait for space to become available
; in the queue, because there is no guarantee that the queue is being
; emptied.  So to handle this case the following things happen:
;	If the queue overflows on either half of the note, the carry
;	flag is returned set as a signal to the music string B$PARSER that
;	the present command needs to be rescanned the next time around
;	If the first half of the note is sent successfully, a flag is
;	set (B$NOTFLG[SI]) indicating that it has been sent.  When the
;	second half of the note is sent successfully, then this flag is
;	cleared indicating that the entire note has been sent.	Before
;	sending the first half, it is necessary to check the flag to see
;	if this part has already been passed to the OEM on a previous
;	pass through the B$PARSER.
PLYNO7:
	POP	CX		;Get Freq
	CMP	B$NOTFLG,LOW 0 	; has the first part of this note already
				;been queued
	JNZ	PLYN7B		;If so, don't send it again
	cCALL	B$SNDNOT		;Send note
	JNB	PLYN7A		;If no queue overflowed, continue
	POP	AX		;If queue overflowed, get out, not even
	JMP	SHORT PLYNO9	; the first part of note was queued.

PLYN7A:
	mov	B$NOTFLG,low 1 	; set flag to say that note has been sent
PLYN7B:

; Now send an inter-note pause for this note (if required)
	POP	AX		;Get back original duration
	JCXZ	PLYNO8		;Brif Pause
	CMP	B$MSCALE,LOW 1	
	JZ	PLYNO8		;Brif Legatto
	MOV	CL,B$MSCALE	; scale factor for current mode (1/8|1/4)
	SHR	AX,CL		;divide note duration by scale factor
	OR	AX,AX		;Pause = 0?
	JZ	PLYNO8		;Don't send anything if so.
	cCALL	B$SNDPSN		;Send the rest
	JB	PLYNO9		;If queue overflowed, don't reset
				; flag for this note
PLYNO8:
	MOV	B$NOTFLG,LOW 0 	; clear the flag to indicate that the
				;complete note has been queued
PLYNO9:
	RET			; else do nothing

;***
; B$SNDNOT,B$SNDPSN
; Purpose:
; Send the specified note information to the OEM routine to be queued.
; B$SNDNOT will queue the first part of a note.
; B$SNDPSN will queue the second (inter-note pause) part of a note.
; Entry:
;	AX	- Duration
;	CX	- Frequency
; Exit:
;	none
; Modifies:
;	SI, DI, CX preserved.

; The registers must be set up as follows for the call to B$DONOTE
; [AL] = B$DONOTE function code number (1 for note, 0 for inter-note pause)
; [AH] = Voice
; [BX] = Volume
; [CX] = FREQUENCY IN HERTZ
; [DX] = DURATION IN CLOCK TICKS (1/18.2 SECONDS)

;****

cProc	B$SNDNOT,<NEAR>	
cBegin				

	XCHG	DX,AX		; B$DONOTE wants duration in DX

;	MOV	BX,B$VCEVOL	; Volume into bx

	MOV	AL,LOW QUENOT	;B$DONOTE function code in AL

; Test if this is the first time a note has been parsed in this play
; statement, and if so, queue sync marks in all voice queues.
	CMP	MQUEFL,LOW 0
	JNE	SDNT20		;branch if cmds have already been queued
				; for this PLAY statement
	MOV	MQUEFL,LOW 1


	JMP	SHORT SDNT20

cEnd	<nogen>			; End of B$SNDNOT


; Queue an internote pause (rest) for the current note

cProc	B$SNDPSN,<NEAR>	
cBegin				

	XCHG	DX,AX		; B$DONOTE wants duration in DX

	MOV	AL,LOW QUERST	;B$DONOTE function code in AL

; Send the instruction to the OEM, and test for any error codes coming
; back
SDNT20:
	cCALL	B$DONOTE 	; PLAY THE NOTE
	JNB	MQD90		;If no error occured, then go on
	CMP	AL,LOW 1	;Test for overflow on this voice
	JNE	PLYMER		;If not queue full, then report error
MQD80:
	STC			;Set error return state
MQD90:

cEnd				; End of B$SNDPSN


; Function call error occured while processing Music Meta command.
PLYMER:
	JMP	B$ERR_FC	; wont return

; B$PLYMET -	Process Music Meta Commands.

cProc	B$PLYMET,<NEAR>	
cBegin				

	CALL	B$FETCHZ 	;Get Meta action or error
	MOV	CL,LOW 1	;Factor for Legatto (1/1)
	CMP	AL,LOW "L"
	JZ	PLYDUR		;Brif Legatto (Full note)
	INC	CL		;Factor for Stecatto (3/4)
	CMP	AL,LOW "S"
	JZ	PLYDUR		;Brif Stecatto (3/4)
	INC	CL		;Factor for Normal (7/8)
	CMP	AL,LOW "N"
	JZ	PLYDUR		;Brif Normal (7/8)
	XOR	CL,CL
	CMP	AL,LOW "F"
	JZ	PLYMOD		;Brif Foreground Music
	DEC	CL
	CMP	AL,LOW "B"
	JNZ	PLYMER		;Brif not Background Music
PLYMOD:
	MOV	[B$MMODE],CL	;Store Music Mode (0=FG, 255=BG)
	JMP	SHORT PLYMET_RET	

PLYDUR:
	MOV	B$MSCALE,CL	; store duration scaling factor

PLYMET_RET:			; Common exit point

cEnd				; End of B$PLYMET


; This is the executive for dispatching the Q command.	It sets
; up the Macro command processor table to point to the Q subcommand
; table, and then uses the macro command processor to dispatch to
; the appropriate routine to process the subcommand.

; Envelope support code has been deleted from here


; TABLE OF INDEXES INTO NOTTAB FOR EACH NOTE
; VALUE OF 255 MEANS NOTE NOT ALLOWED.

NOTXLT	LABEL	BYTE		
	DB	9*2		;A- (G#)
	DB	10*2		;A
	DB	11*2		;A#
	DB	12*2		;B
	DB	255		;NO C- OR B#
	DB	1*2		;C
	DB	2*2		;C#
	DB	3*2		;D
	DB	4*2		;D#
	DB	5*2		;E
	DB	255		;NO E# OR F-
	DB	6*2		;F
	DB	7*2		;F#
	DB	8*2		;G
	DB	9*2		;G#

; TABLE OF NOTE FREQUENCIES
; THESE ARE THE FREQUENCIES IN HERTZ OF THE TOP OCTAVE (6)
; DIVIDED DOWN BY POWERS OF TWO TO GET ALL OTHER OCTAVES

NOTTAB	LABEL	WORD		
	DW	4186		;C
	DW	4435		;C#
	DW	4699		;D
	DW	4978		;D#
	DW	5274		;E
	DW	5588		;F
	DW	5920		;F#
	DW	6272		;G
	DW	6645		;G#
	DW	7040		;A
	DW	7459		;A#
	DW	7902		;B

;***
;B$QSYNC
;Purpose:
;	Output a Syncronization Byte to all voices
;Input:
;	none
;Output:
;	A SYNC byte is put in voice [VOICEN]'s queue
;Modifies:
;	none
;****

;	SYNC byte is output only if multi-voice support is present.
;	For single-voice music it is not necessary and hence not output.


sEnd	SN_TEXT 		

	END

⌨️ 快捷键说明

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