snd_dos.c 13 KB

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