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