added an old version of mikmod for dos
[dosdemo] / libs / oldmik / src / drv_gus.c
1 /*\r
2 \r
3 Name:\r
4 DRV_GUS.C\r
5 \r
6 Description:\r
7 Mikmod driver for output on Gravis Ultrasound (native mode i.e. using\r
8 the onboard DRAM)\r
9 \r
10 Portability:\r
11 \r
12 MSDOS:  BC(y)   Watcom(y)       DJGPP(y)\r
13 Win95:  n\r
14 Os2:    n\r
15 Linux:  n\r
16 \r
17 (y) - yes\r
18 (n) - no (not possible or not useful)\r
19 (?) - may be possible, but not tested\r
20 \r
21 */\r
22 #include <dos.h>\r
23 #include <stdio.h>\r
24 #include <stdlib.h>\r
25 #include <conio.h>\r
26 #include "mikmod.h"\r
27 #include "mirq.h"\r
28 \r
29 /***************************************************************************\r
30 >>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel GUS defines <<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
31 ***************************************************************************/\r
32 \r
33 /* Special macros for Least-most sig. bytes */\r
34 #define MAKE_MSW(x)     ((long)((long)(x)) << 16)\r
35 #define LSW(x)          ((unsigned int)(x))\r
36 #define MSW(x)          ((unsigned int)(((long)x)>>16))\r
37 #define MSB(x)          (unsigned char)((unsigned int)(x)>>8)\r
38 #define LSB(x)          ((unsigned char)(x))\r
39 \r
40 /* Make GF1 address for direct chip i/o. */\r
41 #define ADDR_HIGH(x) ((unsigned int)((unsigned int)((x>>7L)&0x1fffL)))\r
42 #define ADDR_LOW(x)  ((unsigned int)((unsigned int)((x&0x7fL)<<9L)))\r
43 \r
44 #define JOYSTICK_TIMER  (GUS_PORT+0x201)                /* 201 */\r
45 #define JOYSTICK_DATA   (GUS_PORT+0x201)                /* 201 */\r
46 \r
47 #define GF1_MIDI_CTRL   (GUS_PORT+0x100)                /* 3X0 */\r
48 #define GF1_MIDI_DATA   (GUS_PORT+0x101)                /* 3X1 */\r
49 \r
50 #define GF1_PAGE        (GUS_PORT+0x102)                /* 3X2 */\r
51 #define GF1_REG_SELECT  (GUS_PORT+0x103)                /* 3X3 */\r
52 #define GF1_VOICE_SELECT (GUS_PORT+0x102)               /* 3X3 */\r
53 #define GF1_DATA_LOW    (GUS_PORT+0x104)                /* 3X4 */\r
54 #define GF1_DATA_HI     (GUS_PORT+0x105)                /* 3X5 */\r
55 #define GF1_IRQ_STAT    (GUS_PORT+0x006)                /* 2X6 */\r
56 #define GF1_DRAM        (GUS_PORT+0x107)                /* 3X7 */\r
57 \r
58 #define GF1_MIX_CTRL    (GUS_PORT+0x000)                /* 2X0 */\r
59 #define GF1_TIMER_CTRL  (GUS_PORT+0x008)                /* 2X8 */\r
60 #define GF1_TIMER_DATA  (GUS_PORT+0x009)                /* 2X9 */\r
61 #define GF1_IRQ_CTRL    (GUS_PORT+0x00B)                /* 2XB */\r
62 \r
63 /* The GF1 Hardware clock. */\r
64 #define CLOCK_RATE              9878400L\r
65 \r
66 /* Mixer control bits. */\r
67 #define ENABLE_LINE             0x01\r
68 #define ENABLE_DAC              0x02\r
69 #define ENABLE_MIC              0x04\r
70 \r
71 /* interrupt controller 1 */\r
72 #define CNTRL_8259              0x21\r
73 #define OCR_8259                0x20\r
74 #define EOI                     0x20\r
75 #define REARM3                  0x2F3\r
76 #define REARM5                  0x2F5\r
77 \r
78 /* interrupt controller 2 */\r
79 #define CNTRL_M_8259                    0x21\r
80 #define CNTRL_M2_8259                   0xA1\r
81 #define OCR_2_8259              0xA0\r
82 \r
83 #define DMA_CONTROL             0x41\r
84 #define SET_DMA_ADDRESS         0x42\r
85 #define SET_DRAM_LOW            0x43\r
86 #define SET_DRAM_HIGH           0x44\r
87 \r
88 #define TIMER_CONTROL           0x45\r
89 #define TIMER1                  0x46\r
90 #define TIMER2                  0x47\r
91 \r
92 #define SET_SAMPLE_RATE         0x48\r
93 #define SAMPLE_CONTROL          0x49\r
94 \r
95 #define SET_JOYSTICK            0x4B\r
96 #define MASTER_RESET            0x4C\r
97 \r
98 /* Voice register mapping. */\r
99 #define SET_CONTROL                     0x00\r
100 #define SET_FREQUENCY           0x01\r
101 #define SET_START_HIGH          0x02\r
102 #define SET_START_LOW           0x03\r
103 #define SET_END_HIGH            0x04\r
104 #define SET_END_LOW                     0x05\r
105 #define SET_VOLUME_RATE         0x06\r
106 #define SET_VOLUME_START        0x07\r
107 #define SET_VOLUME_END          0x08\r
108 #define SET_VOLUME                      0x09\r
109 #define SET_ACC_HIGH            0x0a\r
110 #define SET_ACC_LOW                     0x0b\r
111 #define SET_BALANCE                     0x0c\r
112 #define SET_VOLUME_CONTROL      0x0d\r
113 #define SET_VOICES                      0x0e\r
114 \r
115 #define GET_CONTROL                     0x80\r
116 #define GET_FREQUENCY           0x81\r
117 #define GET_START_HIGH          0x82\r
118 #define GET_START_LOW           0x83\r
119 #define GET_END_HIGH            0x84\r
120 #define GET_END_LOW                     0x85\r
121 #define GET_VOLUME_RATE         0x86\r
122 #define GET_VOLUME_START        0x87\r
123 #define GET_VOLUME_END          0x88\r
124 #define GET_VOLUME                      0x89\r
125 #define GET_ACC_HIGH            0x8a\r
126 #define GET_ACC_LOW                     0x8b\r
127 #define GET_BALANCE                     0x8c\r
128 #define GET_VOLUME_CONTROL      0x8d\r
129 #define GET_VOICES                      0x8e\r
130 #define GET_IRQV                        0x8f\r
131 \r
132 /********************************************************************\r
133  *\r
134  * MIDI defines\r
135  *\r
136  *******************************************************************/\r
137 \r
138 #define MIDI_RESET      0x03\r
139 #define MIDI_ENABLE_XMIT        0x20\r
140 #define MIDI_ENABLE_RCV         0x80\r
141 \r
142 #define MIDI_RCV_FULL           0x01\r
143 #define MIDI_XMIT_EMPTY         0x02\r
144 #define MIDI_FRAME_ERR          0x10\r
145 #define MIDI_OVERRUN            0x20\r
146 #define MIDI_IRQ_PEND           0x80\r
147 \r
148 /********************************************************************\r
149  *\r
150  * JOYSTICK defines\r
151  *\r
152  *******************************************************************/\r
153 \r
154 #define JOY_POSITION            0x0f\r
155 #define JOY_BUTTONS                     0xf0\r
156 \r
157 /********************************************************************\r
158  *\r
159  * GF1 irq/dma programmable latches\r
160  *\r
161  *******************************************************************/\r
162 \r
163 /* GF1_IRQ_STATUS (port 3X6) */\r
164 #define MIDI_TX_IRQ                     0x01            /* pending MIDI xmit IRQ */\r
165 #define MIDI_RX_IRQ                     0x02            /* pending MIDI recv IRQ */\r
166 #define GF1_TIMER1_IRQ          0x04            /* general purpose timer */\r
167 #define GF1_TIMER2_IRQ          0x08            /* general purpose timer */\r
168 #define WAVETABLE_IRQ           0x20            /* pending wavetable IRQ */\r
169 #define ENVELOPE_IRQ            0x40            /* pending volume envelope IRQ */\r
170 #define DMA_TC_IRQ                      0x80            /* pending dma tc IRQ */\r
171 \r
172 \r
173 /* GF1_MIX_CTRL (port 2X0) */\r
174 #define ENABLE_LINE_IN          0x01            /* 0=enable */\r
175 #define ENABLE_OUTPUT           0x02            /* 0=enable */\r
176 #define ENABLE_MIC_IN           0x04            /* 1=enable */\r
177 #define ENABLE_GF1_IRQ          0x08            /* 1=enable */\r
178 #define GF122                           0x10            /* ?? */\r
179 #define ENABLE_MIDI_LOOP        0x20            /* 1=enable loop back */\r
180 #define SELECT_GF1_REG          0x40            /* 0=irq latches */\r
181                                                                                 /* 1=dma latches */\r
182 \r
183 /********************************************************************\r
184  *\r
185  * GF1 global registers ($41-$4C)\r
186  *\r
187  *******************************************************************/\r
188 \r
189 /* DMA control register */\r
190 #define DMA_ENABLE                      0x01\r
191 #define DMA_READ                        0x02            /* 1=read,0=write */\r
192 #define DMA_WIDTH_16            0x04            /* 1=16 bit,0=8 bit (dma chan width)*/\r
193 #define DMA_RATE                        0x18            /* 00=fast, 11=slow */\r
194 #define DMA_IRQ_ENABLE          0x20            /* 1=enable */\r
195 #define DMA_IRQ_PENDING         0x40            /* read */\r
196 #define DMA_DATA_16                     0x40            /* write (data width) */\r
197 #define DMA_TWOS_COMP           0x80            /* 1=do twos comp */\r
198 \r
199 /* These are the xfer rate bits ... */\r
200 #define DMA_R0          0x00            /* Fastest DMA xfer (~650khz) */\r
201 #define DMA_R1          0x08            /* fastest / 2 */\r
202 #define DMA_R2          0x10            /* fastest / 4 */\r
203 #define DMA_R3          0x18            /* Slowest DMA xfer (fastest / 8) */\r
204 \r
205 /* SAMPLE control register */\r
206 #define ENABLE_ADC                      0x01\r
207 #define ADC_MODE                        0x02            /* 0=mono, 1=stereo */\r
208 #define ADC_DMA_WIDTH           0x04            /* 0=8 bit, 1=16 bit */\r
209 #define ADC_IRQ_ENABLE          0x20            /* 1=enable */\r
210 #define ADC_IRQ_PENDING         0x40            /* 1=irq pending */\r
211 #define ADC_TWOS_COMP           0x80            /* 1=do twos comp */\r
212 \r
213 /* RESET control register */\r
214 #define GF1_MASTER_RESET        0x01            /* 0=hold in reset */\r
215 #define GF1_OUTPUT_ENABLE       0x02            /* enable output */\r
216 #define GF1_MASTER_IRQ          0x04            /* master IRQ enable */\r
217 \r
218 /********************************************************************\r
219  *\r
220  * GF1 voice specific registers ($00 - $0E and $80-$8f)\r
221  *\r
222  *******************************************************************/\r
223 \r
224 /* ($0,$80) Voice control register */\r
225 #define VOICE_STOPPED           0x01            /* voice has stopped */\r
226 #define STOP_VOICE                      0x02            /* stop voice */\r
227 #define VC_DATA_TYPE            0x04            /* 0=8 bit,1=16 bit */\r
228 #define VC_LOOP_ENABLE          0x08            /* 1=enable */\r
229 #define VC_BI_LOOP                      0x10            /* 1=bi directional looping */\r
230 #define VC_WAVE_IRQ                     0x20            /* 1=enable voice's wave irq */\r
231 #define VC_DIRECT                       0x40            /* 0=increasing,1=decreasing */\r
232 #define VC_IRQ_PENDING          0x80            /* 1=wavetable irq pending */\r
233 \r
234 /* ($1,$81) Frequency control */\r
235 /* Bit 0  - Unused */\r
236 /* Bits 1-9 - Fractional portion */\r
237 /* Bits 10-15 - Integer portion */\r
238 \r
239 /* ($2,$82) Accumulator start address (high) */\r
240 /* Bits 0-11 - HIGH 12 bits of address */\r
241 /* Bits 12-15 - Unused */\r
242 \r
243 /* ($3,$83) Accumulator start address (low) */\r
244 /* Bits 0-4 - Unused */\r
245 /* Bits 5-8 - Fractional portion */\r
246 /* Bits 9-15 - Low 7 bits of integer portion */\r
247 \r
248 /* ($4,$84) Accumulator end address (high) */\r
249 /* Bits 0-11 - HIGH 12 bits of address */\r
250 /* Bits 12-15 - Unused */\r
251 \r
252 /* ($5,$85) Accumulator end address (low) */\r
253 /* Bits 0-4 - Unused */\r
254 /* Bits 5-8 - Fractional portion */\r
255 /* Bits 9-15 - Low 7 bits of integer portion */\r
256 \r
257 \r
258 /* ($6,$86) Volume Envelope control register */\r
259 #define VL_RATE_MANTISSA                0x3f\r
260 #define VL_RATE_RANGE                   0xC0\r
261 \r
262 /* ($7,$87) Volume envelope start */\r
263 #define VL_START_MANT                   0x0F\r
264 #define VL_START_EXP                    0xF0\r
265 \r
266 /* ($8,$88) Volume envelope end */\r
267 #define VL_END_MANT                             0x0F\r
268 #define VL_END_EXP                              0xF0\r
269 \r
270 /* ($9,$89) Current volume register */\r
271 /* Bits 0-3 are unused */\r
272 /* Bits 4-11 - Mantissa of current volume */\r
273 /* Bits 10-15 - Exponent of current volume */\r
274 \r
275 /* ($A,$8A) Accumulator value (high) */\r
276 /* Bits 0-12 - HIGH 12 bits of current position (a19-a7) */\r
277 \r
278 /* ($B,$8B) Accumulator value (low) */\r
279 /* Bits 0-8 - Fractional portion */\r
280 /* Bits 9-15 - Integer portion of low adress (a6-a0) */\r
281 \r
282 /* ($C,$8C) Pan (balance) position */\r
283 /* Bits 0-3 - Balance position  0=full left, 0x0f=full right */\r
284 \r
285 /* ($D,$8D) Volume control register */\r
286 #define VOLUME_STOPPED          0x01            /* volume has stopped */\r
287 #define STOP_VOLUME                     0x02            /* stop volume */\r
288 #define VC_ROLLOVER                     0x04            /* Roll PAST end & gen IRQ */\r
289 #define VL_LOOP_ENABLE          0x08            /* 1=enable */\r
290 #define VL_BI_LOOP                      0x10            /* 1=bi directional looping */\r
291 #define VL_WAVE_IRQ                     0x20            /* 1=enable voice's wave irq */\r
292 #define VL_DIRECT                       0x40            /* 0=increasing,1=decreasing */\r
293 #define VL_IRQ_PENDING          0x80            /* 1=wavetable irq pending */\r
294 \r
295 /* ($E,$8E) # of Active voices */\r
296 /* Bits 0-5 - # of active voices -1 */\r
297 \r
298 /* ($F,$8F) - Sources of IRQs */\r
299 /* Bits 0-4 - interrupting voice number */\r
300 /* Bit 5 - Always a 1 */\r
301 #define VOICE_VOLUME_IRQ        0x40            /* individual voice irq bit */\r
302 #define VOICE_WAVE_IRQ          0x80            /* individual waveform irq bit */\r
303 \r
304 \r
305 /***************************************************************************\r
306 >>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel GUS code <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
307 ***************************************************************************/\r
308 \r
309 static UWORD GUS_PORT;\r
310 static UBYTE GUS_VOICES;\r
311 static UBYTE GUS_TIMER_CTRL;\r
312 static UBYTE GUS_TIMER_MASK;\r
313 static UBYTE GUS_MIX_IMAGE;\r
314 \r
315 static UWORD GUS_DRAM_DMA;\r
316 static UWORD GUS_ADC_DMA;\r
317 static UWORD GUS_GF1_IRQ;\r
318 static UWORD GUS_MIDI_IRQ;\r
319 static ULONG GUS_POOL;         /* dram address of first gusmem pool node */\r
320 \r
321 static UBYTE GUS_SELECT;           /* currently selected GF1 register */\r
322 \r
323 static void (*GUS_TIMER1_FUNC)(void);\r
324 static void (*GUS_TIMER2_FUNC)(void);\r
325 \r
326 #define UltraSelect(x) outportb(GF1_REG_SELECT,GUS_SELECT=x)\r
327 \r
328 #define USE_ROLLOVER 0\r
329 \r
330 /***************************************************************\r
331  * This function will convert the value read from the GF1 registers\r
332  * back to a 'real' address.\r
333  ***************************************************************/\r
334 \r
335 #define MAKE_MS_SWORD( x )       ((unsigned long)((unsigned long)(x)) << 16)\r
336 \r
337 static ULONG make_physical_address(UWORD low,UWORD high,UBYTE mode)\r
338 {\r
339         UWORD lower_16, upper_16;\r
340         ULONG ret_address, bit_19_20;\r
341 \r
342         upper_16 = high >> 9;\r
343         lower_16 = ((high & 0x01ff) << 7) | ((low >> 9) & 0x007f);\r
344 \r
345         ret_address = MAKE_MS_SWORD(upper_16) + lower_16;\r
346 \r
347         if (mode & VC_DATA_TYPE)\r
348                 {\r
349                 bit_19_20 = ret_address & 0xC0000;\r
350                 ret_address <<= 1;\r
351                 ret_address &= 0x3ffff;\r
352                 ret_address |= bit_19_20;\r
353                 }\r
354 \r
355         return( ret_address );\r
356 }\r
357 \r
358 /***************************************************************\r
359  * This function will translate the address if the dma channel\r
360  * is a 16 bit channel. This translation is not necessary for\r
361  * an 8 bit dma channel.\r
362  ***************************************************************/\r
363 \r
364 static ULONG convert_to_16bit(ULONG address)\r
365 /* unsigned long address;               /* 20 bit ultrasound dram address */\r
366 {\r
367         ULONG hold_address;\r
368 \r
369         hold_address = address;\r
370 \r
371         /* Convert to 16 translated address. */\r
372         address = address >> 1;\r
373 \r
374         /* Zero out bit 17. */\r
375         address &= 0x0001ffffL;\r
376 \r
377         /* Reset bits 18 and 19. */\r
378         address |= (hold_address & 0x000c0000L);\r
379 \r
380         return(address);\r
381 }\r
382 \r
383 \r
384 static void GF1OutB(UBYTE x,UBYTE y)\r
385 {\r
386         UltraSelect(x);\r
387         outportb(GF1_DATA_HI,y);\r
388 }\r
389 \r
390 \r
391 static void GF1OutW(UBYTE x,UWORD y)\r
392 {\r
393         UltraSelect(x);\r
394         outport(GF1_DATA_LOW,y);\r
395 }\r
396 \r
397 \r
398 static UBYTE GF1InB(UBYTE x)\r
399 {\r
400         UltraSelect(x);\r
401         return inportb(GF1_DATA_HI);\r
402 }\r
403 \r
404 \r
405 static UWORD GF1InW(UBYTE x)\r
406 {\r
407         UltraSelect(x);\r
408         return inport(GF1_DATA_LOW);\r
409 }\r
410 \r
411 \r
412 static void gf1_delay(void)\r
413 {\r
414         inportb(GF1_DRAM);\r
415         inportb(GF1_DRAM);\r
416         inportb(GF1_DRAM);\r
417         inportb(GF1_DRAM);\r
418         inportb(GF1_DRAM);\r
419         inportb(GF1_DRAM);\r
420         inportb(GF1_DRAM);\r
421 }\r
422 \r
423 \r
424 static UBYTE UltraPeek(ULONG address)\r
425 {\r
426         GF1OutW(SET_DRAM_LOW,address);\r
427         GF1OutB(SET_DRAM_HIGH,(address>>16)&0xff);      /* 8 bits */\r
428         return(inportb(GF1_DRAM));\r
429 }\r
430 \r
431 \r
432 static void UltraPoke(ULONG address,UBYTE data)\r
433 {\r
434         GF1OutW(SET_DRAM_LOW,address);\r
435         GF1OutB(SET_DRAM_HIGH,(address>>16)&0xff);\r
436         outportb(GF1_DRAM,data);\r
437 }\r
438 \r
439 \r
440 static void UltraPokeFast(ULONG address,UBYTE *src,ULONG size)\r
441 /*\r
442         [address,size> doesn't cross 64k page boundary\r
443 */\r
444 {\r
445         if(!size) return;\r
446 \r
447         UltraSelect(SET_DRAM_HIGH);\r
448         outportb(GF1_DATA_HI,(address>>16)&0xff);       /* 8 bits */\r
449         UltraSelect(SET_DRAM_LOW);\r
450 \r
451         while(size--){\r
452                 outport(GF1_DATA_LOW,address);\r
453                 outportb(GF1_DRAM,*src);\r
454                 address++;\r
455                 src++;\r
456         }\r
457 }\r
458 \r
459 \r
460 static void UltraPokeChunk(ULONG address,UBYTE *src,ULONG size)\r
461 {\r
462         ULONG todo;\r
463 \r
464         /* first 'todo' is number of bytes 'till first 64k boundary */\r
465 \r
466         todo=0x10000-(address&0xffff);\r
467         if(todo>size) todo=size;\r
468 \r
469         do{\r
470                 UltraPokeFast(address,src,todo);\r
471                 address+=todo;\r
472                 src+=todo;\r
473                 size-=todo;\r
474 \r
475                 /* next 'todo' is in chunks of max 64k at once. */\r
476                 todo=(size>0xffff) ? 0x10000 : size;\r
477 \r
478         } while(todo);\r
479 }\r
480 \r
481 \r
482 static ULONG UltraPeekLong(ULONG address)\r
483 {\r
484         ULONG data;\r
485         char *s=(char *)&data;\r
486         s[0]=UltraPeek(address);\r
487         s[1]=UltraPeek(address+1);\r
488         s[2]=UltraPeek(address+2);\r
489         s[3]=UltraPeek(address+3);\r
490         return data;\r
491 }\r
492 \r
493 \r
494 static void UltraPokeLong(ULONG address,ULONG data)\r
495 {\r
496         UltraPokeChunk(address,(UBYTE *)&data,4);\r
497 }\r
498 \r
499 \r
500 static void UltraEnableOutput(void)\r
501 {\r
502         GUS_MIX_IMAGE &= ~ENABLE_OUTPUT;\r
503         outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);\r
504 }\r
505 \r
506 static void UltraDisableOutput(void)\r
507 {\r
508         GUS_MIX_IMAGE |= ENABLE_OUTPUT;\r
509         outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);\r
510 }\r
511 \r
512 static void UltraEnableLineIn(void)\r
513 {\r
514         GUS_MIX_IMAGE &= ~ENABLE_LINE_IN;\r
515         outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);\r
516 }\r
517 \r
518 static void UltraDisableLineIn(void)\r
519 {\r
520         GUS_MIX_IMAGE |= ENABLE_LINE_IN;\r
521         outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);\r
522 }\r
523 \r
524 static void UltraEnableMicIn(void)\r
525 {\r
526         GUS_MIX_IMAGE |= ENABLE_MIC_IN;\r
527         outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);\r
528 }\r
529 \r
530 \r
531 static void UltraDisableMicIn(void)\r
532 {\r
533         GUS_MIX_IMAGE &= ~ENABLE_MIC_IN;\r
534         outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);\r
535 }\r
536 \r
537 \r
538 static void UltraReset(int voices)\r
539 {\r
540         int v;\r
541 \r
542         if(voices<14) voices=14;\r
543         if(voices>32) voices=32;\r
544 \r
545         GUS_VOICES=voices;\r
546         GUS_TIMER_CTRL=0;\r
547         GUS_TIMER_MASK=0;\r
548 \r
549         UltraPokeLong(0,0);\r
550 \r
551         GF1OutB(MASTER_RESET,0x00);\r
552         for(v=0;v<10;v++) gf1_delay();\r
553 \r
554         /* Release Reset and wait */\r
555         GF1OutB(MASTER_RESET,GF1_MASTER_RESET);\r
556         for (v=0;v<10;v++) gf1_delay();\r
557 \r
558         /* Reset the MIDI port also */\r
559         outportb(GF1_MIDI_CTRL,MIDI_RESET);\r
560         for (v=0;v<10;v++) gf1_delay();\r
561         outportb(GF1_MIDI_CTRL,0x00);\r
562 \r
563         /* Clear all interrupts. */\r
564         GF1OutB(DMA_CONTROL,0x00);\r
565         GF1OutB(TIMER_CONTROL,0x00);\r
566         GF1OutB(SAMPLE_CONTROL,0x00);\r
567 \r
568         /* Set the number of active voices */\r
569         GF1OutB(SET_VOICES,((voices-1) | 0xC0));\r
570 \r
571         /* Clear interrupts on voices. */\r
572         /* Reading the status ports will clear the irqs. */\r
573 \r
574         inportb(GF1_IRQ_STAT);\r
575 \r
576         GF1InB(DMA_CONTROL);\r
577         GF1InB(SAMPLE_CONTROL);\r
578         GF1InB(GET_IRQV);\r
579 \r
580         for(v=0;v<voices;v++){\r
581 \r
582                 /* Select the proper voice */\r
583                 outportb(GF1_PAGE,v);\r
584 \r
585                 /* Stop the voice and volume */\r
586                 GF1OutB(SET_CONTROL,VOICE_STOPPED|STOP_VOICE);\r
587                 GF1OutB(SET_VOLUME_CONTROL,VOLUME_STOPPED|STOP_VOLUME);\r
588 \r
589                 gf1_delay(); /* Wait 4.8 micos. or more. */\r
590 \r
591                 /* Initialize each voice specific registers. This is not */\r
592                 /* really necessary, but is nice for completeness sake .. */\r
593                 /* Each application will set up these to whatever values */\r
594                 /* it needs. */\r
595 \r
596                 GF1OutW(SET_FREQUENCY,0x0400);\r
597                 GF1OutW(SET_START_HIGH,0);\r
598                 GF1OutW(SET_START_LOW,0);\r
599                 GF1OutW(SET_END_HIGH,0);\r
600                 GF1OutW(SET_END_LOW,0);\r
601                 GF1OutB(SET_VOLUME_RATE,0x01);\r
602                 GF1OutB(SET_VOLUME_START,0x10);\r
603                 GF1OutB(SET_VOLUME_END,0xe0);\r
604                 GF1OutW(SET_VOLUME,0x0000);\r
605 \r
606                 GF1OutW(SET_ACC_HIGH,0);\r
607                 GF1OutW(SET_ACC_LOW,0);\r
608                 GF1OutB(SET_BALANCE,0x07);\r
609         }\r
610 \r
611         inportb(GF1_IRQ_STAT);\r
612 \r
613         GF1InB(DMA_CONTROL);\r
614         GF1InB(SAMPLE_CONTROL);\r
615         GF1InB(GET_IRQV);\r
616 \r
617         /* Set up GF1 Chip for interrupts & enable DACs. */\r
618 /*      GF1OutB(MASTER_RESET,GF1_MASTER_RESET|GF1_OUTPUT_ENABLE); */\r
619         GF1OutB(MASTER_RESET,GF1_MASTER_RESET|GF1_OUTPUT_ENABLE|GF1_MASTER_IRQ);\r
620 }\r
621 \r
622 \r
623 static BOOL UltraProbe(void)\r
624 {\r
625         UBYTE s1,s2,t1,t2;\r
626 \r
627         /* Pull a reset on the GF1 */\r
628 \r
629         GF1OutB(MASTER_RESET,0x00);\r
630 \r
631         /* Wait a little while ... */\r
632         gf1_delay();\r
633         gf1_delay();\r
634 \r
635         /* Release Reset */\r
636         GF1OutB(MASTER_RESET,GF1_MASTER_RESET);\r
637 \r
638         gf1_delay();\r
639         gf1_delay();\r
640 \r
641         s1=UltraPeek(0); s2=UltraPeek(1);\r
642         UltraPoke(0,0xaa); t1=UltraPeek(0);\r
643         UltraPoke(1,0x55); t2=UltraPeek(1);\r
644         UltraPoke(0,s1); UltraPoke(1,s2);\r
645 \r
646         return(t1==0xaa && t2==0x55);\r
647 }\r
648 \r
649 \r
650 \r
651 static BOOL UltraDetect(void)\r
652 {\r
653         char *ptr;\r
654 \r
655         if((ptr=getenv("ULTRASND"))==NULL) return 0;\r
656 \r
657         if(sscanf(ptr,"%hx,%hd,%hd,%hd,%hd",\r
658                                 &GUS_PORT,\r
659                                 &GUS_DRAM_DMA,\r
660                                 &GUS_ADC_DMA,\r
661                                 &GUS_GF1_IRQ,\r
662                                 &GUS_MIDI_IRQ)!=5) return 0;\r
663 \r
664         return(UltraProbe());\r
665 }\r
666 \r
667 \r
668 \r
669 \r
670 static UBYTE dmalatch[8]       ={ 0,1,0,2,0,3,4,5 };\r
671 static UBYTE irqlatch[16]      ={ 0,0,1,3,0,2,0,4,0,0,0,5,6,0,0,7 };\r
672 \r
673 \r
674 static void UltraSetInterface(int dram,int adc,int gf1,int midi)\r
675 /* int dram;    /* dram dma chan */\r
676 /* int adc;             /* adc dma chan */\r
677 /* int gf1;             /* gf1 irq # */\r
678 /* int midi;    /* midi irq # */\r
679 {\r
680         UBYTE gf1_irq, midi_irq,dram_dma,adc_dma;\r
681         UBYTE irq_control,dma_control;\r
682         UBYTE mix_image;\r
683 \r
684         /* Don't need to check for 0 irq #. Its latch entry = 0 */\r
685         gf1_irq =irqlatch[gf1];\r
686         midi_irq=irqlatch[midi];\r
687         midi_irq<<=3;\r
688 \r
689         dram_dma=dmalatch[dram];\r
690         adc_dma =dmalatch[adc];\r
691         adc_dma<<=3;\r
692 \r
693         irq_control=dma_control=0x0;\r
694 \r
695         mix_image=GUS_MIX_IMAGE;\r
696 \r
697         irq_control|=gf1_irq;\r
698 \r
699         if((gf1==midi) && (gf1!=0))\r
700                 irq_control|=0x40;\r
701         else\r
702                 irq_control|=midi_irq;\r
703 \r
704         dma_control|=dram_dma;\r
705 \r
706         if((dram==adc) && (dram!=0))\r
707                 dma_control|=0x40;\r
708         else\r
709                 dma_control|=adc_dma;\r
710 \r
711         /* Set up for Digital ASIC */\r
712         outportb(GUS_PORT+0x0f,0x5);\r
713         outportb(GF1_MIX_CTRL,mix_image);\r
714         outportb(GF1_IRQ_CTRL,0x0);\r
715         outportb(GUS_PORT+0x0f,0x0);\r
716 \r
717         /* First do DMA control register */\r
718         outportb(GF1_MIX_CTRL,mix_image);\r
719         outportb(GF1_IRQ_CTRL,dma_control|0x80);\r
720 \r
721         /* IRQ CONTROL REG */\r
722         outportb(GF1_MIX_CTRL,mix_image|0x40);\r
723         outportb(GF1_IRQ_CTRL,irq_control);\r
724 \r
725         /* First do DMA control register */\r
726         outportb(GF1_MIX_CTRL,mix_image);\r
727         outportb(GF1_IRQ_CTRL,dma_control);\r
728 \r
729         /* IRQ CONTROL REG */\r
730         outportb(GF1_MIX_CTRL,mix_image|0x40);\r
731         outportb(GF1_IRQ_CTRL,irq_control);\r
732 \r
733         /* IRQ CONTROL, ENABLE IRQ */\r
734         /* just to Lock out writes to irq\dma register ... */\r
735         outportb(GF1_VOICE_SELECT,0);\r
736 \r
737         /* enable output & irq, disable line & mic input */\r
738         mix_image|=0x09;\r
739         outportb(GF1_MIX_CTRL,mix_image);\r
740 \r
741         /* just to Lock out writes to irq\dma register ... */\r
742         outportb(GF1_VOICE_SELECT,0x0);\r
743 \r
744         /* put image back .... */\r
745         GUS_MIX_IMAGE=mix_image;\r
746 }\r
747 \r
748 \r
749 static BOOL UltraPP(ULONG address)\r
750 {\r
751         UBYTE s,t;\r
752         s=UltraPeek(address);\r
753         UltraPoke(address,0xaa);\r
754         t=UltraPeek(address);\r
755         UltraPoke(address,s);\r
756         return(t==0xaa);\r
757 }\r
758 \r
759 \r
760 static UWORD UltraSizeDram(void)\r
761 {\r
762         if(!UltraPP(0))      return 0;\r
763         if(!UltraPP(262144)) return 256;\r
764         if(!UltraPP(524288)) return 512;\r
765         if(!UltraPP(786432)) return 768;\r
766         return 1024;\r
767 }\r
768 \r
769 \r
770 \r
771 \r
772 \r
773 static ULONG UltraMemTotal(void)\r
774 {\r
775         ULONG node=GUS_POOL,nsize,total=0;\r
776 \r
777         while(node!=0){\r
778                 nsize=UltraPeekLong(node);\r
779                 total+=nsize;\r
780                 node=UltraPeekLong(node+4);\r
781         }\r
782         return total;\r
783 }\r
784 \r
785 \r
786 \r
787 static BOOL Mergeable(ULONG a,ULONG b)\r
788 {\r
789         return(a && b && (a+UltraPeekLong(a))==b);\r
790 }\r
791 \r
792 \r
793 \r
794 static ULONG Merge(ULONG a,ULONG b)\r
795 {\r
796         UltraPokeLong(a,UltraPeekLong(a)+UltraPeekLong(b));\r
797         UltraPokeLong(a+4,UltraPeekLong(b+4));\r
798         return a;\r
799 }\r
800 \r
801 \r
802 \r
803 static void UltraFree(ULONG size,ULONG location)\r
804 {\r
805         ULONG pred=0,succ=GUS_POOL;\r
806 \r
807         if(!size) return;\r
808         size+=31;\r
809         size&=-32L;\r
810 \r
811         UltraPokeLong(location,size);\r
812 \r
813         while(succ!=0 && succ<=location){\r
814                 pred=succ;\r
815                 succ=UltraPeekLong(succ+4);\r
816         }\r
817 \r
818         if(pred)\r
819                 UltraPokeLong(pred+4,location);\r
820         else\r
821                 GUS_POOL=location;\r
822 \r
823         UltraPokeLong(location+4,succ);\r
824 \r
825         if(Mergeable(pred,location)){\r
826                 location=Merge(pred,location);\r
827         }\r
828 \r
829         if(Mergeable(location,succ)){\r
830                 Merge(location,succ);\r
831         }\r
832 }\r
833 \r
834 \r
835 /*\r
836 void DumpPool(void)\r
837 {\r
838         ULONG node=GUS_POOL;\r
839 \r
840         while(node!=0){\r
841                 printf("Node %ld, size %ld, next %ld\n",node,UltraPeekLong(node),UltraPeekLong(node+4));\r
842                 node=UltraPeekLong(node+4);\r
843         }\r
844 }\r
845 */\r
846 \r
847 \r
848 \r
849 \r
850 \r
851 \r
852 static ULONG UltraMalloc(ULONG reqsize)\r
853 {\r
854         ULONG curnode=GUS_POOL,cursize,newnode,newsize,pred,succ;\r
855 \r
856         if(!reqsize) return 0;\r
857 \r
858         /* round size to 32 bytes */\r
859 \r
860         reqsize+=31;\r
861         reqsize&=-32L;\r
862 \r
863         /* as long as there are nodes: */\r
864 \r
865         pred=0;\r
866 \r
867         while(curnode!=0){\r
868 \r
869                 succ=UltraPeekLong(curnode+4);\r
870 \r
871                 /* get current node size */\r
872 \r
873                 cursize=UltraPeekLong(curnode);\r
874 \r
875                 /* requested block fits? */\r
876 \r
877                 if(cursize>=reqsize){\r
878 \r
879                         /* it fits, so we're allocating the first\r
880                            'size' bytes of this node */\r
881 \r
882                         /* find new node position and size */\r
883 \r
884                         newnode=curnode+reqsize;\r
885                         newsize=cursize-reqsize;\r
886 \r
887                         /* create a new freenode if needed: */\r
888 \r
889                         if(newsize>=8){\r
890                                 UltraPokeLong(newnode,newsize);\r
891                                 UltraPokeLong(newnode+4,succ);\r
892                                 succ=newnode;\r
893                         }\r
894 \r
895                         /* link prednode & succnode */\r
896 \r
897                         if(pred)\r
898                                 UltraPokeLong(pred+4,succ);\r
899                         else\r
900                                 GUS_POOL=succ;\r
901 \r
902                         /* store size of allocated memory block in block itself: */\r
903 \r
904                         UltraPokeLong(curnode,reqsize);\r
905                         return curnode;\r
906                 }\r
907 \r
908                 /* doesn't fit, try next node */\r
909                 curnode=succ;\r
910         }\r
911         return 0;\r
912 }\r
913 \r
914 \r
915 \r
916 static ULONG UltraMalloc16(ULONG reqsize)\r
917 /*\r
918         Allocates a free block of gus memory, suited for 16 bit samples i.e.\r
919         smaller than 256k and doesn't cross a 256k page.\r
920 */\r
921 {\r
922         ULONG p,spage,epage;\r
923 \r
924         if(reqsize>262144) return 0;\r
925 \r
926         /* round size to 32 bytes */\r
927 \r
928         reqsize+=31;\r
929         reqsize&=-32L;\r
930 \r
931         p=UltraMalloc(reqsize);\r
932         spage=p>>18;\r
933         epage=(p+reqsize-1)>>18;\r
934 \r
935         if(p && spage!=epage){\r
936                 ULONG newp,esize;\r
937 \r
938                 /* free the second part of the block, and try again */\r
939 \r
940                 esize=(p+reqsize)-(epage<<18);\r
941                 UltraFree(esize,epage<<18);\r
942 \r
943                 newp=UltraMalloc16(reqsize);\r
944 \r
945                 /* free first part of the previous block */\r
946 \r
947                 UltraFree(reqsize-esize,p);\r
948                 p=newp;\r
949         }\r
950 \r
951         return p;\r
952 }\r
953 \r
954 \r
955 \r
956 static void UltraMemInit(void)\r
957 {\r
958         UWORD memsize;\r
959         GUS_POOL=32;\r
960         memsize=UltraSizeDram();\r
961         UltraPokeLong(GUS_POOL,((ULONG)memsize<<10)-32);\r
962         UltraPokeLong(GUS_POOL+4,0);\r
963 }\r
964 \r
965 \r
966 static void UltraNumVoices(int voices)\r
967 {\r
968         UltraDisableLineIn();\r
969         UltraDisableMicIn();\r
970         UltraDisableOutput();\r
971         UltraReset(voices);\r
972         UltraSetInterface(GUS_DRAM_DMA,GUS_ADC_DMA,GUS_GF1_IRQ,GUS_MIDI_IRQ);\r
973 }\r
974 \r
975 \r
976 static void interrupt gf1handler(MIRQARGS)\r
977 {\r
978         UBYTE irq_source;\r
979         UBYTE oldselect=GUS_SELECT;\r
980 \r
981         while(irq_source=inportb(GF1_IRQ_STAT)){\r
982 \r
983 /*              if(irq_source & DMA_TC_IRQ){\r
984                         no provisions for DMA-ready irq yet\r
985                 }\r
986 \r
987                 if(irq_source & (MIDI_TX_IRQ|MIDI_RX_IRQ)){\r
988                         no provisions for MIDI-ready irq yet\r
989                 }\r
990 */\r
991                 if (irq_source & GF1_TIMER1_IRQ){\r
992                         GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL & ~0x04);\r
993                         GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL);\r
994                         if(GUS_TIMER1_FUNC!=NULL) GUS_TIMER1_FUNC();\r
995                 }\r
996 \r
997                 if (irq_source & GF1_TIMER2_IRQ){\r
998                         GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL & ~0x08);\r
999                         GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL);\r
1000                         if(GUS_TIMER2_FUNC!=NULL) GUS_TIMER2_FUNC();\r
1001                 }\r
1002 \r
1003 /*              if (irq_source & (WAVETABLE_IRQ | ENVELOPE_IRQ)){\r
1004                         no wavetable or envelope irq provisions yet\r
1005                 }\r
1006 */\r
1007         }\r
1008 \r
1009         MIrq_EOI(GUS_GF1_IRQ);\r
1010         UltraSelect(oldselect);\r
1011 }\r
1012 \r
1013 \r
1014 static PVI oldhandler;\r
1015 typedef void (*PFV)(void);\r
1016 \r
1017 \r
1018 static PFV UltraTimer1Handler(PFV handler)\r
1019 {\r
1020         PFV old=GUS_TIMER1_FUNC;\r
1021         GUS_TIMER1_FUNC=handler;\r
1022         return old;\r
1023 }\r
1024 \r
1025 \r
1026 static PFV UltraTimer2Handler(PFV handler)\r
1027 {\r
1028         PFV old=GUS_TIMER1_FUNC;\r
1029         GUS_TIMER1_FUNC=handler;\r
1030         return old;\r
1031 }\r
1032 \r
1033 \r
1034 static void UltraOpen(int voices)\r
1035 {\r
1036         GUS_MIX_IMAGE=0x0b;\r
1037         GUS_TIMER1_FUNC=NULL;\r
1038         GUS_TIMER2_FUNC=NULL;\r
1039 \r
1040         UltraDisableLineIn();\r
1041         UltraDisableMicIn();\r
1042         UltraDisableOutput();\r
1043 \r
1044         UltraReset(voices);\r
1045         UltraSetInterface(GUS_DRAM_DMA,GUS_ADC_DMA,GUS_GF1_IRQ,GUS_MIDI_IRQ);\r
1046         UltraMemInit();\r
1047         oldhandler=MIrq_SetHandler(GUS_GF1_IRQ,gf1handler);\r
1048         MIrq_OnOff(GUS_GF1_IRQ,1);\r
1049 }\r
1050 \r
1051 \r
1052 static void UltraClose(void)\r
1053 {\r
1054         MIrq_OnOff(GUS_GF1_IRQ,0);\r
1055         MIrq_SetHandler(GUS_GF1_IRQ,oldhandler);\r
1056         UltraDisableOutput();\r
1057         UltraDisableLineIn();\r
1058         UltraDisableMicIn();\r
1059         UltraReset(14);\r
1060 }\r
1061 \r
1062 \r
1063 static void UltraSelectVoice(UBYTE voice)\r
1064 {\r
1065         /* Make sure was are talking to proper voice */\r
1066         outportb(GF1_VOICE_SELECT,voice);\r
1067 }\r
1068 \r
1069 \r
1070 \r
1071 static void UltraSetVoiceEnd(ULONG end)\r
1072 {\r
1073         ULONG phys_end;\r
1074         UBYTE data;\r
1075 \r
1076         data=GF1InB(GET_CONTROL);\r
1077 \r
1078         phys_end=(data&VC_DATA_TYPE)?convert_to_16bit(end):end;\r
1079 \r
1080         /* Set end address of buffer */\r
1081         GF1OutW(SET_END_LOW,ADDR_LOW(phys_end));\r
1082         GF1OutW(SET_END_HIGH,ADDR_HIGH(phys_end));\r
1083 \r
1084         data&=~(VC_IRQ_PENDING|VOICE_STOPPED|STOP_VOICE);\r
1085 \r
1086         GF1OutB(SET_CONTROL,data);\r
1087         gf1_delay();\r
1088 \r
1089         GF1OutB(SET_CONTROL,data);\r
1090 }\r
1091 \r
1092 \r
1093 /* The formula for this table is:\r
1094         1,000,000 / (1.619695497 * # of active voices)\r
1095 \r
1096         The 1.619695497 is calculated by knowing that 14 voices\r
1097                 gives exactly 44.1 Khz. Therefore,\r
1098                 1,000,000 / (X * 14) = 44100\r
1099                 X = 1.619695497\r
1100 */\r
1101 \r
1102 static UWORD freq_divisor[19] = {\r
1103         44100,          /* 14 active voices */\r
1104         41160,          /* 15 active voices */\r
1105         38587,          /* 16 active voices */\r
1106         36317,          /* 17 active voices */\r
1107         34300,          /* 18 active voices */\r
1108         32494,          /* 19 active voices */\r
1109         30870,          /* 20 active voices */\r
1110         29400,          /* 21 active voices */\r
1111         28063,          /* 22 active voices */\r
1112         26843,          /* 23 active voices */\r
1113         25725,          /* 24 active voices */\r
1114         24696,          /* 25 active voices */\r
1115         23746,          /* 26 active voices */\r
1116         22866,          /* 27 active voices */\r
1117         22050,          /* 28 active voices */\r
1118         21289,          /* 29 active voices */\r
1119         20580,          /* 30 active voices */\r
1120         19916,          /* 31 active voices */\r
1121         19293}          /* 32 active voices */\r
1122 ;\r
1123 \r
1124 static void UltraSetFrequency(ULONG speed_khz)\r
1125 {\r
1126         UWORD fc;\r
1127         ULONG temp;\r
1128 \r
1129         /* FC is calculated based on the # of active voices ... */\r
1130         temp=freq_divisor[GUS_VOICES-14];\r
1131 \r
1132         fc=(((speed_khz<<9L)+(temp>>1L))/temp);\r
1133         GF1OutW(SET_FREQUENCY,fc<<1);\r
1134 }\r
1135 \r
1136 \r
1137 static void UltraSetLoopMode(UBYTE mode)\r
1138 {\r
1139         UBYTE data;\r
1140         UBYTE vmode;\r
1141 \r
1142         /* set/reset the rollover bit as per user request */\r
1143 \r
1144         vmode=GF1InB(GET_VOLUME_CONTROL);\r
1145 \r
1146         if(mode&USE_ROLLOVER)\r
1147                 vmode|=VC_ROLLOVER;\r
1148         else\r
1149                 vmode&=~VC_ROLLOVER;\r
1150 \r
1151         GF1OutB(SET_VOLUME_CONTROL,vmode);\r
1152         gf1_delay();\r
1153         GF1OutB(SET_VOLUME_CONTROL,vmode);\r
1154 \r
1155         data=GF1InB(GET_CONTROL);\r
1156 \r
1157         data&=~(VC_WAVE_IRQ|VC_BI_LOOP|VC_LOOP_ENABLE); /* isolate the bits */\r
1158         mode&=VC_WAVE_IRQ|VC_BI_LOOP|VC_LOOP_ENABLE;    /* no bad bits passed in */\r
1159         data|=mode;             /* turn on proper bits ... */\r
1160 \r
1161         GF1OutB(SET_CONTROL,data);\r
1162         gf1_delay();\r
1163         GF1OutB(SET_CONTROL,data);\r
1164 }\r
1165 \r
1166 \r
1167 static ULONG UltraReadVoice(void)\r
1168 {\r
1169         UWORD count_low,count_high;\r
1170         ULONG acc;\r
1171         UBYTE mode;\r
1172 \r
1173         /* Get the high & low portion of the accumulator */\r
1174         count_high = GF1InW(GET_ACC_HIGH);\r
1175         count_low = GF1InW(GET_ACC_LOW);\r
1176 \r
1177         /* convert from UltraSound's format to a physical address */\r
1178 \r
1179         mode=GF1InB(GET_CONTROL);\r
1180 \r
1181         acc=make_physical_address(count_low,count_high,mode);\r
1182         acc&=0xfffffL;          /* Only 20 bits please ... */\r
1183 \r
1184         return(acc);\r
1185 }\r
1186 \r
1187 \r
1188 \r
1189 static void UltraSetVoice(ULONG location)\r
1190 {\r
1191         ULONG phys_loc;\r
1192         UBYTE data;\r
1193 \r
1194         data=GF1InB(GET_CONTROL);\r
1195 \r
1196         phys_loc=(data&VC_DATA_TYPE)?convert_to_16bit(location):location;\r
1197 \r
1198         /* First set accumulator to beginning of data */\r
1199         GF1OutW(SET_ACC_HIGH,ADDR_HIGH(phys_loc));\r
1200         GF1OutW(SET_ACC_LOW,ADDR_LOW(phys_loc));\r
1201 }\r
1202 \r
1203 \r
1204 \r
1205 static UBYTE UltraPrimeVoice(ULONG begin,ULONG start,ULONG end,UBYTE mode)\r
1206 {\r
1207         ULONG phys_start,phys_end;\r
1208         ULONG phys_begin;\r
1209         ULONG temp;\r
1210         UBYTE vmode;\r
1211 \r
1212         /* if start is greater than end, flip 'em and turn on */\r
1213         /* decrementing addresses */\r
1214         if(start>end){\r
1215                 temp=start;\r
1216                 start=end;\r
1217                 end=temp;\r
1218                 mode|=VC_DIRECT;\r
1219         }\r
1220 \r
1221         /* if 16 bit data, must convert addresses */\r
1222         if(mode&VC_DATA_TYPE){\r
1223                 phys_begin = convert_to_16bit(begin);\r
1224                 phys_start = convert_to_16bit(start);\r
1225                 phys_end   = convert_to_16bit(end);\r
1226         }\r
1227         else{\r
1228                 phys_begin = begin;\r
1229                 phys_start = start;\r
1230                 phys_end = end;\r
1231         }\r
1232 \r
1233         /* set/reset the rollover bit as per user request */\r
1234         vmode=GF1InB(GET_VOLUME_CONTROL);\r
1235 \r
1236         if(mode&USE_ROLLOVER)\r
1237                 vmode |= VC_ROLLOVER;\r
1238         else\r
1239                 vmode &= ~VC_ROLLOVER;\r
1240 \r
1241         GF1OutB(SET_VOLUME_CONTROL,vmode);\r
1242         gf1_delay();\r
1243         GF1OutB(SET_VOLUME_CONTROL,vmode);\r
1244 \r
1245         /* First set accumulator to beginning of data */\r
1246         GF1OutW(SET_ACC_LOW,ADDR_LOW(phys_begin));\r
1247         GF1OutW(SET_ACC_HIGH,ADDR_HIGH(phys_begin));\r
1248 \r
1249         /* Set start loop address of buffer */\r
1250         GF1OutW(SET_START_HIGH,ADDR_HIGH(phys_start));\r
1251         GF1OutW(SET_START_LOW,ADDR_LOW(phys_start));\r
1252 \r
1253         /* Set end address of buffer */\r
1254         GF1OutW(SET_END_HIGH,ADDR_HIGH(phys_end));\r
1255         GF1OutW(SET_END_LOW,ADDR_LOW(phys_end));\r
1256         return(mode);\r
1257 }\r
1258 \r
1259 \r
1260 static void UltraGoVoice(UBYTE mode)\r
1261 {\r
1262         mode&=~(VOICE_STOPPED|STOP_VOICE);      /* turn 'stop' bits off ... */\r
1263 \r
1264         /* NOTE: no irq's from the voice ... */\r
1265 \r
1266         GF1OutB(SET_CONTROL,mode);\r
1267         gf1_delay();\r
1268         GF1OutB(SET_CONTROL,mode);\r
1269 }\r
1270 \r
1271 \r
1272 /**********************************************************************\r
1273  *\r
1274  * This function will start playing a wave out of DRAM. It assumes\r
1275  * the playback rate, volume & balance have been set up before ...\r
1276  *\r
1277  *********************************************************************/\r
1278 \r
1279 static void UltraStartVoice(ULONG begin,ULONG start,ULONG end,UBYTE mode)\r
1280 {\r
1281         mode=UltraPrimeVoice(begin,start,end,mode);\r
1282         UltraGoVoice(mode);\r
1283 }\r
1284 \r
1285 \r
1286 \r
1287 /***************************************************************\r
1288  * This function will stop a given voices output. Note that a delay\r
1289  * is necessary after the stop is issued to ensure the self-\r
1290  * modifying bits aren't a problem.\r
1291  ***************************************************************/\r
1292 \r
1293 static void UltraStopVoice(void)\r
1294 {\r
1295         UBYTE data;\r
1296 \r
1297         /* turn off the roll over bit first ... */\r
1298 \r
1299         data=GF1InB(GET_VOLUME_CONTROL);\r
1300         data&=~VC_ROLLOVER;\r
1301 \r
1302         GF1OutB(SET_VOLUME_CONTROL,data);\r
1303         gf1_delay();\r
1304         GF1OutB(SET_VOLUME_CONTROL,data);\r
1305 \r
1306         /* Now stop the voice  */\r
1307 \r
1308         data=GF1InB(GET_CONTROL);\r
1309         data&=~VC_WAVE_IRQ;             /* disable irq's & stop voice .. */\r
1310         data|=VOICE_STOPPED|STOP_VOICE;\r
1311 \r
1312         GF1OutB(SET_CONTROL,data);                      /* turn it off */\r
1313         gf1_delay();\r
1314         GF1OutB(SET_CONTROL,data);\r
1315 }\r
1316 \r
1317 \r
1318 static int UltraVoiceStopped(void)\r
1319 {\r
1320         return(GF1InB(GET_CONTROL) & (VOICE_STOPPED|STOP_VOICE));\r
1321 }\r
1322 \r
1323 \r
1324 static void UltraSetBalance(UBYTE pan)\r
1325 {\r
1326         GF1OutB(SET_BALANCE,pan&0xf);\r
1327 }\r
1328 \r
1329 \r
1330 static void UltraSetVolume(UWORD volume)\r
1331 {\r
1332         GF1OutW(SET_VOLUME,volume<<4);\r
1333 }\r
1334 \r
1335 \r
1336 static UWORD UltraReadVolume(void)\r
1337 {\r
1338         return(GF1InW(GET_VOLUME)>>4);\r
1339 }\r
1340 \r
1341 \r
1342 static void UltraStopVolume(void)\r
1343 {\r
1344         UBYTE vmode;\r
1345 \r
1346         vmode=GF1InB(GET_VOLUME_CONTROL);\r
1347         vmode|=(VOLUME_STOPPED|STOP_VOLUME);\r
1348 \r
1349         GF1OutB(SET_VOLUME_CONTROL,vmode);\r
1350         gf1_delay();\r
1351         GF1OutB(SET_VOLUME_CONTROL,vmode);\r
1352 }\r
1353 \r
1354 \r
1355 static void UltraRampVolume(UWORD start,UWORD end,UBYTE rate,UBYTE mode)\r
1356 {\r
1357         UWORD begin;\r
1358         UBYTE vmode;\r
1359 \r
1360         if(start==end) return;\r
1361 /*********************************************************************\r
1362  * If the start volume is greater than the end volume, flip them and\r
1363  * turn on decreasing volume. Note that the GF1 requires that the\r
1364  * programmed start volume MUST be less than or equal to the end\r
1365  * volume.\r
1366  *********************************************************************/\r
1367         /* Don't let bad bits thru ... */\r
1368         mode&=~(VL_IRQ_PENDING|VC_ROLLOVER|STOP_VOLUME|VOLUME_STOPPED);\r
1369 \r
1370         begin = start;\r
1371 \r
1372         if(start>end){\r
1373                 /* flip start & end if decreasing numbers ... */\r
1374                 start = end;\r
1375                 end = begin;\r
1376                 mode |= VC_DIRECT;              /* decreasing volumes */\r
1377         }\r
1378 \r
1379         /* looping below 64 or greater that 4032 can cause strange things */\r
1380         if(start<64) start=64;\r
1381         if(end>4032) end=4032;\r
1382 \r
1383         GF1OutB(SET_VOLUME_RATE,rate);\r
1384         GF1OutB(SET_VOLUME_START,start>>4);\r
1385         GF1OutB(SET_VOLUME_END,end>>4);\r
1386 \r
1387         /* Also MUST set the current volume to the start volume ... */\r
1388         UltraSetVolume(begin);\r
1389 \r
1390         vmode=GF1InB(GET_VOLUME_CONTROL);\r
1391 \r
1392         if(vmode&VC_ROLLOVER) mode|=VC_ROLLOVER;\r
1393 \r
1394         /* start 'er up !!! */\r
1395         GF1OutB(SET_VOLUME_CONTROL,mode);\r
1396         gf1_delay();\r
1397         GF1OutB(SET_VOLUME_CONTROL,mode);\r
1398 }\r
1399 \r
1400 \r
1401 static void UltraVectorVolume(UWORD end,UBYTE rate,UBYTE mode)\r
1402 {\r
1403         UltraStopVolume();\r
1404         UltraRampVolume(UltraReadVolume(),end,rate,mode);\r
1405 }\r
1406 \r
1407 \r
1408 static int UltraVolumeStopped(void)\r
1409 {\r
1410         return(GF1InB(GET_VOLUME_CONTROL) & (VOLUME_STOPPED|STOP_VOLUME));\r
1411 }\r
1412 \r
1413 \r
1414 static UWORD vol_rates[19]={\r
1415 23,24,26,28,29,31,32,34,36,37,39,40,42,44,45,47,49,50,52\r
1416 };\r
1417 \r
1418 \r
1419 static UBYTE UltraCalcRate(UWORD start,UWORD end,ULONG mil_secs)\r
1420 {\r
1421         ULONG gap,mic_secs;\r
1422         UWORD i,range,increment;\r
1423         UBYTE rate_val;\r
1424         UWORD value;\r
1425 \r
1426         gap = (start>end) ? (start-end) : (end-start);\r
1427         mic_secs = (mil_secs * 1000L)/gap;\r
1428 \r
1429 /* OK. We now have the # of microseconds for each update to go from */\r
1430 /* A to B in X milliseconds. See what the best fit is in the table */\r
1431 \r
1432         range = 4;\r
1433         value = vol_rates[GUS_VOICES-14];\r
1434 \r
1435         for(i=0;i<3;i++){\r
1436                 if(mic_secs<value){\r
1437                         range = i;\r
1438                         break;\r
1439                 }\r
1440                 else value<<=3;\r
1441         }\r
1442 \r
1443         if(range==4){\r
1444                 range = 3;\r
1445                 increment = 1;\r
1446         }\r
1447         else{\r
1448                 /* calculate increment value ... (round it up ?) */\r
1449                 increment=(unsigned int)((value + (value>>1)) / mic_secs);\r
1450         }\r
1451 \r
1452         rate_val=range<<6;\r
1453         rate_val|=(increment&0x3F);\r
1454         return(rate_val);\r
1455 }\r
1456 \r
1457 \r
1458 static UWORD _gf1_volumes[512] = {\r
1459  0x0000,\r
1460  0x0700, 0x07ff, 0x0880, 0x08ff, 0x0940, 0x0980, 0x09c0, 0x09ff, 0x0a20,\r
1461  0x0a40, 0x0a60, 0x0a80, 0x0aa0, 0x0ac0, 0x0ae0, 0x0aff, 0x0b10, 0x0b20,\r
1462  0x0b30, 0x0b40, 0x0b50, 0x0b60, 0x0b70, 0x0b80, 0x0b90, 0x0ba0, 0x0bb0,\r
1463  0x0bc0, 0x0bd0, 0x0be0, 0x0bf0, 0x0bff, 0x0c08, 0x0c10, 0x0c18, 0x0c20,\r
1464  0x0c28, 0x0c30, 0x0c38, 0x0c40, 0x0c48, 0x0c50, 0x0c58, 0x0c60, 0x0c68,\r
1465  0x0c70, 0x0c78, 0x0c80, 0x0c88, 0x0c90, 0x0c98, 0x0ca0, 0x0ca8, 0x0cb0,\r
1466  0x0cb8, 0x0cc0, 0x0cc8, 0x0cd0, 0x0cd8, 0x0ce0, 0x0ce8, 0x0cf0, 0x0cf8,\r
1467  0x0cff, 0x0d04, 0x0d08, 0x0d0c, 0x0d10, 0x0d14, 0x0d18, 0x0d1c, 0x0d20,\r
1468  0x0d24, 0x0d28, 0x0d2c, 0x0d30, 0x0d34, 0x0d38, 0x0d3c, 0x0d40, 0x0d44,\r
1469  0x0d48, 0x0d4c, 0x0d50, 0x0d54, 0x0d58, 0x0d5c, 0x0d60, 0x0d64, 0x0d68,\r
1470  0x0d6c, 0x0d70, 0x0d74, 0x0d78, 0x0d7c, 0x0d80, 0x0d84, 0x0d88, 0x0d8c,\r
1471  0x0d90, 0x0d94, 0x0d98, 0x0d9c, 0x0da0, 0x0da4, 0x0da8, 0x0dac, 0x0db0,\r
1472  0x0db4, 0x0db8, 0x0dbc, 0x0dc0, 0x0dc4, 0x0dc8, 0x0dcc, 0x0dd0, 0x0dd4,\r
1473  0x0dd8, 0x0ddc, 0x0de0, 0x0de4, 0x0de8, 0x0dec, 0x0df0, 0x0df4, 0x0df8,\r
1474  0x0dfc, 0x0dff, 0x0e02, 0x0e04, 0x0e06, 0x0e08, 0x0e0a, 0x0e0c, 0x0e0e,\r
1475  0x0e10, 0x0e12, 0x0e14, 0x0e16, 0x0e18, 0x0e1a, 0x0e1c, 0x0e1e, 0x0e20,\r
1476  0x0e22, 0x0e24, 0x0e26, 0x0e28, 0x0e2a, 0x0e2c, 0x0e2e, 0x0e30, 0x0e32,\r
1477  0x0e34, 0x0e36, 0x0e38, 0x0e3a, 0x0e3c, 0x0e3e, 0x0e40, 0x0e42, 0x0e44,\r
1478  0x0e46, 0x0e48, 0x0e4a, 0x0e4c, 0x0e4e, 0x0e50, 0x0e52, 0x0e54, 0x0e56,\r
1479  0x0e58, 0x0e5a, 0x0e5c, 0x0e5e, 0x0e60, 0x0e62, 0x0e64, 0x0e66, 0x0e68,\r
1480  0x0e6a, 0x0e6c, 0x0e6e, 0x0e70, 0x0e72, 0x0e74, 0x0e76, 0x0e78, 0x0e7a,\r
1481  0x0e7c, 0x0e7e, 0x0e80, 0x0e82, 0x0e84, 0x0e86, 0x0e88, 0x0e8a, 0x0e8c,\r
1482  0x0e8e, 0x0e90, 0x0e92, 0x0e94, 0x0e96, 0x0e98, 0x0e9a, 0x0e9c, 0x0e9e,\r
1483  0x0ea0, 0x0ea2, 0x0ea4, 0x0ea6, 0x0ea8, 0x0eaa, 0x0eac, 0x0eae, 0x0eb0,\r
1484  0x0eb2, 0x0eb4, 0x0eb6, 0x0eb8, 0x0eba, 0x0ebc, 0x0ebe, 0x0ec0, 0x0ec2,\r
1485  0x0ec4, 0x0ec6, 0x0ec8, 0x0eca, 0x0ecc, 0x0ece, 0x0ed0, 0x0ed2, 0x0ed4,\r
1486  0x0ed6, 0x0ed8, 0x0eda, 0x0edc, 0x0ede, 0x0ee0, 0x0ee2, 0x0ee4, 0x0ee6,\r
1487  0x0ee8, 0x0eea, 0x0eec, 0x0eee, 0x0ef0, 0x0ef2, 0x0ef4, 0x0ef6, 0x0ef8,\r
1488  0x0efa, 0x0efc, 0x0efe, 0x0eff, 0x0f01, 0x0f02, 0x0f03, 0x0f04, 0x0f05,\r
1489  0x0f06, 0x0f07, 0x0f08, 0x0f09, 0x0f0a, 0x0f0b, 0x0f0c, 0x0f0d, 0x0f0e,\r
1490  0x0f0f, 0x0f10, 0x0f11, 0x0f12, 0x0f13, 0x0f14, 0x0f15, 0x0f16, 0x0f17,\r
1491  0x0f18, 0x0f19, 0x0f1a, 0x0f1b, 0x0f1c, 0x0f1d, 0x0f1e, 0x0f1f, 0x0f20,\r
1492  0x0f21, 0x0f22, 0x0f23, 0x0f24, 0x0f25, 0x0f26, 0x0f27, 0x0f28, 0x0f29,\r
1493  0x0f2a, 0x0f2b, 0x0f2c, 0x0f2d, 0x0f2e, 0x0f2f, 0x0f30, 0x0f31, 0x0f32,\r
1494  0x0f33, 0x0f34, 0x0f35, 0x0f36, 0x0f37, 0x0f38, 0x0f39, 0x0f3a, 0x0f3b,\r
1495  0x0f3c, 0x0f3d, 0x0f3e, 0x0f3f, 0x0f40, 0x0f41, 0x0f42, 0x0f43, 0x0f44,\r
1496  0x0f45, 0x0f46, 0x0f47, 0x0f48, 0x0f49, 0x0f4a, 0x0f4b, 0x0f4c, 0x0f4d,\r
1497  0x0f4e, 0x0f4f, 0x0f50, 0x0f51, 0x0f52, 0x0f53, 0x0f54, 0x0f55, 0x0f56,\r
1498  0x0f57, 0x0f58, 0x0f59, 0x0f5a, 0x0f5b, 0x0f5c, 0x0f5d, 0x0f5e, 0x0f5f,\r
1499  0x0f60, 0x0f61, 0x0f62, 0x0f63, 0x0f64, 0x0f65, 0x0f66, 0x0f67, 0x0f68,\r
1500  0x0f69, 0x0f6a, 0x0f6b, 0x0f6c, 0x0f6d, 0x0f6e, 0x0f6f, 0x0f70, 0x0f71,\r
1501  0x0f72, 0x0f73, 0x0f74, 0x0f75, 0x0f76, 0x0f77, 0x0f78, 0x0f79, 0x0f7a,\r
1502  0x0f7b, 0x0f7c, 0x0f7d, 0x0f7e, 0x0f7f, 0x0f80, 0x0f81, 0x0f82, 0x0f83,\r
1503  0x0f84, 0x0f85, 0x0f86, 0x0f87, 0x0f88, 0x0f89, 0x0f8a, 0x0f8b, 0x0f8c,\r
1504  0x0f8d, 0x0f8e, 0x0f8f, 0x0f90, 0x0f91, 0x0f92, 0x0f93, 0x0f94, 0x0f95,\r
1505  0x0f96, 0x0f97, 0x0f98, 0x0f99, 0x0f9a, 0x0f9b, 0x0f9c, 0x0f9d, 0x0f9e,\r
1506  0x0f9f, 0x0fa0, 0x0fa1, 0x0fa2, 0x0fa3, 0x0fa4, 0x0fa5, 0x0fa6, 0x0fa7,\r
1507  0x0fa8, 0x0fa9, 0x0faa, 0x0fab, 0x0fac, 0x0fad, 0x0fae, 0x0faf, 0x0fb0,\r
1508  0x0fb1, 0x0fb2, 0x0fb3, 0x0fb4, 0x0fb5, 0x0fb6, 0x0fb7, 0x0fb8, 0x0fb9,\r
1509  0x0fba, 0x0fbb, 0x0fbc, 0x0fbd, 0x0fbe, 0x0fbf, 0x0fc0, 0x0fc1, 0x0fc2,\r
1510  0x0fc3, 0x0fc4, 0x0fc5, 0x0fc6, 0x0fc7, 0x0fc8, 0x0fc9, 0x0fca, 0x0fcb,\r
1511  0x0fcc, 0x0fcd, 0x0fce, 0x0fcf, 0x0fd0, 0x0fd1, 0x0fd2, 0x0fd3, 0x0fd4,\r
1512  0x0fd5, 0x0fd6, 0x0fd7, 0x0fd8, 0x0fd9, 0x0fda, 0x0fdb, 0x0fdc, 0x0fdd,\r
1513  0x0fde, 0x0fdf, 0x0fe0, 0x0fe1, 0x0fe2, 0x0fe3, 0x0fe4, 0x0fe5, 0x0fe6,\r
1514  0x0fe7, 0x0fe8, 0x0fe9, 0x0fea, 0x0feb, 0x0fec, 0x0fed, 0x0fee, 0x0fef,\r
1515  0x0ff0, 0x0ff1, 0x0ff2, 0x0ff3, 0x0ff4, 0x0ff5, 0x0ff6, 0x0ff7, 0x0ff8,\r
1516  0x0ff9, 0x0ffa, 0x0ffb, 0x0ffc, 0x0ffd, 0x0ffe, 0x0fff\r
1517 };\r
1518 \r
1519 \r
1520 static void UltraSetLinearVolume(UWORD index)\r
1521 {\r
1522         UltraSetVolume(_gf1_volumes[index]);\r
1523 }\r
1524 \r
1525 \r
1526 static void UltraRampLinearVolume(UWORD start_idx,UWORD end_idx,ULONG msecs,UBYTE mode)\r
1527 {\r
1528         UWORD start,end;\r
1529         UBYTE rate;\r
1530 \r
1531         /* Ramp from start to end in x milliseconds */\r
1532 \r
1533         start = _gf1_volumes[start_idx];\r
1534         end   = _gf1_volumes[end_idx];\r
1535 \r
1536         /* calculate a rate to get from start to end in msec milliseconds .. */\r
1537         rate=UltraCalcRate(start,end,msecs);\r
1538         UltraRampVolume(start,end,rate,mode);\r
1539 }\r
1540 \r
1541 \r
1542 static void UltraVectorLinearVolume(UWORD end_idx,UBYTE rate,UBYTE mode)\r
1543 {\r
1544         UltraStopVolume();\r
1545         UltraRampVolume(UltraReadVolume(),_gf1_volumes[end_idx],rate,mode);\r
1546 }\r
1547 \r
1548 \r
1549 static void UltraStartTimer(UBYTE timer,UBYTE time)\r
1550 {\r
1551         UBYTE temp;\r
1552 \r
1553         if (timer == 1){\r
1554                 GUS_TIMER_CTRL |= 0x04;\r
1555                 GUS_TIMER_MASK |= 0x01;\r
1556                 temp = TIMER1;\r
1557         }\r
1558         else{\r
1559                 GUS_TIMER_CTRL |= 0x08;\r
1560                 GUS_TIMER_MASK |= 0x02;\r
1561                 temp = TIMER2;\r
1562         }\r
1563 \r
1564 /*      ENTER_CRITICAL; */\r
1565 \r
1566         time = 256 - time;\r
1567 \r
1568         GF1OutB(temp,time);                                                     /* set timer speed */\r
1569         GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL);          /* enable timer interrupt on gf1 */\r
1570         outportb(GF1_TIMER_CTRL,0x04);                          /* select timer stuff */\r
1571         outportb(GF1_TIMER_DATA,GUS_TIMER_MASK);        /* start the timers */\r
1572 \r
1573 /*      LEAVE_CRITICAL; */\r
1574 }\r
1575 \r
1576 \r
1577 static void UltraStopTimer(int timer)\r
1578 {\r
1579         if (timer == 1){\r
1580                 GUS_TIMER_CTRL &= ~0x04;\r
1581                 GUS_TIMER_MASK &= ~0x01;\r
1582         }\r
1583         else{\r
1584                 GUS_TIMER_CTRL &= ~0x08;\r
1585                 GUS_TIMER_MASK &= ~0x02;\r
1586         }\r
1587 \r
1588 /*      ENTER_CRITICAL; */\r
1589 \r
1590         GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL);          /* disable timer interrupts */\r
1591         outportb(GF1_TIMER_CTRL,0x04);                          /* select timer stuff */\r
1592         outportb(GF1_TIMER_DATA,GUS_TIMER_MASK | 0x80);\r
1593 \r
1594 /*      LEAVE_CRITICAL; */\r
1595 }\r
1596 \r
1597 \r
1598 static BOOL UltraTimerStopped(UBYTE timer)\r
1599 {\r
1600         UBYTE value;\r
1601         UBYTE temp;\r
1602 \r
1603         if (timer == 1)\r
1604                 temp = 0x40;\r
1605         else\r
1606                 temp = 0x20;\r
1607 \r
1608         value = (inportb(GF1_TIMER_CTRL)) & temp;\r
1609 \r
1610         return(value);\r
1611 }\r
1612 \r
1613 \r
1614 /***************************************************************************\r
1615 >>>>>>>>>>>>>>>>>>>>>>>>> The actual GUS driver <<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
1616 ***************************************************************************/\r
1617 \r
1618 \r
1619 static ULONG Ultra[MAXSAMPLEHANDLES];\r
1620 static ULONG Ultrs[MAXSAMPLEHANDLES];\r
1621 \r
1622 /* Ultra[] holds the sample dram adresses\r
1623    of the samples of a module */\r
1624 \r
1625 typedef struct{\r
1626         UBYTE kick;                     /* =1 -> sample has to be restarted */\r
1627         UBYTE active;                   /* =1 -> sample is playing */\r
1628         UWORD flags;                    /* 16/8 bits looping/one-shot */\r
1629         SWORD handle;                  /* identifies the sample */\r
1630         ULONG start;                    /* start index */\r
1631         ULONG size;                     /* samplesize */\r
1632         ULONG reppos;                   /* loop start */\r
1633         ULONG repend;                   /* loop end */\r
1634         ULONG frq;                      /* current frequency */\r
1635         UBYTE vol;                      /* current volume */\r
1636         UBYTE pan;                                              /* current panning position */\r
1637 } GHOLD;\r
1638 \r
1639 static GHOLD ghld[32];\r
1640 \r
1641 \r
1642 static UBYTE timeskip;\r
1643 static UBYTE timecount;\r
1644 static UBYTE GUS_BPM;\r
1645 \r
1646 \r
1647 void UltraSetBPM(UBYTE bpm)\r
1648 {\r
1649         /* The player routine has to be called (bpm*50)/125 times a second,\r
1650            so the interval between calls takes 125/(bpm*50) seconds (amazing!).\r
1651 \r
1652            The Timer1 handler has a resolution of 80 microseconds.\r
1653 \r
1654            So the timer value to program:\r
1655 \r
1656            (125/(bpm*50)) / 80e-6 = 31250/bpm\r
1657         */\r
1658         UWORD rate=31250/bpm;\r
1659 \r
1660         timeskip=0;\r
1661         timecount=0;\r
1662 \r
1663         while(rate>255){\r
1664                 rate>>=1;\r
1665                 timeskip++;\r
1666         }\r
1667         UltraStartTimer(1,rate);\r
1668 }\r
1669 \r
1670 \r
1671 \r
1672 void GUS_Update(void)\r
1673 {\r
1674         UBYTE t;\r
1675         GHOLD *aud;\r
1676         UWORD vol;\r
1677         ULONG base,start,size,reppos,repend;\r
1678 \r
1679         UWORD curvol,bigvol=0,bigvoc=0;\r
1680 \r
1681         if(timecount<timeskip){\r
1682                 timecount++;\r
1683                 return;\r
1684         }\r
1685         timecount=0;\r
1686 \r
1687         md_player();\r
1688 \r
1689         if(GUS_BPM != md_bpm){\r
1690                 UltraSetBPM(md_bpm);\r
1691                 GUS_BPM=md_bpm;\r
1692         }\r
1693 \r
1694         /* ramp down voices that need to be started next */\r
1695 \r
1696         for(t=0;t<md_numchn;t++){\r
1697                 UltraSelectVoice(t);\r
1698                 aud=&ghld[t];\r
1699                 if(aud->kick){\r
1700                         curvol=UltraReadVolume();\r
1701                         if(bigvol<=curvol){\r
1702                                 bigvol=curvol;\r
1703                                 bigvoc=t;\r
1704                         }\r
1705                         UltraVectorLinearVolume(0,0x3f,0);\r
1706                 }\r
1707         }\r
1708 \r
1709 /*      while(!UltraVolumeStopped(bigvoc)); */\r
1710 \r
1711         for(t=0;t<md_numchn;t++){\r
1712                 UltraSelectVoice(t);\r
1713                 aud=&ghld[t];\r
1714 \r
1715                 if(aud->kick){\r
1716                         aud->kick=0;\r
1717 \r
1718                         base=Ultra[aud->handle];\r
1719 \r
1720                         start=aud->start;\r
1721                         reppos=aud->reppos;\r
1722                         repend=aud->repend;\r
1723                         size=aud->size;\r
1724 \r
1725                         if(aud->flags&SF_16BITS){\r
1726                                 start<<=1;\r
1727                                 reppos<<=1;\r
1728                                 repend<<=1;\r
1729                                 size<<=1;\r
1730                         }\r
1731 \r
1732                         /* Stop current sample and start a new one */\r
1733 \r
1734                         UltraStopVoice();\r
1735 \r
1736                         UltraSetFrequency(aud->frq);\r
1737                         UltraVectorLinearVolume(6U*aud->vol,0x3f,0);\r
1738                         UltraSetBalance(aud->pan>>4);\r
1739 \r
1740                         if(aud->flags&SF_LOOP){\r
1741 \r
1742                                 /* Start a looping sample */\r
1743 \r
1744                                 UltraStartVoice(base+start,\r
1745                                                                 base+reppos,\r
1746                                                                 base+repend,0x8|((aud->flags&SF_16BITS)?4:0)|\r
1747                                                                 ((aud->flags&SF_BIDI)?16:0));\r
1748                         }\r
1749                         else{\r
1750 \r
1751                                 /* Start a one-shot sample */\r
1752 \r
1753                                 UltraStartVoice(base+start,\r
1754                                                                 base+start,\r
1755                                                                 base+size+2,(aud->flags&SF_16BITS)?4:0);\r
1756                         }\r
1757                 }\r
1758                 else{\r
1759                         UltraSetFrequency(aud->frq);\r
1760                         UltraVectorLinearVolume(6U*aud->vol,0x3f,0);\r
1761                         UltraSetBalance(aud->pan>>4);\r
1762                 }\r
1763         }\r
1764 }\r
1765 \r
1766 \r
1767 SWORD GUS_Load(FILE *fp,ULONG length,ULONG loopstart,ULONG loopend,UWORD flags)\r
1768 /*\r
1769         callback routine for the MODLOAD module.\r
1770 */\r
1771 {\r
1772         int handle,t;\r
1773         long p,l;\r
1774 \r
1775         SL_Init(fp,flags,flags|SF_SIGNED);\r
1776 \r
1777         /* Find empty slot to put sample address in */\r
1778 \r
1779         for(handle=0;handle<MAXSAMPLEHANDLES;handle++){\r
1780                 if(Ultra[handle]==0) break;\r
1781         }\r
1782 \r
1783         if(handle==MAXSAMPLEHANDLES){\r
1784                 myerr=ERROR_OUT_OF_HANDLES;\r
1785                 return -1;\r
1786         }\r
1787 \r
1788         if(flags&SF_16BITS){\r
1789                 length<<=1;\r
1790                 loopstart<<=1;\r
1791                 loopend<<=1;\r
1792         }\r
1793 \r
1794         /* Allocate GUS dram and store the address in Ultra[handle] */\r
1795         /* Alloc 8 bytes more for anticlick measures. see below. */\r
1796 \r
1797         /* 2.04: use UltraMalloc16 to allocate 16 bit samples */\r
1798 \r
1799         if(!(Ultra[handle]=(flags&SF_16BITS) ? UltraMalloc16(length+8) : UltraMalloc(length+8) )){\r
1800                 myerr=ERROR_SAMPLE_TOO_BIG;\r
1801                 return -1;\r
1802         }\r
1803 \r
1804         /* Load the sample */\r
1805 \r
1806         Ultrs[handle]=length+8;\r
1807         p=Ultra[handle];\r
1808         l=length;\r
1809 \r
1810         while(l>0){\r
1811                 static UBYTE buffer[1024];\r
1812                 long todo;\r
1813 \r
1814                 todo=(l>1024) ? 1024 : l;\r
1815 \r
1816                 SL_Load(buffer,todo);\r
1817 \r
1818                 UltraPokeChunk(p,buffer,todo);\r
1819 \r
1820                 p+=todo;\r
1821                 l-=todo;\r
1822         }\r
1823 \r
1824         if(flags&SF_LOOP && !(flags&SF_BIDI)){  /* looping sample ? */\r
1825 \r
1826                 /*      Anticlick for looping samples:\r
1827                         Copy the first bytes in the loop\r
1828                         beyond the end of the loop */\r
1829 \r
1830                 for(t=0;t<8;t++){\r
1831                         UltraPoke(Ultra[handle]+loopend+t,\r
1832                                           UltraPeek(Ultra[handle]+loopstart+t));\r
1833                 }\r
1834         }\r
1835         else{\r
1836 \r
1837                 /*      Anticlick for one-shot samples:\r
1838                         Zero the bytes beyond the end of the sample.\r
1839                 */\r
1840 \r
1841                 for(t=0;t<8;t++){\r
1842                         UltraPoke(Ultra[handle]+length+t,0);\r
1843                 }\r
1844         }\r
1845 \r
1846         return handle;\r
1847 }\r
1848 \r
1849 \r
1850 \r
1851 void GUS_UnLoad(SWORD handle)\r
1852 /*\r
1853         callback routine to unload samples\r
1854 \r
1855         smp                     :sampleinfo of sample that is being freed\r
1856 */\r
1857 {\r
1858         UltraFree(Ultrs[handle],Ultra[handle]);\r
1859         Ultra[handle]=0;\r
1860 }\r
1861 \r
1862 \r
1863 \r
1864 \r
1865 BOOL GUS_Init(void)\r
1866 {\r
1867         ULONG p1,p2;\r
1868         int irq;\r
1869 \r
1870         if(!(md_mode&DMODE_16BITS)){\r
1871                 md_mode|=DMODE_16BITS;          /* gus can't do 8 bit mixing */\r
1872         }\r
1873 \r
1874         if(!(md_mode&DMODE_STEREO)){\r
1875                 md_mode|=DMODE_STEREO;          /* gus can't do mono mixing */\r
1876         }\r
1877 \r
1878         if(!UltraDetect()){\r
1879                 myerr="Couldn't detect gus, please check env. string";\r
1880                 return 0;\r
1881         }\r
1882 \r
1883         UltraOpen(14);\r
1884         UltraTimer1Handler(GUS_Update);\r
1885 \r
1886         return 1;\r
1887 }\r
1888 \r
1889 \r
1890 \r
1891 void GUS_Exit(void)\r
1892 {\r
1893         UltraClose();\r
1894 }\r
1895 \r
1896 \r
1897 \r
1898 void GUS_PlayStart(void)\r
1899 {\r
1900         int t;\r
1901         for(t=0;t<md_numchn;t++){\r
1902                 ghld[t].flags=0;\r
1903                 ghld[t].handle=0;\r
1904                 ghld[t].kick=0;\r
1905                 ghld[t].active=0;\r
1906                 ghld[t].frq=10000;\r
1907                 ghld[t].vol=0;\r
1908                 ghld[t].pan=(t&1)?0:255;\r
1909         }\r
1910         UltraNumVoices(md_numchn);\r
1911         UltraEnableOutput();\r
1912         GUS_BPM=125;\r
1913         UltraSetBPM(125);\r
1914 }\r
1915 \r
1916 \r
1917 \r
1918 void GUS_PlayStop(void)\r
1919 {\r
1920         UltraStopTimer(1);\r
1921         UltraDisableOutput();\r
1922 }\r
1923 \r
1924 \r
1925 BOOL GUS_IsThere(void)\r
1926 {\r
1927         return(getenv("ULTRASND")!=NULL);\r
1928 }\r
1929 \r
1930 \r
1931 void GUS_VoiceSetVolume(UBYTE voice,UBYTE vol)\r
1932 {\r
1933         ghld[voice].vol=vol;\r
1934 }\r
1935 \r
1936 \r
1937 void GUS_VoiceSetFrequency(UBYTE voice,ULONG frq)\r
1938 {\r
1939         ghld[voice].frq=frq;\r
1940 }\r
1941 \r
1942 void GUS_VoiceSetPanning(UBYTE voice,UBYTE pan)\r
1943 {\r
1944         ghld[voice].pan=pan;\r
1945 }\r
1946 \r
1947 void GUS_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags)\r
1948 {\r
1949         if(start>=size) return;\r
1950 \r
1951         if(flags&SF_LOOP){\r
1952                 if(repend>size) repend=size;    /* repend can't be bigger than size */\r
1953         }\r
1954 \r
1955         ghld[voice].flags=flags;\r
1956         ghld[voice].handle=handle;\r
1957         ghld[voice].start=start;\r
1958         ghld[voice].size=size;\r
1959         ghld[voice].reppos=reppos;\r
1960         ghld[voice].repend=repend;\r
1961         ghld[voice].kick=1;\r
1962 }\r
1963 \r
1964 void GUS_Dummy(void)\r
1965 {\r
1966 }\r
1967 \r
1968 \r
1969 DRIVER drv_gus={\r
1970         NULL,\r
1971         "Gravis Ultrasound",\r
1972         "MikMod GUS Driver v2.1 (uses gus timer interrupt)",\r
1973         GUS_IsThere,\r
1974         GUS_Load,\r
1975         GUS_UnLoad,\r
1976         GUS_Init,\r
1977         GUS_Exit,\r
1978         GUS_PlayStart,\r
1979         GUS_PlayStop,\r
1980         GUS_Dummy,\r
1981         GUS_VoiceSetVolume,\r
1982         GUS_VoiceSetFrequency,\r
1983         GUS_VoiceSetPanning,\r
1984         GUS_VoicePlay\r
1985 };\r