1 /* ScummVM - Graphic Adventure Engine
2  *
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.
6  *
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.
11  *
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.
16  *
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.
20  *
21  */
22 
23 /*
24  * Based on AdLib emulation code of DOSBox
25  * Copyright (C) 2002-2009  The DOSBox Team
26  * Licensed under GPLv2+
27  * http://www.dosbox.com
28  */
29 
30 #ifndef DISABLE_DOSBOX_OPL
31 
32 #include "dosbox.h"
33 #include "dbopl.h"
34 
35 #include "audio/mixer.h"
36 #include "common/system.h"
37 #include "common/scummsys.h"
38 #include "common/util.h"
39 
40 #include <math.h>
41 #include <string.h>
42 
43 namespace OPL {
44 namespace DOSBox {
45 
Timer()46 Timer::Timer() {
47 	masked = false;
48 	overflow = false;
49 	enabled = false;
50 	counter = 0;
51 	delay = 0;
52 }
53 
update(double time)54 void Timer::update(double time) {
55 	if (!enabled || !delay)
56 		return;
57 	double deltaStart = time - startTime;
58 	// Only set the overflow flag when not masked
59 	if (deltaStart >= 0 && !masked)
60 		overflow = 1;
61 }
62 
reset(double time)63 void Timer::reset(double time) {
64 	overflow = false;
65 	if (!delay || !enabled)
66 		return;
67 	double delta = (time - startTime);
68 	double rem = fmod(delta, delay);
69 	double next = delay - rem;
70 	startTime = time + next;
71 }
72 
stop()73 void Timer::stop() {
74 	enabled = false;
75 }
76 
start(double time,int scale)77 void Timer::start(double time, int scale) {
78 	//Don't enable again
79 	if (enabled)
80 		return;
81 	enabled = true;
82 	delay = 0.001 * (256 - counter) * scale;
83 	startTime = time + delay;
84 }
85 
write(uint32 reg,uint8 val)86 bool Chip::write(uint32 reg, uint8 val) {
87 	switch (reg) {
88 	case 0x02:
89 		timer[0].counter = val;
90 		return true;
91 	case 0x03:
92 		timer[1].counter = val;
93 		return true;
94 	case 0x04:
95 		{
96 			double time = g_system->getMillis() / 1000.0;
97 
98 			if (val & 0x80) {
99 				timer[0].reset(time);
100 				timer[1].reset(time);
101 			} else {
102 				timer[0].update(time);
103 				timer[1].update(time);
104 
105 				if (val & 0x1)
106 					timer[0].start(time, 80);
107 				else
108 					timer[0].stop();
109 
110 				timer[0].masked = (val & 0x40) > 0;
111 
112 				if (timer[0].masked)
113 					timer[0].overflow = false;
114 
115 				if (val & 0x2)
116 					timer[1].start(time, 320);
117 				else
118 					timer[1].stop();
119 
120 				timer[1].masked = (val & 0x20) > 0;
121 
122 				if (timer[1].masked)
123 					timer[1].overflow = false;
124 			}
125 		}
126 		return true;
127 	default:
128 		break;
129 	}
130 	return false;
131 }
132 
read()133 uint8 Chip::read() {
134 	double time = g_system->getMillis() / 1000.0;
135 
136 	timer[0].update(time);
137 	timer[1].update(time);
138 
139 	uint8 ret = 0;
140 	// Overflow won't be set if a channel is masked
141 	if (timer[0].overflow) {
142 		ret |= 0x40;
143 		ret |= 0x80;
144 	}
145 	if (timer[1].overflow) {
146 		ret |= 0x20;
147 		ret |= 0x80;
148 	}
149 	return ret;
150 }
151 
OPL(Config::OplType type)152 OPL::OPL(Config::OplType type) : _type(type), _rate(0), _emulator(0) {
153 }
154 
~OPL()155 OPL::~OPL() {
156 	stop();
157 	free();
158 }
159 
free()160 void OPL::free() {
161 	delete _emulator;
162 	_emulator = 0;
163 }
164 
init()165 bool OPL::init() {
166 	free();
167 
168 	memset(&_reg, 0, sizeof(_reg));
169 	ARRAYCLEAR(_chip);
170 
171 	_emulator = new DBOPL::Chip();
172 	if (!_emulator)
173 		return false;
174 
175 	DBOPL::InitTables();
176 	_rate = g_system->getMixer()->getOutputRate();
177 	_emulator->Setup(_rate);
178 
179 	if (_type == Config::kDualOpl2) {
180 		// Setup opl3 mode in the hander
181 		_emulator->WriteReg(0x105, 1);
182 	}
183 
184 	return true;
185 }
186 
reset()187 void OPL::reset() {
188 	init();
189 }
190 
write(int port,int val)191 void OPL::write(int port, int val) {
192 	if (port&1) {
193 		switch (_type) {
194 		case Config::kOpl2:
195 		case Config::kOpl3:
196 			if (!_chip[0].write(_reg.normal, val))
197 				_emulator->WriteReg(_reg.normal, val);
198 			break;
199 		case Config::kDualOpl2:
200 			// Not a 0x??8 port, then write to a specific port
201 			if (!(port & 0x8)) {
202 				byte index = (port & 2) >> 1;
203 				dualWrite(index, _reg.dual[index], val);
204 			} else {
205 				//Write to both ports
206 				dualWrite(0, _reg.dual[0], val);
207 				dualWrite(1, _reg.dual[1], val);
208 			}
209 			break;
210 		default:
211 			break;
212 		}
213 	} else {
214 		// Ask the handler to write the address
215 		// Make sure to clip them in the right range
216 		switch (_type) {
217 		case Config::kOpl2:
218 			_reg.normal = _emulator->WriteAddr(port, val) & 0xff;
219 			break;
220 		case Config::kOpl3:
221 			_reg.normal = _emulator->WriteAddr(port, val) & 0x1ff;
222 			break;
223 		case Config::kDualOpl2:
224 			// Not a 0x?88 port, when write to a specific side
225 			if (!(port & 0x8)) {
226 				byte index = (port & 2) >> 1;
227 				_reg.dual[index] = val & 0xff;
228 			} else {
229 				_reg.dual[0] = val & 0xff;
230 				_reg.dual[1] = val & 0xff;
231 			}
232 			break;
233 		default:
234 			break;
235 		}
236 	}
237 }
238 
read(int port)239 byte OPL::read(int port) {
240 	switch (_type) {
241 	case Config::kOpl2:
242 		if (!(port & 1))
243 			//Make sure the low bits are 6 on opl2
244 			return _chip[0].read() | 0x6;
245 		break;
246 	case Config::kOpl3:
247 		if (!(port & 1))
248 			return _chip[0].read();
249 		break;
250 	case Config::kDualOpl2:
251 		// Only return for the lower ports
252 		if (port & 1)
253 			return 0xff;
254 		// Make sure the low bits are 6 on opl2
255 		return _chip[(port >> 1) & 1].read() | 0x6;
256 	default:
257 		break;
258 	}
259 	return 0;
260 }
261 
writeReg(int r,int v)262 void OPL::writeReg(int r, int v) {
263 	int tempReg = 0;
264 	switch (_type) {
265 	case Config::kOpl2:
266 	case Config::kDualOpl2:
267 	case Config::kOpl3:
268 		// We can't use _handler->writeReg here directly, since it would miss timer changes.
269 
270 		// Backup old setup register
271 		tempReg = _reg.normal;
272 
273 		// We directly allow writing to secondary OPL3 registers by using
274 		// register values >= 0x100.
275 		if (_type == Config::kOpl3 && r >= 0x100) {
276 			// We need to set the register we want to write to via port 0x222,
277 			// since we want to write to the secondary register set.
278 			write(0x222, r);
279 			// Do the real writing to the register
280 			write(0x223, v);
281 		} else {
282 			// We need to set the register we want to write to via port 0x388
283 			write(0x388, r);
284 			// Do the real writing to the register
285 			write(0x389, v);
286 		}
287 
288 		// Restore the old register
289 		if (_type == Config::kOpl3 && tempReg >= 0x100) {
290 			write(0x222, tempReg & ~0x100);
291 		} else {
292 			write(0x388, tempReg);
293 		}
294 		break;
295 	default:
296 		break;
297 	};
298 }
299 
dualWrite(uint8 index,uint8 reg,uint8 val)300 void OPL::dualWrite(uint8 index, uint8 reg, uint8 val) {
301 	// Make sure you don't use opl3 features
302 	// Don't allow write to disable opl3
303 	if (reg == 5)
304 		return;
305 
306 	// Only allow 4 waveforms
307 	if (reg >= 0xE0 && reg <= 0xE8)
308 		val &= 3;
309 
310 	// Write to the timer?
311 	if (_chip[index].write(reg, val))
312 		return;
313 
314 	// Enabling panning
315 	if (reg >= 0xC0 && reg <= 0xC8) {
316 		val &= 15;
317 		val |= index ? 0xA0 : 0x50;
318 	}
319 
320 	uint32 fullReg = reg + (index ? 0x100 : 0);
321 	_emulator->WriteReg(fullReg, val);
322 }
323 
generateSamples(int16 * buffer,int length)324 void OPL::generateSamples(int16 *buffer, int length) {
325 	// For stereo OPL cards, we divide the sample count by 2,
326 	// to match stereo AudioStream behavior.
327 	if (_type != Config::kOpl2)
328 		length >>= 1;
329 
330 	const uint bufferLength = 512;
331 	int32 tempBuffer[bufferLength * 2];
332 
333 	if (_emulator->opl3Active) {
334 		while (length > 0) {
335 			const uint readSamples = MIN<uint>(length, bufferLength);
336 
337 			_emulator->GenerateBlock3(readSamples, tempBuffer);
338 
339 			for (uint i = 0; i < (readSamples << 1); ++i)
340 				buffer[i] = tempBuffer[i];
341 
342 			buffer += (readSamples << 1);
343 			length -= readSamples;
344 		}
345 	} else {
346 		while (length > 0) {
347 			const uint readSamples = MIN<uint>(length, bufferLength << 1);
348 
349 			_emulator->GenerateBlock2(readSamples, tempBuffer);
350 
351 			for (uint i = 0; i < readSamples; ++i)
352 				buffer[i] = tempBuffer[i];
353 
354 			buffer += readSamples;
355 			length -= readSamples;
356 		}
357 	}
358 }
359 
360 } // End of namespace DOSBox
361 } // End of namespace OPL
362 
363 #endif // !DISABLE_DOSBOX_ADLIB
364