Add files via upload

This commit is contained in:
alyosha-tas 2017-05-24 19:36:34 -04:00 committed by GitHub
parent 3a020a1c40
commit b2eecd7bec
10 changed files with 1294 additions and 0 deletions

View File

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class A7800Hawk : IDebuggable
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
return new Dictionary<string, RegisterValue>
{
["A"] = cpu.A,
["X"] = cpu.X,
["Y"] = cpu.Y,
["S"] = cpu.S,
["PC"] = cpu.PC,
["Flag C"] = cpu.FlagC,
["Flag Z"] = cpu.FlagZ,
["Flag I"] = cpu.FlagI,
["Flag D"] = cpu.FlagD,
["Flag B"] = cpu.FlagB,
["Flag V"] = cpu.FlagV,
["Flag N"] = cpu.FlagN,
["Flag T"] = cpu.FlagT
};
}
public void SetCpuRegister(string register, int value)
{
switch (register)
{
default:
throw new InvalidOperationException();
case "A":
cpu.A = (byte)value;
break;
case "X":
cpu.X = (byte)value;
break;
case "Y":
cpu.Y = (byte)value;
break;
case "S":
cpu.S = (byte)value;
break;
case "PC":
cpu.PC = (ushort)value;
break;
case "Flag I":
cpu.FlagI = value > 0;
break;
}
}
public IMemoryCallbackSystem MemoryCallbacks
{
[FeatureNotImplemented]
get { throw new NotImplementedException(); }
}
public bool CanStep(StepType type)
{
return false;
}
[FeatureNotImplemented]
public void Step(StepType type)
{
throw new NotImplementedException();
}
public int TotalExecutedCycles
{
get { return cpu.TotalExecutedCycles; }
}
}
}

View File

@ -0,0 +1,51 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class A7800Hawk : IEmulator
{
public IEmulatorServiceProvider ServiceProvider { get; }
public ControllerDefinition ControllerDefinition { get; private set; }
public void FrameAdvance(IController controller, bool render, bool rendersound)
{
_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();
}
if (_islag)
{
_lagcount++;
}
}
public int Frame => _frame;
public string SystemId => "A7800";
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;
}
}
}

View File

@ -0,0 +1,24 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class A7800Hawk : IInputPollable
{
public int LagCount
{
get { return _lagcount; }
set { _lagcount = value; }
}
public bool IsLagFrame
{
get { return _islag; }
set { _islag = value; }
}
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
private bool _islag = true;
private int _lagcount;
}
}

View File

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class A7800Hawk
{
private IMemoryDomains MemoryDomains;
public void SetupMemoryDomains()
{
var domains = new List<MemoryDomain>
{
new MemoryDomainDelegate(
"Main RAM",
RAM.Length,
MemoryDomain.Endian.Little,
addr => RAM[addr],
(addr, value) => RAM[addr] = value,
1),
new MemoryDomainDelegate(
"TIA Registers",
TIA_regs.Length,
MemoryDomain.Endian.Little,
addr => TIA_regs[addr],
(addr, value) => TIA_regs[addr] = value,
1),
new MemoryDomainDelegate(
"Maria Registers",
Maria_regs.Length,
MemoryDomain.Endian.Little,
addr => Maria_regs[addr],
(addr, value) => Maria_regs[addr] = value,
1),
new MemoryDomainDelegate(
"6532 Registers",
regs_6532.Length,
MemoryDomain.Endian.Little,
addr => regs_6532[addr],
(addr, value) => regs_6532[addr] = value,
1),
new MemoryDomainDelegate(
"Ram Block 0",
0xB,
MemoryDomain.Endian.Little,
addr => RAM[addr-0x840],
(addr, value) => RAM[addr-0x840] = value,
1
),
new MemoryDomainDelegate(
"Ram Block 1",
0xB,
MemoryDomain.Endian.Little,
addr => RAM[addr-0x940],
(addr, value) => RAM[addr-0x940] = value,
1
),
new MemoryDomainDelegate(
"System Bus",
0X10000,
MemoryDomain.Endian.Little,
addr => PeekSystemBus(addr),
(addr, value) => PokeSystemBus(addr, value),
1
)
};
MemoryDomains = new MemoryDomainList(domains);
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(MemoryDomains);
}
private byte PeekSystemBus(long addr)
{
return 0;
}
private void PokeSystemBus(long addr, byte value)
{
}
}
}

View File

@ -0,0 +1,26 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class A7800Hawk : ISaveRam
{
public byte[] CloneSaveRam()
{
return (byte[])_hsram.Clone();
}
public void StoreSaveRam(byte[] data)
{
Buffer.BlockCopy(data, 0, _hsram, 0, data.Length);
}
public bool SaveRamModified
{
get
{
return false;
}
}
}
}

View File

