'******************************************************
'*                                                    *
'*  File Name:     CMSMOD.BAS                         *
'*                                                    *
'*  Description:   Contains all the source code       *
'*                 necessary to play Creative Music   *
'*                 System (CMS) files using a CMS     *
'*                 compatable card (GameBlaster or    *
'*                 SoundBlaster) and CMS driver       *
'*                 (CMSDRV.COM).  This file may be    *           
'*                 loaded as a program or a module.   *
'*                                                    *
'*  Requirements:  CMSDRV resident in memory.         *
'*                 Interruptx in QuickLib             *
'*                 (i.e. QW.QLB).                     *
'*                                                    *
'******************************************************


DEFINT A-Z

TYPE RegTypeX
   ax    AS INTEGER
   bx    AS INTEGER
   cx    AS INTEGER
   dx    AS INTEGER
   bp    AS INTEGER
   si    AS INTEGER
   di    AS INTEGER
   flags AS INTEGER
   ds    AS INTEGER
   es    AS INTEGER
END TYPE

DECLARE SUB CmsPlayMusic (Repeats%)
DECLARE SUB CmsVersion (version$)
DECLARE SUB CmsFindDriver (IntNum%, version$)
DECLARE SUB CmsPauseMusic ()
DECLARE SUB CmsContinueMusic ()
DECLARE SUB CmsStopMusic ()
DECLARE SUB WaitForKey (Key$)
DECLARE SUB GetFileName (FileSpec$)
DECLARE SUB GetCmsFile (FileSpec$)
DECLARE SUB OpenCmsFile (FileSpec$)

CONST False = 0, True = NOT False

DIM SHARED Reg88X AS RegTypeX

' Make all arrays DYNAMIC so the .CMS file won't get relocated
' and cause CMSDRV to lose track of it.
'$DYNAMIC

' The PlayFlag is used to synchronize the user program with the music.
DIM PlayFlag AS INTEGER

' CmsSong array is the .CMS file loaded from disc.
DIM CmsSong(0) AS STRING * 32767

CLS

' Set the default number of plays to 1.
Repeats = 1

' Call the routine that finds the driver interrupt number and version.
CmsFindDriver IntNum, version$

SELECT CASE IntNum
   CASE IS = False
      PRINT "CMSDRV is not installed."
      END
   CASE ELSE

' Users should modify these 2 lines to do what ever they want with
' the version number (i.e. check to see if it is 3.00 or greater)
' and interrupt number.
      PRINT "CMSDRV Version "; version$
      PRINT "CMSDRV is using interrupt &H"; HEX$(IntNum)
END SELECT

' Go get the name of the .CMS file to be played...
GetFileName FileSpec$
' ...and load it into memory.
GetCmsFile FileSpec$

' This is the main part of the program. The one shown is a mini program
' just to demonstrate how the routines are used.
DO
   WaitForKey Key$
   SELECT CASE Key$
      CASE "T"
         CmsStopMusic
      CASE "P"
         CmsPauseMusic
      CASE "C"
         CmsContinueMusic
      CASE "S"
         CmsPlayMusic Repeats
      CASE "Q"
         CmsStopMusic
         END
      CASE "N"
         GetFileName FileSpec$
         CmsStopMusic
         GetCmsFile FileSpec$
   END SELECT

LOOP

REM $STATIC
'This sub continues music play after a pause.
'Entry conditions:
'  AH = 3
'Exit conditions:
'  AX = 0 successful
'  AX = 1 no song had been paused
'
SUB CmsContinueMusic

Reg88X.ax = &H300
CALL Interruptx(&H80, Reg88X, Reg88X)

END SUB

'This sub disables the CMS break function (CTRL-Keypad5)
'Entry conditions:
'  AH = 5
'Exit conditions:
'  None
'
SUB CmsDisableBreak


Reg88X.ax = &H500
CALL Interruptx(&H80, Reg88X, Reg88X)

END SUB

'This sub searches for the CMS driver CMSDRV.COM beginning at the INT 80h
'jump address thru INT 0BFh.  When the SUB finds the string "CMSDRV" at
'offset 104h of the interrupt, the interrupt number used by CMSDRV.COM is
'returned to the main program.
'
SUB CmsFindDriver (Inter, Ver$)

