7 Mikmod driver for output on Gravis Ultrasound (native mode i.e. using
\r
12 MSDOS: BC(y) Watcom(y) DJGPP(y)
\r
18 (n) - no (not possible or not useful)
\r
19 (?) - may be possible, but not tested
\r
29 /***************************************************************************
\r
30 >>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel GUS defines <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
\r
31 ***************************************************************************/
\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
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
44 #define JOYSTICK_TIMER (GUS_PORT+0x201) /* 201 */
\r
45 #define JOYSTICK_DATA (GUS_PORT+0x201) /* 201 */
\r
47 #define GF1_MIDI_CTRL (GUS_PORT+0x100) /* 3X0 */
\r
48 #define GF1_MIDI_DATA (GUS_PORT+0x101) /* 3X1 */
\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
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
63 /* The GF1 Hardware clock. */
\r
64 #define CLOCK_RATE 9878400L
\r
66 /* Mixer control bits. */
\r
67 #define ENABLE_LINE 0x01
\r
68 #define ENABLE_DAC 0x02
\r
69 #define ENABLE_MIC 0x04
\r
71 /* interrupt controller 1 */
\r
72 #define CNTRL_8259 0x21
\r
73 #define OCR_8259 0x20
\r
75 #define REARM3 0x2F3
\r
76 #define REARM5 0x2F5
\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
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
88 #define TIMER_CONTROL 0x45
\r
92 #define SET_SAMPLE_RATE 0x48
\r
93 #define SAMPLE_CONTROL 0x49
\r
95 #define SET_JOYSTICK 0x4B
\r
96 #define MASTER_RESET 0x4C
\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
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
132 /********************************************************************
\r
136 *******************************************************************/
\r
138 #define MIDI_RESET 0x03
\r
139 #define MIDI_ENABLE_XMIT 0x20
\r
140 #define MIDI_ENABLE_RCV 0x80
\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
148 /********************************************************************
\r
152 *******************************************************************/
\r
154 #define JOY_POSITION 0x0f
\r
155 #define JOY_BUTTONS 0xf0
\r
157 /********************************************************************
\r
159 * GF1 irq/dma programmable latches
\r
161 *******************************************************************/
\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
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
183 /********************************************************************
\r
185 * GF1 global registers ($41-$4C)
\r
187 *******************************************************************/
\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
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
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
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
218 /********************************************************************
\r
220 * GF1 voice specific registers ($00 - $0E and $80-$8f)
\r
222 *******************************************************************/
\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
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
239 /* ($2,$82) Accumulator start address (high) */
\r
240 /* Bits 0-11 - HIGH 12 bits of address */
\r
241 /* Bits 12-15 - Unused */
\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
248 /* ($4,$84) Accumulator end address (high) */
\r
249 /* Bits 0-11 - HIGH 12 bits of address */
\r
250 /* Bits 12-15 - Unused */
\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
258 /* ($6,$86) Volume Envelope control register */
\r
259 #define VL_RATE_MANTISSA 0x3f
\r
260 #define VL_RATE_RANGE 0xC0
\r
262 /* ($7,$87) Volume envelope start */
\r
263 #define VL_START_MANT 0x0F
\r
264 #define VL_START_EXP 0xF0
\r
266 /* ($8,$88) Volume envelope end */
\r
267 #define VL_END_MANT 0x0F
\r
268 #define VL_END_EXP 0xF0
\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
275 /* ($A,$8A) Accumulator value (high) */
\r
276 /* Bits 0-12 - HIGH 12 bits of current position (a19-a7) */
\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
282 /* ($C,$8C) Pan (balance) position */
\r
283 /* Bits 0-3 - Balance position 0=full left, 0x0f=full right */
\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
295 /* ($E,$8E) # of Active voices */
\r
296 /* Bits 0-5 - # of active voices -1 */
\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
305 /***************************************************************************
\r
306 >>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel GUS code <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
\r
307 ***************************************************************************/
\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
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
321 static UBYTE GUS_SELECT; /* currently selected GF1 register */
\r
323 static void (*GUS_TIMER1_FUNC)(void);
\r
324 static void (*GUS_TIMER2_FUNC)(void);
\r
326 #define UltraSelect(x) outportb(GF1_REG_SELECT,GUS_SELECT=x)
\r
328 #define USE_ROLLOVER 0
\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
335 #define MAKE_MS_SWORD( x ) ((unsigned long)((unsigned long)(x)) << 16)
\r
337 static ULONG make_physical_address(UWORD low,UWORD high,UBYTE mode)
\r
339 UWORD lower_16, upper_16;
\r
340 ULONG ret_address, bit_19_20;
\r
342 upper_16 = high >> 9;
\r
343 lower_16 = ((high & 0x01ff) << 7) | ((low >> 9) & 0x007f);
\r
345 ret_address = MAKE_MS_SWORD(upper_16) + lower_16;
\r
347 if (mode & VC_DATA_TYPE)
\r
349 bit_19_20 = ret_address & 0xC0000;
\r
351 ret_address &= 0x3ffff;
\r
352 ret_address |= bit_19_20;
\r
355 return( ret_address );
\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
364 static ULONG convert_to_16bit(ULONG address)
\r
365 /* unsigned long address; /* 20 bit ultrasound dram address */
\r
367 ULONG hold_address;
\r
369 hold_address = address;
\r
371 /* Convert to 16 translated address. */
\r
372 address = address >> 1;
\r
374 /* Zero out bit 17. */
\r
375 address &= 0x0001ffffL;
\r
377 /* Reset bits 18 and 19. */
\r
378 address |= (hold_address & 0x000c0000L);
\r
384 static void GF1OutB(UBYTE x,UBYTE y)
\r
387 outportb(GF1_DATA_HI,y);
\r
391 static void GF1OutW(UBYTE x,UWORD y)
\r
394 outport(GF1_DATA_LOW,y);
\r
398 static UBYTE GF1InB(UBYTE x)
\r
401 return inportb(GF1_DATA_HI);
\r
405 static UWORD GF1InW(UBYTE x)
\r
408 return inport(GF1_DATA_LOW);
\r
412 static void gf1_delay(void)
\r
424 static UBYTE UltraPeek(ULONG address)
\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
432 static void UltraPoke(ULONG address,UBYTE data)
\r
434 GF1OutW(SET_DRAM_LOW,address);
\r
435 GF1OutB(SET_DRAM_HIGH,(address>>16)&0xff);
\r
436 outportb(GF1_DRAM,data);
\r
440 static void UltraPokeFast(ULONG address,UBYTE *src,ULONG size)
\r
442 [address,size> doesn't cross 64k page boundary
\r
447 UltraSelect(SET_DRAM_HIGH);
\r
448 outportb(GF1_DATA_HI,(address>>16)&0xff); /* 8 bits */
\r
449 UltraSelect(SET_DRAM_LOW);
\r
452 outport(GF1_DATA_LOW,address);
\r
453 outportb(GF1_DRAM,*src);
\r
460 static void UltraPokeChunk(ULONG address,UBYTE *src,ULONG size)
\r
464 /* first 'todo' is number of bytes 'till first 64k boundary */
\r
466 todo=0x10000-(address&0xffff);
\r
467 if(todo>size) todo=size;
\r
470 UltraPokeFast(address,src,todo);
\r
475 /* next 'todo' is in chunks of max 64k at once. */
\r
476 todo=(size>0xffff) ? 0x10000 : size;
\r
482 static ULONG UltraPeekLong(ULONG address)
\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
494 static void UltraPokeLong(ULONG address,ULONG data)
\r
496 UltraPokeChunk(address,(UBYTE *)&data,4);
\r
500 static void UltraEnableOutput(void)
\r
502 GUS_MIX_IMAGE &= ~ENABLE_OUTPUT;
\r
503 outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);
\r
506 static void UltraDisableOutput(void)
\r
508 GUS_MIX_IMAGE |= ENABLE_OUTPUT;
\r
509 outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);
\r
512 static void UltraEnableLineIn(void)
\r
514 GUS_MIX_IMAGE &= ~ENABLE_LINE_IN;
\r
515 outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);
\r
518 static void UltraDisableLineIn(void)
\r
520 GUS_MIX_IMAGE |= ENABLE_LINE_IN;
\r
521 outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);
\r
524 static void UltraEnableMicIn(void)
\r
526 GUS_MIX_IMAGE |= ENABLE_MIC_IN;
\r
527 outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);
\r
531 static void UltraDisableMicIn(void)
\r
533 GUS_MIX_IMAGE &= ~ENABLE_MIC_IN;
\r
534 outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE);
\r
538 static void UltraReset(int voices)
\r
542 if(voices<14) voices=14;
\r
543 if(voices>32) voices=32;
\r
549 UltraPokeLong(0,0);
\r
551 GF1OutB(MASTER_RESET,0x00);
\r
552 for(v=0;v<10;v++) gf1_delay();
\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
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
563 /* Clear all interrupts. */
\r
564 GF1OutB(DMA_CONTROL,0x00);
\r
565 GF1OutB(TIMER_CONTROL,0x00);
\r
566 GF1OutB(SAMPLE_CONTROL,0x00);
\r
568 /* Set the number of active voices */
\r
569 GF1OutB(SET_VOICES,((voices-1) | 0xC0));
\r
571 /* Clear interrupts on voices. */
\r
572 /* Reading the status ports will clear the irqs. */
\r
574 inportb(GF1_IRQ_STAT);
\r
576 GF1InB(DMA_CONTROL);
\r
577 GF1InB(SAMPLE_CONTROL);
\r
580 for(v=0;v<voices;v++){
\r
582 /* Select the proper voice */
\r
583 outportb(GF1_PAGE,v);
\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
589 gf1_delay(); /* Wait 4.8 micos. or more. */
\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
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
606 GF1OutW(SET_ACC_HIGH,0);
\r
607 GF1OutW(SET_ACC_LOW,0);
\r
608 GF1OutB(SET_BALANCE,0x07);
\r
611 inportb(GF1_IRQ_STAT);
\r
613 GF1InB(DMA_CONTROL);
\r
614 GF1InB(SAMPLE_CONTROL);
\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
623 static BOOL UltraProbe(void)
\r
627 /* Pull a reset on the GF1 */
\r
629 GF1OutB(MASTER_RESET,0x00);
\r
631 /* Wait a little while ... */
\r
635 /* Release Reset */
\r
636 GF1OutB(MASTER_RESET,GF1_MASTER_RESET);
\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
646 return(t1==0xaa && t2==0x55);
\r
651 static BOOL UltraDetect(void)
\r
655 if((ptr=getenv("ULTRASND"))==NULL) return 0;
\r
657 if(sscanf(ptr,"%hx,%hd,%hd,%hd,%hd",
\r
662 &GUS_MIDI_IRQ)!=5) return 0;
\r
664 return(UltraProbe());
\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
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
680 UBYTE gf1_irq, midi_irq,dram_dma,adc_dma;
\r
681 UBYTE irq_control,dma_control;
\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
689 dram_dma=dmalatch[dram];
\r
690 adc_dma =dmalatch[adc];
\r
693 irq_control=dma_control=0x0;
\r
695 mix_image=GUS_MIX_IMAGE;
\r
697 irq_control|=gf1_irq;
\r
699 if((gf1==midi) && (gf1!=0))
\r
702 irq_control|=midi_irq;
\r
704 dma_control|=dram_dma;
\r
706 if((dram==adc) && (dram!=0))
\r
709 dma_control|=adc_dma;
\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
717 /* First do DMA control register */
\r
718 outportb(GF1_MIX_CTRL,mix_image);
\r
719 outportb(GF1_IRQ_CTRL,dma_control|0x80);
\r
721 /* IRQ CONTROL REG */
\r
722 outportb(GF1_MIX_CTRL,mix_image|0x40);
\r
723 outportb(GF1_IRQ_CTRL,irq_control);
\r
725 /* First do DMA control register */
\r
726 outportb(GF1_MIX_CTRL,mix_image);
\r
727 outportb(GF1_IRQ_CTRL,dma_control);
\r
729 /* IRQ CONTROL REG */
\r
730 outportb(GF1_MIX_CTRL,mix_image|0x40);
\r
731 outportb(GF1_IRQ_CTRL,irq_control);
\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
737 /* enable output & irq, disable line & mic input */
\r
739 outportb(GF1_MIX_CTRL,mix_image);
\r
741 /* just to Lock out writes to irq\dma register ... */
\r
742 outportb(GF1_VOICE_SELECT,0x0);
\r
744 /* put image back .... */
\r
745 GUS_MIX_IMAGE=mix_image;
\r
749 static BOOL UltraPP(ULONG address)
\r
752 s=UltraPeek(address);
\r
753 UltraPoke(address,0xaa);
\r
754 t=UltraPeek(address);
\r
755 UltraPoke(address,s);
\r
760 static UWORD UltraSizeDram(void)
\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
773 static ULONG UltraMemTotal(void)
\r
775 ULONG node=GUS_POOL,nsize,total=0;
\r
778 nsize=UltraPeekLong(node);
\r
780 node=UltraPeekLong(node+4);
\r
787 static BOOL Mergeable(ULONG a,ULONG b)
\r
789 return(a && b && (a+UltraPeekLong(a))==b);
\r
794 static ULONG Merge(ULONG a,ULONG b)
\r
796 UltraPokeLong(a,UltraPeekLong(a)+UltraPeekLong(b));
\r
797 UltraPokeLong(a+4,UltraPeekLong(b+4));
\r
803 static void UltraFree(ULONG size,ULONG location)
\r
805 ULONG pred=0,succ=GUS_POOL;
\r
811 UltraPokeLong(location,size);
\r
813 while(succ!=0 && succ<=location){
\r
815 succ=UltraPeekLong(succ+4);
\r
819 UltraPokeLong(pred+4,location);
\r
823 UltraPokeLong(location+4,succ);
\r
825 if(Mergeable(pred,location)){
\r
826 location=Merge(pred,location);
\r
829 if(Mergeable(location,succ)){
\r
830 Merge(location,succ);
\r
836 void DumpPool(void)
\r
838 ULONG node=GUS_POOL;
\r
841 printf("Node %ld, size %ld, next %ld\n",node,UltraPeekLong(node),UltraPeekLong(node+4));
\r
842 node=UltraPeekLong(node+4);
\r
852 static ULONG UltraMalloc(ULONG reqsize)
\r
854 ULONG curnode=GUS_POOL,cursize,newnode,newsize,pred,succ;
\r
856 if(!reqsize) return 0;
\r
858 /* round size to 32 bytes */
\r
863 /* as long as there are nodes: */
\r
869 succ=UltraPeekLong(curnode+4);
\r
871 /* get current node size */
\r
873 cursize=UltraPeekLong(curnode);
\r
875 /* requested block fits? */
\r
877 if(cursize>=reqsize){
\r
879 /* it fits, so we're allocating the first
\r
880 'size' bytes of this node */
\r
882 /* find new node position and size */
\r
884 newnode=curnode+reqsize;
\r
885 newsize=cursize-reqsize;
\r
887 /* create a new freenode if needed: */
\r
890 UltraPokeLong(newnode,newsize);
\r
891 UltraPokeLong(newnode+4,succ);
\r
895 /* link prednode & succnode */
\r
898 UltraPokeLong(pred+4,succ);
\r
902 /* store size of allocated memory block in block itself: */
\r
904 UltraPokeLong(curnode,reqsize);
\r
908 /* doesn't fit, try next node */
\r
916 static ULONG UltraMalloc16(ULONG reqsize)
\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
922 ULONG p,spage,epage;
\r
924 if(reqsize>262144) return 0;
\r
926 /* round size to 32 bytes */
\r
931 p=UltraMalloc(reqsize);
\r
933 epage=(p+reqsize-1)>>18;
\r
935 if(p && spage!=epage){
\r
938 /* free the second part of the block, and try again */
\r
940 esize=(p+reqsize)-(epage<<18);
\r
941 UltraFree(esize,epage<<18);
\r
943 newp=UltraMalloc16(reqsize);
\r
945 /* free first part of the previous block */
\r
947 UltraFree(reqsize-esize,p);
\r
956 static void UltraMemInit(void)
\r
960 memsize=UltraSizeDram();
\r
961 UltraPokeLong(GUS_POOL,((ULONG)memsize<<10)-32);
\r
962 UltraPokeLong(GUS_POOL+4,0);
\r
966 static void UltraNumVoices(int voices)
\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
976 static void interrupt gf1handler(MIRQARGS)
\r
979 UBYTE oldselect=GUS_SELECT;
\r
981 while(irq_source=inportb(GF1_IRQ_STAT)){
\r
983 /* if(irq_source & DMA_TC_IRQ){
\r
984 no provisions for DMA-ready irq yet
\r
987 if(irq_source & (MIDI_TX_IRQ|MIDI_RX_IRQ)){
\r
988 no provisions for MIDI-ready irq yet
\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
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
1003 /* if (irq_source & (WAVETABLE_IRQ | ENVELOPE_IRQ)){
\r
1004 no wavetable or envelope irq provisions yet
\r
1009 MIrq_EOI(GUS_GF1_IRQ);
\r
1010 UltraSelect(oldselect);
\r
1014 static PVI oldhandler;
\r
1015 typedef void (*PFV)(void);
\r
1018 static PFV UltraTimer1Handler(PFV handler)
\r
1020 PFV old=GUS_TIMER1_FUNC;
\r
1021 GUS_TIMER1_FUNC=handler;
\r
1026 static PFV UltraTimer2Handler(PFV handler)
\r
1028 PFV old=GUS_TIMER1_FUNC;
\r
1029 GUS_TIMER1_FUNC=handler;
\r
1034 static void UltraOpen(int voices)
\r
1036 GUS_MIX_IMAGE=0x0b;
\r
1037 GUS_TIMER1_FUNC=NULL;
\r
1038 GUS_TIMER2_FUNC=NULL;
\r
1040 UltraDisableLineIn();
\r
1041 UltraDisableMicIn();
\r
1042 UltraDisableOutput();
\r
1044 UltraReset(voices);
\r
1045 UltraSetInterface(GUS_DRAM_DMA,GUS_ADC_DMA,GUS_GF1_IRQ,GUS_MIDI_IRQ);
\r
1047 oldhandler=MIrq_SetHandler(GUS_GF1_IRQ,gf1handler);
\r
1048 MIrq_OnOff(GUS_GF1_IRQ,1);
\r
1052 static void UltraClose(void)
\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
1063 static void UltraSelectVoice(UBYTE voice)
\r
1065 /* Make sure was are talking to proper voice */
\r
1066 outportb(GF1_VOICE_SELECT,voice);
\r
1071 static void UltraSetVoiceEnd(ULONG end)
\r
1076 data=GF1InB(GET_CONTROL);
\r
1078 phys_end=(data&VC_DATA_TYPE)?convert_to_16bit(end):end;
\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
1084 data&=~(VC_IRQ_PENDING|VOICE_STOPPED|STOP_VOICE);
\r
1086 GF1OutB(SET_CONTROL,data);
\r
1089 GF1OutB(SET_CONTROL,data);
\r
1093 /* The formula for this table is:
\r
1094 1,000,000 / (1.619695497 * # of active voices)
\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
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
1124 static void UltraSetFrequency(ULONG speed_khz)
\r
1129 /* FC is calculated based on the # of active voices ... */
\r
1130 temp=freq_divisor[GUS_VOICES-14];
\r
1132 fc=(((speed_khz<<9L)+(temp>>1L))/temp);
\r
1133 GF1OutW(SET_FREQUENCY,fc<<1);
\r
1137 static void UltraSetLoopMode(UBYTE mode)
\r
1142 /* set/reset the rollover bit as per user request */
\r
1144 vmode=GF1InB(GET_VOLUME_CONTROL);
\r
1146 if(mode&USE_ROLLOVER)
\r
1147 vmode|=VC_ROLLOVER;
\r
1149 vmode&=~VC_ROLLOVER;
\r
1151 GF1OutB(SET_VOLUME_CONTROL,vmode);
\r
1153 GF1OutB(SET_VOLUME_CONTROL,vmode);
\r
1155 data=GF1InB(GET_CONTROL);
\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
1161 GF1OutB(SET_CONTROL,data);
\r
1163 GF1OutB(SET_CONTROL,data);
\r
1167 static ULONG UltraReadVoice(void)
\r
1169 UWORD count_low,count_high;
\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
1177 /* convert from UltraSound's format to a physical address */
\r
1179 mode=GF1InB(GET_CONTROL);
\r
1181 acc=make_physical_address(count_low,count_high,mode);
\r
1182 acc&=0xfffffL; /* Only 20 bits please ... */
\r
1189 static void UltraSetVoice(ULONG location)
\r
1194 data=GF1InB(GET_CONTROL);
\r
1196 phys_loc=(data&VC_DATA_TYPE)?convert_to_16bit(location):location;
\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
1205 static UBYTE UltraPrimeVoice(ULONG begin,ULONG start,ULONG end,UBYTE mode)
\r
1207 ULONG phys_start,phys_end;
\r
1212 /* if start is greater than end, flip 'em and turn on */
\r
1213 /* decrementing addresses */
\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
1228 phys_begin = begin;
\r
1229 phys_start = start;
\r
1233 /* set/reset the rollover bit as per user request */
\r
1234 vmode=GF1InB(GET_VOLUME_CONTROL);
\r
1236 if(mode&USE_ROLLOVER)
\r
1237 vmode |= VC_ROLLOVER;
\r
1239 vmode &= ~VC_ROLLOVER;
\r
1241 GF1OutB(SET_VOLUME_CONTROL,vmode);
\r
1243 GF1OutB(SET_VOLUME_CONTROL,vmode);
\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
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
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
1260 static void UltraGoVoice(UBYTE mode)
\r
1262 mode&=~(VOICE_STOPPED|STOP_VOICE); /* turn 'stop' bits off ... */
\r
1264 /* NOTE: no irq's from the voice ... */
\r
1266 GF1OutB(SET_CONTROL,mode);
\r
1268 GF1OutB(SET_CONTROL,mode);
\r
1272 /**********************************************************************
\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
1277 *********************************************************************/
\r
1279 static void UltraStartVoice(ULONG begin,ULONG start,ULONG end,UBYTE mode)
\r
1281 mode=UltraPrimeVoice(begin,start,end,mode);
\r
1282 UltraGoVoice(mode);
\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
1293 static void UltraStopVoice(void)
\r
1297 /* turn off the roll over bit first ... */
\r
1299 data=GF1InB(GET_VOLUME_CONTROL);
\r
1300 data&=~VC_ROLLOVER;
\r
1302 GF1OutB(SET_VOLUME_CONTROL,data);
\r
1304 GF1OutB(SET_VOLUME_CONTROL,data);
\r
1306 /* Now stop the voice */
\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
1312 GF1OutB(SET_CONTROL,data); /* turn it off */
\r
1314 GF1OutB(SET_CONTROL,data);
\r
1318 static int UltraVoiceStopped(void)
\r
1320 return(GF1InB(GET_CONTROL) & (VOICE_STOPPED|STOP_VOICE));
\r
1324 static void UltraSetBalance(UBYTE pan)
\r
1326 GF1OutB(SET_BALANCE,pan&0xf);
\r
1330 static void UltraSetVolume(UWORD volume)
\r
1332 GF1OutW(SET_VOLUME,volume<<4);
\r
1336 static UWORD UltraReadVolume(void)
\r
1338 return(GF1InW(GET_VOLUME)>>4);
\r
1342 static void UltraStopVolume(void)
\r
1346 vmode=GF1InB(GET_VOLUME_CONTROL);
\r
1347 vmode|=(VOLUME_STOPPED|STOP_VOLUME);
\r
1349 GF1OutB(SET_VOLUME_CONTROL,vmode);
\r
1351 GF1OutB(SET_VOLUME_CONTROL,vmode);
\r
1355 static void UltraRampVolume(UWORD start,UWORD end,UBYTE rate,UBYTE mode)
\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
1366 *********************************************************************/
\r
1367 /* Don't let bad bits thru ... */
\r
1368 mode&=~(VL_IRQ_PENDING|VC_ROLLOVER|STOP_VOLUME|VOLUME_STOPPED);
\r
1373 /* flip start & end if decreasing numbers ... */
\r
1376 mode |= VC_DIRECT; /* decreasing volumes */
\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
1383 GF1OutB(SET_VOLUME_RATE,rate);
\r
1384 GF1OutB(SET_VOLUME_START,start>>4);
\r
1385 GF1OutB(SET_VOLUME_END,end>>4);
\r
1387 /* Also MUST set the current volume to the start volume ... */
\r
1388 UltraSetVolume(begin);
\r
1390 vmode=GF1InB(GET_VOLUME_CONTROL);
\r
1392 if(vmode&VC_ROLLOVER) mode|=VC_ROLLOVER;
\r
1394 /* start 'er up !!! */
\r
1395 GF1OutB(SET_VOLUME_CONTROL,mode);
\r
1397 GF1OutB(SET_VOLUME_CONTROL,mode);
\r
1401 static void UltraVectorVolume(UWORD end,UBYTE rate,UBYTE mode)
\r
1403 UltraStopVolume();
\r
1404 UltraRampVolume(UltraReadVolume(),end,rate,mode);
\r
1408 static int UltraVolumeStopped(void)
\r
1410 return(GF1InB(GET_VOLUME_CONTROL) & (VOLUME_STOPPED|STOP_VOLUME));
\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
1419 static UBYTE UltraCalcRate(UWORD start,UWORD end,ULONG mil_secs)
\r
1421 ULONG gap,mic_secs;
\r
1422 UWORD i,range,increment;
\r
1426 gap = (start>end) ? (start-end) : (end-start);
\r
1427 mic_secs = (mil_secs * 1000L)/gap;
\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
1433 value = vol_rates[GUS_VOICES-14];
\r
1436 if(mic_secs<value){
\r
1448 /* calculate increment value ... (round it up ?) */
\r
1449 increment=(unsigned int)((value + (value>>1)) / mic_secs);
\r
1452 rate_val=range<<6;
\r
1453 rate_val|=(increment&0x3F);
\r
1458 static UWORD _gf1_volumes[512] = {
\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
1520 static void UltraSetLinearVolume(UWORD index)
\r
1522 UltraSetVolume(_gf1_volumes[index]);
\r
1526 static void UltraRampLinearVolume(UWORD start_idx,UWORD end_idx,ULONG msecs,UBYTE mode)
\r
1531 /* Ramp from start to end in x milliseconds */
\r
1533 start = _gf1_volumes[start_idx];
\r
1534 end = _gf1_volumes[end_idx];
\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
1542 static void UltraVectorLinearVolume(UWORD end_idx,UBYTE rate,UBYTE mode)
\r
1544 UltraStopVolume();
\r
1545 UltraRampVolume(UltraReadVolume(),_gf1_volumes[end_idx],rate,mode);
\r
1549 static void UltraStartTimer(UBYTE timer,UBYTE time)
\r
1554 GUS_TIMER_CTRL |= 0x04;
\r
1555 GUS_TIMER_MASK |= 0x01;
\r
1559 GUS_TIMER_CTRL |= 0x08;
\r
1560 GUS_TIMER_MASK |= 0x02;
\r
1564 /* ENTER_CRITICAL; */
\r
1566 time = 256 - time;
\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
1573 /* LEAVE_CRITICAL; */
\r
1577 static void UltraStopTimer(int timer)
\r
1580 GUS_TIMER_CTRL &= ~0x04;
\r
1581 GUS_TIMER_MASK &= ~0x01;
\r
1584 GUS_TIMER_CTRL &= ~0x08;
\r
1585 GUS_TIMER_MASK &= ~0x02;
\r
1588 /* ENTER_CRITICAL; */
\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
1594 /* LEAVE_CRITICAL; */
\r
1598 static BOOL UltraTimerStopped(UBYTE timer)
\r
1608 value = (inportb(GF1_TIMER_CTRL)) & temp;
\r
1614 /***************************************************************************
\r
1615 >>>>>>>>>>>>>>>>>>>>>>>>> The actual GUS driver <<<<<<<<<<<<<<<<<<<<<<<<<<<<
\r
1616 ***************************************************************************/
\r
1619 static ULONG Ultra[MAXSAMPLEHANDLES];
\r
1620 static ULONG Ultrs[MAXSAMPLEHANDLES];
\r
1622 /* Ultra[] holds the sample dram adresses
\r
1623 of the samples of a module */
\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
1639 static GHOLD ghld[32];
\r
1642 static UBYTE timeskip;
\r
1643 static UBYTE timecount;
\r
1644 static UBYTE GUS_BPM;
\r
1647 void UltraSetBPM(UBYTE bpm)
\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
1652 The Timer1 handler has a resolution of 80 microseconds.
\r
1654 So the timer value to program:
\r
1656 (125/(bpm*50)) / 80e-6 = 31250/bpm
\r
1658 UWORD rate=31250/bpm;
\r
1667 UltraStartTimer(1,rate);
\r
1672 void GUS_Update(void)
\r
1677 ULONG base,start,size,reppos,repend;
\r
1679 UWORD curvol,bigvol=0,bigvoc=0;
\r
1681 if(timecount<timeskip){
\r
1689 if(GUS_BPM != md_bpm){
\r
1690 UltraSetBPM(md_bpm);
\r
1694 /* ramp down voices that need to be started next */
\r
1696 for(t=0;t<md_numchn;t++){
\r
1697 UltraSelectVoice(t);
\r
1700 curvol=UltraReadVolume();
\r
1701 if(bigvol<=curvol){
\r
1705 UltraVectorLinearVolume(0,0x3f,0);
\r
1709 /* while(!UltraVolumeStopped(bigvoc)); */
\r
1711 for(t=0;t<md_numchn;t++){
\r
1712 UltraSelectVoice(t);
\r
1718 base=Ultra[aud->handle];
\r
1721 reppos=aud->reppos;
\r
1722 repend=aud->repend;
\r
1725 if(aud->flags&SF_16BITS){
\r
1732 /* Stop current sample and start a new one */
\r
1736 UltraSetFrequency(aud->frq);
\r
1737 UltraVectorLinearVolume(6U*aud->vol,0x3f,0);
\r
1738 UltraSetBalance(aud->pan>>4);
\r
1740 if(aud->flags&SF_LOOP){
\r
1742 /* Start a looping sample */
\r
1744 UltraStartVoice(base+start,
\r
1746 base+repend,0x8|((aud->flags&SF_16BITS)?4:0)|
\r
1747 ((aud->flags&SF_BIDI)?16:0));
\r
1751 /* Start a one-shot sample */
\r
1753 UltraStartVoice(base+start,
\r
1755 base+size+2,(aud->flags&SF_16BITS)?4:0);
\r
1759 UltraSetFrequency(aud->frq);
\r
1760 UltraVectorLinearVolume(6U*aud->vol,0x3f,0);
\r
1761 UltraSetBalance(aud->pan>>4);
\r
1767 SWORD GUS_Load(FILE *fp,ULONG length,ULONG loopstart,ULONG loopend,UWORD flags)
\r
1769 callback routine for the MODLOAD module.
\r
1775 SL_Init(fp,flags,flags|SF_SIGNED);
\r
1777 /* Find empty slot to put sample address in */
\r
1779 for(handle=0;handle<MAXSAMPLEHANDLES;handle++){
\r
1780 if(Ultra[handle]==0) break;
\r
1783 if(handle==MAXSAMPLEHANDLES){
\r
1784 myerr=ERROR_OUT_OF_HANDLES;
\r
1788 if(flags&SF_16BITS){
\r
1794 /* Allocate GUS dram and store the address in Ultra[handle] */
\r
1795 /* Alloc 8 bytes more for anticlick measures. see below. */
\r
1797 /* 2.04: use UltraMalloc16 to allocate 16 bit samples */
\r
1799 if(!(Ultra[handle]=(flags&SF_16BITS) ? UltraMalloc16(length+8) : UltraMalloc(length+8) )){
\r
1800 myerr=ERROR_SAMPLE_TOO_BIG;
\r
1804 /* Load the sample */
\r
1806 Ultrs[handle]=length+8;
\r
1811 static UBYTE buffer[1024];
\r
1814 todo=(l>1024) ? 1024 : l;
\r
1816 SL_Load(buffer,todo);
\r
1818 UltraPokeChunk(p,buffer,todo);
\r
1824 if(flags&SF_LOOP && !(flags&SF_BIDI)){ /* looping sample ? */
\r
1826 /* Anticlick for looping samples:
\r
1827 Copy the first bytes in the loop
\r
1828 beyond the end of the loop */
\r
1831 UltraPoke(Ultra[handle]+loopend+t,
\r
1832 UltraPeek(Ultra[handle]+loopstart+t));
\r
1837 /* Anticlick for one-shot samples:
\r
1838 Zero the bytes beyond the end of the sample.
\r
1842 UltraPoke(Ultra[handle]+length+t,0);
\r
1851 void GUS_UnLoad(SWORD handle)
\r
1853 callback routine to unload samples
\r
1855 smp :sampleinfo of sample that is being freed
\r
1858 UltraFree(Ultrs[handle],Ultra[handle]);
\r
1865 BOOL GUS_Init(void)
\r
1870 if(!(md_mode&DMODE_16BITS)){
\r
1871 md_mode|=DMODE_16BITS; /* gus can't do 8 bit mixing */
\r
1874 if(!(md_mode&DMODE_STEREO)){
\r
1875 md_mode|=DMODE_STEREO; /* gus can't do mono mixing */
\r
1878 if(!UltraDetect()){
\r
1879 myerr="Couldn't detect gus, please check env. string";
\r
1884 UltraTimer1Handler(GUS_Update);
\r
1891 void GUS_Exit(void)
\r
1898 void GUS_PlayStart(void)
\r
1901 for(t=0;t<md_numchn;t++){
\r
1906 ghld[t].frq=10000;
\r
1908 ghld[t].pan=(t&1)?0:255;
\r
1910 UltraNumVoices(md_numchn);
\r
1911 UltraEnableOutput();
\r
1918 void GUS_PlayStop(void)
\r
1920 UltraStopTimer(1);
\r
1921 UltraDisableOutput();
\r
1925 BOOL GUS_IsThere(void)
\r
1927 return(getenv("ULTRASND")!=NULL);
\r
1931 void GUS_VoiceSetVolume(UBYTE voice,UBYTE vol)
\r
1933 ghld[voice].vol=vol;
\r
1937 void GUS_VoiceSetFrequency(UBYTE voice,ULONG frq)
\r
1939 ghld[voice].frq=frq;
\r
1942 void GUS_VoiceSetPanning(UBYTE voice,UBYTE pan)
\r
1944 ghld[voice].pan=pan;
\r
1947 void GUS_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags)
\r
1949 if(start>=size) return;
\r
1951 if(flags&SF_LOOP){
\r
1952 if(repend>size) repend=size; /* repend can't be bigger than size */
\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
1964 void GUS_Dummy(void)
\r
1971 "Gravis Ultrasound",
\r
1972 "MikMod GUS Driver v2.1 (uses gus timer interrupt)",
\r
1981 GUS_VoiceSetVolume,
\r
1982 GUS_VoiceSetFrequency,
\r
1983 GUS_VoiceSetPanning,
\r