home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / c / sbdma / dma.c
Encoding:
C/C++ Source or Header  |  1994-01-28  |  42.1 KB  |  1,269 lines

  1. /*************************************************************************
  2. *
  3. *       D M A    A U T O  -  I N I T    D E M O    P R O G R A M
  4. *
  5. *  AUTHOR:        Tom Bouril  (October 1993)  Special thanks to
  6. *                 Douglas "DMA" Kaden for technical assistance.
  7. *
  8. *  PURPOSE:       This program demonstrates how to play .VOC files using
  9. *                 DMA auto-init mode.  Only this file need be compiled.
  10. *                 No drivers are required by this program.
  11. *
  12. *  DESCRIPTION:   A file of audio data is loaded into a DMA buffer piece
  13. *                 by piece.  The DMA buffer is divded into two sections.
  14. *                 To start off, fill the bottom half of the buffer and then
  15. *                 begin playing it.  As the bottom half is playing, load
  16. *                 data from the file into the top half.  After the top
  17. *                 half is loaded, wait for the bottom half to finish
  18. *                 playing.  (An interrupt will be generated in auto-init
  19. *                 mode every time a 1/2 buffer has finished playing.  This
  20. *                 is how to detect when a 1/2 buffer is done playing.)
  21. *
  22. *                 After the bottom half of the buffer finishes playing,
  23. *                 the top half will immediately start playing.  At this
  24. *                 time load the bottom half of the buffer with
  25. *                 more data and wait for the top half to finish playing
  26. *                 (an interrupt will occur) until you load it with more
  27. *                 data.  This process continues for the length of the file.
  28. *
  29. *                 For all files to be played, the final 1/2 buffer to be
  30. *                 played will be programmed for single-cycle mode for the
  31. *                 number of audio bytes remaining in the 1/2 buffer.  This
  32. *                 includes the case in which the audio data in the file
  33. *                 is smaller than the size of a 1/2 buffer--so less than
  34. *                 1/2 of a buffer of data is played in total.
  35. *
  36. *                 NOTE: The DMA chip is always programmed for auto-init mode.
  37. *                       The DSP chip is programmed for auto-init or
  38. *                       single-cycle mode depending on conditions--see
  39. *                       BeginPlaying() for more details.
  40. *
  41. *
  42. *  COMPATIBILITY: The compiler used is Turbo C++ Ver. 1.01.  This program
  43. *                 uses NO C++ except for the double-slash comments (//).
  44. *                 This program will run on Sound Blaster PRO (all versions)
  45. *                 and Sound Blaster 16.  A "BLASTER" environment variable
  46. *                 must be defined that specifies the IRQ number, base I/O
  47. *                 address (in hex), and DMA channel.
  48. *                 Example: BLASTER=A220 I5 D1
  49. *
  50. *  LIMITATIONS:   * The size of the DMA buffer used is defined below as
  51. *                   DMA_BUF_SIZE.  The value of DMA_BUF_SIZE may need to
  52. *                   be adjusted depending upon the types of files you
  53. *                   play and the speed of your system.  Files with high
  54. *                   sample rates require a larger DMA buffer than files
  55. *                   with low sample rates.
  56. *
  57. *                   Example: On my 50 Mhz 486 DX, an 8-bit mono file
  58. *                            sampled 11025 times per second (11025 bytes
  59. *                            per second) plays perfect with a DMA_BUF_SIZE
  60. *                            of only 256, and is very intelligible with
  61. *                            size of only 2!  But a 16-bit stereo file
  62. *                            sampled 44100 times per second (176,400 bytes
  63. *                            per second) requires a DMA_BUF_SIZE of 8192
  64. *                            to play with no audible degradation.  If you
  65. *                            hear a file play too slow, detect skipping,
  66. *                            or clicking, try increasing DMA_BUF_SIZE.
  67. *
  68. *                 * Only the Creative Voice File format (.VOC files) of
  69. *                   the following types are supported:
  70. *
  71. *                   A) 8-bit PCM   (MONO Only)
  72. *                   B) 8-bit ADPCM (4-bit compressed Creative format)
  73. *                   C) 8-bit ADPCM (2.6-bit compressed Creative format)
  74. *                   D) 8-bit ADPCM (2-bit compressed Creative format)
  75. *                   E) 16-bit PCM  (STEREO and MONO)
  76. *
  77. *                   NOTE: Audio data must be SIGNED for all 8-bit files
  78. *                         and must be UNSIGNED for all 16-bit files.
  79. *                         This program does NOT support 8-bit STEREO!
  80. *
  81. *                 * Only Creative Voice File format block types 0, 1, 2,
  82. *                   8, and 9 are supported.
  83. *
  84. *                 * 16-bit files must use the SB16 and a 16-bit DMA channel.
  85. *
  86. *  DISCLAIMER:    Although this program has been tested with all supported
  87. *                 file types (8-bit PCM, 8-bit ADPCM, 16-bit PCM) and
  88. *                 all supported block types (0, 1, 2, 8, 9), there could
  89. *                 exist some unknown error(s) (bugs).
  90. *
  91. *                 Because of this possibilty coupled with the existence
  92. *                 of lawyers, I must say that neither Creative Labs, Inc.
  93. *                 nor the author is responsible for anything that occurs,
  94. *                 directly or indirectly, as a result of using or following
  95. *                 this piece of sample code.
  96. *
  97. **************************************************************************/
  98. #include <conio.h>
  99. #include <dos.h>
  100. #include <mem.h>
  101. #include <stdio.h>
  102. #include <stdlib.h>
  103.  
  104.  
  105. #define DMA_BUF_SIZE    8192
  106. #define DMA8_FF_REG      0xC
  107. #define DMA8_MASK_REG    0xA
  108. #define DMA8_MODE_REG    0xB
  109. #define DMA16_FF_REG    0xD8
  110. #define DMA16_MASK_REG  0xD4
  111. #define DMA16_MODE_REG  0xD6
  112.  
  113. #define DMA0_ADDR        0
  114. #define DMA0_COUNT       1
  115. #define DMA0_PAGE     0x87
  116. #define DMA1_ADDR        2
  117. #define DMA1_COUNT       3
  118. #define DMA1_PAGE     0x83
  119. #define DMA3_ADDR        6
  120. #define DMA3_COUNT       7
  121. #define DMA3_PAGE     0x82
  122. #define DMA5_ADDR     0xC4
  123. #define DMA5_COUNT    0xC6
  124. #define DMA5_PAGE     0x8B
  125. #define DMA6_ADDR     0xC8
  126. #define DMA6_COUNT    0xCA
  127. #define DMA6_PAGE     0x89
  128. #define DMA7_ADDR     0xCC
  129. #define DMA7_COUNT    0xCE
  130. #define DMA7_PAGE     0x8A
  131.  
  132. #define DSP_BLOCK_SIZE            0x0048
  133. #define DSP_DATA_AVAIL               0xE
  134. #define DSP_HALT_SINGLE_CYCLE_DMA 0x00D0
  135. #define DSP_READ_PORT                0xA
  136. #define DSP_READY                   0xAA
  137. #define DSP_RESET                    0x6
  138. #define DSP_TIME_CONSTANT         0x0040
  139. #define DSP_WRITE_PORT               0xC
  140.  
  141. #define AUTO_INIT                   1
  142. #define FAIL                        0
  143. #define FALSE                       0
  144. #define MASTER_VOLUME            0x22
  145. #define MIC_VOLUME               0x0A
  146. #define MIXER_ADDR                0x4
  147. #define MIXER_DATA                0x5
  148. #define MONO                        0
  149. #define PIC_END_OF_INT           0x20
  150. #define PIC_MASK                 0x21
  151. #define PIC_MODE                 0x20
  152. #define SUCCESS                     1
  153. #define SINGLE_CYCLE                0
  154. #define STEREO                      1
  155. #define TRUE                        1
  156. #define VOICE_VOLUME             0x04
  157.  
  158.  
  159. /*---------  FUNCTION PROTOTYPES  --------------------------------*/
  160. /*----------------------------------------------------------------*/
  161. char           GetBlasterEnv(int *, int *, int *),
  162.            InitDMADSP(unsigned long, int, int, FILE *),
  163.            ResetDSP(int);
  164.  
  165. unsigned int   FillHalfOfBuffer(int *, FILE *, unsigned char *);
  166.  
  167. unsigned long  AllocateDMABuffer(unsigned char **),
  168.            OnSamePage(unsigned char *);
  169.  
  170. void           BeginPlaying(unsigned int, char),
  171.            DSPOut(int, int),
  172.            FillBuffers(unsigned char *, int *, FILE *),
  173.            SetMixer(void);
  174.  
  175. void interrupt DMAOutputISR(void);   // Interrupt Service Routine
  176. /*----------------------------------------------------------------*/
  177.  
  178. /*---------  GLOBAL DECLARATIONS  --------------------------------*/
  179. /*----------------------------------------------------------------*/
  180. char  gBufNowPlaying,
  181.       gEndOfFile,
  182.       gLastBufferDonePlaying,
  183.       gMode,      // indicates MONO or STEREO
  184.       g16BitDMA;
  185.  
  186. int   gFormat,  // Same as "Pack" for 8-bit files (compression mode)
  187.       gIOPort;
  188.  
  189. unsigned long gNoOfBytesLeftInBlock,
  190.           gSamplesPerSec;
  191. /*----------------------------------------------------------------*/
  192.  
  193.  
  194. /*--- BEGIN main() -----------------------------------------------*/
  195. /*----------------------------------------------------------------*/
  196. int main(void)
  197. {
  198.   char  Filename[80],
  199.     Filetype[20],
  200.     RetValue;
  201.  
  202.   FILE *FileToPlay;
  203.  
  204.   int BufToFill,
  205.       DMAChan8Bit,
  206.       DMAChan16Bit,
  207.       IRQNumber,
  208.       IRQMask,
  209.       MaskSave,
  210.       Offset;
  211.  
  212.   unsigned char *DMABuffer;
  213.   unsigned int   BytesLeftToPlay;
  214.   unsigned long  BufPhysAddr;
  215.  
  216.   void interrupt (*IRQSave)(void);
  217.  
  218.  
  219.   clrscr();  // Clear the screen
  220.  
  221.  
  222.   /*--- OPEN FILE TO BE PLAYED -------------------------------*/
  223.   /*----------------------------------------------------------*/
  224.   printf("    Enter the .VOC file to play: ");
  225.   scanf("%s", Filename);
  226.   putchar('\n');
  227.   if ((FileToPlay = fopen(Filename, "rb")) == NULL)
  228.   {
  229.     printf("%s file non-existent or invalid format--PROGRAM TERMINATED!\n",
  230.        Filename);
  231.     exit(0);
  232.   }
  233.  
  234.   /*--- VERIFY FILE IS .VOC FORMAT---------------------------*/
  235.   /*---------------------------------------------------------*/
  236.   fread(Filetype, 20, 1, FileToPlay);  // Get file type description.
  237.   if (memcmp(Filetype, "Creative Voice File", 19))
  238.   {
  239.     puts("File is not a valid .VOC file--PROGRAM ABORTED");
  240.     fclose(FileToPlay);
  241.     exit(0);
  242.   }
  243.   else  // Point file pointer to beginning of the first data block.
  244.   {
  245.     fread(&Offset, 2, 1, FileToPlay);  // Offset is No. of bytes from start
  246.     rewind(FileToPlay);                // File ptr. to beg. of file
  247.     fseek(FileToPlay, (long) Offset, SEEK_CUR);  // Go to first data block
  248.   }
  249.  
  250.  
  251.   /*--- ALLOCATE BUFFERS -----------------------------------------*/
  252.   /*--------------------------------------------------------------*/
  253.   BufPhysAddr = AllocateDMABuffer(&DMABuffer);
  254.   if (BufPhysAddr == FAIL)
  255.   {
  256.     puts("DMA Buffer allocation failed!--PROGRAM ABORTED");
  257.     fclose(FileToPlay);
  258.     exit(0);
  259.   }
  260.  
  261.  
  262.   /*--- GET ENVIRONMENT VALUES -----------------------------------*/
  263.   /*--------------------------------------------------------------*/
  264.   RetValue = GetBlasterEnv(&DMAChan8Bit, &DMAChan16Bit, &IRQNumber);
  265.  
  266.  
  267.   /*--- PRINT OUT INFO -------------------------------------------*/
  268.   /*--------------------------------------------------------------*/
  269.   printf("    DMA Buffer Address     = %4x:%-4x (SEG:OFF) (hex)\n",
  270.      FP_SEG(DMABuffer), FP_OFF(DMABuffer));
  271.   printf("    DMA Buffer Phys. Addr. = %-7lu   (decimal)\n",  BufPhysAddr);
  272.   printf("    8-bit DMA channel      = %-5d     (decimal)\n",   DMAChan8Bit);
  273.   printf("    16-bit DMA channel     = %-5d     (decimal)\n",   DMAChan16Bit);
  274.   printf("    I/O port address       = %-3x       (hex)\n",       gIOPort);
  275.   printf("    IRQ number             = %-2d        (decimal)\n\n", IRQNumber);
  276.  
  277.  
  278.   /*--- ARE ENVIRONMENT VALUES VALID? --------------------------*/
  279.   /*------------------------------------------------------------*/
  280.   if (RetValue == FAIL)
  281.   {
  282.     puts("BLASTER env. string or parameter(s) missing--PROGRAM ABORTED!");
  283.     free(DMABuffer);
  284.     fclose(FileToPlay);
  285.     exit(0);
  286.   }
  287.  
  288.   /*--- RESET THE DSP ----------------------------------------*/
  289.   /*----------------------------------------------------------*/
  290.   if(ResetDSP(gIOPort) == FAIL)
  291.   {
  292.     puts("Unable to reset DSP chip--PROGRAM TERMINATED!");
  293.     free(DMABuffer);
  294.     fclose(FileToPlay);
  295.     exit(0);
  296.   }
  297.  
  298.  
  299.   /*--- SAVE CURRENT ISR FOR IRQNumber, THEN GIVE IRQNumber NEW ISR ---*/
  300.   /*-------------------------------------------------------------------*/
  301.   IRQSave = getvect(IRQNumber + 8);
  302.   setvect(IRQNumber + 8, DMAOutputISR);
  303.  
  304.  
  305.   /*--- SAVE CURRENT INTERRUPT MASK AND SET NEW INTERRUPT MASK -------*/
  306.   /*------------------------------------------------------------------*/
  307.   MaskSave = inp((int) PIC_MASK);
  308.   IRQMask = ((int) 1 << IRQNumber); // Shift a 1 left IRQNumber of bits
  309.   outp(PIC_MASK, (MaskSave & ~IRQMask)); // Enable previous AND new interrupts
  310.  
  311.  
  312.   /*--- PROGRAM THE DMA, DSP CHIPS -----------------------------------*/
  313.   /*------------------------------------------------------------------*/
  314.   if (InitDMADSP(BufPhysAddr, DMAChan8Bit, DMAChan16Bit, FileToPlay) == FAIL)
  315.   {
  316.     puts("InitDMADSP() fails--PROGRAM ABORTED!");
  317.     free(DMABuffer);
  318.     fclose(FileToPlay);
  319.     exit(0);
  320.   }
  321.  
  322.   /*--- FILL THE FIRST 1/2 OF DMA BUFFER BEFORE PLAYING BEGINS -------*/
  323.   /*------------------------------------------------------------------*/
  324.   BufToFill              = 0;      // Altered by FillHalfOfBuffer()
  325.   gEndOfFile             = FALSE;  // Altered by FillHalfOfBuffer()
  326.   gBufNowPlaying         = 0;      // Altered by ISR
  327.   gLastBufferDonePlaying = FALSE;  // Set in ISR
  328.   SetMixer();
  329.   BytesLeftToPlay = FillHalfOfBuffer(&BufToFill, FileToPlay, DMABuffer);
  330.  
  331.  
  332.   /*--- BEGIN PLAYING THE FILE ---------------------------------------*/
  333.   /*------------------------------------------------------------------*/
  334.   if (BytesLeftToPlay < DMA_BUF_SIZE / 2)  // File size is < 1/2 buffer size.
  335.   {
  336.     BeginPlaying(BytesLeftToPlay, SINGLE_CYCLE);
  337.     while (gBufNowPlaying == 0);  // Wait for playing to finish (ISR called)
  338.   }
  339.   else  // File size >= 1/2 buffer size
  340.   {
  341.     BeginPlaying(BytesLeftToPlay, AUTO_INIT);
  342.     FillBuffers(DMABuffer, &BufToFill, FileToPlay);
  343.   }
  344.  
  345.   DSPOut(gIOPort, DSP_HALT_SINGLE_CYCLE_DMA);  // Done playing, halt DMA
  346.  
  347.  
  348.   /*--- RESTORE ISR AND ORIGINAL IRQ VECTOR -------------------------*/
  349.   /*-----------------------------------------------------------------*/
  350.   outp(PIC_MASK, MaskSave);
  351.   setvect(IRQNumber + 8, IRQSave);
  352.  
  353.   free(DMABuffer);
  354.   fclose(FileToPlay);
  355.   return(0);
  356. }
  357.  
  358. /*************************************************************************
  359. *
  360. *  FUNCTION: BeginPlaying()
  361. *
  362. *  DESCRIPTION: If the file to play is smaller than 1/2 the DMA buffer,
  363. *               this function will be called only once.  It will program
  364. *               the DSP chip for single-cycle mode with a count equal to
  365. *               the number of audio bytes to play.  The audio will then
  366. *               begin playing.
  367. *
  368. *               If the file to play is larger than 1/2 the DMA buffer,
  369. *               this function will be called twice.  The first time it
  370. *               will be called by main() and it will:
  371. *
  372. *                  A) Program the DSP chip count (1/2 size of buffer).
  373. *
  374. *                  B) Program the DSP chip for auto-init mode.
  375. *
  376. *                  C) At this time the audio begins.
  377. *
  378. *                  The second (and last) time this function is called,
  379. *                  it will be called by FillBuffers().  This will occur
  380. *                  while the second to last buffer is playing.  In this
  381. *                  case BeginPlaying() will do the following.
  382. *
  383. *                  A) Program the DSP chip for single-cycle mode.
  384. *
  385. *                  B) Program the DSP count for the number of bytes left to
  386. *                     play in the last buffer.
  387. *
  388. *                  C) Now, when the second to last buffer ends playing,
  389. *                     the final buffer will play in single-cycle mode.
  390. *                     This allows only the remaining bytes in the last
  391. *                     buffer to be played, instead of the entire buffer,
  392. *                     as would be the case in auto-init mode.
  393. *
  394. *               NOTE: The last 1/2 buffer will always be programmed for
  395. *                     single-cycle DSP mode.  So when this function returns
  396. *                     to main(), main() sends a DSP command to halt DSP
  397. *                     single-cycle mode.
  398. *
  399. *************************************************************************/
  400. void BeginPlaying(unsigned int BytesLeftToPlay, char DMAMode)
  401. {
  402.   int Command;
  403.  
  404.   /*--- IF BytesLeftToPlay IS 0 OR 1, MAKE SURE THAT WHEN DSPOut() IS ---*/
  405.   /*--- CALLED, THE COUNT DOESN'T WRAP AROUND TO A + NUMBER WHEN 1 IS ---*/
  406.   /*    SUBTRACTED! -----------------------------------------------------*/
  407.   if(BytesLeftToPlay <= 1 && g16BitDMA)
  408.     BytesLeftToPlay = 2;
  409.   else if (BytesLeftToPlay == 0 && !g16BitDMA)
  410.     BytesLeftToPlay = 1;
  411.  
  412.   if (DMAMode == AUTO_INIT)
  413.   {
  414.     if (gFormat < 4)   // Program DSP size for 8-bit files
  415.     {
  416.       DSPOut(gIOPort, DSP_BLOCK_SIZE);
  417.       DSPOut(gIOPort, (int) ((BytesLeftToPlay - 1) & 0x00FF)); // LO byte
  418.       DSPOut(gIOPort, (int) ((BytesLeftToPlay - 1) >> 8));     // HI byte
  419.     }
  420.  
  421.     switch(gFormat)
  422.     {
  423.       case 0: // 8-Bit PCM
  424.     DSPOut(gIOPort, 0x001C);
  425.       break;
  426.  
  427.       case 1: // 4-Bit ADPCM
  428.     DSPOut(gIOPort, 0x007D);
  429.       break;
  430.  
  431.       case 2: // 2.6-bit ADPCM
  432.     DSPOut(gIOPort, 0x007F);
  433.       break;
  434.  
  435.       case 3: // 2-bit ADPCM
  436.     DSPOut(gIOPort, 0x001F);
  437.       break;
  438.  
  439.       case 4: // 16-bit PCM
  440.     DSPOut(gIOPort, 0x0041);
  441.     DSPOut(gIOPort, (int) ((gSamplesPerSec & 0x0000FF00) >> 8)); // Hi byte
  442.     DSPOut(gIOPort, (int) (gSamplesPerSec & 0x000000FF));        // Lo byte
  443.     DSPOut(gIOPort, 0x00B4);
  444.  
  445.     if (gMode == MONO)
  446.       DSPOut(gIOPort, 0x0010);
  447.     else if (gMode == STEREO)
  448.       DSPOut(gIOPort, 0x0030);
  449.  
  450.     // For 16-bits, count is 1/2 of buffer size in WORDS (1 word = 2 bytes)
  451.     DSPOut(gIOPort, (BytesLeftToPlay/2 - 1) & 0x00FF); // LO byte size
  452.     DSPOut(gIOPort, (BytesLeftToPlay/2 - 1) >> 8);     // HI byte size
  453.       break;
  454.  
  455.       default:
  456.     puts("ERROR in BeginPlaying(): Invalid \"Format\" compression type.");
  457.       break;
  458.     }
  459.  
  460.   }
  461.   else if (DMAMode == SINGLE_CYCLE)
  462.   {
  463.     switch(gFormat)
  464.     {
  465.       case 0: // 8-Bit PCM
  466.     puts("File: 8-bit PCM");
  467.     Command = 0x0014;
  468.       break;
  469.  
  470.       case 1: // 4-Bit ADPCM
  471.     puts("File: 4-bit ADPCM");
  472.     Command = 0x0074;
  473.       break;
  474.  
  475.       case 2: // 2.6-Bit ADPCM
  476.     puts("File: 2.6-bit ADPCM");
  477.     Command = 0x0076;
  478.       break;
  479.  
  480.       case 3: // 2-bit ADPCM
  481.     puts("File: 2-bit ADPCM");
  482.     Command = 0x0016;
  483.       break;
  484.  
  485.       case 4:  // 16-bit PCM
  486.     DSPOut(gIOPort, 0x0041);
  487.     DSPOut(gIOPort, (int) ((gSamplesPerSec & 0x0000FF00) >> 8)); // Hi byte
  488.     DSPOut(gIOPort, (int) (gSamplesPerSec & 0x000000FF));        // Lo byte
  489.     DSPOut(gIOPort, 0x00B0);
  490.     if (gMode == MONO)
  491.     {
  492.       puts("16-BIT PCM MONO");
  493.       DSPOut(gIOPort, 0x0010);
  494.     }
  495.     else if (gMode == STEREO)
  496.     {
  497.       puts("16-BIT PCM STEREO");
  498.       DSPOut(gIOPort, 0x0030);
  499.     }
  500.  
  501.     // For 16-bits, count is in WORDS (1 word = 2 bytes)
  502.     DSPOut(gIOPort, (BytesLeftToPlay/2 - 1) & 0x00FF); // LO byte size
  503.     DSPOut(gIOPort, (BytesLeftToPlay/2 - 1) >> 8);     // HI byte size
  504.       break;
  505.  
  506.       default:
  507.     puts("ERROR in BeginPlaying(): Invalid \"Format\" compression type.");
  508.       break;
  509.     }
  510.  
  511.     if (gFormat < 4) // Program DSP for 8-bit files
  512.     {
  513.       DSPOut(gIOPort, Command);
  514.       DSPOut(gIOPort, (BytesLeftToPlay - 1) & 0x00FF); // LO byte size
  515.       DSPOut(gIOPort, (BytesLeftToPlay - 1) >> 8);     // HI byte size
  516.     }
  517.  
  518.   }
  519.   return;
  520. }
  521.  
  522. /*************************************************************************
  523. *
  524. * FUNCTION: FillBuffers()
  525. *
  526. * DESCRIPTION: While one half of the buffer is playing, load the other
  527. *              half with data from the file.  After filling a buffer, wait
  528. *              for the other half to finish playing before filling it with
  529. *              new data.  Continue doing this until FillHalfOfBuffer()
  530. *              sets gEndOfFile to TRUE.
  531. *
  532. *              This function calls FillHalfOfBuffer(), which will return
  533. *              the number of bytes in the 1/2 buffer that should be played.
  534. *              This number will always be 1/2 of the DMA buffer size, except
  535. *              for the last buffer to be played.  When the final buffer is
  536. *              loaded, BeginPlaying() is called, which reprograms the DSP
  537. *              chip to play the last buffer in single-cycle mode, and
  538. *              reprograms the DSP chip to play only the bytes in the
  539. *              buffer that contain audio data.  (Doing this will prevent
  540. *              the contents of the entire buffer from being played.)
  541. *
  542. *              After the last buffer is programmed for single-cycle mode,
  543. *              the do-while loop finishes.  We now must wait until the
  544. *              last buffer finishes playing before we can return to main()
  545. *              and terminate the program.
  546. *
  547. *************************************************************************/
  548. void FillBuffers(unsigned char *DMABuffer, int *BufToFill, FILE *FileToPlay)
  549. {
  550.   unsigned int NumberOfAudioBytesInBuffer;
  551.  
  552.   do
  553.   {
  554.     while (*BufToFill == gBufNowPlaying); // Wait for buffer to finish playing
  555.  
  556.     NumberOfAudioBytesInBuffer = FillHalfOfBuffer(BufToFill, FileToPlay,
  557.                           DMABuffer);
  558.     if (NumberOfAudioBytesInBuffer < DMA_BUF_SIZE / 2)
  559.       BeginPlaying(NumberOfAudioBytesInBuffer, SINGLE_CYCLE);
  560.  
  561.   } while (!gEndOfFile);  // gEndOfFile set in FillHalfOfBuffer()
  562.  
  563.   while (gLastBufferDonePlaying == FALSE);  // Wait until done playing
  564.  
  565.   return;
  566. }
  567.  
  568. /*************************************************************************
  569. *
  570. * FUNCTION: FillHalfOfBuffer()
  571. *
  572. * DESCRIPTION: Depending upon the value of *BufToFill, fill either the
  573. *              bottom or top half of the DMA buffer with audio data from
  574. *              the file.
  575. *
  576. *              This function returns the number of bytes in the 1/2 buffer
  577. *              that should be played.  This number will always be 1/2
  578. *              the buffer size, except for the last buffer to be played.
  579. *              When the last buffer is loaded from the file, end of file
  580. *              will be reached and the number of bytes to be played will
  581. *              be <= 1/2 of the buffer size.
  582. *
  583. *************************************************************************/
  584. unsigned int FillHalfOfBuffer(int *BufToFill, FILE *FileToPlay,
  585.                   unsigned char *DMABuffer)
  586. {
  587.   unsigned char Temp[4];
  588.   unsigned int Count;
  589.  
  590.   if (*BufToFill == 1)  // Fill top 1/2 of DMA buffer
  591.     DMABuffer += DMA_BUF_SIZE / 2;
  592.  
  593.   fread(DMABuffer, DMA_BUF_SIZE/2, 1, FileToPlay);
  594.  
  595.  
  596.   for (Count = 0; Count < DMA_BUF_SIZE/2; Count++)
  597.   {
  598.     if (gNoOfBytesLeftInBlock != 0)  // Originally init'd in InitDMADSP()
  599.       gNoOfBytesLeftInBlock--;
  600.     else
  601.     {
  602.       switch(DMABuffer[Count])
  603.       {
  604.     /*--- A NEW DATA BLOCK HAS BEEN DETECTED--PROCESS OR IGNORE IT ---*/
  605.     case 0:
  606.       puts("BLOCK 0 detected in FillHalfOfBuffer()");
  607.       gEndOfFile = TRUE;
  608.     break;
  609.  
  610.     case 2:
  611.       puts("BLOCK 2 detected in FillHalfOfBuffer()");
  612.       if (Count < DMA_BUF_SIZE/2 - 3)
  613.       {
  614.         /*--- DATA BLOCK NOT ON BUFFER BOUNDARY -------------------*/
  615.         Count++;  // Skip past block type
  616.         gNoOfBytesLeftInBlock = (unsigned long) DMABuffer[Count];
  617.         Count++;
  618.         gNoOfBytesLeftInBlock += (unsigned long) (DMABuffer[Count] << 8);
  619.         Count++;
  620.         gNoOfBytesLeftInBlock += (unsigned long) (DMABuffer[Count] << 16);
  621.  
  622.         /*--- ELIMINATE BLOCK 2 FROM BEING PLAYED AS AUDIO DATA ---*/
  623.         memcpy(&DMABuffer[Count-3], &DMABuffer[Count+1],
  624.            DMA_BUF_SIZE/2 - 1 - Count);
  625.         fread(Temp, 4, 1, FileToPlay);
  626.         memcpy(DMABuffer + DMA_BUF_SIZE/2-3, Temp, 4);
  627.  
  628.       }
  629.       else
  630.       {
  631.         /*--- DATA BLOCK IS ON BUFFER BOUNDARY --------------------*/
  632.         switch(Count)
  633.         {
  634.           case DMA_BUF_SIZE/2 - 3:
  635.         fread(Temp, 4, 1, FileToPlay);
  636.         gNoOfBytesLeftInBlock += (unsigned long) DMABuffer[Count+1];
  637.         gNoOfBytesLeftInBlock += (unsigned long)
  638.                  (((unsigned long) DMABuffer[Count+2]) << 8);
  639.         gNoOfBytesLeftInBlock += (unsigned long)
  640.                  (((unsigned long) Temp[0]) << 16);
  641.         memcpy(&DMABuffer[Count], &Temp[1], 3);
  642.         Count += 3;
  643.           break;
  644.  
  645.           case DMA_BUF_SIZE/2 - 2:
  646.         fread(Temp, 4, 1, FileToPlay);
  647.         gNoOfBytesLeftInBlock += (unsigned long) DMABuffer[Count+1];
  648.         gNoOfBytesLeftInBlock += (unsigned long)
  649.                      (((unsigned long) Temp[0]) << 8);
  650.         gNoOfBytesLeftInBlock += (unsigned long)
  651.                      (((unsigned long) Temp[1]) << 16);
  652.         memcpy(&DMABuffer[Count], &Temp[2], 2);
  653.         Count += 2;
  654.           break;
  655.  
  656.           case DMA_BUF_SIZE/2 - 1:
  657.         fread(Temp, 1, 1, FileToPlay);
  658.         gNoOfBytesLeftInBlock += (unsigned long) Temp[0];
  659.         gNoOfBytesLeftInBlock += (unsigned long)
  660.                      (((unsigned long) Temp[1]) << 8);
  661.         gNoOfBytesLeftInBlock += (unsigned long)
  662.                      (((unsigned long) Temp[2]) << 16);
  663.         memcpy(&DMABuffer[Count], &Temp[3], 1);
  664.         Count++;
  665.           break;
  666.  
  667.           default:
  668.         printf("ERROR in FillHalfOfBuffer()--Count = %u\n", Count);
  669.           break;
  670.         }
  671.       }
  672.     break;  // End: "case 2:"
  673.  
  674.     default:
  675.       printf("ERROR: Block type %d detected in FillHalfOfBuffer()",
  676.          (int) DMABuffer[Count]);
  677.     break;
  678.       }
  679.  
  680.       if (gEndOfFile)  // Exit the for() loop if end-of-file is reached.
  681.     break;
  682.     }
  683.   }
  684.  
  685.   *BufToFill ^= 1;  // Toggle to fill other 1/2 of buffer next time.
  686.  
  687.   return(Count);
  688. }
  689.  
  690. /*************************************************************************
  691. *
  692. * FUNCTION: DMAOutputISR()
  693. *
  694. * DESCRIPTION:  Interrupt service routine.  Every time the DSP chip finishes
  695. *               playing half of the DMA buffer in auto-init mode, an
  696. *               interrupt is generated, which invokes this routine.
  697. *
  698. *               After gEndOfFile is set TRUE in FillHalfOfBuffer(), this
  699. *               ISR can determine that the "second to last buffer" has
  700. *               finished playing (set SecondToLastBufferPlayed to TRUE).
  701. *
  702. *               When this ISR is called again, we will know that the last
  703. *               buffer has finished playing--gLastBufferDonePlaying is
  704. *               set to TRUE.  gLastBufferDonePlaying is used by
  705. *               FillBuffers() to determine when to return control to
  706. *               main()--where the program will be terminated.
  707. *
  708. *************************************************************************/
  709. void interrupt DMAOutputISR(void)
  710. {
  711.   static char SecondToLastBufferPlayed = FALSE;
  712.   int IntStatus;
  713.  
  714.   if (g16BitDMA == TRUE)
  715.   {
  716.     outp(gIOPort + 4, 0x82);       // Select interrupt status reg. in mixer
  717.     IntStatus = inp(gIOPort + 5);  // Read interrupt status reg.
  718.  
  719.     if (IntStatus & 2)
  720.       inp(gIOPort + 0xF);   // Acknowledge interrupt (16-bit)
  721.   }
  722.   else
  723.     inp(gIOPort + (int) DSP_DATA_AVAIL);  // Acknowledge interrupt (8-bit)
  724.  
  725.   gBufNowPlaying ^= 1;
  726.   outp(PIC_MODE, (int) PIC_END_OF_INT); // End of interrupt
  727.  
  728.   if (SecondToLastBufferPlayed)
  729.     gLastBufferDonePlaying = TRUE;
  730.  
  731.   if (gEndOfFile)
  732.     SecondToLastBufferPlayed = TRUE;
  733.  
  734.   return;
  735. }
  736.  
  737.  
  738. /*************************************************************************
  739. *
  740. * FUNCTION: InitDMADSP()
  741. *
  742. * DESCRIPTION: This function reads the first data block of the file and
  743. *              from it obtains information that is needed to program the
  744. *              DMA and DSP chips.  After reading the data block, the file
  745. *              pointer points to the first byte of the voice data.
  746. *
  747. *              NOTE: The DMA chip is ALWAYS programmed for auto-init mode
  748. *                    (command 0x58)!  The DSP chip will be programmed for
  749. *                    auto-init or single-cycle mode depending upon
  750. *                    conditions--see BeginPlaying() for details.
  751. *
  752. *************************************************************************/
  753. char InitDMADSP(unsigned long BufPhysAddr, int DMAChan8Bit, int DMAChan16Bit,
  754.         FILE *FileToPlay)
  755. {
  756.   char BitsPerSample,
  757.        BlockType,
  758.        Pack;
  759.  
  760.   int  DMAAddr,
  761.        DMACount,
  762.        DMAPage,
  763.        Offset,
  764.        Page,
  765.        Temp;
  766.  
  767.   unsigned char ByteTimeConstant;
  768.   unsigned int  WordTimeConstant;
  769.  
  770.  
  771.   /*--- GET INFO NEEDED TO PROG DMA & DSP CHIPS FROM FIRST DATA BLOCK -----*/
  772.   /*-----------------------------------------------------------------------*/
  773.   fread(&BlockType, 1, 1, FileToPlay);
  774.  
  775.   switch(BlockType)
  776.   {
  777.     case 0:
  778.       puts("Data Block Type = 0");
  779.       return(FAIL);  // 1st block is terminator, play nothing
  780.  
  781.     case 1:
  782.       puts("Data Block Type = 1");
  783.       fread(&gNoOfBytesLeftInBlock, 3, 1, FileToPlay);
  784.       fread(&ByteTimeConstant, 1, 1, FileToPlay);
  785.       fread(&Pack, 1, 1, FileToPlay);
  786.       gFormat = (int) Pack;
  787.       gMode = MONO;  // Only MONO supported for block type 1
  788.       gNoOfBytesLeftInBlock -= 2;
  789.     break;
  790.  
  791.     case 8:
  792.       puts("Data Block Type = 8");
  793.       // Get info from block type 8, get voice data after block type 1
  794.       fseek(FileToPlay, 3, SEEK_CUR);
  795.       fread(&WordTimeConstant, 2, 1, FileToPlay);
  796.       ByteTimeConstant = (char) (WordTimeConstant >> 8); // Use upper byte
  797.       fread(&Pack, 1, 1, FileToPlay);
  798.       gFormat = (int) Pack;
  799.       fread(&gMode, 1, 1, FileToPlay);
  800.  
  801.       // Block type 1 immediately follows block type 8.
  802.       // So move file pointer to voice data.
  803.       fseek(FileToPlay, 1, SEEK_CUR);  // Skip block type (it's 1)
  804.       fread(&gNoOfBytesLeftInBlock, 3, 1, FileToPlay);
  805.       fseek(FileToPlay, 2, SEEK_CUR);  // File pointer @ voice data
  806.       gNoOfBytesLeftInBlock -= 2;
  807.     break;
  808.  
  809.     case 9:
  810.       puts("Data Block Type = 9");
  811.       fread(&gNoOfBytesLeftInBlock, 3, 1, FileToPlay);
  812.       fread(&gSamplesPerSec, 4, 1, FileToPlay);
  813.       ByteTimeConstant = (unsigned char) (256-1000000/gSamplesPerSec);
  814.       fread(&BitsPerSample, 1, 1, FileToPlay);
  815.       fread(&gMode, 1, 1, FileToPlay);  // "gMode" is same as "Channels"
  816.       fread(&gFormat, 2, 1, FileToPlay);
  817.       fseek(FileToPlay, 4, SEEK_CUR);  // Point to voice data
  818.       gNoOfBytesLeftInBlock -= 12;
  819.       gMode--;  // Make compatible with old format (0 = MONO, 1 = STEREO)
  820.     break;
  821.  
  822.     default:
  823.       printf("ERROR: InitDMADSP() BlockType = %d unsupported\n",
  824.          (int) BlockType);
  825.     return(FAIL);
  826.   }
  827.  
  828.  
  829.   /*--- GET DMA ADDR., COUNT, AND PAGE FOR THE DMA CHANNEL USED ----------*/
  830.   /*----------------------------------------------------------------------*/
  831.   if (gFormat < 4)
  832.   {
  833.     g16BitDMA = FALSE; // DMA is not 16-bit (it's 8-bit).
  834.  
  835.     switch(DMAChan8Bit)   // File is 8-bit.  Program DMA 8-bit DMA channel
  836.     {
  837.       case 0:
  838.     DMAAddr  = DMA0_ADDR;
  839.     DMACount = DMA0_COUNT;
  840.     DMAPage  = DMA0_PAGE;
  841.       break;
  842.  
  843.       case 1:
  844.     DMAAddr  = DMA1_ADDR;
  845.     DMACount = DMA1_COUNT;
  846.     DMAPage  = DMA1_PAGE;
  847.       break;
  848.  
  849.       case 3:
  850.     DMAAddr  = DMA3_ADDR;
  851.     DMACount = DMA3_COUNT;
  852.     DMAPage  = DMA3_PAGE;
  853.       break;
  854.  
  855.       default:
  856.     puts("File is 8-bit--invalid 8-bit DMA channel");
  857.       return(FAIL);
  858.     }
  859.   }
  860.   else
  861.   {
  862.     g16BitDMA = TRUE;     // DMA is 16-bit (not 8-bit).
  863.  
  864.     switch(DMAChan16Bit)  // File is 16-bit.  Program DMA 16-bit DMA channel
  865.     {
  866.       case 5:
  867.     DMAAddr  = DMA5_ADDR;
  868.     DMACount = DMA5_COUNT;
  869.     DMAPage  = DMA5_PAGE;
  870.       break;
  871.  
  872.       case 6:
  873.     DMAAddr  = DMA6_ADDR;
  874.     DMACount = DMA6_COUNT;
  875.     DMAPage  = DMA6_PAGE;
  876.       break;
  877.  
  878.       case 7:
  879.     DMAAddr  = DMA7_ADDR;
  880.     DMACount = DMA7_COUNT;
  881.     DMAPage  = DMA7_PAGE;
  882.       break;
  883.  
  884.       default:
  885.     puts("File is 16-bit--invalid 16-bit DMA channel");
  886.       return(FAIL);
  887.     }
  888.  
  889.     DMAChan16Bit -= 4; // Convert
  890.   }
  891.  
  892.  
  893.   /*--- PROGRAM THE DMA CHIP ---------------------------------------------*/
  894.   /*----------------------------------------------------------------------*/
  895.   Page   = (int) (BufPhysAddr >> 16);
  896.   Offset = (int) (BufPhysAddr & 0xFFFF);
  897.  
  898.   if (gFormat < 4) // 8-bit file--Program 8-bit DMA controller
  899.   {
  900.     outp(DMA8_MASK_REG, (int) (DMAChan8Bit | 4));     // Disable DMA while prog.
  901.     outp(DMA8_FF_REG,   (int) 0);                     // Clear the flip-flop
  902.  
  903.     outp(DMA8_MODE_REG, (int) (DMAChan8Bit  | 0x58));  // 8-bit auto-init
  904.     outp(DMACount, (int) ((DMA_BUF_SIZE - 1) & 0xFF)); // LO byte of count
  905.     outp(DMACount, (int) ((DMA_BUF_SIZE - 1) >> 8));   // HI byte of count
  906.   }
  907.   else    // 16-bit file--Program 16-bit DMA controller
  908.   {
  909.     // Offset for 16-bit DMA channel must be calculated differently...
  910.     // Shift Offset 1 bit right, then copy LSB of Page to MSB of Offset.
  911.     Temp = Page & 0x0001;  // Get LSB of Page and...
  912.     Temp <<= 15;           // ...move it to MSB of Temp.
  913.     Offset >>= 1;          // Divide Offset by 2
  914.     Offset &= 0x7FFF;      // Clear MSB of Offset
  915.     Offset |= Temp;        // Put LSB of Page into MSB of Offset
  916.  
  917.     outp(DMA16_MASK_REG, (int) (DMAChan16Bit | 4));    // Disable DMA while prog.
  918.     outp(DMA16_FF_REG,   (int) 0);                     // Clear the flip-flop
  919.  
  920.     outp(DMA16_MODE_REG, (int) (DMAChan16Bit  | 0x58));  // 16-bit auto-init
  921.     outp(DMACount, (int) ((DMA_BUF_SIZE/2 - 1) & 0xFF)); // LO byte of count
  922.     outp(DMACount, (int) ((DMA_BUF_SIZE/2 - 1) >> 8));   // HI byte of count
  923.   }
  924.  
  925.  
  926.   outp(DMAPage, Page);                   // Physical page number
  927.   outp(DMAAddr, (int) (Offset & 0xFF));  // LO byte address of buffer
  928.   outp(DMAAddr, (int) (Offset >> 8));    // HI byte address of buffer
  929.  
  930.  
  931.   // Done programming the DMA, enable it
  932.   if (gFormat < 4)
  933.     outp(DMA8_MASK_REG, DMAChan8Bit);
  934.   else
  935.     outp(DMA16_MASK_REG, DMAChan16Bit);
  936.  
  937.  
  938.   /*--- PROGRAM THE DSP CHIP ------------------------------------------*/
  939.   /*-------------------------------------------------------------------*/
  940.   DSPOut(gIOPort, (int) DSP_TIME_CONSTANT);
  941.   DSPOut(gIOPort, (int) ByteTimeConstant);
  942.   DSPOut(gIOPort, 0x00D1);  // Must turn speaker ON before doing D/A conv.
  943.  
  944.   return(SUCCESS);
  945. }
  946.  
  947.  
  948. /*************************************************************************
  949. *
  950. * FUNCTION: AllocateDMABuffer()
  951. *
  952. * DESCRIPTION : Allocate memory for the DMA buffer.  After memory is
  953. *               allocated for the buffer, call OnSamePage() to verify
  954. *               that the entire buffer is located on the same page.
  955. *               If the buffer crosses a page boundary, allocate another
  956. *               buffer. Continue this process until the DMA buffer resides
  957. *               entirely within the same page.
  958. *
  959. *               For every malloc() called, save a pointer that points to
  960. *               the block of memory allocated.  Deallocate ALL memory blocks
  961. *               allocated that cross a page boundary.  Once a memory block
  962. *               is allocated that does NOT cross a page boudary, this block
  963. *               will be used for the DMA buffer--any previously allocated
  964. *               memory blocks will be deallocated.
  965. *
  966. * ENTRY: **DMABuffer is the address of the pointer that will point to
  967. *        the memory allocated.
  968. *
  969. * EXIT: If a buffer is succesfully allocated, *DMABuffer will point to
  970. *       the buffer and the physical address of the buffer pointer will
  971. *       be returned.
  972. *
  973. *       If a buffer is NOT successfully allocated, return FAIL.
  974. *
  975. *************************************************************************/
  976. unsigned long AllocateDMABuffer(unsigned char **DMABuffer)
  977. {
  978.   unsigned char  BufferNotAllocated = TRUE,
  979.          Done = FALSE,
  980.         *PtrAllocated[100];
  981.  
  982.   int            i,
  983.          Index = 0;
  984.  
  985.   unsigned long  PhysAddress;
  986.  
  987.   do
  988.   {
  989.     *DMABuffer = (unsigned char *) malloc(DMA_BUF_SIZE);
  990.  
  991.     if (*DMABuffer != NULL)
  992.     {
  993.       /*--- Save the ptr for every malloc() performed ---*/
  994.       PtrAllocated[Index] = *DMABuffer;
  995.       Index++;
  996.  
  997.       /*--- If entire buffer is within one page, we're out of here! ---*/
  998.       PhysAddress = OnSamePage(*DMABuffer);
  999.       if (PhysAddress != FAIL)
  1000.       {
  1001.     BufferNotAllocated = FALSE;
  1002.     Done = TRUE;
  1003.       }
  1004.     }
  1005.     else
  1006.       Done = TRUE;  // malloc() couldn't supply requested memory
  1007.  
  1008.   } while (!Done);
  1009.  
  1010.  
  1011.   if (BufferNotAllocated)
  1012.   {
  1013.     Index++;             // Incr. Index so most recent malloc() gets free()d
  1014.     PhysAddress = FAIL;  // return FAIL
  1015.   }
  1016.  
  1017.   /*--- Deallocate all memory blocks crossing a page boundary ---*/
  1018.   for (i= 0; i < Index - 1; i++)
  1019.     free(PtrAllocated[i]);
  1020.  
  1021.   return(PhysAddress);
  1022. }
  1023.  
  1024.  
  1025. /**************************************************************************
  1026. *
  1027. * FUNCTION: OnSamePage()
  1028. *
  1029. * DESCRIPTION: Check the memory block pointed to by the parameter
  1030. *              passed to make sure the entire block of memory is on the
  1031. *              same page.  If a buffer DOES cross a page boundary,
  1032. *              return FAIL. Otherwise, return the physical address
  1033. *              of the beginning of the DMA buffer.
  1034. *
  1035. *              A page corresponds to the following addresses:
  1036. *
  1037. *              PAGE NO.   SEG:OFF ADDRESS          PHYSICAL ADDRESS
  1038. *              --------   ----------------------   ----------------
  1039. *                 0       0000:0000 to 0000:FFFF   00000 to 0FFFF
  1040. *                 1       1000:0000 to 1000:FFFF   10000 to 1FFFF
  1041. *                 .                 .                    .
  1042. *                 .                 .                    .
  1043. *                 E       E000:0000 to E000:FFFF   E0000 to EFFFF
  1044. *                 F       F000:0000 to F000:FFFF   F0000 to FFFFF
  1045. *
  1046. *              NOTE: The upper nibble of the physical address is the
  1047. *                    same as the page number!
  1048. *
  1049. * ENTRY: *DMABuffer - Points to beginning of DMA buffer.
  1050. *
  1051. * EXIT: If the buffer is located entirely within one page, return the
  1052. *       physical address of the buffer pointer.  Otherwise return FAIL.
  1053. *
  1054. **************************************************************************/
  1055. unsigned long OnSamePage(unsigned char *DMABuffer)
  1056. {
  1057.   unsigned long BegBuffer,
  1058.         EndBuffer,
  1059.         PhysAddress;
  1060.  
  1061.   /*----- Obtain the physical address of DMABuffer -----*/
  1062.   BegBuffer = ((unsigned long) (FP_SEG(DMABuffer)) << 4) +
  1063.            (unsigned long) FP_OFF(DMABuffer);
  1064.   EndBuffer   = BegBuffer + DMA_BUF_SIZE - 1;
  1065.   PhysAddress = BegBuffer;
  1066.  
  1067.   /*-- Get page numbers for start and end of DMA buffer. --*/
  1068.   BegBuffer >>= 16;
  1069.   EndBuffer >>= 16;
  1070.  
  1071.   if (BegBuffer == EndBuffer)
  1072.     return(PhysAddress);  // Entire buffer IS on same page!
  1073.   return(FAIL); // Entire buffer NOT on same page.  Thanks Intel!
  1074. }
  1075.  
  1076.  
  1077. /**************************************************************************
  1078. *
  1079. * FUNCTION: GetBlasterEnv()
  1080. *
  1081. * DESCRIPTION : Get the BLASTER environment variable and search its
  1082. *               string for the DMA channel, I/O address port, and
  1083. *               IRQ number.  Assign these values to the parameters passed
  1084. *               by the caller.
  1085. *
  1086. * ENTRY: All parameters passed are pointers to integers.  They will be
  1087. *        assigned the values found in the environment string.
  1088. *
  1089. * EXIT:  If DMA channel, I/O address, and IRQ number are found, return
  1090. *        PASS, otherwise return FAIL.
  1091. *
  1092. *
  1093. **************************************************************************/
  1094. char GetBlasterEnv(int *DMAChan8Bit, int *DMAChan16Bit, int *IRQNumber)
  1095. {
  1096.   char  Buffer[5],
  1097.     DMAChannelNotFound = TRUE,
  1098.        *EnvString,
  1099.     IOPortNotFound     = TRUE,
  1100.     IRQNotFound        = TRUE,
  1101.     SaveChar;
  1102.  
  1103.   int   digit,
  1104.     i,
  1105.     multiplier;
  1106.  
  1107.   EnvString = getenv("BLASTER");
  1108.  
  1109.   if (EnvString == NULL)
  1110.     return(FAIL);
  1111.  
  1112.   do
  1113.   {
  1114.     switch(*EnvString)
  1115.     {
  1116.       case 'A':  // I/O base port address found
  1117.       case 'a':
  1118.     EnvString++;
  1119.     for (i = 0; i < 3; i++)  // Grab the digits
  1120.     {
  1121.       Buffer[i] = *EnvString;
  1122.       EnvString++;
  1123.     }
  1124.  
  1125.     // The string is in HEX, convert it to decimal
  1126.     multiplier = 1;
  1127.     gIOPort = 0;
  1128.     for (i -= 1; i >= 0; i--)
  1129.     {
  1130.       // Convert to HEX
  1131.       if (Buffer[i] >= '0' && Buffer[i] <= '9')
  1132.         digit = Buffer[i] - '0';
  1133.       else if (Buffer[i] >= 'A' && Buffer[i] <= 'F')
  1134.         digit = Buffer[i] - 'A' + 10;
  1135.       else if (Buffer[i] >= 'a' && Buffer[i] <= 'f')
  1136.         digit = Buffer[i] - 'a' + 10;
  1137.  
  1138.       gIOPort = gIOPort + digit * multiplier;
  1139.       multiplier *= 16;
  1140.     }
  1141.  
  1142.     IOPortNotFound = FALSE;
  1143.       break;
  1144.  
  1145.  
  1146.       case 'D': // 8-bit DMA channel
  1147.       case 'd':
  1148.       case 'H': // 16-bit DMA channel
  1149.       case 'h':
  1150.     SaveChar = *EnvString;
  1151.     EnvString++;
  1152.     Buffer[0] = *EnvString;
  1153.     EnvString++;
  1154.  
  1155.     if (*EnvString >= '0' && *EnvString <= '9')
  1156.     {
  1157.       Buffer[1] = *EnvString; // DMA Channel No. is 2 digits
  1158.       Buffer[2] = NULL;
  1159.       EnvString++;
  1160.     }
  1161.     else
  1162.       Buffer[1] = NULL;       // DMA Channel No. is 1 digit
  1163.  
  1164.     if (SaveChar == 'D' || SaveChar == 'd')
  1165.       *DMAChan8Bit  = atoi(Buffer);  // 8-Bit DMA channel
  1166.     else
  1167.       *DMAChan16Bit = atoi(Buffer);  // 16-bit DMA channel
  1168.  
  1169.     DMAChannelNotFound = FALSE;
  1170.       break;
  1171.  
  1172.  
  1173.       case 'I':  // IRQ number
  1174.       case 'i':
  1175.     EnvString++;
  1176.     Buffer[0] = *EnvString;
  1177.     EnvString++;
  1178.  
  1179.     if (*EnvString >= '0' && *EnvString <= '9')
  1180.     {
  1181.       Buffer[1] = *EnvString; // IRQ No. is 2 digits
  1182.       Buffer[2] = NULL;
  1183.       EnvString++;
  1184.     }
  1185.     else
  1186.       Buffer[1] = NULL;       // IRQ No. is 1 digit
  1187.  
  1188.     *IRQNumber  = atoi(Buffer);
  1189.     IRQNotFound = FALSE;
  1190.       break;
  1191.  
  1192.  
  1193.       default:
  1194.     EnvString++;
  1195.       break;
  1196.     }
  1197.  
  1198.   } while (*EnvString != NULL);
  1199.  
  1200.   if (DMAChannelNotFound || IOPortNotFound || IRQNotFound)
  1201.     return(FAIL);
  1202.  
  1203.   return(SUCCESS);
  1204. }
  1205.  
  1206.  
  1207. /*************************************************************************
  1208. *
  1209. * FUNCTION: DSPOut()
  1210. *
  1211. * DESCRIPTION: Writes the value passed to this function to the DSP chip.
  1212. *
  1213. *************************************************************************/
  1214. void DSPOut(int IOBasePort, int WriteValue)
  1215. {
  1216.   // Wait until DSP is ready before writing the command
  1217.   while ((inp(IOBasePort + DSP_WRITE_PORT) & 0x80) != 0);
  1218.  
  1219.   outp(IOBasePort + DSP_WRITE_PORT, WriteValue);
  1220.   return;
  1221. }
  1222.  
  1223.  
  1224. /*************************************************************************
  1225. *
  1226. * FUNCTION: ResetDSP()
  1227. *
  1228. * DESCRIPTION: Self explanatory
  1229. *
  1230. *************************************************************************/
  1231. char ResetDSP(int IOBasePort)
  1232. {
  1233.   unsigned long i;
  1234.  
  1235.   outp(IOBasePort + DSP_RESET, (int) 1);
  1236.   delay(10); // wait 10 mS
  1237.   outp(IOBasePort + DSP_RESET, (int) 0);
  1238.  
  1239.   // Wait until data is available
  1240.   while ((inp(IOBasePort + DSP_DATA_AVAIL) & 0x80) == 0);
  1241.  
  1242.   if (inp(IOBasePort + DSP_READ_PORT) == DSP_READY)
  1243.     return(SUCCESS);
  1244.   return(FAIL);
  1245. }
  1246.  
  1247.  
  1248.  
  1249. /**************************************************************************
  1250. *
  1251. * FUNCTION: SetMixer()
  1252. *
  1253. * DESCRIPTION: Self explanatory
  1254. *
  1255. **************************************************************************/
  1256. void SetMixer(void)
  1257. {
  1258.   outp(gIOPort + MIXER_ADDR, (int) MIC_VOLUME);
  1259.   outp(gIOPort + MIXER_DATA, (int) 0x00);
  1260.  
  1261.   outp(gIOPort + MIXER_ADDR, (int) VOICE_VOLUME);
  1262.   outp(gIOPort + MIXER_DATA, (int) 0xFF);
  1263.  
  1264.   outp(gIOPort + MIXER_ADDR, (int) MASTER_VOLUME);
  1265.   outp(gIOPort + MIXER_DATA, (int) 0xFF);
  1266.  
  1267.   return;
  1268. }
  1269.