1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19*/ 20 21#include "quakedef.h" 22#include "dosisms.h" 23 24int BLASTER_GetDMAPos(void); 25 26/* 27=============================================================================== 28GUS SUPPORT 29 30=============================================================================== 31*/ 32 33qboolean GUS_Init (void); 34int GUS_GetDMAPos (void); 35void GUS_Shutdown (void); 36 37 38/* 39=============================================================================== 40 41BLASTER SUPPORT 42 43=============================================================================== 44*/ 45 46short *dma_buffer=0; 47static int dma_size; 48static int dma; 49 50static int dsp_port; 51static int irq; 52static int low_dma; 53static int high_dma; 54static int mixer_port; 55static int mpu401_port; 56 57int dsp_version; 58int dsp_minor_version; 59 60int timeconstant=-1; 61 62 63void 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 75void 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 89int 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 147int 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 164int ReadDSP(void) 165{ 166 while (!(dos_inportb(dsp_port+0xe)&0x80)) ; 167 return dos_inportb(dsp_port+0xa); 168} 169 170void WriteDSP(int val) 171{ 172 while ((dos_inportb(dsp_port+0xc)&0x80)) ; 173 dos_outportb(dsp_port+0xc, val); 174} 175 176int ReadMixer(int addr) 177{ 178 dos_outportb(mixer_port+4, addr); 179 return dos_inportb(mixer_port+5); 180} 181 182void WriteMixer(int addr, int val) 183{ 184 dos_outportb(mixer_port+4, addr); 185 dos_outportb(mixer_port+5, val); 186} 187 188int oldmixervalue; 189 190/* 191================ 192StartSB 193 194================ 195*/ 196void 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 262static int page_reg[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; 263static int addr_reg[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc }; 264static int count_reg[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce }; 265 266static int mode_reg; 267static int flipflop_reg; 268static int disable_reg; 269static int clear_reg; 270 271/* 272================ 273StartDMA 274 275================ 276*/ 277void 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================== 348BLASTER_Init 349 350Returns false if nothing is found. 351================== 352*/ 353qboolean 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============== 473BLASTER_GetDMAPos 474 475return the current sample position (in mono samples read) 476inside the recirculating dma buffer, so the mixing code will know 477how many sample are required to fill it up. 478=============== 479*/ 480int 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============== 521BLASTER_Shutdown 522 523Reset the sound device for exiting 524=============== 525*/ 526void 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 552INTERFACE 553 554=============================================================================== 555*/ 556 557typedef enum 558{ 559 dma_none, 560 dma_blaster, 561 dma_gus 562} dmacard_t; 563 564dmacard_t dmacard; 565 566/* 567================== 568SNDDM_Init 569 570Try to find a sound device to mix for. 571Returns false if nothing is found. 572Returns true and fills in the "shm" structure with information for the mixer. 573================== 574*/ 575qboolean 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============== 596SNDDMA_GetDMAPos 597 598return the current sample position (in mono samples, not stereo) 599inside the recirculating dma buffer, so the mixing code will know 600how many sample are required to fill it up. 601=============== 602*/ 603int 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============== 620SNDDMA_Shutdown 621 622Reset the sound device for exiting 623=============== 624*/ 625void 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============== 645SNDDMA_Submit 646 647Send sound to device if buffer isn't really the dma buffer 648=============== 649*/ 650void SNDDMA_Submit(void) 651{ 652} 653 654