@ -0,0 +1,59 @@
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class A7800Hawk : IStatable
{
public bool BinarySaveStatesPreferred => true;
public void SaveStateText(TextWriter writer)
{
SyncState(new Serializer(writer));
}
public void LoadStateText(TextReader reader)
{
SyncState(new Serializer(reader));
}
public void SaveStateBinary(BinaryWriter bw)
{
SyncState(new Serializer(bw));
}
public void LoadStateBinary(BinaryReader br)
{
SyncState(new Serializer(br));
}
public byte[] SaveStateBinary()
{
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
}
private void SyncState(Serializer ser)
{
byte[] core = null;
if (ser.IsWriter)
{
var ms = new MemoryStream();
ms.Close();
core = ms.ToArray();
}
ser.BeginSection("Atari7800");
ser.Sync("core", ref core, false);
ser.Sync("Lag", ref _lagcount);
ser.Sync("Frame", ref _frame);
ser.Sync("IsLag", ref _islag);
ser.EndSection();
}
}
}

View File

@ -0,0 +1,298 @@
using System;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.M6502;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
[CoreAttributes(
"A7800Hawk",
"",
isPorted: false,
isReleased: true)]
[ServiceNotApplicable(typeof(ISettable<,>), typeof(IDriveLight))]
public partial class A7800Hawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable
{
// memory domains
public byte[] TIA_regs = new byte[0x20];
public byte[] Maria_regs = new byte[0x20];
public byte[] RAM = new byte[0x1000];
public byte[] regs_6532 = new byte[0x80];
private readonly byte[] _rom;
private readonly byte[] _hsbios;
private readonly byte[] _bios;
private readonly byte[] _hsram = new byte[2048];
private int _frame = 0;
public MOS6502X cpu;
public Maria maria;
private bool _isPAL;
public M6532 m6532;
public TIA tia;
public A7800Hawk(CoreComm comm, GameInfo game, byte[] rom, string gameDbFn)
{
var ser = new BasicServiceProvider(this);
ser.Register<IVideoProvider>(maria);
ser.Register<ISoundProvider>(tia);
ServiceProvider = ser;
CoreComm = comm;
byte[] highscoreBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_HSC", false, "Some functions may not work without the high score BIOS.");
byte[] palBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_PAL", false, "The game will not run if the correct region BIOS is not available.");
byte[] ntscBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_NTSC", false, "The game will not run if the correct region BIOS is not available.");
if (rom.Length % 1024 == 128)
{
Console.WriteLine("Trimming 128 byte .a78 header...");
byte[] newrom = new byte[rom.Length - 128];
Buffer.BlockCopy(rom, 128, newrom, 0, newrom.Length);
rom = newrom;
}
_isPAL = false;
// look up hash in gamedb to see what mapper to use
// if none found default is zero
// also check for PAL region
string hash_md5 = null;
string s_mapper = null;
hash_md5 = "md5:" + rom.HashMD5(0, rom.Length);
var gi = Database.CheckDatabase(hash_md5);
if (gi != null)
{
var dict = gi.GetOptionsDict();
if (!dict.ContainsKey("PAL"))
{
_isPAL = true;
}
if (!dict.ContainsKey("board"))
{
s_mapper = dict["board"];
}
else
throw new Exception("No Board selected for this mapper");
}
else
{
throw new Exception("ROM not in gamedb");
}
_rom = rom;
_hsbios = highscoreBios;
_bios = _isPAL ? palBios : ntscBios;
if (_bios == null)
{
throw new MissingFirmwareException("The BIOS corresponding to the region of the game you loaded is required to run Atari 7800 games.");
}
// set up palette and frame rate
if (_isPAL)
{
maria._frameHz = 50;
maria._palette = PALPalette;
}
else
{
maria._frameHz = 60;
maria._palette = NTSCPalette;
}
HardReset();
}
public DisplayType Region => _isPAL ? DisplayType.PAL : DisplayType.NTSC;
public A7800HawkControl ControlAdapter { get; private set; }
private void HardReset()
{
cpu.Reset();
}
/*
* MariaTables.cs
*
* Palette tables for the Maria class.
* All derived from Dan Boris' 7800/MAME code.
*
* Copyright © 2004 Mike Murphy
*
*/
public static readonly int[] NTSCPalette =
{
0x000000, 0x1c1c1c, 0x393939, 0x595959, // Grey
0x797979, 0x929292, 0xababab, 0xbcbcbc,
0xcdcdcd, 0xd9d9d9, 0xe6e6e6, 0xececec,
0xf2f2f2, 0xf8f8f8, 0xffffff, 0xffffff,
0x391701, 0x5e2304, 0x833008, 0xa54716, // Gold
0xc85f24, 0xe37820, 0xff911d, 0xffab1d,
0xffc51d, 0xffce34, 0xffd84c, 0xffe651,
0xfff456, 0xfff977, 0xffff98, 0xffff98,
0x451904, 0x721e11, 0x9f241e, 0xb33a20, // Orange
0xc85122, 0xe36920, 0xff811e, 0xff8c25,
0xff982c, 0xffae38, 0xffc545, 0xffc559,
0xffc66d, 0xffd587, 0xffe4a1, 0xffe4a1,
0x4a1704, 0x7e1a0d, 0xb21d17, 0xc82119, // Red Orange
0xdf251c, 0xec3b38, 0xfa5255, 0xfc6161,
0xff706e, 0xff7f7e, 0xff8f8f, 0xff9d9e,
0xffabad, 0xffb9bd, 0xffc7ce, 0xffc7ce,
0x050568, 0x3b136d, 0x712272, 0x8b2a8c, // Pink
0xa532a6, 0xb938ba, 0xcd3ecf, 0xdb47dd,
0xea51eb, 0xf45ff5, 0xfe6dff, 0xfe7afd,
0xff87fb, 0xff95fd, 0xffa4ff, 0xffa4ff,
0x280479, 0x400984, 0x590f90, 0x70249d, // Purple
0x8839aa, 0xa441c3, 0xc04adc, 0xd054ed,
0xe05eff, 0xe96dff, 0xf27cff, 0xf88aff,
0xff98ff, 0xfea1ff, 0xfeabff, 0xfeabff,
0x35088a, 0x420aad, 0x500cd0, 0x6428d0, // Purple Blue
0x7945d0, 0x8d4bd4, 0xa251d9, 0xb058ec,
0xbe60ff, 0xc56bff, 0xcc77ff, 0xd183ff,
0xd790ff, 0xdb9dff, 0xdfaaff, 0xdfaaff,
0x051e81, 0x0626a5, 0x082fca, 0x263dd4, // Blue1
0x444cde, 0x4f5aee, 0x5a68ff, 0x6575ff,
0x7183ff, 0x8091ff, 0x90a0ff, 0x97a9ff,
0x9fb2ff, 0xafbeff, 0xc0cbff, 0xc0cbff,
0x0c048b, 0x2218a0, 0x382db5, 0x483ec7, // Blue2
0x584fda, 0x6159ec, 0x6b64ff, 0x7a74ff,
0x8a84ff, 0x918eff, 0x9998ff, 0xa5a3ff,
0xb1aeff, 0xb8b8ff, 0xc0c2ff, 0xc0c2ff,
0x1d295a, 0x1d3876, 0x1d4892, 0x1c5cac, // Light Blue
0x1c71c6, 0x3286cf, 0x489bd9, 0x4ea8ec,
0x55b6ff, 0x70c7ff, 0x8cd8ff, 0x93dbff,
0x9bdfff, 0xafe4ff, 0xc3e9ff, 0xc3e9ff,
0x2f4302, 0x395202, 0x446103, 0x417a12, // Turquoise
0x3e9421, 0x4a9f2e, 0x57ab3b, 0x5cbd55,
0x61d070, 0x69e27a, 0x72f584, 0x7cfa8d,
0x87ff97, 0x9affa6, 0xadffb6, 0xadffb6,
0x0a4108, 0x0d540a, 0x10680d, 0x137d0f, // Green Blue
0x169212, 0x19a514, 0x1cb917, 0x1ec919,
0x21d91b, 0x47e42d, 0x6ef040, 0x78f74d,
0x83ff5b, 0x9aff7a, 0xb2ff9a, 0xb2ff9a,
0x04410b, 0x05530e, 0x066611, 0x077714, // Green
0x088817, 0x099b1a, 0x0baf1d, 0x48c41f,
0x86d922, 0x8fe924, 0x99f927, 0xa8fc41,
0xb7ff5b, 0xc9ff6e, 0xdcff81, 0xdcff81,
0x02350f, 0x073f15, 0x0c4a1c, 0x2d5f1e, // Yellow Green
0x4f7420, 0x598324, 0x649228, 0x82a12e,
0xa1b034, 0xa9c13a, 0xb2d241, 0xc4d945,
0xd6e149, 0xe4f04e, 0xf2ff53, 0xf2ff53,
0x263001, 0x243803, 0x234005, 0x51541b, // Orange Green
0x806931, 0x978135, 0xaf993a, 0xc2a73e,
0xd5b543, 0xdbc03d, 0xe1cb38, 0xe2d836,
0xe3e534, 0xeff258, 0xfbff7d, 0xfbff7d,
0x401a02, 0x581f05, 0x702408, 0x8d3a13, // Light Orange
0xab511f, 0xb56427, 0xbf7730, 0xd0853a,
0xe19344, 0xeda04e, 0xf9ad58, 0xfcb75c,
0xffc160, 0xffc671, 0xffcb83, 0xffcb83
};
public static readonly int[] PALPalette =
{
0x000000, 0x1c1c1c, 0x393939, 0x595959, // Grey
0x797979, 0x929292, 0xababab, 0xbcbcbc,
0xcdcdcd, 0xd9d9d9, 0xe6e6e6, 0xececec,
0xf2f2f2, 0xf8f8f8, 0xffffff, 0xffffff,
0x263001, 0x243803, 0x234005, 0x51541b, // Orange Green
0x806931, 0x978135, 0xaf993a, 0xc2a73e,
0xd5b543, 0xdbc03d, 0xe1cb38, 0xe2d836,
0xe3e534, 0xeff258, 0xfbff7d, 0xfbff7d,
0x263001, 0x243803, 0x234005, 0x51541b, // Orange Green
0x806931, 0x978135, 0xaf993a, 0xc2a73e,
0xd5b543, 0xdbc03d, 0xe1cb38, 0xe2d836,
0xe3e534, 0xeff258, 0xfbff7d, 0xfbff7d,
0x401a02, 0x581f05, 0x702408, 0x8d3a13, // Light Orange
0xab511f, 0xb56427, 0xbf7730, 0xd0853a,
0xe19344, 0xeda04e, 0xf9ad58, 0xfcb75c,
0xffc160, 0xffc671, 0xffcb83, 0xffcb83,
0x391701, 0x5e2304, 0x833008, 0xa54716, // Gold
0xc85f24, 0xe37820, 0xff911d, 0xffab1d,
0xffc51d, 0xffce34, 0xffd84c, 0xffe651,
0xfff456, 0xfff977, 0xffff98, 0xffff98,
0x451904, 0x721e11, 0x9f241e, 0xb33a20, // Orange
0xc85122, 0xe36920, 0xff811e, 0xff8c25,
0xff982c, 0xffae38, 0xffc545, 0xffc559,
0xffc66d, 0xffd587, 0xffe4a1, 0xffe4a1,
0x4a1704, 0x7e1a0d, 0xb21d17, 0xc82119, // Red Orange
0xdf251c, 0xec3b38, 0xfa5255, 0xfc6161,
0xff706e, 0xff7f7e, 0xff8f8f, 0xff9d9e,
0xffabad, 0xffb9bd, 0xffc7ce, 0xffc7ce,
0x050568, 0x3b136d, 0x712272, 0x8b2a8c, // Pink
0xa532a6, 0xb938ba, 0xcd3ecf, 0xdb47dd,
0xea51eb, 0xf45ff5, 0xfe6dff, 0xfe7afd,
0xff87fb, 0xff95fd, 0xffa4ff, 0xffa4ff,
0x280479, 0x400984, 0x590f90, 0x70249d, // Purple
0x8839aa, 0xa441c3, 0xc04adc, 0xd054ed,
0xe05eff, 0xe96dff, 0xf27cff, 0xf88aff,
0xff98ff, 0xfea1ff, 0xfeabff, 0xfeabff,
0x051e81, 0x0626a5, 0x082fca, 0x263dd4, // Blue1
0x444cde, 0x4f5aee, 0x5a68ff, 0x6575ff,
0x7183ff, 0x8091ff, 0x90a0ff, 0x97a9ff,
0x9fb2ff, 0xafbeff, 0xc0cbff, 0xc0cbff,
0x0c048b, 0x2218a0, 0x382db5, 0x483ec7, // Blue2
0x584fda, 0x6159ec, 0x6b64ff, 0x7a74ff,
0x8a84ff, 0x918eff, 0x9998ff, 0xa5a3ff,
0xb1aeff, 0xb8b8ff, 0xc0c2ff, 0xc0c2ff,
0x1d295a, 0x1d3876, 0x1d4892, 0x1c5cac, // Light Blue
0x1c71c6, 0x3286cf, 0x489bd9, 0x4ea8ec,
0x55b6ff, 0x70c7ff, 0x8cd8ff, 0x93dbff,
0x9bdfff, 0xafe4ff, 0xc3e9ff, 0xc3e9ff,
0x2f4302, 0x395202, 0x446103, 0x417a12, // Turquoise
0x3e9421, 0x4a9f2e, 0x57ab3b, 0x5cbd55,
0x61d070, 0x69e27a, 0x72f584, 0x7cfa8d,
0x87ff97, 0x9affa6, 0xadffb6, 0xadffb6,
0x0a4108, 0x0d540a, 0x10680d, 0x137d0f, // Green Blue
0x169212, 0x19a514, 0x1cb917, 0x1ec919,
0x21d91b, 0x47e42d, 0x6ef040, 0x78f74d,
0x83ff5b, 0x9aff7a, 0xb2ff9a, 0xb2ff9a,
0x04410b, 0x05530e, 0x066611, 0x077714, // Green
0x088817, 0x099b1a, 0x0baf1d, 0x48c41f,
0x86d922, 0x8fe924, 0x99f927, 0xa8fc41,
0xb7ff5b, 0xc9ff6e, 0xdcff81, 0xdcff81,
0x02350f, 0x073f15, 0x0c4a1c, 0x2d5f1e, // Yellow Green
0x4f7420, 0x598324, 0x649228, 0x82a12e,
0xa1b034, 0xa9c13a, 0xb2d241, 0xc4d945,
0xd6e149, 0xe4f04e, 0xf2ff53, 0xf2ff53
};
}
}

