Fixed the noise channel. Fixed speech in IM. Added 0xED57 to play WBIII
This commit is contained in:
@@ -22,7 +22,9 @@ namespace Core.Audio
|
||||
private int[] _counters = new int[4];
|
||||
private int[] _polarities = new int[4] { 1, 1, 1, 1 }; // 1 = High, -1 = Low
|
||||
private ushort _lfsr = 0x8000; // Linear Feedback Shift Register (For Noise)
|
||||
private bool _noiseFlipFlop = false;
|
||||
// --- FILTER VARIABLES ---
|
||||
private float _previousSample = 0f;
|
||||
private float _previousFiltered = 0f;
|
||||
|
||||
// The SN76489 Volume Table reduces amplitude by exactly 2 decibels per step.
|
||||
private static readonly float[] VolumeTable = {
|
||||
@@ -84,39 +86,34 @@ namespace Core.Audio
|
||||
_counters[3]--;
|
||||
if (_counters[3] <= 0)
|
||||
{
|
||||
// Reload the counter
|
||||
// 1. Reload the counter
|
||||
int shiftRate = Registers[6] & 0x03;
|
||||
if (shiftRate == 0) _counters[3] = 0x10; // Fast
|
||||
else if (shiftRate == 1) _counters[3] = 0x20; // Medium
|
||||
else if (shiftRate == 2) _counters[3] = 0x40; // Slow
|
||||
else _counters[3] = (Registers[4] == 0) ? 1024 : Registers[4]; // Linked to Tone 2
|
||||
|
||||
// THE FIX: Toggle the internal clock (Divide by 2!)
|
||||
_noiseFlipFlop = !_noiseFlipFlop;
|
||||
|
||||
// Only shift the random static when the clock goes High
|
||||
if (_noiseFlipFlop)
|
||||
{
|
||||
int tappedBit = _lfsr & 1;
|
||||
_lfsr >>= 1;
|
||||
|
||||
if (tappedBit == 1)
|
||||
{
|
||||
// 2. NO FLIP FLOP! Shift the LFSR immediately!
|
||||
bool isWhiteNoise = (Registers[6] & 0x04) != 0;
|
||||
if (isWhiteNoise) _lfsr ^= 0x0009; // SMS-specific XOR mask
|
||||
|
||||
_lfsr |= 0x8000; // Inject the high bit
|
||||
}
|
||||
// Read the bits BEFORE shifting
|
||||
int bit0 = _lfsr & 1;
|
||||
int bit3 = (_lfsr >> 3) & 1;
|
||||
|
||||
// The noise channel output is driven directly by the tapped bit
|
||||
_polarities[3] = (tappedBit == 1) ? 1 : -1;
|
||||
}
|
||||
// The Master System physically XORs bit 0 and bit 3 for White Noise.
|
||||
// For Periodic Noise, it just feeds bit 0 straight back in.
|
||||
int feedback = isWhiteNoise ? (bit0 ^ bit3) : bit0;
|
||||
|
||||
// Shift the register right and push the feedback into the highest bit (Bit 15)
|
||||
_lfsr = (ushort)((_lfsr >> 1) | (feedback << 15));
|
||||
|
||||
// The noise speaker is driven directly by the lowest bit
|
||||
_polarities[3] = (_lfsr & 1) == 1 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void MixAndOutputSample()
|
||||
{
|
||||
// If the UI hasn't hooked up the speakers yet, just throw the audio away!
|
||||
if (AudioDevice == null) return;
|
||||
|
||||
float sample = 0f;
|
||||
@@ -124,9 +121,16 @@ namespace Core.Audio
|
||||
// Mix Tone Channels 0, 1, 2
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
// HARDWARE QUIRK: If the tone frequency is 1 or 0, the channel outputs a
|
||||
// constant DC voltage instead of vibrating, meaning it is effectively silent.
|
||||
if (Registers[i * 2] > 1)
|
||||
int tone = Registers[i * 2];
|
||||
|
||||
// THE FIX 1: Catch ALL ultrasonic frequencies (1 through 5) to stop Aliasing!
|
||||
if (tone >= 0 && tone <= 5)
|
||||
{
|
||||
// THE FIX 2: Scale it by 0.5f. A fast square wave spends 50% of its time
|
||||
// high, so it naturally averages out to half volume. This prevents clipping!
|
||||
sample += 0.5f * VolumeTable[Registers[(i * 2) + 1]];
|
||||
}
|
||||
else
|
||||
{
|
||||
sample += _polarities[i] * VolumeTable[Registers[(i * 2) + 1]];
|
||||
}
|
||||
@@ -135,10 +139,18 @@ namespace Core.Audio
|
||||
// Mix Noise Channel 3
|
||||
sample += _polarities[3] * VolumeTable[Registers[7]];
|
||||
|
||||
// Divide by 4 so all 4 channels together never exceed 1.0f (which would cause horrible distortion!)
|
||||
// Divide by 4 so all 4 channels together never exceed 1.0f
|
||||
sample /= 4.0f;
|
||||
|
||||
AudioDevice.AddSample(sample);
|
||||
// THE FIX 3: The DC Blocker (1-Pole High-Pass Filter)
|
||||
// PCM speech creates a massive DC offset (pushes the speaker cone forward and leaves it there).
|
||||
// This filter smoothly pulls the speaker cone back to center at 35Hz, removing the hum and distortion!
|
||||
float filteredSample = sample - _previousSample + 0.995f * _previousFiltered;
|
||||
|
||||
_previousSample = sample;
|
||||
_previousFiltered = filteredSample;
|
||||
|
||||
AudioDevice.AddSample(filteredSample);
|
||||
}
|
||||
public void WritePort7F(byte value)
|
||||
{
|
||||
|
||||
@@ -1340,6 +1340,18 @@ namespace Core.Cpu
|
||||
case 0x56: // IM 1
|
||||
InterruptMode = 1;
|
||||
return 8;
|
||||
case 0x57: // LD A, I
|
||||
{
|
||||
AF.High = I; // Copy I into A
|
||||
byte flags57 = (byte)(AF.Low & 0x01); // Preserve Carry
|
||||
|
||||
if ((AF.High & 0x80) != 0) flags57 |= 0x80; // S flag
|
||||
if (AF.High == 0) flags57 |= 0x40; // Z flag
|
||||
if (IFF2) flags57 |= 0x04; // P/V flag gets IFF2
|
||||
flags57 |= (byte)(AF.High & 0x28);
|
||||
AF.Low = flags57;
|
||||
return 9;
|
||||
}
|
||||
case 0x58: // IN E, (C)
|
||||
byte inVal58 = ReadPort(BC.Word);
|
||||
DE.Low = inVal58;
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace Core
|
||||
public ushort? Breakpoint { get; set; } = null;
|
||||
|
||||
// NTSC SMS T-States per frame
|
||||
public const int TStatesPerFrame = 59736;
|
||||
public const int TStatesPerFrame = 59736; //NTSC
|
||||
//public const int TStatesPerFrame = 49780; //PAL
|
||||
|
||||
public SmsMachine()
|
||||
{
|
||||
@@ -46,7 +47,7 @@ namespace Core
|
||||
public void RunFrame()
|
||||
{
|
||||
int tStatesThisFrame = 0;
|
||||
while (tStatesThisFrame < 59736) // Standard NTSC frame time
|
||||
while (tStatesThisFrame < TStatesPerFrame) // Standard NTSC frame time
|
||||
{
|
||||
// 1. Run one CPU instruction
|
||||
int cycles = Cpu.Step();
|
||||
|
||||
@@ -4,8 +4,15 @@
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
|
||||
<!-- This specific line is what maps Label, Button, TextBox, and STAThread -->
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
|
||||
<!-- Single File Settings -->
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -352,5 +359,4 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -18,7 +18,8 @@ namespace Desktop
|
||||
private Bitmap _screenBitmap = new Bitmap(256, 192, PixelFormat.Format32bppArgb);
|
||||
private NAudioPlayer _audioPlayer;
|
||||
private Task _emulatorTask;
|
||||
private double TargetFrameTime = 16.667f;
|
||||
private double TargetFrameTime = 16.667f; //NTSC
|
||||
//private double TargetFrameTime = 20; //PAL
|
||||
public int TotalFrameCount = 0;
|
||||
public double FrameTime { get; private set; } = 0;
|
||||
public double FramesPerSecond { get; private set; } = 0;
|
||||
@@ -152,7 +153,6 @@ namespace Desktop
|
||||
}
|
||||
}
|
||||
|
||||
// --- METRICS MATH ---
|
||||
double currentTime = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
TotalFrameCount++;
|
||||
framesThisSecond++;
|
||||
@@ -165,7 +165,7 @@ namespace Desktop
|
||||
|
||||
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate
|
||||
{
|
||||
this.Text = $"Parsons Master System - {_currentRomName} [FPS/FT: {FramesPerSecond:F0}/{FrameTime:F1}]";
|
||||
this.Text = $"Parsons Master System 2026 - {_currentRomName} [FPS/FT: {FramesPerSecond:F0}/{FrameTime:F1}]";
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user