Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2. Copyright (C) 1996-1997 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. //=============================================================================
  21. // Routines for GUS support in QUAKE
  22. //
  23. // Author(s): Jayeson Lee-Steere
  24. //=============================================================================
  25.  
  26. #include "quakedef.h"
  27. #include "dosisms.h"
  28.  
  29. //=============================================================================
  30. // Author(s): Jayeson Lee-Steere
  31.  
  32. #define INI_STRING_SIZE 0x100
  33.  
  34. FILE *ini_fopen(const char *filename, const char *modes);
  35. int ini_fclose(FILE *f);
  36. void ini_fgets(FILE *f, const char *section, const char *field, char *s);
  37.  
  38. // Routines for reading from .INI files
  39. // The read routines are fairly efficient.
  40. //
  41. // Author(s): Jayeson Lee-Steere
  42.  
  43. #define MAX_SECTION_WIDTH 20
  44. #define MAX_FIELD_WIDTH 20
  45.  
  46. #define NUM_SECTION_BUFFERS 10
  47. #define NUM_FIELD_BUFFERS 20
  48.  
  49. struct section_buffer
  50. {
  51.    long offset;
  52.    char name[MAX_SECTION_WIDTH+1];
  53. };
  54.  
  55. struct field_buffer
  56. {
  57.    long offset;
  58.    int  section;
  59.    char name[MAX_FIELD_WIDTH+1];
  60. };
  61.  
  62. static FILE *current_file=NULL;
  63. static int   current_section;
  64.  
  65. static int current_section_buffer=0;
  66. static int current_field_buffer=0;
  67.  
  68. static struct section_buffer section_buffers[NUM_SECTION_BUFFERS];
  69. static struct field_buffer field_buffers[NUM_FIELD_BUFFERS];
  70. //***************************************************************************
  71. // Internal routines
  72. //***************************************************************************
  73. static char toupper(char c)
  74. {
  75.    if (c>='a' && c<='z')
  76.       c-=('a'-'A');
  77.    return(c);
  78. }
  79.  
  80. static void reset_buffer(FILE *f)
  81. {
  82.    int i;
  83.  
  84.    for (i=0;i<NUM_SECTION_BUFFERS;i++)
  85.       section_buffers[i].name[0]=0;
  86.    for (i=0;i<NUM_FIELD_BUFFERS;i++)
  87.       field_buffers[i].name[0]=0;
  88.  
  89.    current_file=f;
  90. }
  91.  
  92. // Sees if the current string is section "name" (i.e. ["name"]).
  93. // If "name"=="*", sees if the current string is any section
  94. // (i.e. [....]). Returns 1 if true else 0 if false.
  95. static int is_section(char *s,const char *name)
  96. {
  97.    int wild=0;
  98.  
  99.    // See if wild search
  100.    if (strcmp("*",name)==0)
  101.       wild=1;
  102.  
  103.    // Skip leading spaces
  104.    while (s[0]==' ')
  105.       s++;
  106.    // Look for leading "["
  107.    if (s[0]!='[')
  108.       return(0);
  109.    s++;
  110.    // Make sure name matches
  111.    while (s[0]!=']' && s[0]!=13 && s[0]!=10 && s[0]!=0 && name[0]!=0)
  112.    {
  113.       if (!wild)
  114.          if (toupper(s[0])!=toupper(name[0]))
  115.             return(0);
  116.       s++;
  117.       if (!wild)
  118.          name++;
  119.    }
  120.    if (!wild)
  121.       if (name[0]!=0)
  122.          return(0);
  123.    // Skip trailing spaces
  124.    while (s[0]==' ')
  125.       s++;
  126.    // Make sure we have trailing "]"
  127.    if (s[0]!=']')
  128.       return(0);
  129.    return(1);
  130. }
  131.  
  132. // Sees if the current string is field "name" (i.e. "name"=...).
  133. // If "name"=="*", sees if the current string is any field
  134. // (i.e. ...=...). Returns 1 if true else 0 if false.
  135. static int is_field(char *s,const char *name)
  136. {
  137.    int wild=0;
  138.  
  139.    // See if wild search
  140.    if (strcmp("*",name)==0)
  141.       wild=1;
  142.  
  143.    // Skip leading spaces
  144.    while (s[0]==' ')
  145.       s++;
  146.  
  147.    // Make sure name matches
  148.    while (s[0]!='=' && s[0]!=13 && s[0]!=10 && s[0]!=0 && name[0]!=0)
  149.    {
  150.       if (!wild)
  151.          if (toupper(s[0])!=toupper(name[0]))
  152.             return(0);
  153.       s++;
  154.       if (!wild)
  155.          name++;
  156.    }
  157.    if (!wild)
  158.       if (name[0]!=0)
  159.          return(0);
  160.    // Skip trailing spaces
  161.    while (s[0]==' ')
  162.       s++;
  163.    // Make sure we have an "="
  164.    if (s[0]!='=')
  165.       return(0);
  166.  
  167.    return(1);
  168. }
  169.  
  170. // Extracts the section name from a section heading
  171. // e.g. in="[hey man]" gives out="hey man"
  172. static void get_section_name(char *out, char *in)
  173. {
  174.    int i=0;
  175.  
  176.    // Skip spaces before '['
  177.    while (in[0]==' ')
  178.       in++;
  179.    // Make sure there is a '['
  180.    if (in[0]!='[')
  181.    {
  182.       out[0]=0;
  183.       return;
  184.    }
  185.    // Skip past '['
  186.    in++;
  187.    // Copy string if any to output string.
  188.    while (in[0]!=']' && in[0]!=13 && in[0]!=10 && in[0]!=0)
  189.    {
  190.       if (i<MAX_SECTION_WIDTH)
  191.       {
  192.          out[i]=in[0];
  193.          i++;
  194.       }
  195.       in++;
  196.    }
  197.    // Make sure string was terminated with ']'
  198.    if (in[0]!=']')
  199.    {
  200.       out[0]=0;
  201.       return;
  202.    }
  203.    // Remove trailing spaces
  204.    while (i>0 && out[i-1]==' ')
  205.       i--;
  206.    // Null terminate the output string.
  207.    out[i]=0;
  208. }
  209.  
  210. // Extracts the field name from a field line
  211. // e.g. in="sooty=life be in it" gives out="sooty"
  212. static void get_field_name(char *out, char *in)
  213. {
  214.    int i=0;
  215.  
  216.    // Skip leading spaces
  217.    while (in[0]==' ')
  218.       in++;
  219.    // Copy name to output string
  220.    while (in[0]!='=' && in[0]!=13 && in[0]!=10 && in[0]!=0)
  221.    {
  222.       if (i<MAX_FIELD_WIDTH)
  223.       {
  224.          out[i]=in[0];
  225.          i++;
  226.       }
  227.       in++;
  228.    }
  229.    // Make sure we stopped on "="
  230.    if (in[0]!='=')
  231.    {
  232.       out[0]=0;
  233.       return;
  234.    }
  235.    // Remove trailing spaces
  236.    while (i>0 && out[i-1]==' ')
  237.       i--;
  238.    // Null terminate the output string.
  239.    out[i]=0;
  240. }
  241.  
  242. // Returns the field data from string s.
  243. // e.g. in="wally = golly man" gives out="golly man"
  244. static void get_field_string(char *out, char *in)
  245. {
  246.    int i=0;
  247.  
  248.    // Find '=' if it exists
  249.    while (in[0]!='=' && in[0]!=13 && in[0]!=10 && in[0]!=0)
  250.       in++;
  251.    // If there is an '=', skip past it.
  252.    if (in[0]=='=')
  253.       in++;
  254.    // Skip any spaces between the '=' and string.
  255.    while (in[0]==' ' || in[0]=='[')
  256.       in++;
  257.    // Copy string, if there is one, to the output string.
  258.    while (in[0]!=13 && in[0]!=10 && in[0]!=0 && i<(INI_STRING_SIZE-1))
  259.    {
  260.       out[i]=in[0];
  261.       in++;
  262.       i++;
  263.    }
  264.    // Null terminate the output string.
  265.    out[i]=0;
  266. }
  267.  
  268. // Adds a section to the buffer
  269. static int add_section(char *instring, long offset)
  270. {
  271.    int i;
  272.    char section[MAX_SECTION_WIDTH+1];
  273.  
  274.    // Extract section name
  275.    get_section_name(section,instring);
  276.    // See if section already exists.
  277.    for (i=0;i<NUM_SECTION_BUFFERS;i++)
  278.       if (stricmp(section,section_buffers[i].name)==0)
  279.          return(i);
  280.    // Increment current_section_buffer
  281.    current_section_buffer++;
  282.    if (current_section_buffer>NUM_SECTION_BUFFERS)
  283.       current_section_buffer=0;
  284.    // Delete any field buffers that correspond to this section
  285.    for (i=0;i<NUM_FIELD_BUFFERS;i++)
  286.       if (field_buffers[i].section==current_section_buffer)
  287.          field_buffers[i].name[0]=0;
  288.    // Set buffer information
  289.    strcpy(section_buffers[current_section_buffer].name,section);
  290.    section_buffers[current_section_buffer].offset=offset;
  291.    return(current_section_buffer);
  292. }
  293.  
  294. // Adds a field to the buffer
  295. static void add_field(char *instring, int section, long offset)
  296. {
  297.    int i;
  298.    char field[MAX_FIELD_WIDTH+1];
  299.  
  300.    // Extract field name
  301.    get_field_name(field,instring);
  302.    // See if field already exists
  303.    for (i=0;i<NUM_FIELD_BUFFERS;i++)
  304.       if (field_buffers[i].section==section)
  305.          if (stricmp(field_buffers[i].name,field)==0)
  306.             return;
  307.    // Increment current_field_buffer
  308.    current_field_buffer++;
  309.    if (current_field_buffer>NUM_FIELD_BUFFERS)
  310.       current_field_buffer=0;
  311.    // Set buffer information
  312.    strcpy(field_buffers[current_field_buffer].name,field);
  313.    field_buffers[current_field_buffer].section=section;
  314.    field_buffers[current_field_buffer].offset=offset;
  315. }
  316.  
  317. // Identical to fgets except the string is trucated at the first ';',
  318. // carriage return or line feed.
  319. static char *stripped_fgets(char *s, int n, FILE *f)
  320. {
  321.    int i=0;
  322.  
  323.    if (fgets(s,n,f)==NULL)
  324.       return(NULL);
  325.  
  326.    while (s[i]!=';' && s[i]!=13 && s[i]!=10 && s[i]!=0)
  327.       i++;
  328.    s[i]=0;
  329.  
  330.    return(s);
  331. }
  332.  
  333. //***************************************************************************
  334. // Externally accessable routines
  335. //***************************************************************************
  336. // Opens an .INI file. Works like fopen
  337. FILE *ini_fopen(const char *filename, const char *modes)
  338. {
  339.    return(fopen(filename,modes));
  340. }
  341.  
  342. // Closes a .INI file. Works like fclose
  343. int ini_fclose(FILE *f)
  344. {
  345.    if (f==current_file)
  346.       reset_buffer(NULL);
  347.    return(fclose(f));
  348. }
  349.  
  350. // Puts "field" from "section" from .ini file "f" into "s".
  351. // If "section" does not exist or "field" does not exist in
  352. // section then s="";
  353. void ini_fgets(FILE *f, const char *section, const char *field, char *s)
  354. {
  355.    int i;
  356.    long start_pos,string_start_pos;
  357.    char ts[INI_STRING_SIZE*2];
  358.  
  359.    if (f!=current_file)
  360.       reset_buffer(f);
  361.  
  362.    // Default to "Not found"
  363.    s[0]=0;
  364.  
  365.    // See if section is in buffer
  366.    for (i=0;i<NUM_SECTION_BUFFERS;i++)
  367.       if (strnicmp(section_buffers[i].name,section,MAX_SECTION_WIDTH)==0)
  368.          break;
  369.  
  370.    // If section is in buffer, seek to it if necessary
  371.    if (i<NUM_SECTION_BUFFERS)
  372.    {
  373.       if (i!=current_section)
  374.       {
  375.          current_section=i;
  376.          fseek(f,section_buffers[i].offset,SEEK_SET);
  377.       }
  378.    }
  379.    // else look through .ini file for it.
  380.    else
  381.    {
  382.       // Make sure we are not at eof or this will cause trouble.
  383.       if (feof(f))
  384.          rewind(f);
  385.       start_pos=ftell(f);
  386.       while (1)
  387.       {
  388.          stripped_fgets(ts,INI_STRING_SIZE*2,f);
  389.          // If it is a section, add it to the section buffer
  390.          if (is_section(ts,"*"))
  391.             current_section=add_section(ts,ftell(f));
  392.          // If it is the section we are looking for, break.
  393.          if (is_section(ts,section))
  394.             break;
  395.          // If we reach the end of the file, rewind to the start.
  396.          if (feof(f))
  397.             rewind(f);
  398.          if (ftell(f)==start_pos)
  399.             return;
  400.       }
  401.    }
  402.  
  403.    // See if field is in buffer
  404.    for (i=0;i<NUM_FIELD_BUFFERS;i++)
  405.       if (field_buffers[i].section==current_section)
  406.          if (strnicmp(field_buffers[i].name,field,MAX_FIELD_WIDTH)==0)
  407.             break;
  408.  
  409.    // If field is in buffer, seek to it and read it
  410.    if (i<NUM_FIELD_BUFFERS)
  411.    {
  412.       fseek(f,field_buffers[i].offset,SEEK_SET);
  413.       stripped_fgets(ts,INI_STRING_SIZE*2,f);
  414.       get_field_string(s,ts);
  415.    }
  416.    else
  417.    // else search through section for field.
  418.    {
  419.       // Make sure we do not start at eof or this will cause problems.
  420.       if (feof(f))
  421.          fseek(f,section_buffers[current_section].offset,SEEK_SET);
  422.       start_pos=ftell(f);
  423.       while (1)
  424.       {
  425.          string_start_pos=ftell(f);
  426.          stripped_fgets(ts,INI_STRING_SIZE*2,f);
  427.          // If it is a field, add it to the buffer
  428.          if (is_field(ts,"*"))
  429.             add_field(ts,current_section,string_start_pos);
  430.          // If it is the field we are looking for, save it
  431.          if (is_field(ts,field))
  432.          {
  433.             get_field_string(s,ts);
  434.             break;
  435.          }
  436.          // If we reach the end of the section, start over
  437.          if (feof(f) || is_section(ts,"*"))
  438.             fseek(f,section_buffers[current_section].offset,SEEK_SET);
  439.          if (ftell(f)==start_pos)
  440.             return;
  441.       }
  442.    }
  443. }
  444.  
  445. //=============================================================================
  446.  
  447. #define BYTE unsigned char
  448. #define WORD unsigned short
  449. #define DWORD unsigned long
  450.  
  451. #define BUFFER_SIZE 4096
  452.  
  453. #define CODEC_ADC_INPUT_CONTROL_LEFT    0x00
  454. #define CODEC_ADC_INPUT_CONTROL_RIGHT   0x01
  455. #define CODEC_AUX1_INPUT_CONTROL_LEFT   0x02
  456. #define CODEC_AUX1_INPUT_CONTROL_RIGHT  0x03
  457. #define CODEC_AUX2_INPUT_CONTROL_LEFT   0x04
  458. #define CODEC_AUX2_INPUT_CONTROL_RIGHT  0x05
  459. #define CODEC_DAC_OUTPUT_CONTROL_LEFT   0x06
  460. #define CODEC_DAC_OUTPUT_CONTROL_RIGHT  0x07
  461. #define CODEC_FS_FORMAT                 0x08
  462. #define CODEC_INTERFACE_CONFIG          0x09
  463. #define CODEC_PIN_CONTROL               0x0A
  464. #define CODEC_ERROR_STATUS_AND_INIT     0x0B
  465. #define CODEC_MODE_AND_ID               0x0C
  466. #define CODEC_LOOPBACK_CONTROL          0x0D
  467. #define CODEC_PLAYBACK_UPPER_BASE_COUNT 0x0E
  468. #define CODEC_PLAYBACK_LOWER_BASE_COUNT 0x0F
  469.  
  470. #define SET_CONTROL                     0x00
  471. #define SET_FREQUENCY                   0x01
  472. #define SET_START_HIGH                  0x02
  473. #define SET_START_LOW                   0x03
  474. #define SET_END_HIGH                    0x04
  475. #define SET_END_LOW                     0x05
  476. #define SET_VOLUME_RATE                 0x06
  477. #define SET_VOLUME_START                0x07
  478. #define SET_VOLUME_END                  0x08
  479. #define SET_CURR_VOLUME                 0x09
  480. #define SET_VOLUME                      0x09
  481. #define SET_ACC_HIGH                    0x0A
  482. #define SET_ACC_LOW                     0x0B
  483. #define SET_BALANCE                     0x0C
  484. #define SET_VOLUME_CONTROL              0x0D
  485. #define SET_VOICES                      0x0E
  486.  
  487. #define DMA_CONTROL                     0x41
  488. #define SET_DMA_ADDRESS                 0x42
  489. #define SET_DRAM_LOW                    0x43
  490. #define SET_DRAM_HIGH                   0x44
  491. #define ADLIB_CONTROL                   0x45
  492. #define ADLIB_TIMER1                    0x46
  493. #define ADLIB_TIMER2                    0x47
  494. #define SET_RECORD_RATE                 0x48
  495. #define RECORD_CONTROL                  0x49
  496. #define SET_JOYSTICK                    0x4B
  497. #define MASTER_RESET                    0x4C
  498.  
  499. #define GET_CONTROL                     0x80
  500. #define GET_FREQUENCY                   0x81
  501. #define GET_START_HIGH                  0x82
  502. #define GET_START_LOW                   0x83
  503. #define GET_END_HIGH                    0x84
  504. #define GET_END_LOW                     0x85
  505. #define GET_VOLUME_RATE                 0x86
  506. #define GET_VOLUME_START                0x87
  507. #define GET_VOLUME_END                  0x88
  508. #define GET_VOLUME                      0x89
  509. #define GET_ACC_HIGH                    0x8A
  510. #define GET_ACC_LOW                     0x8B
  511. #define GET_BALANCE                     0x8C
  512. #define GET_VOLUME_CONTROL              0x8D
  513. #define GET_VOICES                      0x8E
  514. #define GET_IRQV                        0x8F
  515.  
  516. struct CodecRateStruct
  517. {
  518.    WORD Rate;
  519.    BYTE FSVal;
  520. };
  521.  
  522. struct Gf1RateStruct
  523. {
  524.    WORD Rate;
  525.    BYTE Voices;
  526. };
  527.  
  528. //=============================================================================
  529. // Reference variables in SND_DOS.C
  530. //=============================================================================
  531. extern short *dma_buffer;
  532.  
  533. //=============================================================================
  534. // GUS-only variables
  535. //=============================================================================
  536. static BYTE HaveCodec=0;
  537.  
  538. static WORD CodecRegisterSelect;
  539. static WORD CodecData;
  540. static WORD CodecStatus;
  541. static WORD Gf1TimerControl;
  542. static WORD Gf1PageRegister;
  543. static WORD Gf1RegisterSelect;
  544. static WORD Gf1DataLow;
  545. static WORD Gf1DataHigh;
  546.  
  547. static BYTE DmaChannel;
  548.  
  549. static BYTE PageRegs[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
  550. static BYTE AddrRegs[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc };
  551. static BYTE CountRegs[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce };
  552.  
  553. static WORD AddrReg;
  554. static WORD CountReg;
  555. static WORD ModeReg;
  556. static WORD DisableReg;
  557. static WORD ClearReg;
  558.  
  559. static struct CodecRateStruct CodecRates[]=
  560. {
  561.    { 5512,0x01},
  562.    { 6620,0x0F},
  563.    { 8000,0x00},
  564.    { 9600,0x0E},
  565.    {11025,0x03},
  566.    {16000,0x02},
  567.    {18900,0x05},
  568.    {22050,0x07},
  569.    {27420,0x04},
  570.    {32000,0x06},
  571.    {33075,0x0D},
  572.    {37800,0x09},
  573.    {44100,0x0B},
  574.    {48000,0x0C},
  575.    {    0,0x00} // End marker
  576. };
  577.  
  578. static struct Gf1RateStruct Gf1Rates[]=
  579. {
  580.    {19293,32},
  581.    {19916,31},
  582.    {20580,30},
  583.    {21289,29},
  584.    {22050,28},
  585.    {22866,27},
  586.    {23746,26},
  587.    {24696,25},
  588.    {25725,24},
  589.    {26843,23},
  590.    {28063,22},
  591.    {29400,21},
  592.    {30870,20},
  593.    {32494,19},
  594.    {34300,18},
  595.    {36317,17},
  596.    {38587,16},
  597.    {41160,15},
  598.    {44100,14},
  599.    {0,0}
  600. };
  601.  
  602. //=============================================================================
  603. // Basic GF1 functions
  604. //=============================================================================
  605. void SetGf18(BYTE reg,BYTE data)
  606. {
  607.    dos_outportb(Gf1RegisterSelect,reg);
  608.    dos_outportb(Gf1DataHigh,data);
  609. }
  610.  
  611. void SetGf116(BYTE reg,WORD data)
  612. {
  613.    dos_outportb(Gf1RegisterSelect,reg);
  614.    dos_outportw(Gf1DataLow,data);
  615. }
  616.  
  617. BYTE GetGf18(BYTE reg)
  618. {
  619.    dos_outportb(Gf1RegisterSelect,reg);
  620.    return(dos_inportb(Gf1DataHigh));
  621. }
  622.  
  623. WORD GetGf116(BYTE reg)
  624. {
  625.    dos_outportb(Gf1RegisterSelect,reg);
  626.    return(dos_inportw(Gf1DataLow));
  627. }
  628.  
  629. void Gf1Delay(void)
  630. {
  631.    int i;
  632.  
  633.    for (i=0;i<27;i++)
  634.       dos_inportb(Gf1TimerControl);
  635. }
  636.  
  637. DWORD ConvertTo16(DWORD Address)
  638. {
  639.    return( ((Address>>1) & 0x0001FFFF) | (Address & 0x000C0000L) );
  640. }
  641.  
  642. void ClearGf1Ints(void)
  643. {
  644.    int i;
  645.  
  646.    SetGf18(DMA_CONTROL,0x00);
  647.    SetGf18(ADLIB_CONTROL,0x00);
  648.    SetGf18(RECORD_CONTROL,0x00);
  649.                
  650.    GetGf18(DMA_CONTROL);
  651.    GetGf18(RECORD_CONTROL);
  652.    for (i=0;i<32;i++);
  653.       GetGf18(GET_IRQV);
  654. }
  655.  
  656.  
  657. //=============================================================================
  658. // Get Interwave (UltraSound PnP) configuration if any
  659. //=============================================================================
  660. static qboolean GUS_GetIWData(void)
  661. {
  662.    char *Interwave,s[INI_STRING_SIZE];
  663.    FILE *IwFile;
  664.    int  CodecBase,CodecDma,i;
  665.  
  666.    Interwave=getenv("INTERWAVE");
  667.    if (Interwave==NULL)
  668.       return(false);
  669.  
  670.    // Open IW.INI
  671.    IwFile=ini_fopen(Interwave,"rt");
  672.    if (IwFile==NULL)
  673.       return(false);
  674.  
  675.    // Read codec base and codec DMA
  676.    ini_fgets(IwFile,"setup 0","CodecBase",s);
  677.    sscanf(s,"%X",&CodecBase);
  678.    ini_fgets(IwFile,"setup 0","DMA2",s);
  679.    sscanf(s,"%i",&CodecDma);
  680.  
  681.    ini_fclose(IwFile);
  682.  
  683.    // Make sure numbers OK
  684.    if (CodecBase==0 || CodecDma==0)
  685.       return(false);
  686.  
  687.    CodecRegisterSelect=CodecBase;
  688.    CodecData=CodecBase+1;
  689.    CodecStatus=CodecBase+2;
  690.    DmaChannel=CodecDma;
  691.  
  692.    // Make sure there is a CODEC at the CODEC base
  693.  
  694.    // Clear any pending IRQs
  695.    dos_inportb(CodecStatus);
  696.    dos_outportb(CodecStatus,0);
  697.  
  698.    // Wait for 'INIT' bit to clear
  699.    for (i=0;i<0xFFFF;i++)
  700.       if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0)
  701.          break;
  702.    if (i==0xFFFF)
  703.       return(false);
  704.  
  705.    // Get chip revision - can not be zero
  706.    dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID);
  707.    if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID)
  708.       return(false);
  709.    if ((dos_inportb(CodecData) & 0x0F) == 0)
  710.       return(false);
  711.  
  712.    HaveCodec=1;
  713.    Con_Printf("Sound Card is UltraSound PnP\n");
  714.    return(true);
  715. }
  716.  
  717. //=============================================================================
  718. // Get UltraSound MAX configuration if any
  719. //=============================================================================
  720. static qboolean GUS_GetMAXData(void)
  721. {
  722.    char *Ultrasnd,*Ultra16;
  723.    int  i;
  724.    int  GusBase,Dma1,Dma2,Irq1,Irq2;
  725.    int  CodecBase,CodecDma,CodecIrq,CodecType;
  726.    BYTE MaxVal;
  727.  
  728.    Ultrasnd=getenv("ULTRASND");
  729.    Ultra16=getenv("ULTRA16");
  730.    if (Ultrasnd==NULL || Ultra16==NULL)
  731.       return(false);
  732.  
  733.    sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2);
  734.    sscanf(Ultra16,"%x,%i,%i,%i",&CodecBase,&CodecDma,&CodecIrq,&CodecType);
  735.  
  736.    if (CodecType==0 && CodecDma!=0)
  737.       DmaChannel=CodecDma & 0x07;
  738.    else
  739.       DmaChannel=Dma2 & 0x07;
  740.  
  741.    // Make sure there is a GUS at GUS base
  742.    dos_outportb(GusBase+0x08,0x55);
  743.    if (dos_inportb(GusBase+0x0A)!=0x55)
  744.       return(false);
  745.    dos_outportb(GusBase+0x08,0xAA);
  746.    if (dos_inportb(GusBase+0x0A)!=0xAA)
  747.       return(false);
  748.  
  749.    // Program CODEC control register
  750.    MaxVal=((CodecBase & 0xF0)>>4) | 0x40;
  751.    if (Dma1 > 3)
  752.       MaxVal|=0x10;
  753.    if (Dma2 > 3)
  754.       MaxVal|=0x20;
  755.    dos_outportb(GusBase+0x106,MaxVal);
  756.  
  757.    CodecRegisterSelect=CodecBase;
  758.    CodecData=CodecBase+1;
  759.    CodecStatus=CodecBase+2;
  760.  
  761.    // Make sure there is a CODEC at the CODEC base
  762.  
  763.    // Clear any pending IRQs
  764.    dos_inportb(CodecStatus);
  765.    dos_outportb(CodecStatus,0);
  766.  
  767.    // Wait for 'INIT' bit to clear
  768.    for (i=0;i<0xFFFF;i++)
  769.       if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0)
  770.          break;
  771.    if (i==0xFFFF)
  772.       return(false);
  773.  
  774.    // Get chip revision - can not be zero
  775.    dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID);
  776.    if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID)
  777.       return(false);
  778.    if ((dos_inportb(CodecData) & 0x0F) == 0)
  779.       return(false);
  780.  
  781.    HaveCodec=1;
  782.    Con_Printf("Sound Card is UltraSound MAX\n");
  783.    return(true);
  784. }
  785.  
  786. //=============================================================================
  787. // Get regular UltraSound configuration if any
  788. //=============================================================================
  789. static qboolean GUS_GetGUSData(void)
  790. {
  791.    char *Ultrasnd;
  792.    int  GusBase,Dma1,Dma2,Irq1,Irq2,i;
  793.  
  794.    Ultrasnd=getenv("ULTRASND");
  795.    if (Ultrasnd==NULL)
  796.       return(false);
  797.  
  798.    sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2);
  799.  
  800.    DmaChannel=Dma1 & 0x07;
  801.  
  802.    // Make sure there is a GUS at GUS base
  803.    dos_outportb(GusBase+0x08,0x55);
  804.    if (dos_inportb(GusBase+0x0A)!=0x55)
  805.       return(false);
  806.    dos_outportb(GusBase+0x08,0xAA);
  807.    if (dos_inportb(GusBase+0x0A)!=0xAA)
  808.       return(false);
  809.  
  810.    Gf1TimerControl   = GusBase+0x008;
  811.    Gf1PageRegister   = GusBase+0x102;
  812.    Gf1RegisterSelect = GusBase+0x103;
  813.    Gf1DataLow        = GusBase+0x104;
  814.    Gf1DataHigh       = GusBase+0x105;
  815.  
  816.    // Reset the GUS
  817.    SetGf18(MASTER_RESET,0x00);
  818.    Gf1Delay();
  819.    Gf1Delay();
  820.    SetGf18(MASTER_RESET,0x01);
  821.    Gf1Delay();
  822.    Gf1Delay();
  823.  
  824.    // Set to max (32) voices
  825.    SetGf18(SET_VOICES,0xDF);
  826.  
  827.    // Clear any pending IRQ's
  828.    ClearGf1Ints();
  829.  
  830.    // Set all registers to known values
  831.    for (i=0;i<32;i++)
  832.    {
  833.       dos_outportb(Gf1PageRegister,i);
  834.       SetGf18(SET_CONTROL,0x03);
  835.       SetGf18(SET_VOLUME_CONTROL,0x03);
  836.       Gf1Delay();
  837.       SetGf18(SET_CONTROL,0x03);
  838.       SetGf18(SET_VOLUME_CONTROL,0x03);
  839.       SetGf116(SET_START_HIGH,0);
  840.       SetGf116(SET_START_LOW,0);
  841.       SetGf116(SET_END_HIGH,0);
  842.       SetGf116(SET_END_LOW,0);
  843.       SetGf116(SET_ACC_HIGH,0);
  844.       SetGf116(SET_ACC_LOW,0);
  845.       SetGf18(SET_VOLUME_RATE,63);
  846.       SetGf18(SET_VOLUME_START,5);
  847.       SetGf18(SET_VOLUME_END,251);
  848.       SetGf116(SET_VOLUME,5<<8);
  849.    }
  850.  
  851.    // Clear any pending IRQ's
  852.    ClearGf1Ints();
  853.  
  854.    // Enable DAC etc.
  855.    SetGf18(MASTER_RESET,0x07);
  856.  
  857.    // Enable line output so we can hear something
  858.    dos_outportb(GusBase,0x08);
  859.  
  860.    HaveCodec=0;
  861.    Con_Printf("Sound Card is UltraSound\n");
  862.    return(true);
  863. }
  864.  
  865.  
  866. //=============================================================================
  867. // Programs the DMA controller to start DMAing in Auto-init mode
  868. //=============================================================================
  869. static void GUS_StartDMA(BYTE DmaChannel,short *dma_buffer,int count)
  870. {
  871.    int mode;
  872.    int RealAddr;
  873.  
  874.    RealAddr = ptr2real(dma_buffer);
  875.  
  876.    if (DmaChannel <= 3)
  877.    {
  878.       ModeReg = 0x0B;
  879.       DisableReg = 0x0A;
  880.       ClearReg = 0x0E;
  881.    }
  882.    else
  883.    {
  884.       ModeReg = 0xD6;
  885.       DisableReg = 0xD4;
  886.       ClearReg = 0xDC;
  887.    }
  888.    CountReg=CountRegs[DmaChannel];
  889.    AddrReg=AddrRegs[DmaChannel];
  890.  
  891.    dos_outportb(DisableReg, DmaChannel | 4);    // disable channel
  892.  
  893.    // set mode- see "undocumented pc", p.876
  894.    mode = (1<<6)                // single-cycle
  895.           +(0<<5)               // address increment
  896.           +(1<<4)               // auto-init dma
  897.           +(2<<2)               // read
  898.           +(DmaChannel & 0x03); // channel #
  899.    dos_outportb(ModeReg, mode);
  900.  
  901.    // set page
  902.    dos_outportb(PageRegs[DmaChannel], RealAddr >> 16);
  903.  
  904.    if (DmaChannel <= 3)
  905.    {    // address is in bytes
  906.       dos_outportb(0x0C, 0);            // prepare to send 16-bit value
  907.       dos_outportb(AddrReg, RealAddr & 0xff);
  908.       dos_outportb(AddrReg, (RealAddr>>8) & 0xff);
  909.  
  910.       dos_outportb(0x0C, 0);            // prepare to send 16-bit value
  911.       dos_outportb(CountReg, (count-1) & 0xff);
  912.       dos_outportb(CountReg, (count-1) >> 8);
  913.    }
  914.    else
  915.    {    // address is in words
  916.       dos_outportb(0xD8, 0);            // prepare to send 16-bit value
  917.       dos_outportb(AddrReg, (RealAddr>>1) & 0xff);
  918.       dos_outportb(AddrReg, (RealAddr>>9) & 0xff);
  919.  
  920.       dos_outportb(0xD8, 0);            // prepare to send 16-bit value
  921.       dos_outportb(CountReg, ((count>>1)-1) & 0xff);
  922.       dos_outportb(CountReg, ((count>>1)-1) >> 8);
  923.    }
  924.  
  925.    dos_outportb(ClearReg, 0);           // clear write mask
  926.    dos_outportb(DisableReg, DmaChannel & ~4);
  927. }
  928.  
  929. //=============================================================================
  930. // Starts the CODEC playing
  931. //=============================================================================
  932. static void GUS_StartCODEC(int count,BYTE FSVal)
  933. {
  934.    int i,j;
  935.  
  936.    // Clear any pending IRQs
  937.    dos_inportb(CodecStatus);
  938.    dos_outportb(CodecStatus,0);
  939.  
  940.    // Set mode to 2
  941.    dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID);
  942.    dos_outportb(CodecData,0xC0);
  943.  
  944.    // Stop any playback or capture which may be happening
  945.    dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
  946.    dos_outportb(CodecData,dos_inportb(CodecData) & 0xFC);
  947.  
  948.    // Set FS
  949.    dos_outportb(CodecRegisterSelect,CODEC_FS_FORMAT | 0x40);
  950.    dos_outportb(CodecData,FSVal | 0x50); // Or in stereo and 16 bit bits
  951.  
  952.    // Wait a bit
  953.    for (i=0;i<10;i++)
  954.       dos_inportb(CodecData);
  955.  
  956.    // Routine 1 to counter CODEC bug - wait for init bit to clear and then a
  957.    // bit longer (i=min loop count, j=timeout
  958.    for (i=0,j=0;i<1000 && j<0x7FFFF;j++)
  959.       if ((dos_inportb(CodecRegisterSelect) & 0x80)==0)
  960.          i++;
  961.  
  962.    // Routine 2 to counter CODEC bug - this is from Forte's code. For me it
  963.    // does not seem to cure the problem, but is added security
  964.    // Waits till we can modify index register
  965.    for (j=0;j<0x7FFFF;j++)
  966.    {
  967.       dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40);
  968.       if (dos_inportb(CodecRegisterSelect)==(CODEC_INTERFACE_CONFIG | 0x40))
  969.          break;
  970.    }
  971.  
  972.    // Perform ACAL
  973.    dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40);
  974.    dos_outportb(CodecData,0x08);
  975.  
  976.    // Clear MCE bit - this makes ACAL happen
  977.    dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
  978.  
  979.    // Wait for ACAL to finish
  980.    for (j=0;j<0x7FFFF;j++)
  981.    {
  982.       if ((dos_inportb(CodecRegisterSelect) & 0x80) != 0)
  983.          continue;
  984.       dos_outportb(CodecRegisterSelect,CODEC_ERROR_STATUS_AND_INIT);
  985.       if ((dos_inportb(CodecData) & 0x20) == 0)
  986.          break;
  987.    }
  988.  
  989.    // Clear ACAL bit
  990.    dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40);
  991.    dos_outportb(CodecData,0x00);
  992.    dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
  993.  
  994.    // Set some other junk
  995.    dos_outportb(CodecRegisterSelect,CODEC_LOOPBACK_CONTROL);
  996.    dos_outportb(CodecData,0x00);
  997.    dos_outportb(CodecRegisterSelect,CODEC_PIN_CONTROL);
  998.    dos_outportb(CodecData,0x08); // IRQ is disabled in PIN control
  999.  
  1000.    // Set count (it doesn't really matter what value we stuff in here
  1001.    dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_LOWER_BASE_COUNT);
  1002.    dos_outportb(CodecData,count & 0xFF);
  1003.    dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_UPPER_BASE_COUNT);
  1004.    dos_outportb(CodecData,count >> 8);
  1005.  
  1006.    // Start playback
  1007.    dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
  1008.    dos_outportb(CodecData,0x01);
  1009. }
  1010.  
  1011. //=============================================================================
  1012. // Starts the GF1 playing
  1013. //=============================================================================
  1014. static void GUS_StartGf1(int count,BYTE Voices)
  1015. {
  1016.    DWORD StartAddressL,EndAddressL,StartAddressR,EndAddressR;
  1017.  
  1018.    // Set number of voices to give us the sampling rate we want
  1019.    SetGf18(SET_VOICES,0xC0 | (Voices-1));
  1020.  
  1021.    // Figure out addresses
  1022.    StartAddressL=ConvertTo16(0);
  1023.    EndAddressL=ConvertTo16(count-2-2);
  1024.    StartAddressR=ConvertTo16(2);
  1025.    EndAddressR=ConvertTo16(count-2);
  1026.  
  1027.    // Set left voice addresses
  1028.    dos_outportb(Gf1PageRegister,0);
  1029.    SetGf116(SET_START_LOW,StartAddressL<<9);
  1030.    SetGf116(SET_START_HIGH,StartAddressL>>7);
  1031.    SetGf116(SET_ACC_LOW,StartAddressL<<9);
  1032.    SetGf116(SET_ACC_HIGH,StartAddressL>>7);
  1033.    SetGf116(SET_END_LOW,EndAddressL<<9);
  1034.    SetGf116(SET_END_HIGH,EndAddressL>>7);
  1035.    // Set balance to full left
  1036.    SetGf18(SET_BALANCE,0);
  1037.    // Set volume to full
  1038.    SetGf116(SET_VOLUME,0xFFF0);
  1039.    // Set FC to 2 (so we play every second sample)
  1040.    SetGf116(SET_FREQUENCY,0x0800);
  1041.  
  1042.    // Set right voice addresses
  1043.    dos_outportb(Gf1PageRegister,1);
  1044.    SetGf116(SET_START_LOW,StartAddressR<<9);
  1045.    SetGf116(SET_START_HIGH,StartAddressR>>7);
  1046.    SetGf116(SET_ACC_LOW,StartAddressR<<9);
  1047.    SetGf116(SET_ACC_HIGH,StartAddressR>>7);
  1048.    SetGf116(SET_END_LOW,EndAddressR<<9);
  1049.    SetGf116(SET_END_HIGH,EndAddressR>>7);
  1050.    // Set balance to full right
  1051.    SetGf18(SET_BALANCE,15);
  1052.    // Set volume to full
  1053.    SetGf116(SET_VOLUME,0xFFF0);
  1054.    // Set FC to 2 (so we play every second sample)
  1055.    SetGf116(SET_FREQUENCY,0x0800);
  1056.  
  1057.    // Start voices
  1058.    dos_outportb(Gf1PageRegister,0);
  1059.    SetGf18(SET_CONTROL,0x0C);
  1060.    dos_outportb(Gf1PageRegister,1);
  1061.    SetGf18(SET_CONTROL,0x0C);
  1062.    Gf1Delay();
  1063.    dos_outportb(Gf1PageRegister,0);
  1064.    SetGf18(SET_CONTROL,0x0C);
  1065.    dos_outportb(Gf1PageRegister,1);
  1066.    SetGf18(SET_CONTROL,0x0C);
  1067. }
  1068.  
  1069.  
  1070. //=============================================================================
  1071. // Figures out what kind of UltraSound we have, if any, and starts it playing
  1072. //=============================================================================
  1073. qboolean GUS_Init(void)
  1074. {
  1075.         int rc;
  1076.         int RealAddr;
  1077.         BYTE FSVal,Voices;
  1078.         struct CodecRateStruct *CodecRate;
  1079.         struct Gf1RateStruct *Gf1Rate;
  1080.  
  1081.         // See what kind of UltraSound we have, if any
  1082.         if (GUS_GetIWData()==false)
  1083.                 if (GUS_GetMAXData()==false)
  1084.                         if (GUS_GetGUSData()==false)
  1085.                                 return(false);
  1086.  
  1087.         shm = &sn;
  1088.  
  1089.         if (HaveCodec)
  1090.         {
  1091.                 // do 11khz sampling rate unless command line parameter wants different
  1092.                 shm->speed = 11025;
  1093.                 FSVal = 0x03;
  1094.                 rc = COM_CheckParm("-sspeed");
  1095.                 if (rc)
  1096.                 {
  1097.                         shm->speed = Q_atoi(com_argv[rc+1]);
  1098.        
  1099.                         // Make sure rate not too high
  1100.                         if (shm->speed>48000)
  1101.                                 shm->speed=48000;
  1102.        
  1103.                         // Adjust speed to match one of the possible CODEC rates
  1104.                         for (CodecRate=CodecRates;CodecRate->Rate!=0;CodecRate++)
  1105.                         {
  1106.                                 if (shm->speed <= CodecRate->Rate)
  1107.                                 {
  1108.                                         shm->speed=CodecRate->Rate;
  1109.                                         FSVal=CodecRate->FSVal;
  1110.                                         break;
  1111.                                 }
  1112.                         }
  1113.                 }
  1114.  
  1115.        
  1116.                 // Always do 16 bit stereo
  1117.                 shm->channels = 2;
  1118.                 shm->samplebits = 16;
  1119.        
  1120.                 // allocate buffer twice the size we need so we can get aligned buffer
  1121.                 dma_buffer = dos_getmemory(BUFFER_SIZE*2);
  1122.                 if (dma_buffer==NULL)
  1123.                 {
  1124.                         Con_Printf("Couldn't allocate sound dma buffer");
  1125.                         return false;
  1126.                 }
  1127.  
  1128.                 RealAddr = ptr2real(dma_buffer);
  1129.                 RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1);
  1130.                 dma_buffer = (short *) real2ptr(RealAddr);
  1131.  
  1132.                 // Zero off DMA buffer
  1133.                 memset(dma_buffer, 0, BUFFER_SIZE);
  1134.  
  1135.                 shm->soundalive = true;
  1136.                 shm->splitbuffer = false;
  1137.  
  1138.                 shm->samplepos = 0;
  1139.                 shm->submission_chunk = 1;
  1140.                 shm->buffer = (unsigned char *) dma_buffer;
  1141.                 shm->samples = BUFFER_SIZE/(shm->samplebits/8);
  1142.  
  1143.                 GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE);
  1144.                 GUS_StartCODEC(BUFFER_SIZE,FSVal);
  1145.         }
  1146.         else
  1147.         {
  1148.                 // do 19khz sampling rate unless command line parameter wants different
  1149.                 shm->speed = 19293;
  1150.                 Voices=32;
  1151.                 rc = COM_CheckParm("-sspeed");
  1152.                 if (rc)
  1153.                 {
  1154.                         shm->speed = Q_atoi(com_argv[rc+1]);
  1155.  
  1156.                         // Make sure rate not too high
  1157.                         if (shm->speed>44100)
  1158.                                 shm->speed=44100;
  1159.  
  1160.                         // Adjust speed to match one of the possible GF1 rates
  1161.                         for (Gf1Rate=Gf1Rates;Gf1Rate->Rate!=0;Gf1Rate++)
  1162.                         {
  1163.                                 if (shm->speed <= Gf1Rate->Rate)
  1164.                                 {
  1165.                                         shm->speed=Gf1Rate->Rate;
  1166.                                         Voices=Gf1Rate->Voices;
  1167.                                         break;
  1168.                                 }
  1169.                         }
  1170.                 }
  1171.  
  1172.                 // Always do 16 bit stereo
  1173.                 shm->channels = 2;
  1174.                 shm->samplebits = 16;
  1175.  
  1176.                 // allocate buffer twice the size we need so we can get aligned buffer
  1177.                 dma_buffer = dos_getmemory(BUFFER_SIZE*2);
  1178.                 if (dma_buffer==NULL)
  1179.                 {
  1180.                         Con_Printf("Couldn't allocate sound dma buffer");
  1181.                         return false;
  1182.                 }
  1183.  
  1184.                 RealAddr = ptr2real(dma_buffer);
  1185.                 RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1);
  1186.                 dma_buffer = (short *) real2ptr(RealAddr);
  1187.  
  1188.                 // Zero off DMA buffer
  1189.                 memset(dma_buffer, 0, BUFFER_SIZE);
  1190.  
  1191.                 shm->soundalive = true;
  1192.                 shm->splitbuffer = false;
  1193.  
  1194.                 shm->samplepos = 0;
  1195.                 shm->submission_chunk = 1;
  1196.                 shm->buffer = (unsigned char *) dma_buffer;
  1197.                 shm->samples = BUFFER_SIZE/(shm->samplebits/8);
  1198.  
  1199.                 GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE);
  1200.                 SetGf116(SET_DMA_ADDRESS,0x0000);
  1201.                 if (DmaChannel<=3)
  1202.                         SetGf18(DMA_CONTROL,0x41);
  1203.                 else
  1204.                         SetGf18(DMA_CONTROL,0x45);
  1205.                 GUS_StartGf1(BUFFER_SIZE,Voices);
  1206.         }
  1207.         return(true);
  1208. }
  1209.  
  1210. //=============================================================================
  1211. // Returns the current playback position
  1212. //=============================================================================
  1213. int GUS_GetDMAPos(void)
  1214. {
  1215.    int count;
  1216.  
  1217.         if (HaveCodec)
  1218.         {
  1219.            // clear 16-bit reg flip-flop
  1220.           // load the current dma count register
  1221.           if (DmaChannel < 4)
  1222.           {
  1223.              dos_outportb(0x0C, 0);
  1224.              count = dos_inportb(CountReg);
  1225.              count += dos_inportb(CountReg) << 8;
  1226.              if (shm->samplebits == 16)
  1227.                 count /= 2;
  1228.              count = shm->samples - (count+1);
  1229.           }
  1230.           else
  1231.           {
  1232.              dos_outportb(0xD8, 0);
  1233.              count = dos_inportb(CountReg);
  1234.              count += dos_inportb(CountReg) << 8;
  1235.              if (shm->samplebits == 8)
  1236.                 count *= 2;
  1237.              count = shm->samples - (count+1);
  1238.           }
  1239.  
  1240.         }
  1241.         else
  1242.         {
  1243.                 // Read current position from GF1
  1244.                 dos_outportb(Gf1PageRegister,0);
  1245.                 count=(GetGf116(GET_ACC_HIGH)<<7) & 0xFFFF;
  1246.                 // See which half of buffer we are in. Note that since this is 16 bit
  1247.                 // data we are playing, position is in 16 bit samples
  1248.                 if (GetGf18(DMA_CONTROL) & 0x40)
  1249.                 {
  1250.                         GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE);
  1251.                         SetGf116(SET_DMA_ADDRESS,0x0000);
  1252.                         if (DmaChannel<=3)
  1253.                                 SetGf18(DMA_CONTROL,0x41);
  1254.                         else
  1255.                                 SetGf18(DMA_CONTROL,0x45);
  1256.                 }
  1257.         }
  1258.  
  1259.    shm->samplepos = count & (shm->samples-1);
  1260.    return(shm->samplepos);
  1261. }
  1262.  
  1263. //=============================================================================
  1264. // Stops the UltraSound playback
  1265. //=============================================================================
  1266. void GUS_Shutdown (void)
  1267. {
  1268.         if (HaveCodec)
  1269.         {
  1270.                 // Stop CODEC
  1271.                 dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
  1272.                 dos_outportb(CodecData,0x01);
  1273.         }
  1274.         else
  1275.         {
  1276.                 // Stop Voices
  1277.                 dos_outportb(Gf1PageRegister,0);
  1278.                 SetGf18(SET_CONTROL,0x03);
  1279.                 dos_outportb(Gf1PageRegister,1);
  1280.                 SetGf18(SET_CONTROL,0x03);
  1281.                 Gf1Delay();
  1282.                 dos_outportb(Gf1PageRegister,0);
  1283.                 SetGf18(SET_CONTROL,0x03);
  1284.                 dos_outportb(Gf1PageRegister,1);
  1285.                 SetGf18(SET_CONTROL,0x03);
  1286.  
  1287.                 // Stop any DMA
  1288.                 SetGf18(DMA_CONTROL,0x00);
  1289.                 GetGf18(DMA_CONTROL);
  1290.         }
  1291.  
  1292.         dos_outportb(DisableReg, DmaChannel | 4); // disable dma channel
  1293. }
  1294.