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. #include "quakedef.h"
  22. #include "dosisms.h"
  23.  
  24. int BLASTER_GetDMAPos(void);
  25.  
  26. /*
  27. ===============================================================================
  28. GUS SUPPORT
  29.  
  30. ===============================================================================
  31. */
  32.  
  33. qboolean GUS_Init (void);
  34. int GUS_GetDMAPos (void);
  35. void GUS_Shutdown (void);
  36.  
  37.  
  38. /*
  39. ===============================================================================
  40.  
  41. BLASTER SUPPORT
  42.  
  43. ===============================================================================
  44. */
  45.  
  46. short *dma_buffer=0;
  47. static int dma_size;
  48. static  int dma;
  49.  
  50. static  int dsp_port;
  51. static  int irq;
  52. static  int low_dma;
  53. static  int high_dma;
  54. static  int mixer_port;
  55. static  int mpu401_port;
  56.  
  57. int dsp_version;
  58. int dsp_minor_version;
  59.  
  60. int timeconstant=-1;
  61.  
  62.  
  63. void PrintBits (byte b)
  64. {
  65.         int     i;
  66.         char    str[9];
  67.        
  68.         for (i=0 ; i<8 ; i++)
  69.                 str[i] = '0' + ((b & (1<<(7-i))) > 0);
  70.                
  71.         str[8] = 0;
  72.         Con_Printf ("%s (%i)", str, b);
  73. }
  74.  
  75. void SB_Info_f(void)
  76. {
  77.         Con_Printf ("BLASTER=%s\n", getenv("BLASTER"));
  78.         Con_Printf("dsp version=%d.%d\n", dsp_version, dsp_minor_version);
  79.         Con_Printf("dma=%d\n", dma);
  80.         if (timeconstant != -1)
  81.                 Con_Printf("timeconstant=%d\n", timeconstant);
  82.         Con_Printf("dma position:%i\n", BLASTER_GetDMAPos ());
  83. }
  84.  
  85. // =======================================================================
  86. // Interprets BLASTER variable
  87. // =======================================================================
  88.  
  89. int GetBLASTER(void)
  90. {
  91.         char *BLASTER;
  92.         char *param;
  93.  
  94.         BLASTER = getenv("BLASTER");
  95.         if (!BLASTER)
  96.                 return 0;
  97.  
  98.         param = strchr(BLASTER, 'A');
  99.         if (!param)
  100.                 param = strchr(BLASTER, 'a');
  101.         if (!param)
  102.                 return 0;
  103.         sscanf(param+1, "%x", &dsp_port);
  104.  
  105.         param = strchr(BLASTER, 'I');
  106.         if (!param)
  107.                 param = strchr(BLASTER, 'i');
  108.         if (!param)
  109.                 return 0;
  110.         sscanf(param+1, "%d", &irq);
  111.  
  112.         param = strchr(BLASTER, 'D');
  113.         if (!param)
  114.                 param = strchr(BLASTER, 'd');
  115.         if (!param)
  116.                 return 0;
  117.         sscanf(param+1, "%d", &low_dma);
  118.  
  119.         param = strchr(BLASTER, 'H');
  120.         if (!param)
  121.                 param = strchr(BLASTER, 'h');
  122.         if (param)
  123.                 sscanf(param+1, "%d", &high_dma);
  124.  
  125.         param = strchr(BLASTER, 'M');
  126.         if (!param)
  127.                 param = strchr(BLASTER, 'm');
  128.         if (param)
  129.                 sscanf(param+1, "%x", &mixer_port);
  130.         else
  131.                 mixer_port = dsp_port;
  132.  
  133.         param = strchr(BLASTER, 'P');
  134.         if (!param)
  135.                 param = strchr(BLASTER, 'p');
  136.         if (param)
  137.                 sscanf(param+1, "%x", &mpu401_port);
  138.  
  139.         return 1;
  140.  
  141. }
  142.  
  143. // ==================================================================
  144. // Resets DSP.  Returns 0 on success.
  145. // ==================================================================
  146.  
  147. int ResetDSP(void)
  148. {
  149.         volatile int i;
  150.  
  151.         dos_outportb(dsp_port + 6, 1);
  152.         for (i=65536 ; i ; i--) ;
  153.         dos_outportb(dsp_port + 6, 0);
  154.         for (i=65536 ; i ; i--)
  155.         {
  156.                 if (!(dos_inportb(dsp_port + 0xe) & 0x80)) continue;
  157.                 if (dos_inportb(dsp_port + 0xa) == 0xaa) break;
  158.         }
  159.         if (i) return 0;
  160.         else return 1;
  161.  
  162. }
  163.  
  164. int ReadDSP(void)
  165. {
  166.         while (!(dos_inportb(dsp_port+0xe)&0x80)) ;
  167.         return dos_inportb(dsp_port+0xa);
  168. }
  169.  
  170. void WriteDSP(int val)
  171. {
  172.         while ((dos_inportb(dsp_port+0xc)&0x80)) ;
  173.         dos_outportb(dsp_port+0xc, val);
  174. }
  175.  
  176. int ReadMixer(int addr)
  177. {
  178.         dos_outportb(mixer_port+4, addr);
  179.         return dos_inportb(mixer_port+5);
  180. }
  181.  
  182. void WriteMixer(int addr, int val)
  183. {
  184.         dos_outportb(mixer_port+4, addr);
  185.         dos_outportb(mixer_port+5, val);
  186. }
  187.  
  188. int             oldmixervalue;
  189.  
  190. /*
  191. ================
  192. StartSB
  193.  
  194. ================
  195. */
  196. void StartSB(void)
  197. {
  198.         int             i;
  199.  
  200. // version 4.xx startup code
  201.         if (dsp_version >= 4)
  202.         {
  203.                 Con_Printf("Version 4 SB startup\n");
  204.                 WriteDSP(0xd1); // turn on speaker
  205.  
  206.                 WriteDSP(0x41);
  207.  
  208.                 WriteDSP(shm->speed>>8);
  209.                 WriteDSP(shm->speed&0xff);
  210.  
  211.                 WriteDSP(0xb6); // 16-bit output
  212.                 WriteDSP(0x30); // stereo
  213.                 WriteDSP((shm->samples-1) & 0xff);      // # of samples - 1
  214.                 WriteDSP((shm->samples-1) >> 8);
  215.         }
  216. // version 3.xx startup code
  217.         else if (dsp_version == 3)
  218.         {
  219.                 Con_Printf("Version 3 SB startup\n");
  220.                 WriteDSP(0xd1); // turn on speaker
  221.  
  222.                 oldmixervalue = ReadMixer (0xe);
  223.                 WriteMixer (0xe, oldmixervalue | 0x2);// turn on stereo
  224.  
  225.                 WriteDSP(0x14);                 // send one byte
  226.                 WriteDSP(0x0);
  227.                 WriteDSP(0x0);
  228.  
  229.                 for (i=0 ; i<0x10000 ; i++)
  230.                         dos_inportb(dsp_port+0xe);              // ack the dsp
  231.                
  232.                 timeconstant = 65536-(256000000/(shm->channels*shm->speed));
  233.                 WriteDSP(0x40);
  234.                 WriteDSP(timeconstant>>8);
  235.  
  236.                 WriteMixer (0xe, ReadMixer(0xe) | 0x20);// turn off filter
  237.  
  238.                 WriteDSP(0x48);
  239.                 WriteDSP((shm->samples-1) & 0xff);      // # of samples - 1
  240.                 WriteDSP((shm->samples-1) >> 8);
  241.  
  242.                 WriteDSP(0x90); // high speed 8 bit stereo
  243.         }
  244. // normal speed mono
  245.         else
  246.         {
  247.                 Con_Printf("Version 2 SB startup\n");
  248.                 WriteDSP(0xd1); // turn on speaker
  249.  
  250.                 timeconstant = 65536-(256000000/(shm->channels*shm->speed));
  251.                 WriteDSP(0x40);
  252.                 WriteDSP(timeconstant>>8);
  253.  
  254.                 WriteDSP(0x48);
  255.                 WriteDSP((shm->samples-1) & 0xff);      // # of samples - 1
  256.                 WriteDSP((shm->samples-1) >> 8);
  257.  
  258.                 WriteDSP(0x1c); // normal speed 8 bit mono
  259.         }
  260. }
  261.  
  262. static int page_reg[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
  263. static int addr_reg[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc };
  264. static int count_reg[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce };
  265.  
  266. static int mode_reg;
  267. static int flipflop_reg;
  268. static int disable_reg;
  269. static int clear_reg;
  270.  
  271. /*
  272. ================
  273. StartDMA
  274.  
  275. ================
  276. */
  277. void StartDMA(void)
  278. {
  279.         int mode;
  280.         int realaddr;
  281.  
  282.         realaddr = ptr2real(dma_buffer);
  283.  
  284. // use a high dma channel if specified
  285.         if (high_dma && dsp_version >= 4)       // 8 bit snd can never use 16 bit dma
  286.                 dma = high_dma;
  287.         else
  288.                 dma = low_dma;
  289.  
  290.         Con_Printf ("Using DMA channel %i\n", dma);
  291.  
  292.         if (dma > 3)
  293.         {
  294.                 mode_reg = 0xd6;
  295.                 flipflop_reg = 0xd8;
  296.                 disable_reg = 0xd4;
  297.                 clear_reg = 0xdc;
  298.         }
  299.         else
  300.         {
  301.                 mode_reg = 0xb;
  302.                 flipflop_reg = 0xc;
  303.                 disable_reg = 0xa;
  304.                 clear_reg = 0xe;
  305.         }
  306.  
  307.         dos_outportb(disable_reg, dma|4);       // disable channel
  308.         // set mode- see "undocumented pc", p.876
  309.         mode =  (1<<6)  // single-cycle
  310.                 +(0<<5)         // address increment
  311.                 +(1<<4)         // auto-init dma
  312.                 +(2<<2)         // read
  313.                 +(dma&3);       // channel #
  314.         dos_outportb(mode_reg, mode);
  315.        
  316. // set address
  317.         // set page
  318.         dos_outportb(page_reg[dma], realaddr >> 16);
  319.  
  320.         if (dma > 3)
  321.         {       // address is in words
  322.                 dos_outportb(flipflop_reg, 0);          // prepare to send 16-bit value
  323.                 dos_outportb(addr_reg[dma], (realaddr>>1) & 0xff);
  324.                 dos_outportb(addr_reg[dma], (realaddr>>9) & 0xff);
  325.  
  326.                 dos_outportb(flipflop_reg, 0);          // prepare to send 16-bit value
  327.                 dos_outportb(count_reg[dma], ((dma_size>>1)-1) & 0xff);
  328.                 dos_outportb(count_reg[dma], ((dma_size>>1)-1) >> 8);
  329.         }
  330.         else
  331.         {       // address is in bytes
  332.                 dos_outportb(flipflop_reg, 0);          // prepare to send 16-bit value
  333.                 dos_outportb(addr_reg[dma], realaddr & 0xff);
  334.                 dos_outportb(addr_reg[dma], (realaddr>>8) & 0xff);
  335.  
  336.                 dos_outportb(flipflop_reg, 0);          // prepare to send 16-bit value
  337.                 dos_outportb(count_reg[dma], (dma_size-1) & 0xff);
  338.                 dos_outportb(count_reg[dma], (dma_size-1) >> 8);
  339.         }
  340.  
  341.         dos_outportb(clear_reg, 0);             // clear write mask
  342.         dos_outportb(disable_reg, dma&~4);
  343. }
  344.  
  345.  
  346. /*
  347. ==================
  348. BLASTER_Init
  349.  
  350. Returns false if nothing is found.
  351. ==================
  352. */
  353. qboolean BLASTER_Init(void)
  354. {
  355.         int     size;
  356.         int     realaddr;
  357.         int     rc;
  358.         int             p;
  359.        
  360.         shm = 0;
  361.         rc = 0;
  362.  
  363. //
  364. // must have a blaster variable set
  365. //
  366.         if (!GetBLASTER())
  367.         {
  368.                 Con_NotifyBox (
  369.                 "The BLASTER environment variable\n"
  370.                 "is not set, sound effects are\n"
  371.                 "disabled.  See README.TXT for help.\n"
  372.                 );                     
  373.                 return 0;
  374.         }
  375.  
  376.         if (ResetDSP())
  377.         {
  378.                 Con_Printf("Could not reset SB");
  379.                 return 0;
  380.         }
  381.  
  382. //
  383. // get dsp version
  384. //
  385.         WriteDSP(0xe1);
  386.         dsp_version = ReadDSP();
  387.         dsp_minor_version = ReadDSP();
  388.  
  389. // we need at least v2 for auto-init dma
  390.         if (dsp_version < 2)
  391.         {
  392.                 Con_Printf ("Sound blaster must be at least v2.0\n");
  393.                 return 0;
  394.         }
  395.  
  396. // allow command line parm to set quality down
  397.         p = COM_CheckParm ("-dsp");
  398.         if (p && p < com_argc - 1)
  399.         {
  400.                 p = Q_atoi (com_argv[p+1]);
  401.                 if (p < 2 || p > 4)
  402.                         Con_Printf ("-dsp parameter can only be 2, 3, or 4\n");
  403.                 else if (p > dsp_version)
  404.                         Con_Printf ("Can't -dsp %i on v%i hardware\n", p, dsp_version);
  405.                 else
  406.                         dsp_version = p;
  407.         }      
  408.  
  409.  
  410. // everyone does 11khz sampling rate unless told otherwise
  411.         shm = &sn;
  412.         shm->speed = 11025;
  413.         rc = COM_CheckParm("-sspeed");
  414.         if (rc)
  415.                 shm->speed = Q_atoi(com_argv[rc+1]);
  416.  
  417. // version 4 cards (sb 16) do 16 bit stereo
  418.         if (dsp_version >= 4)
  419.         {
  420.                 shm->channels = 2;
  421.                 shm->samplebits = 16;
  422.         }
  423. // version 3 cards (sb pro) do 8 bit stereo
  424.         else if (dsp_version == 3)
  425.         {
  426.                 shm->channels = 2;
  427.                 shm->samplebits = 8;   
  428.         }
  429. // v2 cards do 8 bit mono
  430.         else
  431.         {
  432.                 shm->channels = 1;
  433.                 shm->samplebits = 8;   
  434.         }
  435.  
  436.        
  437.         Cmd_AddCommand("sbinfo", SB_Info_f);
  438.         size = 4096;
  439.  
  440. // allocate 8k and get a 4k-aligned buffer from it
  441.         dma_buffer = dos_getmemory(size*2);
  442.         if (!dma_buffer)
  443.         {
  444.                 Con_Printf("Couldn't allocate sound dma buffer");
  445.                 return false;
  446.         }
  447.  
  448.         realaddr = ptr2real(dma_buffer);
  449.         realaddr = (realaddr + size) & ~(size-1);
  450.         dma_buffer = (short *) real2ptr(realaddr);
  451.         dma_size = size;
  452.  
  453.         memset(dma_buffer, 0, dma_size);
  454.  
  455.         shm->soundalive = true;
  456.         shm->splitbuffer = false;
  457.  
  458.         shm->samples = size/(shm->samplebits/8);
  459.         shm->samplepos = 0;
  460.         shm->submission_chunk = 1;
  461.         shm->buffer = (unsigned char *) dma_buffer;
  462.         shm->samples = size/(shm->samplebits/8);
  463.  
  464.         StartDMA();
  465.         StartSB();
  466.  
  467.         return true;
  468. }
  469.  
  470.  
  471. /*
  472. ==============
  473. BLASTER_GetDMAPos
  474.  
  475. return the current sample position (in mono samples read)
  476. inside the recirculating dma buffer, so the mixing code will know
  477. how many sample are required to fill it up.
  478. ===============
  479. */
  480. int BLASTER_GetDMAPos(void)
  481. {
  482.         int count;
  483.  
  484. // this function is called often.  acknowledge the transfer completions
  485. // all the time so that it loops
  486.         if (dsp_version >= 4)
  487.                 dos_inportb(dsp_port+0xf);      // 16 bit audio
  488.         else
  489.                 dos_inportb(dsp_port+0xe);      // 8 bit audio
  490.  
  491. // clear 16-bit reg flip-flop
  492. // load the current dma count register
  493.         if (dma < 4)
  494.         {
  495.                 dos_outportb(0xc, 0);
  496.                 count = dos_inportb(dma*2+1);
  497.                 count += dos_inportb(dma*2+1) << 8;
  498.                 if (shm->samplebits == 16)
  499.                         count /= 2;
  500.                 count = shm->samples - (count+1);
  501.         }
  502.         else
  503.         {
  504.                 dos_outportb(0xd8, 0);
  505.                 count = dos_inportb(0xc0+(dma-4)*4+2);
  506.                 count += dos_inportb(0xc0+(dma-4)*4+2) << 8;
  507.                 if (shm->samplebits == 8)
  508.                         count *= 2;
  509.                 count = shm->samples - (count+1);
  510.         }
  511.  
  512. //      Con_Printf("DMA pos = 0x%x\n", count);
  513.  
  514.         shm->samplepos = count & (shm->samples-1);
  515.         return shm->samplepos;
  516.  
  517. }
  518.  
  519. /*
  520. ==============
  521. BLASTER_Shutdown
  522.  
  523. Reset the sound device for exiting
  524. ===============
  525. */
  526. void BLASTER_Shutdown(void)
  527. {
  528.         if (dsp_version >= 4)
  529.         {
  530.         }
  531.         else if (dsp_version == 3)
  532.         {
  533.                 ResetDSP ();                    // stop high speed mode
  534.                 WriteMixer (0xe, oldmixervalue); // turn stereo off and filter on
  535.         }
  536.         else
  537.         {
  538.        
  539.         }
  540.        
  541.         WriteDSP(0xd3); // turn off speaker
  542.         ResetDSP ();
  543.  
  544.         dos_outportb(disable_reg, dma|4);       // disable dma channel
  545. }
  546.  
  547.  
  548.  
  549. /*
  550. ===============================================================================
  551.  
  552. INTERFACE
  553.  
  554. ===============================================================================
  555. */
  556.  
  557. typedef enum
  558. {
  559.         dma_none,
  560.         dma_blaster,
  561.         dma_gus
  562. } dmacard_t;
  563.  
  564. dmacard_t       dmacard;
  565.  
  566. /*
  567. ==================
  568. SNDDM_Init
  569.  
  570. Try to find a sound device to mix for.
  571. Returns false if nothing is found.
  572. Returns true and fills in the "shm" structure with information for the mixer.
  573. ==================
  574. */
  575. qboolean SNDDMA_Init(void)
  576. {
  577.         if (GUS_Init ())
  578.         {
  579.                 dmacard = dma_gus;
  580.                 return true;
  581.         }
  582.         if (BLASTER_Init ())
  583.         {
  584.                 dmacard = dma_blaster;
  585.                 return true;
  586.         }
  587.        
  588.         dmacard = dma_none;
  589.        
  590.         return false;
  591. }
  592.  
  593.  
  594. /*
  595. ==============
  596. SNDDMA_GetDMAPos
  597.  
  598. return the current sample position (in mono samples, not stereo)
  599. inside the recirculating dma buffer, so the mixing code will know
  600. how many sample are required to fill it up.
  601. ===============
  602. */
  603. int SNDDMA_GetDMAPos(void)
  604. {
  605.         switch (dmacard)
  606.         {
  607.         case dma_blaster:
  608.                 return BLASTER_GetDMAPos ();
  609.         case dma_gus:
  610.                 return GUS_GetDMAPos ();
  611.         case dma_none:
  612.                 break;
  613.         }
  614.        
  615.         return 0;
  616. }
  617.  
  618. /*
  619. ==============
  620. SNDDMA_Shutdown
  621.  
  622. Reset the sound device for exiting
  623. ===============
  624. */
  625. void SNDDMA_Shutdown(void)
  626. {
  627.         switch (dmacard)
  628.         {
  629.         case dma_blaster:
  630.                 BLASTER_Shutdown ();
  631.                 break;
  632.         case dma_gus:
  633.                 GUS_Shutdown ();
  634.                 break;
  635.         case dma_none:
  636.                 break;
  637.         }
  638.  
  639.         dmacard = dma_none;
  640.         return;
  641. }
  642.  
  643. /*
  644. ==============
  645. SNDDMA_Submit
  646.  
  647. Send sound to device if buffer isn't really the dma buffer
  648. ===============
  649. */
  650. void SNDDMA_Submit(void)
  651. {
  652. }
  653.  
  654.