Merge pull request #859 from alyosha-tas/master

A7800Hawk initial PR
This commit is contained in:
alyosha-tas 2017-05-25 15:56:46 -04:00 committed by GitHub
commit 77c20a6776
18 changed files with 2280 additions and 0 deletions

View File

@ -0,0 +1,156 @@
;;NOTE: These hashes were generated using trimmed ROM headers
md5:4332c24e4f3bc72e7fe1b77adf66c2b7 3D Asteroids A78 NTSC=true;board=0
md5:0be996d25144966d5541c9eb4919b289 Ace of Aces A78 NTSC=true;board=A78SG
md5:aadde920b3aaba03bc10b40bd0619c94 Ace of Aces A78 PAL=true;board=A78SG
md5:877dcc97a775ed55081864b2dbf5f1e2 Alien Brigade A78 NTSC=true;board=A78S9
md5:de3e9496cb7341f865f27e5a72c7f2f5 Alien Brigade A78 PAL=true;board=A78S9
md5:0a9e58ef5eb9ff93246e0fff684dc7f1 Arkanoid (0911) A78 NTSC=true;board=A7832P
md5:f9fb84658c5586df159a0c75cc46b54c Asteroids Deluxe A78 NTSC=true;board=0
md5:a65f79ad4a0bbdecd59d5f7eb3623fd7 Asteroids Deluxe A78 NTSC=true;board=A7832
md5:1baf41de200f26ec643625021290bec2 Asteroids Deluxe A78 PAL=true;board=A7832
md5:07342c78619ba6ffcc61c10e907e3b50 Asteroids A78 NTSC=true;board=0
md5:8fc3a695eaea3984912d98ed4a543376 Ballblazer A78 NTSC=true;board=A7832P
md5:b558814d54904ce0582e2f6a801d03af Ballblazer A78 PAL=true;board=A7832P
md5:42682415906c21c6af80e4198403ffda Barnyard Blaster A78 NTSC=true;board=A78SG
md5:babe2bc2976688bafb8b23c192658126 Barnyard Blaster A78 PAL=true;board=A78SG
md5:f5f6b69c5eb4b55fc163158d1a6b423e Basketbrawl A78 NTSC=true;board=A78SG
md5:fba002089fcfa176454ab507e0eb76cb Basketbrawl A78 PAL=true;board=A78SG
md5:6010a398070dfacb4c0173d75d73c50a Beef Drop A78 NTSC=true;board=0
md5:c534db0a062225b17cfb8ecce0fb9090 Beef Drop A78 NTSC=true;board=0
md5:6da5b1b9fa0001e3517f6084ff651b07 Bentley Bear - Crystal Quest A78 NTSC=true;board=A78S9
md5:5a09946e57dbe30408a8f253a28d07db Centipede A78 NTSC=true;board=0
md5:38c056a48472d9a9e16ebda5ed91dae7 Centipede A78 PAL=true;board=0
md5:93e4387864b014c155d7c17877990d1e Choplifter! A78 NTSC=true;board=0
md5:59d4edb0230b5acc918b94f6bc94779f Choplifter! A78 PAL=true;board=0
md5:441ac404cdc7bcbd4d787f911df7bf0d Color Test A78 NTSC=true;board=0
md5:2e8e28f6ad8b9b9267d518d880c73ebb Commando A78 NTSC=true;board=A78SGP
md5:55da6c6c3974d013f517e725aa60f48e Commando A78 PAL=true;board=A78SGP
md5:db691469128d9a4217ec7e315930b646 Crack'ed A78 NTSC=true;board=A78SG
md5:7cbe78fa06f47ba6516a67a4b003c9ee Crack'ed A78 PAL=true;board=A78SG
md5:0c9b124355d5328697a3b9e0011353f2 Crazy Brix A78 NTSC=true;board=A7816
md5:45e1d527becc96d1715e810d1c07ac27 Crazy Brix A78 NTSC=true;board=A7816
md5:fc7db1a9243ce2140f716762b8352a5c Crazy Brix A78 PAL=true;board=A7816
md5:a94e4560b6ad053a1c24e096f1262ebf Crossbow A78 NTSC=true;board=A78S9
md5:63db371d67a98daec547b2abd5e7aa95 Crossbow A78 PAL=true;board=A78S9
md5:179b76ff729d4849b8f66a502398acae CDark Chambers A78 NTSC=true;board=A78SG
md5:a2b8e2f159642c4b91de82e9a2928494 Dark Chambers A78 PAL=true;board=A78SG
md5:95ac811c7d27af0032ba090f28c107bd Desert Falcon A78 NTSC=true;board=0
md5:2d5d99b993a885b063f9f22ce5e6523d Desert Falcon A78 PAL=true;board=0
md5:731879ea82fc0ca245e39e036fe293e6 Dig Dug A78 NTSC=true;board=0
md5:408dca9fc40e2b5d805f403fa0509436 Dig Dug A78 PAL=true;board=0
md5:5e332fbfc1e0fc74223d2e73271ce650 Donkey Kong Junior A78 NTSC=true;board=0
md5:4dc5f88243250461bd61053b13777060 Donkey Kong Junior A78 PAL=true;board=0
md5:19f1ee292a23636bd57d408b62de79c7 Donkey Kong A78 NTSC=true;board=0
md5:8e96ef14ce9b5d84bcbc996b66d6d4c7 Donkey Kong A78 PAL=true;board=0
md5:de2ebafcf0e37aaa9d0e9525a7f4dd62 Double Dragon A78 PAL=true;board=A78AC
md5:543484c00ba233736bcaba2da20eeea9 Double Dragon A78 NTSC=true;board=A78AC
md5:2251a6a0f3aec84cc0aff66fc9fa91e8 F-18 Hornet A78 NTSC=true;board=A78AB
md5:e7709da8e49d3767301947a0a0b9d2e6 F-18 Hornet A78 PAL=true;board=A78AB
md5:6287727ab36391a62f728bbdee88675c FailSafe A78 NTSC=true;board=A7848
md5:d2bb22f704f1610a4c396c51f5188e15 FailSafe A78 NTSC=true;board=A7848
md5:d25d5d19188e9f149977c49eb0367cd1 Fatal Run A78 NTSC=true;board=A78SG
md5:23505651ac2e47f3637152066c3aa62f Fatal Run A78 PAL=true;board=A78SG
md5:e80f24e953563e6b61556737d67d3836 Fight Night A78 PAL=true;board=A78SG
md5:07dbbfe612a0a28e283c01545e59f25e Fight Night A78 NTSC=true;board=A78SG
md5:cf76b00244105b8e03cdc37677ec1073 Food Fight A78 NTSC=true;board=0
md5:de0d4f5a9bf1c1bddee3ed2f7ec51209 Food Fight A78 PAL=true;board=0
md5:45136d1d9eddf0bebad32995647b3298 Frogger Demo A78 NTSC=true;board=0
md5:fb8d803b328b2e442548f7799cfa9a4a Galaga A78 NTSC=true;board=0
md5:f5dc7dc8e38072d3d65bd90a660148ce Galaga A78 PAL=true;board=0
md5:06204dadc975be5e5e37e7cc66f984cf Gato A78 NTSC=true;board=0
md5:0baec96787ce17f390e204de1a136e59 Hat Trick A78 PAL=true;board=0
md5:fd9e78e201b6baafddfd3e1fbfe6ba31 Hat Trick A78 NTSC=true;board=0
md5:c3672482ca93f70eafd9134b936c3feb Ikari Warriors A78 NTSC=true;board=A78SG
md5:8c2c2a1ea6e9a928a44c3151ba5c1ce3 Ikari Warriors A78 PAL=true;board=A78SG
md5:1745feadabb24e7cefc375904c73fa4c Impossible Mission Fixed A78 NTSC=true;board=A78SGR
md5:baebc9246c087e893dfa489632157180 Impossible Mission A78 NTSC=true;board=A78SGR
md5:80dead01ea2db5045f6f4443faa6fce8 Impossible Mission A78 PAL=true;board=A78SGR
md5:045fd12050b7f2b842d5970f2414e912 Jinks A78 NTSC=true;board=A78SGR
md5:dfb86f4d06f05ad00cf418f0a59a24f7 Jinks A78 PAL=true;board=A78SGR
md5:f18b3b897a25ab3885b43b4bd141b396 Joust A78 NTSC=true;board=0
md5:f2dae0264a4b4a73762b9d7177e989f6 Joust A78 PAL=true;board=0
md5:548ba2e54e4fc45ab84ed634d702c136 Jr. Ms. Pac-Man A78 NTSC=true;board=A7832P
md5:6bc2daeb48e28d103a4298a276e7e551 Jr. Pac-Man (Tunnels) A78 NTSC=true;board=A7832P
md5:0b3baf47886915dd2eec5da7671bfa63 Jr. Pac-Man A78 NTSC=true;board=A78SGR
md5:8281ab17fa3bfc0a6c497d6a4f350061 Jr. Pac-Man A78 NTSC=true;board=A78SGR
md5:17b3b764d33eae9b5260f01df7bb9d2f KLAX A78 NTSC=true;board=A78SG
md5:5e0a1e832bbcea6facb832fde23a440a Karateka A78 PAL=true;board=A78S4
md5:c3a5a8692a423d43d9d28dd5b7d109d9 Karateka A78 NTSC=true;board=0
md5:f57d0af323d4e173fb49ed447f0563d7 Kung-Fu Master A78 NTSC=true;board=0
md5:2931b75811ad03f3ac9330838f3d231b Kung-Fu Master A78 PAL=true;board=0
md5:431ca060201ee1f9eb49d44962874049 Mario Bros. A78 NTSC=true;board=0
md5:d2e861306be78e44248bb71d7475d8a3 Mario Bros. A78 PAL=true;board=0
md5:37b5692e33a98115e574185fa8398c22 Mat Mania Challenge A78 NTSC=true;board=A78SG
md5:6819c37b96063b024898a19dbae2df54 Mat Mania Challenge A78 PAL=true;board=A78SG
md5:f2f5e5841e4dda89a2faf8933dc33ea6 Mean 18 Ultimate Golf A78 NTSC=true;board=A78SG
md5:2e9dbad6c0fa381a6cd1bb9abf98a104 Mean 18 Ultimate Golf A78 PAL=true;board=A78SG
md5:bedc30ec43587e0c98fc38c39c1ef9d0 Meltdown A78 NTSC=true;board=A78SG
md5:c80155d7eec9e3dcb79aa6b83c9ccd1e Meltdown A78 PAL=true;board=A78SG
md5:b02f93661f4b7e712810d2bf8e02ad79 Meteor Shower A78 NTSC=true;board=A7816
md5:2f1f199ecc2b414d28e01f0de53ca8f7 Meteor Shower A78 PAL=true;board=A7816
md5:bc1e905db1008493a9632aa83ab4682b Midnight Mutants A78 NTSC=true;board=A78SG
md5:6794ea31570eba0b88a0bf1ead3f3f1b Midnight Mutants A78 PAL=true;board=A78SG
md5:017066f522908081ec3ee624f5e4a8aa Missing in Action A78 NTSC=true;board=A78S9
md5:d0f46bf92ed6e7b1cce63278420cae8a Missing in Action A78 NTSC=true;board=A78S9
md5:9ff38ea62004201d870caa8bd9463525 Moon Cresta A78 NTSC=true;board=A7832
md5:3bc8f554cf86f8132a623cc2201a564b Motor Psycho A78 NTSC=true;board=A78SG
md5:5330bfe428a6b601b7e76c2cfc4cd049 Motor Psycho A78 PAL=true;board=A78SG
md5:fc0ea52a9fac557251b65ee680d951e5 Ms. Pac-Man A78 NTSC=true;board=0
md5:56469e8c5ff8983c6cb8dadc64eb0363 Ms. Pac-Man A78 PAL=true;board=0
md5:220121f771fc4b98cef97dc040e8d378 Ninja Golf A78 NTSC=true;board=A78SG
md5:ea0c859aa54fe5eaf4c1f327fab06221 Ninja Golf A78 PAL=true;board=A78SG
md5:74569571a208f8b0b1ccfb22d7c914e1 One on One Basketball A78 NTSC=true;board=0
md5:8dba0425f0262e5704581d8757a1a6e3 One on One Basketball A78 PAL=true;board=0
md5:5d7bc7092de69095137456733e7b685d Pac-Man Collection A78 NTSC=true;board=0
md5:90223a8a363bdf643a19d0f97e63b1b2 PacArcade A78 NTSC=true;board=A7816
md5:386bded4a944bae455fedf56206dd1dd Pete Rose Baseball A78 PAL=true;board=0
md5:1a5207870dec6fae9111cb747e20d8e3 Pete Rose Baseball A78 NTSC=true;board=0
md5:05f43244465943ce819780a71a5b572a Pitfighter A78 NTSC=true;board=A78S4
md5:33aea1e2b6634a1dec8c7006d9afda22 Planet Smashers A78 NTSC=true;board=A78SG
md5:2837a8fd49b7fc7ccd70fd45b69c5099 Planet Smashers A78 PAL=true;board=A78SG
md5:86546808dc60961cdb1b20e761c50ab1 Plutos A78 NTSC=true;board=A78SGR
md5:584582bb09ee8122e7fc09dc7d1ed813 Pole Position II A78 NTSC=true;board=0
md5:865457e0e0f48253b08f77b9e18f93b2 Pole Position II A78 PAL=true;board=0
md5:66e7230f7ef9d14db82d76b06b241bc0 Q-bert A78 NTSC=true;board=A7832
md5:ac03806cef2558fc795a7d5d8dba7bc0 Rampage A78 NTSC=true;board=A78AC
md5:383ed9bd1efb9b6cb3388a777678c928 Realsports Baseball A78 NTSC=true;board=A78S4
md5:8f7eb10ad0bd75474abf0c6c36c08486 Rescue on Fractalus A78 NTSC=true;board=A7832
md5:43525a0405184875c2ecfd0196886a34 Rip Off A78 NTSC=true;board=A7816
md5:106b409c6f4c219b1a3b3d099ead3b2b Rip Off A78 PAL=true;board=0A7816
md5:505f05e7f161f62ccd749dab3c4a204b Robot Finds Kitten A78 NTSC=true;board=A7832
md5:66ecaafe1b82ae68ffc96267aaf7a4d7 Robotron 2084 A78 NTSC=true;board=0
md5:ae85689b21bdf85cb9dc57c3b1fec9db Santa Simon A78 NTSC=true;board=A7848
md5:57651b6c8e62811fab0361cea537b79c Scramble A78 NTSC=true;board=0
md5:c265cfd65534a4514f226cb4c7f7d6bf Scramble A78 NTSC=true;board=0
md5:1ee26fc6b06b4c9ba74931914b7e719d Scramble A78 PAL=true;board=0
md5:65fe82f419f6583a0f9a736242cb303d Scramble A78 PAL=true;board=0
md5:980c35ae9625773a450aa7ef51751c04 Scrapyard Dog A78 NTSC=true;board=A78SG
md5:53db322c201323fe2ca8f074c0a2bf86 Scrapyard Dog A78 PAL=true;board=A78SG
md5:b697d9c2d1b9f6cb21041286d1bbfa7f Sentinel A78 NTSC=true;board=A78SG
md5:5469b4de0608f23a5c4f98f331c9e75f Sentinel A78 PAL=true;board=A78SG
md5:2d643ac548c40e58c99d0fe433ba4ba0 Sirius A78 NTSC=true;board=A78SGR
md5:a84c1b2300fbfbf21b1c02387f613dad Space Duel A78 PAL=true;board=0
md5:771cb4609347657f63e6f0eb26036e35 Space Duel A78 NTSC=true;board=A7832
md5:6adf79558a3d7f5beca1bb8d34337417 Space Invaders A78 NTSC=true;board=0
md5:cbb0746192540a13b4c7775c7ce2021f Summer Games A78 NTSC=true;board=A78SGR
md5:cc18e3b37a507c4217eb6cb1de8c8538 Super Huey UH-IX A78 NTSC=true;board=0
md5:162f9c953f0657689cc74ab20b40280f Super Huey UH-IX A78 PAL=true;board=0
md5:59b5793bece1c80f77b55d60fb39cb94 Super Skateboardin' A78 NTSC=true;board=0
md5:95d7c321dce8f57623a9c5b4947bb375 Super Skateboardin' A78 PAL=true;board=0
md5:5c4f752371a523f15e9980fea73b874d Tank Command A78 NTSC=true;board=A78S4
md5:3bb9c8d9adc912dd7f8471c97445cd8d Titlematch Pro Wrestling A78 PAL=true;board=0
md5:1af475ff6429a160752b592f0f92b287 Titlematch Pro Wrestling A78 NTSC=true;board=0
md5:c3903ab01a51222a52197dbfe6538ecf Tomcat F14 A78 NTSC=true;board=0
md5:682338364243b023ecc9d24f0abfc9a7 Tomcat F14 A78 PAL=true;board=0
md5:208ef955fa90a29815eb097bce89bace Touchdown Football A78 NTSC=true;board=A78SG
md5:8d64763db3100aadc552db5e6868506a Tower Toppler A78 NTSC=true;board=A78S4R
md5:32a37244a9c6cc928dcdf02b45365aa8 Tower Toppler A78 PAL=true;board=A78S4R
md5:412cc5bfa08bd03244b9c4e8d46cd0a0 Wasp (Standard Edition) A78 NTSC=true;board=A7832
md5:427cb05d0a1abb068998e2760d77f4fb Water Ski A78 NTSC=true;board=A78S4
md5:3799d72f78dda2ee87b0ef8bf7b91186 Winter Games A78 NTSC=true;board=A78SGR
md5:6813ffff510f930c867b3f0aba78ac85 Worm (0703) A78 NTSC=true;board=A7816
md5:05fb699db9eef564e2fe45c568746dbc Xenophobe A78 NTSC=true;board=A78SG
md5:70937c3184f0be33d06f7f4382ca54de Xenophobe A78 PAL=true;board=A78SG
md5:d7dc17379aa25e5ae3c14b9e780c6f6d Xevious A78 NTSC=true;board=0
md5:b1a9f196ce5f47ca8caf8fa7bc4ca46c Xevious A78 PAL=true;board=0
md5:ce6fbdc7b037a4efdaf87267f5f292cc b*nQ A78 NTSC=true;board=0

