snd_gus.c 33 KB

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