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.
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
36 #include "common/system.h"
37 #include "common/scummsys.h"
53 void Timer::update(double time
) {
54 if (!enabled
|| !delay
)
56 double deltaStart
= time
- startTime
;
57 // Only set the overflow flag when not masked
58 if (deltaStart
>= 0 && !masked
)
62 void Timer::reset(double time
) {
64 if (!delay
|| !enabled
)
66 double delta
= (time
- startTime
);
67 double rem
= fmod(delta
, delay
);
68 double next
= delay
- rem
;
69 startTime
= time
+ next
;
76 void Timer::start(double time
, int scale
) {
81 delay
= 0.001 * (256 - counter
) * scale
;
82 startTime
= time
+ delay
;
85 bool Chip::write(uint32 reg
, uint8 val
) {
88 timer
[0].counter
= val
;
91 timer
[1].counter
= val
;
94 double time
= g_system
->getMillis() / 1000.0;
100 timer
[0].update(time
);
101 timer
[1].update(time
);
104 timer
[0].start(time
, 80);
108 timer
[0].masked
= (val
& 0x40) > 0;
111 timer
[0].overflow
= false;
114 timer
[1].start(time
, 320);
118 timer
[1].masked
= (val
& 0x20) > 0;
121 timer
[1].overflow
= false;
129 double time
= g_system
->getMillis() / 1000.0;
131 timer
[0].update(time
);
132 timer
[1].update(time
);
135 // Overflow won't be set if a channel is masked
136 if (timer
[0].overflow
) {
140 if (timer
[1].overflow
) {
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
) {
159 void generate(int16
*chan
, uint samples
) {
160 adlib_getsample(chan
, samples
);
163 void init(uint rate
) {
167 } // end of namespace OPL2
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
);
183 void generate(int16
*chan
, uint samples
) {
184 adlib_getsample(chan
, samples
);
187 void init(uint rate
) {
191 } // end of namespace OPL3
193 OPL::OPL(Config::OplType type
) : _type(type
), _rate(0), _handler(0) {
205 bool OPL::init(int rate
) {
208 memset(&_reg
, 0, sizeof(_reg
));
209 memset(_chip
, 0, sizeof(_chip
));
213 _handler
= new OPL2::Handler();
216 case Config::kDualOpl2
:
218 _handler
= new OPL3::Handler();
225 _handler
->init(rate
);
227 if (_type
== Config::kDualOpl2
) {
228 // Setup opl3 mode in the hander
229 _handler
->writeReg(0x105, 1);
240 void OPL::write(int port
, int val
) {
245 if (!_chip
[0].write(_reg
.normal
, val
))
246 _handler
->writeReg(_reg
.normal
, val
);
248 case Config::kDualOpl2
:
249 // Not a 0x??8 port, then write to a specific port
251 byte index
= (port
& 2) >> 1;
252 dualWrite(index
, _reg
.dual
[index
], val
);
254 //Write to both ports
255 dualWrite(0, _reg
.dual
[0], val
);
256 dualWrite(1, _reg
.dual
[1], val
);
261 // Ask the handler to write the address
262 // Make sure to clip them in the right range
265 _reg
.normal
= _handler
->writeAddr(port
, val
) & 0xff;
268 _reg
.normal
= _handler
->writeAddr(port
, val
) & 0x1ff;
270 case Config::kDualOpl2
:
271 // Not a 0x?88 port, when write to a specific side
273 byte index
= (port
& 2) >> 1;
274 _reg
.dual
[index
] = val
& 0xff;
276 _reg
.dual
[0] = val
& 0xff;
277 _reg
.dual
[1] = val
& 0xff;
284 byte
OPL::read(int port
) {
288 //Make sure the low bits are 6 on opl2
289 return _chip
[0].read() | 0x6;
293 return _chip
[0].read();
295 case Config::kDualOpl2
:
296 // Only return for the lower ports
299 // Make sure the low bits are 6 on opl2
300 return _chip
[(port
>> 1) & 1].read() | 0x6;
305 void OPL::writeReg(int r
, int v
) {
309 case Config::kDualOpl2
:
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
318 // Do the real writing to the register
320 // Restore the old register
321 write(0x388, tempReg
);
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
332 // Only allow 4 waveforms
333 if (reg
>= 0xE0 && reg
<= 0xE8)
336 // Write to the timer?
337 if (_chip
[index
].write(reg
, val
))
341 if (reg
>= 0xC0 && reg
<= 0xC8) {
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
)
356 _handler
->generate(buffer
, length
);
359 } // end of namespace DOSBox
360 } // end of namespace OPL
362 #endif // !DISABLE_DOSBOX_ADLIB