View File

@ -0,0 +1,409 @@
using System;
using EMU7800.Core;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public class A7800HawkControl
{
private static readonly ControllerDefinition Joystick = new ControllerDefinition
{
Name = "Atari 7800 Joystick Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"BW", // should be "Color"??
"Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Right Difficulty",
// ports
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Trigger",
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Trigger"
}
};
private static readonly ControllerDefinition Paddles = new ControllerDefinition
{
Name = "Atari 7800 Paddle Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"BW", // should be "Color"??
"Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Right Difficulty",
// ports
"P1 Trigger",
"P2 Trigger",
"P3 Trigger",
"P4 Trigger"
},
FloatControls = // should be in [0..700000]
{
"P1 Paddle",
"P2 Paddle",
"P3 Paddle",
"P4 Paddle"
},
FloatRanges =
{
// what is the center point supposed to be here?
new[] { 0.0f, 0.0f, 700000.0f },
new[] { 0.0f, 0.0f, 700000.0f },
new[] { 0.0f, 0.0f, 700000.0f },
new[] { 0.0f, 0.0f, 700000.0f }
}
};
private static readonly ControllerDefinition Keypad = new ControllerDefinition
{
Name = "Atari 7800 Keypad Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"BW", // should be "Color"??
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Toggle Right Difficulty",
// ports
"P1 Keypad1", "P1 Keypad2", "P1 Keypad3",
"P1 Keypad4", "P1 Keypad5", "P1 Keypad6",
"P1 Keypad7", "P1 Keypad8", "P1 Keypad9",
"P1 KeypadA", "P1 Keypad0", "P1 KeypadP",
"P2 Keypad1", "P2 Keypad2", "P2 Keypad3",
"P2 Keypad4", "P2 Keypad5", "P2 Keypad6",
"P2 Keypad7", "P2 Keypad8", "P2 Keypad9",
"P2 KeypadA", "P2 Keypad0", "P2 KeypadP",
"P3 Keypad1", "P3 Keypad2", "P3 Keypad3",
"P3 Keypad4", "P3 Keypad5", "P3 Keypad6",
"P3 Keypad7", "P3 Keypad8", "P3 Keypad9",
"P3 KeypadA", "P3 Keypad0", "P3 KeypadP",
"P4 Keypad1", "P4 Keypad2", "P4 Keypad3",
"P4 Keypad4", "P4 Keypad5", "P4 Keypad6",
"P4 Keypad7", "P4 Keypad8", "P4 Keypad9",
"P4 KeypadA", "P4 Keypad0", "P4 KeypadP"
}
};
private static readonly ControllerDefinition Driving = new ControllerDefinition
{
Name = "Atari 7800 Driving Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"BW", // should be "Color"??
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Toggle Right Difficulty",
// ports
"P1 Trigger",
"P2 Trigger"
},
FloatControls = // should be in [0..3]
{
"P1 Driving",
"P2 Driving"
},
FloatRanges =
{
new[] { 0.0f, 0.0f, 3.0f },
new[] { 0.0f, 0.0f, 3.0f },
new[] { 0.0f, 0.0f, 3.0f }
}
};
private static readonly ControllerDefinition BoosterGrip = new ControllerDefinition
{
Name = "Atari 7800 Booster Grip Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"BW", // should be "Color"??
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Toggle Right Difficulty",
// ports
// NB: as referenced by the emu, p1t2 = p1t2, p1t3 = p2t2, p2t2 = p3t2, p2t3 = p4t2
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Trigger", "P1 Trigger 2", "P1 Trigger 3",
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Trigger", "P2 Trigger 2", "P2 Trigger 3"
}
};
private static readonly ControllerDefinition ProLineJoystick = new ControllerDefinition
{
Name = "Atari 7800 ProLine Joystick Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"Pause",
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Toggle Right Difficulty",
// ports
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Trigger", "P1 Trigger 2",
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Trigger", "P2 Trigger 2"
}
};
private static readonly ControllerDefinition Lightgun = new ControllerDefinition
{
Name = "Atari 7800 Light Gun Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"Pause",
"Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Right Difficulty",
// ports
"P1 Trigger",
"P2 Trigger"
},
FloatControls = // vpos should be actual scanline number. hpos should be in [0..319]??
{
"P1 VPos", "P1 HPos",
"P2 VPos", "P2 HPos"
},
FloatRanges =
{
// how many scanlines are there again??
new[] { 0.0f, 0.0f, 240.0f },
new[] { 0.0f, 0.0f, 319.0f },
new[] { 0.0f, 0.0f, 240.0f },
new[] { 0.0f, 0.0f, 319.0f }
}
};
private struct ControlAdapter
{
public readonly ControllerDefinition Type;
public readonly Controller Left;
public readonly Controller Right;
public readonly Action<IController, InputState> Convert;
public ControlAdapter(ControllerDefinition type, Controller left, Controller right, Action<IController, InputState> convert)
{
Type = type;
Left = left;
Right = right;
Convert = convert;
}
}
private static readonly ControlAdapter[] Adapters =
{
new ControlAdapter(Joystick, Controller.Joystick, Controller.Joystick, ConvertJoystick),
new ControlAdapter(Paddles, Controller.Paddles, Controller.Paddles, ConvertPaddles),
new ControlAdapter(Keypad, Controller.Keypad, Controller.Keypad, ConvertKeypad),
new ControlAdapter(Driving, Controller.Driving, Controller.Driving, ConvertDriving),
new ControlAdapter(BoosterGrip, Controller.BoosterGrip, Controller.BoosterGrip, ConvertBoosterGrip),
new ControlAdapter(ProLineJoystick, Controller.ProLineJoystick, Controller.ProLineJoystick, ConvertProLineJoystick),
new ControlAdapter(Lightgun, Controller.Lightgun, Controller.Lightgun, ConvertLightgun),
};
private static void ConvertConsoleButtons(IController c, InputState s)
{
s.RaiseInput(0, MachineInput.Reset, c.IsPressed("Reset"));
s.RaiseInput(0, MachineInput.Select, c.IsPressed("Select"));
s.RaiseInput(0, MachineInput.Color, c.IsPressed("BW"));
if (c.IsPressed("Toggle Left Difficulty"))
{
s.RaiseInput(0, MachineInput.LeftDifficulty, c.IsPressed("Toggle Left Difficulty"));
}
if (c.IsPressed("Toggle Right Difficulty"))
{
s.RaiseInput(0, MachineInput.RightDifficulty, c.IsPressed("Toggle Right Difficulty"));
}
}
private static void ConvertConsoleButtons7800(IController c, InputState s)
{
s.RaiseInput(0, MachineInput.Reset, c.IsPressed("Reset"));
s.RaiseInput(0, MachineInput.Select, c.IsPressed("Select"));
s.RaiseInput(0, MachineInput.Color, c.IsPressed("Pause"));
if (c.IsPressed("Toggle Left Difficulty"))
{
s.RaiseInput(0, MachineInput.LeftDifficulty, c.IsPressed("Toggle Left Difficulty"));
}
if (c.IsPressed("Toggle Right Difficulty"))
{
s.RaiseInput(0, MachineInput.RightDifficulty, c.IsPressed("Toggle Right Difficulty"));
}
}
private static void ConvertDirections(IController c, InputState s, int p)
{
string ps = $"P{p + 1} ";
s.RaiseInput(p, MachineInput.Up, c.IsPressed(ps + "Up"));
s.RaiseInput(p, MachineInput.Down, c.IsPressed(ps + "Down"));
s.RaiseInput(p, MachineInput.Left, c.IsPressed(ps + "Left"));
s.RaiseInput(p, MachineInput.Right, c.IsPressed(ps + "Right"));
}
private static void ConvertTrigger(IController c, InputState s, int p)
{
string ps = $"P{p + 1} ";
s.RaiseInput(p, MachineInput.Fire, c.IsPressed(ps + "Trigger"));
}
private static void ConvertJoystick(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons(c, s);
ConvertDirections(c, s, 0);
ConvertDirections(c, s, 1);
ConvertTrigger(c, s, 0);
ConvertTrigger(c, s, 1);
}
private static void ConvertPaddles(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons(c, s);
for (int i = 0; i < 4; i++)
{
string ps = $"P{i + 1} ";
ConvertTrigger(c, s, i);
s.RaisePaddleInput(i, 700000, (int)c.GetFloat(ps + "Trigger"));
}
}
private static void ConvertKeypad(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons(c, s);
for (int i = 0; i < 4; i++)
{
string ps = $"P{i + 1} ";
s.RaiseInput(i, MachineInput.NumPad1, c.IsPressed(ps + "Keypad1"));
s.RaiseInput(i, MachineInput.NumPad2, c.IsPressed(ps + "Keypad2"));
s.RaiseInput(i, MachineInput.NumPad3, c.IsPressed(ps + "Keypad3"));
s.RaiseInput(i, MachineInput.NumPad4, c.IsPressed(ps + "Keypad4"));
s.RaiseInput(i, MachineInput.NumPad5, c.IsPressed(ps + "Keypad5"));
s.RaiseInput(i, MachineInput.NumPad6, c.IsPressed(ps + "Keypad6"));
s.RaiseInput(i, MachineInput.NumPad7, c.IsPressed(ps + "Keypad7"));
s.RaiseInput(i, MachineInput.NumPad8, c.IsPressed(ps + "Keypad8"));
s.RaiseInput(i, MachineInput.NumPad9, c.IsPressed(ps + "Keypad9"));
s.RaiseInput(i, MachineInput.NumPadMult, c.IsPressed(ps + "KeypadA"));
s.RaiseInput(i, MachineInput.NumPad0, c.IsPressed(ps + "Keypad0"));
s.RaiseInput(i, MachineInput.NumPadHash, c.IsPressed(ps + "KeypadP"));
}
}
private static readonly MachineInput[] Drvlut =
{
MachineInput.Driving0,
MachineInput.Driving1,
MachineInput.Driving2,
MachineInput.Driving3
};
private static void ConvertDriving(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons(c, s);
ConvertTrigger(c, s, 0);
ConvertTrigger(c, s, 1);
s.RaiseInput(0, Drvlut[(int)c.GetFloat("P1 Driving")], true);
s.RaiseInput(1, Drvlut[(int)c.GetFloat("P2 Driving")], true);
}
private static void ConvertBoosterGrip(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons(c, s);
ConvertDirections(c, s, 0);
ConvertDirections(c, s, 1);
// weird mapping is intentional
s.RaiseInput(0, MachineInput.Fire, c.IsPressed("P1 Trigger"));
s.RaiseInput(0, MachineInput.Fire2, c.IsPressed("P1 Trigger 2"));
s.RaiseInput(1, MachineInput.Fire2, c.IsPressed("P1 Trigger 3"));
s.RaiseInput(1, MachineInput.Fire, c.IsPressed("P2 Trigger"));
s.RaiseInput(2, MachineInput.Fire2, c.IsPressed("P2 Trigger 2"));
s.RaiseInput(3, MachineInput.Fire2, c.IsPressed("P2 Trigger 3"));
}
private static void ConvertProLineJoystick(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons7800(c, s);
ConvertDirections(c, s, 0);
ConvertDirections(c, s, 1);
s.RaiseInput(0, MachineInput.Fire, c.IsPressed("P1 Trigger"));
s.RaiseInput(0, MachineInput.Fire2, c.IsPressed("P1 Trigger 2"));
s.RaiseInput(1, MachineInput.Fire, c.IsPressed("P2 Trigger"));
s.RaiseInput(1, MachineInput.Fire2, c.IsPressed("P2 Trigger 2"));
}
private static void ConvertLightgun(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons7800(c, s);
ConvertTrigger(c, s, 0);
ConvertTrigger(c, s, 1);
s.RaiseLightgunPos(0, (int)c.GetFloat("P1 VPos"), (int)c.GetFloat("P1 HPos"));
s.RaiseLightgunPos(1, (int)c.GetFloat("P2 VPos"), (int)c.GetFloat("P2 HPos"));
}
public Action<IController, InputState> Convert { get; private set; }
public ControllerDefinition ControlType { get; private set; }
public A7800HawkControl(MachineBase mac)
{
var l = mac.InputState.LeftControllerJack;
var r = mac.InputState.RightControllerJack;
foreach (var a in Adapters)
{
if (a.Left == l && a.Right == r)
{
Convert = a.Convert;
ControlType = a.Type;
return;
}
}
throw new Exception($"Couldn't connect Atari 7800 controls \"{l}\" and \"{r}\"");
}
}
}

