ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

dosbox.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 /*
00024  * Based on AdLib emulation code of DOSBox
00025  * Copyright (C) 2002-2009  The DOSBox Team
00026  * Licensed under GPLv2+
00027  * http://www.dosbox.com
00028  */
00029 
00030 #ifndef DISABLE_DOSBOX_OPL
00031 
00032 #include "dosbox.h"
00033 #include "dbopl.h"
00034 
00035 #include "audio/mixer.h"
00036 #include "common/system.h"
00037 #include "common/scummsys.h"
00038 #include "common/util.h"
00039 
00040 #include <math.h>
00041 #include <string.h>
00042 
00043 namespace OPL {
00044 namespace DOSBox {
00045 
00046 Timer::Timer() {
00047     masked = false;
00048     overflow = false;
00049     enabled = false;
00050     counter = 0;
00051     delay = 0;
00052 }
00053 
00054 void Timer::update(double time) {
00055     if (!enabled || !delay)
00056         return;
00057     double deltaStart = time - startTime;
00058     // Only set the overflow flag when not masked
00059     if (deltaStart >= 0 && !masked)
00060         overflow = 1;
00061 }
00062 
00063 void Timer::reset(double time) {
00064     overflow = false;
00065     if (!delay || !enabled)
00066         return;
00067     double delta = (time - startTime);
00068     double rem = fmod(delta, delay);
00069     double next = delay - rem;
00070     startTime = time + next;
00071 }
00072 
00073 void Timer::stop() {
00074     enabled = false;
00075 }
00076 
00077 void Timer::start(double time, int scale) {
00078     //Don't enable again
00079     if (enabled)
00080         return;
00081     enabled = true;
00082     delay = 0.001 * (256 - counter) * scale;
00083     startTime = time + delay;
00084 }
00085 
00086 bool Chip::write(uint32 reg, uint8 val) {
00087     switch (reg) {
00088     case 0x02:
00089         timer[0].counter = val;
00090         return true;
00091     case 0x03:
00092         timer[1].counter = val;
00093         return true;
00094     case 0x04:
00095         double time = g_system->getMillis() / 1000.0;
00096 
00097         if (val & 0x80) {
00098             timer[0].reset(time);
00099             timer[1].reset(time);
00100         } else {
00101             timer[0].update(time);
00102             timer[1].update(time);
00103 
00104             if (val & 0x1)
00105                 timer[0].start(time, 80);
00106             else
00107                 timer[0].stop();
00108 
00109             timer[0].masked = (val & 0x40) > 0;
00110 
00111             if (timer[0].masked)
00112                 timer[0].overflow = false;
00113 
00114             if (val & 0x2)
00115                 timer[1].start(time, 320);
00116             else
00117                 timer[1].stop();
00118 
00119             timer[1].masked = (val & 0x20) > 0;
00120 
00121             if (timer[1].masked)
00122                 timer[1].overflow = false;
00123         }
00124         return true;
00125     }
00126     return false;
00127 }
00128 
00129 uint8 Chip::read() {
00130     double time = g_system->getMillis() / 1000.0;
00131 
00132     timer[0].update(time);
00133     timer[1].update(time);
00134 
00135     uint8 ret = 0;
00136     // Overflow won't be set if a channel is masked
00137     if (timer[0].overflow) {
00138         ret |= 0x40;
00139         ret |= 0x80;
00140     }
00141     if (timer[1].overflow) {
00142         ret |= 0x20;
00143         ret |= 0x80;
00144     }
00145     return ret;
00146 }
00147 
00148 OPL::OPL(Config::OplType type) : _type(type), _rate(0), _emulator(0) {
00149 }
00150 
00151 OPL::~OPL() {
00152     stop();
00153     free();
00154 }
00155 
00156 void OPL::free() {
00157     delete _emulator;
00158     _emulator = 0;
00159 }
00160 
00161 bool OPL::init() {
00162     free();
00163 
00164     memset(&_reg, 0, sizeof(_reg));
00165     memset(_chip, 0, sizeof(_chip));
00166 
00167     _emulator = new DBOPL::Chip();
00168     if (!_emulator)
00169         return false;
00170 
00171     DBOPL::InitTables();
00172     _rate = g_system->getMixer()->getOutputRate();
00173     _emulator->Setup(_rate);
00174 
00175     if (_type == Config::kDualOpl2) {
00176         // Setup opl3 mode in the hander
00177         _emulator->WriteReg(0x105, 1);
00178     }
00179 
00180     return true;
00181 }
00182 
00183 void OPL::reset() {
00184     init();
00185 }
00186 
00187 void OPL::write(int port, int val) {
00188     if (port&1) {
00189         switch (_type) {
00190         case Config::kOpl2:
00191         case Config::kOpl3:
00192             if (!_chip[0].write(_reg.normal, val))
00193                 _emulator->WriteReg(_reg.normal, val);
00194             break;
00195         case Config::kDualOpl2:
00196             // Not a 0x??8 port, then write to a specific port
00197             if (!(port & 0x8)) {
00198                 byte index = (port & 2) >> 1;
00199                 dualWrite(index, _reg.dual[index], val);
00200             } else {
00201                 //Write to both ports
00202                 dualWrite(0, _reg.dual[0], val);
00203                 dualWrite(1, _reg.dual[1], val);
00204             }
00205             break;
00206         }
00207     } else {
00208         // Ask the handler to write the address
00209         // Make sure to clip them in the right range
00210         switch (_type) {
00211         case Config::kOpl2:
00212             _reg.normal = _emulator->WriteAddr(port, val) & 0xff;
00213             break;
00214         case Config::kOpl3:
00215             _reg.normal = _emulator->WriteAddr(port, val) & 0x1ff;
00216             break;
00217         case Config::kDualOpl2:
00218             // Not a 0x?88 port, when write to a specific side
00219             if (!(port & 0x8)) {
00220                 byte index = (port & 2) >> 1;
00221                 _reg.dual[index] = val & 0xff;
00222             } else {
00223                 _reg.dual[0] = val & 0xff;
00224                 _reg.dual[1] = val & 0xff;
00225             }
00226             break;
00227         }
00228     }
00229 }
00230 
00231 byte OPL::read(int port) {
00232     switch (_type) {
00233     case Config::kOpl2:
00234         if (!(port & 1))
00235             //Make sure the low bits are 6 on opl2
00236             return _chip[0].read() | 0x6;
00237         break;
00238     case Config::kOpl3:
00239         if (!(port & 1))
00240             return _chip[0].read();
00241         break;
00242     case Config::kDualOpl2:
00243         // Only return for the lower ports
00244         if (port & 1)
00245             return 0xff;
00246         // Make sure the low bits are 6 on opl2
00247         return _chip[(port >> 1) & 1].read() | 0x6;
00248     }
00249     return 0;
00250 }
00251 
00252 void OPL::writeReg(int r, int v) {
00253     int tempReg = 0;
00254     switch (_type) {
00255     case Config::kOpl2:
00256     case Config::kDualOpl2:
00257     case Config::kOpl3:
00258         // We can't use _handler->writeReg here directly, since it would miss timer changes.
00259 
00260         // Backup old setup register
00261         tempReg = _reg.normal;
00262 
00263         // We directly allow writing to secondary OPL3 registers by using
00264         // register values >= 0x100.
00265         if (_type == Config::kOpl3 && r >= 0x100) {
00266             // We need to set the register we want to write to via port 0x222,
00267             // since we want to write to the secondary register set.
00268             write(0x222, r);
00269             // Do the real writing to the register
00270             write(0x223, v);
00271         } else {
00272             // We need to set the register we want to write to via port 0x388
00273             write(0x388, r);
00274             // Do the real writing to the register
00275             write(0x389, v);
00276         }
00277 
00278         // Restore the old register
00279         if (_type == Config::kOpl3 && tempReg >= 0x100) {
00280             write(0x222, tempReg & ~0x100);
00281         } else {
00282             write(0x388, tempReg);
00283         }
00284         break;
00285     };
00286 }
00287 
00288 void OPL::dualWrite(uint8 index, uint8 reg, uint8 val) {
00289     // Make sure you don't use opl3 features
00290     // Don't allow write to disable opl3
00291     if (reg == 5)
00292         return;
00293 
00294     // Only allow 4 waveforms
00295     if (reg >= 0xE0 && reg <= 0xE8)
00296         val &= 3;
00297 
00298     // Write to the timer?
00299     if (_chip[index].write(reg, val))
00300         return;
00301 
00302     // Enabling panning
00303     if (reg >= 0xC0 && reg <= 0xC8) {
00304         val &= 15;
00305         val |= index ? 0xA0 : 0x50;
00306     }
00307 
00308     uint32 fullReg = reg + (index ? 0x100 : 0);
00309     _emulator->WriteReg(fullReg, val);
00310 }
00311 
00312 void OPL::generateSamples(int16 *buffer, int length) {
00313     // For stereo OPL cards, we divide the sample count by 2,
00314     // to match stereo AudioStream behavior.
00315     if (_type != Config::kOpl2)
00316         length >>= 1;
00317 
00318     const uint bufferLength = 512;
00319     int32 tempBuffer[bufferLength * 2];
00320 
00321     if (_emulator->opl3Active) {
00322         while (length > 0) {
00323             const uint readSamples = MIN<uint>(length, bufferLength);
00324 
00325             _emulator->GenerateBlock3(readSamples, tempBuffer);
00326 
00327             for (uint i = 0; i < (readSamples << 1); ++i)
00328                 buffer[i] = tempBuffer[i];
00329 
00330             buffer += (readSamples << 1);
00331             length -= readSamples;
00332         }
00333     } else {
00334         while (length > 0) {
00335             const uint readSamples = MIN<uint>(length, bufferLength << 1);
00336 
00337             _emulator->GenerateBlock2(readSamples, tempBuffer);
00338 
00339             for (uint i = 0; i < readSamples; ++i)
00340                 buffer[i] = tempBuffer[i];
00341 
00342             buffer += readSamples;
00343             length -= readSamples;
00344         }
00345     }
00346 }
00347 
00348 } // End of namespace DOSBox
00349 } // End of namespace OPL
00350 
00351 #endif // !DISABLE_DOSBOX_ADLIB


Generated on Sat Mar 16 2019 05:01:25 for ResidualVM by doxygen 1.7.1
curved edge   curved edge