1 /* MikMod sound library
2 (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 /*==============================================================================
25 libGUS-alike definitions for DOS
27 ==============================================================================*/
37 /* Private header file for a libGUS-alike library for DOS */
39 #define JOYSTICK_TIMER (gus.port+0x201) /* 201 */
40 #define JOYSTICK_DATA (gus.port+0x201) /* 201 */
42 #define GF1_MIDI_CTRL (gus.port+0x100) /* 3X0 */
43 #define GF1_MIDI_DATA (gus.port+0x101) /* 3X1 */
45 #define GF1_VOICESEL (gus.port+0x102) /* 3X2 */
46 #define GF1_REGSEL (gus.port+0x103) /* 3X3 */
47 #define GF1_DATA (gus.port+0x104) /* 3X4 */
48 #define GF1_DATA_LOW (gus.port+0x104) /* 3X4 */
49 #define GF1_DATA_HIGH (gus.port+0x105) /* 3X5 */
50 #define GF1_IRQ_STATUS (gus.port+0x006) /* 2X6 */
51 #define GF1_DRAM (gus.port+0x107) /* 3X7 */
53 #define GF1_MIX_CTRL (gus.port+0x000) /* 2X0 */
54 #define GF1_TIMER_CTRL (gus.port+0x008) /* 2X8 */
55 #define GF1_TIMER_DATA (gus.port+0x009) /* 2X9 */
56 #define GF1_IRQ_CTRL (gus.port+0x00B) /* 2XB */
57 #define GF1_REG_CTRL (gus.port+0x00F) /* 2XF */
59 #define GF1_REVISION (gus.port+0x506) /* 7X6 */
61 /* The GF1 hardware clock rate */
62 #define CLOCK_RATE 9878400L
64 /* GF1 voice-independent registers */
65 #define GF1R_DMA_CONTROL 0x41
66 #define GF1R_DMA_ADDRESS 0x42
67 #define GF1R_DRAM_LOW 0x43
68 #define GF1R_DRAM_HIGH 0x44
70 #define GF1R_TIMER_CONTROL 0x45
71 #define GF1R_TIMER1 0x46
72 #define GF1R_TIMER2 0x47
74 #define GF1R_SAMPLE_RATE 0x48
75 #define GF1R_SAMPLE_CONTROL 0x49
77 #define GF1R_JOYSTICK 0x4B
78 #define GF1R_RESET 0x4C
80 /* GF1 voice-specific registers */
81 #define GF1R_VOICE_CONTROL 0x00
82 #define GF1R_FREQUENCY 0x01
83 #define GF1R_START_HIGH 0x02
84 #define GF1R_START_LOW 0x03
85 #define GF1R_END_HIGH 0x04
86 #define GF1R_END_LOW 0x05
87 #define GF1R_VOLUME_RATE 0x06
88 #define GF1R_VOLUME_START 0x07
89 #define GF1R_VOLUME_END 0x08
90 #define GF1R_VOLUME 0x09
91 #define GF1R_ACC_HIGH 0x0a
92 #define GF1R_ACC_LOW 0x0b
93 #define GF1R_BALANCE 0x0c
94 #define GF1R_VOLUME_CONTROL 0x0d
95 #define GF1R_VOICES 0x0e
96 #define GF1R_IRQ_SOURCE 0x0f
98 /* Add this to above registers for reading */
99 #define GF1R_READ_MASK 0x80
102 #define GF1M_MIDI_RESET 0x03
103 #define GF1M_MIDI_ENABLE_XMIT 0x20
104 #define GF1M_MIDI_ENABLE_RCV 0x80
106 #define GF1M_MIDI_RCV_FULL 0x01
107 #define GF1M_MIDI_XMIT_EMPTY 0x02
108 #define GF1M_MIDI_FRAME_ERR 0x10
109 #define GF1M_MIDI_OVERRUN 0x20
110 #define GF1M_MIDI_IRQ_PEND 0x80
113 #define GF1M_JOY_POSITION 0x0f
114 #define GF1M_JOY_BUTTONS 0xf0
116 /* GF1_IRQ_STATUS (port 2X6) */
117 #define GF1M_IRQ_MIDI_TX 0x01 /* pending MIDI xmit IRQ */
118 #define GF1M_IRQ_MIDI_RX 0x02 /* pending MIDI recv IRQ */
119 #define GF1M_IRQ_TIMER1 0x04 /* general purpose timer */
120 #define GF1M_IRQ_TIMER2 0x08 /* general purpose timer */
121 #define GF1M_IRQ_WAVETABLE 0x20 /* pending wavetable IRQ */
122 #define GF1M_IRQ_ENVELOPE 0x40 /* pending volume envelope IRQ */
123 #define GF1M_IRQ_DMA_COMPLETE 0x80 /* pending dma transfer complete IRQ */
125 /* GF1_MIX_CTRL (port 2X0) */
126 #define GF1M_MIXER_NO_LINE_IN 0x01 /* 0: enable */
127 #define GF1M_MIXER_NO_OUTPUT 0x02 /* 0: enable */
128 #define GF1M_MIXER_MIC_IN 0x04 /* 1: enable */
129 #define GF1M_MIXER_GF1_IRQ 0x08 /* 1: enable */
130 #define GF1M_GF1_COMBINED_IRQ 0x10 /* 1: IRQ1 == IRQ2 */
131 #define GF1M_MIDI_LOOPBACK 0x20 /* 1: enable loop back */
132 #define GF1M_CONTROL_SELECT 0x40 /* 0: DMA latches; 1: IRQ latches */
134 /* Timer data register (2X9) */
135 #define GF1M_START_TIMER1 0x01
136 #define GF1M_START_TIMER2 0x02
137 #define GF1M_MASK_TIMER1 0x20
138 #define GF1M_MASK_TIMER2 0x40
139 #define GF1M_TIMER_CLRIRQ 0x80
141 /* IRQ/DMA control register (2XB) */
142 #define GF1M_IRQ_EQUAL 0x40
143 #define GF1M_DMA_EQUAL 0x40
145 /* (0x41) DMA control register bits */
146 #define GF1M_DMAR_ENABLE 0x01 /* 1: go */
147 #define GF1M_DMAR_READ 0x02 /* 1: read (->RAM), 0: write (->DRAM) */
148 #define GF1M_DMAR_CHAN16 0x04 /* 1: 16 bit, 0: 8 bit DMA channel */
149 #define GF1M_DMAR_RATE 0x18 /* 00: fast, 11: slow */
150 #define GF1M_DMAR_IRQ_ENABLE 0x20 /* 1: enable */
151 #define GF1M_DMAR_IRQ_PENDING 0x40 /* R: DMA irq pending */
152 #define GF1M_DMAR_DATA16 0x40 /* W: 0: 8 bits; 1: 16 bits per sample */
153 #define GF1M_DMAR_TOGGLE_SIGN 0x80 /* W: 1: invert high bit */
155 /* DMA transfer rate divisors */
156 #define GF1M_DMAR_RATE0 0x00 /* Fastest DMA xfer (~650khz) */
157 #define GF1M_DMAR_RATE1 0x08 /* fastest / 2 */
158 #define GF1M_DMAR_RATE2 0x10 /* fastest / 4 */
159 #define GF1M_DMAR_RATE3 0x18 /* Slowest DMA xfer (fastest / 8) */
161 /* (0x45) Timer Control */
162 #define GF1M_TIMER1 0x04 /* Enable timer 1 IRQ */
163 #define GF1M_TIMER2 0x08 /* Enable timer 2 IRQ */
165 /* (0x49) Sampling (ADC) control register */
166 #define GF1M_DMAW_ENABLE 0x01 /* 1: Start sampling */
167 #define GF1M_DMAW_MODE 0x02 /* 0: mono, 1: stereo */
168 #define GF1M_DMAW_CHAN16 0x04 /* 0: 8 bit, 1: 16 bit */
169 #define GF1M_DMAW_IRQ_ENABLE 0x20 /* 1: enable IRQ */
170 #define GF1M_DMAW_IRQ_PENDING 0x40 /* 1: irq pending */
171 #define GF1M_DMAW_TOGGLE_SIGN 0x80 /* 1: invert sign bit */
173 /* (0x4C) GF1 reset register */
174 #define GF1M_MASTER_RESET 0x01 /* 0: hold in reset */
175 #define GF1M_OUTPUT_ENABLE 0x02 /* 1: enable output */
176 #define GF1M_MASTER_IRQ 0x04 /* 1: master IRQ enable */
178 /* (0x0,0x80) Voice control register - GF1R_VOICE_CONTROL */
179 #define GF1VC_STOPPED 0x01 /* 1: voice has stopped */
180 #define GF1VC_STOP 0x02 /* 1: stop voice */
181 #define GF1VC_DATA16 0x04 /* 0: 8 bit, 1: 16 bit */
182 #define GF1VC_LOOP_ENABLE 0x08 /* 1: enable */
183 #define GF1VC_BI_LOOP 0x10 /* 1: bi directional looping */
184 #define GF1VC_IRQ 0x20 /* 1: enable voice's wave irq */
185 #define GF1VC_BACKWARD 0x40 /* 0: increasing, 1: decreasing */
186 #define GF1VC_IRQ_PENDING 0x80 /* 1: wavetable irq pending */
188 /* (0x01,0x81) Frequency control */
190 /* Bits 1-9 - Fractional portion */
191 /* Bits 10-15 - Integer portion */
193 /* (0x02,0x82) Accumulator start address - GF1R_START_HIGH */
194 /* Bits 0-11 - HIGH 12 bits of address */
195 /* Bits 12-15 - Unused */
197 /* (0x03,0x83) Accumulator start address - GF1R_START_LOW */
198 /* Bits 0-4 - Unused */
199 /* Bits 5-8 - Fractional portion */
200 /* Bits 9-15 - Low 7 bits of integer portion */
202 /* (0x04,0x84) Accumulator end address - GF1R_END_HIGH */
203 /* Bits 0-11 - HIGH 12 bits of address */
204 /* Bits 12-15 - Unused */
206 /* (0x05,0x85) Accumulator end address - GF1R_END_LOW */
207 /* Bits 0-4 - Unused */
208 /* Bits 5-8 - Fractional portion */
209 /* Bits 9-15 - Low 7 bits of integer portion */
211 /* (0x06,0x86) Volume Envelope control register - GF1R_VOLUME_RATE */
212 #define GF1VL_RATE_MANTISSA 0x3f
213 #define GF1VL_RATE_RANGE 0xC0
215 /* (0x07,0x87) Volume envelope start - GF1R_VOLUME_START */
216 #define GF1VL_START_MANT 0x0F
217 #define GF1VL_START_EXP 0xF0
219 /* (0x08,0x88) Volume envelope end - GF1R_VOLUME_END */
220 #define GF1VL_END_MANT 0x0F
221 #define GF1VL_END_EXP 0xF0
223 /* (0x09,0x89) Current volume register - GF1R_VOLUME */
224 /* Bits 0-3 - Unused */
225 /* Bits 4-11 - Mantissa of current volume */
226 /* Bits 10-15 - Exponent of current volume */
228 /* (0x0A,0x8A) Accumulator value (high) */
229 /* Bits 0-12 - HIGH 12 bits of current position (a19-a7) */
231 /* (0x0B,0x8B) Accumulator value (low) */
232 /* Bits 0-8 - Fractional portion */
233 /* Bits 9-15 - Integer portion of low adress (a6-a0) */
235 /* (0x0C,0x8C) Pan (balance) position */
236 /* Bits 0-3 - Balance position 0=full left, 0x0f=full right */
238 /* (0x0D,0x8D) Volume control register - GF1R_VOLUME_CONTROL */
239 #define GF1VL_STOPPED 0x01 /* volume has stopped */
240 #define GF1VL_STOP 0x02 /* stop volume */
241 #define GF1VL_ROLLOVER 0x04 /* Roll PAST end & gen IRQ */
242 #define GF1VL_LOOP_ENABLE 0x08 /* 1: enable */
243 #define GF1VL_BI_LOOP 0x10 /* 1: bi directional looping */
244 #define GF1VL_IRQ 0x20 /* 1: enable voice's volume irq */
245 #define GF1VL_BACKWARD 0x40 /* 0: increasing, 1: decreasing */
246 #define GF1VL_IRQ_PENDING 0x80 /* 1: wavetable irq pending */
248 /* (0x0E,0x8E) Number of active voices */
249 /* Bits 0-5 - Number of active voices - 1 */
251 /* (0x0F,0x8F) Sources of IRQs */
252 /* Bits 0-4 - interrupting voice number */
253 /* Bit 5 - Always a 1 */
254 #define GF1IRQ_VOLUME 0x40 /* individual voice irq bit */
255 #define GF1IRQ_WAVE 0x80 /* individual waveform irq bit */
257 /* Commands are pooled and executed ON TIMER (1st timer) interrupt.
258 * Currently there is a limit on the number of commands that you can
259 * issue between gus_do_flush (...); this should not be an issue however
260 * because each voice has a limited (little) set of parameters that
261 * you can change (freq, vol, pan... what else?)
263 * The pool is a pseudo-CPU code that gets executed once per timer interrupt.
266 /* Below are definitions for commands placed in GUS command pool */
267 #define PCMD_NOP 0x00 /* Traditionally ... */
268 #define PCMD_VOICE 0x01 /* +B: select voice */
269 #define PCMD_START 0x02 /* +L: start voice */
270 #define PCMD_STOP 0x03 /* stop voice */
271 #define PCMD_FREQ 0x04 /* +W: set frequence */
272 #define PCMD_VOLUME 0x05 /* +W: set volume */
273 #define PCMD_VOLUME_PREPARE 0x06 /* +W: prepare to set volume on (soon to follow) kick */
274 #define PCMD_PAN 0x07 /* +B: set panning */
275 #define PCMD_OFFSET 0x08 /* +L: set DRAM offset */
276 #define PCMD_STOP_LOOP 0x09 /* stop looping */
278 #define GUS_VOLCHANGE_RAMP 0x20 /* Volume change ramp speed */
280 /* Definition for the boolean type */
281 typedef unsigned char boolean;
282 /* Prototype for functions that do block transfers to GUS DRAM:
283 flags can contain any of the following bits:
284 GUS_WAVE_16BIT - sample is 16-bit
285 GUS_WAVE_UNSIGNED - do not invert sign bit while downloading
287 typedef void (*__gus_transfer_func) (unsigned long address,
288 unsigned char *source,
289 unsigned long size, int flags);
290 typedef void (*__gus_callback) ();
291 typedef void (*__gus_callback_3) (unsigned int, unsigned int, unsigned int);
293 /* Structure used to keep track of all on-board GUS memory */
294 typedef struct __struct_gus_mcb {
295 struct __struct_gus_mcb *next; /* Next MCB in chain */
296 struct __struct_gus_mcb *prev; /* Previous MCB in chain */
297 unsigned int addr; /* GUS DRAM address */
298 unsigned int size; /* Memory block size */
299 int free; /* 1: block is free */
302 /* Structure defining overall GUS state/information */
303 typedef struct __gus_state_s {
304 unsigned int port; /* Base I/O port (0x220, 0x240, ...) */
305 unsigned int irq[2]; /* GF1 IRQ and MIDI IRQ */
306 unsigned int dma[2]; /* Play / record DMA */
307 unsigned int ram; /* Memory size (K), i.e. 256, 1024 etc */
308 unsigned int version; /* GUS version (see GUS_CARD_VERSION_XXX in libgus.h */
309 unsigned int freq; /* Current mixing frequency */
310 unsigned int voices; /* Active voices (14-32) */
311 unsigned int dynmask; /* Dynamically allocated voices mask */
312 unsigned int timer_base; /* The relative timer speed in percents (def: 100) */
313 volatile unsigned int t1_ticks; /* Incremented per each timer1 tick */
314 volatile unsigned int t2_ticks; /* Incremented per each timer2 tick */
315 volatile unsigned int t1_countdown; /* t1_callback is called when this reaches zero */
316 volatile unsigned int t2_countdown; /* t2_callback is called when this reaches zero */
317 unsigned int t1_multiple; /* Timer1 handler is called once per such many ticks */
318 unsigned int t2_multiple; /* Timer2 handler is called once per such many ticks */
319 struct irq_handle *gf1_irq; /* The interrupt handler for GF1 events */
320 dma_buffer *dma_buff; /* Pre-allocated DMA buffer */
321 __gus_callback dma_callback; /* Routine called at end of DMA transfers */
322 __gus_callback t1_callback; /* Routine called on Timer1 events */
323 __gus_callback t2_callback; /* Routine called on Timer1 events */
324 __gus_callback timer_callback; /* Called once per TEMPO ticks */
325 __gus_callback_3 wt_callback; /* Routine called on WaveTable events */
326 __gus_callback_3 vl_callback; /* Routine called on Volume ramp events */
327 __gus_mcb *mcb; /* Chained list of memory control blocks */
328 __gus_transfer_func transfer; /* Best working function for DRAM transfer */
329 gus_instrument_t *instr; /* The list of registered instruments */
330 unsigned short mixer; /* Current mixer register state */
331 unsigned char dma_rate; /* One of GF1M_DMAR_RATEX constants defined above */
332 unsigned char timer_ctl; /* Timer control register value (2x8/2x9) */
333 unsigned char timer_ctl_reg; /* Timer control register value (GF1/0x45) */
334 boolean ok; /* Is the information below okay? */
335 boolean open; /* 1 if between gus_open() and gus_close() */
336 boolean ics; /* Is it equipped with an ICS mixer? */
337 boolean ics_flipped; /* rev 5 (3.7) has flipped R/L mixer */
338 boolean codec; /* Is it equipped with a GUS MAX codec? */
339 boolean interwave; /* GUS InterWave card */
340 volatile boolean dma_active; /* DMA is transferring data */
341 volatile boolean cmd_pool_ready; /* Flush cmd_pool during timer interrupt */
342 unsigned char cmd_voice; /* Pool selection index cache */
343 unsigned int cmd_pool_top; /* Command pool top */
344 unsigned char *cmd_pool; /* Async commands pool */
345 /* The following data is for private use only by interrupt routines! */
346 gus_wave_t *cur_wave[32]; /* Currently played waves */
347 boolean voice_kick[32]; /* Kick wave on next volume ramp IRQ */
348 unsigned int kick_offs[32]; /* Sample start position on kick */
349 unsigned short cur_vol[32]; /* Current voice volumes */
350 unsigned int cur_voice; /* Current voice */
351 unsigned int eow_ignore; /* Temp ignore end-of-wave IRQ for these voices */
354 extern __gus_state gus;
355 extern void __gus_delay();
357 static unsigned long __gus_convert_addr16(unsigned long address)
359 return ((address & 0x0003ffff) >> 1) | (address & ~0x0003ffff);
362 /* The XXX_slow routines cannot be used outside IRQ handler! */
363 static inline void __gus_outregb_slow(unsigned char reg, unsigned char value)
365 outportb(GF1_REGSEL, reg);
366 outportb(GF1_DATA_HIGH, value);
368 outportb(GF1_DATA_HIGH, value);
371 static inline void __gus_outregw_slow(unsigned char reg, unsigned short value)
373 outportb(GF1_REGSEL, reg);
374 outportw(GF1_DATA, value);
376 outportw(GF1_DATA, value);
379 static inline void __gus_outregb(unsigned char reg, unsigned char value)
381 outportb(GF1_REGSEL, reg);
382 outportb(GF1_DATA_HIGH, value);
385 static inline void __gus_outregw(unsigned char reg, unsigned short value)
387 outportb(GF1_REGSEL, reg);
388 outportw(GF1_DATA, value);
391 static inline unsigned char __gus_inregb(unsigned char reg)
394 reg |= GF1R_READ_MASK;
395 outportb(GF1_REGSEL, reg);
396 return inportb(GF1_DATA_HIGH);
399 static inline unsigned short __gus_inregw(unsigned char reg)
402 reg |= GF1R_READ_MASK;
403 outportb(GF1_REGSEL, reg);
404 return inportw(GF1_DATA);
407 static inline void __gus_set_dram_address(unsigned int address)
409 __gus_outregb(GF1R_DRAM_HIGH, address >> 16);
410 __gus_outregw(GF1R_DRAM_LOW, address);
413 static inline unsigned char __gus_peek(unsigned int address)
415 __gus_set_dram_address(address);
416 return inportb(GF1_DRAM);
419 static inline void __gus_poke(unsigned int address, unsigned char value)
421 __gus_set_dram_address(address);
422 outportb(GF1_DRAM, value);
425 static inline void __gus_select_voice(unsigned char voice)
427 outportb(GF1_VOICESEL, voice);
430 static inline void __gus_set_current(unsigned char mode,
431 unsigned long address)
433 if (mode & GF1VC_DATA16)
434 address = __gus_convert_addr16(address);
435 __gus_outregw_slow(GF1R_ACC_HIGH, address >> 11);
436 __gus_outregw_slow(GF1R_ACC_LOW, address << 5);
439 static inline void __gus_set_loop_start(unsigned char mode,
440 unsigned long address)
442 if (mode & GF1VC_DATA16)
443 address = __gus_convert_addr16(address);
444 __gus_outregw_slow(GF1R_START_HIGH, address >> 11);
445 __gus_outregw_slow(GF1R_START_LOW, address << 5);
448 static inline void __gus_set_loop_end(unsigned char mode,
449 unsigned long address)
452 if (mode & GF1VC_DATA16)
453 address = __gus_convert_addr16(address);
454 __gus_outregw_slow(GF1R_END_HIGH, address >> 11);
455 __gus_outregw_slow(GF1R_END_LOW, address << 5);
458 static inline void __gus_mixer_output(boolean state)
461 gus.mixer &= ~GF1M_MIXER_NO_OUTPUT;
463 gus.mixer |= GF1M_MIXER_NO_OUTPUT;
464 outportb(GF1_MIX_CTRL, gus.mixer);
465 /* Dummy read to avoid touching DMA latches */
466 __gus_inregb(GF1R_BALANCE);
469 /* Inline routines for working with command pools */
471 /* WARNING: no bounds checking due to performance reasons */
472 #define __POOL_VALUE(type,value) \
473 *((unsigned type *)&gus.cmd_pool [gus.cmd_pool_top]) = value; \
474 gus.cmd_pool_top += sizeof (type);
476 static inline void __pool_command(unsigned char command)
478 __POOL_VALUE(char, command);
481 static inline void __pool_command_b(unsigned char command, unsigned char arg)
483 __POOL_VALUE(char, command);
484 __POOL_VALUE(char, arg);
487 static inline void __pool_command_w(unsigned char command, unsigned short arg)
489 __POOL_VALUE(char, command);
490 __POOL_VALUE(short, arg);
493 static inline void __pool_command_l(unsigned char command, unsigned long arg)
495 __POOL_VALUE(char, command);
496 __POOL_VALUE(long, arg);
499 static inline void __pool_select_voice(unsigned char voice)
501 if (gus.cmd_voice != voice)
502 __pool_command_b(PCMD_VOICE, gus.cmd_voice = voice);
508 /* Debug dump of GUS DRAM heap */
509 extern void __gus_mem_dump();
512 #endif /* __DOSGUS_H__ */