View File

@ -362,6 +362,35 @@
<Compile Include="Consoles\Atari\2600\Tia\Tia.PlayerData.cs" />
<Compile Include="Consoles\Atari\2600\Tia\Tia.PlayfieldData.cs" />
<Compile Include="Consoles\Atari\2600\Tia\Tia.SyncState.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\A7800Hawk.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\A7800Hawk.IDebuggable.cs">
<DependentUpon>A7800Hawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\A7800Hawk\A7800Hawk.IEmulator.cs">
<DependentUpon>A7800Hawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\A7800Hawk\A7800Hawk.IInputPollable.cs">
<DependentUpon>A7800Hawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\A7800Hawk\A7800Hawk.IMemoryDomains.cs">
<DependentUpon>A7800Hawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\A7800Hawk\A7800Hawk.IStatable.cs">
<DependentUpon>A7800Hawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\A7800Hawk\A7800Hawk.ISaveRam.cs">
<DependentUpon>A7800Hawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\A7800Hawk\MemoryMap.cs">
<DependentUpon>A7800Hawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\A7800Hawk\A7800HawkControl.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\M6532.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\Maria.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\TIA_Sound\Tia.Audio.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\TIA_Sound\TIA.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\TIA_Sound\Tia.ISoundProvider.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\TIA_Sound\Tia.SyncState.cs" />
<Compile Include="Consoles\Atari\7800\Atari7800.cs" />
<Compile Include="Consoles\Atari\7800\Atari7800.IDebuggable.cs">
<DependentUpon>Atari7800.cs</DependentUpon>

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,53 @@
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++;
}
maria.FrameAdvance();
}
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;
m6532 = 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,307 @@
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
{
// this register selects between 2600 and 7800 mode in the A7800
// however, we already have a 2600 emulator so this core will only be loading A7800 games
// furthermore, the location of the register is in the same place as TIA registers (0x0-0x1F)
// any writes to this location before the register is 'locked' will go to the register and not the TIA
public byte A7800_control_register;
// 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()
{
A7800_control_register = 0;
tia.Reset();
cpu.Reset();
maria.Reset();
m6532 = new M6532();
}
/*
* 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,42 @@
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;
//Maria related variables
public int cycle;
public void FrameAdvance()
{
}
public void Reset()
{
cycle = 0;
}
}
}

View File

@ -0,0 +1,147 @@
using System;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.M6502;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class A7800Hawk
{
public byte ReadMemory(int addr)
{
addr &= 0xFFFF;
if (addr < 0x0400) {
if ((addr & 0xFF) < 0x20)
{
// return TIA registers or control register if it is still unlocked
if ((A7800_control_register & 0x1) == 0 && (addr < 0x20))
{
return 0xFF; // TODO: what to return here?
}
else
{
return TIA_regs[addr]; // TODO: what to return here?
}
}
else if ((addr & 0xFF) < 0x40)
{
return Maria_regs[(addr & 0x3F) - 0x20];
}
else if (addr < 0x100)
{
// RAM block 0
return RAM[addr - 0x40 + 0x840];
}
else if (addr < 0x200)
{
// RAM block 1
return RAM[addr - 0x140 + 0x940];
}
else if (addr < 0x300)
{
return regs_6532[addr - 0x240];
}
else
{
return 0xFF; // what is mapped here?
}
}
else if (addr < 0x480)
{
return 0xFF; // cartridge space available
}
else if (addr < 0x500)
{
// this is where RAM for the 6532 resides for use in 2600 mode
return 0xFF;
}
else if (addr < 0x1800)
{
return 0xFF; // cartridge space available
}
else if (addr < 0x2800)
{
return RAM[addr - 0x1800];
}
else if (addr < 0x4000)
{
return RAM[addr - 0x2800 + 0x800];
}
else
{
return 0xFF; // cartridge and other OPSYS
}
}
public void WriteMemory(int addr, byte value)
{
addr &= 0xFFFF;
if (addr < 0x0400)
{
if ((addr & 0xFF) < 0x20)
{
// return TIA registers or control register if it is still unlocked
if ((A7800_control_register & 0x1) == 0 && (addr < 0x20))
{
A7800_control_register = value; // TODO: what to return here?
}
else
{
TIA_regs[addr] = value; // TODO: what to return here?
}
}
else if ((addr & 0xFF) < 0x40)
{
Maria_regs[(addr & 0x3F) - 0x20] = value;
}
else if (addr < 0x100)
{
// RAM block 0
RAM[addr - 0x40 + 0x840] = value;
}
else if (addr < 0x200)
{
// RAM block 1
RAM[addr - 0x140 + 0x940] = value;
}
else if (addr < 0x300)
{
regs_6532[addr - 0x240] = value;
}
else
{
// what is mapped here?
}
}
else if (addr < 0x480)
{
// cartridge space available
}
else if (addr < 0x500)
{
// this is where RAM for the 6532 resides for use in 2600 mode
// is it accessible in 7800 mode?
}
else if (addr < 0x1800)
{
// cartridge space available
}
else if (addr < 0x2800)
{
RAM[addr - 0x1800] = value;
}
else if (addr < 0x4000)
{
RAM[addr - 0x2800 + 0x800] = value;
}
else
{
// cartridge and other OPSYS
}
}
}
}

View File

@ -0,0 +1 @@
todo:

View File

@ -0,0 +1,334 @@
using System;
using System.Numerics;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
// Emulates the TIA
public partial class TIA : ISoundProvider
{
public byte BusState;
private bool _doTicks;
private byte _hsyncCnt;
private int _capChargeStart;
private bool _capCharging;
public int AudioClocks; // not savestated
private readonly Audio[] AUD = { new Audio(), new Audio() };
// current audio register state used to sample correct positions in the scanline (clrclk 0 and 114)
////public byte[] current_audio_register = new byte[6];
public readonly short[] LocalAudioCycles = new short[2000];
public void Reset()
{
_hsyncCnt = 0;
_capChargeStart = 0;
_capCharging = false;
AudioClocks = 0;
_doTicks = false;
}
// Execute TIA cycles
public void Execute(int cycles)
{
// do the audio sampling
if (_hsyncCnt == 36 || _hsyncCnt == 148)
{
LocalAudioCycles[AudioClocks] += (short)(AUD[0].Cycle() / 2);
LocalAudioCycles[AudioClocks] += (short)(AUD[1].Cycle() / 2);
AudioClocks++;
}
}
public byte ReadMemory(ushort addr, bool peek)
{
var maskedAddr = (ushort)(addr & 0x000F);
byte coll = 0;
int mask = 0;
if (maskedAddr == 0x00) // CXM0P
{
}
if (maskedAddr == 0x01) // CXM1P
{
}
if (maskedAddr == 0x02) // CXP0FB
{
}
if (maskedAddr == 0x03) // CXP1FB
{
}
if (maskedAddr == 0x04) // CXM0FB
{
}
if (maskedAddr == 0x05) // CXM1FB
{
}
if (maskedAddr == 0x06) // CXBLPF
{
}
if (maskedAddr == 0x07) // CXPPMM
{
}
// inputs 0-3 are measured by a charging capacitor, these inputs are used with the paddles and the keyboard
// Changing the hard coded value will change the paddle position. The range seems to be roughly 0-56000 according to values from stella
// 6105 roughly centers the paddle in Breakout
if (maskedAddr == 0x08) // INPT0
{
}
if (maskedAddr == 0x09) // INPT1
{
}
if (maskedAddr == 0x0A) // INPT2
{
}
if (maskedAddr == 0x0B) // INPT3
{
}
if (maskedAddr == 0x0C) // INPT4
{
}
if (maskedAddr == 0x0D) // INPT5
{
}
// some bits of the databus will be undriven when a read call is made. Our goal here is to sort out what
// happens to the undriven pins. Most of the time, they will be in whatever state they were when previously
// assigned in some other bus access, so let's go with that.
coll += (byte)(mask & BusState);
if (!peek)
{
BusState = coll;
}
return coll;
}
public void WriteMemory(ushort addr, byte value, bool poke)
{
var maskedAddr = (ushort)(addr & 0x3f);
if (!poke)
{
BusState = value;
}
if (maskedAddr == 0x00) // VSYNC
{
}
else if (maskedAddr == 0x01) // VBLANK
{
}
else if (maskedAddr == 0x02) // WSYNC
{
}
else if (maskedAddr == 0x04) // NUSIZ0
{
}
else if (maskedAddr == 0x05) // NUSIZ1
{
}
else if (maskedAddr == 0x06) // COLUP0
{
}
else if (maskedAddr == 0x07) // COLUP1
{
}
else if (maskedAddr == 0x08) // COLUPF
{
}
else if (maskedAddr == 0x09) // COLUBK
{
}
else if (maskedAddr == 0x0A) // CTRLPF
{
}
else if (maskedAddr == 0x0B) // REFP0
{
}
else if (maskedAddr == 0x0C) // REFP1
{
}
else if (maskedAddr == 0x0D) // PF0
{
}
else if (maskedAddr == 0x0E) // PF1
{
}
else if (maskedAddr == 0x0F) // PF2
{
}
else if (maskedAddr == 0x10) // RESP0
{
}
else if (maskedAddr == 0x11) // RESP1
{
}
else if (maskedAddr == 0x12) // RESM0
{
}
else if (maskedAddr == 0x13) // RESM1
{
}
else if (maskedAddr == 0x14) // RESBL
{
}
else if (maskedAddr == 0x15) // AUDC0
{
AUD[0].AUDC = (byte)(value & 15);
}
else if (maskedAddr == 0x16) // AUDC1
{
AUD[1].AUDC = (byte)(value & 15);
}
else if (maskedAddr == 0x17) // AUDF0
{
AUD[0].AUDF = (byte)((value & 31) + 1);
}
else if (maskedAddr == 0x18) // AUDF1
{
AUD[1].AUDF = (byte)((value & 31) + 1);
}
else if (maskedAddr == 0x19) // AUDV0
{
AUD[0].AUDV = (byte)(value & 15);
}
else if (maskedAddr == 0x1A) // AUDV1
{
AUD[1].AUDV = (byte)(value & 15);
}
else if (maskedAddr == 0x1B) // GRP0
{
}
else if (maskedAddr == 0x1C) // GRP1
{
}
else if (maskedAddr == 0x1D) // ENAM0
{
}
else if (maskedAddr == 0x1E) // ENAM1
{
}
else if (maskedAddr == 0x1F) // ENABL
{
}
else if (maskedAddr == 0x20) // HMP0
{
}
else if (maskedAddr == 0x21) // HMP1
{
}
else if (maskedAddr == 0x22) // HMM0
{
}
else if (maskedAddr == 0x23) // HMM1
{
}
else if (maskedAddr == 0x24) // HMBL
{
}
else if (maskedAddr == 0x25) // VDELP0
{
}
else if (maskedAddr == 0x26) // VDELP1
{
}
else if (maskedAddr == 0x27) // VDELBL
{
}
else if (maskedAddr == 0x28) // RESMP0
{
}
else if (maskedAddr == 0x29) // RESMP1
{
}
else if (maskedAddr == 0x2A) // HMOVE
{
}
else if (maskedAddr == 0x2B) // HMCLR
{
}
else if (maskedAddr == 0x2C) // CXCLR
{
}
}
private enum AudioRegister : byte
{
AUDC, AUDF, AUDV
}
private int _frameStartCycles, _frameEndCycles;
}
}

View File

@ -0,0 +1,212 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class TIA
{
public class Audio
{
// noise/division control
public byte AUDC = 0;
// frequency divider
public byte AUDF = 1;
// volume
public byte AUDV = 0;
// 2 state counter
private bool sr1 = true;
// 4 bit shift register
private int sr4 = 0x0f;
// 5 bit shift register
private int sr5 = 0x1f;
// 9 bit shift register
private int sr9 = 0x1ff;
// 3 state counter
private int sr3 = 2;
// counter based off AUDF
private byte freqcnt;
// latched audio value
private bool on = true;
private bool Run3()
{
sr3++;
if (sr3 == 3)
{
sr3 = 0;
return true;
}
return false;
}
private bool Run4()
{
bool ret = (sr4 & 1) != 0;
bool c = ((sr4 & 1) != 0) ^ ((sr4 & 2) != 0);
sr4 = (sr4 >> 1) | (c ? 8 : 0);
return ret;
}
private bool Run5()
{
bool ret = (sr5 & 1) != 0;
bool c = ((sr5 & 1) != 0) ^ ((sr5 & 4) != 0);
sr5 = (sr5 >> 1) | (c ? 16 : 0);
return ret;
}
private bool One4()
{
bool ret = (sr4 & 1) != 0;
sr4 = (sr4 >> 1) | 8;
return ret;
}
private bool One5()
{
bool ret = (sr5 & 1) != 0;
sr5 = (sr5 >> 1) | 16;
return ret;
}
private bool Run1()
{
sr1 = !sr1;
return !sr1;
}
private bool Run9()
{
bool ret = (sr9 & 1) != 0;
bool c = ((sr9 & 1) != 0) ^ ((sr9 & 16) != 0);
sr9 = (sr9 >> 1) | (c ? 256 : 0);
return ret;
}
/// <summary>
/// call me approx 31k times a second
/// </summary>
/// <returns>16 bit audio sample</returns>
public short Cycle()
{
if (++freqcnt >= AUDF)
{
freqcnt = 0;
switch (AUDC)
{
case 0x00:
case 0x0b:
// Both have a 1 s
One5();
on = One4();
break;
case 0x01:
// Both run, but the 5 bit is ignored
on = Run4();
//Run5();
break;
case 0x02:
if ((sr5 & 0x0f) == 0 || (sr5 & 0x0f) == 0x0f)
{
on = Run4();
}
Run5();
break;
case 0x03:
if (Run5())
{
on = Run4();
}
break;
case 0x04:
Run5();
One4();
on = Run1();
break;
case 0x05:
One5();
Run4();
on = Run1();
break;
case 0x06:
case 0x0a:
Run5();
if ((sr5 & 0x0f) == 0)
{
on = false;
}
else if ((sr5 & 0x0f) == 0x0f)
{
on = true;
}
break;
case 0x07:
case 0x09:
on = Run5();
break;
case 0x08:
on = Run9();
break;
case 0x0c:
case 0x0d:
if (Run3())
{
on = Run1();
}
break;
case 0x0e:
if (Run3())
{
goto case 0x06;
}
break;
case 0x0f:
if (Run3())
{
goto case 0x07;
}
break;
}
}
return (short)(on ? AUDV * 1092 : 0);
}
public void SyncState(Serializer ser)
{
ser.Sync("AUDC", ref AUDC);
ser.Sync("AUDF", ref AUDF);
ser.Sync("AUDV", ref AUDV);
ser.Sync("sr1", ref sr1);
ser.Sync("sr3", ref sr3);
ser.Sync("sr4", ref sr4);
ser.Sync("sr5", ref sr5);
ser.Sync("sr9", ref sr9);
ser.Sync("freqcnt", ref freqcnt);
ser.Sync("on", ref on);
}
}
}
}

View File

@ -0,0 +1,65 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class TIA : ISoundProvider
{
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)
{
short[] ret = new short[_spf * 2];
GetSamples(ret);
samples = ret;
nsamp = _spf;
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
AudioClocks = 0;
}
private readonly int _spf;
// Exposing this as GetSamplesAsync would allow this to provide async sound
// However, it does nothing special for async sound so I don't see a point
private void GetSamples(short[] samples)
{
if (AudioClocks > 0)
{
var samples31Khz = new short[AudioClocks]; // mono
for (int i = 0; i < AudioClocks; i++)
{
samples31Khz[i] = LocalAudioCycles[i];
LocalAudioCycles[i] = 0;
}
// convert from 31khz to 44khz
for (var i = 0; i < samples.Length / 2; i++)
{
samples[i * 2] = samples31Khz[(int)(((double)samples31Khz.Length / (double)(samples.Length / 2)) * i)];
samples[(i * 2) + 1] = samples[i * 2];
}
}
AudioClocks = 0;
}
}
}

View File

@ -0,0 +1,28 @@
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class TIA
{
public void SyncState(Serializer ser)
{
ser.BeginSection("TIA");
ser.Sync("hsyncCnt", ref _hsyncCnt);
// add everything to the state
ser.Sync("Bus_State", ref BusState);
ser.Sync("Ticks", ref _doTicks);
// some of these things weren't in the state because they weren't needed if
// states were always taken at frame boundaries
ser.Sync("capChargeStart", ref _capChargeStart);
ser.Sync("capCharging", ref _capCharging);
ser.Sync("AudioClocks", ref AudioClocks);
ser.Sync("FrameStartCycles", ref _frameStartCycles);
ser.Sync("FrameEndCycles", ref _frameEndCycles);
}
}
}