diff --git a/Core/Io/IO_Bus.cs b/Core/Io/IO_Bus.cs
index 1361e84..4ccd033 100644
--- a/Core/Io/IO_Bus.cs
+++ b/Core/Io/IO_Bus.cs
@@ -1,13 +1,13 @@
-using System.Diagnostics;
-using Core.Interfaces;
+using Core.Interfaces;
+using System.Diagnostics;
+using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Core.Io
{
public class IO_Bus
{
- public byte BorderColorIndex { get; private set; } = 7; // 7 is White
-
- // 8 rows representing the Spectrum keyboard matrix. Default to 0xFF (unpressed).
+ public byte BorderColorIndex { get; private set; } = 7;
+ public bool BeeperState { get; private set; } = false;
public byte[] KeyboardRows = new byte[8] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
public byte ReadPort(ushort portAddress)
@@ -42,11 +42,13 @@ namespace Core.Io
// The ULA intercepts any write to an even port address
if ((portAddress & 0x01) == 0)
{
- // The bottom 3 bits (0-2) define the border color!
+ // The bottom 3 bits (0-2) define the border color
BorderColorIndex = (byte)(portValue & 0x07);
- // (Bits 3 and 4 handle the cassette MIC output and the internal speaker,
- // which we will need when we start playing audio!)
+ // Bit 4 controls the speaker
+ BeeperState = (portValue & 0x10) != 0;
+
+ // Bit 3 handles the cassette MIC output
}
}
}
diff --git a/Desktop/BeeperDevice.cs b/Desktop/BeeperDevice.cs
new file mode 100644
index 0000000..fe54642
--- /dev/null
+++ b/Desktop/BeeperDevice.cs
@@ -0,0 +1,41 @@
+using NAudio.Wave;
+using System;
+
+namespace Desktop
+{
+ public class BeeperDevice
+ {
+ private WaveOutEvent _waveOut;
+ private BufferedWaveProvider _buffer;
+
+ public BeeperDevice()
+ {
+ _waveOut = new WaveOutEvent();
+ _waveOut.DesiredLatency = 50; // 100ms latency to prevent buffer stutter
+
+ // 44.1kHz, 1 channel (Mono), Float format
+ _buffer = new BufferedWaveProvider(WaveFormat.CreateIeeeFloatWaveFormat(44100, 1));
+ _buffer.BufferDuration = TimeSpan.FromSeconds(1);
+ _buffer.DiscardOnBufferOverflow = true;
+
+ _waveOut.Init(_buffer);
+ _waveOut.Play();
+ }
+
+ public void AddSample(bool isHigh)
+ {
+ //Buffer overrun check and dump
+ if (_buffer.BufferedDuration.TotalMilliseconds > 100)
+ {
+ _buffer.ClearBuffer();
+ }
+
+ // Convert the boolean into a physical sound wave (-0.2 or +0.2)
+ float sampleValue = isHigh ? 0.2f : -0.2f;
+
+ // Convert the float to bytes and drop it in the pipe
+ byte[] bytes = BitConverter.GetBytes(sampleValue);
+ _buffer.AddSamples(bytes, 0, 4);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Desktop/Desktop.csproj b/Desktop/Desktop.csproj
index ca6f524..132ab54 100644
--- a/Desktop/Desktop.csproj
+++ b/Desktop/Desktop.csproj
@@ -8,6 +8,10 @@
enable
+
+
+
+
diff --git a/Desktop/Form1.cs b/Desktop/Form1.cs
index 2393f0e..dfbf00f 100644
--- a/Desktop/Form1.cs
+++ b/Desktop/Form1.cs
@@ -17,6 +17,7 @@ namespace Desktop
private ULA _ula = null!;
private TapManager _tapManager = null!;
private DebuggerForm? _debugger = null;
+ private BeeperDevice _beeper = null!;
private string _baseTitle = "";
private bool _isRunning = false;
private bool _isPaused = false;
@@ -41,6 +42,7 @@ namespace Desktop
_memoryBus = new MemoryBus();
_simpleIoBus = new IO_Bus();
_ula = new ULA(_memoryBus, _simpleIoBus);
+ _beeper = new BeeperDevice();
_tapManager = new TapManager();
_memoryBus.CrapRAMData();
byte[] romData = RomLoader.Load("48.rom");
@@ -70,6 +72,7 @@ namespace Desktop
var stopwatch = Stopwatch.StartNew();
var fpsStopwatch = Stopwatch.StartNew();
long scanlineCount = 0;
+ long audioSampleCount = 0;
while (_isRunning)
{
@@ -90,6 +93,13 @@ namespace Desktop
// --- Execute Instruction ---
_cpu.Step();
+ //Process audio at the correct time
+ while (_cpu.TotalTStates >= (long)(audioSampleCount * 79.365))
+ {
+ _beeper.AddSample(_simpleIoBus.BeeperState);
+ audioSampleCount++;
+ }
+
// --- Check for End of Frame ---
if (_cpu.TotalTStates >= nextScanlineTarget)
{