Speech bubbles can point down right.
[scummvm-innocent.git] / sound / softsynth / opl / dosbox.cpp
blob25fda7f2e6a39a9df993676c228e2e3ba6cb7223
1 /* ScummVM - Graphic Adventure Engine
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $URL$
22 * $Id$
26 * Based on AdLib emulation code of DOSBox
27 * Copyright (C) 2002-2009 The DOSBox Team
28 * Licensed under GPLv2+
29 * http://www.dosbox.com
32 #ifndef DISABLE_DOSBOX_OPL
34 #include "dosbox.h"
36 #include "common/system.h"
37 #include "common/scummsys.h"
39 #include <math.h>
40 #include <string.h>
42 namespace OPL {
43 namespace DOSBox {
45 Timer::Timer() {
46 masked = false;
47 overflow = false;
48 enabled = false;
49 counter = 0;
50 delay = 0;
53 void Timer::update(double time) {
54 if (!enabled || !delay)
55 return;
56 double deltaStart = time - startTime;
57 // Only set the overflow flag when not masked
58 if (deltaStart >= 0 && !masked)
59 overflow = 1;
62 void Timer::reset(double time) {
63 overflow = false;
64 if (!delay || !enabled)
65 return;
66 double delta = (time - startTime);
67 double rem = fmod(delta, delay);
68 double next = delay - rem;
69 startTime = time + next;
72 void Timer::stop() {
73 enabled = false;
76 void Timer::start(double time, int scale) {
77 //Don't enable again
78 if (enabled)
79 return;
80 enabled = true;
81 delay = 0.001 * (256 - counter) * scale;
82 startTime = time + delay;
85 bool Chip::write(uint32 reg, uint8 val) {
86 switch (reg) {
87 case 0x02:
88 timer[0].counter = val;
89 return true;
90 case 0x03:
91 timer[1].counter = val;
92 return true;
93 case 0x04:
94 double time = g_system->getMillis() / 1000.0;
96 if (val & 0x80) {
97 timer[0].reset(time);
98 timer[1].reset(time);
99 } else {
100 timer[0].update(time);
101 timer[1].update(time);
103 if (val & 0x1)
104 timer[0].start(time, 80);
105 else
106 timer[0].stop();
108 timer[0].masked = (val & 0x40) > 0;
110 if (timer[0].masked)
111 timer[0].overflow = false;
113 if (val & 0x2)
114 timer[1].start(time, 320);
115 else
116 timer[1].stop();
118 timer[1].masked = (val & 0x20) > 0;
120 if (timer[1].masked)
121 timer[1].overflow = false;
123 return true;
125 return false;
128 uint8 Chip::read() {
129 double time = g_system->getMillis() / 1000.0;
131 timer[0].update(time);
132 timer[1].update(time);
134 uint8 ret = 0;
135 // Overflow won't be set if a channel is masked
136 if (timer[0].overflow) {
137 ret |= 0x40;
138 ret |= 0x80;
140 if (timer[1].overflow) {
141 ret |= 0x20;
142 ret |= 0x80;
144 return ret;
147 namespace OPL2 {
148 #include "opl_impl.h"
150 struct Handler : public DOSBox::Handler {
151 void writeReg(uint32 reg, uint8 val) {
152 adlib_write(reg, val);
155 uint32 writeAddr(uint32 port, uint8 val) {
156 return val;
159 void generate(int16 *chan, uint samples) {
160 adlib_getsample(chan, samples);
163 void init(uint rate) {
164 adlib_init(rate);
167 } // end of namespace OPL2
169 namespace OPL3 {
170 #define OPLTYPE_IS_OPL3
171 #include "opl_impl.h"
173 struct Handler : public DOSBox::Handler {
174 void writeReg(uint32 reg, uint8 val) {
175 adlib_write(reg, val);
178 uint32 writeAddr(uint32 port, uint8 val) {
179 adlib_write_index(port, val);
180 return index;
183 void generate(int16 *chan, uint samples) {
184 adlib_getsample(chan, samples);
187 void init(uint rate) {
188 adlib_init(rate);
191 } // end of namespace OPL3
193 OPL::OPL(Config::OplType type) : _type(type), _rate(0), _handler(0) {
196 OPL::~OPL() {
197 free();
200 void OPL::free() {
201 delete _handler;
202 _handler = 0;
205 bool OPL::init(int rate) {
206 free();
208 memset(&_reg, 0, sizeof(_reg));
209 memset(_chip, 0, sizeof(_chip));
211 switch (_type) {
212 case Config::kOpl2:
213 _handler = new OPL2::Handler();
214 break;
216 case Config::kDualOpl2:
217 case Config::kOpl3:
218 _handler = new OPL3::Handler();
219 break;
221 default:
222 return false;
225 _handler->init(rate);
227 if (_type == Config::kDualOpl2) {
228 // Setup opl3 mode in the hander
229 _handler->writeReg(0x105, 1);
232 _rate = rate;
233 return true;
236 void OPL::reset() {
237 init(_rate);
240 void OPL::write(int port, int val) {
241 if (port&1) {
242 switch (_type) {
243 case Config::kOpl2:
244 case Config::kOpl3:
245 if (!_chip[0].write(_reg.normal, val))
246 _handler->writeReg(_reg.normal, val);
247 break;
248 case Config::kDualOpl2:
249 // Not a 0x??8 port, then write to a specific port
250 if (!(port & 0x8)) {
251 byte index = (port & 2) >> 1;
252 dualWrite(index, _reg.dual[index], val);
253 } else {
254 //Write to both ports
255 dualWrite(0, _reg.dual[0], val);
256 dualWrite(1, _reg.dual[1], val);
258 break;
260 } else {
261 // Ask the handler to write the address
262 // Make sure to clip them in the right range
263 switch (_type) {
264 case Config::kOpl2:
265 _reg.normal = _handler->writeAddr(port, val) & 0xff;
266 break;
267 case Config::kOpl3:
268 _reg.normal = _handler->writeAddr(port, val) & 0x1ff;
269 break;
270 case Config::kDualOpl2:
271 // Not a 0x?88 port, when write to a specific side
272 if (!(port & 0x8)) {
273 byte index = (port & 2) >> 1;
274 _reg.dual[index] = val & 0xff;
275 } else {
276 _reg.dual[0] = val & 0xff;
277 _reg.dual[1] = val & 0xff;
279 break;
284 byte OPL::read(int port) {
285 switch (_type) {
286 case Config::kOpl2:
287 if (!(port & 1))
288 //Make sure the low bits are 6 on opl2
289 return _chip[0].read() | 0x6;
290 break;
291 case Config::kOpl3:
292 if (!(port & 1))
293 return _chip[0].read();
294 break;
295 case Config::kDualOpl2:
296 // Only return for the lower ports
297 if (port & 1)
298 return 0xff;
299 // Make sure the low bits are 6 on opl2
300 return _chip[(port >> 1) & 1].read() | 0x6;
302 return 0;
305 void OPL::writeReg(int r, int v) {
306 byte tempReg = 0;
307 switch (_type) {
308 case Config::kOpl2:
309 case Config::kDualOpl2:
310 case Config::kOpl3:
311 // We can't use _handler->writeReg here directly, since it would miss timer changes.
313 // Backup old setup register
314 tempReg = _reg.normal;
316 // We need to set the register we want to write to via port 0x388
317 write(0x388, r);
318 // Do the real writing to the register
319 write(0x389, v);
320 // Restore the old register
321 write(0x388, tempReg);
322 break;
326 void OPL::dualWrite(uint8 index, uint8 reg, uint8 val) {
327 // Make sure you don't use opl3 features
328 // Don't allow write to disable opl3
329 if (reg == 5)
330 return;
332 // Only allow 4 waveforms
333 if (reg >= 0xE0 && reg <= 0xE8)
334 val &= 3;
336 // Write to the timer?
337 if (_chip[index].write(reg, val))
338 return;
340 // Enabling panning
341 if (reg >= 0xC0 && reg <= 0xC8) {
342 val &= 15;
343 val |= index ? 0xA0 : 0x50;
346 uint32 fullReg = reg + (index ? 0x100 : 0);
347 _handler->writeReg(fullReg, val);
350 void OPL::readBuffer(int16 *buffer, int length) {
351 // For stereo OPL cards, we divide the sample count by 2,
352 // to match stereo AudioStream behavior.
353 if (_type != Config::kOpl2)
354 length >>= 1;
356 _handler->generate(buffer, length);
359 } // end of namespace DOSBox
360 } // end of namespace OPL
362 #endif // !DISABLE_DOSBOX_ADLIB