View File

@ -0,0 +1,225 @@
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
// Emulates the M6532 RIOT Chip
public class M6532
{
private byte _ddRa = 0x00;
private byte _ddRb = 0x00;
private byte _outputA = 0x00;
public TimerData Timer;
public M6532()
{
// arbitrary value to start with.
Timer.Value = 0x73;
Timer.PrescalerShift = 10;
Timer.PrescalerCount = 1 << Timer.PrescalerShift;
}
public byte ReadMemory(ushort addr, bool peek)
{
if ((addr & 0x0200) == 0) // If not register select, read Ram
{
//return _core.Ram[(ushort)(addr & 0x007f)];
return 0;
}
var registerAddr = (ushort)(addr & 0x0007);
if (registerAddr == 0x00)
{
// Read Output reg A
// Combine readings from player 1 and player 2
// actually depends on setting in SWCHCNTA (aka DDRa)
byte temp = 0;// (byte)(_core.ReadControls1(peek) & 0xF0 | ((_core.ReadControls2(peek) >> 4) & 0x0F));
temp = (byte)(temp & ~_ddRa);
temp = (byte)(temp + (_outputA & _ddRa));
return temp;
}
if (registerAddr == 0x01)
{
// Read DDRA
return _ddRa;
}
if (registerAddr == 0x02)
{
// Read Output reg B
byte temp = 0;// _core.ReadConsoleSwitches(peek);
temp = (byte)(temp & ~_ddRb);
return temp;
}
if (registerAddr == 0x03) // Read DDRB
{
return _ddRb;
}
if ((registerAddr & 0x5) == 0x4)
{
// Bit 0x0080 contains interrupt enable/disable
Timer.InterruptEnabled = (addr & 0x0080) != 0;
// The interrupt flag will be reset whenever the Timer is access by a read or a write
// However, the reading of the timer at the same time the interrupt occurs will not reset the interrupt flag
// (M6532 Datasheet)
if (!(Timer.PrescalerCount == 0 && Timer.Value == 0))
{
Timer.InterruptFlag = false;
}
return Timer.Value;
}
// TODO: fix this to match real behaviour
// This is an undocumented instruction whose behaviour is more dynamic then indicated here
if ((registerAddr & 0x5) == 0x5)
{
// Read interrupt flag
if (Timer.InterruptFlag) // Timer.InterruptEnabled && )
{
return 0xC0;
}
return 0x00;
}
return 0x3A;
}
public void WriteMemory(ushort addr, byte value)
{
if ((addr & 0x0200) == 0) // If the RS bit is not set, this is a ram write
{
//_core.Ram[(ushort)(addr & 0x007f)] = value;
}
else
{
// If bit 0x0010 is set, and bit 0x0004 is set, this is a timer write
if ((addr & 0x0014) == 0x0014)
{
var registerAddr = (ushort)(addr & 0x0007);
// Bit 0x0080 contains interrupt enable/disable
Timer.InterruptEnabled = (addr & 0x0080) != 0;
// The interrupt flag will be reset whenever the Timer is access by a read or a write
// (M6532 datasheet)
if (registerAddr == 0x04)
{
// Write to Timer/1
Timer.PrescalerShift = 0;
Timer.Value = value;
Timer.PrescalerCount = 0; // 1 << Timer.PrescalerShift;
Timer.InterruptFlag = false;
}
else if (registerAddr == 0x05)
{
// Write to Timer/8
Timer.PrescalerShift = 3;
Timer.Value = value;
Timer.PrescalerCount = 0; // 1 << Timer.PrescalerShift;
Timer.InterruptFlag = false;
}
else if (registerAddr == 0x06)
{
// Write to Timer/64
Timer.PrescalerShift = 6;
Timer.Value = value;
Timer.PrescalerCount = 0; // 1 << Timer.PrescalerShift;
Timer.InterruptFlag = false;
}
else if (registerAddr == 0x07)
{
// Write to Timer/1024
Timer.PrescalerShift = 10;
Timer.Value = value;
Timer.PrescalerCount = 0; // 1 << Timer.PrescalerShift;
Timer.InterruptFlag = false;
}
}
// If bit 0x0004 is not set, bit 0x0010 is ignored and
// these are register writes
else if ((addr & 0x0004) == 0)
{
var registerAddr = (ushort)(addr & 0x0007);
if (registerAddr == 0x00)
{
// Write Output reg A
_outputA = value;
}
else if (registerAddr == 0x01)
{
// Write DDRA
_ddRa = value;
}
else if (registerAddr == 0x02)
{
// Write Output reg B
// But is read only
}
else if (registerAddr == 0x03)
{
// Write DDRB
_ddRb = value;
}
}
}
}
public void SyncState(Serializer ser)
{
ser.BeginSection("M6532");
ser.Sync("ddra", ref _ddRa);
ser.Sync("ddrb", ref _ddRb);
ser.Sync("OutputA", ref _outputA);
Timer.SyncState(ser);
ser.EndSection();
}
public struct TimerData
{
public int PrescalerCount;
public byte PrescalerShift;
public byte Value;
public bool InterruptEnabled;
public bool InterruptFlag;
public void Tick()
{
if (PrescalerCount == 0)
{
Value--;
PrescalerCount = 1 << PrescalerShift;
}
PrescalerCount--;
if (PrescalerCount == 0)
{
if (Value == 0)
{
InterruptFlag = true;
PrescalerShift = 0;
}
}
}
public void SyncState(Serializer ser)
{
ser.Sync("prescalerCount", ref PrescalerCount);
ser.Sync("prescalerShift", ref PrescalerShift);
ser.Sync("value", ref Value);
ser.Sync("interruptEnabled", ref InterruptEnabled);
ser.Sync("interruptFlag", ref InterruptFlag);
}
}
}
}

View File

@ -0,0 +1,39 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
// Emulates the Atari 7800 Maria graphics chip
public class Maria : IVideoProvider
{
public int _frameHz;
public int[] _vidbuffer;
public int[] _palette;
public int[] GetVideoBuffer()
{
return _vidbuffer;
}
public int VirtualWidth => 275;
public int VirtualHeight => BufferHeight;
public int BufferWidth { get; private set; }
public int BufferHeight { get; private set; }
public int BackgroundColor => unchecked((int)0xff000000);
public int VsyncNumerator => _frameHz;
public int VsyncDenominator => 1;
public void FrameAdvance()
{
}
public void Reset()
{
}
}
}