BizHawk/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs

436 lines
9.7 KiB
C#
Raw Normal View History

2018-12-05 15:01:25 +00:00
using BizHawk.Emulation.Common;
2017-06-11 22:06:50 +00:00
using System;
using System.Collections.Generic;
2017-05-24 23:36:34 +00:00
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
2017-08-25 15:28:19 +00:00
public partial class A7800Hawk : IEmulator, IVideoProvider, ISoundProvider
2017-05-24 23:36:34 +00:00
{
public IEmulatorServiceProvider ServiceProvider { get; }
2017-06-11 22:06:50 +00:00
public ControllerDefinition ControllerDefinition => _controllerDeck.Definition;
2017-05-24 23:36:34 +00:00
2017-05-28 00:40:13 +00:00
//Maria related variables
public int cycle;
public int cpu_cycle;
public bool cpu_is_haltable;
public bool cpu_is_halted;
public bool cpu_halt_pending;
public bool cpu_resume_pending;
2017-05-28 00:40:13 +00:00
// input state of controllers and console
public byte p1_state;
public byte p2_state;
public byte p1_fire;
public byte p2_fire;
public byte p1_fire_2x;
public byte p2_fire_2x;
public byte con_state;
public bool left_toggle;
public bool right_toggle;
public bool left_was_pressed;
public bool right_was_pressed;
public bool p1_is_2button;
public bool p2_is_2button;
public bool p1_is_lightgun;
public bool p2_is_lightgun;
public float p1_lightgun_x;
public float p1_lightgun_y;
public float p2_lightgun_x;
public float p2_lightgun_y;
2017-07-31 14:13:57 +00:00
public int lg_1_counting_down;
public int lg_1_counting_down_2;
public int lg_2_counting_down;
public int lg_2_counting_down_2;
public byte lg_1_trigger_hit;
public byte lg_2_trigger_hit;
2018-11-12 15:29:11 +00:00
public int temp_s_tia;
public int temp_s_pokey;
public int samp_l, samp_c;
public uint master_audio_clock;
public int temp;
2017-06-11 22:06:50 +00:00
// there are 4 maria cycles in a CPU cycle (fast access, both NTSC and PAL)
// if the 6532 or TIA are accessed (PC goes to one of those addresses) the next access will be slower by 1/2 a CPU cycle
// i.e. it will take 6 Maria cycles instead of 4
public bool slow_access = false;
2017-07-25 15:30:53 +00:00
public int slow_countdown;
2017-06-11 22:06:50 +00:00
2017-05-24 23:36:34 +00:00
public void FrameAdvance(IController controller, bool render, bool rendersound)
{
2017-06-11 22:06:50 +00:00
if (_tracer.Enabled)
{
cpu.TraceCallback = s => _tracer.Put(s);
}
else
{
cpu.TraceCallback = null;
}
2017-05-24 23:36:34 +00:00
_frame++;
if (controller.IsPressed("Power"))
{
// it seems that theMachine.Reset() doesn't clear ram, etc
// this should leave hsram intact but clear most other things
HardReset();
}
_islag = true;
GetControllerState(controller);
GetConsoleState(controller);
maria.RunFrame();
2017-05-24 23:36:34 +00:00
if (_islag)
{
_lagcount++;
}
}
public void RunCPUCycle()
{
2017-07-25 15:30:53 +00:00
if (slow_countdown==0)
{
cpu_cycle++;
}
else
{
slow_countdown--;
}
2017-07-29 19:18:45 +00:00
2017-07-31 14:13:57 +00:00
if (p1_is_lightgun)
2017-07-29 19:18:45 +00:00
{
2017-07-31 14:13:57 +00:00
if (lg_1_counting_down > 0)
2017-07-29 19:18:45 +00:00
{
2017-07-31 14:13:57 +00:00
lg_1_counting_down--;
if (lg_1_counting_down == 0 && lg_1_counting_down_2 > 0)
{
lg_1_trigger_hit = 0;
lg_1_counting_down = 454;
lg_1_counting_down_2--;
}
if (lg_1_counting_down < 424)
{
lg_1_trigger_hit = 0x80;
}
}
if ((maria.scanline - 20) == (p1_lightgun_y - 4))
{
if (maria.cycle == (132 + p1_lightgun_x))
{
// return true 64 cycles into the future
lg_1_counting_down = 64;
lg_1_counting_down_2 = 9;
}
2017-07-29 19:18:45 +00:00
}
}
2017-07-31 14:13:57 +00:00
if (p2_is_lightgun)
2017-07-29 19:18:45 +00:00
{
2017-07-31 14:13:57 +00:00
if (lg_2_counting_down > 0)
{
2017-07-31 14:13:57 +00:00
lg_2_counting_down--;
if (lg_2_counting_down == 0 && lg_2_counting_down_2 > 0)
{
lg_2_trigger_hit = 0;
lg_2_counting_down = 454;
lg_2_counting_down_2--;
}
if (lg_2_counting_down < 424)
{
lg_2_trigger_hit = 0x80;
}
}
2017-07-31 14:13:57 +00:00
if ((maria.scanline - 20) == (p2_lightgun_y - 4))
2017-07-29 19:18:45 +00:00
{
2017-07-31 14:13:57 +00:00
if (maria.cycle == (132 + p2_lightgun_x))
{
// return true 64 cycles into the future
lg_2_counting_down = 64;
lg_2_counting_down_2 = 9;
}
2017-07-29 19:18:45 +00:00
}
}
tia._hsyncCnt++;
tia._hsyncCnt %= 454;
2018-12-05 15:01:25 +00:00
// do the audio sampling of TIA audio
if (tia._hsyncCnt == 113 || tia._hsyncCnt == 340)
{
2018-11-12 15:29:11 +00:00
temp_s_tia = tia.Execute(0);
}
2017-05-28 00:40:13 +00:00
// tick the m6532 timer, which is still active although not recommended to use
2017-07-25 15:30:53 +00:00
// also it runs off of the cpu cycle timer
2017-08-25 14:21:10 +00:00
// similarly tick the pokey if it is in use
2017-07-25 15:30:53 +00:00
if (cpu_cycle== 4)
{
m6532.Timer.Tick();
}
2018-11-12 15:29:11 +00:00
samp_l = temp_s_tia + 3 * temp_s_pokey;
if (samp_l != samp_c)
{
_blip.AddDelta(master_audio_clock, samp_l - samp_c);
samp_c = samp_l;
}
temp++;
if (temp == 4)
2017-08-25 14:21:10 +00:00
{
2018-11-12 15:29:11 +00:00
// the pokey chip ticks at the nominal cpu speed, but is unaffected by cpu slowdown (I think)
if (is_pokey)
{
pokey.Tick();
temp_s_pokey = pokey.sample();
}
master_audio_clock++;
temp = 0;
2017-08-25 14:21:10 +00:00
}
2018-11-12 15:29:11 +00:00
2017-08-25 14:21:10 +00:00
if (cpu_cycle <= (2 + (slow_access ? 1 : 0)))
2017-05-28 00:40:13 +00:00
{
cpu_is_haltable = true;
}
else
{
cpu_is_haltable = false;
}
// the time a cpu cycle takes depends on the status of the address bus
// any address in range of the TIA or m6532 takes an extra cycle to complete
if (cpu_cycle == (4 + (slow_access ? 2 : 0)))
{
if (!cpu_is_halted)
{
2017-06-11 22:06:50 +00:00
cpu.ExecuteOne();
2017-07-25 15:30:53 +00:00
// we need to stall the next cpu cycle from starting if the current one is a slow access
if (slow_access)
{
slow_access = false;
slow_countdown = 2;
}
}
else
{
// we still want to keep track of CPU time even if it is halted, so increment the counter here
// The basic 6502 has no halt state, this feature is specific to SALLY
cpu.TotalExecutedCycles++;
}
cpu_cycle = 0;
if (cpu_halt_pending)
{
cpu_halt_pending = false;
cpu_is_halted = true;
2017-06-11 22:06:50 +00:00
}
if (cpu_resume_pending)
2017-06-11 22:06:50 +00:00
{
cpu_resume_pending = false;
cpu_is_halted = false;
}
}
2017-05-24 23:36:34 +00:00
}
public void GetControllerState(IController controller)
2017-06-11 22:06:50 +00:00
{
InputCallbacks.Call();
p1_state = _controllerDeck.ReadPort1(controller);
p2_state = _controllerDeck.ReadPort2(controller);
p1_fire = _controllerDeck.ReadFire1(controller);
p2_fire = _controllerDeck.ReadFire2(controller);
p1_fire_2x = _controllerDeck.ReadFire1_2x(controller);
p2_fire_2x = _controllerDeck.ReadFire2_2x(controller);
p1_is_2button = _controllerDeck.Is_2_button1(controller);
p2_is_2button = _controllerDeck.Is_2_button2(controller);
p1_is_lightgun = _controllerDeck.Is_LightGun1(controller, out p1_lightgun_x, out p1_lightgun_y);
p2_is_lightgun = _controllerDeck.Is_LightGun2(controller, out p2_lightgun_x, out p2_lightgun_y);
}
public void GetConsoleState(IController controller)
{
byte result = 0;
2017-07-31 14:23:33 +00:00
if (controller.IsPressed("Toggle Right Difficulty"))
{
if (!right_was_pressed)
{
right_toggle = !right_toggle;
}
right_was_pressed = true;
result |= (byte)((right_toggle ? 1 : 0) << 7);
}
else
{
right_was_pressed = false;
result |= (byte)((right_toggle ? 1 : 0) << 7);
}
2017-07-31 14:23:33 +00:00
if (controller.IsPressed("Toggle Left Difficulty"))
{
if (!left_was_pressed)
{
left_toggle = !left_toggle;
}
left_was_pressed = true;
result |= (byte)((left_toggle ? 1 : 0) << 6);
}
else
{
left_was_pressed = false;
result |= (byte)((left_toggle ? 1 : 0) << 6);
}
if (!controller.IsPressed("Pause"))
{
result |= (1 << 3);
}
if (!controller.IsPressed("Select"))
{
result |= (1 << 1);
}
if (!controller.IsPressed("Reset"))
{
result |= 1;
}
2017-06-11 22:06:50 +00:00
con_state = result;
2017-06-11 22:06:50 +00:00
}
2017-05-24 23:36:34 +00:00
public int Frame => _frame;
2017-08-04 22:17:14 +00:00
public string SystemId => "A78";
2017-05-24 23:36:34 +00:00
public bool DeterministicEmulation { get; set; }
public void ResetCounters()
{
_frame = 0;
_lagcount = 0;
_islag = false;
}
public CoreComm CoreComm { get; }
public void Dispose()
{
maria = null;
tia = null;
2017-05-25 00:40:02 +00:00
m6532 = null;
2018-11-12 15:29:11 +00:00
DisposeSound();
2017-05-24 23:36:34 +00:00
}
#region Video provider
public int _frameHz = 60;
public int _screen_width = 320;
public int _screen_height = 263;
public int _vblanklines = 20;
public int[] _vidbuffer;
public int[] GetVideoBuffer()
{
if (_syncSettings.Filter != "None")
{
apply_filter();
}
return _vidbuffer;
}
public int VirtualWidth => 320;
public int VirtualHeight => _screen_height - _vblanklines;
public int BufferWidth => 320;
public int BufferHeight => _screen_height - _vblanklines;
public int BackgroundColor => unchecked((int)0xff000000);
public int VsyncNumerator => _frameHz;
public int VsyncDenominator => 1;
public void apply_filter()
{
}
public static Dictionary<string, string> ValidFilterTypes = new Dictionary<string, string>
{
{ "None", "None"},
{ "NTSC", "NTSC"},
{ "Pal", "Pal"}
};
#endregion
2017-08-25 15:28:19 +00:00
#region Sound provider
2018-11-12 15:29:11 +00:00
private BlipBuffer _blip = new BlipBuffer(4096);
2017-08-25 15:28:19 +00:00
public bool CanProvideAsync => false;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Only Sync mode is supported.");
}
}
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
2018-11-12 15:29:11 +00:00
_blip.EndFrame(master_audio_clock);
2017-08-26 16:46:59 +00:00
2018-11-12 15:29:11 +00:00
nsamp = _blip.SamplesAvailable();
samples = new short[nsamp * 2];
_blip.ReadSamples(samples, nsamp, true);
for (int i = 0; i < nsamp * 2; i += 2)
2017-08-26 16:46:59 +00:00
{
2018-11-12 15:29:11 +00:00
samples[i + 1] = samples[i];
2017-08-26 16:46:59 +00:00
}
2017-08-25 15:28:19 +00:00
2018-11-12 15:29:11 +00:00
master_audio_clock = 0;
temp = 0;
2017-08-25 15:28:19 +00:00
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
tia.AudioClocks = 0;
2018-11-12 15:29:11 +00:00
master_audio_clock = 0;
_blip.Clear();
}
public void DisposeSound()
{
_blip.Clear();
_blip.Dispose();
_blip = null;
2017-08-25 15:28:19 +00:00
}
#endregion
2017-05-24 23:36:34 +00:00
}
}