Improve Virtual Boy emulator (#2446)

This commit is contained in:
Floogle 2025-06-24 02:52:20 +02:00 committed by GitHub
parent d243bafc6f
commit 83c9d0a3ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 88 additions and 48 deletions

View file

@ -51,6 +51,8 @@ void VSU::Power(void)
SweepControl = 0; SweepControl = 0;
SweepModCounter = 0; SweepModCounter = 0;
SweepModClockDivider = 1; SweepModClockDivider = 1;
ModState = 0;
ModLock = 0;
for(int ch = 0; ch < 6; ch++) for(int ch = 0; ch < 6; ch++)
{ {
@ -62,7 +64,9 @@ void VSU::Power(void)
RAMAddress[ch] = 0; RAMAddress[ch] = 0;
EffFreq[ch] = 0; EffFreq[ch] = 0;
Envelope[ch] = 0; EnvelopeReload[ch] = 0;
EnvelopeValue[ch] = 0;
EnvelopeModMask[ch] = 0;
WavePos[ch] = 0; WavePos[ch] = 0;
FreqCounter[ch] = 1; FreqCounter[ch] = 1;
IntervalCounter[ch] = 0; IntervalCounter[ch] = 0;
@ -100,6 +104,8 @@ void VSU::Write(int timestamp, unsigned int A, unsigned char V)
Update(timestamp); Update(timestamp);
ModLock = 0;
//printf("VSU Write: %d, %08x %02x\n", timestamp, A, V); //printf("VSU Write: %d, %08x %02x\n", timestamp, A, V);
if(A < 0x280) if(A < 0x280)
@ -133,7 +139,6 @@ void VSU::Write(int timestamp, unsigned int A, unsigned char V)
if(V & 0x80) if(V & 0x80)
{ {
EffFreq[ch] = Frequency[ch];
if(ch == 5) if(ch == 5)
FreqCounter[ch] = 10 * (2048 - EffFreq[ch]); FreqCounter[ch] = 10 * (2048 - EffFreq[ch]);
else else
@ -146,6 +151,7 @@ void VSU::Write(int timestamp, unsigned int A, unsigned char V)
SweepModCounter = (SweepControl >> 4) & 7; SweepModCounter = (SweepControl >> 4) & 7;
SweepModClockDivider = (SweepControl & 0x80) ? 8 : 1; SweepModClockDivider = (SweepControl & 0x80) ? 8 : 1;
ModWavePos = 0; ModWavePos = 0;
ModState = 0;
} }
WavePos[ch] = 0; WavePos[ch] = 0;
@ -153,8 +159,11 @@ void VSU::Write(int timestamp, unsigned int A, unsigned char V)
if(ch == 5) // Not sure if this is correct. if(ch == 5) // Not sure if this is correct.
lfsr = 1; lfsr = 1;
//if(!(IntlControl[ch] & 0x80)) EnvelopeModMask[ch] = 0;
// Envelope[ch] = (EnvControl[ch] >> 4) & 0xF; if(!(EnvControl[ch] & 0x200) && (
(EnvelopeValue[ch] == 0 && !(EnvControl[ch] & 0x0008)) ||
(EnvelopeValue[ch] == 0xF && (EnvControl[ch] & 0x0008))))
EnvelopeModMask[ch] = 1;
EffectsClockDivider[ch] = 4800; EffectsClockDivider[ch] = 4800;
IntervalClockDivider[ch] = 4; IntervalClockDivider[ch] = 4;
@ -170,18 +179,24 @@ void VSU::Write(int timestamp, unsigned int A, unsigned char V)
Frequency[ch] |= V << 0; Frequency[ch] |= V << 0;
EffFreq[ch] &= 0xFF00; EffFreq[ch] &= 0xFF00;
EffFreq[ch] |= V << 0; EffFreq[ch] |= V << 0;
ModLock = 1;
break; break;
case 0x3: Frequency[ch] &= 0x00FF; case 0x3: Frequency[ch] &= 0x00FF;
Frequency[ch] |= (V & 0x7) << 8; Frequency[ch] |= (V & 0x7) << 8;
EffFreq[ch] &= 0x00FF; EffFreq[ch] &= 0x00FF;
EffFreq[ch] |= (V & 0x7) << 8; EffFreq[ch] |= (V & 0x7) << 8;
ModLock = 2;
break; break;
case 0x4: EnvControl[ch] &= 0xFF00; case 0x4: EnvControl[ch] &= 0xFF00;
EnvControl[ch] |= V << 0; EnvControl[ch] |= V << 0;
Envelope[ch] = (V >> 4) & 0xF; EnvelopeReload[ch] = (V >> 4) & 0xF;
EnvelopeValue[ch] = (V >> 4) & 0xF;
if(EnvelopeModMask[ch] == 1)
EnvelopeModMask[ch] = 2;
break; break;
case 0x5: EnvControl[ch] &= 0x00FF; case 0x5: EnvControl[ch] &= 0x00FF;
@ -194,6 +209,12 @@ void VSU::Write(int timestamp, unsigned int A, unsigned char V)
} }
else else
EnvControl[ch] |= (V & 0x03) << 8; EnvControl[ch] |= (V & 0x03) << 8;
if(EnvelopeModMask[ch] == 0 && !(EnvControl[ch] & 0x200) && (
(EnvelopeValue[ch] == 0 && !(EnvControl[ch] & 0x0008)) ||
(EnvelopeValue[ch] == 0xF && (EnvControl[ch] & 0x0008))))
EnvelopeModMask[ch] = 1;
break; break;
case 0x6: RAMAddress[ch] = V & 0xF; case 0x6: RAMAddress[ch] = V & 0xF;
@ -228,14 +249,14 @@ inline void VSU::CalcCurrentOutput(int ch, int &left, int &right)
else else
WD = WaveData[RAMAddress[ch]][WavePos[ch]]; // - 0x20; WD = WaveData[RAMAddress[ch]][WavePos[ch]]; // - 0x20;
} }
l_ol = Envelope[ch] * LeftLevel[ch]; l_ol = EnvelopeValue[ch] * LeftLevel[ch];
if(l_ol) if(l_ol)
{ {
l_ol >>= 3; l_ol >>= 3;
l_ol += 1; l_ol += 1;
} }
r_ol = Envelope[ch] * RightLevel[ch]; r_ol = EnvelopeValue[ch] * RightLevel[ch];
if(r_ol) if(r_ol)
{ {
r_ol >>= 3; r_ol >>= 3;
@ -355,23 +376,27 @@ void VSU::Update(int timestamp)
{ {
EnvelopeClockDivider[ch] += 4; EnvelopeClockDivider[ch] += 4;
int new_envelope = EnvelopeValue[ch];
if(EnvelopeValue[ch] < 0xF && (EnvControl[ch] & 0x0008))
new_envelope++;
else if(EnvelopeValue[ch] > 0 && !(EnvControl[ch] & 0x0008))
new_envelope--;
else if((EnvControl[ch] & 0x200) && EnvelopeModMask[ch] != 2)
{
new_envelope = EnvelopeReload[ch];
EnvelopeModMask[ch] = 0;
}
else if(EnvelopeModMask[ch] == 0)
EnvelopeModMask[ch] = 1;
if(EnvControl[ch] & 0x0100) // Enveloping enabled? if(EnvControl[ch] & 0x0100) // Enveloping enabled?
{ {
EnvelopeCounter[ch]--; EnvelopeCounter[ch]--;
if(!EnvelopeCounter[ch]) if(!EnvelopeCounter[ch])
{ {
EnvelopeCounter[ch] = (EnvControl[ch] & 0x7) + 1; EnvelopeCounter[ch] = (EnvControl[ch] & 0x7) + 1;
if(EnvelopeModMask[ch] == 0)
if(EnvControl[ch] & 0x0008) // Grow EnvelopeValue[ch] = new_envelope;
{
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;
}
} }
} }
@ -380,6 +405,19 @@ void VSU::Update(int timestamp)
if(ch == 4) if(ch == 4)
{ {
// Calculate sweep early
int delta = EffFreq[ch] >> (SweepControl & 0x7);
int NewSweepFreq = EffFreq[ch] + ((SweepControl & 0x8) ? delta : -delta);
if(!(EnvControl[ch] & 0x1000))
{
if(NewSweepFreq < 0)
NewSweepFreq = 0;
else if(NewSweepFreq > 0x7FF)
IntlControl[ch] &= ~0x80;
}
SweepModClockDivider--; SweepModClockDivider--;
while(SweepModClockDivider <= 0) while(SweepModClockDivider <= 0)
{ {
@ -396,30 +434,27 @@ void VSU::Update(int timestamp)
if(EnvControl[ch] & 0x1000) // Modulation if(EnvControl[ch] & 0x1000) // Modulation
{ {
if(ModWavePos < 32 || (EnvControl[ch] & 0x2000)) if(ModState == 0 || (EnvControl[ch] & 0x2000))
{
ModWavePos &= 0x1F;
EffFreq[ch] = (Frequency[ch] + (signed char)ModData[ModWavePos]) & 0x7FF; EffFreq[ch] = (Frequency[ch] + (signed char)ModData[ModWavePos]) & 0x7FF;
ModWavePos++; if(ModState == 1)
} ModState = 2;
}
else // Sweep
{
int delta = EffFreq[ch] >> (SweepControl & 0x7);
int NewFreq = EffFreq[ch] + ((SweepControl & 0x8) ? delta : -delta);
//printf("Sweep(%d): Old: %d, New: %d\n", ch, EffFreq[ch], NewFreq); // Hardware bug: writing to S5FQ* locks the relevant byte when modulating
if(ModLock == 1)
if(NewFreq < 0) EffFreq[ch] = (EffFreq[ch] & 0x700) | (Frequency[ch] & 0xFF);
EffFreq[ch] = 0; else if(ModLock == 2)
else if(NewFreq > 0x7FF) EffFreq[ch] = (EffFreq[ch] & 0xFF) | (Frequency[ch] & 0x700);
{
//EffFreq[ch] = 0x7FF;
IntlControl[ch] &= ~0x80;
} }
else else if(ModState < 2) // Sweep
EffFreq[ch] = NewFreq; {
EffFreq[ch] = NewSweepFreq;
}
if(++ModWavePos >= 32)
{
if(ModState == 0)
ModState = 1;
ModWavePos = 0;
} }
} }
} }

View file

@ -74,7 +74,8 @@ class VSU
// //
// //
int EffFreq[6]; int EffFreq[6];
int Envelope[6]; int EnvelopeValue[6];
int EnvelopeReload[6];
int WavePos[6]; int WavePos[6];
int ModWavePos; int ModWavePos;
@ -91,6 +92,10 @@ class VSU
int EnvelopeClockDivider[6]; int EnvelopeClockDivider[6];
int SweepModClockDivider; int SweepModClockDivider;
int EnvelopeModMask[6];
int ModState;
int ModLock;
int NoiseLatcherClockDivider; int NoiseLatcherClockDivider;
unsigned int NoiseLatcher; unsigned int NoiseLatcher;