From 4e745b4fbc175c7738f49556ebc9aa8f762e4fc7 Mon Sep 17 00:00:00 2001 From: parsons Date: Fri, 15 May 2026 22:09:20 +0100 Subject: [PATCH] Fixed the noise channel. Fixed speech in IM. Added 0xED57 to play WBIII --- Core/Audio/SmsApu.cs | 66 ++-- Core/Cpu/Z80.cs | 12 + Core/SmsMachine.cs | 5 +- Desktop/Desktop.csproj | 702 +++++++++++++++++++++-------------------- Desktop/Form1.cs | 6 +- 5 files changed, 411 insertions(+), 380 deletions(-) diff --git a/Core/Audio/SmsApu.cs b/Core/Audio/SmsApu.cs index 30e7253..da9efeb 100644 --- a/Core/Audio/SmsApu.cs +++ b/Core/Audio/SmsApu.cs @@ -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; + // 2. NO FLIP FLOP! Shift the LFSR immediately! + bool isWhiteNoise = (Registers[6] & 0x04) != 0; + + // Read the bits BEFORE shifting + int bit0 = _lfsr & 1; + int bit3 = (_lfsr >> 3) & 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)); - // Only shift the random static when the clock goes High - if (_noiseFlipFlop) - { - int tappedBit = _lfsr & 1; - _lfsr >>= 1; - - if (tappedBit == 1) - { - bool isWhiteNoise = (Registers[6] & 0x04) != 0; - if (isWhiteNoise) _lfsr ^= 0x0009; // SMS-specific XOR mask - - _lfsr |= 0x8000; // Inject the high bit - } - - // The noise channel output is driven directly by the tapped bit - _polarities[3] = (tappedBit == 1) ? 1 : -1; - } + // 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) { diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs index 8bb241c..df53c9c 100644 --- a/Core/Cpu/Z80.cs +++ b/Core/Cpu/Z80.cs @@ -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; diff --git a/Core/SmsMachine.cs b/Core/SmsMachine.cs index ce410cb..bf87c61 100644 --- a/Core/SmsMachine.cs +++ b/Core/SmsMachine.cs @@ -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(); diff --git a/Desktop/Desktop.csproj b/Desktop/Desktop.csproj index 3441ec4..f47696b 100644 --- a/Desktop/Desktop.csproj +++ b/Desktop/Desktop.csproj @@ -1,356 +1,362 @@  - - WinExe - net8.0-windows - enable - true - enable - + + WinExe + net8.0-windows + enable + enable - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + true - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - - Never - - + + true + true + win-x64 + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + + Never + + + + + + + + + + \ No newline at end of file diff --git a/Desktop/Form1.cs b/Desktop/Form1.cs index b9859c8..d4a834f 100644 --- a/Desktop/Form1.cs +++ b/Desktop/Form1.cs @@ -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}]"; }); }