gwplays.asm
上传用户:xiaoan1112
上传日期:2013-04-11
资源大小:19621k
文件大小:26k
源码类别:

操作系统开发

开发平台:

Visual C++

  1. TITLE GWPLAYS - GW BASIC 2.0 Multi Voice Play
  2. ;***
  3. ; GWPLAYS - GW BASIC 2.0 Multi Voice Play
  4. ;
  5. ; Copyright <C> 1986 - 1988, Microsoft Corporation
  6. ;
  7. ;Purpose:
  8. ; Multi Voice Play statement processor
  9. ;
  10. ; BASIC Syntax mapping to included runtime entry points:
  11. ;
  12. ; - PLAY Statement:
  13. ;
  14. ;      PLAY string
  15. ;  |
  16. ;      B$SPLY
  17. ;
  18. ;    NOTE: the more advanced syntax of 'PLAY string, string, ...' (i.e.
  19. ;    multi-voiced PLAY) will be accepted by the compiler. For all but the
  20. ;    last parameter (which is passed to B$SPLY), defaulted parameters will
  21. ;    generate a call to B$PL1, and specified parameters get passed to B$PL0
  22. ;    both of these, in turn, just generate an Advanced Feature error.
  23. ;    NOTE 2: the above comment doesn't apply to bascom 30.  The compiler
  24. ; will not generate calls to B$PL0 and B$PL1.  They will be flagged as
  25. ; syntax errors. B$PL0 and B$PL1 will not be present for BASCOM 30.
  26. ; The code to support multivoice play will be left for future reference,
  27. ; but it will need to be changed to the new style interface (stack based)
  28. ; if a multi voice version is ever produced.
  29. ;
  30. ;******************************************************************************
  31. INCLUDE switch.inc ; switch file [new]
  32. INCLUDE rmacros.inc ; Runtime Macro Defintions
  33. USESEG _DATA
  34. USESEG _BSS
  35. USESEG SN_TEXT 
  36. INCLUDE seg.inc 
  37. INCLUDE ibmunv.inc
  38. INCLUDE rtps.inc
  39. INCLUDE idmac.inc
  40. INCLUDE string.inc
  41. .RADIX 10
  42. ; Two new macros Alloc & DeclareB/DeclareW are defined here.
  43. ; The main purpose of Declare macro is to allocate given number of
  44. ; bytes (static variable) and initialize it to the specified value.
  45. ; Additionally, it declares an EQUate for the specified variable
  46. ; for automatic indexing if FS_MVOICE is TRUE. The index register
  47. ; assumed is SI. Actually, the variable defined has a trailing underscore
  48. ; and the indexed EQUate is identical to the name supplied to this macro.
  49. ; Alloc is just a helper macro.
  50. Alloc MACRO v,nm,init,siz
  51. static&v <nm&_>,<init>,<siz>
  52. nm EQU nm&_
  53. ENDM
  54. DeclareB MACRO nm,init,siz
  55. Alloc B,<nm>,<init>,<siz>
  56. ENDM
  57. DeclareW MACRO nm,init,siz
  58. Alloc W,<nm>,<init>,<siz>
  59. ENDM
  60. sBegin _DATA
  61. externW B$AC
  62. sEnd
  63. sBegin _BSS
  64. externW B$MCLTAB  ;defined in GWDATA.ASM
  65. externW B$MCLLEN  ;defined in GWDATA.ASM
  66. externW B$MCLPTR  ;defined in GWDATA.ASM
  67. externB B$MMODE ;defined in GWINI.ASM
  68. externB B$BEATS ;defined in GWINI.ASM
  69. externB B$NOTE1L  ;defined in GWINI.ASM
  70. externB B$NOTELN  ;defined in GWINI.ASM
  71. externB B$NOTFLG  ;defined in GWINI.ASM
  72. externB B$MSCALE  ;defined in GWINI.ASM
  73. externB B$OCTAVE  ;defined in GWINI.ASM
  74. externB b$VTYP
  75. externW b$curlevel ;current program level
  76. DeclareB MQUEFL,?,1
  77. DeclareB PLYPRS,?,1
  78. DeclareW STRDSC,?,NUM_VOICES
  79. DeclareW VSTACK,?,<NUM_VOICES*NUM_VSTACK> 
  80. DeclareW VSTBAS,?,NUM_VOICES
  81. DeclareW VSTOFF,?,NUM_VOICES
  82. sEnd _BSS
  83. assumes CS,SN_TEXT
  84. sBegin SN_TEXT 
  85. ; externNP B$MCLXEQ ;execute substring routine
  86. externNP B$DONOTE ;oem routine
  87. externNP B$STRTSD
  88. externNP B$SNDWAT
  89. externNP B$ERR_FC
  90. externNP B$ERR_OM
  91. externNP B$ERR_TM ; Type mismatch error
  92. externNP B$SETMCL
  93. externNP B$MACCMD
  94. externNP B$FETCHR
  95. externNP B$DECFET
  96. externNP B$FETCHZ
  97. externNP B$VALSC2
  98. externNP B$BREAK_CHK
  99. externNP B$GETSTKC ; Check stack
  100. externNP B$SCNVAR ; get descriptor from VARPTR$
  101. externNP B$STDALCALLTMP ; **** liberate temporaries ****
  102. ;PLAY Statement
  103. ;Syntax:
  104. ;   PLAY ON
  105. ;   PLAY OFF
  106. ;   PLAY STOP
  107. ;   PLAY [A$] [, [B$] [, [C$]]]   - multi-voice play
  108. ;PLAY is made up of a forground task and a background task.  A queue exists
  109. ;for each voice through which the forground task passes the background task
  110. ;Commands and Syncronization information.  One SYNC byte is placed in each
  111. ;voice queue at the start of each PLAY statement.  All commands which are
  112. ;produced by the PLAY statement follow the SYNC byte in the queue.
  113. ;When the background task encounters the SYNC byte, it disables that voice
  114. ;until it has received a SYNC byte for each voice and PLYBGC .GT. 0.  This
  115. ;mechanism insures that all voices for a play statement will begin at
  116. ;the same time, even if one of the voices in the previous play statement
  117. ;was shorter than the rest.
  118. ;A String-Pointer stack is associated with each voice to allow each voice
  119. ;string to include (nest) other voice strings via the X macro-language
  120. ;command.
  121. ;PLAY Algorithm:
  122. ;    Initialize a str-ptr stack for every voice.
  123. ;    Parse n strings (saving ptrs to string temps in STRDSC(i)).
  124. ;    For each voice,
  125. ;      Put SYNC byte at start of each voice queue
  126. ;      Release Temp string desc and push str ptr&len to voice's str-ptr stack.
  127. ;    For each voice,
  128. ;1)    Fill each queue as far as possible (till end-of-string or end-of-queue)
  129. ;      if 1st pass
  130. ;  B$STRTSD (start clock int routine, all voices)
  131. ;      if no voices active and all strings are not empty, B$STRTSD(all voices)
  132. ;  (This is a safety valve to insure BASIC will never hang during PLAY)
  133. ;    If all strings have not been completely consumed,
  134. ;      goto step 1
  135. ;  If Music Forground, wait till all voice activity halts
  136. PLAYS:
  137. ; Entry point for the PLAY statement.  Test to see which of the
  138. ; allowed forms has been specified.
  139. ;***
  140. ; B$PL0, B$PL1, B$SPLY - Play   <sexp>... statement
  141. ;
  142. ; Purpose:
  143. ; Runtime Entry Points.
  144. ; B$PL0 and B$PL1 are alternate PLAY Statement preamble entry points
  145. ; B$SPLY is called with the last parm, and it actually executes
  146. ; the statement.
  147. ; B$PL0   called if a parm is specified
  148. ; B$PL1   called if a parm is defaulted
  149. ; B$SPLY   called for last parm (may be the only parm as well)
  150. ;
  151. ;  Parse the strings from the program statement, and set up the pointers
  152. ;  in the string stacks.
  153. ;
  154. ; Input:
  155. ; sdPlay == string descriptor of a single argument (B$PL0 & B$SPLY only -
  156. ; no input is given for B$PL1)
  157. ; Output:
  158. ; NONE
  159. ; Modifies:
  160. ; NONE
  161. ;****
  162. cProc B$SPLY,<PUBLIC,FAR>,<SI> 
  163. parmSD sdPlay
  164. cBegin
  165. cCALL B$CHKINI
  166. GetpSD BX,sdPlay ;BX = psd for play string
  167. MOV STRDSC,BX ; save str desc for B$PARSER.
  168. cCALL B$PARSE0 ; Go process everything
  169. MOV AX,b$curlevel ;deallocate all temps >= current level
  170. cCALL B$STDALCALLTMP
  171. cEnd
  172. ;***
  173. ;B$CHKINI
  174. ;
  175. ;PURPOSE:
  176. ; Initialize runtime variables related to music if starting play statement
  177. ;
  178. ;ENTRY:
  179. ;
  180. ;EXIT:
  181. ;
  182. ;MODIFIES:
  183. ; AX,CX,SI
  184. ;
  185. ;****
  186. cProc B$CHKINI,<NEAR>
  187. cBegin
  188. ;exit -- bx is preserved
  189. XOR AX,AX
  190. MOV [B$MCLTAB],OFFSET PLYTAB ;B$MCLTAB points to play command table
  191. ; for B$MACCMD
  192. MOV [MQUEFL],AL ; indicates no music cmds have been queued
  193. ; Initialize the string stacks for use while parsing the play strings.
  194. ; These stacks are used to support the X music command which functions
  195. ; like a subroutine call.
  196. MOV CX,OFFSET DGROUP:VSTACK ; *cx = string stack for voice 1
  197. inistl:
  198. MOV VSTOFF,AX ; reset offset for voice si
  199. MOV VSTBAS,CX ; set stack base ptr for voice si
  200. MOV STRDSC,AX ; reset string desr ptr for voice si
  201. chkret:
  202. cEnd ; End of B$CHKINI
  203. ;
  204. ;***
  205. ;B$PARSE0
  206. ;
  207. ;PURPOSE:
  208. ; Pre-PARSER for play string
  209. ;
  210. ;ENTRY:
  211. ;
  212. ;EXIT:
  213. ;
  214. ;USES:
  215. ; per convention
  216. ;
  217. ;****
  218. cProc B$PARSE0,<NEAR>
  219. cBegin ; **** HERE AFTER LAST PARM ****
  220. ; Release string temps in reverse order (because its a stack)
  221. MOV [PLYPRS],NUM_VOICES ;NUM_VOICES strings have been parsed
  222. SETSTL:
  223. MOV BX,STRDSC ; *bx = temp str desc for voice(si)
  224. OR BX,BX
  225. JE EMPTYS ;branch if no string was parsed
  226. CALL B$SETMCL  ;set B$MCLPTR, B$MCLLEN to str @bx
  227. cCALL B$PUTSTR ;save B$MCLPTR, B$MCLLEN on voicen's stack
  228. DEC [PLYPRS] ;one less string has been parsed
  229. emptys:
  230. JMP SHORT B$PARSER
  231. cEnd <nogen> ; End of B$PARSE0
  232. ;***
  233. ;B$PARSER fills the background-music queues as follows:
  234. ; Repeat
  235. ;   For Each voice
  236. ;     while (input string is not empty) and
  237. ;     (room exists in voice's queue for 1 command)
  238. ; Call B$MACCMD to process next command in string.
  239. ; (This causes some routine in PLYTAB to be called which may try to
  240. ;  queue information for the background task into that voice's queue.
  241. ;   If 1st pass, DI, bump PLYBGC, cCALL B$STRTSD(7) to initiate background task
  242. ; Until all voice strings are empty
  243. ; If music-forground, wait until PLYMSK=0
  244. ;****
  245. cProc B$PARSER,<NEAR>
  246. cBegin
  247. PARSER_BEGIN:
  248. ; Get music macro commands from the string for this voice until the
  249. ; queue for this voice fills or we hit the end of the string.
  250. PRSL10:
  251. cCALL B$GETSTR ;Get B$MCLPTR, B$MCLLEN for [voicen]
  252. CMP [B$MCLLEN],0 ; Check if end of string
  253. JNE PRSCONT ; Brif more in string, process it
  254. CMP VSTOFF,0 ; Test if string was nested by X command
  255. JNE PRSL10 ; Brif so, jump to return to next string up
  256. JMP SHORT PRSLPX ; Else, the top level string s done 
  257. PRSCONT: ; Continue with parsing
  258. PUSH [B$MCLPTR] ;SAve the current string pointers
  259. PUSH [B$MCLLEN] ;  in case the queue overflows
  260. CALL B$MACCMD  ;Parse one command from this string
  261. JB PRSL20 ;Quit this loop if the queue overflowed
  262. POP AX ;Clear old pointers from stack
  263. POP AX
  264. cCALL B$PUTSTR ;save new B$MCLPTR, B$MCLLEN for [voicen]
  265. JMP SHORT PRSL10 ;continue trying to fill this queue
  266. ; Had a queue overflow on the last call to B$MACCMD.  Restore the original
  267. ; string pointers so that the command which caused the overflow can be
  268. ; reprocessed the next time.
  269. PRSL20:
  270. POP [B$MCLLEN] ;RESTORE the old pointers
  271. POP [B$MCLPTR]
  272. cCALL B$PUTSTR ;And  put them back on the local stack
  273. PRSLPX:
  274. ; Test if the user wants to break
  275. CALL B$BREAK_CHK ;CTRL-BREAK?
  276. ; Have made a pass through the strings.  If this is pass 1 and there
  277. ; is data in any of the queues, then start the music playing.
  278. TEST PLYPRS,LOW 128D
  279. JNZ NOTPS1 ;Brif not Pass 1
  280. OR PLYPRS,LOW 128D ;Set parsed once flag
  281. CMP MQUEFL,LOW 0
  282. JE NOTPS1 ;branch if no cmds have been queued for
  283. ; this PLAY statement (no need to start
  284. ; background task
  285. cCALL B$STRTSD ;Start sounds
  286. NOTPS1:
  287. ; Test if we are all done.  This occurs when the count of voice strings
  288. ; completely parsed (contained in PLYPRS) equals the number of voices.
  289. CMP [PLYPRS],LOW 200O+NUM_VOICES
  290. JE PRSDON ;Brif all NUM_VOICES strings parsed
  291. MOV AL,LOW TSTVOC ;Ask the OEM if any voices are active
  292. cCALL B$DONOTE
  293. OR AL,AL ;See what they had to say about it
  294. JNE PARSER_BEGIN ; If there are active voices, then the
  295. ;queue's are emptying, so go try to
  296. ;parse the rest of the strings.
  297. ; If we get here, then it means that we are not done parsing the PLAY
  298. ; strings, the queues are full, but none of the voices are active.
  299. ; This should never occur, but IF it did, BASIC would be hung indefinitely.
  300. ; This simple safeguard ensures this will never happen.
  301. MOV AL,LOW STRSND ;Function code to start music
  302. cCALL B$DONOTE  ; Tell OEM to start playing the music
  303. JMP PARSER_BEGIN ; and go parse the rest of the strings
  304. ; We are all done parsing the input strings.  If the music mode is
  305. ; Background, then we are done.  If mode is Foreground, we need to
  306. ; wait around until the sound stops.
  307. PRSDON:
  308. cCALL B$SNDWAT ;wait if Music Foreground
  309. cEnd ; End of B$PARSER
  310. ;***
  311. ;B$PUTSTR
  312. ;Purpose:
  313. ; Save B$MCLPTR, B$MCLLEN on Voice Stack for voice [VOICEN]
  314. ;Input:
  315. ; [VOICEN] = voice id (0..2)
  316. ; [B$MCLLEN]=number of bytes left in current string for this voice
  317. ; [B$MCLPTR]=pointer to next byte in current string for this voice
  318. ;Output:
  319. ; If stack overflows, an Out of Memory error is issued
  320. ;Modifies:
  321. ; AX
  322. ;****
  323. cProc B$PUTSTR,<NEAR>,<BX,SI,DI>
  324. cBegin
  325. ; Get the stack pointer for this string and check for stack overflow
  326. MOV DI,VSTBAS ; di points to stack base for voice si
  327. MOV BX,VSTOFF ; bx = offset for current top of stack
  328. CMP BX,NUM_VSTACK ;Check if the stack is full
  329. JB STKOK ;branch if still room on stack
  330. JMP B$ERR_OM ;NO room, so signal out of memory error
  331. ; Save the current data on the stack
  332. STKOK:
  333. MOV AX,[B$MCLPTR]
  334. MOV [BX+DI],AX ;save string pointer on stack
  335. MOV AX,[B$MCLLEN]
  336. MOV [BX+DI]+2,AX ;save string length on stack
  337. ; Update the stack pointer to account for new stack size
  338. ADD BX,4
  339. MOV VSTOFF,BX ; save offset to top of stack
  340. cEnd ; End of B$PUTSTR
  341. ;***
  342. ;B$GETSTR
  343. ;Purpose:
  344. ; Set B$MCLPTR, B$MCLLEN for Voice [VOICEN]
  345. ;Input:
  346. ; [VOICEN] = voice id (0..2)
  347. ;Output:
  348. ; [B$MCLLEN]=number of bytes left in current string for this voice
  349. ; [B$MCLPTR]=pointer to next byte in current string for this voice
  350. ; If this voice's string has been completely consumed then
  351. ;   PLYPRS is incremented and B$MCLLEN=0
  352. ;Modifies:
  353. ; AX
  354. ;****
  355. cProc B$GETSTR,<NEAR>,<BX,SI,DI>
  356. cBegin
  357. ; Get the stack pointer for this voice, and test if stack is empty
  358. MOV BX,VSTOFF ; bx = offset for current top of stack
  359. MOV [B$MCLLEN],BX ;length=0 if no more strings stacked
  360. OR BX,BX
  361. JE GETSTX ;brif no entries exist on voice's stack
  362. MOV DI,VSTBAS ; DI points to stack base for voice SI
  363. ; Get the next set of entries from the stack.
  364. GETSTL:
  365. SUB BX,4 ;Adjust stack pointer to next entry
  366. MOV AX,[BX+DI] ;get string pointer from stack
  367. MOV [B$MCLPTR],AX
  368. MOV AX,[BX+DI]+2 ;get string length from stack
  369. MOV [B$MCLLEN],AX
  370. OR AX,AX
  371. JNE GETSTX ;exit if this string is not empty
  372. OR BX,BX
  373. JNE GETSTL ;brif more entries on voice's stack
  374. INC [PLYPRS] ;Bump number of strings consumed
  375. ; Update the stack pointer, restore the registers, and get out
  376. GETSTX:
  377. MOV VSTOFF,BX ; save offset to top of stack
  378. cEnd ; End of B$GETSTR
  379. ;--------------------------------------------------------------------------
  380. ; Music Macro Language command table
  381. ; This table contains all of the command characters allowed in the music
  382. ; language strings, and the entry points of the routines to process them
  383. PLYTAB LABEL BYTE
  384. DB "A" ;The notes A-G
  385. DW OFFSET PLYNOT
  386. DB "B"
  387. DW OFFSET PLYNOT
  388. DB "C"
  389. DW OFFSET PLYNOT
  390. DB "D"
  391. DW OFFSET PLYNOT
  392. DB "E"
  393. DW OFFSET PLYNOT
  394. DB "F"
  395. DW OFFSET PLYNOT
  396. DB "G"
  397. DW OFFSET PLYNOT
  398. DB "M" ;Music Meta Command
  399. DW OFFSET B$PLYMET
  400. ; DB "Q" ;Envelope Subcommand Lead In
  401. ; DW OFFSET QCMNDS
  402. DB "N"+128  ;PLAY NUMERIC NOTE
  403. DW OFFSET PLYNUM
  404. DB "O"+128  ;OCTAVE
  405. DW OFFSET POCTAV
  406. DB "P"+128  ;PAUSE
  407. DW OFFSET PPAUSE
  408. DB "T"+128  ;TEMPO
  409. DW OFFSET PTEMPO
  410. DB "L"+128  ;LENGTH
  411. DW OFFSET PLYLEN
  412. ; DB "V"+128  ;Volume
  413. ; DW OFFSET PVOLUM
  414. DB "X" ;EXECUTE STRING
  415. ; DW OFFSET B$MCLXEQ ; substring handler in MCLPRC
  416. DW OFFSET B$PLYXEQ ; Substring handler is local
  417. DB "<" ;Decrement Octave
  418. DW OFFSET POCTAD
  419. DB ">" ;Increment Octave
  420. DW OFFSET POCTAI
  421. DB 00 ;END OF TABLE
  422. ; This table contains the allowed subcommands for the Q command
  423. ; in the Music Macro Language.
  424. ; This table has been removed as part of PC Jr code removal
  425. ;B$PLYXEQ
  426. ;Purpose:
  427. ; Reimplemented as part of revision [5]
  428. ; This routine is dispatched to by the X command in the PLAY statement.
  429. ; It is equivalent to a macro-language subroutine call, in that it
  430. ; specifies a variable which is to be inserted in the Macro String.  It:
  431. ;
  432. ;  1)  Calls B$GETSTKC to check for enought stack space.
  433. ;  2)  Calls B$SCNVAR to get string descriptor in the FAC
  434. ;  3)  Calls B$PUTSTR to stack the current string pointer & length,
  435. ;  4)  Sets B$MCLPTR & B$MCLLEN to point to new nested string,
  436. ;  5)  Returns to its caller (presumably PLAY (via B$MACCMD)
  437. cProc B$PLYXEQ,<NEAR>
  438. cBegin
  439. MOV CL,100 ; Get size for stack check
  440. CALL B$GETSTKC ; Check the stack for enough room
  441. CALL B$SCNVAR ; Get the VARPTR$ descriptor offset in FAC
  442. CMP [b$VTYP],VT_SD ; Test if type was a string
  443. JNE PLYERR ; Brif not, signal an error
  444. cCALL B$PUTSTR ; Put B$MCLPTR & B$MCLLEN on local stack
  445. MOV BX,OFFSET DGROUP:B$AC ; Get FAC for descriptor
  446. CALL B$SETMCL ; Set new values of B$MCLPTR & B$MCLLEN
  447. CLC ; To indicate to use new values
  448. cEnd ; End of B$PLYXEQ
  449. PLYERR: ; Indicate type mismatch error
  450. JMP B$ERR_TM
  451. ; Decrement the current octave number
  452. POCTAD: CMP B$OCTAVE,LOW 0
  453. JZ PLYRET
  454. DEC B$OCTAVE ; octave -1
  455. RET
  456. ; Increment the current octave number
  457. POCTAI: cmp B$OCTAVE,low 6
  458. JNB PLYRET
  459. CLC
  460. INC B$OCTAVE ; octave +1
  461. RET
  462. ; Set the volume level
  463. ; Volume support code has been deleted from here
  464. ; Set the note length
  465. PLYLEN:
  466. JNB PLGOFC ;ERROR IF NO ARG
  467. CMP DL,LOW 65 ;ALLOW ONLY UP TO 64
  468. JNB PLGOFC ;FC ERROR IF TOO BIG
  469. OR DL,DL ;DON'T ALLOW ZERO
  470. JZ PLGOFC ;FC ERROR IF ZERO
  471. MOV B$NOTELN,DL ; store note length
  472. RET
  473. ; Set the play tempo
  474. PTEMPO:
  475. CMP DL,LOW 32 ;ALLOW ONLY 32 - 255
  476. JB PLGOFC ;FC ERROR IF TOO SMALL
  477. mov B$BEATS,dl ; store beats per minute
  478. CLC
  479. RET
  480. ; Play a rest (Pause command)
  481. PPAUSE:
  482. JNB PLGOFC ;ERROR IF NO ARG
  483. XOR CX,CX ;PASS FREQ OF 0
  484. CMP DL,LOW 65 ;ALLOW ONLY 1-64
  485. JNB PLGOFC ;FC ERROR IF TOO BIG
  486. OR DL,DL ;SEE IF ZERO
  487. JZ PLYRET ;RETURN IF SO - NO PAUSE
  488. JMP PPAUS2 ;[DX]=PAUSE LENGTH
  489. ; Set the current octave number
  490. POCTAV:
  491. JNB PLGOFC ;ERROR IF NO ARG
  492. CMP DL,LOW 7 ;ALLOW ONLY OCTAVES 0..6
  493. JNB PLGOFC ;FC ERROR IF TO BIG
  494. mov B$OCTAVE,dl
  495. CLC
  496. PLYRET: RET
  497. ; Play a particular note by note number
  498. PLYNUM:
  499. JNB PLGOFC ;ERROR IF NO ARG
  500. MOV AL,DL ;GET NOTE NUMBER INTO [AL]
  501. OR AL,AL ;SEE IF ZERO (PAUSE)
  502. JZ PLYNO3 ;DO THE PAUSE
  503. CMP AL,LOW 85 ;ALLOW ONLY 0..84
  504. JNB PLGOFC ;FC ERROR IF TOO BIG
  505. CBW ;CLEAR HI BYTE FOR DIVIDE
  506. DEC AX ;MAP TO 0..83
  507. MOV DL,LOW 12 ;DIVIDE BY 12
  508. DIV DL
  509. MOV DH,AL ;OCTAVE TO [DH]
  510. MOV AL,AH ;NOTE NUMBER IS REMAINDER
  511. INC AL ;ADD ONE
  512. ADD AL,AL ;DOUBLE TO MAKE INDEX
  513. JMP SHORT PLYNU3 ;PLAY NOTE [AL], OCTAVE [DH]
  514. PLGOFC: JMP B$ERR_FC ; GIVE FUNCTION CALL ERROR
  515. ; Play a note by name
  516. PLYNOT: SUB CL,LOW "A"-1 ;MAP TO 1..7
  517. ADD CL,CL ;MAP TO 2..14 (THIS ASSUMES SHARP)
  518. CALL B$FETCHR  ;GET NEXT CHARACTER
  519. JZ PLYNO2 ;END OF STRING - NO SHARP OR FLAT
  520. CMP AL,LOW "#" ;CHECK FOR POSSIBLE SHARP
  521. JZ PLYSHP ;SHARP IT THEN
  522. CMP AL,LOW "+" ;"+" ALSO MEANS SHARP
  523. JZ PLYSHP
  524. CMP AL,LOW "-" ;"-" MEANS FLAT
  525. JZ PLYFLT
  526. CALL B$DECFET  ;PUT CHAR BACK IN STRING.
  527. JMP SHORT PLYNO2 ;TREAT AS UNMODIFIED NOTE.
  528. PLYFLT: DEC CL ;DECREMENT TWICE TO FLAT IT
  529. PLYNO2: DEC CL ;MAP BACK TO UNSHARPED
  530. PLYSHP: MOV AL,CL ;INTO [AL] FOR XLAT
  531. MOV BX,OFFSET NOTXLT ;POINT TO TRANSLATE TABLE
  532. XLAT BYTE PTR CS:[BX] ; TRANSLATE INTO NOTE TABLE INDEX
  533. OR AL,AL ;SEE IF LEGAL NOTE
  534. JS PLGOFC ;NOTE'S OK IF NOT .GT. 127
  535. ; ENTER HERE WITH NOTE TO PLAY IN [AL]
  536. ; NOTE 0 IS PAUSE, 2,4,6,8..10,12 ARE A-G AND FRIENDS.
  537. PLYNO3:
  538. mov dh,B$OCTAVE ; get B$OCTAVE into [dh] for later math
  539. PLYNU3:
  540. PUSH AX ;Save Note
  541. PUSH DX ; Save Octave
  542. MOV AL,B$NOTELN
  543. MOV B$NOTE1L,AL ; one note duration = note length
  544. CALL B$FETCHR
  545. JZ PLYNU4 ;Brif end of string
  546. CALL B$VALSC2  ;See if possible number
  547. CMP DL,LOW 65 ;If was .gt. 64
  548. JNB PLGOFC ; then error
  549. OR DL,DL ;Any Length?
  550. JZ PLYNU4 ;Brif not, just do note
  551. MOV B$NOTE1L,DL ; store duration for this note
  552. PLYNU4:
  553. POP DX ;Get Octave
  554. POP AX ;Restore Note
  555. CBW ;FILL [AH] WITH ZEROS
  556. MOV BX,AX ;TRANSFER TO BX FOR INDEXING
  557. OR BX,BX ;SEE IF PAUSE (NOTE # 0)
  558. JZ PLYNO4 ;IF PAUSE, PASS [BX]=0
  559. MOV BX,WORD PTR CS:NOTTAB-2[BX] ;FETCH FREQUENCY
  560. MOV CL,LOW 6 ;CALCULATE 6-OCTAVE
  561. SUB CL,DH ;FOR # OF TIMES TO SHIFT FREQ.
  562. SHR BX,CL ;DIVIDE BY 2^(6-OCTAVE)
  563. ADC BX,0 ;ADD IN CARRY TO ROUND UP
  564. PLYNO4:
  565. MOV CX,BX ;FREQUENCY INTO [CX] FOR DONOTE
  566. MOV DL,B$NOTE1L ; get this note's length
  567. PPAUS2:
  568. MOV AL,B$BEATS ; GET BEATS PER UNIT TIME
  569. MUL DL ;CALC NOTE LENGTH * B$BEATS
  570. PUSH CX ;SAVE [CX] WHILE WE DIVIDE
  571. MOV CX,AX ;CALC TIME CONST/(B$BEATS * NOTE LENGTH)
  572. MOV DX,1 ;96000 (4*60*400) is
  573. MOV AX,73400O ;SPECIAL TIME CONSTANT
  574. DIV CX
  575. POP CX ;RESTORE FREQUENCY
  576. OR AX,AX ;IF DURATION IS ZERO, GET OUT.
  577. JZ PLYNO8
  578. PUSH CX ;Save Freq
  579. PLYDOT:
  580. MOV CX,AX ; Copy of duration for doted notes
  581. PLYDOT1:
  582. PUSH AX ; Save duration
  583. PUSH CX ; Save the current dot duration
  584. CALL B$FETCHR
  585. JZ PLYDOX ; Brif EOS
  586. CMP AL,LOW "." ; Note duration extender?
  587. JNZ PLYDO2 ; Brif not
  588. POP CX ; Get last dot duration
  589. POP AX ; Get current duration
  590. SHR CX,1 ; This dot = previous dot / 2
  591. ADD AX,CX ; Update the new duration
  592. JNB PLYDOT1  ; Loop if not overflow
  593. JMP B$ERR_FC ; else complain.... (wont return)
  594. PLYDO2:
  595. CALL B$DECFET  ; Put char back
  596. PLYDOX:
  597. POP AX ; Trash the dot duration
  598. POP AX ; Duration
  599. POP CX ; Get freq
  600. PUSH AX ;Save Duration
  601. PUSH CX ;Save Frequency
  602. JCXZ PLYNO7 ;Brif Pause
  603. CMP B$MSCALE,LOW 1
  604. JZ PLYNO7 ;Brif Legatto
  605. MOV CL,B$MSCALE ; using scale for shift count
  606. MOV BX,3 ;Stacatto multiplier
  607. CMP CL,LOW 2
  608. JZ PLYNO6 ;Brif Stacatto
  609. MOV BX,7 ; else Normal
  610. PLYNO6:
  611. MUL BX ;Duration * 7/8 or 3/4
  612. SHR AX,CL
  613. OR AX,AX
  614. JNZ PLYNO7 ;If zero
  615. INC AX ; then make 1
  616. ; Have all of the parameters for this note.  Send the info to the OEM
  617. ; to queue the note.
  618. ; Because a note is sent via two separate commands (one for the first,
  619. ; sound generating, part of the note, and a second one for the inter-
  620. ; note pause) it is possible for the queue to overflow in the middle of
  621. ; the note.  It isn't possible to simply wait for space to become available
  622. ; in the queue, because there is no guarantee that the queue is being
  623. ; emptied.  So to handle this case the following things happen:
  624. ; If the queue overflows on either half of the note, the carry
  625. ; flag is returned set as a signal to the music string B$PARSER that
  626. ; the present command needs to be rescanned the next time around
  627. ; If the first half of the note is sent successfully, a flag is
  628. ; set (B$NOTFLG[SI]) indicating that it has been sent.  When the
  629. ; second half of the note is sent successfully, then this flag is
  630. ; cleared indicating that the entire note has been sent. Before
  631. ; sending the first half, it is necessary to check the flag to see
  632. ; if this part has already been passed to the OEM on a previous
  633. ; pass through the B$PARSER.
  634. PLYNO7:
  635. POP CX ;Get Freq
  636. CMP B$NOTFLG,LOW 0  ; has the first part of this note already
  637. ;been queued
  638. JNZ PLYN7B ;If so, don't send it again
  639. cCALL B$SNDNOT ;Send note
  640. JNB PLYN7A ;If no queue overflowed, continue
  641. POP AX ;If queue overflowed, get out, not even
  642. JMP SHORT PLYNO9 ; the first part of note was queued.
  643. PLYN7A:
  644. mov B$NOTFLG,low 1  ; set flag to say that note has been sent
  645. PLYN7B:
  646. ; Now send an inter-note pause for this note (if required)
  647. POP AX ;Get back original duration
  648. JCXZ PLYNO8 ;Brif Pause
  649. CMP B$MSCALE,LOW 1
  650. JZ PLYNO8 ;Brif Legatto
  651. MOV CL,B$MSCALE ; scale factor for current mode (1/8|1/4)
  652. SHR AX,CL ;divide note duration by scale factor
  653. OR AX,AX ;Pause = 0?
  654. JZ PLYNO8 ;Don't send anything if so.
  655. cCALL B$SNDPSN ;Send the rest
  656. JB PLYNO9 ;If queue overflowed, don't reset
  657. ; flag for this note
  658. PLYNO8:
  659. MOV B$NOTFLG,LOW 0  ; clear the flag to indicate that the
  660. ;complete note has been queued
  661. PLYNO9:
  662. RET ; else do nothing
  663. ;***
  664. ; B$SNDNOT,B$SNDPSN
  665. ; Purpose:
  666. ; Send the specified note information to the OEM routine to be queued.
  667. ; B$SNDNOT will queue the first part of a note.
  668. ; B$SNDPSN will queue the second (inter-note pause) part of a note.
  669. ; Entry:
  670. ; AX - Duration
  671. ; CX - Frequency
  672. ; Exit:
  673. ; none
  674. ; Modifies:
  675. ; SI, DI, CX preserved.
  676. ; The registers must be set up as follows for the call to B$DONOTE
  677. ; [AL] = B$DONOTE function code number (1 for note, 0 for inter-note pause)
  678. ; [AH] = Voice
  679. ; [BX] = Volume
  680. ; [CX] = FREQUENCY IN HERTZ
  681. ; [DX] = DURATION IN CLOCK TICKS (1/18.2 SECONDS)
  682. ;****
  683. cProc B$SNDNOT,<NEAR>
  684. cBegin
  685. XCHG DX,AX ; B$DONOTE wants duration in DX
  686. ; MOV BX,B$VCEVOL ; Volume into bx
  687. MOV AL,LOW QUENOT ;B$DONOTE function code in AL
  688. ; Test if this is the first time a note has been parsed in this play
  689. ; statement, and if so, queue sync marks in all voice queues.
  690. CMP MQUEFL,LOW 0
  691. JNE SDNT20 ;branch if cmds have already been queued
  692. ; for this PLAY statement
  693. MOV MQUEFL,LOW 1
  694. JMP SHORT SDNT20
  695. cEnd <nogen> ; End of B$SNDNOT
  696. ; Queue an internote pause (rest) for the current note
  697. cProc B$SNDPSN,<NEAR>
  698. cBegin
  699. XCHG DX,AX ; B$DONOTE wants duration in DX
  700. MOV AL,LOW QUERST ;B$DONOTE function code in AL
  701. ; Send the instruction to the OEM, and test for any error codes coming
  702. ; back
  703. SDNT20:
  704. cCALL B$DONOTE  ; PLAY THE NOTE
  705. JNB MQD90 ;If no error occured, then go on
  706. CMP AL,LOW 1 ;Test for overflow on this voice
  707. JNE PLYMER ;If not queue full, then report error
  708. MQD80:
  709. STC ;Set error return state
  710. MQD90:
  711. cEnd ; End of B$SNDPSN
  712. ; Function call error occured while processing Music Meta command.
  713. PLYMER:
  714. JMP B$ERR_FC ; wont return
  715. ; B$PLYMET - Process Music Meta Commands.
  716. cProc B$PLYMET,<NEAR>
  717. cBegin
  718. CALL B$FETCHZ  ;Get Meta action or error
  719. MOV CL,LOW 1 ;Factor for Legatto (1/1)
  720. CMP AL,LOW "L"
  721. JZ PLYDUR ;Brif Legatto (Full note)
  722. INC CL ;Factor for Stecatto (3/4)
  723. CMP AL,LOW "S"
  724. JZ PLYDUR ;Brif Stecatto (3/4)
  725. INC CL ;Factor for Normal (7/8)
  726. CMP AL,LOW "N"
  727. JZ PLYDUR ;Brif Normal (7/8)
  728. XOR CL,CL
  729. CMP AL,LOW "F"
  730. JZ PLYMOD ;Brif Foreground Music
  731. DEC CL
  732. CMP AL,LOW "B"
  733. JNZ PLYMER ;Brif not Background Music
  734. PLYMOD:
  735. MOV [B$MMODE],CL ;Store Music Mode (0=FG, 255=BG)
  736. JMP SHORT PLYMET_RET
  737. PLYDUR:
  738. MOV B$MSCALE,CL ; store duration scaling factor
  739. PLYMET_RET: ; Common exit point
  740. cEnd ; End of B$PLYMET
  741. ; This is the executive for dispatching the Q command. It sets
  742. ; up the Macro command processor table to point to the Q subcommand
  743. ; table, and then uses the macro command processor to dispatch to
  744. ; the appropriate routine to process the subcommand.
  745. ; Envelope support code has been deleted from here
  746. ; TABLE OF INDEXES INTO NOTTAB FOR EACH NOTE
  747. ; VALUE OF 255 MEANS NOTE NOT ALLOWED.
  748. NOTXLT LABEL BYTE
  749. DB 9*2 ;A- (G#)
  750. DB 10*2 ;A
  751. DB 11*2 ;A#
  752. DB 12*2 ;B
  753. DB 255 ;NO C- OR B#
  754. DB 1*2 ;C
  755. DB 2*2 ;C#
  756. DB 3*2 ;D
  757. DB 4*2 ;D#
  758. DB 5*2 ;E
  759. DB 255 ;NO E# OR F-
  760. DB 6*2 ;F
  761. DB 7*2 ;F#
  762. DB 8*2 ;G
  763. DB 9*2 ;G#
  764. ; TABLE OF NOTE FREQUENCIES
  765. ; THESE ARE THE FREQUENCIES IN HERTZ OF THE TOP OCTAVE (6)
  766. ; DIVIDED DOWN BY POWERS OF TWO TO GET ALL OTHER OCTAVES
  767. NOTTAB LABEL WORD
  768. DW 4186 ;C
  769. DW 4435 ;C#
  770. DW 4699 ;D
  771. DW 4978 ;D#
  772. DW 5274 ;E
  773. DW 5588 ;F
  774. DW 5920 ;F#
  775. DW 6272 ;G
  776. DW 6645 ;G#
  777. DW 7040 ;A
  778. DW 7459 ;A#
  779. DW 7902 ;B
  780. ;***
  781. ;B$QSYNC
  782. ;Purpose:
  783. ; Output a Syncronization Byte to all voices
  784. ;Input:
  785. ; none
  786. ;Output:
  787. ; A SYNC byte is put in voice [VOICEN]'s queue
  788. ;Modifies:
  789. ; none
  790. ;****
  791. ; SYNC byte is output only if multi-voice support is present.
  792. ; For single-voice music it is not necessary and hence not output.
  793. sEnd SN_TEXT 
  794. END