From af81beb129aab827a674bd38947e82cc9142943c Mon Sep 17 00:00:00 2001 From: Maximilien Noal Date: Mon, 16 Sep 2024 21:03:09 +0200 Subject: [PATCH] refactor: wire up the new OPL Here be dragons. Signed-off-by: Maximilien Noal --- .../Devices/Sound/Blaster/SoundBlaster.cs | 10 +-- .../Emulator/Devices/Sound/OPL.cs | 72 ++++++++++++++----- src/Spice86.Core/Emulator/VM/Machine.cs | 10 ++- src/Spice86/Spice86DependencyInjection.cs | 6 +- 4 files changed, 65 insertions(+), 33 deletions(-) diff --git a/src/Spice86.Core/Emulator/Devices/Sound/Blaster/SoundBlaster.cs b/src/Spice86.Core/Emulator/Devices/Sound/Blaster/SoundBlaster.cs index 4c74a408f..a036e8689 100644 --- a/src/Spice86.Core/Emulator/Devices/Sound/Blaster/SoundBlaster.cs +++ b/src/Spice86.Core/Emulator/Devices/Sound/Blaster/SoundBlaster.cs @@ -155,11 +155,6 @@ public class SoundBlaster : DefaultIOPortHandler, IDmaDevice8, IDmaDevice16, IRe /// The SoundBlaster's OPL3 FM sound channel. /// public SoundChannel FMSynthSoundChannel { get; } - - /// - /// The internal FM synth chip for music. - /// - public OPL3FM Opl3Fm { get; } /// /// The type of SoundBlaster card currently emulated. @@ -196,7 +191,6 @@ public SoundBlaster(IOPortDispatcher ioPortDispatcher, SoftwareMixer softwareMix }; PCMSoundChannel = softwareMixer.CreateChannel(nameof(SoundBlaster)); FMSynthSoundChannel = softwareMixer.CreateChannel(nameof(OPL3FM)); - Opl3Fm = new OPL3FM(FMSynthSoundChannel, state, ioPortDispatcher, failOnUnhandledPort, loggerService, pauseHandler); _ctMixer = new HardwareMixer(soundBlasterHardwareConfig, PCMSoundChannel, FMSynthSoundChannel, loggerService); InitPortHandlers(ioPortDispatcher); } @@ -370,8 +364,8 @@ private void InitPortHandlers(IOPortDispatcher ioPortDispatcher) { ioPortDispatcher.AddIOPortHandler(LEFT_SPEAKER_DATA_PORT_NUMBER, this); ioPortDispatcher.AddIOPortHandler(RIGHT_SPEAKER_STATUS_PORT_NUMBER, this); ioPortDispatcher.AddIOPortHandler(RIGHT_SPEAKER_DATA_PORT_NUMBER, this); - ioPortDispatcher.AddIOPortHandler(FM_MUSIC_STATUS_PORT_NUMBER, this); - ioPortDispatcher.AddIOPortHandler(FM_MUSIC_DATA_PORT_NUMBER, this); + //ioPortDispatcher.AddIOPortHandler(FM_MUSIC_STATUS_PORT_NUMBER, this); + //ioPortDispatcher.AddIOPortHandler(FM_MUSIC_DATA_PORT_NUMBER, this); ioPortDispatcher.AddIOPortHandler(IGNORE_PORT, this); // Those are managed by OPL3FM class. //ioPortDispatcher.AddIOPortHandler(FM_MUSIC_STATUS_PORT_NUMBER_2, this); diff --git a/src/Spice86.Core/Emulator/Devices/Sound/OPL.cs b/src/Spice86.Core/Emulator/Devices/Sound/OPL.cs index 2cb48f32b..bee20ca2a 100644 --- a/src/Spice86.Core/Emulator/Devices/Sound/OPL.cs +++ b/src/Spice86.Core/Emulator/Devices/Sound/OPL.cs @@ -1,9 +1,11 @@ namespace Spice86.Core.Emulator.Devices.Sound; -using System.Runtime.InteropServices; +using Spice86.Core.Emulator.CPU; +using Spice86.Core.Emulator.Devices.Timer; +using Spice86.Core.Emulator.IOPorts; +using Spice86.Shared.Interfaces; -using Spice86.Core.Emulator.Devices.Sound.Ym7128b; -using Spice86.Core.Emulator.VM; +using System.Runtime.InteropServices; public enum Mode { Opl2, DualOpl2, Opl3, Opl3Gold @@ -105,9 +107,9 @@ public bool Update(double time) { } public class Chip { - private Machine _machine; - public Chip(Machine machine) { - _machine = machine; + private Timer _timer; + public Chip(Timer timer) { + _timer = timer; Timer0 = new(80); Timer1 = new(320); } @@ -126,11 +128,11 @@ public bool Write(ushort reg, byte val) { // LOG(LOG_MISC,LOG_ERROR)("write adlib timer %X %X",reg,val); switch (reg) { case 0x02: - Timer0.Update(TimeSpan.FromTicks(_machine.Timer.NumberOfTicks).TotalMilliseconds); + Timer0.Update(TimeSpan.FromTicks(_timer.NumberOfTicks).TotalMilliseconds); Timer0.SetCounter(val); return true; case 0x03: - Timer1.Update(TimeSpan.FromTicks(_machine.Timer.NumberOfTicks).TotalMilliseconds); + Timer1.Update(TimeSpan.FromTicks(_timer.NumberOfTicks).TotalMilliseconds); Timer1.SetCounter(val); return true; case 0x04: @@ -139,7 +141,7 @@ public bool Write(ushort reg, byte val) { Timer0.Reset(); Timer1.Reset(); } else { - double time = TimeSpan.FromTicks(_machine.Timer.NumberOfTicks).TotalMilliseconds; + double time = TimeSpan.FromTicks(_timer.NumberOfTicks).TotalMilliseconds; if ((val & 0x1) > 0) { Timer0.Start(time); } else { @@ -163,7 +165,7 @@ public bool Write(ushort reg, byte val) { /// Read the current timer state, will use current double /// public byte Read() { - TimeSpan time = TimeSpan.FromTicks(_machine.Timer.NumberOfTicks); + TimeSpan time = TimeSpan.FromTicks(_timer.NumberOfTicks); byte ret = 0; // Overflow won't be set if a channel is masked @@ -179,7 +181,10 @@ public byte Read() { } } -public class Opl { +/// +/// The OPL3 / OPL2 / Adlib Gold OPL chip emulation class. +/// +public class Opl : DefaultIOPortHandler { public const byte DefaultVolumeValue = 0xff; //public MixerChannel Channel { get; private set; } = new(); @@ -200,6 +205,8 @@ public class Opl { private byte _mem; private AdlibGold _adlibGold; + + private OplMode _oplMode; // Playback related private double _lastRenderedMs = 0.0; @@ -208,16 +215,13 @@ public class Opl { // Last selected address in the chip for the different modes private const int DefaultVolume = 0xff; - - - [StructLayout(LayoutKind.Explicit)] + private struct Reg { - [FieldOffset(0)] public byte normal; - [FieldOffset(0)] public byte[] dual; public Reg() { + normal = 0; dual = new byte[2]; } } @@ -226,11 +230,43 @@ public Reg() { private Control _ctrl = new(); - public Opl(AdlibGold adlibGold, OplMode mode) { + private bool _dualOpl = false; + + public Opl(State state, Timer timer, IOPortDispatcher ioPortDispatcher, + bool failOnUnhandledPort, ILoggerService loggerService, + AdlibGold adlibGold, OplMode oplMode) + : base(state, failOnUnhandledPort, loggerService) { _adlibGold = adlibGold; + _oplMode = oplMode; + _chip[0] = new Chip(timer); + _chip[1] = new Chip(timer); + InitPortHandlers(ioPortDispatcher); + } + + public override byte ReadByte(ushort port) { + return _chip[0].Read(); + } + + public override void WriteByte(ushort port, byte value) { + _chip[0].Write(port, value); } - private void AdlibGoldControlWrite(byte val) { + private void InitPortHandlers(IOPortDispatcher ioPortDispatcher) { + ioPortDispatcher.AddIOPortHandler(0x388, this); + ioPortDispatcher.AddIOPortHandler(0x38b, this); + if (_dualOpl) { + //Read/Write + ioPortDispatcher.AddIOPortHandler(0x220, this); + //Read/Write + ioPortDispatcher.AddIOPortHandler(0x223, this); + } + //Read/Write + ioPortDispatcher.AddIOPortHandler(0x228, this); + //Write + ioPortDispatcher.AddIOPortHandler(0x229, this); + } + + private void AdlibGoldControlWrite(byte val) { switch (_ctrl.Index) { case 0x04: _adlibGold.StereoControlWrite((byte)StereoProcessorControlReg.VolumeLeft, diff --git a/src/Spice86.Core/Emulator/VM/Machine.cs b/src/Spice86.Core/Emulator/VM/Machine.cs index d3838871d..d3f4143b2 100644 --- a/src/Spice86.Core/Emulator/VM/Machine.cs +++ b/src/Spice86.Core/Emulator/VM/Machine.cs @@ -10,12 +10,10 @@ using Spice86.Core.Emulator.Devices.Sound.Blaster; using Spice86.Core.Emulator.Devices.Sound.Midi; using Spice86.Core.Emulator.Devices.Sound.PCSpeaker; -using Spice86.Core.Emulator.Devices.Sound.Ymf262Emu; using Spice86.Core.Emulator.Devices.Timer; using Spice86.Core.Emulator.Devices.Video; using Spice86.Core.Emulator.InterruptHandlers.Bios; using Spice86.Core.Emulator.InterruptHandlers.Common.Callback; -using Spice86.Core.Emulator.InterruptHandlers.Common.RoutineInstall; using Spice86.Core.Emulator.InterruptHandlers.Input.Keyboard; using Spice86.Core.Emulator.InterruptHandlers.Input.Mouse; using Spice86.Core.Emulator.InterruptHandlers.SystemClock; @@ -191,7 +189,7 @@ public sealed class Machine : IDisposable { /// /// The OPL3 FM Synth chip. /// - public OPL3FM OPL3FM { get; } + public Opl OPL { get; } /// /// The internal software mixer for all sound channels. @@ -226,7 +224,7 @@ public sealed class Machine : IDisposable { /// /// Initializes a new instance of the class. /// - public Machine(BiosDataArea biosDataArea, BiosEquipmentDeterminationInt11Handler biosEquipmentDeterminationInt11Handler, BiosKeyboardInt9Handler biosKeyboardInt9Handler, CallbackHandler callbackHandler, Cpu cpu, CfgCpu cfgCpu, State cpuState, Dos dos, GravisUltraSound gravisUltraSound, IOPortDispatcher ioPortDispatcher, Joystick joystick, Keyboard keyboard, KeyboardInt16Handler keyboardInt16Handler, EmulatorBreakpointsManager emulatorBreakpointsManager, IMemory memory, Midi midiDevice, PcSpeaker pcSpeaker, DualPic dualPic, SoundBlaster soundBlaster, SystemBiosInt12Handler systemBiosInt12Handler, SystemBiosInt15Handler systemBiosInt15Handler, SystemClockInt1AHandler systemClockInt1AHandler, Timer timer, TimerInt8Handler timerInt8Handler, VgaCard vgaCard, IVideoState vgaRegisters, IIOPortHandler vgaIoPortHandler, IVgaRenderer vgaRenderer, IVideoInt10Handler videoInt10Handler, VgaRom vgaRom, DmaController dmaController, OPL3FM opl3FM, SoftwareMixer softwareMixer, IMouseDevice mouseDevice, IMouseDriver mouseDriver, IVgaFunctionality vgaFunctions, IPauseHandler pauseHandler) { + public Machine(BiosDataArea biosDataArea, BiosEquipmentDeterminationInt11Handler biosEquipmentDeterminationInt11Handler, BiosKeyboardInt9Handler biosKeyboardInt9Handler, CallbackHandler callbackHandler, Cpu cpu, CfgCpu cfgCpu, State cpuState, Dos dos, GravisUltraSound gravisUltraSound, IOPortDispatcher ioPortDispatcher, Joystick joystick, Keyboard keyboard, KeyboardInt16Handler keyboardInt16Handler, EmulatorBreakpointsManager emulatorBreakpointsManager, IMemory memory, Midi midiDevice, PcSpeaker pcSpeaker, DualPic dualPic, SoundBlaster soundBlaster, SystemBiosInt12Handler systemBiosInt12Handler, SystemBiosInt15Handler systemBiosInt15Handler, SystemClockInt1AHandler systemClockInt1AHandler, Timer timer, TimerInt8Handler timerInt8Handler, VgaCard vgaCard, IVideoState vgaRegisters, IIOPortHandler vgaIoPortHandler, IVgaRenderer vgaRenderer, IVideoInt10Handler videoInt10Handler, VgaRom vgaRom, DmaController dmaController, Opl opl, SoftwareMixer softwareMixer, IMouseDevice mouseDevice, IMouseDriver mouseDriver, IVgaFunctionality vgaFunctions, IPauseHandler pauseHandler) { BiosDataArea = biosDataArea; BiosEquipmentDeterminationInt11Handler = biosEquipmentDeterminationInt11Handler; BiosKeyboardInt9Handler = biosKeyboardInt9Handler; @@ -258,7 +256,7 @@ public Machine(BiosDataArea biosDataArea, BiosEquipmentDeterminationInt11Handler VideoInt10Handler = videoInt10Handler; VgaRom = vgaRom; DmaController = dmaController; - OPL3FM = opl3FM; + OPL = opl; SoftwareMixer = softwareMixer; MouseDevice = mouseDevice; MouseDriver = mouseDriver; @@ -275,7 +273,7 @@ private void Dispose(bool disposing) { if (disposing) { MidiDevice.Dispose(); SoundBlaster.Dispose(); - OPL3FM.Dispose(); + //OPL3FM.Dispose(); PcSpeaker.Dispose(); SoftwareMixer.Dispose(); } diff --git a/src/Spice86/Spice86DependencyInjection.cs b/src/Spice86/Spice86DependencyInjection.cs index ab68ac52c..da19694e0 100644 --- a/src/Spice86/Spice86DependencyInjection.cs +++ b/src/Spice86/Spice86DependencyInjection.cs @@ -41,6 +41,7 @@ namespace Spice86; using Spice86.Core.Emulator.VM; using Spice86.Core.Emulator.VM.Breakpoint; using Spice86.Infrastructure; +using Spice86.Logging; using Spice86.Shared.Emulator.Memory; using Spice86.Shared.Interfaces; using Spice86.Shared.Utils; @@ -190,7 +191,10 @@ public Spice86DependencyInjection(ILoggerService loggerService, Configuration co timer, timerInt8Handler, vgaCard, videoState, videoInt10Handler, vgaRenderer, vgaBios, vgaRom, - dmaController, soundBlaster.Opl3Fm, softwareMixer, mouse, mouseDriver, + dmaController, new Opl( + state, timer, ioPortDispatcher, configuration.FailOnUnhandledPort, loggerService, + new AdlibGold(loggerService), OplMode.Opl3), + softwareMixer, mouse, mouseDriver, vgaFunctionality, pauseHandler); IDictionary functionsInformation = reader.ReadGhidraSymbolsFromFileOrCreate();