2017-10-13 22:01:14 +00:00
|
|
|
|
using BizHawk.Emulation.Common;
|
2019-02-23 17:31:09 +00:00
|
|
|
|
using System;
|
2017-10-13 22:01:14 +00:00
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Cores.Sega.MasterSystem
|
|
|
|
|
{
|
2019-02-23 17:31:09 +00:00
|
|
|
|
public partial class SMS : IEmulator, ISoundProvider
|
2017-10-13 22:01:14 +00:00
|
|
|
|
{
|
|
|
|
|
public IEmulatorServiceProvider ServiceProvider { get; }
|
|
|
|
|
|
|
|
|
|
public ControllerDefinition ControllerDefinition
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2018-05-12 00:08:42 +00:00
|
|
|
|
if (IsGameGear_C)
|
2017-10-13 22:01:14 +00:00
|
|
|
|
{
|
|
|
|
|
return GGController;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-23 03:18:14 +00:00
|
|
|
|
// Sorta a hack but why not
|
|
|
|
|
PortDEEnabled = SyncSettings.ControllerType == "Keyboard";
|
|
|
|
|
|
2017-10-27 23:57:18 +00:00
|
|
|
|
switch(SyncSettings.ControllerType)
|
2017-10-13 22:01:14 +00:00
|
|
|
|
{
|
|
|
|
|
case "Paddle":
|
|
|
|
|
return SMSPaddleController;
|
|
|
|
|
case "Light Phaser":
|
|
|
|
|
// scale the vertical to the display mode
|
|
|
|
|
SMSLightPhaserController.FloatRanges[1] = new ControllerDefinition.FloatRange(0, Vdp.FrameHeight / 2, Vdp.FrameHeight - 1);
|
|
|
|
|
|
|
|
|
|
return SMSLightPhaserController;
|
2017-10-27 23:57:18 +00:00
|
|
|
|
case "Sports Pad":
|
|
|
|
|
return SMSSportsPadController;
|
2017-11-23 03:18:14 +00:00
|
|
|
|
case "Keyboard":
|
|
|
|
|
return SMSKeyboardController;
|
2017-10-13 22:01:14 +00:00
|
|
|
|
default:
|
|
|
|
|
return SmsController;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-24 17:02:59 +00:00
|
|
|
|
// not savestated variables
|
|
|
|
|
int s_L, s_R;
|
|
|
|
|
|
2019-01-06 20:05:29 +00:00
|
|
|
|
public bool FrameAdvance(IController controller, bool render, bool rendersound)
|
2017-10-13 22:01:14 +00:00
|
|
|
|
{
|
|
|
|
|
_controller = controller;
|
|
|
|
|
_lagged = true;
|
|
|
|
|
_frame++;
|
|
|
|
|
|
|
|
|
|
if (!IsGameGear)
|
|
|
|
|
{
|
2019-02-23 17:31:09 +00:00
|
|
|
|
PSG.Set_Panning(Settings.ForceStereoSeparation ? ForceStereoByte : (byte)0xFF);
|
2017-10-13 00:21:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Tracer.Enabled)
|
|
|
|
|
{
|
|
|
|
|
Cpu.TraceCallback = s => Tracer.Put(s);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Cpu.TraceCallback = null;
|
2017-10-13 22:01:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 00:08:42 +00:00
|
|
|
|
if (IsGameGear_C == false)
|
2017-10-13 22:01:14 +00:00
|
|
|
|
{
|
|
|
|
|
Cpu.NonMaskableInterrupt = controller.IsPressed("Pause");
|
|
|
|
|
}
|
2018-07-02 12:23:29 +00:00
|
|
|
|
else if (!IsGameGear && IsGameGear_C)
|
2018-05-12 00:08:42 +00:00
|
|
|
|
{
|
|
|
|
|
Cpu.NonMaskableInterrupt = controller.IsPressed("P1 Start");
|
|
|
|
|
}
|
2017-10-13 22:01:14 +00:00
|
|
|
|
|
|
|
|
|
if (IsGame3D && Settings.Fix3D)
|
|
|
|
|
{
|
2019-02-23 17:31:09 +00:00
|
|
|
|
render = ((Frame & 1) == 0) & render;
|
2017-10-13 22:01:14 +00:00
|
|
|
|
}
|
2019-02-23 17:31:09 +00:00
|
|
|
|
|
|
|
|
|
int scanlinesPerFrame = Vdp.DisplayType == DisplayType.NTSC ? 262 : 313;
|
|
|
|
|
Vdp.SpriteLimit = Settings.SpriteLimit;
|
|
|
|
|
for (int i = 0; i < scanlinesPerFrame; i++)
|
2017-10-13 22:01:14 +00:00
|
|
|
|
{
|
2019-02-23 17:31:09 +00:00
|
|
|
|
Vdp.ScanLine = i;
|
|
|
|
|
|
|
|
|
|
Vdp.RenderCurrentScanline(render);
|
|
|
|
|
|
|
|
|
|
Vdp.ProcessFrameInterrupt();
|
|
|
|
|
Vdp.ProcessLineInterrupt();
|
|
|
|
|
ProcessLineControls();
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < Vdp.IPeriod; j++)
|
|
|
|
|
{
|
|
|
|
|
Cpu.ExecuteOne();
|
|
|
|
|
|
2019-02-24 17:02:59 +00:00
|
|
|
|
PSG.generate_sound();
|
|
|
|
|
|
|
|
|
|
s_L = PSG.current_sample_L;
|
|
|
|
|
s_R = PSG.current_sample_R;
|
2019-02-23 17:31:09 +00:00
|
|
|
|
|
|
|
|
|
if (s_L != old_s_L)
|
|
|
|
|
{
|
|
|
|
|
blip_L.AddDelta(sampleclock, s_L - old_s_L);
|
|
|
|
|
old_s_L = s_L;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s_R != old_s_R)
|
|
|
|
|
{
|
|
|
|
|
blip_R.AddDelta(sampleclock, s_R - old_s_R);
|
|
|
|
|
old_s_R = s_R;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sampleclock++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Vdp.ScanLine == scanlinesPerFrame - 1)
|
|
|
|
|
{
|
|
|
|
|
Vdp.ProcessGGScreen();
|
|
|
|
|
Vdp.ProcessOverscan();
|
|
|
|
|
}
|
2017-10-13 22:01:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_lagged)
|
|
|
|
|
{
|
|
|
|
|
_lagCount++;
|
|
|
|
|
_isLag = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_isLag = false;
|
|
|
|
|
}
|
2019-01-06 20:05:29 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
2017-10-13 22:01:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-09 17:45:29 +00:00
|
|
|
|
// Used for GG linked play
|
|
|
|
|
public void FrameAdvancePrep()
|
|
|
|
|
{
|
|
|
|
|
_lagged = true;
|
|
|
|
|
_frame++;
|
|
|
|
|
|
|
|
|
|
if (!IsGameGear && IsGameGear_C)
|
|
|
|
|
{
|
|
|
|
|
Cpu.NonMaskableInterrupt = start_pressed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Used for GG linked play
|
|
|
|
|
public void FrameAdvancePost()
|
|
|
|
|
{
|
|
|
|
|
if (_lagged)
|
|
|
|
|
{
|
|
|
|
|
_lagCount++;
|
|
|
|
|
_isLag = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_isLag = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Frame => _frame;
|
2017-10-13 22:01:14 +00:00
|
|
|
|
|
|
|
|
|
public string SystemId => "SMS";
|
|
|
|
|
|
|
|
|
|
public bool DeterministicEmulation => true;
|
|
|
|
|
|
|
|
|
|
public void ResetCounters()
|
|
|
|
|
{
|
|
|
|
|
_frame = 0;
|
|
|
|
|
_lagCount = 0;
|
|
|
|
|
_isLag = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public CoreComm CoreComm { get; }
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
2019-02-23 17:31:09 +00:00
|
|
|
|
if (blip_L != null)
|
|
|
|
|
{
|
|
|
|
|
blip_L.Dispose();
|
|
|
|
|
blip_L = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (blip_R != null)
|
|
|
|
|
{
|
|
|
|
|
blip_R.Dispose();
|
|
|
|
|
blip_R = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Audio
|
|
|
|
|
|
|
|
|
|
public BlipBuffer blip_L = new BlipBuffer(4096);
|
|
|
|
|
public BlipBuffer blip_R = new BlipBuffer(4096);
|
|
|
|
|
const int blipbuffsize = 4096;
|
|
|
|
|
|
|
|
|
|
public uint sampleclock;
|
|
|
|
|
public int old_s_L = 0;
|
|
|
|
|
public int old_s_R = 0;
|
|
|
|
|
|
|
|
|
|
public bool CanProvideAsync { get { return false; } }
|
|
|
|
|
|
|
|
|
|
public void SetSyncMode(SyncSoundMode mode)
|
|
|
|
|
{
|
|
|
|
|
if (mode != SyncSoundMode.Sync)
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException("Only sync mode is supported");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void GetSamplesAsync(short[] samples)
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException("Async not supported");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SyncSoundMode SyncMode
|
|
|
|
|
{
|
|
|
|
|
get { return SyncSoundMode.Sync; }
|
2017-10-13 22:01:14 +00:00
|
|
|
|
}
|
2019-02-23 17:31:09 +00:00
|
|
|
|
|
|
|
|
|
public void GetSamplesSync(out short[] samples, out int nsamp)
|
|
|
|
|
{
|
|
|
|
|
if (!disablePSG)
|
|
|
|
|
{
|
|
|
|
|
blip_L.EndFrame(sampleclock);
|
|
|
|
|
blip_R.EndFrame(sampleclock);
|
|
|
|
|
|
|
|
|
|
nsamp = Math.Max(Math.Max(blip_L.SamplesAvailable(), blip_R.SamplesAvailable()), 1);
|
|
|
|
|
samples = new short[nsamp * 2];
|
|
|
|
|
|
|
|
|
|
blip_L.ReadSamplesLeft(samples, nsamp);
|
|
|
|
|
blip_R.ReadSamplesRight(samples, nsamp);
|
|
|
|
|
|
|
|
|
|
ApplyYMAudio(samples);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
nsamp = 735;
|
|
|
|
|
samples = new short[nsamp * 2];
|
|
|
|
|
ApplyYMAudio(samples);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sampleclock = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DiscardSamples()
|
|
|
|
|
{
|
|
|
|
|
blip_L.Clear();
|
|
|
|
|
blip_R.Clear();
|
|
|
|
|
sampleclock = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ApplyYMAudio(short[] samples)
|
|
|
|
|
{
|
|
|
|
|
if (HasYM2413)
|
|
|
|
|
{
|
|
|
|
|
short[] fmsamples = new short[samples.Length];
|
|
|
|
|
YM2413.GetSamples(fmsamples);
|
|
|
|
|
//naive mixing. need to study more
|
|
|
|
|
int len = samples.Length;
|
|
|
|
|
for (int i = 0; i < len; i++)
|
|
|
|
|
{
|
|
|
|
|
short fmsamp = fmsamples[i];
|
|
|
|
|
samples[i] = (short)(samples[i] + fmsamp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2017-10-13 22:01:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|