master
c 311 lines 7.23 KB
Raw
1 /*
2 * QEMU Proxy for OPL2/3 emulation by MAME team
3 *
4 * Copyright (c) 2004-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
25 #include "qemu/osdep.h"
26 #include "qapi/error.h"
27 #include "qemu/module.h"
28 #include "hw/audio/model.h"
29 #include "qemu/audio.h"
30 #include "hw/isa/isa.h"
31 #include "hw/core/qdev-properties.h"
32 #include "qemu/error-report.h"
33 #include "qom/object.h"
34
35 #define DEBUG 0
36
37 #define ADLIB_DESC "Yamaha YM3812 (OPL2)"
38
39 #if DEBUG
40 #include "qemu/timer.h"
41 #endif
42
43 #define ldebug(fmt, ...) do { \
44 if (DEBUG) { \
45 error_report("adlib: " fmt, ##__VA_ARGS__); \
46 } \
47 } while (0)
48
49 #include "fmopl.h"
50 #define SHIFT 1
51
52 #define TYPE_ADLIB "adlib"
53 OBJECT_DECLARE_SIMPLE_TYPE(AdlibState, ADLIB)
54
55 struct AdlibState {
56 ISADevice parent_obj;
57
58 AudioBackend *audio_be;
59 uint32_t freq;
60 uint32_t port;
61 int ticking[2];
62 int enabled;
63 int active;
64 int bufpos;
65 #if DEBUG
66 int64_t exp[2];
67 #endif
68 int16_t *mixbuf;
69 SWVoiceOut *voice;
70 int left, pos, samples;
71 FM_OPL *opl;
72 PortioList port_list;
73 };
74
75 static void adlib_stop_opl_timer (AdlibState *s, size_t n)
76 {
77 OPLTimerOver (s->opl, n);
78 s->ticking[n] = 0;
79 }
80
81 static void adlib_kill_timers (AdlibState *s)
82 {
83 size_t i;
84
85 for (i = 0; i < 2; ++i) {
86 if (s->ticking[i]) {
87 adlib_stop_opl_timer(s, i);
88 }
89 }
90 }
91
92 static void adlib_write(void *opaque, uint32_t nport, uint32_t val)
93 {
94 AdlibState *s = opaque;
95 int a = nport & 3;
96
97 s->active = 1;
98 audio_be_set_active_out(s->audio_be, s->voice, 1);
99
100 adlib_kill_timers (s);
101
102 OPLWrite (s->opl, a, val);
103 }
104
105 static uint32_t adlib_read(void *opaque, uint32_t nport)
106 {
107 AdlibState *s = opaque;
108 int a = nport & 3;
109
110 adlib_kill_timers (s);
111 return OPLRead (s->opl, a);
112 }
113
114 static void timer_handler (void *opaque, int c, double interval_Sec)
115 {
116 AdlibState *s = opaque;
117 unsigned n = c & 1;
118 #if DEBUG
119 double interval;
120 int64_t exp;
121 #endif
122
123 if (interval_Sec == 0.0) {
124 s->ticking[n] = 0;
125 return;
126 }
127
128 s->ticking[n] = 1;
129 #if DEBUG
130 interval = NANOSECONDS_PER_SECOND * interval_Sec;
131 exp = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + interval;
132 s->exp[n] = exp;
133 #endif
134
135 }
136
137 static int write_audio (AdlibState *s, int samples)
138 {
139 int net = 0;
140 int pos = s->pos;
141
142 while (samples) {
143 int nbytes, wbytes, wsampl;
144
145 nbytes = samples << SHIFT;
146 wbytes = audio_be_write(
147 s->audio_be,
148 s->voice,
149 s->mixbuf + (pos << (SHIFT - 1)),
150 nbytes
151 );
152
153 if (wbytes) {
154 wsampl = wbytes >> SHIFT;
155
156 samples -= wsampl;
157 pos = (pos + wsampl) % s->samples;
158
159 net += wsampl;
160 }
161 else {
162 break;
163 }
164 }
165
166 return net;
167 }
168
169 static void adlib_callback (void *opaque, int free)
170 {
171 AdlibState *s = opaque;
172 int samples, to_play, written;
173
174 samples = free >> SHIFT;
175 if (!(s->active && s->enabled) || !samples) {
176 return;
177 }
178
179 to_play = MIN (s->left, samples);
180 while (to_play) {
181 written = write_audio (s, to_play);
182
183 if (written) {
184 s->left -= written;
185 samples -= written;
186 to_play -= written;
187 s->pos = (s->pos + written) % s->samples;
188 }
189 else {
190 return;
191 }
192 }
193
194 samples = MIN (samples, s->samples - s->pos);
195 if (!samples) {
196 return;
197 }
198
199 YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
200
201 while (samples) {
202 written = write_audio (s, samples);
203
204 if (written) {
205 samples -= written;
206 s->pos = (s->pos + written) % s->samples;
207 }
208 else {
209 s->left = samples;
210 return;
211 }
212 }
213 }
214
215 static void Adlib_fini (AdlibState *s)
216 {
217 if (s->opl) {
218 OPLDestroy (s->opl);
219 s->opl = NULL;
220 }
221
222 g_free(s->mixbuf);
223
224 s->active = 0;
225 s->enabled = 0;
226 }
227
228 static MemoryRegionPortio adlib_portio_list[] = {
229 { 0, 4, 1, .read = adlib_read, .write = adlib_write, },
230 { 0, 2, 1, .read = adlib_read, .write = adlib_write, },
231 { 0x388, 4, 1, .read = adlib_read, .write = adlib_write, },
232 PORTIO_END_OF_LIST(),
233 };
234
235 static void adlib_realizefn (DeviceState *dev, Error **errp)
236 {
237 AdlibState *s = ADLIB(dev);
238 struct audsettings as;
239
240 if (!audio_be_check(&s->audio_be, errp)) {
241 return;
242 }
243
244 s->opl = OPLCreate (3579545, s->freq);
245 if (!s->opl) {
246 error_setg (errp, "OPLCreate %d failed", s->freq);
247 return;
248 }
249 else {
250 OPLSetTimerHandler(s->opl, timer_handler, s);
251 s->enabled = 1;
252 }
253
254 as.freq = s->freq;
255 as.nchannels = SHIFT;
256 as.fmt = AUDIO_FORMAT_S16;
257 as.big_endian = HOST_BIG_ENDIAN;
258
259 s->voice = audio_be_open_out(
260 s->audio_be,
261 s->voice,
262 "adlib",
263 s,
264 adlib_callback,
265 &as
266 );
267 if (!s->voice) {
268 Adlib_fini (s);
269 error_setg (errp, "Initializing audio voice failed");
270 return;
271 }
272
273 s->samples = audio_be_get_buffer_size_out(s->audio_be, s->voice) >> SHIFT;
274 s->mixbuf = g_malloc0 (s->samples << SHIFT);
275
276 adlib_portio_list[0].offset = s->port;
277 adlib_portio_list[1].offset = s->port + 8;
278 portio_list_init (&s->port_list, OBJECT(s), adlib_portio_list, s, "adlib");
279 portio_list_add (&s->port_list, isa_address_space_io(&s->parent_obj), 0);
280 }
281
282 static const Property adlib_properties[] = {
283 DEFINE_AUDIO_PROPERTIES(AdlibState, audio_be),
284 DEFINE_PROP_UINT32 ("iobase", AdlibState, port, 0x220),
285 DEFINE_PROP_UINT32 ("freq", AdlibState, freq, 44100),
286 };
287
288 static void adlib_class_initfn(ObjectClass *klass, const void *data)
289 {
290 DeviceClass *dc = DEVICE_CLASS (klass);
291
292 dc->realize = adlib_realizefn;
293 set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
294 dc->desc = ADLIB_DESC;
295 device_class_set_props(dc, adlib_properties);
296 }
297
298 static const TypeInfo adlib_info = {
299 .name = TYPE_ADLIB,
300 .parent = TYPE_ISA_DEVICE,
301 .instance_size = sizeof (AdlibState),
302 .class_init = adlib_class_initfn,
303 };
304
305 static void adlib_register_types (void)
306 {
307 type_register_static (&adlib_info);
308 audio_register_model("adlib", ADLIB_DESC, TYPE_ADLIB);
309 }
310
311 type_init (adlib_register_types)