DOSBox-X
|
00001 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher 00002 * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev 00003 * 00004 * This program is free software: you can redistribute it and/or modify 00005 * it under the terms of the GNU Lesser General Public License as published by 00006 * the Free Software Foundation, either version 2.1 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU Lesser General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU Lesser General Public License 00015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00016 */ 00017 00018 /* 00019 Some notes on this class: 00020 00021 This emulates the LA-32's implementation of "ramps". A ramp in this context is a smooth transition from one value to another, handled entirely within the LA-32. 00022 The LA-32 provides this feature for amplitude and filter cutoff values. 00023 00024 The 8095 starts ramps on the LA-32 by setting two values in memory-mapped registers: 00025 00026 (1) The target value (between 0 and 255) for the ramp to end on. This is represented by the "target" argument to startRamp(). 00027 (2) The speed at which that value should be approached. This is represented by the "increment" argument to startRamp(). 00028 00029 Once the ramp target value has been hit, the LA-32 raises an interrupt. 00030 00031 Note that the starting point of the ramp is whatever internal value the LA-32 had when the registers were set. This is usually the end point of a previously completed ramp. 00032 00033 Our handling of the "target" and "increment" values is based on sample analysis and a little guesswork. 00034 Here's what we're pretty confident about: 00035 - The most significant bit of "increment" indicates the direction that the LA32's current internal value ("current" in our emulation) should change in. 00036 Set means downward, clear means upward. 00037 - The lower 7 bits of "increment" indicate how quickly "current" should be changed. 00038 - If "increment" is 0, no change to "current" is made and no interrupt is raised. [SEMI-CONFIRMED by sample analysis] 00039 - Otherwise, if the MSb is set: 00040 - If "current" already corresponds to a value <= "target", "current" is set immediately to the equivalent of "target" and an interrupt is raised. 00041 - Otherwise, "current" is gradually reduced (at a rate determined by the lower 7 bits of "increment"), and once it reaches the equivalent of "target" an interrupt is raised. 00042 - Otherwise (the MSb is unset): 00043 - If "current" already corresponds to a value >= "target", "current" is set immediately to the equivalent of "target" and an interrupt is raised. 00044 - Otherwise, "current" is gradually increased (at a rate determined by the lower 7 bits of "increment"), and once it reaches the equivalent of "target" an interrupt is raised. 00045 00046 We haven't fully explored: 00047 - Values when ramping between levels (though this is probably correct). 00048 - Transition timing (may not be 100% accurate, especially for very fast ramps). 00049 */ 00050 #include <cmath> 00051 00052 #include "mt32emu.h" 00053 #include "LA32Ramp.h" 00054 #include "mmath.h" 00055 00056 namespace MT32Emu { 00057 00058 // SEMI-CONFIRMED from sample analysis. 00059 const int TARGET_MULT = 0x40000; 00060 const unsigned int MAX_CURRENT = 0xFF * TARGET_MULT; 00061 00062 // We simulate the delay in handling "target was reached" interrupts by waiting 00063 // this many samples before setting interruptRaised. 00064 // FIXME: This should vary with the sample rate, but doesn't. 00065 // SEMI-CONFIRMED: Since this involves asynchronous activity between the LA32 00066 // and the 8095, a good value is hard to pin down. 00067 // This one matches observed behaviour on a few digital captures I had handy, 00068 // and should be double-checked. We may also need a more sophisticated delay 00069 // scheme eventually. 00070 const int INTERRUPT_TIME = 7; 00071 00072 LA32Ramp::LA32Ramp() : 00073 current(0), 00074 largeTarget(0), 00075 largeIncrement(0), 00076 interruptCountdown(0), 00077 interruptRaised(false) { 00078 } 00079 00080 void LA32Ramp::startRamp(Bit8u target, Bit8u increment) { 00081 // CONFIRMED: From sample analysis, this appears to be very accurate. 00082 if (increment == 0) { 00083 largeIncrement = 0; 00084 } else { 00085 // Three bits in the fractional part, no need to interpolate 00086 // (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f) 00087 Bit32u expArg = increment & 0x7F; 00088 largeIncrement = 8191 - Tables::getInstance().exp9[~(expArg << 6) & 511]; 00089 largeIncrement <<= expArg >> 3; 00090 largeIncrement += 64; 00091 largeIncrement >>= 9; 00092 } 00093 descending = (increment & 0x80) != 0; 00094 if (descending) { 00095 // CONFIRMED: From sample analysis, descending increments are slightly faster 00096 largeIncrement++; 00097 } 00098 00099 largeTarget = target * TARGET_MULT; 00100 interruptCountdown = 0; 00101 interruptRaised = false; 00102 } 00103 00104 Bit32u LA32Ramp::nextValue() { 00105 if (interruptCountdown > 0) { 00106 if (--interruptCountdown == 0) { 00107 interruptRaised = true; 00108 } 00109 } else if (largeIncrement != 0) { 00110 // CONFIRMED from sample analysis: When increment is 0, the LA32 does *not* change the current value at all (and of course doesn't fire an interrupt). 00111 if (descending) { 00112 // Lowering current value 00113 if (largeIncrement > current) { 00114 current = largeTarget; 00115 interruptCountdown = INTERRUPT_TIME; 00116 } else { 00117 current -= largeIncrement; 00118 if (current <= largeTarget) { 00119 current = largeTarget; 00120 interruptCountdown = INTERRUPT_TIME; 00121 } 00122 } 00123 } else { 00124 // Raising current value 00125 if (MAX_CURRENT - current < largeIncrement) { 00126 current = largeTarget; 00127 interruptCountdown = INTERRUPT_TIME; 00128 } else { 00129 current += largeIncrement; 00130 if (current >= largeTarget) { 00131 current = largeTarget; 00132 interruptCountdown = INTERRUPT_TIME; 00133 } 00134 } 00135 } 00136 } 00137 return current; 00138 } 00139 00140 bool LA32Ramp::checkInterrupt() { 00141 bool wasRaised = interruptRaised; 00142 interruptRaised = false; 00143 return wasRaised; 00144 } 00145 00146 void LA32Ramp::reset() { 00147 current = 0; 00148 largeTarget = 0; 00149 largeIncrement = 0; 00150 descending = false; 00151 interruptCountdown = 0; 00152 interruptRaised = false; 00153 } 00154 00155 00156 void LA32Ramp::saveState( std::ostream &stream ) 00157 { 00158 stream.write(reinterpret_cast<const char*>(¤t), sizeof(current) ); 00159 stream.write(reinterpret_cast<const char*>(&largeTarget), sizeof(largeTarget) ); 00160 stream.write(reinterpret_cast<const char*>(&largeIncrement), sizeof(largeIncrement) ); 00161 stream.write(reinterpret_cast<const char*>(&descending), sizeof(descending) ); 00162 stream.write(reinterpret_cast<const char*>(&interruptCountdown), sizeof(interruptCountdown) ); 00163 stream.write(reinterpret_cast<const char*>(&interruptRaised), sizeof(interruptRaised) ); 00164 } 00165 00166 00167 void LA32Ramp::loadState( std::istream &stream ) 00168 { 00169 stream.read(reinterpret_cast<char*>(¤t), sizeof(current) ); 00170 stream.read(reinterpret_cast<char*>(&largeTarget), sizeof(largeTarget) ); 00171 stream.read(reinterpret_cast<char*>(&largeIncrement), sizeof(largeIncrement) ); 00172 stream.read(reinterpret_cast<char*>(&descending), sizeof(descending) ); 00173 stream.read(reinterpret_cast<char*>(&interruptCountdown), sizeof(interruptCountdown) ); 00174 stream.read(reinterpret_cast<char*>(&interruptRaised), sizeof(interruptRaised) ); 00175 } 00176 00177 }