Skip to content

Commit

Permalink
refactor: wire up the new OPL
Browse files Browse the repository at this point in the history
Here be dragons.

Signed-off-by: Maximilien Noal <[email protected]>
  • Loading branch information
maximilien-noal committed Feb 16, 2025
1 parent e86247e commit af81beb
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 33 deletions.
10 changes: 2 additions & 8 deletions src/Spice86.Core/Emulator/Devices/Sound/Blaster/SoundBlaster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,6 @@ public class SoundBlaster : DefaultIOPortHandler, IDmaDevice8, IDmaDevice16, IRe
/// The SoundBlaster's OPL3 FM sound channel.
/// </summary>
public SoundChannel FMSynthSoundChannel { get; }

/// <summary>
/// The internal FM synth chip for music.
/// </summary>
public OPL3FM Opl3Fm { get; }

/// <summary>
/// The type of SoundBlaster card currently emulated.
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
72 changes: 54 additions & 18 deletions src/Spice86.Core/Emulator/Devices/Sound/OPL.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -105,9 +107,9 @@ public bool Update(double time) {
}

public class Chip {
private Machine _machine;
public Chip(Machine machine) {
_machine = machine;
private Timer _timer;

Check notice

Code scanning / CodeQL

Missed 'readonly' opportunity Note

Field '_timer' can be 'readonly'.
public Chip(Timer timer) {
_timer = timer;
Timer0 = new(80);
Timer1 = new(320);
}
Expand All @@ -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:
Expand All @@ -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 {
Expand All @@ -163,7 +165,7 @@ public bool Write(ushort reg, byte val) {
/// Read the current timer state, will use current double
/// </summary>
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
Expand All @@ -179,7 +181,10 @@ public byte Read() {
}
}

public class Opl {
/// <summary>
/// The OPL3 / OPL2 / Adlib Gold OPL chip emulation class.
/// </summary>
public class Opl : DefaultIOPortHandler {
public const byte DefaultVolumeValue = 0xff;

//public MixerChannel Channel { get; private set; } = new();
Expand All @@ -200,6 +205,8 @@ public class Opl {
private byte _mem;

private AdlibGold _adlibGold;

Check notice

Code scanning / CodeQL

Missed 'readonly' opportunity Note

Field '_adlibGold' can be 'readonly'.

private OplMode _oplMode;

Check notice

Code scanning / CodeQL

Missed 'readonly' opportunity Note

Field '_oplMode' can be 'readonly'.

// Playback related
private double _lastRenderedMs = 0.0;

Check notice

Code scanning / CodeQL

Missed 'readonly' opportunity Note

Field '_lastRenderedMs' can be 'readonly'.
Expand All @@ -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;

Check notice

Code scanning / CodeQL

Missed 'readonly' opportunity Note

Field 'normal' can be 'readonly'.
[FieldOffset(0)]
public byte[] dual;

Check notice

Code scanning / CodeQL

Missed 'readonly' opportunity Note

Field 'dual' can be 'readonly'.

public Reg() {
normal = 0;
dual = new byte[2];
}
}
Expand All @@ -226,11 +230,43 @@ public Reg() {

private Control _ctrl = new();

Check notice

Code scanning / CodeQL

Missed 'readonly' opportunity Note

Field '_ctrl' can be 'readonly'.

public Opl(AdlibGold adlibGold, OplMode mode) {
private bool _dualOpl = false;

Check notice

Code scanning / CodeQL

Missed 'readonly' opportunity Note

Field '_dualOpl' can be 'readonly'.

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,
Expand Down
10 changes: 4 additions & 6 deletions src/Spice86.Core/Emulator/VM/Machine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -191,7 +189,7 @@ public sealed class Machine : IDisposable {
/// <summary>
/// The OPL3 FM Synth chip.
/// </summary>
public OPL3FM OPL3FM { get; }
public Opl OPL { get; }

/// <summary>
/// The internal software mixer for all sound channels.
Expand Down Expand Up @@ -226,7 +224,7 @@ public sealed class Machine : IDisposable {
/// <summary>
/// Initializes a new instance of the <see cref="Machine"/> class.
/// </summary>
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;
Expand Down Expand Up @@ -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;
Expand All @@ -275,7 +273,7 @@ private void Dispose(bool disposing) {
if (disposing) {
MidiDevice.Dispose();
SoundBlaster.Dispose();
OPL3FM.Dispose();
//OPL3FM.Dispose();
PcSpeaker.Dispose();
SoftwareMixer.Dispose();
}
Expand Down
6 changes: 5 additions & 1 deletion src/Spice86/Spice86DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<SegmentedAddress, FunctionInformation> functionsInformation = reader.ReadGhidraSymbolsFromFileOrCreate();
Expand Down

0 comments on commit af81beb

Please sign in to comment.