root / hw / adlib.c @ 71be0fc3
History | View | Annotate | Download (7.4 kB)
1 | 85571bc7 | bellard | /*
|
---|---|---|---|
2 | 85571bc7 | bellard | * QEMU Adlib emulation
|
3 | 85571bc7 | bellard | *
|
4 | 85571bc7 | bellard | * Copyright (c) 2004 Vassili Karpov (malc)
|
5 | 85571bc7 | bellard | *
|
6 | 85571bc7 | bellard | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | 85571bc7 | bellard | * of this software and associated documentation files (the "Software"), to deal
|
8 | 85571bc7 | bellard | * in the Software without restriction, including without limitation the rights
|
9 | 85571bc7 | bellard | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | 85571bc7 | bellard | * copies of the Software, and to permit persons to whom the Software is
|
11 | 85571bc7 | bellard | * furnished to do so, subject to the following conditions:
|
12 | 85571bc7 | bellard | *
|
13 | 85571bc7 | bellard | * The above copyright notice and this permission notice shall be included in
|
14 | 85571bc7 | bellard | * all copies or substantial portions of the Software.
|
15 | 85571bc7 | bellard | *
|
16 | 85571bc7 | bellard | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | 85571bc7 | bellard | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | 85571bc7 | bellard | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | 85571bc7 | bellard | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | 85571bc7 | bellard | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | 85571bc7 | bellard | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | 85571bc7 | bellard | * THE SOFTWARE.
|
23 | 85571bc7 | bellard | */
|
24 | 85571bc7 | bellard | #include "vl.h" |
25 | 85571bc7 | bellard | |
26 | fb065187 | bellard | #define dolog(...) AUD_log ("adlib", __VA_ARGS__) |
27 | fb065187 | bellard | #ifdef DEBUG
|
28 | fb065187 | bellard | #define ldebug(...) dolog (__VA_ARGS__)
|
29 | fb065187 | bellard | #else
|
30 | fb065187 | bellard | #define ldebug(...)
|
31 | fb065187 | bellard | #endif
|
32 | 85571bc7 | bellard | |
33 | 85571bc7 | bellard | #ifdef USE_YMF262
|
34 | 85571bc7 | bellard | #define HAS_YMF262 1 |
35 | 85571bc7 | bellard | #include "ymf262.h" |
36 | 85571bc7 | bellard | void YMF262UpdateOneQEMU(int which, INT16 *dst, int length); |
37 | 85571bc7 | bellard | #define SHIFT 2 |
38 | 85571bc7 | bellard | #else
|
39 | 85571bc7 | bellard | #include "fmopl.h" |
40 | 85571bc7 | bellard | #define SHIFT 1 |
41 | 85571bc7 | bellard | #endif
|
42 | 85571bc7 | bellard | |
43 | 85571bc7 | bellard | #ifdef _WIN32
|
44 | 85571bc7 | bellard | #include <windows.h> |
45 | 85571bc7 | bellard | #define small_delay() Sleep (1) |
46 | 85571bc7 | bellard | #else
|
47 | 85571bc7 | bellard | #define small_delay() usleep (1) |
48 | 85571bc7 | bellard | #endif
|
49 | 85571bc7 | bellard | |
50 | 85571bc7 | bellard | #define IO_READ_PROTO(name) \
|
51 | 85571bc7 | bellard | uint32_t name (void *opaque, uint32_t nport)
|
52 | 85571bc7 | bellard | #define IO_WRITE_PROTO(name) \
|
53 | 85571bc7 | bellard | void name (void *opaque, uint32_t nport, uint32_t val) |
54 | 85571bc7 | bellard | |
55 | 85571bc7 | bellard | static struct { |
56 | 85571bc7 | bellard | int port;
|
57 | 85571bc7 | bellard | int freq;
|
58 | 85571bc7 | bellard | } conf = {0x220, 44100}; |
59 | 85571bc7 | bellard | |
60 | 85571bc7 | bellard | typedef struct { |
61 | 85571bc7 | bellard | int enabled;
|
62 | 85571bc7 | bellard | int active;
|
63 | 85571bc7 | bellard | int cparam;
|
64 | 85571bc7 | bellard | int64_t ticks; |
65 | 85571bc7 | bellard | int bufpos;
|
66 | 85571bc7 | bellard | int16_t *mixbuf; |
67 | 85571bc7 | bellard | double interval;
|
68 | 85571bc7 | bellard | QEMUTimer *ts, *opl_ts; |
69 | 85571bc7 | bellard | SWVoice *voice; |
70 | 85571bc7 | bellard | int left, pos, samples, bytes_per_second, old_free;
|
71 | 85571bc7 | bellard | int refcount;
|
72 | 85571bc7 | bellard | #ifndef USE_YMF262
|
73 | 85571bc7 | bellard | FM_OPL *opl; |
74 | 85571bc7 | bellard | #endif
|
75 | 85571bc7 | bellard | } AdlibState; |
76 | 85571bc7 | bellard | |
77 | 85571bc7 | bellard | static AdlibState adlib;
|
78 | 85571bc7 | bellard | |
79 | 85571bc7 | bellard | static IO_WRITE_PROTO(adlib_write)
|
80 | 85571bc7 | bellard | { |
81 | 85571bc7 | bellard | AdlibState *s = opaque; |
82 | 85571bc7 | bellard | int a = nport & 3; |
83 | 85571bc7 | bellard | int status;
|
84 | 85571bc7 | bellard | |
85 | 85571bc7 | bellard | s->ticks = qemu_get_clock (vm_clock); |
86 | 85571bc7 | bellard | s->active = 1;
|
87 | 85571bc7 | bellard | AUD_enable (s->voice, 1);
|
88 | 85571bc7 | bellard | |
89 | 85571bc7 | bellard | #ifdef USE_YMF262
|
90 | 85571bc7 | bellard | status = YMF262Write (0, a, val);
|
91 | 85571bc7 | bellard | #else
|
92 | 85571bc7 | bellard | status = OPLWrite (s->opl, a, val); |
93 | 85571bc7 | bellard | #endif
|
94 | 85571bc7 | bellard | } |
95 | 85571bc7 | bellard | |
96 | 85571bc7 | bellard | static IO_READ_PROTO(adlib_read)
|
97 | 85571bc7 | bellard | { |
98 | 85571bc7 | bellard | AdlibState *s = opaque; |
99 | 85571bc7 | bellard | uint8_t data; |
100 | 85571bc7 | bellard | int a = nport & 3; |
101 | 85571bc7 | bellard | |
102 | 85571bc7 | bellard | #ifdef USE_YMF262
|
103 | 85571bc7 | bellard | (void) s;
|
104 | 85571bc7 | bellard | data = YMF262Read (0, a);
|
105 | 85571bc7 | bellard | #else
|
106 | 85571bc7 | bellard | data = OPLRead (s->opl, a); |
107 | 85571bc7 | bellard | #endif
|
108 | 85571bc7 | bellard | return data;
|
109 | 85571bc7 | bellard | } |
110 | 85571bc7 | bellard | |
111 | 85571bc7 | bellard | static void OPL_timer (void *opaque) |
112 | 85571bc7 | bellard | { |
113 | 85571bc7 | bellard | AdlibState *s = opaque; |
114 | 85571bc7 | bellard | #ifdef USE_YMF262
|
115 | 85571bc7 | bellard | YMF262TimerOver (s->cparam >> 1, s->cparam & 1); |
116 | 85571bc7 | bellard | #else
|
117 | 85571bc7 | bellard | OPLTimerOver (s->opl, s->cparam); |
118 | 85571bc7 | bellard | #endif
|
119 | 85571bc7 | bellard | qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval); |
120 | 85571bc7 | bellard | } |
121 | 85571bc7 | bellard | |
122 | 85571bc7 | bellard | static void YMF262TimerHandler (int c, double interval_Sec) |
123 | 85571bc7 | bellard | { |
124 | 85571bc7 | bellard | AdlibState *s = &adlib; |
125 | 85571bc7 | bellard | if (interval_Sec == 0.0) { |
126 | 85571bc7 | bellard | qemu_del_timer (s->opl_ts); |
127 | 85571bc7 | bellard | return;
|
128 | 85571bc7 | bellard | } |
129 | 85571bc7 | bellard | s->cparam = c; |
130 | 85571bc7 | bellard | s->interval = ticks_per_sec * interval_Sec; |
131 | 85571bc7 | bellard | qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval); |
132 | 85571bc7 | bellard | small_delay (); |
133 | 85571bc7 | bellard | } |
134 | 85571bc7 | bellard | |
135 | 85571bc7 | bellard | static int write_audio (AdlibState *s, int samples) |
136 | 85571bc7 | bellard | { |
137 | 85571bc7 | bellard | int net = 0; |
138 | 85571bc7 | bellard | int ss = samples;
|
139 | 85571bc7 | bellard | while (samples) {
|
140 | 85571bc7 | bellard | int nbytes = samples << SHIFT;
|
141 | 85571bc7 | bellard | int wbytes = AUD_write (s->voice,
|
142 | 85571bc7 | bellard | s->mixbuf + (s->pos << (SHIFT - 1)),
|
143 | 85571bc7 | bellard | nbytes); |
144 | 85571bc7 | bellard | int wsampl = wbytes >> SHIFT;
|
145 | 85571bc7 | bellard | samples -= wsampl; |
146 | 85571bc7 | bellard | s->pos = (s->pos + wsampl) % s->samples; |
147 | 85571bc7 | bellard | net += wsampl; |
148 | 85571bc7 | bellard | if (!wbytes)
|
149 | 85571bc7 | bellard | break;
|
150 | 85571bc7 | bellard | } |
151 | 85571bc7 | bellard | if (net > ss) {
|
152 | 85571bc7 | bellard | dolog ("WARNING: net > ss\n");
|
153 | 85571bc7 | bellard | } |
154 | 85571bc7 | bellard | return net;
|
155 | 85571bc7 | bellard | } |
156 | 85571bc7 | bellard | |
157 | 85571bc7 | bellard | static void timer (void *opaque) |
158 | 85571bc7 | bellard | { |
159 | 85571bc7 | bellard | AdlibState *s = opaque; |
160 | 85571bc7 | bellard | int elapsed, samples, net = 0; |
161 | 85571bc7 | bellard | |
162 | 85571bc7 | bellard | if (s->refcount)
|
163 | 85571bc7 | bellard | dolog ("refcount=%d\n", s->refcount);
|
164 | 85571bc7 | bellard | |
165 | 85571bc7 | bellard | s->refcount += 1;
|
166 | 85571bc7 | bellard | if (!(s->active && s->enabled))
|
167 | 85571bc7 | bellard | goto reset;
|
168 | 85571bc7 | bellard | |
169 | 85571bc7 | bellard | AUD_run (); |
170 | 85571bc7 | bellard | |
171 | 85571bc7 | bellard | while (s->left) {
|
172 | 85571bc7 | bellard | int written = write_audio (s, s->left);
|
173 | 85571bc7 | bellard | net += written; |
174 | 85571bc7 | bellard | if (!written)
|
175 | 85571bc7 | bellard | goto reset2;
|
176 | 85571bc7 | bellard | s->left -= written; |
177 | 85571bc7 | bellard | } |
178 | 85571bc7 | bellard | s->pos = 0;
|
179 | 85571bc7 | bellard | |
180 | 85571bc7 | bellard | elapsed = AUD_calc_elapsed (s->voice); |
181 | 85571bc7 | bellard | if (!elapsed)
|
182 | 85571bc7 | bellard | goto reset2;
|
183 | 85571bc7 | bellard | |
184 | 85571bc7 | bellard | /* elapsed = AUD_get_free (s->voice); */
|
185 | 85571bc7 | bellard | samples = elapsed >> SHIFT; |
186 | 85571bc7 | bellard | if (!samples)
|
187 | 85571bc7 | bellard | goto reset2;
|
188 | 85571bc7 | bellard | |
189 | 85571bc7 | bellard | samples = audio_MIN (samples, s->samples - s->pos); |
190 | 85571bc7 | bellard | if (s->left)
|
191 | 85571bc7 | bellard | dolog ("left=%d samples=%d elapsed=%d free=%d\n",
|
192 | 85571bc7 | bellard | s->left, samples, elapsed, AUD_get_free (s->voice)); |
193 | 85571bc7 | bellard | |
194 | 85571bc7 | bellard | if (!samples)
|
195 | 85571bc7 | bellard | goto reset2;
|
196 | 85571bc7 | bellard | |
197 | 85571bc7 | bellard | #ifdef USE_YMF262
|
198 | 85571bc7 | bellard | YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples); |
199 | 85571bc7 | bellard | #else
|
200 | 85571bc7 | bellard | YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples); |
201 | 85571bc7 | bellard | #endif
|
202 | 85571bc7 | bellard | |
203 | 85571bc7 | bellard | while (samples) {
|
204 | 85571bc7 | bellard | int written = write_audio (s, samples);
|
205 | 85571bc7 | bellard | net += written; |
206 | 85571bc7 | bellard | if (!written)
|
207 | 85571bc7 | bellard | break;
|
208 | 85571bc7 | bellard | samples -= written; |
209 | 85571bc7 | bellard | } |
210 | 85571bc7 | bellard | if (!samples)
|
211 | 85571bc7 | bellard | s->pos = 0;
|
212 | 85571bc7 | bellard | s->left = samples; |
213 | 85571bc7 | bellard | |
214 | 85571bc7 | bellard | reset2:
|
215 | 85571bc7 | bellard | AUD_adjust (s->voice, net << SHIFT); |
216 | 85571bc7 | bellard | reset:
|
217 | 85571bc7 | bellard | qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + ticks_per_sec / 1024);
|
218 | 85571bc7 | bellard | s->refcount -= 1;
|
219 | 85571bc7 | bellard | } |
220 | 85571bc7 | bellard | |
221 | 85571bc7 | bellard | static void Adlib_fini (AdlibState *s) |
222 | 85571bc7 | bellard | { |
223 | 85571bc7 | bellard | #ifdef USE_YMF262
|
224 | 85571bc7 | bellard | YMF262Shutdown (); |
225 | 85571bc7 | bellard | #else
|
226 | 85571bc7 | bellard | if (s->opl) {
|
227 | 85571bc7 | bellard | OPLDestroy (s->opl); |
228 | 85571bc7 | bellard | s->opl = NULL;
|
229 | 85571bc7 | bellard | } |
230 | 85571bc7 | bellard | #endif
|
231 | 85571bc7 | bellard | |
232 | 85571bc7 | bellard | if (s->opl_ts)
|
233 | 85571bc7 | bellard | qemu_free_timer (s->opl_ts); |
234 | 85571bc7 | bellard | |
235 | 85571bc7 | bellard | if (s->ts)
|
236 | 85571bc7 | bellard | qemu_free_timer (s->ts); |
237 | 85571bc7 | bellard | |
238 | 85571bc7 | bellard | #define maybe_free(p) if (p) qemu_free (p) |
239 | 85571bc7 | bellard | maybe_free (s->mixbuf); |
240 | 85571bc7 | bellard | #undef maybe_free
|
241 | 85571bc7 | bellard | |
242 | 85571bc7 | bellard | s->active = 0;
|
243 | 85571bc7 | bellard | s->enabled = 0;
|
244 | 85571bc7 | bellard | } |
245 | 85571bc7 | bellard | |
246 | 85571bc7 | bellard | void Adlib_init (void) |
247 | 85571bc7 | bellard | { |
248 | 85571bc7 | bellard | AdlibState *s = &adlib; |
249 | 85571bc7 | bellard | |
250 | 85571bc7 | bellard | memset (s, 0, sizeof (*s)); |
251 | 85571bc7 | bellard | |
252 | 85571bc7 | bellard | #ifdef USE_YMF262
|
253 | 85571bc7 | bellard | if (YMF262Init (1, 14318180, conf.freq)) { |
254 | 85571bc7 | bellard | dolog ("YMF262Init %d failed\n", conf.freq);
|
255 | 85571bc7 | bellard | return;
|
256 | 85571bc7 | bellard | } |
257 | 85571bc7 | bellard | else {
|
258 | 85571bc7 | bellard | YMF262SetTimerHandler (0, YMF262TimerHandler, 0); |
259 | 85571bc7 | bellard | s->enabled = 1;
|
260 | 85571bc7 | bellard | } |
261 | 85571bc7 | bellard | #else
|
262 | 85571bc7 | bellard | s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
|
263 | 85571bc7 | bellard | if (!s->opl) {
|
264 | 85571bc7 | bellard | dolog ("OPLCreate %d failed\n", conf.freq);
|
265 | 85571bc7 | bellard | return;
|
266 | 85571bc7 | bellard | } |
267 | 85571bc7 | bellard | else {
|
268 | 85571bc7 | bellard | OPLSetTimerHandler (s->opl, YMF262TimerHandler, 0);
|
269 | 85571bc7 | bellard | s->enabled = 1;
|
270 | 85571bc7 | bellard | } |
271 | 85571bc7 | bellard | #endif
|
272 | 85571bc7 | bellard | |
273 | 85571bc7 | bellard | s->opl_ts = qemu_new_timer (vm_clock, OPL_timer, s); |
274 | 85571bc7 | bellard | if (!s->opl_ts) {
|
275 | 85571bc7 | bellard | dolog ("Can not get timer for adlib emulation\n");
|
276 | 85571bc7 | bellard | Adlib_fini (s); |
277 | 85571bc7 | bellard | return;
|
278 | 85571bc7 | bellard | } |
279 | 85571bc7 | bellard | |
280 | 85571bc7 | bellard | s->ts = qemu_new_timer (vm_clock, timer, s); |
281 | 85571bc7 | bellard | if (!s->opl_ts) {
|
282 | 85571bc7 | bellard | dolog ("Can not get timer for adlib emulation\n");
|
283 | 85571bc7 | bellard | Adlib_fini (s); |
284 | 85571bc7 | bellard | return;
|
285 | 85571bc7 | bellard | } |
286 | 85571bc7 | bellard | |
287 | 85571bc7 | bellard | s->voice = AUD_open (s->voice, "adlib", conf.freq, SHIFT, AUD_FMT_S16);
|
288 | 85571bc7 | bellard | if (!s->voice) {
|
289 | 85571bc7 | bellard | Adlib_fini (s); |
290 | 85571bc7 | bellard | return;
|
291 | 85571bc7 | bellard | } |
292 | 85571bc7 | bellard | |
293 | 85571bc7 | bellard | s->bytes_per_second = conf.freq << SHIFT; |
294 | 85571bc7 | bellard | s->samples = AUD_get_buffer_size (s->voice) >> SHIFT; |
295 | 85571bc7 | bellard | s->mixbuf = qemu_mallocz (s->samples << SHIFT); |
296 | 85571bc7 | bellard | |
297 | 85571bc7 | bellard | if (!s->mixbuf) {
|
298 | 85571bc7 | bellard | dolog ("not enough memory for adlib mixing buffer (%d)\n",
|
299 | 85571bc7 | bellard | s->samples << SHIFT); |
300 | 85571bc7 | bellard | Adlib_fini (s); |
301 | 85571bc7 | bellard | return;
|
302 | 85571bc7 | bellard | } |
303 | 85571bc7 | bellard | register_ioport_read (0x388, 4, 1, adlib_read, s); |
304 | 85571bc7 | bellard | register_ioport_write (0x388, 4, 1, adlib_write, s); |
305 | 85571bc7 | bellard | |
306 | 85571bc7 | bellard | register_ioport_read (conf.port, 4, 1, adlib_read, s); |
307 | 85571bc7 | bellard | register_ioport_write (conf.port, 4, 1, adlib_write, s); |
308 | 85571bc7 | bellard | |
309 | 85571bc7 | bellard | register_ioport_read (conf.port + 8, 2, 1, adlib_read, s); |
310 | 85571bc7 | bellard | register_ioport_write (conf.port + 8, 2, 1, adlib_write, s); |
311 | 85571bc7 | bellard | |
312 | 85571bc7 | bellard | qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
|
313 | 85571bc7 | bellard | } |