Revision 423d65f4
b/Makefile.target | ||
---|---|---|
421 | 421 |
ifdef CONFIG_ADLIB |
422 | 422 |
SOUND_HW += fmopl.o adlib.o |
423 | 423 |
endif |
424 |
ifdef CONFIG_GUS |
|
425 |
SOUND_HW += gus.o gusemu_hal.o gusemu_mixer.o |
|
426 |
endif |
|
424 | 427 |
|
425 | 428 |
ifdef CONFIG_VNC_TLS |
426 | 429 |
CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS) |
b/configure | ||
---|---|---|
86 | 86 |
slirp="yes" |
87 | 87 |
adlib="no" |
88 | 88 |
ac97="no" |
89 |
gus="no" |
|
89 | 90 |
oss="no" |
90 | 91 |
dsound="no" |
91 | 92 |
coreaudio="no" |
... | ... | |
283 | 284 |
;; |
284 | 285 |
--enable-ac97) ac97="yes" |
285 | 286 |
;; |
287 |
--enable-gus) gus="yes" |
|
288 |
;; |
|
286 | 289 |
--disable-kqemu) kqemu="no" |
287 | 290 |
;; |
288 | 291 |
--enable-profiler) profiler="yes" |
... | ... | |
410 | 413 |
echo " --enable-mingw32 enable Win32 cross compilation with mingw32" |
411 | 414 |
echo " --enable-adlib enable Adlib emulation" |
412 | 415 |
echo " --enable-ac97 enable AC97 emulation" |
416 |
echo " --enable-gus enable Gravis Ultrasound emulation" |
|
413 | 417 |
echo " --enable-coreaudio enable Coreaudio audio driver" |
414 | 418 |
echo " --enable-alsa enable ALSA audio driver" |
415 | 419 |
echo " --enable-esd enable EsoundD audio driver" |
... | ... | |
724 | 728 |
echo "mingw32 support $mingw32" |
725 | 729 |
echo "Adlib support $adlib" |
726 | 730 |
echo "AC97 support $ac97" |
731 |
echo "GUS support $gus" |
|
727 | 732 |
echo "CoreAudio support $coreaudio" |
728 | 733 |
echo "ALSA support $alsa" |
729 | 734 |
echo "EsounD support $esd" |
... | ... | |
904 | 909 |
echo "CONFIG_AC97=yes" >> $config_mak |
905 | 910 |
echo "#define CONFIG_AC97 1" >> $config_h |
906 | 911 |
fi |
912 |
if test "$gus" = "yes" ; then |
|
913 |
echo "CONFIG_GUS=yes" >> $config_mak |
|
914 |
echo "#define CONFIG_GUS 1" >> $config_h |
|
915 |
fi |
|
907 | 916 |
if test "$oss" = "yes" ; then |
908 | 917 |
echo "CONFIG_OSS=yes" >> $config_mak |
909 | 918 |
echo "#define CONFIG_OSS 1" >> $config_h |
b/hw/gus.c | ||
---|---|---|
1 |
/* |
|
2 |
* QEMU Proxy for Gravis Ultrasound GF1 emulation by Tibor "TS" Schütz |
|
3 |
* |
|
4 |
* Copyright (c) 2002-2005 Vassili Karpov (malc) |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 |
* of this software and associated documentation files (the "Software"), to deal |
|
8 |
* in the Software without restriction, including without limitation the rights |
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 |
* copies of the Software, and to permit persons to whom the Software is |
|
11 |
* furnished to do so, subject to the following conditions: |
|
12 |
* |
|
13 |
* The above copyright notice and this permission notice shall be included in |
|
14 |
* all copies or substantial portions of the Software. |
|
15 |
* |
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 |
* THE SOFTWARE. |
|
23 |
*/ |
|
24 |
#include "hw.h" |
|
25 |
#include "audiodev.h" |
|
26 |
#include "audio/audio.h" |
|
27 |
#include "isa.h" |
|
28 |
#include "gusemu.h" |
|
29 |
#include "gustate.h" |
|
30 |
|
|
31 |
#define dolog(...) AUD_log ("audio", __VA_ARGS__) |
|
32 |
#ifdef DEBUG |
|
33 |
#define ldebug(...) dolog (__VA_ARGS__) |
|
34 |
#else |
|
35 |
#define ldebug(...) |
|
36 |
#endif |
|
37 |
|
|
38 |
#ifdef WORDS_BIGENDIAN |
|
39 |
#define GUS_ENDIANNESS 1 |
|
40 |
#else |
|
41 |
#define GUS_ENDIANNESS 0 |
|
42 |
#endif |
|
43 |
|
|
44 |
#define IO_READ_PROTO(name) \ |
|
45 |
uint32_t name (void *opaque, uint32_t nport) |
|
46 |
#define IO_WRITE_PROTO(name) \ |
|
47 |
void name (void *opaque, uint32_t nport, uint32_t val) |
|
48 |
|
|
49 |
static struct { |
|
50 |
int port; |
|
51 |
int irq; |
|
52 |
int dma; |
|
53 |
int freq; |
|
54 |
} conf = {0x240, 7, 3, 44100}; |
|
55 |
|
|
56 |
typedef struct GUSState { |
|
57 |
GUSEmuState emu; |
|
58 |
QEMUSoundCard card; |
|
59 |
int freq; |
|
60 |
int pos, left, shift, irqs; |
|
61 |
uint16_t *mixbuf; |
|
62 |
uint8_t himem[1024 * 1024 + 32 + 4096]; |
|
63 |
int samples; |
|
64 |
SWVoiceOut *voice; |
|
65 |
int64_t last_ticks; |
|
66 |
qemu_irq *pic; |
|
67 |
} GUSState; |
|
68 |
|
|
69 |
IO_READ_PROTO (gus_readb) |
|
70 |
{ |
|
71 |
GUSState *s = opaque; |
|
72 |
|
|
73 |
return gus_read (&s->emu, nport, 1); |
|
74 |
} |
|
75 |
|
|
76 |
IO_READ_PROTO (gus_readw) |
|
77 |
{ |
|
78 |
GUSState *s = opaque; |
|
79 |
|
|
80 |
return gus_read (&s->emu, nport, 2); |
|
81 |
} |
|
82 |
|
|
83 |
IO_WRITE_PROTO (gus_writeb) |
|
84 |
{ |
|
85 |
GUSState *s = opaque; |
|
86 |
|
|
87 |
gus_write (&s->emu, nport, 1, val); |
|
88 |
} |
|
89 |
|
|
90 |
IO_WRITE_PROTO (gus_writew) |
|
91 |
{ |
|
92 |
GUSState *s = opaque; |
|
93 |
|
|
94 |
gus_write (&s->emu, nport, 2, val); |
|
95 |
} |
|
96 |
|
|
97 |
static int write_audio (GUSState *s, int samples) |
|
98 |
{ |
|
99 |
int net = 0; |
|
100 |
int pos = s->pos; |
|
101 |
|
|
102 |
while (samples) { |
|
103 |
int nbytes, wbytes, wsampl; |
|
104 |
|
|
105 |
nbytes = samples << s->shift; |
|
106 |
wbytes = AUD_write ( |
|
107 |
s->voice, |
|
108 |
s->mixbuf + (pos << (s->shift - 1)), |
|
109 |
nbytes |
|
110 |
); |
|
111 |
|
|
112 |
if (wbytes) { |
|
113 |
wsampl = wbytes >> s->shift; |
|
114 |
|
|
115 |
samples -= wsampl; |
|
116 |
pos = (pos + wsampl) % s->samples; |
|
117 |
|
|
118 |
net += wsampl; |
|
119 |
} |
|
120 |
else { |
|
121 |
break; |
|
122 |
} |
|
123 |
} |
|
124 |
|
|
125 |
return net; |
|
126 |
} |
|
127 |
|
|
128 |
static void GUS_callback (void *opaque, int free) |
|
129 |
{ |
|
130 |
int samples, to_play, net = 0; |
|
131 |
GUSState *s = opaque; |
|
132 |
|
|
133 |
samples = free >> s->shift; |
|
134 |
to_play = audio_MIN (samples, s->left); |
|
135 |
|
|
136 |
while (to_play) { |
|
137 |
int written = write_audio (s, to_play); |
|
138 |
|
|
139 |
if (!written) { |
|
140 |
goto reset; |
|
141 |
} |
|
142 |
|
|
143 |
s->left -= written; |
|
144 |
to_play -= written; |
|
145 |
samples -= written; |
|
146 |
net += written; |
|
147 |
} |
|
148 |
|
|
149 |
samples = audio_MIN (samples, s->samples); |
|
150 |
if (samples) { |
|
151 |
gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf); |
|
152 |
|
|
153 |
while (samples) { |
|
154 |
int written = write_audio (s, samples); |
|
155 |
if (!written) { |
|
156 |
break; |
|
157 |
} |
|
158 |
samples -= written; |
|
159 |
net += written; |
|
160 |
} |
|
161 |
} |
|
162 |
s->left = samples; |
|
163 |
|
|
164 |
reset: |
|
165 |
gus_irqgen (&s->emu, (double) (net * 1000000) / s->freq); |
|
166 |
} |
|
167 |
|
|
168 |
int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n) |
|
169 |
{ |
|
170 |
GUSState *s = emu->opaque; |
|
171 |
/* qemu_irq_lower (s->pic[hwirq]); */ |
|
172 |
qemu_irq_raise (s->pic[hwirq]); |
|
173 |
s->irqs += n; |
|
174 |
ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs); |
|
175 |
return n; |
|
176 |
} |
|
177 |
|
|
178 |
void GUS_irqclear (GUSEmuState *emu, int hwirq) |
|
179 |
{ |
|
180 |
GUSState *s = emu->opaque; |
|
181 |
ldebug ("irqclear %d %d\n", hwirq, s->irqs); |
|
182 |
qemu_irq_lower (s->pic[hwirq]); |
|
183 |
s->irqs -= 1; |
|
184 |
#ifdef IRQ_STORM |
|
185 |
if (s->irqs > 0) { |
|
186 |
qemu_irq_raise (s->pic[hwirq]); |
|
187 |
} |
|
188 |
#endif |
|
189 |
} |
|
190 |
|
|
191 |
void GUS_dmarequest (GUSEmuState *der) |
|
192 |
{ |
|
193 |
/* GUSState *s = (GUSState *) der; */ |
|
194 |
ldebug ("dma request %d\n", der->gusdma); |
|
195 |
DMA_hold_DREQ (der->gusdma); |
|
196 |
} |
|
197 |
|
|
198 |
int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) |
|
199 |
{ |
|
200 |
GUSState *s = opaque; |
|
201 |
int8_t tmpbuf[4096]; |
|
202 |
int pos = dma_pos, mode, left = dma_len - dma_pos; |
|
203 |
|
|
204 |
ldebug ("read DMA %#x %d\n", dma_pos, dma_len); |
|
205 |
mode = DMA_get_channel_mode (s->emu.gusdma); |
|
206 |
while (left) { |
|
207 |
int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf)); |
|
208 |
int copied; |
|
209 |
|
|
210 |
ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos); |
|
211 |
copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy); |
|
212 |
gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied); |
|
213 |
left -= copied; |
|
214 |
pos += copied; |
|
215 |
} |
|
216 |
|
|
217 |
if (0 == ((mode >> 4) & 1)) { |
|
218 |
DMA_release_DREQ (s->emu.gusdma); |
|
219 |
} |
|
220 |
return dma_len; |
|
221 |
} |
|
222 |
|
|
223 |
int GUS_init (AudioState *audio, qemu_irq *pic) |
|
224 |
{ |
|
225 |
GUSState *s; |
|
226 |
audsettings_t as; |
|
227 |
|
|
228 |
if (!audio) { |
|
229 |
dolog ("No audio state\n"); |
|
230 |
return -1; |
|
231 |
} |
|
232 |
|
|
233 |
s = qemu_mallocz (sizeof (*s)); |
|
234 |
if (!s) { |
|
235 |
dolog ("Could not allocate memory for GUS (%zu bytes)\n", |
|
236 |
sizeof (*s)); |
|
237 |
return -1; |
|
238 |
} |
|
239 |
|
|
240 |
AUD_register_card (audio, "gus", &s->card); |
|
241 |
|
|
242 |
as.freq = conf.freq; |
|
243 |
as.nchannels = 2; |
|
244 |
as.fmt = AUD_FMT_S16; |
|
245 |
as.endianness = GUS_ENDIANNESS; |
|
246 |
|
|
247 |
s->voice = AUD_open_out ( |
|
248 |
&s->card, |
|
249 |
NULL, |
|
250 |
"gus", |
|
251 |
s, |
|
252 |
GUS_callback, |
|
253 |
&as |
|
254 |
); |
|
255 |
|
|
256 |
if (!s->voice) { |
|
257 |
AUD_remove_card (&s->card); |
|
258 |
qemu_free (s); |
|
259 |
return -1; |
|
260 |
} |
|
261 |
|
|
262 |
s->shift = 2; |
|
263 |
s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift; |
|
264 |
s->mixbuf = qemu_mallocz (s->samples << s->shift); |
|
265 |
if (!s->mixbuf) { |
|
266 |
AUD_close_out (&s->card, s->voice); |
|
267 |
AUD_remove_card (&s->card); |
|
268 |
qemu_free (s); |
|
269 |
return -1; |
|
270 |
} |
|
271 |
|
|
272 |
register_ioport_write (conf.port, 1, 1, gus_writeb, s); |
|
273 |
register_ioport_write (conf.port, 1, 2, gus_writew, s); |
|
274 |
|
|
275 |
register_ioport_read ((conf.port + 0x100) & 0xf00, 1, 1, gus_readb, s); |
|
276 |
register_ioport_read ((conf.port + 0x100) & 0xf00, 1, 2, gus_readw, s); |
|
277 |
|
|
278 |
register_ioport_write (conf.port + 6, 10, 1, gus_writeb, s); |
|
279 |
register_ioport_write (conf.port + 6, 10, 2, gus_writew, s); |
|
280 |
register_ioport_read (conf.port + 6, 10, 1, gus_readb, s); |
|
281 |
register_ioport_read (conf.port + 6, 10, 2, gus_readw, s); |
|
282 |
|
|
283 |
|
|
284 |
register_ioport_write (conf.port + 0x100, 8, 1, gus_writeb, s); |
|
285 |
register_ioport_write (conf.port + 0x100, 8, 2, gus_writew, s); |
|
286 |
register_ioport_read (conf.port + 0x100, 8, 1, gus_readb, s); |
|
287 |
register_ioport_read (conf.port + 0x100, 8, 2, gus_readw, s); |
|
288 |
|
|
289 |
DMA_register_channel (conf.dma, GUS_read_DMA, s); |
|
290 |
s->emu.gusirq = conf.irq; |
|
291 |
s->emu.gusdma = conf.dma; |
|
292 |
s->emu.himemaddr = s->himem; |
|
293 |
s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32; |
|
294 |
s->emu.opaque = s; |
|
295 |
s->freq = conf.freq; |
|
296 |
s->pic = pic; |
|
297 |
|
|
298 |
AUD_set_active_out (s->voice, 1); |
|
299 |
return 0; |
|
300 |
} |
b/hw/gusemu.h | ||
---|---|---|
1 |
/* |
|
2 |
* GUSEMU32 - API |
|
3 |
* |
|
4 |
* Copyright (C) 2000-2007 Tibor "TS" Schütz |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 |
* of this software and associated documentation files (the "Software"), to deal |
|
8 |
* in the Software without restriction, including without limitation the rights |
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 |
* copies of the Software, and to permit persons to whom the Software is |
|
11 |
* furnished to do so, subject to the following conditions: |
|
12 |
* |
|
13 |
* The above copyright notice and this permission notice shall be included in |
|
14 |
* all copies or substantial portions of the Software. |
|
15 |
* |
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 |
* THE SOFTWARE. |
|
23 |
*/ |
|
24 |
|
|
25 |
#ifndef GUSEMU_H |
|
26 |
#define GUSEMU_H |
|
27 |
|
|
28 |
/* data types (need to be adjusted if neither a VC6 nor a C99 compatible compiler is used) */ |
|
29 |
|
|
30 |
#if defined _WIN32 && defined _MSC_VER /* doesnt support other win32 compilers yet, do it yourself... */ |
|
31 |
typedef unsigned char GUSbyte; |
|
32 |
typedef unsigned short GUSword; |
|
33 |
typedef unsigned int GUSdword; |
|
34 |
typedef signed char GUSchar; |
|
35 |
#else |
|
36 |
#include <stdint.h> |
|
37 |
typedef int8_t GUSchar; |
|
38 |
typedef uint8_t GUSbyte; |
|
39 |
typedef uint16_t GUSword; |
|
40 |
typedef uint32_t GUSdword; |
|
41 |
#endif |
|
42 |
|
|
43 |
typedef struct _GUSEmuState |
|
44 |
{ |
|
45 |
GUSbyte *himemaddr; /* 1024*1024 bytes used for storing uploaded samples (+32 additional bytes for read padding) */ |
|
46 |
GUSbyte *gusdatapos; /* (gusdataend-gusdata) bytes used for storing emulated GF1/mixer register states (32*32+4 bytes in initial GUSemu32 version) */ |
|
47 |
int gusirq; |
|
48 |
int gusdma; |
|
49 |
unsigned int timer1fraction; |
|
50 |
unsigned int timer2fraction; |
|
51 |
void *opaque; |
|
52 |
} GUSEmuState; |
|
53 |
|
|
54 |
/* ** Callback functions needed: */ |
|
55 |
/* NMI is defined as hwirq=-1 (not supported (yet?)) */ |
|
56 |
/* GUS_irqrequest returns the number of IRQs actually scheduled into the virtual machine */ |
|
57 |
/* Level triggered IRQ simulations normally return 1 */ |
|
58 |
/* Event triggered IRQ simulation can safely ignore GUS_irqclear calls */ |
|
59 |
int GUS_irqrequest(GUSEmuState *state, int hwirq, int num);/* needed in both mixer and bus emulation functions. */ |
|
60 |
void GUS_irqclear( GUSEmuState *state, int hwirq); /* used by gus_write() only - can be left empty for mixer functions */ |
|
61 |
void GUS_dmarequest(GUSEmuState *state); /* used by gus_write() only - can be left empty for mixer functions */ |
|
62 |
|
|
63 |
/* ** ISA bus interface functions: */ |
|
64 |
|
|
65 |
/* Port I/O handlers */ |
|
66 |
/* support the following ports: */ |
|
67 |
/* 2x0,2x6,2x8...2xF,3x0...3x7; */ |
|
68 |
/* optional: 388,389 (at least writes should be forwarded or some GUS detection algorithms will fail) */ |
|
69 |
/* data is passed in host byte order */ |
|
70 |
unsigned int gus_read( GUSEmuState *state, int port, int size); |
|
71 |
void gus_write(GUSEmuState *state, int port, int size, unsigned int data); |
|
72 |
/* size is given in bytes (1 for byte, 2 for word) */ |
|
73 |
|
|
74 |
/* DMA data transfer function */ |
|
75 |
/* data pointed to is passed in native x86 order */ |
|
76 |
void gus_dma_transferdata(GUSEmuState *state, char *dma_addr, unsigned int count, int TC); |
|
77 |
/* Called back by GUS_start_DMA as soon as the emulated DMA controller is ready for a transfer to or from GUS */ |
|
78 |
/* (might be immediately if the DMA controller was programmed first) */ |
|
79 |
/* dma_addr is an already translated address directly pointing to the beginning of the memory block */ |
|
80 |
/* do not forget to update DMA states after the call, including the DREQ and TC flags */ |
|
81 |
/* it is possible to break down a single transfer into multiple ones, but take care that: */ |
|
82 |
/* -dma_count is actually count-1 */ |
|
83 |
/* -before and during a transfer, DREQ is set and TC cleared */ |
|
84 |
/* -when calling gus_dma_transferdata(), TC is only set true for call transfering the last byte */ |
|
85 |
/* -after the last transfer, DREQ is cleared and TC is set */ |
|
86 |
|
|
87 |
/* ** GF1 mixer emulation functions: */ |
|
88 |
/* Usually, gus_irqgen should be called directly after gus_mixvoices if you can meet the recommended ranges. */ |
|
89 |
/* If the interrupts are executed immediately (i.e., are synchronous), it may be useful to break this */ |
|
90 |
/* down into a sequence of gus_mixvoice();gus_irqgen(); calls while mixing an audio block. */ |
|
91 |
/* If the interrupts are asynchronous, it may be needed to use a separate thread mixing into a temporary */ |
|
92 |
/* audio buffer in order to avoid quality loss caused by large numsamples and elapsed_time values. */ |
|
93 |
|
|
94 |
void gus_mixvoices(GUSEmuState *state, unsigned int playback_freq, unsigned int numsamples, short *bufferpos); |
|
95 |
/* recommended range: 10 < numsamples < 100 */ |
|
96 |
/* lower values may result in increased rounding error, higher values often cause audible timing delays */ |
|
97 |
|
|
98 |
void gus_irqgen(GUSEmuState *state, unsigned int elapsed_time); |
|
99 |
/* recommended range: 80us < elapsed_time < max(1000us, numsamples/playback_freq) */ |
|
100 |
/* lower values won´t provide any benefit at all, higher values can cause audible timing delays */ |
|
101 |
/* note: masked timers are also calculated by this function, thus it might be needed even without any IRQs in use! */ |
|
102 |
|
|
103 |
#endif /* gusemu.h */ |
b/hw/gusemu_hal.c | ||
---|---|---|
1 |
/* |
|
2 |
* GUSEMU32 - bus interface part |
|
3 |
* |
|
4 |
* Copyright (C) 2000-2007 Tibor "TS" Schütz |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 |
* of this software and associated documentation files (the "Software"), to deal |
|
8 |
* in the Software without restriction, including without limitation the rights |
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 |
* copies of the Software, and to permit persons to whom the Software is |
|
11 |
* furnished to do so, subject to the following conditions: |
|
12 |
* |
|
13 |
* The above copyright notice and this permission notice shall be included in |
|
14 |
* all copies or substantial portions of the Software. |
|
15 |
* |
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 |
* THE SOFTWARE. |
|
23 |
*/ |
|
24 |
|
|
25 |
/* |
|
26 |
* TODO: check mixer: see 7.20 of sdk for panning pos (applies to all gus models?)? |
|
27 |
*/ |
|
28 |
|
|
29 |
#include "gustate.h" |
|
30 |
#include "gusemu.h" |
|
31 |
|
|
32 |
#define GUSregb(position) (* (gusptr+(position))) |
|
33 |
#define GUSregw(position) (*(GUSword *) (gusptr+(position))) |
|
34 |
#define GUSregd(position) (*(GUSdword *)(gusptr+(position))) |
|
35 |
|
|
36 |
/* size given in bytes */ |
|
37 |
unsigned int gus_read(GUSEmuState * state, int port, int size) |
|
38 |
{ |
|
39 |
int value_read = 0; |
|
40 |
|
|
41 |
GUSbyte *gusptr; |
|
42 |
gusptr = state->gusdatapos; |
|
43 |
GUSregd(portaccesses)++; |
|
44 |
|
|
45 |
switch (port & 0xff0f) |
|
46 |
{ |
|
47 |
/* MixerCtrlReg (read not supported on GUS classic) */ |
|
48 |
/* case 0x200: return GUSregb(MixerCtrlReg2x0); */ |
|
49 |
case 0x206: /* IRQstatReg / SB2x6IRQ */ |
|
50 |
/* adlib/sb bits set in port handlers */ |
|
51 |
/* timer/voice bits set in gus_irqgen() */ |
|
52 |
/* dma bit set in gus_dma_transferdata */ |
|
53 |
/* midi not implemented yet */ |
|
54 |
return GUSregb(IRQStatReg2x6); |
|
55 |
/* case 0x308: */ /* AdLib388 */ |
|
56 |
case 0x208: |
|
57 |
if (GUSregb(GUS45TimerCtrl) & 1) |
|
58 |
return GUSregb(TimerStatus2x8); |
|
59 |
return GUSregb(AdLibStatus2x8); /* AdLibStatus */ |
|
60 |
case 0x309: /* AdLib389 */ |
|
61 |
case 0x209: |
|
62 |
return GUSregb(AdLibData2x9); /* AdLibData */ |
|
63 |
case 0x20A: |
|
64 |
return GUSregb(AdLibCommand2xA); /* AdLib2x8_2xA */ |
|
65 |
|
|
66 |
#if 0 |
|
67 |
case 0x20B: /* GUS hidden registers (read not supported on GUS classic) */ |
|
68 |
switch (GUSregb(RegCtrl_2xF) & 0x07) |
|
69 |
{ |
|
70 |
case 0: /* IRQ/DMA select */ |
|
71 |
if (GUSregb(MixerCtrlReg2x0) & 0x40) |
|
72 |
return GUSregb(IRQ_2xB); /* control register select bit */ |
|
73 |
else |
|
74 |
return GUSregb(DMA_2xB); |
|
75 |
/* case 1-5: */ /* general purpose emulation regs */ |
|
76 |
/* return ... */ /* + status reset reg (write only) */ |
|
77 |
case 6: |
|
78 |
return GUSregb(Jumper_2xB); /* Joystick/MIDI enable (JumperReg) */ |
|
79 |
default:; |
|
80 |
} |
|
81 |
break; |
|
82 |
#endif |
|
83 |
|
|
84 |
case 0x20C: /* SB2xCd */ |
|
85 |
value_read = GUSregb(SB2xCd); |
|
86 |
if (GUSregb(StatRead_2xF) & 0x20) |
|
87 |
GUSregb(SB2xCd) ^= 0x80; /* toggle MSB on read */ |
|
88 |
return value_read; |
|
89 |
/* case 0x20D: */ /* SB2xD is write only -> 2xE writes to it*/ |
|
90 |
case 0x20E: |
|
91 |
if (GUSregb(RegCtrl_2xF) & 0x80) /* 2xE read IRQ enabled? */ |
|
92 |
{ |
|
93 |
GUSregb(StatRead_2xF) |= 0x80; |
|
94 |
GUS_irqrequest(state, state->gusirq, 1); |
|
95 |
} |
|
96 |
return GUSregb(SB2xE); /* SB2xE */ |
|
97 |
case 0x20F: /* StatRead_2xF */ |
|
98 |
/*set/clear fixed bits */ |
|
99 |
/*value_read = (GUSregb(StatRead_2xF) & 0xf9)|1; */ /*(LSB not set on GUS classic!)*/ |
|
100 |
value_read = (GUSregb(StatRead_2xF) & 0xf9); |
|
101 |
if (GUSregb(MixerCtrlReg2x0) & 0x08) |
|
102 |
value_read |= 2; /* DMA/IRQ enabled flag */ |
|
103 |
return value_read; |
|
104 |
/* case 0x300: */ /* MIDI (not implemented) */ |
|
105 |
/* case 0x301: */ /* MIDI (not implemented) */ |
|
106 |
case 0x302: |
|
107 |
return GUSregb(VoiceSelReg3x2); /* VoiceSelReg */ |
|
108 |
case 0x303: |
|
109 |
return GUSregb(FunkSelReg3x3); /* FunkSelReg */ |
|
110 |
case 0x304: /* DataRegLoByte3x4 + DataRegWord3x4 */ |
|
111 |
case 0x305: /* DataRegHiByte3x5 */ |
|
112 |
switch (GUSregb(FunkSelReg3x3)) |
|
113 |
{ |
|
114 |
/* common functions */ |
|
115 |
case 0x41: /* DramDMAContrReg */ |
|
116 |
value_read = GUSregb(GUS41DMACtrl); /* &0xfb */ |
|
117 |
GUSregb(GUS41DMACtrl) &= 0xbb; |
|
118 |
if (state->gusdma >= 4) |
|
119 |
value_read |= 0x04; |
|
120 |
if (GUSregb(IRQStatReg2x6) & 0x80) |
|
121 |
{ |
|
122 |
value_read |= 0x40; |
|
123 |
GUSregb(IRQStatReg2x6) &= 0x7f; |
|
124 |
if (!GUSregb(IRQStatReg2x6)) |
|
125 |
GUS_irqclear(state, state->gusirq); |
|
126 |
} |
|
127 |
return (GUSbyte) value_read; |
|
128 |
/* DramDMAmemPosReg */ |
|
129 |
/* case 0x42: value_read=GUSregw(GUS42DMAStart); break;*/ |
|
130 |
/* 43h+44h write only */ |
|
131 |
case 0x45: |
|
132 |
return GUSregb(GUS45TimerCtrl); /* TimerCtrlReg */ |
|
133 |
/* 46h+47h write only */ |
|
134 |
/* 48h: samp freq - write only */ |
|
135 |
case 0x49: |
|
136 |
return GUSregb(GUS49SampCtrl) & 0xbf; /* SampCtrlReg */ |
|
137 |
/* case 4bh: */ /* joystick trim not supported */ |
|
138 |
/* case 0x4c: return GUSregb(GUS4cReset); */ /* GUSreset: write only*/ |
|
139 |
/* voice specific functions */ |
|
140 |
case 0x80: |
|
141 |
case 0x81: |
|
142 |
case 0x82: |
|
143 |
case 0x83: |
|
144 |
case 0x84: |
|
145 |
case 0x85: |
|
146 |
case 0x86: |
|
147 |
case 0x87: |
|
148 |
case 0x88: |
|
149 |
case 0x89: |
|
150 |
case 0x8a: |
|
151 |
case 0x8b: |
|
152 |
case 0x8c: |
|
153 |
case 0x8d: |
|
154 |
{ |
|
155 |
int offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f); |
|
156 |
offset += ((int) GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */ |
|
157 |
value_read = GUSregw(offset); |
|
158 |
} |
|
159 |
break; |
|
160 |
/* voice unspecific functions */ |
|
161 |
case 0x8e: /* NumVoice */ |
|
162 |
return GUSregb(NumVoices); |
|
163 |
case 0x8f: /* irqstatreg */ |
|
164 |
/* (pseudo IRQ-FIFO is processed during a gus_write(0x3X3,0x8f)) */ |
|
165 |
return GUSregb(SynVoiceIRQ8f); |
|
166 |
default: |
|
167 |
return 0xffff; |
|
168 |
} |
|
169 |
if (size == 1) |
|
170 |
{ |
|
171 |
if ((port & 0xff0f) == 0x305) |
|
172 |
value_read = value_read >> 8; |
|
173 |
value_read &= 0xff; |
|
174 |
} |
|
175 |
return (GUSword) value_read; |
|
176 |
/* case 0x306: */ /* Mixer/Version info */ |
|
177 |
/* return 0xff; */ /* Pre 3.6 boards, ICS mixer NOT present */ |
|
178 |
case 0x307: /* DRAMaccess */ |
|
179 |
{ |
|
180 |
GUSbyte *adr; |
|
181 |
adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff); |
|
182 |
return *adr; |
|
183 |
} |
|
184 |
default:; |
|
185 |
} |
|
186 |
return 0xffff; |
|
187 |
} |
|
188 |
|
|
189 |
void gus_write(GUSEmuState * state, int port, int size, unsigned int data) |
|
190 |
{ |
|
191 |
GUSbyte *gusptr; |
|
192 |
gusptr = state->gusdatapos; |
|
193 |
GUSregd(portaccesses)++; |
|
194 |
|
|
195 |
switch (port & 0xff0f) |
|
196 |
{ |
|
197 |
case 0x200: /* MixerCtrlReg */ |
|
198 |
GUSregb(MixerCtrlReg2x0) = (GUSbyte) data; |
|
199 |
break; |
|
200 |
case 0x206: /* IRQstatReg / SB2x6IRQ */ |
|
201 |
if (GUSregb(GUS45TimerCtrl) & 0x20) /* SB IRQ enabled? -> set 2x6IRQ bit */ |
|
202 |
{ |
|
203 |
GUSregb(TimerStatus2x8) |= 0x08; |
|
204 |
GUSregb(IRQStatReg2x6) = 0x10; |
|
205 |
GUS_irqrequest(state, state->gusirq, 1); |
|
206 |
} |
|
207 |
break; |
|
208 |
case 0x308: /* AdLib 388h */ |
|
209 |
case 0x208: /* AdLibCommandReg */ |
|
210 |
GUSregb(AdLibCommand2xA) = (GUSbyte) data; |
|
211 |
break; |
|
212 |
case 0x309: /* AdLib 389h */ |
|
213 |
case 0x209: /* AdLibDataReg */ |
|
214 |
if ((GUSregb(AdLibCommand2xA) == 0x04) && (!(GUSregb(GUS45TimerCtrl) & 1))) /* GUS auto timer mode enabled? */ |
|
215 |
{ |
|
216 |
if (data & 0x80) |
|
217 |
GUSregb(TimerStatus2x8) &= 0x1f; /* AdLib IRQ reset? -> clear maskable adl. timer int regs */ |
|
218 |
else |
|
219 |
GUSregb(TimerDataReg2x9) = (GUSbyte) data; |
|
220 |
} |
|
221 |
else |
|
222 |
{ |
|
223 |
GUSregb(AdLibData2x9) = (GUSbyte) data; |
|
224 |
if (GUSregb(GUS45TimerCtrl) & 0x02) |
|
225 |
{ |
|
226 |
GUSregb(TimerStatus2x8) |= 0x01; |
|
227 |
GUSregb(IRQStatReg2x6) = 0x10; |
|
228 |
GUS_irqrequest(state, state->gusirq, 1); |
|
229 |
} |
|
230 |
} |
|
231 |
break; |
|
232 |
case 0x20A: |
|
233 |
GUSregb(AdLibStatus2x8) = (GUSbyte) data; |
|
234 |
break; /* AdLibStatus2x8 */ |
|
235 |
case 0x20B: /* GUS hidden registers */ |
|
236 |
switch (GUSregb(RegCtrl_2xF) & 0x7) |
|
237 |
{ |
|
238 |
case 0: |
|
239 |
if (GUSregb(MixerCtrlReg2x0) & 0x40) |
|
240 |
GUSregb(IRQ_2xB) = (GUSbyte) data; /* control register select bit */ |
|
241 |
else |
|
242 |
GUSregb(DMA_2xB) = (GUSbyte) data; |
|
243 |
break; |
|
244 |
/* case 1-4: general purpose emulation regs */ |
|
245 |
case 5: /* clear stat reg 2xF */ |
|
246 |
GUSregb(StatRead_2xF) = 0; /* ToDo: is this identical with GUS classic? */ |
|
247 |
if (!GUSregb(IRQStatReg2x6)) |
|
248 |
GUS_irqclear(state, state->gusirq); |
|
249 |
break; |
|
250 |
case 6: /* Jumper reg (Joystick/MIDI enable) */ |
|
251 |
GUSregb(Jumper_2xB) = (GUSbyte) data; |
|
252 |
break; |
|
253 |
default:; |
|
254 |
} |
|
255 |
break; |
|
256 |
case 0x20C: /* SB2xCd */ |
|
257 |
if (GUSregb(GUS45TimerCtrl) & 0x20) |
|
258 |
{ |
|
259 |
GUSregb(TimerStatus2x8) |= 0x10; /* SB IRQ enabled? -> set 2xCIRQ bit */ |
|
260 |
GUSregb(IRQStatReg2x6) = 0x10; |
|
261 |
GUS_irqrequest(state, state->gusirq, 1); |
|
262 |
} |
|
263 |
case 0x20D: /* SB2xCd no IRQ */ |
|
264 |
GUSregb(SB2xCd) = (GUSbyte) data; |
|
265 |
break; |
|
266 |
case 0x20E: /* SB2xE */ |
|
267 |
GUSregb(SB2xE) = (GUSbyte) data; |
|
268 |
break; |
|
269 |
case 0x20F: |
|
270 |
GUSregb(RegCtrl_2xF) = (GUSbyte) data; |
|
271 |
break; /* CtrlReg2xF */ |
|
272 |
case 0x302: /* VoiceSelReg */ |
|
273 |
GUSregb(VoiceSelReg3x2) = (GUSbyte) data; |
|
274 |
break; |
|
275 |
case 0x303: /* FunkSelReg */ |
|
276 |
GUSregb(FunkSelReg3x3) = (GUSbyte) data; |
|
277 |
if ((GUSbyte) data == 0x8f) /* set irqstatreg, get voicereg and clear IRQ */ |
|
278 |
{ |
|
279 |
int voice; |
|
280 |
if (GUSregd(voicewavetableirq)) /* WavetableIRQ */ |
|
281 |
{ |
|
282 |
for (voice = 0; voice < 31; voice++) |
|
283 |
{ |
|
284 |
if (GUSregd(voicewavetableirq) & (1 << voice)) |
|
285 |
{ |
|
286 |
GUSregd(voicewavetableirq) ^= (1 << voice); /* clear IRQ bit */ |
|
287 |
GUSregb(voice << 5) &= 0x7f; /* clear voice reg irq bit */ |
|
288 |
if (!GUSregd(voicewavetableirq)) |
|
289 |
GUSregb(IRQStatReg2x6) &= 0xdf; |
|
290 |
if (!GUSregb(IRQStatReg2x6)) |
|
291 |
GUS_irqclear(state, state->gusirq); |
|
292 |
GUSregb(SynVoiceIRQ8f) = voice | 0x60; /* (bit==0 => IRQ wartend) */ |
|
293 |
return; |
|
294 |
} |
|
295 |
} |
|
296 |
} |
|
297 |
else if (GUSregd(voicevolrampirq)) /* VolRamp IRQ */ |
|
298 |
{ |
|
299 |
for (voice = 0; voice < 31; voice++) |
|
300 |
{ |
|
301 |
if (GUSregd(voicevolrampirq) & (1 << voice)) |
|
302 |
{ |
|
303 |
GUSregd(voicevolrampirq) ^= (1 << voice); /* clear IRQ bit */ |
|
304 |
GUSregb((voice << 5) + VSRVolRampControl) &= 0x7f; /* clear voice volume reg irq bit */ |
|
305 |
if (!GUSregd(voicevolrampirq)) |
|
306 |
GUSregb(IRQStatReg2x6) &= 0xbf; |
|
307 |
if (!GUSregb(IRQStatReg2x6)) |
|
308 |
GUS_irqclear(state, state->gusirq); |
|
309 |
GUSregb(SynVoiceIRQ8f) = voice | 0x80; /* (bit==0 => IRQ wartend) */ |
|
310 |
return; |
|
311 |
} |
|
312 |
} |
|
313 |
} |
|
314 |
GUSregb(SynVoiceIRQ8f) = 0xe8; /* kein IRQ wartet */ |
|
315 |
} |
|
316 |
break; |
|
317 |
case 0x304: |
|
318 |
case 0x305: |
|
319 |
{ |
|
320 |
GUSword writedata = (GUSword) data; |
|
321 |
GUSword readmask = 0x0000; |
|
322 |
if (size == 1) |
|
323 |
{ |
|
324 |
readmask = 0xff00; |
|
325 |
writedata &= 0xff; |
|
326 |
if ((port & 0xff0f) == 0x305) |
|
327 |
{ |
|
328 |
writedata = (GUSword) (writedata << 8); |
|
329 |
readmask = 0x00ff; |
|
330 |
} |
|
331 |
} |
|
332 |
switch (GUSregb(FunkSelReg3x3)) |
|
333 |
{ |
|
334 |
/* voice specific functions */ |
|
335 |
case 0x00: |
|
336 |
case 0x01: |
|
337 |
case 0x02: |
|
338 |
case 0x03: |
|
339 |
case 0x04: |
|
340 |
case 0x05: |
|
341 |
case 0x06: |
|
342 |
case 0x07: |
|
343 |
case 0x08: |
|
344 |
case 0x09: |
|
345 |
case 0x0a: |
|
346 |
case 0x0b: |
|
347 |
case 0x0c: |
|
348 |
case 0x0d: |
|
349 |
{ |
|
350 |
int offset; |
|
351 |
if (!(GUSregb(GUS4cReset) & 0x01)) |
|
352 |
break; /* reset flag active? */ |
|
353 |
offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f); |
|
354 |
offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */ |
|
355 |
GUSregw(offset) = (GUSword) ((GUSregw(offset) & readmask) | writedata); |
|
356 |
} |
|
357 |
break; |
|
358 |
/* voice unspecific functions */ |
|
359 |
case 0x0e: /* NumVoices */ |
|
360 |
GUSregb(NumVoices) = (GUSbyte) data; |
|
361 |
break; |
|
362 |
/* case 0x0f: */ /* read only */ |
|
363 |
/* common functions */ |
|
364 |
case 0x41: /* DramDMAContrReg */ |
|
365 |
GUSregb(GUS41DMACtrl) = (GUSbyte) data; |
|
366 |
if (data & 0x01) |
|
367 |
GUS_dmarequest(state); |
|
368 |
break; |
|
369 |
case 0x42: /* DramDMAmemPosReg */ |
|
370 |
GUSregw(GUS42DMAStart) = (GUSregw(GUS42DMAStart) & readmask) | writedata; |
|
371 |
GUSregb(GUS50DMAHigh) &= 0xf; /* compatibility stuff... */ |
|
372 |
break; |
|
373 |
case 0x43: /* DRAMaddrLo */ |
|
374 |
GUSregd(GUSDRAMPOS24bit) = |
|
375 |
(GUSregd(GUSDRAMPOS24bit) & (readmask | 0xff0000)) | writedata; |
|
376 |
break; |
|
377 |
case 0x44: /* DRAMaddrHi */ |
|
378 |
GUSregd(GUSDRAMPOS24bit) = |
|
379 |
(GUSregd(GUSDRAMPOS24bit) & 0xffff) | ((data & 0x0f) << 16); |
|
380 |
break; |
|
381 |
case 0x45: /* TCtrlReg */ |
|
382 |
GUSregb(GUS45TimerCtrl) = (GUSbyte) data; |
|
383 |
if (!(data & 0x20)) |
|
384 |
GUSregb(TimerStatus2x8) &= 0xe7; /* sb IRQ dis? -> clear 2x8/2xC sb IRQ flags */ |
|
385 |
if (!(data & 0x02)) |
|
386 |
GUSregb(TimerStatus2x8) &= 0xfe; /* adlib data IRQ dis? -> clear 2x8 adlib IRQ flag */ |
|
387 |
if (!(GUSregb(TimerStatus2x8) & 0x19)) |
|
388 |
GUSregb(IRQStatReg2x6) &= 0xef; /* 0xe6; $$clear IRQ if both IRQ bits are inactive or cleared */ |
|
389 |
/* catch up delayed timer IRQs: */ |
|
390 |
if ((GUSregw(TimerIRQs) > 1) && (GUSregb(TimerDataReg2x9) & 3)) |
|
391 |
{ |
|
392 |
if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */ |
|
393 |
{ |
|
394 |
if (!(GUSregb(TimerDataReg2x9) & 0x40)) |
|
395 |
GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */ |
|
396 |
if (data & 4) /* timer1 irq enable */ |
|
397 |
{ |
|
398 |
GUSregb(TimerStatus2x8) |= 4; /* nonmaskable bit */ |
|
399 |
GUSregb(IRQStatReg2x6) |= 4; /* timer 1 irq pending */ |
|
400 |
} |
|
401 |
} |
|
402 |
if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */ |
|
403 |
{ |
|
404 |
if (!(GUSregb(TimerDataReg2x9) & 0x20)) |
|
405 |
GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */ |
|
406 |
if (data & 8) /* timer2 irq enable */ |
|
407 |
{ |
|
408 |
GUSregb(TimerStatus2x8) |= 2; /* nonmaskable bit */ |
|
409 |
GUSregb(IRQStatReg2x6) |= 8; /* timer 2 irq pending */ |
|
410 |
} |
|
411 |
} |
|
412 |
GUSregw(TimerIRQs)--; |
|
413 |
if (GUSregw(BusyTimerIRQs) > 1) |
|
414 |
GUSregw(BusyTimerIRQs)--; |
|
415 |
else |
|
416 |
GUSregw(BusyTimerIRQs) = |
|
417 |
GUS_irqrequest(state, state->gusirq, GUSregw(TimerIRQs)); |
|
418 |
} |
|
419 |
else |
|
420 |
GUSregw(TimerIRQs) = 0; |
|
421 |
|
|
422 |
if (!(data & 0x04)) |
|
423 |
{ |
|
424 |
GUSregb(TimerStatus2x8) &= 0xfb; /* clear non-maskable timer1 bit */ |
|
425 |
GUSregb(IRQStatReg2x6) &= 0xfb; |
|
426 |
} |
|
427 |
if (!(data & 0x08)) |
|
428 |
{ |
|
429 |
GUSregb(TimerStatus2x8) &= 0xfd; /* clear non-maskable timer2 bit */ |
|
430 |
GUSregb(IRQStatReg2x6) &= 0xf7; |
|
431 |
} |
|
432 |
if (!GUSregb(IRQStatReg2x6)) |
|
433 |
GUS_irqclear(state, state->gusirq); |
|
434 |
break; |
|
435 |
case 0x46: /* Counter1 */ |
|
436 |
GUSregb(GUS46Counter1) = (GUSbyte) data; |
|
437 |
break; |
|
438 |
case 0x47: /* Counter2 */ |
|
439 |
GUSregb(GUS47Counter2) = (GUSbyte) data; |
|
440 |
break; |
|
441 |
/* case 0x48: */ /* sampling freq reg not emulated (same as interwave) */ |
|
442 |
case 0x49: /* SampCtrlReg */ |
|
443 |
GUSregb(GUS49SampCtrl) = (GUSbyte) data; |
|
444 |
break; |
|
445 |
/* case 0x4b: */ /* joystick trim not emulated */ |
|
446 |
case 0x4c: /* GUSreset */ |
|
447 |
GUSregb(GUS4cReset) = (GUSbyte) data; |
|
448 |
if (!(GUSregb(GUS4cReset) & 1)) /* reset... */ |
|
449 |
{ |
|
450 |
GUSregd(voicewavetableirq) = 0; |
|
451 |
GUSregd(voicevolrampirq) = 0; |
|
452 |
GUSregw(TimerIRQs) = 0; |
|
453 |
GUSregw(BusyTimerIRQs) = 0; |
|
454 |
GUSregb(NumVoices) = 0xcd; |
|
455 |
GUSregb(IRQStatReg2x6) = 0; |
|
456 |
GUSregb(TimerStatus2x8) = 0; |
|
457 |
GUSregb(AdLibData2x9) = 0; |
|
458 |
GUSregb(TimerDataReg2x9) = 0; |
|
459 |
GUSregb(GUS41DMACtrl) = 0; |
|
460 |
GUSregb(GUS45TimerCtrl) = 0; |
|
461 |
GUSregb(GUS49SampCtrl) = 0; |
|
462 |
GUSregb(GUS4cReset) &= 0xf9; /* clear IRQ and DAC enable bits */ |
|
463 |
GUS_irqclear(state, state->gusirq); |
|
464 |
} |
|
465 |
/* IRQ enable bit checked elsewhere */ |
|
466 |
/* EnableDAC bit may be used by external callers */ |
|
467 |
break; |
|
468 |
} |
|
469 |
} |
|
470 |
break; |
|
471 |
case 0x307: /* DRAMaccess */ |
|
472 |
{ |
|
473 |
GUSbyte *adr; |
|
474 |
adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff); |
|
475 |
*adr = (GUSbyte) data; |
|
476 |
} |
|
477 |
break; |
|
478 |
} |
|
479 |
} |
|
480 |
|
|
481 |
/* Attention when breaking up a single DMA transfer to multiple ones: |
|
482 |
* it may lead to multiple terminal count interrupts and broken transfers: |
|
483 |
* |
|
484 |
* 1. Whenever you transfer a piece of data, the gusemu callback is invoked |
|
485 |
* 2. The callback may generate a TC irq (if the register was set up to do so) |
|
486 |
* 3. The irq may result in the program using the GUS to reprogram the GUS |
|
487 |
* |
|
488 |
* Some programs also decide to upload by just checking if TC occurs |
|
489 |
* (via interrupt or a cleared GUS dma flag) |
|
490 |
* and then start the next transfer, without checking DMA state |
|
491 |
* |
|
492 |
* Thus: Always make sure to set the TC flag correctly! |
|
493 |
* |
|
494 |
* Note that the genuine GUS had a granularity of 16 bytes/words for low/high DMA |
|
495 |
* while later cards had atomic granularity provided by an additional GUS50DMAHigh register |
|
496 |
* GUSemu also uses this register to support byte-granular transfers for better compatibility |
|
497 |
* with emulators other than GUSemu32 |
|
498 |
*/ |
|
499 |
|
|
500 |
void gus_dma_transferdata(GUSEmuState * state, char *dma_addr, unsigned int count, int TC) |
|
501 |
{ |
|
502 |
/* this function gets called by the callback function as soon as a DMA transfer is about to start |
|
503 |
* dma_addr is a translated address within accessible memory, not the physical one, |
|
504 |
* count is (real dma count register)+1 |
|
505 |
* note that the amount of bytes transfered is fully determined by values in the DMA registers |
|
506 |
* do not forget to update DMA states after transferring the entire block: |
|
507 |
* DREQ cleared & TC asserted after the _whole_ transfer */ |
|
508 |
|
|
509 |
char *srcaddr; |
|
510 |
char *destaddr; |
|
511 |
char msbmask = 0; |
|
512 |
GUSbyte *gusptr; |
|
513 |
gusptr = state->gusdatapos; |
|
514 |
|
|
515 |
srcaddr = dma_addr; /* system memory address */ |
|
516 |
{ |
|
517 |
int offset = (GUSregw(GUS42DMAStart) << 4) + (GUSregb(GUS50DMAHigh) & 0xf); |
|
518 |
if (state->gusdma >= 4) |
|
519 |
offset = (offset & 0xc0000) + (2 * (offset & 0x1fff0)); /* 16 bit address translation */ |
|
520 |
destaddr = (char *) state->himemaddr + offset; /* wavetable RAM adress */ |
|
521 |
} |
|
522 |
|
|
523 |
GUSregw(GUS42DMAStart) += (GUSword) (count >> 4); /* ToDo: add 16bit GUS page limit? */ |
|
524 |
GUSregb(GUS50DMAHigh) = (GUSbyte) ((count + GUSregb(GUS50DMAHigh)) & 0xf); /* ToDo: add 16bit GUS page limit? */ |
|
525 |
|
|
526 |
if (GUSregb(GUS41DMACtrl) & 0x02) /* direction, 0 := sysram->gusram */ |
|
527 |
{ |
|
528 |
char *tmpaddr = destaddr; |
|
529 |
destaddr = srcaddr; |
|
530 |
srcaddr = tmpaddr; |
|
531 |
} |
|
532 |
|
|
533 |
if ((GUSregb(GUS41DMACtrl) & 0x80) && (!(GUSregb(GUS41DMACtrl) & 0x02))) |
|
534 |
msbmask = (const char) 0x80; /* invert MSB */ |
|
535 |
for (; count > 0; count--) |
|
536 |
{ |
|
537 |
if (GUSregb(GUS41DMACtrl) & 0x40) |
|
538 |
*(destaddr++) = *(srcaddr++); /* 16 bit lobyte */ |
|
539 |
else |
|
540 |
*(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 8 bit */ |
|
541 |
if (state->gusdma >= 4) |
|
542 |
*(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 16 bit hibyte */ |
|
543 |
} |
|
544 |
|
|
545 |
if (TC) |
|
546 |
{ |
|
547 |
(GUSregb(GUS41DMACtrl)) &= 0xfe; /* clear DMA request bit */ |
|
548 |
if (GUSregb(GUS41DMACtrl) & 0x20) /* DMA terminal count IRQ */ |
|
549 |
{ |
|
550 |
GUSregb(IRQStatReg2x6) |= 0x80; |
|
551 |
GUS_irqrequest(state, state->gusirq, 1); |
|
552 |
} |
|
553 |
} |
|
554 |
} |
b/hw/gusemu_mixer.c | ||
---|---|---|
1 |
/* |
|
2 |
* GUSEMU32 - mixing engine (similar to Interwave GF1 compatibility) |
|
3 |
* |
|
4 |
* Copyright (C) 2000-2007 Tibor "TS" Schütz |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 |
* of this software and associated documentation files (the "Software"), to deal |
|
8 |
* in the Software without restriction, including without limitation the rights |
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 |
* copies of the Software, and to permit persons to whom the Software is |
|
11 |
* furnished to do so, subject to the following conditions: |
|
12 |
* |
|
13 |
* The above copyright notice and this permission notice shall be included in |
|
14 |
* all copies or substantial portions of the Software. |
|
15 |
* |
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 |
* THE SOFTWARE. |
|
23 |
*/ |
|
24 |
|
|
25 |
#include "gusemu.h" |
|
26 |
#include "gustate.h" |
|
27 |
|
|
28 |
#define GUSregb(position) (* (gusptr+(position))) |
|
29 |
#define GUSregw(position) (*(GUSword *) (gusptr+(position))) |
|
30 |
#define GUSregd(position) (*(GUSdword *)(gusptr+(position))) |
|
31 |
|
|
32 |
#define GUSvoice(position) (*(GUSword *)(voiceptr+(position))) |
|
33 |
|
|
34 |
/* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */ |
|
35 |
void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples, |
|
36 |
short *bufferpos) |
|
37 |
{ |
|
38 |
/* note that byte registers are stored in the upper half of each voice register! */ |
|
39 |
GUSbyte *gusptr; |
|
40 |
int Voice; |
|
41 |
GUSword *voiceptr; |
|
42 |
|
|
43 |
unsigned int count; |
|
44 |
for (count = 0; count < numsamples * 2; count++) |
|
45 |
*(bufferpos + count) = 0; /* clear */ |
|
46 |
|
|
47 |
gusptr = state->gusdatapos; |
|
48 |
voiceptr = (GUSword *) gusptr; |
|
49 |
if (!(GUSregb(GUS4cReset) & 0x01)) /* reset flag active? */ |
|
50 |
return; |
|
51 |
|
|
52 |
for (Voice = 0; Voice <= (GUSregb(NumVoices) & 31); Voice++) |
|
53 |
{ |
|
54 |
if (GUSvoice(wVSRControl) & 0x200) |
|
55 |
GUSvoice(wVSRControl) |= 0x100; /* voice stop request */ |
|
56 |
if (GUSvoice(wVSRVolRampControl) & 0x200) |
|
57 |
GUSvoice(wVSRVolRampControl) |= 0x100; /* Volume ramp stop request */ |
|
58 |
if (!(GUSvoice(wVSRControl) & GUSvoice(wVSRVolRampControl) & 0x100)) /* neither voice nor volume calculation active - save some time here ;) */ |
|
59 |
{ |
|
60 |
unsigned int sample; |
|
61 |
|
|
62 |
unsigned int LoopStart = (GUSvoice(wVSRLoopStartHi) << 16) | GUSvoice(wVSRLoopStartLo); /* 23.9 format */ |
|
63 |
unsigned int LoopEnd = (GUSvoice(wVSRLoopEndHi) << 16) | GUSvoice(wVSRLoopEndLo); /* 23.9 format */ |
|
64 |
unsigned int CurrPos = (GUSvoice(wVSRCurrPosHi) << 16) | GUSvoice(wVSRCurrPosLo); /* 23.9 format */ |
|
65 |
int VoiceIncrement = ((((unsigned long) GUSvoice(wVSRFreq) * 44100) / playback_freq) * (14 >> 1)) / |
|
66 |
((GUSregb(NumVoices) & 31) + 1); /* 6.10 increment/frame to 23.9 increment/sample */ |
|
67 |
|
|
68 |
int PanningPos = (GUSvoice(wVSRPanning) >> 8) & 0xf; |
|
69 |
|
|
70 |
unsigned int Volume32 = 32 * GUSvoice(wVSRCurrVol); /* 32 times larger than original gus for maintaining precision while ramping */ |
|
71 |
unsigned int StartVol32 = (GUSvoice(wVSRVolRampStartVol) & 0xff00) * 32; |
|
72 |
unsigned int EndVol32 = (GUSvoice(wVSRVolRampEndVol) & 0xff00) * 32; |
|
73 |
int VolumeIncrement32 = (32 * 16 * (GUSvoice(wVSRVolRampRate) & 0x3f00) >> 8) >> ((((GUSvoice(wVSRVolRampRate) & 0xc000) >> 8) >> 6) * 3); /* including 1/8/64/512 volume speed divisor */ |
|
74 |
VolumeIncrement32 = (((VolumeIncrement32 * 44100 / 2) / playback_freq) * 14) / ((GUSregb(NumVoices) & 31) + 1); /* adjust ramping speed to playback speed */ |
|
75 |
|
|
76 |
if (GUSvoice(wVSRControl) & 0x4000) |
|
77 |
VoiceIncrement = -VoiceIncrement; /* reverse playback */ |
|
78 |
if (GUSvoice(wVSRVolRampControl) & 0x4000) |
|
79 |
VolumeIncrement32 = -VolumeIncrement32; /* reverse ramping */ |
|
80 |
|
|
81 |
for (sample = 0; sample < numsamples; sample++) |
|
82 |
{ |
|
83 |
int sample1, sample2, Volume; |
|
84 |
if (GUSvoice(wVSRControl) & 0x400) /* 16bit */ |
|
85 |
{ |
|
86 |
int offset = ((CurrPos >> 9) & 0xc0000) + (((CurrPos >> 9) & 0x1ffff) << 1); |
|
87 |
GUSchar *adr; |
|
88 |
adr = (GUSchar *) state->himemaddr + offset; |
|
89 |
sample1 = (*adr & 0xff) + (*(adr + 1) * 256); |
|
90 |
sample2 = (*(adr + 2) & 0xff) + (*(adr + 2 + 1) * 256); |
|
91 |
} |
|
92 |
else /* 8bit */ |
|
93 |
{ |
|
94 |
int offset = (CurrPos >> 9) & 0xfffff; |
|
95 |
GUSchar *adr; |
|
96 |
adr = (GUSchar *) state->himemaddr + offset; |
|
97 |
sample1 = (*adr) * 256; |
|
98 |
sample2 = (*(adr + 1)) * 256; |
|
99 |
} |
|
100 |
|
|
101 |
Volume = ((((Volume32 >> (4 + 5)) & 0xff) + 256) << (Volume32 >> ((4 + 8) + 5))) / 512; /* semi-logarithmic volume, +5 due to additional precision */ |
|
102 |
sample1 = (((sample1 * Volume) >> 16) * (512 - (CurrPos % 512))) / 512; |
|
103 |
sample2 = (((sample2 * Volume) >> 16) * (CurrPos % 512)) / 512; |
|
104 |
sample1 += sample2; |
|
105 |
|
|
106 |
if (!(GUSvoice(wVSRVolRampControl) & 0x100)) |
|
107 |
{ |
|
108 |
Volume32 += VolumeIncrement32; |
|
109 |
if ((GUSvoice(wVSRVolRampControl) & 0x4000) ? (Volume32 <= StartVol32) : (Volume32 >= EndVol32)) /* ramp up boundary cross */ |
|
110 |
{ |
|
111 |
if (GUSvoice(wVSRVolRampControl) & 0x2000) |
|
112 |
GUSvoice(wVSRVolRampControl) |= 0x8000; /* volramp IRQ enabled? -> IRQ wait flag */ |
|
113 |
if (GUSvoice(wVSRVolRampControl) & 0x800) /* loop enabled */ |
|
114 |
{ |
|
115 |
if (GUSvoice(wVSRVolRampControl) & 0x1000) /* bidir. loop */ |
|
116 |
{ |
|
117 |
GUSvoice(wVSRVolRampControl) ^= 0x4000; /* toggle dir */ |
|
118 |
VolumeIncrement32 = -VolumeIncrement32; |
|
119 |
} |
|
120 |
else |
|
121 |
Volume32 = (GUSvoice(wVSRVolRampControl) & 0x4000) ? EndVol32 : StartVol32; /* unidir. loop ramp */ |
|
122 |
} |
|
123 |
else |
|
124 |
{ |
|
125 |
GUSvoice(wVSRVolRampControl) |= 0x100; |
|
126 |
Volume32 = |
|
127 |
(GUSvoice(wVSRVolRampControl) & 0x4000) ? StartVol32 : EndVol32; |
|
128 |
} |
|
129 |
} |
|
130 |
} |
|
131 |
if ((GUSvoice(wVSRVolRampControl) & 0xa000) == 0xa000) /* volramp IRQ set and enabled? */ |
|
132 |
{ |
|
133 |
GUSregd(voicevolrampirq) |= 1 << Voice; /* set irq slot */ |
|
134 |
} |
|
135 |
else |
|
136 |
{ |
|
137 |
GUSregd(voicevolrampirq) &= (~(1 << Voice)); /* clear irq slot */ |
|
138 |
GUSvoice(wVSRVolRampControl) &= 0x7f00; |
|
139 |
} |
|
140 |
|
|
141 |
if (!(GUSvoice(wVSRControl) & 0x100)) |
|
142 |
{ |
|
143 |
CurrPos += VoiceIncrement; |
|
144 |
if ((GUSvoice(wVSRControl) & 0x4000) ? (CurrPos <= LoopStart) : (CurrPos >= LoopEnd)) /* playback boundary cross */ |
|
145 |
{ |
|
146 |
if (GUSvoice(wVSRControl) & 0x2000) |
|
147 |
GUSvoice(wVSRControl) |= 0x8000; /* voice IRQ enabled -> IRQ wait flag */ |
|
148 |
if (GUSvoice(wVSRControl) & 0x800) /* loop enabled */ |
|
149 |
{ |
|
150 |
if (GUSvoice(wVSRControl) & 0x1000) /* pingpong loop */ |
|
151 |
{ |
|
152 |
GUSvoice(wVSRControl) ^= 0x4000; /* toggle dir */ |
|
153 |
VoiceIncrement = -VoiceIncrement; |
|
154 |
} |
|
155 |
else |
|
156 |
CurrPos = (GUSvoice(wVSRControl) & 0x4000) ? LoopEnd : LoopStart; /* unidir. loop */ |
|
157 |
} |
|
158 |
else if (!(GUSvoice(wVSRVolRampControl) & 0x400)) |
|
159 |
GUSvoice(wVSRControl) |= 0x100; /* loop disabled, rollover check */ |
|
160 |
} |
|
161 |
} |
|
162 |
if ((GUSvoice(wVSRControl) & 0xa000) == 0xa000) /* wavetable IRQ set and enabled? */ |
|
163 |
{ |
|
164 |
GUSregd(voicewavetableirq) |= 1 << Voice; /* set irq slot */ |
|
165 |
} |
|
166 |
else |
|
167 |
{ |
|
168 |
GUSregd(voicewavetableirq) &= (~(1 << Voice)); /* clear irq slot */ |
|
169 |
GUSvoice(wVSRControl) &= 0x7f00; |
|
170 |
} |
|
171 |
|
|
172 |
/* mix samples into buffer */ |
|
173 |
*(bufferpos + 2 * sample) += (short) ((sample1 * PanningPos) >> 4); /* right */ |
|
174 |
*(bufferpos + 2 * sample + 1) += (short) ((sample1 * (15 - PanningPos)) >> 4); /* left */ |
|
175 |
} |
|
176 |
/* write back voice and volume */ |
|
177 |
GUSvoice(wVSRCurrVol) = Volume32 / 32; |
|
178 |
GUSvoice(wVSRCurrPosHi) = CurrPos >> 16; |
|
179 |
GUSvoice(wVSRCurrPosLo) = CurrPos & 0xffff; |
|
180 |
} |
|
181 |
voiceptr += 16; /* next voice */ |
|
182 |
} |
|
183 |
} |
|
184 |
|
|
185 |
void gus_irqgen(GUSEmuState * state, unsigned int elapsed_time) |
|
186 |
/* time given in microseconds */ |
|
187 |
{ |
|
188 |
int requestedIRQs = 0; |
|
189 |
GUSbyte *gusptr; |
|
190 |
gusptr = state->gusdatapos; |
|
191 |
if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */ |
|
192 |
{ |
|
193 |
unsigned int timer1fraction = state->timer1fraction; |
|
194 |
int newtimerirqs; |
|
195 |
newtimerirqs = (elapsed_time + timer1fraction) / (80 * (256 - GUSregb(GUS46Counter1))); |
|
196 |
state->timer1fraction = (elapsed_time + timer1fraction) % (80 * (256 - GUSregb(GUS46Counter1))); |
|
197 |
if (newtimerirqs) |
|
198 |
{ |
|
199 |
if (!(GUSregb(TimerDataReg2x9) & 0x40)) |
|
200 |
GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */ |
|
201 |
if (GUSregb(GUS45TimerCtrl) & 4) /* timer1 irq enable */ |
|
202 |
{ |
|
203 |
GUSregb(TimerStatus2x8) |= 4; /* nonmaskable bit */ |
|
204 |
GUSregb(IRQStatReg2x6) |= 4; /* timer 1 irq pending */ |
|
205 |
GUSregw(TimerIRQs) += newtimerirqs; |
|
206 |
requestedIRQs += newtimerirqs; |
|
207 |
} |
|
208 |
} |
|
209 |
} |
|
210 |
if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */ |
|
211 |
{ |
|
212 |
unsigned int timer2fraction = state->timer2fraction; |
|
213 |
int newtimerirqs; |
|
214 |
newtimerirqs = (elapsed_time + timer2fraction) / (320 * (256 - GUSregb(GUS47Counter2))); |
|
215 |
state->timer2fraction = (elapsed_time + timer2fraction) % (320 * (256 - GUSregb(GUS47Counter2))); |
|
216 |
if (newtimerirqs) |
|
217 |
{ |
|
218 |
if (!(GUSregb(TimerDataReg2x9) & 0x20)) |
|
219 |
GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */ |
|
220 |
if (GUSregb(GUS45TimerCtrl) & 8) /* timer2 irq enable */ |
|
221 |
{ |
|
222 |
GUSregb(TimerStatus2x8) |= 2; /* nonmaskable bit */ |
|
223 |
GUSregb(IRQStatReg2x6) |= 8; /* timer 2 irq pending */ |
|
224 |
GUSregw(TimerIRQs) += newtimerirqs; |
|
225 |
requestedIRQs += newtimerirqs; |
|
226 |
} |
|
227 |
} |
|
228 |
} |
|
229 |
if (GUSregb(GUS4cReset) & 0x4) /* synth IRQ enable */ |
|
230 |
{ |
|
231 |
if (GUSregd(voicewavetableirq)) |
|
232 |
GUSregb(IRQStatReg2x6) |= 0x20; |
|
233 |
if (GUSregd(voicevolrampirq)) |
|
234 |
GUSregb(IRQStatReg2x6) |= 0x40; |
|
235 |
} |
|
236 |
if ((!requestedIRQs) && GUSregb(IRQStatReg2x6)) |
|
237 |
requestedIRQs++; |
|
238 |
if (GUSregb(IRQStatReg2x6)) |
|
239 |
GUSregw(BusyTimerIRQs) = GUS_irqrequest(state, state->gusirq, requestedIRQs); |
|
240 |
} |
b/hw/gustate.h | ||
---|---|---|
1 |
/* |
|
2 |
* GUSEMU32 - persistent GUS register state |
|
3 |
* |
|
4 |
* Copyright (C) 2000-2007 Tibor "TS" Schütz |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 |
* of this software and associated documentation files (the "Software"), to deal |
|
8 |
* in the Software without restriction, including without limitation the rights |
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 |
* copies of the Software, and to permit persons to whom the Software is |
|
11 |
* furnished to do so, subject to the following conditions: |
|
12 |
* |
|
13 |
* The above copyright notice and this permission notice shall be included in |
|
14 |
* all copies or substantial portions of the Software. |
|
15 |
* |
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 |
* THE SOFTWARE. |
|
23 |
*/ |
|
24 |
|
|
25 |
#ifndef GUSTATE_H |
|
26 |
#define GUSTATE_H |
|
27 |
|
|
28 |
/*state block offset*/ |
|
29 |
#define gusdata (0) |
|
30 |
|
|
31 |
/* data stored using this structure is in host byte order! */ |
|
32 |
|
|
33 |
/*access type*/ |
|
34 |
#define PortRead (0) |
|
35 |
#define PortWrite (1) |
|
36 |
|
|
37 |
#define Port8Bitacc (0) |
|
38 |
#define Port16Bitacc (1) |
|
39 |
|
|
40 |
/*voice register offsets (in bytes)*/ |
|
41 |
#define VSRegs (0) |
|
42 |
#define VSRControl (0) |
|
43 |
#define VSRegsEnd (VSRControl+VSRegs + 32*(16*2)) |
|
44 |
#define VSRFreq (2) |
|
45 |
#define VSRLoopStartHi (4) |
|
46 |
#define VSRLoopStartLo (6) |
|
47 |
#define VSRLoopEndHi (8) |
|
48 |
#define VSRLoopEndLo (10) |
|
49 |
#define VSRVolRampRate (12) |
|
50 |
#define VSRVolRampStartVol (14) |
|
51 |
#define VSRVolRampEndVol (16) |
|
52 |
#define VSRCurrVol (18) |
|
53 |
#define VSRCurrPosHi (20) |
|
54 |
#define VSRCurrPosLo (22) |
|
55 |
#define VSRPanning (24) |
|
56 |
#define VSRVolRampControl (26) |
|
57 |
|
|
58 |
/*voice register offsets (in words)*/ |
|
59 |
#define wVSRegs (0) |
|
60 |
#define wVSRControl (0) |
|
61 |
#define wVSRegsEnd (wVSRControl+wVSRegs + 32*(16)) |
|
62 |
#define wVSRFreq (1) |
|
63 |
#define wVSRLoopStartHi (2) |
|
64 |
#define wVSRLoopStartLo (3) |
|
65 |
#define wVSRLoopEndHi (4) |
|
66 |
#define wVSRLoopEndLo (5) |
|
67 |
#define wVSRVolRampRate (6) |
|
68 |
#define wVSRVolRampStartVol (7) |
|
69 |
#define wVSRVolRampEndVol (8) |
|
70 |
#define wVSRCurrVol (9) |
|
71 |
#define wVSRCurrPosHi (10) |
|
72 |
#define wVSRCurrPosLo (11) |
|
73 |
#define wVSRPanning (12) |
|
74 |
#define wVSRVolRampControl (13) |
|
75 |
|
|
76 |
/*GUS register state block: 32 voices, padding filled with remaining registers*/ |
|
77 |
#define DataRegLoByte3x4 (VSRVolRampControl+2) |
|
78 |
#define DataRegWord3x4 (DataRegLoByte3x4) |
|
79 |
#define DataRegHiByte3x5 (VSRVolRampControl+2 +1) |
|
80 |
#define DMA_2xB (VSRVolRampControl+2+2) |
|
81 |
#define IRQ_2xB (VSRVolRampControl+2+3) |
|
82 |
|
|
83 |
#define RegCtrl_2xF (VSRVolRampControl+2+(16*2)) |
|
84 |
#define Jumper_2xB (VSRVolRampControl+2+(16*2)+1) |
|
85 |
#define GUS42DMAStart (VSRVolRampControl+2+(16*2)+2) |
|
86 |
|
|
87 |
#define GUS43DRAMIOlo (VSRVolRampControl+2+(16*2)*2) |
|
88 |
#define GUSDRAMPOS24bit (GUS43DRAMIOlo) |
|
89 |
#define GUS44DRAMIOhi (VSRVolRampControl+2+(16*2)*2+2) |
|
90 |
|
|
91 |
#define voicewavetableirq (VSRVolRampControl+2+(16*2)*3) /* voice IRQ pseudoqueue: 1 bit per voice */ |
|
92 |
|
|
93 |
#define voicevolrampirq (VSRVolRampControl+2+(16*2)*4) /* voice IRQ pseudoqueue: 1 bit per voice */ |
|
94 |
|
|
95 |
#define startvoices (VSRVolRampControl+2+(16*2)*5) /* statistics / optimizations */ |
|
96 |
|
|
97 |
#define IRQStatReg2x6 (VSRVolRampControl+2+(16*2)*6) |
|
98 |
#define TimerStatus2x8 (VSRVolRampControl+2+(16*2)*6+1) |
|
99 |
#define TimerDataReg2x9 (VSRVolRampControl+2+(16*2)*6+2) |
|
100 |
#define MixerCtrlReg2x0 (VSRVolRampControl+2+(16*2)*6+3) |
|
101 |
|
|
102 |
#define VoiceSelReg3x2 (VSRVolRampControl+2+(16*2)*7) |
|
103 |
#define FunkSelReg3x3 (VSRVolRampControl+2+(16*2)*7+1) |
|
104 |
#define AdLibStatus2x8 (VSRVolRampControl+2+(16*2)*7+2) |
|
105 |
#define StatRead_2xF (VSRVolRampControl+2+(16*2)*7+3) |
|
106 |
|
|
107 |
#define GUS48SampSpeed (VSRVolRampControl+2+(16*2)*8) |
|
108 |
#define GUS41DMACtrl (VSRVolRampControl+2+(16*2)*8+1) |
|
109 |
#define GUS45TimerCtrl (VSRVolRampControl+2+(16*2)*8+2) |
|
110 |
#define GUS46Counter1 (VSRVolRampControl+2+(16*2)*8+3) |
|
111 |
|
|
112 |
#define GUS47Counter2 (VSRVolRampControl+2+(16*2)*9) |
|
113 |
#define GUS49SampCtrl (VSRVolRampControl+2+(16*2)*9+1) |
|
114 |
#define GUS4cReset (VSRVolRampControl+2+(16*2)*9+2) |
|
115 |
#define NumVoices (VSRVolRampControl+2+(16*2)*9+3) |
|
116 |
|
|
117 |
#define TimerIRQs (VSRVolRampControl+2+(16*2)*10) /* delayed IRQ, statistics */ |
|
118 |
#define BusyTimerIRQs (VSRVolRampControl+2+(16*2)*10+2) /* delayed IRQ, statistics */ |
|
119 |
|
|
120 |
#define AdLibCommand2xA (VSRVolRampControl+2+(16*2)*11) |
|
121 |
#define AdLibData2x9 (VSRVolRampControl+2+(16*2)*11+1) |
|
122 |
#define SB2xCd (VSRVolRampControl+2+(16*2)*11+2) |
|
123 |
#define SB2xE (VSRVolRampControl+2+(16*2)*11+3) |
|
124 |
|
|
125 |
#define SynVoiceIRQ8f (VSRVolRampControl+2+(16*2)*12) |
|
126 |
#define GUS50DMAHigh (VSRVolRampControl+2+(16*2)*12+1) |
|
127 |
|
|
128 |
#define portaccesses (VSRegsEnd) /* statistics / suspend mode */ |
|
129 |
|
|
130 |
#define gusdataend (VSRegsEnd+4) |
|
131 |
|
|
132 |
#endif /* gustate.h */ |
b/qemu-doc.texi | ||
---|---|---|
175 | 175 |
|
176 | 176 |
SMP is supported with up to 255 CPUs. |
177 | 177 |
|
178 |
Note that adlib and ac97 are only available when QEMU was configured
|
|
179 |
with --enable-adlib, --enable-ac97 respectively. |
|
178 |
Note that adlib, ac97 and gus are only available when QEMU was configured
|
|
179 |
with --enable-adlib, --enable-ac97 or --enable-gus respectively.
|
|
180 | 180 |
|
181 | 181 |
QEMU uses the PC BIOS from the Bochs project and the Plex86/Bochs LGPL |
182 | 182 |
VGA BIOS. |
183 | 183 |
|
184 | 184 |
QEMU uses YM3812 emulation by Tatsuyuki Satoh. |
185 | 185 |
|
186 |
QEMU uses GUS emulation(GUSEMU32) by Tibor "TS" Schütz. |
|
187 |
|
|
186 | 188 |
@c man end |
187 | 189 |
|
188 | 190 |
@node pcsys_quickstart |
Also available in: Unified diff