CONST CMSDRV$ = "CMSDRV"
CONST MaxInter = &HBF, StartInter = &H80

RightString = False
Inter = StartInter

DO
   DEF SEG = 0
   SegmentLo = PEEK((Inter * 4) + 2)
   SegmentHi = PEEK((Inter * 4) + 3)
   Segment = SegmentHi * 256 + SegmentLo
   DEF SEG = Segment
   Offset = &H104
   i = 1

   DO
      x$ = CHR$(PEEK(Offset))
         IF x$ = MID$(CMSDRV$, i, 1) THEN
            RightString = True
            Offset = Offset + 1
            i = i + 1
         ELSE
            RightString = False
         END IF
   LOOP WHILE (RightString = True) AND (i <= LEN(CMSDRV$))

   IF RightString = True THEN
      CmsVersion Ver$
      EXIT DO
   END IF

   Inter = Inter + 1
LOOP WHILE (Inter <= MaxInter)

DEF SEG
IF Inter > MaxInter THEN Inter = False

END SUB

'This sub pauses the music currently playing.
'Entry conditions:
'  AH = 2
'Exit conditions:
'  AX = 0 successful
'  AX = 1 no music was being played
'
SUB CmsPauseMusic

Reg88X.ax = &H200
CALL Interruptx(&H80, Reg88X, Reg88X)

END SUB

'This sub plays music from a .CMS music file.
'Entry conditions:
'  AH = 1
'  AL = number of times to play (1-255; 0 for non-play)
'  ES = segment address of PLAY-FLAG
'  BX = offset address of PLAY-FLAG
'  CX = segment of music score (.CMS file in memory)
'Exit conditions:
'  AX = 0 successful
'  AX = 1 non-CMS file structure
'  AX = 2 wrong COMPOSEr version
'
SUB CmsPlayMusic (Rpts)
 
SHARED PlayFlag AS INTEGER
SHARED CmsSong() AS STRING * 32767

Dummy& = FRE("")
PfSeg = VARSEG(PlayFlag)
PfOff = VARPTR(PlayFlag)
MSeg = VARSEG(CmsSong(0))

Reg88X.ax = &H100 + Rpts
Reg88X.es = PfSeg
Reg88X.bx = PfOff
Reg88X.cx = MSeg

CALL Interruptx(&H80, Reg88X, Reg88X)

END SUB

'This sub stops playing the current music.
'Entry conditions:
'  AH = 4
'Exit conditions:
'  None
'
SUB CmsStopMusic

Reg88X.ax = &H400
CALL Interruptx(&H80, Reg88X, Reg88X)

END SUB

' This sub gets the the version number of the .CMS driver and returns
' it as Ver$.
'
SUB CmsVersion (Ver$)

Reg88X.ax = 0
CALL Interruptx(&H80, Reg88X, Reg88X)
SELECT CASE LEN(HEX$(Reg88X.ax))
   CASE IS = 3
      Ver$ = LEFT$(HEX$(Reg88X.ax), 1) + "." + RIGHT$(HEX$(Reg88X.ax), 2)
   CASE IS = 4
      Ver$ = LEFT$(HEX$(Reg88X.ax), 2) + "." + RIGHT$(HEX$(Reg88X.ax), 2)
END SELECT


END SUB

' This routine loads a .CMS file (FSpec$) from disc
' into CmsSong array.
'
SUB GetCmsFile (FSpec$)

SHARED CmsSong() AS STRING * 32767

OPEN FSpec$ FOR BINARY AS #1
GET #1, , CmsSong(0)
CLOSE #1

END SUB

SUB GetFileName (FileSpec$)

GoodFile = True
LOCATE 3, 1
PRINT SPACE$(79)
LOCATE 10, 1
INPUT "Which drive and path"; Drive$

FILES Drive$ + "*.cms"

DO
   LOCATE 3, 1
   LINE INPUT "Enter CMS file name: "; FileSpec$
LOOP WHILE GoodFile = False
FileSpec$ = Drive$ + FileSpec$

END SUB

SUB WaitForKey (K$)

LOCATE 5, 1
PRINT "N)ew file, S)tart, C)ontinue, P)ause, T)erminate, Q)uit: ";

DO
   K$ = INKEY$
LOOP WHILE K$ = ""

K$ = UCASE$(K$)
PRINT K$;

END SUB

