diff --git a/D/Darkstar.csproj b/D/Darkstar.csproj index f32bba3..4289c25 100644 --- a/D/Darkstar.csproj +++ b/D/Darkstar.csproj @@ -122,6 +122,7 @@ + True True diff --git a/D/Ethernet/EthernetController.cs b/D/Ethernet/EthernetController.cs index ebad542..52a674f 100644 --- a/D/Ethernet/EthernetController.cs +++ b/D/Ethernet/EthernetController.cs @@ -126,7 +126,7 @@ namespace D.Ethernet public int EtherDisp() { // - // Pin 139(YIODisp.1) : Hooked to "Attn," which appear to be whether any attention is needed by the receiver + // Pin 139(YIODisp.1) : Hooked to "Attn," which indicates whether any attention is needed by the receiver // or transmitter. // Pin 39(YIODisp.0) : "(schematic) Must be zero for the transmitting inner loop uCode. It is also used to // determine if the Option card is plugged in." @@ -343,8 +343,8 @@ namespace D.Ethernet // if (_fifo.Count > 0) { - //ss - // if cycle == 2 we dequeue the next item from the FIFO; + // + // If cycle == 2 we dequeue the next item from the FIFO; // otherwise the last-dequeued item is returned. // (See OPT schematic, sheet 6: // "<-EIData not in Cycle2 is rereading EIData in the case @@ -359,8 +359,6 @@ namespace D.Ethernet if (Log.Enabled) Log.Write(LogComponent.EthernetReceive, " <-EIData: Returning FIFO word 0x{0:x4}. FIFO count is now {1}", value, _fifo.Count); - - _debugPacket.Add(value); } else { @@ -389,41 +387,6 @@ namespace D.Ethernet _rxEvenLen = _loopBack || _localLoop ? true : _evenPacketLength; if (Log.Enabled) Log.Write(LogComponent.EthernetReceive, " <-EIData: completing transfer."); - - if (Log.Enabled) - { - StringBuilder sb = new StringBuilder(); - int byteNum = 0; - StringBuilder dataLine = new StringBuilder(); - StringBuilder asciiLine = new StringBuilder(); - dataLine.AppendFormat("000: "); - - for (int i = 0; i < _debugPacket.Count; i++) - { - dataLine.AppendFormat("{0:x2} {1:x2}", (_debugPacket[i] >> 8), _debugPacket[i] & 0xff); - asciiLine.Append(GetPrintableChar((byte)(_debugPacket[i] >> 8))); - asciiLine.Append(GetPrintableChar((byte)_debugPacket[i])); - - byteNum++; - if ((byteNum % 16) == 0) - { - Log.Write(LogComponent.EthernetPacket, "{0} {1}", dataLine.ToString(), asciiLine.ToString()); - dataLine.Clear(); - asciiLine.Clear(); - dataLine.AppendFormat("{0:x3}: ", i + 1); - byteNum = 0; - } - } - - if (byteNum > 0) - { - Log.Write(LogComponent.EthernetPacket, "{0} {1}", dataLine.ToString(), asciiLine.ToString()); - } - - Log.Write(LogComponent.EthernetPacket, ""); - } - - _debugPacket.Clear(); } UpdateWakeup(); @@ -1019,7 +982,5 @@ namespace D.Ethernet // private IPacketInterface _hostInterface; private Queue _outputPacket; - - private List _debugPacket = new List(); } } diff --git a/D/IO/FloppyDrive.cs b/D/IO/FloppyDrive.cs index 9893ecb..c4e88d3 100644 --- a/D/IO/FloppyDrive.cs +++ b/D/IO/FloppyDrive.cs @@ -155,7 +155,7 @@ namespace D if (DriveSelect && IsLoaded && !_index) { // Raise the index signal, hold for a short period. - _index = true; + _index = true; _system.Scheduler.Schedule(_indexDuration, IndexCallback); if (Log.Enabled) Log.Write(LogComponent.IOPFloppy, "Disk rotation complete, raising INDEX signal for 10us."); @@ -179,7 +179,7 @@ namespace D // Index signal and timing private bool _index; - private ulong _indexInterval = 250 * Conversion.MsecToNsec; // 1/5 second at 300rpm + private ulong _indexInterval = 250 * Conversion.MsecToNsec; // 1/5 second at 300rpm - we slow this down just a bit. private ulong _indexDuration = 10 * Conversion.UsecToNsec; // 10uSec duration for index signal. } } diff --git a/D/IOP/IOProcessor.cs b/D/IOP/IOProcessor.cs index 8a2f940..64314ff 100644 --- a/D/IOP/IOProcessor.cs +++ b/D/IOP/IOProcessor.cs @@ -54,6 +54,7 @@ namespace D.IOP _floppyController = new FloppyController(_floppyDrive, _system); _dma = new DMAController(this); _tty = new Printer(); + _tone = new Tone(); // // Register DMA devices with controller @@ -65,7 +66,7 @@ namespace D.IOP _io.RegisterDevice(_floppyController); _io.RegisterDevice(_dma); _io.RegisterDevice(_system.CP); - //_io.RegisterDevice(_tty); + _io.RegisterDevice(_tty); Reset(); } @@ -142,6 +143,11 @@ namespace D.IOP get { return _tty; } } + public Tone Tone + { + get { return _tone; } + } + private i8085 _cpu; private IOPIOBus _io; private I8085MemoryBus _mem; @@ -155,6 +161,7 @@ namespace D.IOP private Keyboard _keyboard; private Mouse _mouse; private Printer _tty; + private Tone _tone; private DSystem _system; // diff --git a/D/IOP/MiscIO.cs b/D/IOP/MiscIO.cs index 0a54104..dd6c275 100644 --- a/D/IOP/MiscIO.cs +++ b/D/IOP/MiscIO.cs @@ -116,6 +116,24 @@ namespace D.IOP { switch (port) { + case 0x8d: + // i8253 Timer channel #1 - used to set the Keyboard bell (tone) frequency. + // This is a 16-bit value loaded one byte at a time, LSB first. + // Send the word off to the tone generator. + _iop.Tone.LoadInterval(value); + break; + + case 0x8f: + // i8253 Timer Mode. + // This is used to control the timer used for the Keyboard bell + // and for the USART. It specifies which timer will be active, + // how that timer's interval is loaded, and what the output waveform + // looks like. + // At this time there's no particular reason to pay attention to what + // gets written here, as we don't actually emulate the i8253. + if (Log.Enabled) Log.Write(LogComponent.IOPMisc, "Misc IO port Timer Mode written {0:x2}", value); + break; + case 0xd0: // // DMA Test Register @@ -160,6 +178,15 @@ namespace D.IOP if (Log.Enabled) Log.Write(LogComponent.IOPMisc, "Misc IO Keyboard data clock."); } + if ((value & 0x20) != 0) + { + _iop.Tone.EnableTone(); + } + else + { + _iop.Tone.DisableTone(); + } + if ((value & 0x10) != 0) { _iop.Keyboard.EnableDiagnosticMode(); @@ -401,6 +428,8 @@ namespace D.IOP private readonly int[] _writePorts = new int[] { + 0x8d, // i8253 Timer Control 1 (Tone frequency) + 0x8f, // i8253 Timer Mode 0xd0, // DMA Test Register 0xe9, // KB, MP, TOD clocks (write) 0xea, // Clear TOD interrupt (write) diff --git a/D/IOP/Tone.cs b/D/IOP/Tone.cs new file mode 100644 index 0000000..4e1fdd0 --- /dev/null +++ b/D/IOP/Tone.cs @@ -0,0 +1,91 @@ +using D.Logging; +using SDL2; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace D.IOP +{ + /// + /// Implements the tone generator used to generate simple beeps. + /// This is driven by an i8253 programmable interval timer. + /// + public class Tone + { + public Tone() + { + Reset(); + } + + public void Reset() + { + _lsb = 0; + _loadLSB = true; + _frequency = 0.0; + _enabled = false; + _sampleOn = false; + _periodInSamples = 0; + } + + public void LoadInterval(byte value) + { + if (_loadLSB) + { + _lsb = value; + } + else + { + //;Frequency constant (1843.2/f, f in kHz) + _frequency = ((value << 8) | value) / 1.8432; + if (Log.Enabled) Log.Write(LogComponent.Tone, "Tone frequency set to {0}", _frequency); + _periodInSamples = (44100.0 / _frequency) / 2; + } + + _loadLSB = false; + } + + public void EnableTone() + { + if (Log.Enabled) Log.Write(LogComponent.Tone, "Tone enabled.", _frequency); + _enabled = true; + } + + public void DisableTone() + { + // if (Log.Enabled) Log.Write(LogComponent.Tone, "Tone disabled.", _frequency); + _enabled = false; + } + + public void AudioCallback(IntPtr userData, IntPtr stream, int length) + { + byte[] samples = new byte[length]; + + for (int i = 0; i < length; i++) + { + _position++; + + if (_position > _periodInSamples) + { + _position -= _periodInSamples; + _sampleOn = !_sampleOn; + } + + samples[i] = (byte)(_enabled ? (_sampleOn ? 0xff : 0x00) : 0x00); + } + + // Marshal.Copy(samples, 0, stream, length); + } + + private bool _loadLSB; + private byte _lsb; + + private double _frequency; + private bool _enabled; + private double _position; + private double _periodInSamples; + private bool _sampleOn; + } +} diff --git a/D/Logging/Log.cs b/D/Logging/Log.cs index 7608f0b..fc85083 100644 --- a/D/Logging/Log.cs +++ b/D/Logging/Log.cs @@ -81,6 +81,9 @@ namespace D.Logging EthernetReceive = 0x8000000, EthernetPacket = 0x10000000, + // Keyboard tone + Tone = 0x20000000, + // Configuration Configuration = 0x40000000, @@ -108,9 +111,9 @@ namespace D.Logging { static Log() { - Enabled = false; - _components = LogComponent.None; - _type = LogType.None; + Enabled = true; + _components = LogComponent.Tone | LogComponent.IOPPrinter; + _type = LogType.All; _logIndex = 0; } diff --git a/D/Program.cs b/D/Program.cs index 241b3e1..ed5e0c3 100644 --- a/D/Program.cs +++ b/D/Program.cs @@ -88,7 +88,7 @@ namespace D // Cons up a system to run stuff on. DSystem system = new DSystem(); - system.Reset(); + system.Reset(); // // Start the UI, this will not return from ShowDialog diff --git a/D/UI/DWindow-IO.cs b/D/UI/DWindow-IO.cs index c7f2aaf..09fdffe 100644 --- a/D/UI/DWindow-IO.cs +++ b/D/UI/DWindow-IO.cs @@ -28,6 +28,7 @@ using D.IOP; +using D.Logging; using SDL2; using System; using System.Collections.Generic; @@ -974,7 +975,7 @@ namespace D.UI int retVal; // Get SDL humming - if ((retVal = SDL.SDL_Init(SDL.SDL_INIT_VIDEO)) < 0) + if ((retVal = SDL.SDL_Init(SDL.SDL_INIT_EVERYTHING)) < 0) { throw new InvalidOperationException(String.Format("SDL_Init failed. Error {0:x}", retVal)); } @@ -1028,6 +1029,26 @@ namespace D.UI _renderEventType = SDL.SDL_RegisterEvents(1); _renderEvent = new SDL.SDL_Event(); _renderEvent.type = (SDL.SDL_EventType)_renderEventType; + + + + SDL.SDL_AudioSpec desired = new SDL.SDL_AudioSpec(); + SDL.SDL_AudioSpec obtained = new SDL.SDL_AudioSpec(); + + desired.freq = 44100; + desired.format = SDL.AUDIO_U8; + desired.channels = 1; + desired.callback = _system.IOP.Tone.AudioCallback; + desired.samples = 1; + + uint deviceId = SDL.SDL_OpenAudioDevice(null, 0, ref desired, out obtained, 0); + + + SDL.SDL_PauseAudioDevice(deviceId, 0); + + if (Log.Enabled) Log.Write(LogComponent.Tone, "SDL Audio initialized, device id {0}", deviceId); + + } private void CreateDisplayTexture(bool filter)