BizHawk/waterbox/vb/vsu.cpp

499 lines
11 KiB
C++

/******************************************************************************/
/* Mednafen Virtual Boy Emulation Module */
/******************************************************************************/
/* vsu.cpp:
** Copyright (C) 2010-2016 Mednafen Team
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation, Inc.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "vb.h"
static const unsigned int Tap_LUT[8] = {15 - 1, 11 - 1, 14 - 1, 5 - 1, 9 - 1, 7 - 1, 10 - 1, 12 - 1};
VSU::VSU()
{
Synth.volume(1.0 / 6 / 2);
for (int ch = 0; ch < 6; ch++)
{
for (int lr = 0; lr < 2; lr++)
last_output[ch][lr] = 0;
}
}
VSU::~VSU()
{
}
void VSU::SetSoundRate(double rate)
{
for (int y = 0; y < 2; y++)
{
sbuf[y].set_sample_rate(rate ? rate : 44100, 50);
sbuf[y].clock_rate((long)(VB_MASTER_CLOCK / 4));
sbuf[y].bass_freq(20);
}
}
void VSU::Power(void)
{
SweepControl = 0;
SweepModCounter = 0;
SweepModClockDivider = 1;
for (int ch = 0; ch < 6; ch++)
{
IntlControl[ch] = 0;
LeftLevel[ch] = 0;
RightLevel[ch] = 0;
Frequency[ch] = 0;
EnvControl[ch] = 0;
RAMAddress[ch] = 0;
EffFreq[ch] = 0;
Envelope[ch] = 0;
WavePos[ch] = 0;
FreqCounter[ch] = 1;
IntervalCounter[ch] = 0;
EnvelopeCounter[ch] = 1;
EffectsClockDivider[ch] = 4800;
IntervalClockDivider[ch] = 4;
EnvelopeClockDivider[ch] = 4;
LatcherClockDivider[ch] = 120;
}
NoiseLatcherClockDivider = 120;
NoiseLatcher = 0;
memset(WaveData, 0, sizeof(WaveData));
memset(ModData, 0, sizeof(ModData));
last_ts = 0;
}
void VSU::Write(int32 timestamp, uint32 A, uint8 V)
{
A &= 0x7FF;
Update(timestamp);
//printf("VSU Write: %d, %08x %02x\n", timestamp, A, V);
if (A < 0x280)
WaveData[A >> 7][(A >> 2) & 0x1F] = V & 0x3F;
else if (A < 0x400)
{
//if(A >= 0x300)
// printf("Modulation mirror write? %08x %02x\n", A, V);
ModData[(A >> 2) & 0x1F] = V;
}
else if (A < 0x600)
{
int ch = (A >> 6) & 0xF;
//if(ch < 6)
//printf("Ch: %d, Reg: %d, Value: %02x\n", ch, (A >> 2) & 0xF, V);
if (ch > 5)
{
if (A == 0x580 && (V & 1))
{
//puts("STOP, HAMMER TIME");
for (int i = 0; i < 6; i++)
IntlControl[i] &= ~0x80;
}
}
else
switch ((A >> 2) & 0xF)
{
case 0x0:
IntlControl[ch] = V & ~0x40;
if (V & 0x80)
{
EffFreq[ch] = Frequency[ch];
if (ch == 5)
FreqCounter[ch] = 10 * (2048 - EffFreq[ch]);
else
FreqCounter[ch] = 2048 - EffFreq[ch];
IntervalCounter[ch] = (V & 0x1F) + 1;
EnvelopeCounter[ch] = (EnvControl[ch] & 0x7) + 1;
if (ch == 4)
{
SweepModCounter = (SweepControl >> 4) & 7;
SweepModClockDivider = (SweepControl & 0x80) ? 8 : 1;
ModWavePos = 0;
}
WavePos[ch] = 0;
if (ch == 5) // Not sure if this is correct.
lfsr = 1;
//if(!(IntlControl[ch] & 0x80))
// Envelope[ch] = (EnvControl[ch] >> 4) & 0xF;
EffectsClockDivider[ch] = 4800;
IntervalClockDivider[ch] = 4;
EnvelopeClockDivider[ch] = 4;
}
break;
case 0x1:
LeftLevel[ch] = (V >> 4) & 0xF;
RightLevel[ch] = (V >> 0) & 0xF;
break;
case 0x2:
Frequency[ch] &= 0xFF00;
Frequency[ch] |= V << 0;
EffFreq[ch] &= 0xFF00;
EffFreq[ch] |= V << 0;
break;
case 0x3:
Frequency[ch] &= 0x00FF;
Frequency[ch] |= (V & 0x7) << 8;
EffFreq[ch] &= 0x00FF;
EffFreq[ch] |= (V & 0x7) << 8;
break;
case 0x4:
EnvControl[ch] &= 0xFF00;
EnvControl[ch] |= V << 0;
Envelope[ch] = (V >> 4) & 0xF;
break;
case 0x5:
EnvControl[ch] &= 0x00FF;
if (ch == 4)
EnvControl[ch] |= (V & 0x73) << 8;
else if (ch == 5)
{
EnvControl[ch] |= (V & 0x73) << 8;
lfsr = 1;
}
else
EnvControl[ch] |= (V & 0x03) << 8;
break;
case 0x6:
RAMAddress[ch] = V & 0xF;
break;
case 0x7:
if (ch == 4)
{
SweepControl = V;
}
break;
}
}
}
INLINE void VSU::CalcCurrentOutput(int ch, int &left, int &right)
{
if (!(IntlControl[ch] & 0x80))
{
left = right = 0;
return;
}
int WD;
int l_ol, r_ol;
if (ch == 5)
WD = NoiseLatcher; //(NoiseLatcher << 6) - NoiseLatcher;
else
{
if (RAMAddress[ch] > 4)
WD = 0;
else
WD = WaveData[RAMAddress[ch]][WavePos[ch]]; // - 0x20;
}
l_ol = Envelope[ch] * LeftLevel[ch];
if (l_ol)
{
l_ol >>= 3;
l_ol += 1;
}
r_ol = Envelope[ch] * RightLevel[ch];
if (r_ol)
{
r_ol >>= 3;
r_ol += 1;
}
left = WD * l_ol;
right = WD * r_ol;
}
void VSU::Update(int32 timestamp)
{
//puts("VSU Start");
int left, right;
for (int ch = 0; ch < 6; ch++)
{
int32 clocks = timestamp - last_ts;
int32 running_timestamp = last_ts;
// Output sound here
CalcCurrentOutput(ch, left, right);
Synth.offset_inline(running_timestamp, left - last_output[ch][0], &sbuf[0]);
Synth.offset_inline(running_timestamp, right - last_output[ch][1], &sbuf[1]);
last_output[ch][0] = left;
last_output[ch][1] = right;
if (!(IntlControl[ch] & 0x80))
continue;
while (clocks > 0)
{
int32 chunk_clocks = clocks;
if (chunk_clocks > EffectsClockDivider[ch])
chunk_clocks = EffectsClockDivider[ch];
if (ch == 5)
{
if (chunk_clocks > NoiseLatcherClockDivider)
chunk_clocks = NoiseLatcherClockDivider;
}
else
{
if (EffFreq[ch] >= 2040)
{
if (chunk_clocks > LatcherClockDivider[ch])
chunk_clocks = LatcherClockDivider[ch];
}
else
{
if (chunk_clocks > FreqCounter[ch])
chunk_clocks = FreqCounter[ch];
}
}
if (ch == 5 && chunk_clocks > NoiseLatcherClockDivider)
chunk_clocks = NoiseLatcherClockDivider;
FreqCounter[ch] -= chunk_clocks;
while (FreqCounter[ch] <= 0)
{
if (ch == 5)
{
int feedback = ((lfsr >> 7) & 1) ^ ((lfsr >> Tap_LUT[(EnvControl[5] >> 12) & 0x7]) & 1) ^ 1;
lfsr = ((lfsr << 1) & 0x7FFF) | feedback;
FreqCounter[ch] += 10 * (2048 - EffFreq[ch]);
}
else
{
FreqCounter[ch] += 2048 - EffFreq[ch];
WavePos[ch] = (WavePos[ch] + 1) & 0x1F;
}
}
LatcherClockDivider[ch] -= chunk_clocks;
while (LatcherClockDivider[ch] <= 0)
LatcherClockDivider[ch] += 120;
if (ch == 5)
{
NoiseLatcherClockDivider -= chunk_clocks;
if (!NoiseLatcherClockDivider)
{
NoiseLatcherClockDivider = 120;
NoiseLatcher = ((lfsr & 1) << 6) - (lfsr & 1);
}
}
EffectsClockDivider[ch] -= chunk_clocks;
while (EffectsClockDivider[ch] <= 0)
{
EffectsClockDivider[ch] += 4800;
IntervalClockDivider[ch]--;
while (IntervalClockDivider[ch] <= 0)
{
IntervalClockDivider[ch] += 4;
if (IntlControl[ch] & 0x20)
{
IntervalCounter[ch]--;
if (!IntervalCounter[ch])
{
IntlControl[ch] &= ~0x80;
}
}
EnvelopeClockDivider[ch]--;
while (EnvelopeClockDivider[ch] <= 0)
{
EnvelopeClockDivider[ch] += 4;
if (EnvControl[ch] & 0x0100) // Enveloping enabled?
{
EnvelopeCounter[ch]--;
if (!EnvelopeCounter[ch])
{
EnvelopeCounter[ch] = (EnvControl[ch] & 0x7) + 1;
if (EnvControl[ch] & 0x0008) // Grow
{
if (Envelope[ch] < 0xF || (EnvControl[ch] & 0x200))
Envelope[ch] = (Envelope[ch] + 1) & 0xF;
}
else // Decay
{
if (Envelope[ch] > 0 || (EnvControl[ch] & 0x200))
Envelope[ch] = (Envelope[ch] - 1) & 0xF;
}
}
}
} // end while(EnvelopeClockDivider[ch] <= 0)
} // end while(IntervalClockDivider[ch] <= 0)
if (ch == 4)
{
SweepModClockDivider--;
while (SweepModClockDivider <= 0)
{
SweepModClockDivider += (SweepControl & 0x80) ? 8 : 1;
if (((SweepControl >> 4) & 0x7) && (EnvControl[ch] & 0x4000))
{
if (SweepModCounter)
SweepModCounter--;
if (!SweepModCounter)
{
SweepModCounter = (SweepControl >> 4) & 0x7;
if (EnvControl[ch] & 0x1000) // Modulation
{
if (ModWavePos < 32 || (EnvControl[ch] & 0x2000))
{
ModWavePos &= 0x1F;
EffFreq[ch] = (EffFreq[ch] + (int8)ModData[ModWavePos]);
if (EffFreq[ch] < 0)
{
//puts("Underflow");
EffFreq[ch] = 0;
}
else if (EffFreq[ch] > 0x7FF)
{
//puts("Overflow");
EffFreq[ch] = 0x7FF;
}
ModWavePos++;
}
//puts("Mod");
}
else // Sweep
{
int32 delta = EffFreq[ch] >> (SweepControl & 0x7);
int32 NewFreq = EffFreq[ch] + ((SweepControl & 0x8) ? delta : -delta);
//printf("Sweep(%d): Old: %d, New: %d\n", ch, EffFreq[ch], NewFreq);
if (NewFreq < 0)
EffFreq[ch] = 0;
else if (NewFreq > 0x7FF)
{
//EffFreq[ch] = 0x7FF;
IntlControl[ch] &= ~0x80;
}
else
EffFreq[ch] = NewFreq;
}
}
}
} // end while(SweepModClockDivider <= 0)
} // end if(ch == 4)
} // end while(EffectsClockDivider[ch] <= 0)
clocks -= chunk_clocks;
running_timestamp += chunk_clocks;
// Output sound here too.
CalcCurrentOutput(ch, left, right);
Synth.offset_inline(running_timestamp, left - last_output[ch][0], &sbuf[0]);
Synth.offset_inline(running_timestamp, right - last_output[ch][1], &sbuf[1]);
last_output[ch][0] = left;
last_output[ch][1] = right;
}
}
last_ts = timestamp;
//puts("VSU End");
}
int32 VSU::EndFrame(int32 timestamp, int16 *SoundBuf, int32 SoundBufMaxSize)
{
int32 ret = 0;
Update(timestamp);
last_ts = 0;
if (SoundBuf)
{
for (int y = 0; y < 2; y++)
{
sbuf[y].end_frame(timestamp);
ret = sbuf[y].read_samples(SoundBuf + y, SoundBufMaxSize, 1);
}
}
return ret;
}
uint8 VSU::PeekWave(const unsigned int which, uint32 Address)
{
assert(which <= 4);
Address &= 0x1F;
return (WaveData[which][Address]);
}
void VSU::PokeWave(const unsigned int which, uint32 Address, uint8 value)
{
assert(which <= 4);
Address &= 0x1F;
WaveData[which][Address] = value & 0x3F;
}
uint8 VSU::PeekModWave(uint32 Address)
{
Address &= 0x1F;
return (ModData[Address]);
}
void VSU::PokeModWave(uint32 Address, uint8 value)
{
Address &= 0x1F;
ModData[Address] = value & 0xFF;
}