Apply C64 core update patch.

This commit is contained in:
Anthony Konzel 2016-02-22 17:50:11 -06:00
parent 997877c05b
commit ac9a4ef777
103 changed files with 8311 additions and 12749 deletions

View File

@ -46,13 +46,16 @@ namespace BizHawk.Emulation.Common
//FirmwareAndOption("BFAAC75F101C135E32E2AAF541DE6B1BE4C8C62D", "NDS", "Bios_Arm9", "biosnds9.rom", "ARM9 Bios");
FirmwareAndOption("5A65B922B562CB1F57DAB51B73151283F0E20C7A", 8192, "INTV", "EROM", "erom.bin", "Executive Rom");
FirmwareAndOption("F9608BB4AD1CFE3640D02844C7AD8E0BCD974917", 2048, "INTV", "GROM", "grom.bin", "Graphics Rom");
FirmwareAndOption("1D503E56DF85A62FEE696E7618DC5B4E781DF1BB", 8192, "C64", "Kernal", "c64-kernal.bin", "Kernal Rom");
FirmwareAndOption("79015323128650C742A3694C9429AA91F355905E", 8192, "C64", "Basic", "c64-basic.bin", "Basic Rom");
FirmwareAndOption("ADC7C31E18C7C7413D54802EF2F4193DA14711AA", 4096, "C64", "Chargen", "c64-chargen.bin", "Chargen Rom");
FirmwareAndOption("AB16F56989B27D89BABE5F89C5A8CB3DA71A82F0", 16384, "C64", "Drive1541", "drive-1541.bin", "1541 Disk Drive Rom");
FirmwareAndOption("D3B78C3DBAC55F5199F33F3FE0036439811F7FB3", 16384, "C64", "Drive1541II", "drive-1541ii.bin", "1541-II Disk Drive Rom");
//for saturn, we think any bios region can pretty much run any iso
//so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region
var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)");
//for saturn, we think any bios region can pretty much run any iso
//so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region
var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)");
var ss_100_ue = File("FAA8EA183A6D7BBE5D4E03BB1332519800D3FBC3", 524288, "saturn-1.00-(U+E).bin", "Bios v1.00 (U+E)");
var ss_100a_ue = File("3BB41FEB82838AB9A35601AC666DE5AACFD17A58", 524288, "saturn-1.00a-(U+E).bin", "Bios v1.00a (U+E)"); //?? is this size correct?
var ss_101_j = File("DF94C5B4D47EB3CC404D88B33A8FDA237EAF4720", 524288, "saturn-1.01-(J).bin", "Bios v1.01 (J)"); //?? is this size correct?

View File

@ -177,37 +177,54 @@
<Compile Include="Computers\Commodore64\Cartridge\Mapper0012.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Mapper0013.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Mapper0020.cs" />
<Compile Include="Computers\Commodore64\CassettePort\CassettePortDevice.cs" />
<Compile Include="Computers\Commodore64\CassettePort\Tape.cs" />
<Compile Include="Computers\Commodore64\Cassette\CassettePortDevice.cs" />
<Compile Include="Computers\Commodore64\Cassette\TapeDrive.cs" />
<Compile Include="Computers\Commodore64\Media\Tape.cs" />
<Compile Include="Computers\Commodore64\InputFileInfo.cs" />
<Compile Include="Computers\Commodore64\Media\PRG.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Cart.cs" />
<Compile Include="Computers\Commodore64\MOS\CartridgePort.cs" />
<Compile Include="Computers\Commodore64\Cartridge\CartridgeDevice.cs" />
<Compile Include="Computers\Commodore64\Cartridge\CartridgePort.cs" />
<Compile Include="Computers\Commodore64\Cassette\CassettePort.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip2114.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip23XX.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip23128.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip4864.cs" />
<Compile Include="Computers\Commodore64\MOS\MOS6567R56A.cs" />
<Compile Include="Computers\Commodore64\MOS\MOS6510.cs" />
<Compile Include="Computers\Commodore64\MOS\MOS6522.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6510.IDebuggable.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6510.IDisassemblable.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6581R3.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6581R4AR.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip8580R5.cs" />
<Compile Include="Computers\Commodore64\MOS\Cia.cs" />
<Compile Include="Computers\Commodore64\MOS\Cia.Port.cs" />
<Compile Include="Computers\Commodore64\MOS\Cia.Registers.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6567R56A.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6510.cs" />
<Compile Include="Computers\Commodore64\Media\D64.cs" />
<Compile Include="Computers\Commodore64\Media\Disk.cs" />
<Compile Include="Computers\Commodore64\Media\G64.cs" />
<Compile Include="Computers\Commodore64\MOS\MOS6526-2.cs" />
<Compile Include="Computers\Commodore64\MOS\MOS6526-2.Interface.cs" />
<Compile Include="Computers\Commodore64\MOS\MOS6526-2.PortIO.cs" />
<Compile Include="Computers\Commodore64\MOS\MOS6526.cs" />
<Compile Include="Computers\Commodore64\MOS\MOS6567R8.cs" />
<Compile Include="Computers\Commodore64\MOS\MOS6572.cs" />
<Compile Include="Computers\Commodore64\MOS\MOS6581.cs" />
<Compile Include="Computers\Commodore64\MOS\MOS6569.cs" />
<Compile Include="Computers\Commodore64\MOS\MOSPLA.cs" />
<Compile Include="Computers\Commodore64\MOS\Port.cs" />
<Compile Include="Computers\Commodore64\MOS\SerialPort.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6526.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6567R8.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6572.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6581R2.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6569.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip90611401.cs" />
<Compile Include="Computers\Commodore64\MOS\Cia.Tod.cs" />
<Compile Include="Computers\Commodore64\MOS\LatchedPort.cs" />
<Compile Include="Computers\Commodore64\MOS\Sid.cs" />
<Compile Include="Computers\Commodore64\MOS\Sid.Envelope.cs" />
<Compile Include="Computers\Commodore64\MOS\Sid.Registers.cs" />
<Compile Include="Computers\Commodore64\MOS\Sid.SoundProvider.cs" />
<Compile Include="Computers\Commodore64\MOS\Sid.Voice.cs" />
<Compile Include="Computers\Commodore64\MOS\UserPort.cs" />
<Compile Include="Computers\Commodore64\MOS\Via.cs" />
<Compile Include="Computers\Commodore64\MOS\Via.Port.cs" />
<Compile Include="Computers\Commodore64\MOS\Via.Registers.cs" />
<Compile Include="Computers\Commodore64\Serial\Drive1541.cs" />
<Compile Include="Computers\Commodore64\Serial\Drive1541.FluxTransitions.cs" />
<Compile Include="Computers\Commodore64\Serial\Drive1541.IDebuggable.cs" />
<Compile Include="Computers\Commodore64\Serial\Drive1541.IDisassemblable.cs" />
<Compile Include="Computers\Commodore64\Serial\Drive1541.Motor.cs" />
<Compile Include="Computers\Commodore64\Serial\SerialPort.cs" />
<Compile Include="Computers\Commodore64\Serial\SerialPortDevice.cs" />
<Compile Include="Computers\Commodore64\User\UserPort.cs" />
<Compile Include="Computers\Commodore64\MOS\Vic.cs" />
<Compile Include="Computers\Commodore64\MOS\Vic.Parse.cs" />
<Compile Include="Computers\Commodore64\MOS\Vic.Registers.cs" />
@ -217,7 +234,7 @@
<Compile Include="Computers\Commodore64\MOS\Vic.TimingBuilder.cs" />
<Compile Include="Computers\Commodore64\MOS\Vic.VideoProvider.cs" />
<Compile Include="Computers\Commodore64\SaveState.cs" />
<Compile Include="Computers\Commodore64\UserPort\UserPortDevice.cs" />
<Compile Include="Computers\Commodore64\User\UserPortDevice.cs" />
<Compile Include="Consoles\Atari\2600\Atari2600.cs" />
<Compile Include="Consoles\Atari\2600\Atari2600.Core.cs">
<DependentUpon>Atari2600.cs</DependentUpon>
@ -926,7 +943,6 @@
<Compile Include="Consoles\Sega\SMS\VDP.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Computers\Commodore64\docs\CRT.TXT" />
<Content Include="Consoles\Coleco\docs\CV-Sound.txt" />
<Content Include="Consoles\Coleco\docs\CV-Tech.txt" />
<Content Include="Consoles\Nintendo\NES\Docs\sunsoft.txt" />
@ -969,7 +985,6 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Computers\Commodore64\MOS\MOS6526-2.Registers.cs" />
<None Include="Consoles\Atari\docs\stella.pdf" />
<None Include="Consoles\Coleco\docs\colecovision tech1.pdf" />
<None Include="Consoles\Coleco\docs\colecovision tech2.pdf" />

View File

@ -243,6 +243,12 @@ namespace BizHawk.Emulation.Cores.Components.M6502
return (ushort)(ReadMemory(address) | (ReadMemory(highAddress) << 8));
}
// SO pin
public void SetOverflow()
{
FlagV = true;
}
private static readonly byte[] TableNZ =
{
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

View File

@ -1,160 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public partial class C64 : IDebuggable
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
[SaveState.DoNotSave] private IDebuggable _selectedDebuggable;
private IEnumerable<IDebuggable> GetAvailableDebuggables()
{
yield return _board.Cpu;
if (_board.DiskDrive != null)
{
yield return _board.DiskDrive;
}
}
private void SetDefaultDebuggable()
{
_selectedDebuggable = GetAvailableDebuggables().First();
}
IDictionary<string, RegisterValue> IDebuggable.GetCpuFlagsAndRegisters()
{
return new Dictionary<string, RegisterValue>
{
{ "A", board.cpu.A },
{ "X", board.cpu.X },
{ "Y", board.cpu.Y },
{ "S", board.cpu.S },
{ "PC", board.cpu.PC },
{ "Flag C", board.cpu.FlagC },
{ "Flag Z", board.cpu.FlagZ },
{ "Flag I", board.cpu.FlagI },
{ "Flag D", board.cpu.FlagD },
{ "Flag B", board.cpu.FlagB },
{ "Flag V", board.cpu.FlagV },
{ "Flag N", board.cpu.FlagN },
{ "Flag T", board.cpu.FlagT }
};
if (_selectedDebuggable == null)
{
SetDefaultDebuggable();
}
return _selectedDebuggable.GetCpuFlagsAndRegisters();
}
public void SetCpuRegister(string register, int value)
void IDebuggable.SetCpuRegister(string register, int value)
{
switch (register)
{
default:
throw new InvalidOperationException();
case "A":
board.cpu.A = (byte)value;
break;
case "X":
board.cpu.X = (byte)value;
break;
case "Y":
board.cpu.Y = (byte)value;
break;
case "S":
board.cpu.S = (byte)value;
break;
case "PC":
board.cpu.PC = (ushort)value;
break;
}
if (_selectedDebuggable == null)
{
SetDefaultDebuggable();
}
_selectedDebuggable.SetCpuRegister(register, value);
}
public bool CanStep(StepType type)
bool IDebuggable.CanStep(StepType type)
{
switch (type)
{
case StepType.Into:
case StepType.Over:
case StepType.Out:
return true;
default:
return false;
}
if (_selectedDebuggable == null)
{
SetDefaultDebuggable();
}
return _selectedDebuggable.CanStep(type);
}
public void Step(StepType type)
void IDebuggable.Step(StepType type)
{
switch (type)
{
case StepType.Into:
StepInto();
break;
case StepType.Out:
StepOut();
break;
case StepType.Over:
StepOver();
break;
}
if (_selectedDebuggable == null)
{
SetDefaultDebuggable();
}
_selectedDebuggable.Step(type);
}
private void StepInto()
{
while (board.cpu.AtInstructionStart())
{
DoCycle();
}
while (!board.cpu.AtInstructionStart())
{
DoCycle();
}
}
[SaveState.DoNotSave]
private readonly IMemoryCallbackSystem _memoryCallbacks;
private void StepOver()
{
var instruction = board.cpu.Peek(board.cpu.PC);
if (instruction == JSR)
{
var destination = board.cpu.PC + JSRSize;
while (board.cpu.PC != destination)
{
StepInto();
}
}
else
{
StepInto();
}
}
private void StepOut()
{
var instr = board.cpu.Peek(board.cpu.PC);
JSRCount = instr == JSR ? 1 : 0;
var bailOutFrame = Frame + 1;
while (true)
{
StepInto();
instr = board.cpu.Peek(board.cpu.PC);
if (instr == JSR)
{
JSRCount++;
}
else if ((instr == RTS || instr == RTI) && JSRCount <= 0)
{
StepInto();
JSRCount = 0;
break;
}
else if (instr == RTS || instr == RTI)
{
JSRCount--;
}
else //Emergency Bailout Logic
{
if (Frame == bailOutFrame)
{
break;
}
}
}
}
private int JSRCount = 0;
private const byte JSR = 0x20;
private const byte RTI = 0x40;
private const byte RTS = 0x60;
private const byte JSRSize = 3;
public IMemoryCallbackSystem MemoryCallbacks { get; private set; }
[SaveState.DoNotSave]
IMemoryCallbackSystem IDebuggable.MemoryCallbacks { get { return _memoryCallbacks; } }
}
}

View File

@ -1,30 +1,76 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public partial class C64 : IDisassemblable
{
public string Cpu
{
get { return "6510"; } set { }
}
public string PCRegisterName
{
get { return "PC"; }
}
public IEnumerable<string> AvailableCpus
{
get { yield return "6510"; }
}
public string Disassemble(MemoryDomain m, uint addr, out int length)
{
return Components.M6502.MOS6502X.Disassemble((ushort)addr, out length, (a) => m.PeekByte(a));
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public partial class C64 : IDisassemblable
{
[SaveState.DoNotSave] private IDisassemblable _selectedDisassemblable;
private IEnumerable<IDisassemblable> GetAvailableDisassemblables()
{
yield return _board.Cpu;
if (_board.DiskDrive != null)
{
yield return _board.DiskDrive;
}
}
private void SetDefaultDisassemblable()
{
_selectedDisassemblable = GetAvailableDisassemblables().First();
}
[SaveState.DoNotSave]
public string Cpu
{
get
{
if (_selectedDisassemblable == null)
{
SetDefaultDisassemblable();
}
return _selectedDisassemblable.Cpu;
}
set
{
var currentSelectedDisassemblable = _selectedDisassemblable;
_selectedDisassemblable = GetAvailableDisassemblables().FirstOrDefault(d => d.Cpu == value) ?? currentSelectedDisassemblable;
if (_selectedDisassemblable is IDebuggable)
{
_selectedDebuggable = _selectedDisassemblable as IDebuggable;
}
}
}
[SaveState.DoNotSave]
public string PCRegisterName
{
get
{
if (_selectedDisassemblable == null)
{
SetDefaultDisassemblable();
}
return _selectedDisassemblable.PCRegisterName;
}
}
[SaveState.DoNotSave]
public IEnumerable<string> AvailableCpus
{
get { return GetAvailableDisassemblables().SelectMany(d => d.AvailableCpus); }
}
public string Disassemble(MemoryDomain m, uint addr, out int length)
{
if (_selectedDisassemblable == null)
{
SetDefaultDisassemblable();
}
return _selectedDisassemblable.Disassemble(m, addr, out length);
}
}
}

View File

@ -6,14 +6,5 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public bool DriveLightEnabled { get { return true; } }
public bool DriveLightOn { get; private set; }
public bool DriveLED
{
get
{
//return (disk.PeekVia1(0x00) & 0x08) != 0;
return false;
}
}
}
}

View File

@ -4,21 +4,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public partial class C64 : IInputPollable
{
public bool IsLagFrame
{
get { return _islag; }
set { _islag = value; }
}
public bool IsLagFrame { get; set; }
public int LagCount { get; set; }
public int LagCount
{
get { return _lagcount; }
set { _lagcount = value; }
}
public IInputCallbackSystem InputCallbacks { get; private set; }
private bool _islag = true;
private int _lagcount = 0;
[SaveState.DoNotSave]
public IInputCallbackSystem InputCallbacks { get; private set; }
}
}

View File

@ -1,29 +1,45 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public partial class C64
{
private IMemoryDomains memoryDomains;
private IMemoryDomains _memoryDomains;
private void SetupMemoryDomains()
private void SetupMemoryDomains(bool diskDriveEnabled)
{
var domains = new List<MemoryDomain>
{
C64MemoryDomainFactory.Create("System Bus", 0x10000, a => _board.Cpu.Peek(a), (a, v) => _board.Cpu.Poke(a, v)),
C64MemoryDomainFactory.Create("RAM", 0x10000, a => _board.Ram.Peek(a), (a, v) => _board.Ram.Poke(a, v)),
C64MemoryDomainFactory.Create("CIA0", 0x10, a => _board.Cia0.Peek(a), (a, v) => _board.Cia0.Poke(a, v)),
C64MemoryDomainFactory.Create("CIA1", 0x10, a => _board.Cia1.Peek(a), (a, v) => _board.Cia1.Poke(a, v)),
C64MemoryDomainFactory.Create("VIC", 0x40, a => _board.Vic.Peek(a), (a, v) => _board.Vic.Poke(a, v)),
C64MemoryDomainFactory.Create("SID", 0x20, a => _board.Sid.Peek(a), (a, v) => _board.Sid.Poke(a, v)),
};
// chips must be initialized before this code runs!
var domains = new List<MemoryDomain>(1);
domains.Add(new MemoryDomain("System Bus", 0x10000, MemoryDomain.Endian.Little, board.cpu.Peek, board.cpu.Poke));
domains.Add(new MemoryDomain("RAM", 0x10000, MemoryDomain.Endian.Little, board.ram.Peek, board.ram.Poke));
domains.Add(new MemoryDomain("CIA0", 0x10, MemoryDomain.Endian.Little, board.cia0.Peek, board.cia0.Poke));
domains.Add(new MemoryDomain("CIA1", 0x10, MemoryDomain.Endian.Little, board.cia1.Peek, board.cia1.Poke));
domains.Add(new MemoryDomain("VIC", 0x40, MemoryDomain.Endian.Little, board.vic.Peek, board.vic.Poke));
domains.Add(new MemoryDomain("SID", 0x20, MemoryDomain.Endian.Little, board.sid.Peek, board.sid.Poke));
//domains.Add(new MemoryDomain("1541 Bus", 0x10000, MemoryDomain.Endian.Little, new Func<int, byte>(disk.Peek), new Action<int, byte>(disk.Poke)));
//domains.Add(new MemoryDomain("1541 VIA0", 0x10, MemoryDomain.Endian.Little, new Func<int, byte>(disk.PeekVia0), new Action<int, byte>(disk.PokeVia0)));
//domains.Add(new MemoryDomain("1541 VIA1", 0x10, MemoryDomain.Endian.Little, new Func<int, byte>(disk.PeekVia1), new Action<int, byte>(disk.PokeVia1)));
//domains.Add(new MemoryDomain("1541 RAM", 0x1000, MemoryDomain.Endian.Little, new Func<int, byte>(disk.PeekRam), new Action<int, byte>(disk.PokeRam)));
memoryDomains = new MemoryDomainList(domains);
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(memoryDomains);
if (diskDriveEnabled)
{
domains.AddRange(new[]
{
C64MemoryDomainFactory.Create("1541 Bus", 0x10000, a => _board.DiskDrive.Peek(a), (a, v) => _board.DiskDrive.Poke(a, v)),
C64MemoryDomainFactory.Create("1541 RAM", 0x800, a => _board.DiskDrive.Peek(a), (a, v) => _board.DiskDrive.Poke(a, v)),
C64MemoryDomainFactory.Create("1541 VIA0", 0x10, a => _board.DiskDrive.PeekVia0(a), (a, v) => _board.DiskDrive.PokeVia0(a, v)),
C64MemoryDomainFactory.Create("1541 VIA1", 0x10, a => _board.DiskDrive.PeekVia1(a), (a, v) => _board.DiskDrive.PokeVia1(a, v))
});
}
_memoryDomains = new MemoryDomainList(domains);
((BasicServiceProvider) ServiceProvider).Register(_memoryDomains);
}
}
private static class C64MemoryDomainFactory
{
public static MemoryDomain Create(string name, int size, Func<int, int> peekByte, Action<int, int> pokeByte)
{
return new MemoryDomain(name, size, MemoryDomain.Endian.Little, addr => unchecked((byte)peekByte((int)addr)), (addr, val) => pokeByte(unchecked((int)addr), val));
}
}
}
}

View File

@ -9,13 +9,12 @@ using System.Drawing;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
// adelikat: changing settings to default object untl there are actually settings, as the ui depends on it to know if there are any settings avaialable
public partial class C64 : ISettable<object, C64.C64SyncSettings>
// adelikat: changing settings to default object until there are actually settings, as the ui depends on it to know if there are any settings avaialable
public partial class C64 : ISettable<C64.C64Settings, C64.C64SyncSettings>
{
public object /*C64Settings*/ GetSettings()
public C64Settings GetSettings()
{
//return Settings.Clone();
return null;
return Settings.Clone();
}
public C64SyncSettings GetSyncSettings()
@ -23,9 +22,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
return SyncSettings.Clone();
}
public bool PutSettings(object /*C64Settings*/ o)
public bool PutSettings(C64Settings o)
{
//Settings = o;
Settings = o;
return false;
}
@ -40,6 +39,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
public class C64Settings
{
[DisplayName("Border type")]
[Description("Select how to show the border area")]
[DefaultValue(BorderType.SmallProportional)]
public BorderType BorderType { get; set; }
public C64Settings Clone()
{
return (C64Settings)MemberwiseClone();
@ -55,10 +59,25 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
[DisplayName("VIC type")]
[Description("Set the type of video chip to use")]
[DefaultValue(VicType.PAL)]
public VicType vicType { get; set; }
[DefaultValue(VicType.Pal)]
public VicType VicType { get; set; }
public C64SyncSettings Clone()
[DisplayName("SID type")]
[Description("Set the type of sound chip to use")]
[DefaultValue(SidType.OldR2)]
public SidType SidType { get; set; }
[DisplayName("Tape drive type")]
[Description("Set the type of tape drive attached")]
[DefaultValue(TapeDriveType.None)]
public TapeDriveType TapeDriveType { get; set; }
[DisplayName("Disk drive type")]
[Description("Set the type of disk drive attached")]
[DefaultValue(DiskDriveType.None)]
public DiskDriveType DiskDriveType { get; set; }
public C64SyncSettings Clone()
{
return (C64SyncSettings)MemberwiseClone();
}
@ -71,7 +90,32 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
public enum VicType
{
PAL, NTSC, NTSC_OLD, DREAN
Pal, Ntsc, NtscOld, Drean
}
public enum CiaType
{
Pal, Ntsc, PalRevA, NtscRevA
}
public enum BorderType
{
SmallProportional, SmallFixed, Normal, Full
}
public enum SidType
{
OldR2, OldR3, OldR4AR, NewR5
}
public enum TapeDriveType
{
None, Commodore1530
}
public enum DiskDriveType
{
None, Commodore1541
}
}
}

View File

@ -5,7 +5,7 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
sealed public partial class C64 : IStatable
public sealed partial class C64 : IStatable
{
public bool BinarySaveStatesPreferred { get { return false; } }
@ -31,20 +31,19 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
public byte[] SaveStateBinary()
{
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
}
using (var ms = new MemoryStream())
{
var bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
}
}
private void SyncState(Serializer ser)
{
ser.BeginSection("core");
ser.Sync("frame", ref _frame);
ser.Sync("islag", ref _islag);
ser.Sync("lagcount", ref _lagcount);
board.SyncState(ser);
SaveState.SyncObject(ser, this);
ser.EndSection();
}
}

View File

@ -2,17 +2,15 @@
{
public sealed partial class Motherboard
{
private int[] joystickPressed = new int[10];
private int[] keyboardPressed = new int[64];
private readonly int[] _joystickPressed = new int[10];
private readonly int[] _keyboardPressed = new int[64];
private static string[,] joystickMatrix = new string[2, 5]
{
private static readonly string[,] JoystickMatrix = {
{"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button"},
{"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Button"}
};
private static string[,] keyboardMatrix = new string[8, 8]
{
private static readonly string[,] KeyboardMatrix = {
{ "Key Insert/Delete", "Key Return", "Key Cursor Left/Right", "Key F7", "Key F1", "Key F3", "Key F5", "Key Cursor Up/Down" },
{ "Key 3", "Key W", "Key A", "Key 4", "Key Z", "Key S", "Key E", "Key Left Shift" },
{ "Key 5", "Key R", "Key D", "Key 6", "Key C", "Key F", "Key T", "Key X" },
@ -23,79 +21,33 @@
{ "Key 1", "Key Left Arrow", "Key Control", "Key 2", "Key Space", "Key Commodore", "Key Q", "Key Run/Stop" }
};
static private byte[] inputBitMask = new byte[] { 0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F };
static private byte[] inputBitSelect = new byte[] { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
byte cia0InputLatchA;
byte cia0InputLatchB;
int pollIndex;
[SaveState.DoNotSave] int _pollIndex;
private bool _restorePressed;
public void PollInput()
{
_c64.InputCallbacks.Call();
// scan joysticks
pollIndex = 0;
for (int j = 0; j < 5; j++)
_pollIndex = 0;
for (var j = 0; j < 2; j++)
{
for (int i = 0; i < 2; i++)
for (var i = 0; i < 5; i++)
{
joystickPressed[pollIndex++] = controller[joystickMatrix[i, j]] ? -1 : 0;
_joystickPressed[_pollIndex++] = Controller[JoystickMatrix[j, i]] ? -1 : 0;
}
}
// scan keyboard
pollIndex = 0;
for (int i = 0; i < 8; i++)
_pollIndex = 0;
for (var i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
for (var j = 0; j < 8; j++)
{
keyboardPressed[pollIndex++] = controller[keyboardMatrix[i, j]] ? -1 : 0;
}
}
}
private void WriteInputPort()
{
byte portA = cia0.PortAData;
byte portB = cia0.PortBData;
byte resultA = 0xFF;
byte resultB = 0xFF;
byte joyA = 0xFF;
byte joyB = 0xFF;
pollIndex = 0;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
if (keyboardPressed[pollIndex++] != 0)
{
if (((portA & inputBitSelect[i]) == 0) || ((portB & inputBitSelect[j]) == 0))
{
resultA &= inputBitMask[i];
resultB &= inputBitMask[j];
}
}
_keyboardPressed[_pollIndex++] = Controller[KeyboardMatrix[i, j]] ? -1 : 0;
}
}
pollIndex = 0;
for (int i = 0; i < 5; i++)
{
if (joystickPressed[pollIndex++] != 0)
joyB &= inputBitMask[i];
if (joystickPressed[pollIndex++] != 0)
joyA &= inputBitMask[i];
}
resultA &= joyA;
resultB &= joyB;
cia0InputLatchA = resultA;
cia0InputLatchB = resultB;
// this joystick has special rules.
cia0.PortAMask = joyA;
_restorePressed = Controller["Key Restore"];
}
}
}

View File

@ -2,280 +2,282 @@
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge;
using BizHawk.Emulation.Cores.Computers.Commodore64.Cassette;
using BizHawk.Emulation.Cores.Computers.Commodore64.MOS;
using BizHawk.Emulation.Cores.Computers.Commodore64.CassettePort;
using BizHawk.Emulation.Cores.Computers.Commodore64.UserPort;
using BizHawk.Emulation.Cores.Computers.Commodore64.Serial;
using BizHawk.Emulation.Cores.Computers.Commodore64.User;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
/// <summary>
/// Contains the onboard chipset and glue.
/// </summary>
sealed public partial class Motherboard
public sealed partial class Motherboard
{
// chips
public Chip23XX basicRom;
public Chip23XX charRom;
public MOS6526 cia0;
public MOS6526 cia1;
public Chip2114 colorRam;
public MOS6510 cpu;
public Chip23XX kernalRom;
public MOSPLA pla;
public Chip4864 ram;
public Sid sid;
public Vic vic;
// chips
public readonly Chip23128 BasicRom;
public readonly Chip23128 CharRom;
public readonly Cia Cia0;
public readonly Cia Cia1;
public readonly Chip2114 ColorRam;
public readonly Chip6510 Cpu;
public readonly Chip23128 KernalRom;
public readonly Chip90611401 Pla;
public readonly Chip4864 Ram;
public readonly Sid Sid;
public readonly Vic Vic;
// ports
public CartridgePort cartPort;
public CassettePortDevice cassPort;
public IController controller;
public SerialPort serPort;
public UserPortDevice userPort;
// ports
public readonly CartridgePort CartPort;
public readonly CassettePort Cassette;
public IController Controller;
public readonly SerialPort Serial;
public readonly TapeDrive TapeDrive;
public readonly UserPort User;
// state
//public int address;
public byte bus;
public bool inputRead;
public bool irq;
public bool nmi;
// devices
public readonly Drive1541 DiskDrive;
private C64 _c64;
// state
//public int address;
public int Bus;
public bool InputRead;
public bool Irq;
public bool Nmi;
public Motherboard(C64 c64, C64.VicType initRegion)
private readonly C64 _c64;
public Motherboard(C64 c64, C64.VicType initRegion, C64.BorderType borderType, C64.SidType sidType, C64.TapeDriveType tapeDriveType, C64.DiskDriveType diskDriveType)
{
// note: roms need to be added on their own externally
_c64 = c64;
int clockNum, clockDen, mainsFrq;
int clockNum, clockDen;
switch (initRegion)
{
case C64.VicType.PAL:
case C64.VicType.Pal:
clockNum = 17734475;
clockDen = 18;
mainsFrq = 50;
break;
case C64.VicType.NTSC:
case C64.VicType.NTSC_OLD:
break;
case C64.VicType.Ntsc:
clockNum = 14318181;
clockDen = 14;
break;
case C64.VicType.NtscOld:
clockNum = 11250000;
clockDen = 11;
mainsFrq = 60;
break;
case C64.VicType.DREAN:
break;
case C64.VicType.Drean:
clockNum = 14328225;
clockDen = 14;
mainsFrq = 50;
break;
break;
default:
throw new System.Exception();
}
cartPort = new CartridgePort();
cassPort = new CassettePortDevice();
cia0 = new MOS6526(clockNum, clockDen*mainsFrq);
cia1 = new MOS6526(clockNum, clockDen*mainsFrq);
colorRam = new Chip2114();
cpu = new MOS6510();
pla = new MOSPLA();
ram = new Chip4864();
serPort = new SerialPort();
sid = MOS6581.Create(44100, clockNum, clockDen);
CartPort = new CartridgePort();
Cassette = new CassettePort();
ColorRam = new Chip2114();
Cpu = new Chip6510();
Pla = new Chip90611401();
Ram = new Chip4864();
Serial = new SerialPort();
switch (sidType)
{
case C64.SidType.OldR2:
Sid = Chip6581R2.Create(44100, clockNum, clockDen);
break;
case C64.SidType.OldR3:
Sid = Chip6581R3.Create(44100, clockNum, clockDen);
break;
case C64.SidType.OldR4AR:
Sid = Chip6581R4AR.Create(44100, clockNum, clockDen);
break;
case C64.SidType.NewR5:
Sid = Chip8580R5.Create(44100, clockNum, clockDen);
break;
}
switch (initRegion)
{
case C64.VicType.NTSC: vic = MOS6567R8.Create(); break;
case C64.VicType.PAL: vic = MOS6569.Create(); break;
case C64.VicType.NTSC_OLD: vic = MOS6567R56A.Create(); break;
case C64.VicType.DREAN: vic = MOS6572.Create(); break;
case C64.VicType.Ntsc:
Vic = Chip6567R8.Create(borderType);
Cia0 = Chip6526.Create(C64.CiaType.Ntsc, _keyboardPressed, _joystickPressed);
Cia1 = Chip6526.Create(C64.CiaType.Ntsc, Cia1_ReadPortA);
break;
case C64.VicType.Pal:
Vic = Chip6569.Create(borderType);
Cia0 = Chip6526.Create(C64.CiaType.Pal, _keyboardPressed, _joystickPressed);
Cia1 = Chip6526.Create(C64.CiaType.Pal, Cia1_ReadPortA);
break;
case C64.VicType.NtscOld:
Vic = Chip6567R56A.Create(borderType);
Cia0 = Chip6526.Create(C64.CiaType.NtscRevA, _keyboardPressed, _joystickPressed);
Cia1 = Chip6526.Create(C64.CiaType.NtscRevA, Cia1_ReadPortA);
break;
case C64.VicType.Drean:
Vic = Chip6572.Create(borderType);
Cia0 = Chip6526.Create(C64.CiaType.Pal, _keyboardPressed, _joystickPressed);
Cia1 = Chip6526.Create(C64.CiaType.Pal, Cia1_ReadPortA);
break;
}
userPort = new UserPortDevice();
User = new UserPort();
ClockNumerator = clockNum;
ClockDenominator = clockDen;
// Initialize disk drive
switch (diskDriveType)
{
case C64.DiskDriveType.Commodore1541:
DiskDrive = new Drive1541(ClockNumerator, ClockDenominator);
Serial.Connect(DiskDrive);
break;
}
// Initialize tape drive
switch (tapeDriveType)
{
case C64.TapeDriveType.Commodore1530:
TapeDrive = new TapeDrive();
Cassette.Connect(TapeDrive);
break;
}
BasicRom = new Chip23128();
CharRom = new Chip23128();
KernalRom = new Chip23128();
}
[SaveState.DoNotSave] public int ClockNumerator { get; private set; }
[SaveState.DoNotSave] public int ClockDenominator { get; private set; }
// -----------------------------------------
public void Execute()
{
vic.ExecutePhase1();
cpu.ExecutePhase1();
cia0.ExecutePhase1();
cia1.ExecutePhase1();
_vicBank = (0x3 - (Cia1.EffectivePrA & 0x3)) << 14;
vic.ExecutePhase2();
cpu.ExecutePhase2();
cia0.ExecutePhase2();
cia1.ExecutePhase2();
sid.ExecutePhase2();
Vic.ExecutePhase();
Cassette.ExecutePhase();
Serial.ExecutePhase();
Sid.ExecutePhase();
Cia0.ExecutePhase();
Cia1.ExecutePhase();
Cpu.ExecutePhase();
}
public void Flush()
{
sid.Flush();
Sid.Flush();
}
// -----------------------------------------
public void HardReset()
{
bus = 0xFF;
inputRead = false;
Bus = 0xFF;
InputRead = false;
cpu.HardReset();
cia0.HardReset();
cia1.HardReset();
colorRam.HardReset();
ram.HardReset();
serPort.HardReset();
sid.HardReset();
vic.HardReset();
userPort.HardReset();
cassPort.HardReset();
Cia0.HardReset();
Cia1.HardReset();
ColorRam.HardReset();
Ram.HardReset();
Serial.HardReset();
Sid.HardReset();
Vic.HardReset();
User.HardReset();
Cassette.HardReset();
Serial.HardReset();
Cpu.HardReset();
}
// because of how mapping works, the cpu needs to be hard reset twice
cpu.HardReset();
}
public void Init()
public void Init()
{
cartPort.ReadIRQ = Glue_ReadIRQ;
cartPort.ReadNMI = cia1.ReadIRQBuffer;
Cassette.ReadDataOutput = CassPort_ReadDataOutput;
Cassette.ReadMotor = CassPort_ReadMotor;
cassPort.ReadDataOutput = CassPort_ReadDataOutput;
cassPort.ReadMotor = CassPort_ReadMotor;
Cia0.ReadFlag = Cassette.ReadDataInputBuffer;
cia0.ReadCNT = Cia0_ReadCnt;
cia0.ReadFlag = cassPort.ReadDataInputBuffer;
cia0.ReadPortA = Cia0_ReadPortA;
cia0.ReadPortB = Cia0_ReadPortB;
cia0.ReadSP = Cia0_ReadSP;
Cpu.PeekMemory = Pla.Peek;
Cpu.PokeMemory = Pla.Poke;
Cpu.ReadAec = Vic.ReadAec;
Cpu.ReadIrq = Glue_ReadIRQ;
Cpu.ReadNmi = Glue_ReadNMI;
Cpu.ReadPort = Cpu_ReadPort;
Cpu.ReadRdy = Vic.ReadBa;
Cpu.ReadMemory = Pla.Read;
Cpu.WriteMemory = Pla.Write;
Cpu.WriteMemoryPort = Cpu_WriteMemoryPort;
cia1.ReadCNT = Cia1_ReadCnt;
cia1.ReadFlag = userPort.ReadFlag2;
cia1.ReadPortA = Cia1_ReadPortA;
cia1.ReadPortB = userPort.ReadData;
cia1.ReadSP = Cia1_ReadSP;
Pla.PeekBasicRom = BasicRom.Peek;
Pla.PeekCartridgeHi = CartPort.PeekHiRom;
Pla.PeekCartridgeLo = CartPort.PeekLoRom;
Pla.PeekCharRom = CharRom.Peek;
Pla.PeekCia0 = Cia0.Peek;
Pla.PeekCia1 = Cia1.Peek;
Pla.PeekColorRam = ColorRam.Peek;
Pla.PeekExpansionHi = CartPort.PeekHiExp;
Pla.PeekExpansionLo = CartPort.PeekLoExp;
Pla.PeekKernalRom = KernalRom.Peek;
Pla.PeekMemory = Ram.Peek;
Pla.PeekSid = Sid.Peek;
Pla.PeekVic = Vic.Peek;
Pla.PokeCartridgeHi = CartPort.PokeHiRom;
Pla.PokeCartridgeLo = CartPort.PokeLoRom;
Pla.PokeCia0 = Cia0.Poke;
Pla.PokeCia1 = Cia1.Poke;
Pla.PokeColorRam = ColorRam.Poke;
Pla.PokeExpansionHi = CartPort.PokeHiExp;
Pla.PokeExpansionLo = CartPort.PokeLoExp;
Pla.PokeMemory = Ram.Poke;
Pla.PokeSid = Sid.Poke;
Pla.PokeVic = Vic.Poke;
Pla.ReadAec = Vic.ReadAec;
Pla.ReadBa = Vic.ReadBa;
Pla.ReadBasicRom = BasicRom.Read;
Pla.ReadCartridgeHi = CartPort.ReadHiRom;
Pla.ReadCartridgeLo = CartPort.ReadLoRom;
Pla.ReadCharen = Pla_ReadCharen;
Pla.ReadCharRom = CharRom.Read;
Pla.ReadCia0 = Pla_ReadCia0;
Pla.ReadCia1 = Cia1.Read;
Pla.ReadColorRam = Pla_ReadColorRam;
Pla.ReadExpansionHi = Pla_ReadExpansion1;
Pla.ReadExpansionLo = Pla_ReadExpansion0;
Pla.ReadExRom = CartPort.ReadExRom;
Pla.ReadGame = CartPort.ReadGame;
Pla.ReadHiRam = Pla_ReadHiRam;
Pla.ReadKernalRom = KernalRom.Read;
Pla.ReadLoRam = Pla_ReadLoRam;
Pla.ReadMemory = Ram.Read;
Pla.ReadSid = Sid.Read;
Pla.ReadVic = Vic.Read;
Pla.WriteCartridgeHi = CartPort.WriteHiRom;
Pla.WriteCartridgeLo = CartPort.WriteLoRom;
Pla.WriteCia0 = Cia0.Write;
Pla.WriteCia1 = Cia1.Write;
Pla.WriteColorRam = ColorRam.Write;
Pla.WriteExpansionHi = CartPort.WriteHiExp;
Pla.WriteExpansionLo = CartPort.WriteLoExp;
Pla.WriteMemory = Ram.Write;
Pla.WriteSid = Sid.Write;
Pla.WriteVic = Vic.Write;
cpu.PeekMemory = pla.Peek;
cpu.PokeMemory = pla.Poke;
cpu.ReadAEC = vic.ReadAECBuffer;
cpu.ReadIRQ = Glue_ReadIRQ;
cpu.ReadNMI = cia1.ReadIRQBuffer;
cpu.ReadPort = Cpu_ReadPort;
cpu.ReadRDY = vic.ReadBABuffer;
cpu.ReadMemory = pla.Read;
cpu.WriteMemory = pla.Write;
cpu.WriteMemoryPort = Cpu_WriteMemoryPort;
Serial.ReadMasterAtn = SerPort_ReadAtnOut;
Serial.ReadMasterClk = SerPort_ReadClockOut;
Serial.ReadMasterData = SerPort_ReadDataOut;
pla.PeekBasicRom = basicRom.Peek;
pla.PeekCartridgeHi = cartPort.PeekHiRom;
pla.PeekCartridgeLo = cartPort.PeekLoRom;
pla.PeekCharRom = charRom.Peek;
pla.PeekCia0 = cia0.Peek;
pla.PeekCia1 = cia1.Peek;
pla.PeekColorRam = colorRam.Peek;
pla.PeekExpansionHi = cartPort.PeekHiExp;
pla.PeekExpansionLo = cartPort.PeekLoExp;
pla.PeekKernalRom = kernalRom.Peek;
pla.PeekMemory = ram.Peek;
pla.PeekSid = sid.Peek;
pla.PeekVic = vic.Peek;
pla.PokeCartridgeHi = cartPort.PokeHiRom;
pla.PokeCartridgeLo = cartPort.PokeLoRom;
pla.PokeCia0 = cia0.Poke;
pla.PokeCia1 = cia1.Poke;
pla.PokeColorRam = colorRam.Poke;
pla.PokeExpansionHi = cartPort.PokeHiExp;
pla.PokeExpansionLo = cartPort.PokeLoExp;
pla.PokeMemory = ram.Poke;
pla.PokeSid = sid.Poke;
pla.PokeVic = vic.Poke;
pla.ReadAEC = vic.ReadAECBuffer;
pla.ReadBA = vic.ReadBABuffer;
pla.ReadBasicRom = basicRom.Read;
pla.ReadCartridgeHi = cartPort.ReadHiRom;
pla.ReadCartridgeLo = cartPort.ReadLoRom;
pla.ReadCharen = Pla_ReadCharen;
pla.ReadCharRom = charRom.Read;
pla.ReadCia0 = Pla_ReadCia0;
pla.ReadCia1 = cia1.Read;
pla.ReadColorRam = Pla_ReadColorRam;
pla.ReadExpansionHi = cartPort.ReadHiExp;
pla.ReadExpansionLo = cartPort.ReadLoExp;
pla.ReadExRom = cartPort.ReadExRom;
pla.ReadGame = cartPort.ReadGame;
pla.ReadHiRam = Pla_ReadHiRam;
pla.ReadKernalRom = kernalRom.Read;
pla.ReadLoRam = Pla_ReadLoRam;
pla.ReadMemory = ram.Read;
pla.ReadSid = sid.Read;
pla.ReadVic = vic.Read;
pla.WriteCartridgeHi = cartPort.WriteHiRom;
pla.WriteCartridgeLo = cartPort.WriteLoRom;
pla.WriteCia0 = cia0.Write;
pla.WriteCia1 = cia1.Write;
pla.WriteColorRam = colorRam.Write;
pla.WriteExpansionHi = cartPort.WriteHiExp;
pla.WriteExpansionLo = cartPort.WriteLoExp;
pla.WriteMemory = ram.Write;
pla.WriteSid = sid.Write;
pla.WriteVic = vic.Write;
Sid.ReadPotX = Sid_ReadPotX;
Sid.ReadPotY = Sid_ReadPotY;
serPort.ReadAtnOut = SerPort_ReadAtnOut;
serPort.ReadClockOut = SerPort_ReadClockOut;
serPort.ReadDataOut = SerPort_ReadDataOut;
sid.ReadPotX = Sid_ReadPotX;
sid.ReadPotY = Sid_ReadPotY;
vic.ReadMemory = Vic_ReadMemory;
vic.ReadColorRam = colorRam.Read;
Vic.ReadMemory = Vic_ReadMemory;
Vic.ReadColorRam = ColorRam.Read;
}
public void SyncState(Serializer ser)
{
ser.BeginSection("motherboard");
SaveState.SyncObject(ser, this);
ser.EndSection();
//ser.BeginSection("cartridge");
//cartPort.SyncState(ser);
//ser.EndSection();
//ser.BeginSection("cassette");
//cassPort.SyncState(ser);
//ser.EndSection();
//ser.BeginSection("cia0");
//cia0.SyncState(ser);
//ser.EndSection();
//ser.BeginSection("cia1");
//cia1.SyncState(ser);
//ser.EndSection();
//ser.BeginSection("colorram");
//colorRam.SyncState(ser);
//ser.EndSection();
//ser.BeginSection("cpu");
//cpu.SyncState(ser);
//ser.EndSection();
//ser.BeginSection("pla");
//pla.SyncState(ser);
//ser.EndSection();
//ser.BeginSection("ram");
//ram.SyncState(ser);
//ser.EndSection();
//ser.BeginSection("sid");
//sid.SyncState(ser);
//ser.EndSection();
//ser.BeginSection("user");
//userPort.SyncState(ser);
//ser.EndSection();
//ser.BeginSection("vic");
//vic.SyncState(ser);
//ser.EndSection();
}
}
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -5,140 +5,163 @@ using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
sealed public partial class Motherboard
public sealed partial class Motherboard
{
bool CassPort_ReadDataOutput()
private int _lastReadVicAddress = 0x3FFF;
private int _lastReadVicData = 0xFF;
private int _vicBank = 0xC000;
private int _tempCia1Cra;
private bool CassPort_ReadDataOutput()
{
return (cpu.PortData & 0x08) != 0;
return (Cpu.PortData & 0x08) != 0;
}
bool CassPort_ReadMotor()
private bool CassPort_ReadMotor()
{
return (cpu.PortData & 0x20) != 0;
return (Cpu.PortData & 0x20) != 0;
}
bool Cia0_ReadCnt()
/*
private bool Cia0_ReadCnt()
{
return (userPort.ReadCounter1Buffer() && cia0.ReadCNTBuffer());
return User.ReadCounter1() && Cia0.ReadCntBuffer();
}
byte Cia0_ReadPortA()
private int Cia0_ReadPortA()
{
return cia0InputLatchA;
}
byte Cia0_ReadPortB()
private int Cia0_ReadPortB()
{
return cia0InputLatchB;
}
bool Cia0_ReadSP()
private bool Cia0_ReadSP()
{
return (userPort.ReadSerial1Buffer() && cia0.ReadSPBuffer());
return User.ReadSerial1() && Cia0.ReadSpBuffer();
}
bool Cia1_ReadCnt()
private bool Cia1_ReadSP()
{
return (userPort.ReadCounter2Buffer() && cia1.ReadCNTBuffer());
return User.ReadSerial2() && Cia1.ReadSpBuffer();
}
byte Cia1_ReadPortA()
private bool Cia1_ReadCnt()
{
// the low bits are actually the VIC memory address.
byte result = 0xFF;
if (serPort.WriteDataIn())
result &= 0x7F;
if (serPort.WriteClockIn())
result &= 0xBF;
return result;
return User.ReadCounter2() && Cia1.ReadCntBuffer();
}
*/
private int Cia1_ReadPortA()
{
// the low bits are actually the VIC memory address.
return (SerPort_ReadDataOut() && Serial.ReadDeviceData() ? 0x80 : 0x00) |
(SerPort_ReadClockOut() && Serial.ReadDeviceClock() ? 0x40 : 0x00);
}
bool Cia1_ReadSP()
{
return (userPort.ReadSerial2Buffer() && cia1.ReadSPBuffer());
}
private int Cia1_ReadPortB()
{
return 0xFF;
}
byte Cpu_ReadPort()
private int Cpu_ReadPort()
{
byte data = 0x1F;
if (!cassPort.ReadSenseBuffer())
var data = 0x1F;
if (!Cassette.ReadSenseBuffer())
data &= 0xEF;
return data;
}
void Cpu_WriteMemoryPort(int addr, byte val)
private void Cpu_WriteMemoryPort(int addr, int val)
{
pla.WriteMemory(addr, bus);
Pla.WriteMemory(addr, Bus);
}
bool Glue_ReadIRQ()
private bool Glue_ReadIRQ()
{
return cia0.ReadIRQBuffer() & vic.ReadIRQBuffer() & cartPort.ReadIRQBuffer();
return Cia0.ReadIrq() && Vic.ReadIrq() && CartPort.ReadIrq();
}
bool Pla_ReadCharen()
private bool Glue_ReadNMI()
{
return !_restorePressed && Cia1.ReadIrq() && CartPort.ReadNmi();
}
private bool Pla_ReadCharen()
{
return (cpu.PortData & 0x04) != 0;
return (Cpu.PortData & 0x04) != 0;
}
byte Pla_ReadCia0(int addr)
private int Pla_ReadCia0(int addr)
{
if (addr == 0xDC00 || addr == 0xDC01)
{
WriteInputPort();
inputRead = true;
InputRead = true;
}
return cia0.Read(addr);
return Cia0.Read(addr);
}
byte Pla_ReadColorRam(int addr)
private int Pla_ReadColorRam(int addr)
{
byte result = bus;
var result = Bus;
result &= 0xF0;
result |= colorRam.Read(addr);
result |= ColorRam.Read(addr);
return result;
}
bool Pla_ReadHiRam()
private bool Pla_ReadHiRam()
{
return (cpu.PortData & 0x02) != 0;
return (Cpu.PortData & 0x02) != 0;
}
bool Pla_ReadLoRam()
private bool Pla_ReadLoRam()
{
return (cpu.PortData & 0x01) != 0;
return (Cpu.PortData & 0x01) != 0;
}
bool SerPort_ReadAtnOut()
private int Pla_ReadExpansion0(int addr)
{
return CartPort.IsConnected ? CartPort.ReadLoExp(addr) : _lastReadVicData;
}
private int Pla_ReadExpansion1(int addr)
{
return CartPort.IsConnected ? CartPort.ReadHiExp(addr) : _lastReadVicData;
}
private bool SerPort_ReadAtnOut()
{
return (cia1.PortBData & 0x08) == 0;
return !((Cia1.DdrA & 0x08) != 0 && (Cia1.PrA & 0x08) != 0);
}
bool SerPort_ReadClockOut()
{
return (cia1.PortAData & 0x10) == 0;
private bool SerPort_ReadClockOut()
{
return !((Cia1.DdrA & 0x10) != 0 && (Cia1.PrA & 0x10) != 0);
}
bool SerPort_ReadDataOut()
private bool SerPort_ReadDataOut()
{
return (cia1.PortAData & 0x20) == 0;
}
return !((Cia1.DdrA & 0x20) != 0 && (Cia1.PrA & 0x20) != 0);
}
byte Sid_ReadPotX()
private int Sid_ReadPotX()
{
return 0;
}
byte Sid_ReadPotY()
private int Sid_ReadPotY()
{
return 0;
}
byte Vic_ReadMemory(int addr)
private int Vic_ReadMemory(int addr)
{
// the system sees (cia1.PortAData & 0x3) but we use a shortcut
addr |= (0x3 - (((cia1.PortALatch & cia1.PortADirection) | (~cia1.PortADirection)) & 0x3)) << 14;
return pla.VicRead(addr);
// the system sees (cia1.PortAData & 0x3) but we use a shortcut
_lastReadVicAddress = addr | _vicBank;
_lastReadVicData = Pla.VicRead(_lastReadVicAddress);
return _lastReadVicData;
}
}
}

View File

@ -1,101 +1,102 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Computers.Commodore64.MOS;
using System.Windows.Forms;
using BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge;
using BizHawk.Emulation.Cores.Computers.Commodore64.Cassette;
using BizHawk.Emulation.Cores.Computers.Commodore64.Media;
using BizHawk.Emulation.Cores.Computers.Commodore64.Serial;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
[CoreAttributes(
"C64Hawk",
"SaxxonPIke",
"SaxxonPike",
isPorted: false,
isReleased: false
)]
[ServiceNotApplicable(typeof(ISettable<,>))]
sealed public partial class C64 : IEmulator, IStatable, IInputPollable, IDriveLight, IDebuggable, IDisassemblable, IRegionable, ISettable<object, C64.C64SyncSettings>
public sealed partial class C64 : IEmulator, IRegionable
{
// framework
public C64(CoreComm comm, GameInfo game, byte[] rom, string romextension, object Settings, object SyncSettings)
public C64(CoreComm comm, GameInfo game, byte[] rom, string romextension, object settings, object syncSettings)
{
PutSyncSettings((C64SyncSettings)SyncSettings ?? new C64SyncSettings());
PutSettings((C64Settings)Settings ?? new C64Settings());
PutSyncSettings((C64SyncSettings)syncSettings ?? new C64SyncSettings());
PutSettings((C64Settings)settings ?? new C64Settings());
ServiceProvider = new BasicServiceProvider(this);
InputCallbacks = new InputCallbackSystem();
inputFileInfo = new InputFileInfo();
inputFileInfo.Data = rom;
inputFileInfo.Extension = romextension;
CoreComm = comm;
Init(this.SyncSettings.vicType);
cyclesPerFrame = board.vic.CyclesPerFrame;
SetupMemoryDomains();
MemoryCallbacks = new MemoryCallbackSystem();
_inputFileInfo = new InputFileInfo
{
Data = rom,
Extension = romextension
};
CoreComm = comm;
Init(SyncSettings.VicType, Settings.BorderType, SyncSettings.SidType, SyncSettings.TapeDriveType, SyncSettings.DiskDriveType);
_cyclesPerFrame = _board.Vic.CyclesPerFrame;
SetupMemoryDomains(_board.DiskDrive != null);
_memoryCallbacks = new MemoryCallbackSystem();
HardReset();
(ServiceProvider as BasicServiceProvider).Register<IVideoProvider>(board.vic);
}
switch (SyncSettings.VicType)
{
case VicType.Ntsc:
case VicType.Drean:
case VicType.NtscOld:
Region = DisplayType.NTSC;
break;
case VicType.Pal:
Region = DisplayType.PAL;
break;
}
/*private DisplayType queryUserForRegion()
{
Form prompt = new Form() { Width = 160, Height = 120, FormBorderStyle = FormBorderStyle.FixedDialog, Text = "Region selector", StartPosition = FormStartPosition.CenterScreen };
Label textLabel = new Label() { Left = 10, Top = 10, Width = 260, Text = "Please choose a region:" };
RadioButton palButton = new RadioButton() { Left = 10, Top = 30, Width = 70, Text = "PAL", Checked = true };
RadioButton ntscButton = new RadioButton() { Left = 80, Top = 30, Width = 70, Text = "NTSC" };
Button confirmation = new Button() { Text = "Ok", Left = 40, Width = 80, Top = 60, DialogResult = DialogResult.OK };
confirmation.Click += (sender, e) => { prompt.Close(); };
prompt.Controls.Add(textLabel);
prompt.Controls.Add(palButton);
prompt.Controls.Add(ntscButton);
prompt.Controls.Add(confirmation);
prompt.AcceptButton = confirmation;
if (prompt.ShowDialog() != DialogResult.OK || !palButton.Checked && !ntscButton.Checked)
{
throw new Exception("Can't construct new C64 because you didn't choose anything");
}
return palButton.Checked ? DisplayType.PAL : DisplayType.NTSC;
}*/
((BasicServiceProvider) ServiceProvider).Register<IVideoProvider>(_board.Vic);
((BasicServiceProvider) ServiceProvider).Register<IDriveLight>(_board.Serial);
}
// internal variables
private int _frame = 0;
private int cyclesPerFrame;
private InputFileInfo inputFileInfo;
private int _frame;
[SaveState.DoNotSave] private readonly int _cyclesPerFrame;
[SaveState.DoNotSave] private InputFileInfo _inputFileInfo;
private bool _driveLed;
// bizhawk I/O
public CoreComm CoreComm { get; private set; }
// bizhawk I/O
[SaveState.DoNotSave] public CoreComm CoreComm { get; private set; }
// game/rom specific
public GameInfo game;
public string SystemId { get { return "C64"; } }
// game/rom specific
[SaveState.DoNotSave] public GameInfo Game;
[SaveState.DoNotSave] public string SystemId { get { return "C64"; } }
public string BoardName { get { return null; } }
[SaveState.DoNotSave] public string BoardName { get { return null; } }
// running state
public bool DeterministicEmulation { get { return true; } set { ; } }
public int Frame { get { return _frame; } set { _frame = value; } }
[SaveState.DoNotSave] public int Frame { get { return _frame; } set { _frame = value; } }
public void ResetCounters()
{
_frame = 0;
_lagcount = 0;
_islag = false;
frameCycles = 0;
LagCount = 0;
IsLagFrame = false;
_frameCycles = 0;
}
// audio/video
public void EndAsyncSound() { } //TODO
public ISoundProvider SoundProvider { get { return null; } }
[SaveState.DoNotSave] public ISoundProvider SoundProvider { get { return null; } }
public bool StartAsyncSound() { return false; } //TODO
public ISyncSoundProvider SyncSoundProvider { get { return board.sid.resampler; } }
[SaveState.DoNotSave] public ISyncSoundProvider SyncSoundProvider { get { return DCFilter.AsISyncSoundProvider(_board.Sid, 512); } }
// controller
public ControllerDefinition ControllerDefinition { get { return C64ControllerDefinition; } }
public IController Controller { get { return board.controller; } set { board.controller = value; } }
public static readonly ControllerDefinition C64ControllerDefinition = new ControllerDefinition
[SaveState.DoNotSave] public ControllerDefinition ControllerDefinition { get { return C64ControllerDefinition; } }
[SaveState.DoNotSave] public IController Controller { get { return _board.Controller; } set { _board.Controller = value; } }
[SaveState.DoNotSave]
private static readonly ControllerDefinition C64ControllerDefinition = new ControllerDefinition
{
Name = "Commodore 64 Controller",
BoolButtons =
@ -111,7 +112,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
}
};
public IEmulatorServiceProvider ServiceProvider { get; private set; }
[SaveState.DoNotSave] public IEmulatorServiceProvider ServiceProvider { get; private set; }
public DisplayType Region
{
@ -121,14 +122,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
public void Dispose()
{
if (board.sid != null)
{
board.sid.Dispose();
board.sid = null;
}
}
int frameCycles;
private int _frameCycles;
// process frame
public void FrameAdvance(bool render, bool rendersound)
@ -137,130 +133,157 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
DoCycle();
}
while (frameCycles != 0);
while (_frameCycles != 0);
}
private void DoCycle()
{
if (frameCycles == 0) {
board.inputRead = false;
board.PollInput();
board.cpu.LagCycles = 0;
if (_frameCycles == 0) {
_board.InputRead = false;
_board.PollInput();
_board.Cpu.LagCycles = 0;
}
//disk.Execute();
board.Execute();
frameCycles++;
_driveLed = _board.Serial.ReadDeviceLight();
_board.Execute();
_frameCycles++;
// load PRG file if needed
if (loadPrg)
if (_loadPrg)
{
// check to see if cpu PC is at the BASIC warm start vector
if (board.cpu.PC == ((board.ram.Peek(0x0303) << 8) | board.ram.Peek(0x0302)))
if (_board.Cpu.Pc != 0 && _board.Cpu.Pc == ((_board.Ram.Peek(0x0303) << 8) | _board.Ram.Peek(0x0302)))
{
//board.ram.Poke(0x0302, 0xAE);
//board.ram.Poke(0x0303, 0xA7);
////board.ram.Poke(0x0302, board.ram.Peek(0x0308));
////board.ram.Poke(0x0303, board.ram.Peek(0x0309));
//if (inputFileInfo.Data.Length >= 6)
//{
// board.ram.Poke(0x0039, inputFileInfo.Data[4]);
// board.ram.Poke(0x003A, inputFileInfo.Data[5]);
//}
PRG.Load(board.pla, inputFileInfo.Data);
loadPrg = false;
Prg.Load(_board.Pla, _inputFileInfo.Data);
_loadPrg = false;
}
}
if (frameCycles == cyclesPerFrame)
{
board.Flush();
_islag = !board.inputRead;
if (_frameCycles != _cyclesPerFrame)
{
return;
}
if (_islag)
_lagcount++;
frameCycles -= cyclesPerFrame;
_frame++;
_board.Flush();
IsLagFrame = !_board.InputRead;
//Console.WriteLine("CPUPC: " + C64Util.ToHex(board.cpu.PC, 4) + " 1541PC: " + C64Util.ToHex(disk.PC, 4));
int test = board.cpu.LagCycles;
DriveLightOn = DriveLED;
}
if (IsLagFrame)
LagCount++;
_frameCycles -= _cyclesPerFrame;
_frame++;
}
private void HandleFirmwareError(string file)
{
System.Windows.Forms.MessageBox.Show("the C64 core is referencing a firmware file which could not be found. Please make sure it's in your configured C64 firmwares folder. The referenced filename is: " + file);
MessageBox.Show("the C64 core is referencing a firmware file which could not be found. Please make sure it's in your configured C64 firmwares folder. The referenced filename is: " + file);
throw new FileNotFoundException();
}
private Motherboard board;
private bool loadPrg;
private Motherboard _board;
private bool _loadPrg;
private byte[] GetFirmware(string name, int length)
private byte[] GetFirmware(int length, params string[] names)
{
byte[] result = CoreComm.CoreFileProvider.GetFirmware("C64", name, true);
if (result.Length != length)
throw new MissingFirmwareException(string.Format("Firmware {0} was {1} bytes, should be {2} bytes", name, result.Length, length));
var result = names.Select(n => CoreComm.CoreFileProvider.GetFirmware("C64", n, false)).FirstOrDefault(b => b != null && b.Length == length);
if (result == null)
throw new MissingFirmwareException(string.Format("At least one of these firmwares is required: {0}", string.Join(", ", names)));
return result;
}
private void Init(VicType initRegion)
private void Init(VicType initRegion, BorderType borderType, SidType sidType, TapeDriveType tapeDriveType, DiskDriveType diskDriveType)
{
board = new Motherboard(this, initRegion);
InitRoms();
board.Init();
// Force certain drive types to be available depending on ROM type
switch (_inputFileInfo.Extension.ToUpper())
{
case @".D64":
case @".G64":
if (diskDriveType == DiskDriveType.None)
{
diskDriveType = DiskDriveType.Commodore1541;
}
break;
case @".TAP":
if (tapeDriveType == TapeDriveType.None)
{
tapeDriveType = TapeDriveType.Commodore1530;
}
break;
}
_board = new Motherboard(this, initRegion, borderType, sidType, tapeDriveType, diskDriveType);
InitRoms(diskDriveType);
_board.Init();
InitMedia();
// configure video
CoreComm.VsyncDen = board.vic.CyclesPerFrame;
CoreComm.VsyncNum = board.vic.CyclesPerSecond;
}
// configure video
CoreComm.VsyncDen = _board.Vic.CyclesPerFrame;
CoreComm.VsyncNum = _board.Vic.CyclesPerSecond;
}
private void InitMedia()
{
switch (inputFileInfo.Extension.ToUpper())
switch (_inputFileInfo.Extension.ToUpper())
{
case @".CRT":
Cart cart = Cart.Load(inputFileInfo.Data);
case @".D64":
var d64 = D64.Read(_inputFileInfo.Data);
if (d64 != null)
{
_board.DiskDrive.InsertMedia(d64);
}
break;
case @".G64":
var g64 = G64.Read(_inputFileInfo.Data);
if (g64 != null)
{
_board.DiskDrive.InsertMedia(g64);
}
break;
case @".CRT":
var cart = CartridgeDevice.Load(_inputFileInfo.Data);
if (cart != null)
{
board.cartPort.Connect(cart);
_board.CartPort.Connect(cart);
}
break;
case @".TAP":
CassettePort.Tape tape = CassettePort.Tape.Load(inputFileInfo.Data);
var tape = Tape.Load(_inputFileInfo.Data);
if (tape != null)
{
board.cassPort.Connect(tape);
_board.TapeDrive.Insert(tape);
}
break;
case @".PRG":
if (inputFileInfo.Data.Length > 2)
loadPrg = true;
if (_inputFileInfo.Data.Length > 2)
_loadPrg = true;
break;
}
}
private void InitRoms()
private void InitRoms(DiskDriveType diskDriveType)
{
byte[] basicRom = GetFirmware("Basic", 0x2000);
byte[] charRom = GetFirmware("Chargen", 0x1000);
byte[] kernalRom = GetFirmware("Kernal", 0x2000);
var basicRom = GetFirmware(0x2000, "Basic");
var charRom = GetFirmware(0x1000, "Chargen");
var kernalRom = GetFirmware(0x2000, "Kernal");
board.basicRom = new Chip23XX(Chip23XXmodel.Chip2364, basicRom);
board.kernalRom = new Chip23XX(Chip23XXmodel.Chip2364, kernalRom);
board.charRom = new Chip23XX(Chip23XXmodel.Chip2332, charRom);
_board.BasicRom.Flash(basicRom);
_board.KernalRom.Flash(kernalRom);
_board.CharRom.Flash(charRom);
if (diskDriveType == DiskDriveType.Commodore1541)
{
var diskRom = GetFirmware(0x4000, "Drive1541II", "Drive1541");
_board.DiskDrive.DriveRom.Flash(diskRom);
}
}
// ------------------------------------
public void HardReset()
{
board.HardReset();
//disk.HardReset();
_board.HardReset();
}
}
}

View File

@ -1,35 +1,37 @@
namespace BizHawk.Emulation.Cores.Computers.Commodore64
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public static class C64Util
{
static public string ToBinary(int n, int charsmin)
public static string ToBinary(int n, int charsmin)
{
string result = "";
var result = new StringBuilder(string.Empty);
while (n > 0 || charsmin > 0)
{
result = (((n & 0x1) != 0) ? "1" : "0") + result;
result.Insert(0, (n & 0x1) != 0 ? "1" : "0");
n >>= 1;
if (charsmin > 0)
charsmin--;
}
return result;
return result.ToString();
}
static public string ToHex(int n, int charsmin)
public static string ToHex(int n, int charsmin)
{
string result = "";
var result = new StringBuilder(string.Empty);
while (n > 0 || charsmin > 0)
while (n > 0 || charsmin > 0)
{
result = "0123456789ABCDEF".Substring((n & 0xF), 1) + result;
result.Insert(0, "0123456789ABCDEF".Substring(n & 0xF, 1));
n >>= 4;
if (charsmin > 0)
charsmin--;
}
return result;
return result.ToString();
}
}
}

View File

@ -1,268 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
// this is the base cartridge class
public class Cart
{
// ---------------------------------
static public Cart Load(byte[] crtFile)
{
Cart result = null;
MemoryStream mem = new MemoryStream(crtFile);
BinaryReader reader = new BinaryReader(mem);
if (new string(reader.ReadChars(16)) == "C64 CARTRIDGE ")
{
List<int> chipAddress = new List<int>();
List<int> chipBank = new List<int>();
List<byte[]> chipData = new List<byte[]>();
List<int> chipType = new List<int>();
int headerLength = ReadCRTInt(reader);
int version = ReadCRTShort(reader);
int mapper = ReadCRTShort(reader);
bool exrom = (reader.ReadByte() != 0);
bool game = (reader.ReadByte() != 0);
// reserved
reader.ReadBytes(6);
// cartridge name
reader.ReadBytes(0x20);
// skip extra header bytes
if (headerLength > 0x40)
{
reader.ReadBytes(headerLength - 0x40);
}
// read chips
while (reader.PeekChar() >= 0)
{
if (new string(reader.ReadChars(4)) == "CHIP")
{
int chipLength = ReadCRTInt(reader);
chipType.Add(ReadCRTShort(reader));
chipBank.Add(ReadCRTShort(reader));
chipAddress.Add(ReadCRTShort(reader));
int chipDataLength = ReadCRTShort(reader);
chipData.Add(reader.ReadBytes(chipDataLength));
chipLength -= (chipDataLength + 0x10);
if (chipLength > 0)
reader.ReadBytes(chipLength);
}
}
if (chipData.Count > 0)
{
switch (mapper)
{
case 0x0000:
result = new Mapper0000(chipAddress, chipBank, chipData, game, exrom);
break;
case 0x0005:
result = new Mapper0005(chipAddress, chipBank, chipData);
break;
case 0x000B:
result = new Mapper000B(chipAddress, chipBank, chipData);
break;
case 0x000F:
result = new Mapper000F(chipAddress, chipBank, chipData);
break;
case 0x0011:
result = new Mapper0011(chipAddress, chipBank, chipData);
break;
case 0x0012:
result = new Mapper0012(chipAddress, chipBank, chipData);
break;
case 0x0013:
result = new Mapper0013(chipAddress, chipBank, chipData);
break;
case 0x0020:
result = new Mapper0020(chipAddress, chipBank, chipData);
break;
default:
throw new Exception("This cartridge file uses an unrecognized mapper: " + mapper);
}
result.HardReset();
}
}
return result;
}
static private int ReadCRTShort(BinaryReader reader)
{
int result;
result = (int)reader.ReadByte() << 8;
result |= (int)reader.ReadByte();
return result;
}
static private int ReadCRTInt(BinaryReader reader)
{
int result;
result = (int)reader.ReadByte() << 24;
result |= (int)reader.ReadByte() << 16;
result |= (int)reader.ReadByte() << 8;
result |= (int)reader.ReadByte();
return result;
}
// ---------------------------------
protected bool pinExRom;
protected bool pinGame;
protected bool pinIRQ;
protected bool pinNMI;
protected bool pinReset;
protected bool validCartridge;
public virtual void ExecutePhase1()
{
}
public virtual void ExecutePhase2()
{
}
public bool ExRom
{
get
{
return pinExRom;
}
}
public bool Game
{
get
{
return pinGame;
}
}
public virtual void HardReset()
{
pinIRQ = true;
pinNMI = true;
pinReset = true;
}
public bool IRQ
{
get
{
return pinIRQ;
}
}
public bool NMI
{
get
{
return pinNMI;
}
}
public virtual byte Peek8000(int addr)
{
return 0xFF;
}
public virtual byte PeekA000(int addr)
{
return 0xFF;
}
public virtual byte PeekDE00(int addr)
{
return 0xFF;
}
public virtual byte PeekDF00(int addr)
{
return 0xFF;
}
public virtual void Poke8000(int addr, byte val)
{
}
public virtual void PokeA000(int addr, byte val)
{
}
public virtual void PokeDE00(int addr, byte val)
{
}
public virtual void PokeDF00(int addr, byte val)
{
}
public virtual byte Read8000(int addr)
{
return 0xFF;
}
public virtual byte ReadA000(int addr)
{
return 0xFF;
}
public virtual byte ReadDE00(int addr)
{
return 0xFF;
}
public virtual byte ReadDF00(int addr)
{
return 0xFF;
}
public bool Reset
{
get
{
return pinReset;
}
}
public virtual void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
public bool Valid
{
get
{
return validCartridge;
}
}
public virtual void Write8000(int addr, byte val)
{
}
public virtual void WriteA000(int addr, byte val)
{
}
public virtual void WriteDE00(int addr, byte val)
{
}
public virtual void WriteDF00(int addr, byte val)
{
}
}
}

View File

@ -0,0 +1,268 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
public abstract partial class CartridgeDevice
{
// ---------------------------------
public static CartridgeDevice Load(byte[] crtFile)
{
var mem = new MemoryStream(crtFile);
var reader = new BinaryReader(mem);
if (new string(reader.ReadChars(16)) != "C64 CARTRIDGE ")
{
return null;
}
var chipAddress = new List<int>();
var chipBank = new List<int>();
var chipData = new List<int[]>();
var chipType = new List<int>();
var headerLength = ReadCRTInt(reader);
var version = ReadCRTShort(reader);
var mapper = ReadCRTShort(reader);
var exrom = reader.ReadByte() != 0;
var game = reader.ReadByte() != 0;
// reserved
reader.ReadBytes(6);
// cartridge name
reader.ReadBytes(0x20);
// skip extra header bytes
if (headerLength > 0x40)
{
reader.ReadBytes(headerLength - 0x40);
}
// read chips
while (reader.PeekChar() >= 0)
{
if (new string(reader.ReadChars(4)) != "CHIP")
{
break;
}
var chipLength = ReadCRTInt(reader);
chipType.Add(ReadCRTShort(reader));
chipBank.Add(ReadCRTShort(reader));
chipAddress.Add(ReadCRTShort(reader));
var chipDataLength = ReadCRTShort(reader);
chipData.Add(reader.ReadBytes(chipDataLength).Select(x => (int)x).ToArray());
chipLength -= chipDataLength + 0x10;
if (chipLength > 0)
reader.ReadBytes(chipLength);
}
if (chipData.Count <= 0)
{
return null;
}
CartridgeDevice result;
switch (mapper)
{
case 0x0000:
result = new Mapper0000(chipAddress, chipBank, chipData, game, exrom);
break;
case 0x0005:
result = new Mapper0005(chipAddress, chipBank, chipData);
break;
case 0x000B:
result = new Mapper000B(chipAddress, chipBank, chipData);
break;
case 0x000F:
result = new Mapper000F(chipAddress, chipBank, chipData);
break;
case 0x0011:
result = new Mapper0011(chipAddress, chipBank, chipData);
break;
case 0x0012:
result = new Mapper0012(chipAddress, chipBank, chipData);
break;
case 0x0013:
result = new Mapper0013(chipAddress, chipBank, chipData);
break;
case 0x0020:
result = new Mapper0020(chipAddress, chipBank, chipData);
break;
default:
throw new Exception("This cartridge file uses an unrecognized mapper: " + mapper);
}
result.HardReset();
return result;
}
private static int ReadCRTShort(BinaryReader reader)
{
return (reader.ReadByte() << 8) |
reader.ReadByte();
}
private static int ReadCRTInt(BinaryReader reader)
{
return (reader.ReadByte() << 24) |
(reader.ReadByte() << 16) |
(reader.ReadByte() << 8) |
reader.ReadByte();
}
// ---------------------------------
protected bool pinExRom;
protected bool pinGame;
protected bool pinIRQ;
protected bool pinNMI;
protected bool pinReset;
protected bool validCartridge;
public virtual void ExecutePhase1()
{
}
public virtual void ExecutePhase2()
{
}
public bool ExRom
{
get
{
return pinExRom;
}
}
public bool Game
{
get
{
return pinGame;
}
}
public virtual void HardReset()
{
pinIRQ = true;
pinNMI = true;
pinReset = true;
}
public bool IRQ
{
get
{
return pinIRQ;
}
}
public bool NMI
{
get
{
return pinNMI;
}
}
public virtual int Peek8000(int addr)
{
return 0xFF;
}
public virtual int PeekA000(int addr)
{
return 0xFF;
}
public virtual int PeekDE00(int addr)
{
return 0xFF;
}
public virtual int PeekDF00(int addr)
{
return 0xFF;
}
public virtual void Poke8000(int addr, int val)
{
}
public virtual void PokeA000(int addr, int val)
{
}
public virtual void PokeDE00(int addr, int val)
{
}
public virtual void PokeDF00(int addr, int val)
{
}
public virtual int Read8000(int addr)
{
return 0xFF;
}
public virtual int ReadA000(int addr)
{
return 0xFF;
}
public virtual int ReadDE00(int addr)
{
return 0xFF;
}
public virtual int ReadDF00(int addr)
{
return 0xFF;
}
public bool Reset
{
get
{
return pinReset;
}
}
public virtual void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
public bool Valid
{
get
{
return validCartridge;
}
}
public virtual void Write8000(int addr, int val)
{
}
public virtual void WriteA000(int addr, int val)
{
}
public virtual void WriteDE00(int addr, int val)
{
}
public virtual void WriteDF00(int addr, int val)
{
}
}
}

View File

@ -0,0 +1,124 @@
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
public sealed class CartridgePort
{
private CartridgeDevice _cartridgeDevice;
private bool _connected;
public CartridgePort()
{
// start up with no media connected
Disconnect();
}
// ------------------------------------------
public int PeekHiExp(int addr)
{
return _connected ? _cartridgeDevice.PeekDF00(addr & 0x00FF) : 0xFF;
}
public int PeekHiRom(int addr)
{
return _connected ? _cartridgeDevice.PeekA000(addr & 0x1FFF) : 0xFF;
}
public int PeekLoExp(int addr)
{
return _connected ? _cartridgeDevice.PeekDE00(addr & 0x00FF) : 0xFF;
}
public int PeekLoRom(int addr)
{
return _connected ? _cartridgeDevice.Peek8000(addr & 0x1FFF) : 0xFF;
}
public void PokeHiExp(int addr, int val) { if (_connected) { _cartridgeDevice.PokeDF00(addr & 0x00FF, val); } }
public void PokeHiRom(int addr, int val) { if (_connected) { _cartridgeDevice.PokeA000(addr & 0x1FFF, val); } }
public void PokeLoExp(int addr, int val) { if (_connected) { _cartridgeDevice.PokeDE00(addr & 0x00FF, val); } }
public void PokeLoRom(int addr, int val) { if (_connected) { _cartridgeDevice.Poke8000(addr & 0x1FFF, val); } }
public bool ReadExRom()
{
return !_connected || _cartridgeDevice.ExRom;
}
public bool ReadGame()
{
return !_connected || _cartridgeDevice.Game;
}
public int ReadHiExp(int addr)
{
return _connected ? _cartridgeDevice.ReadDF00(addr & 0x00FF) : 0xFF;
}
public int ReadHiRom(int addr)
{
return _connected ? _cartridgeDevice.ReadA000(addr & 0x1FFF) : 0xFF;
}
public int ReadLoExp(int addr)
{
return _connected ? _cartridgeDevice.ReadDE00(addr & 0x00FF) : 0xFF;
}
public int ReadLoRom(int addr)
{
return _connected ? _cartridgeDevice.Read8000(addr & 0x1FFF) : 0xFF;
}
public void WriteHiExp(int addr, int val) { if (_connected) { _cartridgeDevice.WriteDF00(addr & 0x00FF, val); } }
public void WriteHiRom(int addr, int val) { if (_connected) { _cartridgeDevice.WriteA000(addr & 0x1FFF, val); } }
public void WriteLoExp(int addr, int val) { if (_connected) { _cartridgeDevice.WriteDE00(addr & 0x00FF, val); } }
public void WriteLoRom(int addr, int val) { if (_connected) { _cartridgeDevice.Write8000(addr & 0x1FFF, val); } }
// ------------------------------------------
public void Connect(CartridgeDevice newCartridgeDevice)
{
_connected = true;
_cartridgeDevice = newCartridgeDevice;
}
public void Disconnect()
{
_cartridgeDevice = null;
_connected = false;
}
public void HardReset()
{
// note: this will not disconnect any attached media
if (_connected)
{
_cartridgeDevice.HardReset();
}
}
public bool IsConnected
{
get
{
return _connected;
}
}
public bool ReadIrq()
{
return !_connected || _cartridgeDevice.IRQ;
}
public bool ReadNmi()
{
return !_connected || _cartridgeDevice.NMI;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -1,96 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
sealed public class Mapper0000 : Cart
{
private byte[] romA;
private int romAMask;
private byte[] romB;
private int romBMask;
public abstract partial class CartridgeDevice
{
private sealed class Mapper0000 : CartridgeDevice
{
private readonly int[] _romA;
private readonly int _romAMask;
private readonly int[] _romB;
private readonly int _romBMask;
// standard cartridge mapper (Commodore)
// note that this format also covers Ultimax carts
// standard cartridge mapper (Commodore)
// note that this format also covers Ultimax carts
public Mapper0000(List<int> newAddresses, List<int> newBanks, List<byte[]> newData, bool game, bool exrom)
{
pinGame = game;
pinExRom = exrom;
public Mapper0000(IList<int> newAddresses, IList<int> newBanks, IList<int[]> newData, bool game, bool exrom)
{
pinGame = game;
pinExRom = exrom;
validCartridge = true;
// default to empty banks
romA = new byte[1];
romB = new byte[1];
romA[0] = 0xFF;
romB[0] = 0xFF;
validCartridge = true;
for (int i = 0; i < newAddresses.Count; i++)
{
if (newAddresses[i] == 0x8000)
{
switch (newData[i].Length)
{
case 0x1000:
romAMask = 0x0FFF;
romA = newData[i];
break;
case 0x2000:
romAMask = 0x1FFF;
romA = newData[i];
break;
case 0x4000:
romAMask = 0x1FFF;
romBMask = 0x1FFF;
// split the rom into two banks
romA = new byte[0x2000];
romB = new byte[0x2000];
Array.Copy(newData[i], 0x0000, romA, 0x0000, 0x2000);
Array.Copy(newData[i], 0x2000, romB, 0x0000, 0x2000);
break;
default:
validCartridge = false;
return;
}
}
else if (newAddresses[i] == 0xA000 || newAddresses[i] == 0xE000)
{
switch (newData[i].Length)
{
case 0x1000:
romBMask = 0x0FFF;
break;
case 0x2000:
romBMask = 0x1FFF;
break;
default:
validCartridge = false;
return;
}
romB = newData[i];
}
}
}
// default to empty banks
_romA = new int[1];
_romB = new int[1];
_romA[0] = 0xFF;
_romB[0] = 0xFF;
public override byte Peek8000(int addr)
{
return romA[addr & romAMask];
}
for (var i = 0; i < newAddresses.Count; i++)
{
if (newAddresses[i] == 0x8000)
{
switch (newData[i].Length)
{
case 0x1000:
_romAMask = 0x0FFF;
_romA = newData[i];
break;
case 0x2000:
_romAMask = 0x1FFF;
_romA = newData[i];
break;
case 0x4000:
_romAMask = 0x1FFF;
_romBMask = 0x1FFF;
// split the rom into two banks
_romA = new int[0x2000];
_romB = new int[0x2000];
Array.Copy(newData[i], 0x0000, _romA, 0x0000, 0x2000);
Array.Copy(newData[i], 0x2000, _romB, 0x0000, 0x2000);
break;
default:
validCartridge = false;
return;
}
}
else if (newAddresses[i] == 0xA000 || newAddresses[i] == 0xE000)
{
switch (newData[i].Length)
{
case 0x1000:
_romBMask = 0x0FFF;
break;
case 0x2000:
_romBMask = 0x1FFF;
break;
default:
validCartridge = false;
return;
}
_romB = newData[i];
}
}
}
public override byte PeekA000(int addr)
{
return romB[addr & romBMask];
}
public override int Peek8000(int addr)
{
return _romA[addr & _romAMask];
}
public override byte Read8000(int addr)
{
return romA[addr & romAMask];
}
public override int PeekA000(int addr)
{
return _romB[addr & _romBMask];
}
public override byte ReadA000(int addr)
{
return romB[addr & romBMask];
}
}
public override int Read8000(int addr)
{
return _romA[addr & _romAMask];
}
public override int ReadA000(int addr)
{
return _romB[addr & _romBMask];
}
}
}
}

View File

@ -1,159 +1,150 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
sealed public class Mapper0005 : Cart
{
private byte[][] banksA = new byte[0][]; //8000
private byte[][] banksB = new byte[0][]; //A000
private int bankMask;
private int bankNumber;
private byte[] currentBankA;
private byte[] currentBankB;
private byte[] dummyBank;
public abstract partial class CartridgeDevice
{
private sealed class Mapper0005 : CartridgeDevice
{
private readonly int[][] _banksA; //8000
private readonly int[][] _banksB = new int[0][]; //A000
private readonly int _bankMask;
private int _bankNumber;
private int[] _currentBankA;
private int[] _currentBankB;
private readonly int[] _dummyBank;
public Mapper0005(List<int> newAddresses, List<int> newBanks, List<byte[]> newData)
{
int count = newAddresses.Count;
public Mapper0005(IList<int> newAddresses, IList<int> newBanks, IList<int[]> newData)
{
var count = newAddresses.Count;
// build dummy bank
dummyBank = new byte[0x2000];
for (int i = 0; i < 0x2000; i++)
dummyBank[i] = 0xFF; // todo: determine if this is correct
// build dummy bank
_dummyBank = new int[0x2000];
for (var i = 0; i < 0x2000; i++)
_dummyBank[i] = 0xFF; // todo: determine if this is correct
if (count == 64) //512k
{
pinGame = true;
pinExRom = false;
bankMask = 0x3F;
banksA = new byte[64][];
}
else if (count == 32) //256k
{
// this specific config is a weird exception
pinGame = false;
pinExRom = false;
bankMask = 0x0F;
banksA = new byte[16][];
banksB = new byte[16][];
}
else if (count == 16) //128k
{
pinGame = true;
pinExRom = false;
bankMask = 0x0F;
banksA = new byte[16][];
}
else if (count == 8) //64k
{
pinGame = true;
pinExRom = false;
bankMask = 0x07;
banksA = new byte[8][];
}
else if (count == 4) //32k
{
pinGame = true;
pinExRom = false;
bankMask = 0x03;
banksA = new byte[4][];
}
else if (count == 2) //16k
{
pinGame = true;
pinExRom = false;
bankMask = 0x01;
banksA = new byte[2][];
}
else if (count == 1) //8k
{
pinGame = true;
pinExRom = false;
bankMask = 0x00;
banksA = new byte[1][];
}
else
{
// we don't know what format this is...
throw new Exception("This looks like an Ocean cartridge but cannot be loaded...");
}
switch (count)
{
case 64:
pinGame = true;
pinExRom = false;
_bankMask = 0x3F;
_banksA = new int[64][];
break;
case 32:
// this specific config is a weird exception
pinGame = false;
pinExRom = false;
_bankMask = 0x0F;
_banksA = new int[16][];
_banksB = new int[16][];
break;
case 16:
pinGame = true;
pinExRom = false;
_bankMask = 0x0F;
_banksA = new int[16][];
break;
case 8:
pinGame = true;
pinExRom = false;
_bankMask = 0x07;
_banksA = new int[8][];
break;
case 4:
pinGame = true;
pinExRom = false;
_bankMask = 0x03;
_banksA = new int[4][];
break;
case 2:
pinGame = true;
pinExRom = false;
_bankMask = 0x01;
_banksA = new int[2][];
break;
case 1:
pinGame = true;
pinExRom = false;
_bankMask = 0x00;
_banksA = new int[1][];
break;
default:
throw new Exception("This looks like an Ocean cartridge but cannot be loaded...");
}
// for safety, initialize all banks to dummy
for (int i = 0; i < banksA.Length; i++)
banksA[i] = dummyBank;
for (int i = 0; i < banksB.Length; i++)
banksB[i] = dummyBank;
// for safety, initialize all banks to dummy
for (var i = 0; i < _banksA.Length; i++)
_banksA[i] = _dummyBank;
for (var i = 0; i < _banksB.Length; i++)
_banksB[i] = _dummyBank;
// now load in the banks
for (int i = 0; i < count; i++)
{
if (newAddresses[i] == 0x8000)
{
banksA[newBanks[i] & bankMask] = newData[i];
}
else if (newAddresses[i] == 0xA000 || newAddresses[i] == 0xE000)
{
banksB[newBanks[i] & bankMask] = newData[i];
}
}
// now load in the banks
for (var i = 0; i < count; i++)
{
switch (newAddresses[i])
{
case 0x8000:
_banksA[newBanks[i] & _bankMask] = newData[i];
break;
case 0xA000:
case 0xE000:
_banksB[newBanks[i] & _bankMask] = newData[i];
break;
}
}
BankSet(0);
}
BankSet(0);
}
private void BankSet(int index)
{
bankNumber = index & bankMask;
if (!pinExRom)
currentBankA = banksA[bankNumber];
else
currentBankA = dummyBank;
private void BankSet(int index)
{
_bankNumber = index & _bankMask;
_currentBankA = !pinExRom ? _banksA[_bankNumber] : _dummyBank;
_currentBankB = !pinGame ? _banksB[_bankNumber] : _dummyBank;
}
if (!pinGame)
currentBankB = banksB[bankNumber];
else
currentBankB = dummyBank;
}
public override int Peek8000(int addr)
{
return _currentBankA[addr];
}
public override byte Peek8000(int addr)
{
return currentBankA[addr];
}
public override int PeekA000(int addr)
{
return _currentBankB[addr];
}
public override byte PeekA000(int addr)
{
return currentBankB[addr];
}
public override void PokeDE00(int addr, int val)
{
if (addr == 0x00)
BankSet(val);
}
public override void PokeDE00(int addr, byte val)
{
if (addr == 0x00)
BankSet(val);
}
public override int Read8000(int addr)
{
return _currentBankA[addr];
}
public override byte Read8000(int addr)
{
return currentBankA[addr];
}
public override int ReadA000(int addr)
{
return _currentBankB[addr];
}
public override byte ReadA000(int addr)
{
return currentBankB[addr];
}
public override void WriteDE00(int addr, int val)
{
if (addr == 0x00)
BankSet(val);
}
public override void WriteDE00(int addr, byte val)
{
if (addr == 0x00)
BankSet(val);
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
if (ser.IsReader)
BankSet(bankNumber);
}
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
if (ser.IsReader)
BankSet(_bankNumber);
}
}
}
}

View File

@ -1,57 +1,63 @@
using System;
using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
// Westermann Learning mapper.
// Starts up with both banks enabled, any read to DFxx
// turns off the high bank by bringing GAME high.
// I suspect that the game loads by copying all hirom to
// the RAM underneath (BASIC variable values probably)
// and then disables once loaded.
// Westermann Learning mapper.
// Starts up with both banks enabled, any read to DFxx
// turns off the high bank by bringing GAME high.
// I suspect that the game loads by copying all hirom to
// the RAM underneath (BASIC variable values probably)
// and then disables once loaded.
sealed public class Mapper000B : Cart
{
private byte[] rom = new byte[0x4000];
public abstract partial class CartridgeDevice
{
private sealed class Mapper000B : CartridgeDevice
{
private readonly int[] _rom = new int[0x4000];
public Mapper000B(List<int> newAddresses, List<int> newBanks, List<byte[]> newData)
{
validCartridge = false;
public Mapper000B(IList<int> newAddresses, IList<int> newBanks, IList<int[]> newData)
{
validCartridge = false;
for (int i = 0; i < 0x4000; i++)
rom[i] = 0xFF;
for (var i = 0; i < 0x4000; i++)
_rom[i] = 0xFF;
if (newAddresses[0] == 0x8000)
{
Array.Copy(newData[0], rom, Math.Min(newData[0].Length, 0x4000));
validCartridge = true;
}
}
if (newAddresses[0] != 0x8000)
{
return;
}
public override byte Peek8000(int addr)
{
return rom[addr];
}
Array.Copy(newData[0], _rom, Math.Min(newData[0].Length, 0x4000));
validCartridge = true;
}
public override byte PeekA000(int addr)
{
return rom[addr | 0x2000];
}
public override int Peek8000(int addr)
{
return _rom[addr];
}
public override byte Read8000(int addr)
{
return rom[addr];
}
public override int PeekA000(int addr)
{
return _rom[addr | 0x2000];
}
public override byte ReadA000(int addr)
{
return rom[addr | 0x2000];
}
public override int Read8000(int addr)
{
return _rom[addr];
}
public override int ReadA000(int addr)
{
return _rom[addr | 0x2000];
}
public override int ReadDF00(int addr)
{
pinGame = true;
return base.ReadDF00(addr);
}
}
}
public override byte ReadDF00(int addr)
{
pinGame = true;
return base.ReadDF00(addr);
}
}
}

View File

@ -1,130 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
// This is a mapper used commonly by System 3. It is
// also utilized by the short-lived C64 Game System.
// This is a mapper used commonly by System 3. It is
// also utilized by the short-lived C64 Game System.
// Bank select is DExx. You select them by writing to the
// register DE00+BankNr. For example, bank 01 is a write
// to DE01.
// Bank select is DExx. You select them by writing to the
// register DE00+BankNr. For example, bank 01 is a write
// to DE01.
public class Mapper000F : Cart
{
private byte[][] banks = new byte[0][]; //8000
private int bankMask;
private int bankNumber;
private byte[] currentBank;
private byte[] dummyBank;
public abstract partial class CartridgeDevice
{
private class Mapper000F : CartridgeDevice
{
private readonly int[][] _banks; //8000
private readonly int _bankMask;
private int _bankNumber;
private int[] _currentBank;
public Mapper000F(List<int> newAddresses, List<int> newBanks, List<byte[]> newData)
{
int count = newAddresses.Count;
public Mapper000F(IList<int> newAddresses, IList<int> newBanks, IList<int[]> newData)
{
var count = newAddresses.Count;
pinGame = true;
pinExRom = false;
pinGame = true;
pinExRom = false;
// build dummy bank
dummyBank = new byte[0x2000];
for (int i = 0; i < 0x2000; i++)
dummyBank[i] = 0xFF; // todo: determine if this is correct
// build dummy bank
var dummyBank = new int[0x2000];
for (var i = 0; i < 0x2000; i++)
dummyBank[i] = 0xFF; // todo: determine if this is correct
if (count == 64) //512k
{
bankMask = 0x3F;
banks = new byte[64][];
}
else if (count == 32) //256k
{
bankMask = 0x1F;
banks = new byte[32][];
}
else if (count == 16) //128k
{
bankMask = 0x0F;
banks = new byte[16][];
}
else if (count == 8) //64k
{
bankMask = 0x07;
banks = new byte[8][];
}
else if (count == 4) //32k
{
bankMask = 0x03;
banks = new byte[4][];
}
else if (count == 2) //16k
{
bankMask = 0x01;
banks = new byte[2][];
}
else if (count == 1) //8k
{
bankMask = 0x00;
banks = new byte[1][];
}
else
{
// we don't know what format this is...
throw new Exception("This looks like a System 3/C64GS cartridge but cannot be loaded...");
}
switch (count)
{
case 64:
_bankMask = 0x3F;
_banks = new int[64][];
break;
case 32:
_bankMask = 0x1F;
_banks = new int[32][];
break;
case 16:
_bankMask = 0x0F;
_banks = new int[16][];
break;
case 8:
_bankMask = 0x07;
_banks = new int[8][];
break;
case 4:
_bankMask = 0x03;
_banks = new int[4][];
break;
case 2:
_bankMask = 0x01;
_banks = new int[2][];
break;
case 1:
_bankMask = 0x00;
_banks = new int[1][];
break;
default:
throw new Exception("This looks like a System 3/C64GS cartridge but cannot be loaded...");
}
// for safety, initialize all banks to dummy
for (int i = 0; i < banks.Length; i++)
banks[i] = dummyBank;
// for safety, initialize all banks to dummy
for (var i = 0; i < _banks.Length; i++)
_banks[i] = dummyBank;
// now load in the banks
for (int i = 0; i < count; i++)
{
if (newAddresses[i] == 0x8000)
{
banks[newBanks[i] & bankMask] = newData[i];
}
}
// now load in the banks
for (var i = 0; i < count; i++)
{
if (newAddresses[i] == 0x8000)
{
_banks[newBanks[i] & _bankMask] = newData[i];
}
}
BankSet(0);
}
BankSet(0);
}
protected void BankSet(int index)
{
bankNumber = index & bankMask;
UpdateState();
}
protected void BankSet(int index)
{
_bankNumber = index & _bankMask;
UpdateState();
}
public override byte Peek8000(int addr)
{
return currentBank[addr];
}
public override int Peek8000(int addr)
{
return _currentBank[addr];
}
public override void PokeDE00(int addr, byte val)
{
BankSet(addr);
}
public override void PokeDE00(int addr, int val)
{
BankSet(addr);
}
public override byte Read8000(int addr)
{
return currentBank[addr];
}
public override int Read8000(int addr)
{
return _currentBank[addr];
}
private void UpdateState()
{
currentBank = banks[bankNumber];
}
private void UpdateState()
{
_currentBank = _banks[_bankNumber];
}
public override void WriteDE00(int addr, byte val)
{
BankSet(addr);
}
public override void WriteDE00(int addr, int val)
{
BankSet(addr);
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
if (ser.IsReader)
BankSet(bankNumber);
}
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
if (ser.IsReader)
BankSet(_bankNumber);
}
}
}
}

View File

@ -1,34 +1,37 @@
using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
// This mapper comes from Dinamic. It is in fact identical
// to the System 3 mapper (000F) except that bank switching is
// done by reads to the DExx region instead of writes.
// This is why mapper 0011 inherits directly from 000F.
// This mapper comes from Dinamic. It is in fact identical
// to the System 3 mapper (000F) except that bank switching is
// done by reads to the DExx region instead of writes.
// This is why mapper 0011 inherits directly from 000F.
public class Mapper0011 : Mapper000F
{
public Mapper0011(List<int> newAddresses, List<int> newBanks, List<byte[]> newData)
: base(newAddresses, newBanks, newData)
{
// required to pass information to base class
}
public abstract partial class CartridgeDevice
{
private class Mapper0011 : Mapper000F
{
public Mapper0011(IList<int> newAddresses, IList<int> newBanks, IList<int[]> newData)
: base(newAddresses, newBanks, newData)
{
// required to pass information to base class
}
public override void PokeDE00(int addr, byte val)
{
// do nothing
}
public override void PokeDE00(int addr, int val)
{
// do nothing
}
public override byte ReadDE00(int addr)
{
BankSet(addr);
return base.ReadDE00(addr);
}
public override int ReadDE00(int addr)
{
BankSet(addr);
return base.ReadDE00(addr);
}
public override void WriteDE00(int addr, byte val)
{
// do nothing
}
}
public override void WriteDE00(int addr, int val)
{
// do nothing
}
}
}
}

View File

@ -1,80 +1,82 @@
using System;
using System.Collections.Generic;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
sealed public class Mapper0012 : Cart
{
private byte[] bankMain;
private byte[][] bankHigh;
private byte[] bankHighSelected;
private int bankIndex;
private byte[] dummyBank;
public abstract partial class CartridgeDevice
{
private sealed class Mapper0012 : CartridgeDevice
{
private readonly int[] _bankMain;
private readonly int[][] _bankHigh;
private int[] _bankHighSelected;
private int _bankIndex;
// Zaxxon and Super Zaxxon cartridges
// - read to 8xxx selects bank 0 in A000-BFFF
// - read to 9xxx selects bank 1 in A000-BFFF
// Zaxxon and Super Zaxxon cartridges
// - read to 8xxx selects bank 0 in A000-BFFF
// - read to 9xxx selects bank 1 in A000-BFFF
public Mapper0012(List<int> newAddresses, List<int> newBanks, List<byte[]> newData)
{
bankMain = new byte[0x2000];
bankHigh = new byte[2][];
dummyBank = new byte[0x2000];
public Mapper0012(IList<int> newAddresses, IList<int> newBanks, IList<int[]> newData)
{
_bankMain = new int[0x2000];
_bankHigh = new int[2][];
var dummyBank = new int[0x2000];
// create dummy bank just in case
for (int i = 0; i < 0x2000; i++)
dummyBank[i] = 0xFF;
// create dummy bank just in case
for (var i = 0; i < 0x2000; i++)
dummyBank[i] = 0xFF;
bankHigh[0] = dummyBank;
bankHigh[1] = dummyBank;
_bankHigh[0] = dummyBank;
_bankHigh[1] = dummyBank;
// load in the banks
for (int i = 0; i < newAddresses.Count; i++)
{
if (newAddresses[i] == 0x8000)
Array.Copy(newData[i], bankMain, 0x1000);
else if ((newAddresses[i] == 0xA000 || newAddresses[i] == 0xE000) && newBanks[i] < 2)
bankHigh[newBanks[i]] = newData[i];
}
// load in the banks
for (var i = 0; i < newAddresses.Count; i++)
{
if (newAddresses[i] == 0x8000)
Array.Copy(newData[i], _bankMain, 0x1000);
else if ((newAddresses[i] == 0xA000 || newAddresses[i] == 0xE000) && newBanks[i] < 2)
_bankHigh[newBanks[i]] = newData[i];
}
// mirror the main rom from 8000 to 9000
Array.Copy(bankMain, 0x0000, bankMain, 0x1000, 0x1000);
// mirror the main rom from 8000 to 9000
Array.Copy(_bankMain, 0x0000, _bankMain, 0x1000, 0x1000);
// set both pins low for 16k rom config
pinExRom = false;
pinGame = false;
// set both pins low for 16k rom config
pinExRom = false;
pinGame = false;
}
}
public override byte Peek8000(int addr)
{
return bankMain[addr];
}
public override int Peek8000(int addr)
{
return _bankMain[addr];
}
public override byte PeekA000(int addr)
{
return bankHighSelected[addr];
}
public override int PeekA000(int addr)
{
return _bankHighSelected[addr];
}
public override byte Read8000(int addr)
{
bankIndex = (addr & 0x1000) >> 12;
bankHighSelected = bankHigh[bankIndex];
return bankMain[addr];
}
public override int Read8000(int addr)
{
_bankIndex = (addr & 0x1000) >> 12;
_bankHighSelected = _bankHigh[_bankIndex];
return _bankMain[addr];
}
public override byte ReadA000(int addr)
{
return bankHighSelected[addr];
}
public override int ReadA000(int addr)
{
return _bankHighSelected[addr];
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
if (ser.IsReader)
bankHighSelected = bankHigh[bankIndex];
}
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
if (ser.IsReader)
_bankHighSelected = _bankHigh[_bankIndex];
}
}
}
}

View File

@ -2,124 +2,123 @@
using System.Collections.Generic;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
// Mapper for a few Domark and HES Australia games.
// It seems a lot of people dumping these have remapped
// them to the Ocean mapper (0005) but this is still here
// for compatibility.
//
// Bank select is DE00, bit 7 enabled means to disable
// ROM in 8000-9FFF.
// Mapper for a few Domark and HES Australia games.
// It seems a lot of people dumping these have remapped
// them to the Ocean mapper (0005) but this is still here
// for compatibility.
//
// Bank select is DE00, bit 7 enabled means to disable
// ROM in 8000-9FFF.
sealed public class Mapper0013 : Cart
{
private byte[][] banks = new byte[0][]; //8000
private int bankMask;
private int bankNumber;
private byte[] currentBank;
private byte[] dummyBank;
private bool romEnable;
public abstract partial class CartridgeDevice
{
private sealed class Mapper0013 : CartridgeDevice
{
private readonly int[][] _banks; //8000
private readonly int _bankMask;
private int _bankNumber;
private int[] _currentBank;
private bool _romEnable;
public Mapper0013(List<int> newAddresses, List<int> newBanks, List<byte[]> newData)
{
int count = newAddresses.Count;
public Mapper0013(IList<int> newAddresses, IList<int> newBanks, IList<int[]> newData)
{
var count = newAddresses.Count;
pinGame = true;
pinExRom = false;
romEnable = true;
pinGame = true;
pinExRom = false;
_romEnable = true;
// build dummy bank
dummyBank = new byte[0x2000];
for (int i = 0; i < 0x2000; i++)
dummyBank[i] = 0xFF; // todo: determine if this is correct
// build dummy bank
var dummyBank = new int[0x2000];
for (var i = 0; i < 0x2000; i++)
dummyBank[i] = 0xFF; // todo: determine if this is correct
if (count == 16) //128k
{
bankMask = 0x0F;
banks = new byte[16][];
}
else if (count == 8) //64k
{
bankMask = 0x07;
banks = new byte[8][];
}
else if (count == 4) //32k
{
bankMask = 0x03;
banks = new byte[4][];
}
else
{
// we don't know what format this is...
throw new Exception("This looks like a Domark/HES cartridge but cannot be loaded...");
}
switch (count)
{
case 16:
_bankMask = 0x0F;
_banks = new int[16][];
break;
case 8:
_bankMask = 0x07;
_banks = new int[8][];
break;
case 4:
_bankMask = 0x03;
_banks = new int[4][];
break;
default:
throw new Exception("This looks like a Domark/HES cartridge but cannot be loaded...");
}
// for safety, initialize all banks to dummy
for (int i = 0; i < banks.Length; i++)
banks[i] = dummyBank;
// for safety, initialize all banks to dummy
for (var i = 0; i < _banks.Length; i++)
_banks[i] = dummyBank;
// now load in the banks
for (int i = 0; i < count; i++)
{
if (newAddresses[i] == 0x8000)
{
banks[newBanks[i] & bankMask] = newData[i];
}
}
// now load in the banks
for (var i = 0; i < count; i++)
{
if (newAddresses[i] == 0x8000)
{
_banks[newBanks[i] & _bankMask] = newData[i];
}
}
BankSet(0);
}
BankSet(0);
}
private void BankSet(int index)
{
bankNumber = index & bankMask;
romEnable = ((index & 0x80) == 0);
UpdateState();
}
private void BankSet(int index)
{
_bankNumber = index & _bankMask;
_romEnable = (index & 0x80) == 0;
UpdateState();
}
public override byte Peek8000(int addr)
{
return currentBank[addr];
}
public override int Peek8000(int addr)
{
return _currentBank[addr];
}
public override void PokeDE00(int addr, byte val)
{
if (addr == 0x00)
BankSet(val);
}
public override void PokeDE00(int addr, int val)
{
if (addr == 0x00)
BankSet(val);
}
public override byte Read8000(int addr)
{
return currentBank[addr];
}
public override int Read8000(int addr)
{
return _currentBank[addr];
}
private void UpdateState()
{
currentBank = banks[bankNumber];
if (romEnable)
{
pinExRom = false;
pinGame = true;
}
else
{
pinExRom = true;
pinGame = true;
}
}
private void UpdateState()
{
_currentBank = _banks[_bankNumber];
if (_romEnable)
{
pinExRom = false;
pinGame = true;
}
else
{
pinExRom = true;
pinGame = true;
}
}
public override void WriteDE00(int addr, byte val)
{
if (addr == 0x00)
BankSet(val);
}
public override void WriteDE00(int addr, int val)
{
if (addr == 0x00)
BankSet(val);
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
if (ser.IsReader)
BankSet(bankNumber | (romEnable ? 0x00 : 0x80));
}
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
if (ser.IsReader)
BankSet(_bankNumber | (_romEnable ? 0x00 : 0x80));
}
}
}
}

View File

@ -1,290 +1,270 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
// EasyFlash cartridge
// No official games came on one of these but there
// are a few dumps from GameBase64 that use this mapper
// EasyFlash cartridge
// No official games came on one of these but there
// are a few dumps from GameBase64 that use this mapper
// There are 64 banks total, DE00 is bank select.
// Selecing a bank will select both Lo and Hi ROM.
// DE02 will switch exrom/game bits: bit 0=game,
// bit 1=exrom, bit 2=for our cases, always set true.
// These two registers are write only.
// There are 64 banks total, DE00 is bank select.
// Selecing a bank will select both Lo and Hi ROM.
// DE02 will switch exrom/game bits: bit 0=game,
// bit 1=exrom, bit 2=for our cases, always set true.
// These two registers are write only.
// This cartridge always starts up in Ultimax mode,
// with Game set high and ExRom set low.
// This cartridge always starts up in Ultimax mode,
// with Game set high and ExRom set low.
// There is also 256 bytes RAM at DF00-DFFF.
// There is also 256 bytes RAM at DF00-DFFF.
// We emulate having the AM29F040 chip.
// We emulate having the AM29F040 chip.
sealed public class Mapper0020 : Cart
{
private byte[][] banksA = new byte[64][]; //8000
private byte[][] banksB = new byte[64][]; //A000
private int bankNumber;
private bool boardLed;
private byte[] currentBankA;
private byte[] currentBankB;
private byte[] dummyBank;
private bool jumper = false;
private int stateBits;
private byte[] ram = new byte[256];
private bool commandLatch55;
private bool commandLatchAA;
private int internalRomState;
public Mapper0020(List<int> newAddresses, List<int> newBanks, List<byte[]> newData)
{
int count = newAddresses.Count;
// build dummy bank
dummyBank = new byte[0x2000];
for (int i = 0; i < 0x2000; i++)
dummyBank[i] = 0xFF; // todo: determine if this is correct
// force ultimax mode (the cart SHOULD set this
// otherwise on load, according to the docs)
pinGame = false;
pinExRom = true;
// for safety, initialize all banks to dummy
for (int i = 0; i < 64; i++)
banksA[i] = dummyBank;
for (int i = 0; i < 64; i++)
banksB[i] = dummyBank;
// load in all banks
for (int i = 0; i < count; i++)
{
if (newAddresses[i] == 0x8000)
{
banksA[newBanks[i]] = newData[i];
}
else if (newAddresses[i] == 0xA000 || newAddresses[i] == 0xE000)
{
banksB[newBanks[i]] = newData[i];
}
}
// default to bank 0
BankSet(0);
// internal operation settings
commandLatch55 = false;
commandLatchAA = false;
internalRomState = 0;
}
private void BankSet(int index)
{
bankNumber = index & 0x3F;
UpdateState();
}
public override byte Peek8000(int addr)
{
return currentBankA[addr];
}
public override byte PeekA000(int addr)
{
return currentBankB[addr];
}
public override byte PeekDE00(int addr)
{
// normally you can't read these regs
// but Peek is provided here for debug reasons
// and may not stay around
addr &= 0x02;
if (addr == 0x00)
return (byte)bankNumber;
else
return (byte)stateBits;
}
public override byte PeekDF00(int addr)
{
return ram[addr];
}
public override void PokeDE00(int addr, byte val)
{
addr &= 0x02;
if (addr == 0x00)
BankSet(val);
else
StateSet(val);
}
public override void PokeDF00(int addr, byte val)
{
ram[addr] = val;
}
public override byte Read8000(int addr)
{
return ReadInternal(addr);
}
public override byte ReadA000(int addr)
{
return ReadInternal(addr | 0x2000);
}
public override byte ReadDF00(int addr)
{
return ram[addr];
}
private byte ReadInternal(int addr)
{
switch (internalRomState)
{
case 0x80:
break;
case 0x90:
switch (addr & 0x1FFF)
{
case 0x0000:
return 0x01;
case 0x0001:
return 0xA4;
case 0x0002:
return 0x00;
}
break;
case 0xA0:
break;
case 0xF0:
break;
}
if ((addr & 0x3FFF) < 0x2000)
{
return currentBankA[addr & 0x1FFF];
}
else
{
return currentBankB[addr & 0x1FFF];
}
}
private void StateSet(byte val)
{
stateBits = val &= 0x87;
if ((val & 0x04) != 0)
pinGame = ((val & 0x01) == 0);
else
pinGame = jumper;
pinExRom = ((val & 0x02) == 0);
boardLed = ((val & 0x80) != 0);
internalRomState = 0;
UpdateState();
}
private void UpdateState()
{
currentBankA = banksA[bankNumber];
currentBankB = banksB[bankNumber];
}
public override void Write8000(int addr, byte val)
public abstract partial class CartridgeDevice
{
private sealed class Mapper0020 : CartridgeDevice
{
WriteInternal(addr, val);
private int _bankOffset = 63 << 13;
private readonly int[] _banksA = new int[64 << 13]; //8000
private readonly int[] _banksB = new int[64 << 13]; //A000
private bool _boardLed;
private bool _jumper = false;
private int _stateBits;
private readonly int[] _ram = new int[256];
private bool _commandLatch55;
private bool _commandLatchAa;
private int _internalRomState;
public Mapper0020(IList<int> newAddresses, IList<int> newBanks, IList<int[]> newData)
{
var count = newAddresses.Count;
// force ultimax mode (the cart SHOULD set this
// otherwise on load, according to the docs)
pinGame = false;
pinExRom = true;
// for safety, initialize all banks to dummy
for (var i = 0; i < 64*0x2000; i++)
{
_banksA[i] = 0xFF;
_banksB[i] = 0xFF;
}
// load in all banks
for (var i = 0; i < count; i++)
{
switch (newAddresses[i])
{
case 0x8000:
Array.Copy(newData[i], 0, _banksA, newBanks[i] * 0x2000, 0x2000);
break;
case 0xA000:
case 0xE000:
Array.Copy(newData[i], 0, _banksB, newBanks[i] * 0x2000, 0x2000);
break;
}
}
// default to bank 0
BankSet(0);
// internal operation settings
_commandLatch55 = false;
_commandLatchAa = false;
_internalRomState = 0;
}
private void BankSet(int index)
{
_bankOffset = (index & 0x3F) << 13;
}
public override int Peek8000(int addr)
{
addr &= 0x1FFF;
return _banksA[addr | _bankOffset];
}
public override int PeekA000(int addr)
{
addr &= 0x1FFF;
return _banksB[addr | _bankOffset];
}
public override int PeekDE00(int addr)
{
// normally you can't read these regs
// but Peek is provided here for debug reasons
// and may not stay around
addr &= 0x02;
return addr == 0x00 ? _bankOffset >> 13 : _stateBits;
}
public override int PeekDF00(int addr)
{
addr &= 0xFF;
return _ram[addr];
}
public override void PokeDE00(int addr, int val)
{
addr &= 0x02;
if (addr == 0x00)
BankSet(val);
else
StateSet(val);
}
public override void PokeDF00(int addr, int val)
{
addr &= 0xFF;
_ram[addr] = val & 0xFF;
}
public override int Read8000(int addr)
{
return ReadInternal(addr & 0x1FFF, _banksA);
}
public override int ReadA000(int addr)
{
return ReadInternal(addr & 0x1FFF, _banksB);
}
public override int ReadDF00(int addr)
{
addr &= 0xFF;
return _ram[addr];
}
private int ReadInternal(int addr, int[] bank)
{
switch (_internalRomState)
{
case 0x80:
break;
case 0x90:
switch (addr & 0x1FFF)
{
case 0x0000:
return 0x01;
case 0x0001:
return 0xA4;
case 0x0002:
return 0x00;
}
break;
case 0xA0:
break;
case 0xF0:
break;
}
return bank[addr | _bankOffset];
}
private void StateSet(int val)
{
_stateBits = val &= 0x87;
if ((val & 0x04) != 0)
pinGame = (val & 0x01) == 0;
else
pinGame = _jumper;
pinExRom = (val & 0x02) == 0;
_boardLed = (val & 0x80) != 0;
_internalRomState = 0;
}
public override void Write8000(int addr, int val)
{
WriteInternal(addr, val);
}
public override void WriteA000(int addr, int val)
{
WriteInternal(addr | 0x2000, val);
}
private void WriteInternal(int addr, int val)
{
if (pinGame || !pinExRom)
{
return;
}
if (val == 0xF0) // any address, resets flash
{
_internalRomState = 0;
_commandLatch55 = false;
_commandLatchAa = false;
}
else if (_internalRomState != 0x00 && _internalRomState != 0xF0)
{
switch (_internalRomState)
{
case 0xA0:
if ((addr & 0x2000) == 0)
{
addr &= 0x1FFF;
_banksA[addr | _bankOffset] = val & 0xFF;
}
else
{
addr &= 0x1FFF;
_banksB[addr | _bankOffset] = val & 0xFF;
}
break;
}
}
else if (addr == 0x0555) // $8555
{
if (!_commandLatchAa)
{
if (val == 0xAA)
{
_commandLatch55 = true;
}
}
else
{
// process EZF command
_internalRomState = val;
}
}
else if (addr == 0x02AA) // $82AA
{
if (_commandLatch55 && val == 0x55)
{
_commandLatchAa = true;
}
else
{
_commandLatch55 = false;
}
}
else
{
_commandLatch55 = false;
_commandLatchAa = false;
}
}
public override void WriteDE00(int addr, int val)
{
addr &= 0x02;
if (addr == 0x00)
BankSet(val);
else
StateSet(val);
}
public override void WriteDF00(int addr, int val)
{
_ram[addr] = val & 0xFF;
}
}
public override void WriteA000(int addr, byte val)
{
WriteInternal(addr | 0x2000, val);
}
private void WriteInternal(int addr, byte val)
{
if (!pinGame && pinExRom)
{
System.Diagnostics.Debug.WriteLine("EasyFlash Write: $" + C64Util.ToHex(addr | 0x8000, 4) + " = " + C64Util.ToHex(val, 2));
if (val == 0xF0) // any address, resets flash
{
internalRomState = 0;
commandLatch55 = false;
commandLatchAA = false;
}
else if (internalRomState != 0x00 && internalRomState != 0xF0)
{
switch (internalRomState)
{
case 0xA0:
if ((addr & 0x2000) == 0)
{
addr &= 0x1FFF;
banksA[bankNumber][addr] = val;
currentBankA[addr] = val;
}
else
{
addr &= 0x1FFF;
banksB[bankNumber][addr] = val;
currentBankB[addr] = val;
}
break;
}
}
else if (addr == 0x0555) // $8555
{
if (!commandLatchAA)
{
if (val == 0xAA)
{
commandLatch55 = true;
}
}
else
{
// process EZF command
internalRomState = val;
}
}
else if (addr == 0x02AA) // $82AA
{
if (commandLatch55 && val == 0x55)
{
commandLatchAA = true;
}
else
{
commandLatch55 = false;
}
}
else
{
commandLatch55 = false;
commandLatchAA = false;
}
}
}
public override void WriteDE00(int addr, byte val)
{
addr &= 0x02;
if (addr == 0x00)
BankSet(val);
else
StateSet(val);
}
public override void WriteDF00(int addr, byte val)
{
ram[addr] = val;
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
if (ser.IsReader)
UpdateState();
}
}
}
}

View File

@ -0,0 +1,57 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cassette
{
public sealed class CassettePort
{
public Func<bool> ReadDataOutput = () => true;
public Func<bool> ReadMotor = () => true;
private CassettePortDevice _device;
private bool _connected;
public void HardReset()
{
if (_connected)
{
_device.HardReset();
}
}
public void ExecutePhase()
{
if (_connected)
{
_device.ExecutePhase2();
}
}
public bool ReadDataInputBuffer()
{
return !_connected || _device.ReadDataInputBuffer();
}
public bool ReadSenseBuffer()
{
return !_connected || _device.ReadSenseBuffer();
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
public void Connect(CassettePortDevice device)
{
_connected = device != null;
_device = device;
if (_device == null)
{
return;
}
_device.ReadDataOutput = () => ReadDataOutput();
_device.ReadMotor = () => ReadMotor();
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cassette
{
public abstract class CassettePortDevice
{
public Func<bool> ReadDataOutput;
public Func<bool> ReadMotor;
public virtual void ExecutePhase2()
{
}
public virtual void HardReset()
{
}
public virtual bool ReadDataInputBuffer()
{
return true;
}
public virtual bool ReadSenseBuffer()
{
return true;
}
public virtual void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -0,0 +1,40 @@
using BizHawk.Common;
using BizHawk.Emulation.Cores.Computers.Commodore64.Media;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cassette
{
public class TapeDrive : CassettePortDevice
{
private Tape _tape;
public override void ExecutePhase2()
{
if (_tape != null && !ReadMotor()) _tape.ExecuteCycle();
}
public override void HardReset()
{
if (_tape != null) _tape.Rewind();
}
public override bool ReadDataInputBuffer()
{
return _tape == null || _tape.Read();
}
public override bool ReadSenseBuffer()
{
return _tape == null;
}
public override void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
public void Insert(Tape tape)
{
_tape = tape;
}
}
}

View File

@ -1,41 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.CassettePort
{
public class CassettePortDevice
{
public Func<bool> ReadDataOutput;
public Func<bool> ReadMotor;
Commodore64.CassettePort.Tape tape;
public void HardReset()
{
if (tape != null) tape.rewind();
}
virtual public bool ReadDataInputBuffer()
{
return tape != null && !ReadMotor() ? tape.read() : true;
}
virtual public bool ReadSenseBuffer()
{
return tape == null; // Just assume that "play" is constantly pressed as long as a tape is inserted
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
internal void Connect(Tape tape)
{
this.tape = tape;
}
}
}

View File

@ -1,101 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.CassettePort
{
/**
* This class represents a tape. Only TAP-style tapes are supported for now.
*/
class Tape
{
private readonly byte[] tapeData;
private readonly byte version;
private uint pos, cycle;
private readonly uint start, end;
public Tape(byte version, byte[] tapeData, uint start, uint end)
{
this.version = version;
this.tapeData = tapeData;
this.start = start;
this.end = end;
rewind();
}
// Rewinds the tape back to start
public void rewind()
{
pos = start;
cycle = 0;
}
// Reads from tape, this will tell the caller if the flag pin should be raised
public bool read()
{
if (cycle == 0)
{
if (pos >= end)
{
return true;
}
else
{
cycle = ((uint)tapeData[pos++])*8;
if (cycle == 0)
{
if (version == 0)
{
cycle = 256 * 8; // unspecified overflow condition
}
else
{
cycle = BitConverter.ToUInt32(tapeData, (int)pos-1)>>8;
pos += 3;
if (cycle == 0)
{
throw new Exception("Bad tape data");
}
}
}
}
}
// Send a single negative pulse at the end of a cycle
return --cycle != 0;
}
// Try to construct a tape file from file data. Returns null if not a tape file, throws exceptions for bad tape files.
// (Note that some error conditions aren't caught right here.)
static public Tape Load(byte[] tapeFile)
{
Tape result = null;
if (System.Text.Encoding.ASCII.GetString(tapeFile, 0, 12) == "C64-TAPE-RAW")
{
byte version = tapeFile[12];
if (version > 1) throw new Exception("This tape has an unsupported version");
uint size = BitConverter.ToUInt32(tapeFile, 16);
if (size + 20 != tapeFile.Length)
{
throw new Exception("Tape file header specifies a length that doesn't match the file size");
}
result = new Tape(version, tapeFile, 20, (uint)tapeFile.Length);
}
return result;
}
public void SyncState(Serializer ser)
{
ser.BeginSection("tape");
ser.Sync("pos", ref pos);
ser.Sync("cycle", ref cycle);
ser.EndSection();
}
}
}

View File

@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public struct InputFileInfo
{

View File

@ -1,86 +0,0 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public class CartridgePort
{
public Func<bool> ReadIRQ;
public Func<bool> ReadNMI;
Cart cart;
bool connected;
public CartridgePort()
{
// start up with no media connected
Disconnect();
}
// ------------------------------------------
public byte PeekHiExp(int addr) { if (connected) { return cart.PeekDF00(addr & 0x00FF); } else { return 0xFF; } }
public byte PeekHiRom(int addr) { if (connected) { return cart.PeekA000(addr & 0x1FFF); } else { return 0xFF; } }
public byte PeekLoExp(int addr) { if (connected) { return cart.PeekDE00(addr & 0x00FF); } else { return 0xFF; } }
public byte PeekLoRom(int addr) { if (connected) { return cart.Peek8000(addr & 0x1FFF); } else { return 0xFF; } }
public void PokeHiExp(int addr, byte val) { if (connected) { cart.PokeDF00(addr & 0x00FF, val); } }
public void PokeHiRom(int addr, byte val) { if (connected) { cart.PokeA000(addr & 0x1FFF, val); } }
public void PokeLoExp(int addr, byte val) { if (connected) { cart.PokeDE00(addr & 0x00FF, val); } }
public void PokeLoRom(int addr, byte val) { if (connected) { cart.Poke8000(addr & 0x1FFF, val); } }
public bool ReadExRom() { if (connected) { return cart.ExRom; } else { return true; } }
public bool ReadGame() { if (connected) { return cart.Game; } else { return true; } }
public byte ReadHiExp(int addr) { if (connected) { return cart.ReadDF00((addr & 0x00FF)); } else { return 0xFF; } }
public byte ReadHiRom(int addr) { if (connected) { return cart.ReadA000((addr & 0x1FFF)); } else { return 0xFF; } }
public byte ReadLoExp(int addr) { if (connected) { return cart.ReadDE00((addr & 0x00FF)); } else { return 0xFF; } }
public byte ReadLoRom(int addr) { if (connected) { return cart.Read8000((addr & 0x1FFF)); } else { return 0xFF; } }
public void WriteHiExp(int addr, byte val) { if (connected) { cart.WriteDF00((addr & 0x00FF), val); } }
public void WriteHiRom(int addr, byte val) { if (connected) { cart.WriteA000((addr & 0x1FFF), val); } }
public void WriteLoExp(int addr, byte val) { if (connected) { cart.WriteDE00((addr & 0x00FF), val); } }
public void WriteLoRom(int addr, byte val) { if (connected) { cart.Write8000((addr & 0x1FFF), val); } }
// ------------------------------------------
public void Connect(Cart newCart)
{
cart = newCart;
connected = true;
}
public void Disconnect()
{
cart = null;
connected = false;
}
public void HardReset()
{
// note: this will not disconnect any attached media
}
public bool IsConnected
{
get
{
return connected;
}
}
public bool ReadIRQBuffer()
{
return true;
}
public bool ReadNMIBuffer()
{
return true;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -4,9 +4,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// used as Color RAM in C64
sealed public class Chip2114
public sealed class Chip2114
{
byte[] ram;
private int[] _ram = new int[0x400];
public Chip2114()
{
@ -15,27 +15,30 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public void HardReset()
{
ram = new byte[0x400];
for (var i = 0; i < 0x400; i++)
{
_ram[i] = 0x0;
}
}
public byte Peek(int addr)
public int Peek(int addr)
{
return ram[addr & 0x3FF];
return _ram[addr & 0x3FF];
}
public void Poke(int addr, byte val)
public void Poke(int addr, int val)
{
ram[addr & 0x3FF] = (byte)(val & 0xF);
_ram[addr & 0x3FF] = val & 0xF;
}
public byte Read(int addr)
public int Read(int addr)
{
return ram[addr & 0x3FF];
return _ram[addr & 0x3FF];
}
public int ReadInt(int addr)
{
return ram[addr & 0x3FF];
return _ram[addr & 0x3FF];
}
public void SyncState(Serializer ser)
@ -43,9 +46,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
SaveState.SyncObject(ser, this);
}
public void Write(int addr, byte val)
public void Write(int addr, int val)
{
ram[addr & 0x3FF] = (byte)(val & 0xF);
_ram[addr & 0x3FF] = val & 0xF;
}
}
}

View File

@ -0,0 +1,45 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// ROM chips
public sealed class Chip23128
{
[SaveState.DoNotSave] private readonly int[] _rom;
public Chip23128()
{
_rom = new int[0x4000];
}
public Chip23128(byte[] data) : this()
{
Flash(data);
}
public void Flash(byte[] data)
{
// ensures ROM is mirrored
for (var i = 0; i < _rom.Length; i += data.Length)
{
Array.Copy(data, 0, _rom, i, data.Length);
}
}
public int Peek(int addr)
{
return _rom[addr & 0x3FFF];
}
public int Read(int addr)
{
return _rom[addr & 0x3FFF];
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -1,60 +0,0 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// ROM chips
// 2332: 32 kbit (4kbyte)
// 2364: 64 kbit (8kbyte)
// 23128: 128 kbit (16kbyte)
public enum Chip23XXmodel
{
Chip2332,
Chip2364,
Chip23128
}
sealed public class Chip23XX
{
int addrMask;
byte[] rom;
public Chip23XX(Chip23XXmodel model, byte[] data)
{
switch (model)
{
case Chip23XXmodel.Chip2332:
rom = new byte[0x1000];
addrMask = 0xFFF;
break;
case Chip23XXmodel.Chip2364:
rom = new byte[0x2000];
addrMask = 0x1FFF;
break;
case Chip23XXmodel.Chip23128:
rom = new byte[0x4000];
addrMask = 0x3FFF;
break;
default:
throw new Exception("Invalid chip model.");
}
Array.Copy(data, rom, rom.Length);
}
public byte Peek(int addr)
{
return rom[addr & addrMask];
}
public byte Read(int addr)
{
return rom[addr & addrMask];
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -13,46 +13,36 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
// memory is striped 00/FF at intervals of 0x40
sealed public class Chip4864
public sealed class Chip4864
{
byte[] ram;
private int[] _ram;
public Chip4864()
{
ram = new byte[0x10000];
_ram = new int[0x10000];
HardReset();
}
public void HardReset()
{
// stripe the ram
for (int i = 0; i < 10000; i++)
ram[i] = ((i & 0x40) != 0) ? (byte)0xFF : (byte)0x00;
for (var i = 0; i < 10000; i++)
_ram[i] = (i & 0x40) != 0 ? 0xFF : 0x00;
}
public byte Peek(long addr)
public int Peek(int addr)
{
return ram[addr];
return _ram[addr];
}
public void Poke(long addr, byte val)
public void Poke(int addr, int val)
{
ram[addr] = val;
_ram[addr] = val;
}
public byte Peek(int addr)
public int Read(int addr)
{
return ram[addr];
}
public void Poke(int addr, byte val)
{
ram[addr] = val;
}
public byte Read(int addr)
{
return ram[addr];
return _ram[addr];
}
public void SyncState(Serializer ser)
@ -60,9 +50,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
SaveState.SyncObject(ser, this);
}
public void Write(int addr, byte val)
public void Write(int addr, int val)
{
ram[addr] = val;
_ram[addr] = val;
}
}
}

View File

@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed partial class Chip6510 : IDebuggable
{
IDictionary<string, RegisterValue> IDebuggable.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 }
};
}
void IDebuggable.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;
}
}
bool IDebuggable.CanStep(StepType type)
{
switch (type)
{
case StepType.Into:
case StepType.Over:
case StepType.Out:
return DebuggerStep != null;
default:
return false;
}
}
void IDebuggable.Step(StepType type)
{
switch (type)
{
case StepType.Into:
StepInto();
break;
case StepType.Out:
StepOut();
break;
case StepType.Over:
StepOver();
break;
}
}
private void StepInto()
{
while (_cpu.AtInstructionStart())
{
DebuggerStep();
}
while (!_cpu.AtInstructionStart())
{
DebuggerStep();
}
}
private void StepOver()
{
var instruction = CpuPeek(_cpu.PC);
if (instruction == Jsr)
{
var destination = _cpu.PC + JsrSize;
while (_cpu.PC != destination)
{
StepInto();
}
}
else
{
StepInto();
}
}
private void StepOut()
{
var instructionsBeforeBailout = 1000000;
var instr = CpuPeek(_cpu.PC);
_jsrCount = instr == Jsr ? 1 : 0;
while (--instructionsBeforeBailout > 0)
{
StepInto();
instr = CpuPeek(_cpu.PC);
if (instr == Jsr)
{
_jsrCount++;
}
else if ((instr == Rts || instr == Rti) && _jsrCount <= 0)
{
StepInto();
_jsrCount = 0;
break;
}
else if (instr == Rts || instr == Rti)
{
_jsrCount--;
}
}
}
[SaveState.DoNotSave]
private int _jsrCount;
[SaveState.DoNotSave]
private const byte Jsr = 0x20;
[SaveState.DoNotSave]
private const byte Rti = 0x40;
[SaveState.DoNotSave]
private const byte Rts = 0x60;
[SaveState.DoNotSave]
private const byte JsrSize = 3;
[SaveState.DoNotSave] private IMemoryCallbackSystem _memoryCallbacks = new MemoryCallbackSystem();
[SaveState.DoNotSave]
IMemoryCallbackSystem IDebuggable.MemoryCallbacks { get; }
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed partial class Chip6510 : IDisassemblable
{
public IEnumerable<string> AvailableCpus
{
get { yield return "6510"; }
}
public string Cpu
{
get { return "6510"; }
set
{
}
}
public string PCRegisterName
{
get { return "PC"; }
}
public string Disassemble(MemoryDomain m, uint addr, out int length)
{
return Components.M6502.MOS6502X.Disassemble((ushort)addr, out length, CpuPeek);
}
}
}

View File

@ -0,0 +1,242 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.Emulation.Cores.Components.M6502;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// an extension of the 6502 processor
public sealed partial class Chip6510
{
// ------------------------------------
private MOS6502X _cpu;
private bool _pinNmiLast;
private LatchedPort _port;
private bool _thisNmi;
public Func<int, int> PeekMemory;
public Action<int, int> PokeMemory;
public Func<bool> ReadAec;
public Func<bool> ReadIrq;
public Func<bool> ReadNmi;
public Func<bool> ReadRdy;
public Func<int, int> ReadMemory;
public Func<int> ReadPort;
public Action<int, int> WriteMemory;
public Action<int, int> WriteMemoryPort;
public Action DebuggerStep;
// ------------------------------------
public Chip6510()
{
// configure cpu r/w
_cpu = new MOS6502X
{
DummyReadMemory = CpuRead,
ReadMemory = CpuRead,
WriteMemory = CpuWrite,
PeekMemory = CpuPeek
};
// perform hard reset
HardReset();
}
public void SetOverflow()
{
}
private byte CpuPeek(ushort addr)
{
return unchecked((byte)Peek(addr));
}
private byte CpuRead(ushort addr)
{
return unchecked((byte) Read(addr));
}
private void CpuWrite(ushort addr, byte val)
{
Write(addr, val);
}
public void HardReset()
{
_cpu.NESSoftReset();
_port = new LatchedPort
{
Direction = 0x00,
Latch = 0xFF
};
_pinNmiLast = true;
}
// ------------------------------------
public void ExecutePhase()
{
_cpu.RDY = ReadRdy();
if (ReadAec())
{
_cpu.IRQ = !ReadIrq();
_pinNmiLast = _thisNmi;
_thisNmi = ReadNmi();
_cpu.NMI |= _pinNmiLast && !_thisNmi;
_cpu.ExecuteOne();
}
else
{
LagCycles++;
}
}
public int LagCycles;
internal bool AtInstructionStart()
{
return _cpu.AtInstructionStart();
}
// ------------------------------------
[SaveState.DoNotSave]
public ushort Pc
{
get
{
return _cpu.PC;
}
set
{
_cpu.PC = value;
}
}
[SaveState.DoNotSave]
public int A
{
get { return _cpu.A; } set { _cpu.A = unchecked((byte)value); }
}
[SaveState.DoNotSave]
public int X
{
get { return _cpu.X; } set { _cpu.X = unchecked((byte)value); }
}
[SaveState.DoNotSave]
public int Y
{
get { return _cpu.Y; } set { _cpu.Y = unchecked((byte)value); }
}
[SaveState.DoNotSave]
public int S
{
get { return _cpu.S; } set { _cpu.S = unchecked((byte)value); }
}
[SaveState.DoNotSave]
public bool FlagC { get { return _cpu.FlagC; } }
[SaveState.DoNotSave]
public bool FlagZ { get { return _cpu.FlagZ; } }
[SaveState.DoNotSave]
public bool FlagI { get { return _cpu.FlagI; } }
[SaveState.DoNotSave]
public bool FlagD { get { return _cpu.FlagD; } }
[SaveState.DoNotSave]
public bool FlagB { get { return _cpu.FlagB; } }
[SaveState.DoNotSave]
public bool FlagV { get { return _cpu.FlagV; } }
[SaveState.DoNotSave]
public bool FlagN { get { return _cpu.FlagN; } }
[SaveState.DoNotSave]
public bool FlagT { get { return _cpu.FlagT; } }
public int Peek(int addr)
{
switch (addr)
{
case 0x0000:
return _port.Direction;
case 0x0001:
return PortData;
default:
return PeekMemory(addr);
}
}
public void Poke(int addr, int val)
{
switch (addr)
{
case 0x0000:
_port.Direction = val;
break;
case 0x0001:
_port.Latch = val;
break;
default:
PokeMemory(addr, val);
break;
}
}
[SaveState.DoNotSave]
public int PortData
{
get
{
return _port.ReadInput(ReadPort());
}
set
{
_port.Latch = value;
}
}
public int Read(int addr)
{
switch (addr)
{
case 0x0000:
return _port.Direction;
case 0x0001:
return PortData;
default:
return ReadMemory(addr);
}
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
public void Write(int addr, int val)
{
switch (addr)
{
case 0x0000:
_port.Direction = val;
WriteMemoryPort(addr, val);
break;
case 0x0001:
_port.Latch = val;
WriteMemoryPort(addr, val);
break;
default:
WriteMemory(addr, val);
break;
}
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// MOS technology 6526 "CIA"
//
// emulation notes:
// * CS, R/W and RS# pins are not emulated. (not needed)
// * A low RES pin is emulated via HardReset().
public static class Chip6526
{
public static Cia Create(C64.CiaType type, Func<int> readIec)
{
switch (type)
{
case C64.CiaType.Ntsc:
return new Cia(14318181, 14*60, readIec)
{
DelayedInterrupts = true
};
case C64.CiaType.NtscRevA:
return new Cia(14318181, 14 * 60, readIec)
{
DelayedInterrupts = false
};
case C64.CiaType.Pal:
return new Cia(17734472, 18 * 50, readIec)
{
DelayedInterrupts = true
};
case C64.CiaType.PalRevA:
return new Cia(17734472, 18 * 50, readIec)
{
DelayedInterrupts = false
};
default:
throw new Exception("Unrecognized CIA timer type.");
}
}
public static Cia Create(C64.CiaType type, int[] keyboard, int[] joysticks)
{
switch (type)
{
case C64.CiaType.Ntsc:
return new Cia(14318181, 14 * 60, keyboard, joysticks)
{
DelayedInterrupts = true
};
case C64.CiaType.NtscRevA:
return new Cia(14318181, 14 * 60, keyboard, joysticks)
{
DelayedInterrupts = false
};
case C64.CiaType.Pal:
return new Cia(17734472, 18 * 50, keyboard, joysticks)
{
DelayedInterrupts = true
};
case C64.CiaType.PalRevA:
return new Cia(17734472, 18 * 50, keyboard, joysticks)
{
DelayedInterrupts = false
};
default:
throw new Exception("Unrecognized CIA timer type.");
}
}
}
}

View File

@ -0,0 +1,40 @@
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// vic ntsc old
public static class Chip6567R56A
{
private static readonly int Cycles = 64;
private static readonly int ScanWidth = Cycles * 8;
private static readonly int Lines = 262;
private static readonly int Vblankstart = 0x00D % Lines;
private static readonly int VblankEnd = 0x018 % Lines;
private static readonly int HblankOffset = 24;
private static readonly int HblankStart = (0x18C + HblankOffset) % ScanWidth;
private static readonly int HblankEnd = (0x1F0 + HblankOffset) % ScanWidth;
private static readonly int[] Timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, ScanWidth, -1, -1);
private static readonly int[] Fetch = Vic.TimingBuilder_Fetch(Timing, 0x16C);
private static readonly int[] Ba = Vic.TimingBuilder_BA(Fetch);
private static readonly int[] Act = Vic.TimingBuilder_Act(Timing, 0x004, 0x154, 0x164);
private static readonly int[][] Pipeline = {
Timing,
Fetch,
Ba,
Act
};
public static Vic Create(C64.BorderType borderType)
{
return new Vic(
Cycles, Lines,
Pipeline,
14318181 / 14,
HblankStart, HblankEnd,
Vblankstart, VblankEnd,
borderType,
762, 1000
);
}
}
}

View File

@ -0,0 +1,40 @@
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// vic ntsc
public static class Chip6567R8
{
private static readonly int Cycles = 65;
private static readonly int ScanWidth = Cycles * 8;
private static readonly int Lines = 263;
private static readonly int VblankStart = 0x00D % Lines;
private static readonly int VblankEnd = 0x018 % Lines;
private static readonly int HblankOffset = 24;
private static readonly int HblankStart = (0x18C + HblankOffset) % ScanWidth;
private static readonly int HblankEnd = (0x1F0 + HblankOffset) % ScanWidth;
private static readonly int[] Timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, ScanWidth, 0x184, 8);
private static readonly int[] Fetch = Vic.TimingBuilder_Fetch(Timing, 0x174);
private static readonly int[] Ba = Vic.TimingBuilder_BA(Fetch);
private static readonly int[] Act = Vic.TimingBuilder_Act(Timing, 0x004, 0x154, 0x16C);
private static readonly int[][] Pipeline = {
Timing,
Fetch,
Ba,
Act
};
public static Vic Create(C64.BorderType borderType)
{
return new Vic(
Cycles, Lines,
Pipeline,
14318181 / 14,
HblankStart, HblankEnd,
VblankStart, VblankEnd,
borderType,
6136, 8182
);
}
}
}

View File

@ -0,0 +1,40 @@
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// vic pal
public static class Chip6569
{
private static readonly int Cycles = 63;
private static readonly int ScanWidth = Cycles * 8;
private static readonly int Lines = 312;
private static readonly int VblankStart = 0x120 % Lines;
private static readonly int VblankEnd = 0x00F % Lines;
private static readonly int HblankOffset = 24;
private static readonly int HblankStart = (0x178 + HblankOffset) % ScanWidth;
private static readonly int HblankEnd = (0x1F0 + HblankOffset) % ScanWidth;
private static readonly int[] Timing = Vic.TimingBuilder_XRaster(0x194, 0x1F8, ScanWidth, -1, -1);
private static readonly int[] Fetch = Vic.TimingBuilder_Fetch(Timing, 0x164);
private static readonly int[] Ba = Vic.TimingBuilder_BA(Fetch);
private static readonly int[] Act = Vic.TimingBuilder_Act(Timing, 0x004, 0x14C, 0x164);
private static readonly int[][] Pipeline = {
Timing,
Fetch,
Ba,
Act
};
public static Vic Create(C64.BorderType borderType)
{
return new Vic(
Cycles, Lines,
Pipeline,
17734472 / 18,
HblankStart, HblankEnd,
VblankStart, VblankEnd,
borderType,
7375, 7882
);
}
}
}

View File

@ -0,0 +1,40 @@
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// pal n / drean - TODO correct?
public static class Chip6572
{
private static readonly int Cycles = 65;
private static readonly int ScanWidth = Cycles * 8;
private static readonly int Lines = 312;
private static readonly int VblankStart = 0x12C % Lines;
private static readonly int VblankEnd = 0x00F % Lines;
private static readonly int HblankOffset = 24;
private static readonly int HblankStart = (0x18C + HblankOffset) % ScanWidth;
private static readonly int HblankEnd = (0x1F0 + HblankOffset) % ScanWidth;
private static readonly int[] Timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, ScanWidth, 0x18C, 8);
private static readonly int[] Fetch = Vic.TimingBuilder_Fetch(Timing, 0x174);
private static readonly int[] Ba = Vic.TimingBuilder_BA(Fetch);
private static readonly int[] Act = Vic.TimingBuilder_Act(Timing, 0x004, 0x154, 0x16C);
private static readonly int[][] Pipeline = {
Timing,
Fetch,
Ba,
Act
};
public static Vic Create(C64.BorderType borderType)
{
return new Vic(
Cycles, Lines,
Pipeline,
14328225 / 14,
HblankStart, HblankEnd,
VblankStart, VblankEnd,
borderType,
908, 1000
);
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,352 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// emulates the PLA
// which handles all bank switching
public sealed class Chip90611401
{
// ------------------------------------
public Func<int, int> PeekBasicRom;
public Func<int, int> PeekCartridgeLo;
public Func<int, int> PeekCartridgeHi;
public Func<int, int> PeekCharRom;
public Func<int, int> PeekCia0;
public Func<int, int> PeekCia1;
public Func<int, int> PeekColorRam;
public Func<int, int> PeekExpansionLo;
public Func<int, int> PeekExpansionHi;
public Func<int, int> PeekKernalRom;
public Func<int, int> PeekMemory;
public Func<int, int> PeekSid;
public Func<int, int> PeekVic;
public Action<int, int> PokeCartridgeLo;
public Action<int, int> PokeCartridgeHi;
public Action<int, int> PokeCia0;
public Action<int, int> PokeCia1;
public Action<int, int> PokeColorRam;
public Action<int, int> PokeExpansionLo;
public Action<int, int> PokeExpansionHi;
public Action<int, int> PokeMemory;
public Action<int, int> PokeSid;
public Action<int, int> PokeVic;
public Func<bool> ReadAec;
public Func<bool> ReadBa;
public Func<int, int> ReadBasicRom;
public Func<int, int> ReadCartridgeLo;
public Func<int, int> ReadCartridgeHi;
public Func<bool> ReadCharen;
public Func<int, int> ReadCharRom;
public Func<int, int> ReadCia0;
public Func<int, int> ReadCia1;
public Func<int, int> ReadColorRam;
public Func<int, int> ReadExpansionLo;
public Func<int, int> ReadExpansionHi;
public Func<bool> ReadExRom;
public Func<bool> ReadGame;
public Func<bool> ReadHiRam;
public Func<int, int> ReadKernalRom;
public Func<bool> ReadLoRam;
public Func<int, int> ReadMemory;
public Func<int, int> ReadSid;
public Func<int, int> ReadVic;
public Action<int, int> WriteCartridgeLo;
public Action<int, int> WriteCartridgeHi;
public Action<int, int> WriteCia0;
public Action<int, int> WriteCia1;
public Action<int, int> WriteColorRam;
public Action<int, int> WriteExpansionLo;
public Action<int, int> WriteExpansionHi;
public Action<int, int> WriteMemory;
public Action<int, int> WriteSid;
public Action<int, int> WriteVic;
// ------------------------------------
private enum PlaBank
{
None,
Ram,
BasicRom,
KernalRom,
CharRom,
CartridgeLo,
CartridgeHi,
Vic,
Sid,
ColorRam,
Cia0,
Cia1,
Expansion0,
Expansion1
}
// ------------------------------------
private bool _p24;
private bool _p25;
private bool _p26;
private bool _p27;
private bool _p28;
private bool _loram;
private bool _hiram;
private bool _game;
private bool _exrom;
private bool _charen;
private bool _a15;
private bool _a14;
private bool _a13;
private bool _a12;
private PlaBank Bank(int addr, bool read)
{
_loram = ReadLoRam();
_hiram = ReadHiRam();
_game = ReadGame();
_a15 = (addr & 0x08000) != 0;
_a14 = (addr & 0x04000) != 0;
_a13 = (addr & 0x02000) != 0;
_a12 = (addr & 0x01000) != 0;
// upper memory regions 8000-FFFF
_exrom = ReadExRom();
if (_a15)
{
// io/character access
if (_a14 && !_a13 && _a12)
{
// character rom, banked in at D000-DFFF
_charen = ReadCharen();
if (read && !_charen && (((_hiram || _loram) && _game) || (_hiram && !_exrom && !_game)))
return PlaBank.CharRom;
// io block, banked in at D000-DFFF
if ((_charen && (_hiram || _loram)) || (_exrom && !_game))
{
if (addr < 0xD400)
return PlaBank.Vic;
if (addr < 0xD800)
return PlaBank.Sid;
if (addr < 0xDC00)
return PlaBank.ColorRam;
if (addr < 0xDD00)
return PlaBank.Cia0;
if (addr < 0xDE00)
return PlaBank.Cia1;
return addr < 0xDF00
? PlaBank.Expansion0
: PlaBank.Expansion1;
}
}
// cartridge high, banked either at A000-BFFF or E000-FFFF depending
if (_a13 && !_game && ((_hiram && !_a14 && read && !_exrom) || (_a14 && _exrom)))
return PlaBank.CartridgeHi;
// cartridge low, banked at 8000-9FFF
if (!_a14 && !_a13 && ((_loram && _hiram && read && !_exrom) || (_exrom && !_game)))
return PlaBank.CartridgeLo;
// kernal rom, banked at E000-FFFF
if (_hiram && _a14 && _a13 && read && (_game || (!_exrom && !_game)))
return PlaBank.KernalRom;
// basic rom, banked at A000-BFFF
if (_loram && _hiram && !_a14 && _a13 && read && _game)
return PlaBank.BasicRom;
}
// ultimax mode ram exclusion
if (_exrom && !_game)
{
_p24 = !_a15 && !_a14 && _a12; //00x1 1000-1FFF, 3000-3FFF
_p25 = !_a15 && !_a14 && _a13; //001x 2000-3FFF
_p26 = !_a15 && _a14; //01xx 4000-7FFF
_p27 = _a15 && !_a14 && _a13; //101x A000-BFFF
_p28 = _a15 && _a14 && !_a13 && !_a12; //1100 C000-CFFF
if (_p24 || _p25 || _p26 || _p27 || _p28)
return PlaBank.None;
}
return PlaBank.Ram;
}
public int Peek(int addr)
{
switch (Bank(addr, true))
{
case PlaBank.BasicRom:
return PeekBasicRom(addr);
case PlaBank.CartridgeHi:
return PeekCartridgeHi(addr);
case PlaBank.CartridgeLo:
return PeekCartridgeLo(addr);
case PlaBank.CharRom:
return PeekCharRom(addr);
case PlaBank.Cia0:
return PeekCia0(addr);
case PlaBank.Cia1:
return PeekCia1(addr);
case PlaBank.ColorRam:
return PeekColorRam(addr);
case PlaBank.Expansion0:
return PeekExpansionLo(addr);
case PlaBank.Expansion1:
return PeekExpansionHi(addr);
case PlaBank.KernalRom:
return PeekKernalRom(addr);
case PlaBank.Ram:
return PeekMemory(addr);
case PlaBank.Sid:
return PeekSid(addr);
case PlaBank.Vic:
return PeekVic(addr);
}
return 0xFF;
}
public void Poke(int addr, int val)
{
switch (Bank(addr, false))
{
case PlaBank.CartridgeHi:
PokeCartridgeHi(addr, val);
break;
case PlaBank.CartridgeLo:
PokeCartridgeLo(addr, val);
break;
case PlaBank.Cia0:
PokeCia0(addr, val);
break;
case PlaBank.Cia1:
PokeCia1(addr, val);
break;
case PlaBank.ColorRam:
PokeColorRam(addr, val);
break;
case PlaBank.Expansion0:
PokeExpansionLo(addr, val);
break;
case PlaBank.Expansion1:
PokeExpansionHi(addr, val);
break;
case PlaBank.Ram:
PokeMemory(addr, val);
break;
case PlaBank.Sid:
PokeSid(addr, val);
break;
case PlaBank.Vic:
PokeVic(addr, val);
break;
}
}
public int Read(int addr)
{
switch (Bank(addr, true))
{
case PlaBank.BasicRom:
return ReadBasicRom(addr);
case PlaBank.CartridgeHi:
return ReadCartridgeHi(addr);
case PlaBank.CartridgeLo:
return ReadCartridgeLo(addr);
case PlaBank.CharRom:
return ReadCharRom(addr);
case PlaBank.Cia0:
return ReadCia0(addr);
case PlaBank.Cia1:
return ReadCia1(addr);
case PlaBank.ColorRam:
return ReadColorRam(addr);
case PlaBank.Expansion0:
return ReadExpansionLo(addr);
case PlaBank.Expansion1:
return ReadExpansionHi(addr);
case PlaBank.KernalRom:
return ReadKernalRom(addr);
case PlaBank.Ram:
return ReadMemory(addr);
case PlaBank.Sid:
return ReadSid(addr);
case PlaBank.Vic:
return ReadVic(addr);
}
return 0xFF;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
public int VicRead(int addr)
{
_game = ReadGame();
_exrom = ReadExRom();
_a14 = (addr & 0x04000) == 0;
_a13 = (addr & 0x02000) != 0;
_a12 = (addr & 0x01000) != 0;
// read char rom at 1000-1FFF and 9000-9FFF
if (_a14 && !_a13 && _a12 && (_game || !_exrom))
return ReadCharRom(addr);
// read cartridge rom in ultimax mode
if (_a13 && _a12 && _exrom && !_game)
return ReadCartridgeHi(addr);
return ReadMemory(addr);
}
public void Write(int addr, int val)
{
switch (Bank(addr, false))
{
case PlaBank.CartridgeHi:
WriteCartridgeHi(addr, val);
if (ReadGame() || !ReadExRom())
{
WriteMemory(addr, val);
}
break;
case PlaBank.CartridgeLo:
WriteCartridgeLo(addr, val);
if (ReadGame() || !ReadExRom())
{
WriteMemory(addr, val);
}
break;
case PlaBank.Cia0:
WriteCia0(addr, val);
break;
case PlaBank.Cia1:
WriteCia1(addr, val);
break;
case PlaBank.ColorRam:
WriteColorRam(addr, val);
break;
case PlaBank.Expansion0:
WriteExpansionLo(addr, val);
return;
case PlaBank.Expansion1:
WriteExpansionHi(addr, val);
return;
case PlaBank.Ram:
WriteMemory(addr, val);
break;
case PlaBank.Sid:
WriteSid(addr, val);
break;
case PlaBank.Vic:
WriteVic(addr, val);
break;
}
}
}
}

View File

@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed partial class Cia
{
private abstract class Port
{
public abstract int ReadPra(int pra, int ddra, int prb, int ddrb);
public abstract int ReadPrb(int pra, int ddra, int prb, int ddrb);
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
private sealed class DisconnectedPort : Port
{
public override int ReadPra(int pra, int ddra, int prb, int ddrb)
{
return (pra | ~ddra) & 0xFF;
}
public override int ReadPrb(int pra, int ddra, int prb, int ddrb)
{
return (prb | ~ddrb) & 0xFF;
}
}
private sealed class JoystickKeyboardPort : Port
{
[SaveState.DoNotSave] private int _ret;
[SaveState.DoNotSave] private int _tst;
[SaveState.DoNotSave] private readonly int[] _joyData;
[SaveState.DoNotSave] private readonly int[] _keyData;
public JoystickKeyboardPort(int[] joyData, int[] keyData)
{
_joyData = joyData;
_keyData = keyData;
}
private int GetJoystick1()
{
return 0xE0 |
(_joyData[0] == 0 ? 0x01 : 0x00) |
(_joyData[1] == 0 ? 0x02 : 0x00) |
(_joyData[2] == 0 ? 0x04 : 0x00) |
(_joyData[3] == 0 ? 0x08 : 0x00) |
(_joyData[4] == 0 ? 0x10 : 0x00);
}
private int GetJoystick2()
{
return 0xE0 |
(_joyData[5] == 0 ? 0x01 : 0x00) |
(_joyData[6] == 0 ? 0x02 : 0x00) |
(_joyData[7] == 0 ? 0x04 : 0x00) |
(_joyData[8] == 0 ? 0x08 : 0x00) |
(_joyData[9] == 0 ? 0x10 : 0x00);
}
private int GetKeyboardRows(int activeColumns)
{
var result = 0xFF;
for (var r = 0; r < 8; r++)
{
if ((activeColumns & 0x1) == 0)
{
var i = r << 3;
for (var c = 0; c < 8; c++)
{
if (_keyData[i++] != 0)
{
result &= ~(1 << c);
}
}
}
activeColumns >>= 1;
}
return result;
}
private int GetKeyboardColumns(int activeRows)
{
var result = 0xFF;
for (var c = 0; c < 8; c++)
{
if ((activeRows & 0x1) == 0)
{
var i = c;
for (var r = 0; r < 8; r++)
{
if (_keyData[i] != 0)
{
result &= ~(1 << r);
}
i += 0x8;
}
}
activeRows >>= 1;
}
return result;
}
public override int ReadPra(int pra, int ddra, int prb, int ddrb)
{
_ret = (pra | ~ddra) & 0xFF;
_tst = (prb | ~ddrb) & GetJoystick1();
_ret &= GetKeyboardColumns(_tst);
return _ret & GetJoystick2();
}
public override int ReadPrb(int pra, int ddra, int prb, int ddrb)
{
_ret = ~ddrb & 0xFF;
_tst = (pra | ~ddra) & GetJoystick2();
_ret &= GetKeyboardRows(_tst);
return (_ret | (prb & ddrb)) & GetJoystick1();
}
}
private sealed class IecPort : Port
{
private readonly Func<int> _readIec;
public IecPort(Func<int> readIec)
{
_readIec = readIec;
}
public override int ReadPra(int pra, int ddra, int prb, int ddrb)
{
return (pra & ddra) | (~ddra & _readIec());
}
public override int ReadPrb(int pra, int ddra, int prb, int ddrb)
{
return (prb | ~ddrb) & 0xFF;
}
}
}
}

View File

@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed partial class Cia
{
public int Peek(int addr)
{
return ReadRegister(addr & 0xF);
}
public bool ReadIrq()
{
return (_icr & 0x80) == 0;
}
public int ReadPortA()
{
return (_pra | ~_ddra) & 0xFF;
}
public int Read(int addr)
{
addr &= 0xF;
switch (addr)
{
case 0x8:
_todLatch = false;
return _latch10Ths;
case 0x9:
return _latchSec;
case 0xA:
return _latchMin;
case 0xB:
_todLatch = true;
return _latchHr;
case 0xD:
var icrTemp = _icr;
_icr = 0;
return icrTemp;
}
return ReadRegister(addr);
}
private int ReadRegister(int addr)
{
switch (addr)
{
case 0x0:
return _port.ReadPra(_pra, _ddra, _prb, _ddrb);
case 0x1:
return _port.ReadPrb(_pra, _ddra, _prb, _ddrb);
case 0x2:
return _ddra;
case 0x3:
return _ddrb;
case 0x4:
return _ta & 0xFF;
case 0x5:
return (_ta >> 8) & 0xFF;
case 0x6:
return _tb & 0xFF;
case 0x7:
return (_tb >> 8) & 0xFF;
case 0x8:
return _tod10Ths;
case 0x9:
return _todSec;
case 0xA:
return _todMin;
case 0xB:
return _todHr;
case 0xC:
return _sdr;
case 0xD:
return _icr;
case 0xE:
return _cra;
case 0xF:
return _crb;
}
return 0;
}
public void Poke(int addr, int val)
{
WriteRegister(addr, val);
}
public void Write(int addr, int val)
{
addr &= 0xF;
switch (addr)
{
case 0x4:
_latcha = (_latcha & 0xFF00) | val;
break;
case 0x5:
_latcha = (_latcha & 0xFF) | (val << 8);
if ((_cra & 0x01) == 0)
{
_ta = _latcha;
}
break;
case 0x6:
_latchb = (_latchb & 0xFF00) | val;
break;
case 0x7:
_latchb = (_latchb & 0xFF) | (val << 8);
if ((_crb & 0x01) == 0)
{
_tb = _latchb;
}
break;
case 0x8:
if ((_crb & 0x80) != 0)
{
_alm10Ths = val & 0xF;
}
else
{
_tod10Ths = val & 0xF;
}
break;
case 0x9:
if ((_crb & 0x80) != 0)
{
_almSec = val & 0x7F;
}
else
{
_todSec = val & 0x7F;
}
break;
case 0xA:
if ((_crb & 0x80) != 0)
{
_almMin = val & 0x7F;
}
else
{
_todMin = val & 0x7F;
}
break;
case 0xB:
if ((_crb & 0x80) != 0)
{
_almHr = val & 0x9F;
}
else
{
_todHr = val & 0x9F;
}
break;
case 0xC:
WriteRegister(addr, val);
TriggerInterrupt(8);
break;
case 0xD:
if ((val & 0x80) != 0)
{
_intMask |= (val & 0x7F);
}
else
{
_intMask &= ~val;
}
if ((_icr & _intMask & 0x1F) != 0)
{
_icr |= 0x80;
}
break;
default:
WriteRegister(addr, val);
break;
}
}
private void WriteRegister(int addr, int val)
{
switch (addr)
{
case 0x0:
_pra = val;
break;
case 0x1:
_prb = val;
break;
case 0x2:
_ddra = val;
break;
case 0x3:
_ddrb = val;
break;
case 0x4:
_latcha = (_latcha & 0xFF00) | val;
_ta = _latcha;
break;
case 0x5:
_latcha = (_latcha & 0xFF) | (val << 8);
_ta = _latcha;
break;
case 0x6:
_latchb = (_latchb & 0xFF00) | val;
_tb = _latchb;
break;
case 0x7:
_latchb = (_latchb & 0xFF) | (val << 8);
_tb = _latchb;
break;
case 0x8:
_tod10Ths = val & 0xF;
break;
case 0x9:
_todSec = val & 0x7F;
break;
case 0xA:
_todMin = val & 0x7F;
break;
case 0xB:
_todHr = val & 0x9F;
break;
case 0xC:
_sdr = val;
break;
case 0xD:
_intMask = val;
break;
case 0xE:
_hasNewCra = true;
_newCra = val;
_taCntPhi2 = ((val & 0x20) == 0);
_taCntCnt = ((val & 0x20) == 0x20);
break;
case 0xF:
_hasNewCrb = true;
_newCrb = val;
_tbCntPhi2 = ((val & 0x60) == 0);
_tbCntTa = ((val & 0x40) == 0x40);
_tbCntCnt = ((val & 0x20) == 0x20);
break;
}
}
[SaveState.DoNotSave]
public int DdrA
{
get { return _ddra; }
}
[SaveState.DoNotSave]
public int DdrB
{
get { return _ddrb; }
}
[SaveState.DoNotSave]
public int PrA
{
get { return _pra; }
}
[SaveState.DoNotSave]
public int PrB
{
get { return _prb; }
}
[SaveState.DoNotSave]
public int EffectivePrA
{
get { return _pra | ~_ddra; }
}
[SaveState.DoNotSave]
public int EffectivePrB
{
get { return _prb | ~_ddrb; }
}
}
}

View File

@ -0,0 +1,69 @@
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed partial class Cia
{
private void CountTod()
{
if (_todCounter > 0)
{
_todCounter -= _todDen;
return;
}
_todCounter += _todNum * ((_cra & 0x80) != 0 ? 6 : 5);
_tod10Ths++;
if (_tod10Ths > 9)
{
_tod10Ths = 0;
_todlo = (_todSec & 0x0F) + 1;
_todhi = (_todSec >> 4);
if (_todlo > 9)
{
_todlo = 0;
_todhi++;
}
if (_todhi > 5)
{
_todSec = 0;
_todlo = (_todMin & 0x0F) + 1;
_todhi = (_todMin >> 4);
if (_todlo > 9)
{
_todlo = 0;
_todhi++;
}
if (_todhi > 5)
{
_todMin = 0;
_todlo = (_todHr & 0x0F) + 1;
_todhi = (_todHr >> 4);
_todHr &= 0x80;
if (_todlo > 9)
{
_todlo = 0;
_todhi++;
}
_todHr |= (_todhi << 4) | _todlo;
if ((_todHr & 0x1F) > 0x11)
{
_todHr &= 0x80 ^ 0x80;
}
}
else
{
_todMin = (_todhi << 4) | _todlo;
}
}
else
{
_todSec = (_todhi << 4) | _todlo;
}
}
if (_tod10Ths == _alm10Ths && _todSec == _almSec && _todMin == _almMin && _todHr == _almHr)
{
TriggerInterrupt(4);
}
}
}
}

View File

@ -0,0 +1,504 @@
using BizHawk.Common;
using System;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed partial class Cia
{
/*
Commodore CIA 6526 core.
Many thanks to:
- 6502.org for hosting the 6526 datasheet
http://archive.6502.org/datasheets/mos_6526_cia.pdf
- Christian Bauer for information on the delayed interrupt mechanism on the 6526
http://frodo.cebix.net/
*/
private enum TimerState
{
Stop = 0,
WaitThenCount = 1,
LoadThenStop = 2,
LoadThenCount = 3,
LoadThenWaitThenCount = 4,
Count = 5,
CountThenStop = 6
}
public Func<bool> ReadFlag = () => true;
public bool DelayedInterrupts = true;
private int _pra;
private int _prb;
private int _ddra;
private int _ddrb;
private int _ta;
private int _tb;
private int _latcha;
private int _latchb;
private int _tod10Ths;
private int _todSec;
private int _todMin;
private int _todHr;
private int _latch10Ths;
private int _latchSec;
private int _latchMin;
private int _latchHr;
private int _alm10Ths;
private int _almSec;
private int _almMin;
private int _almHr;
private int _sdr;
private int _icr;
private int _cra;
private int _crb;
private int _intMask;
private bool _todLatch;
private bool _taCntPhi2;
private bool _taCntCnt;
private bool _tbCntPhi2;
private bool _tbCntTa;
private bool _tbCntCnt;
private bool _taIrqNextCycle;
private bool _taPrb6NegativeNextCycle;
private bool _tbIrqNextCycle;
private bool _tbPrb7NegativeNextCycle;
private bool _hasNewCra;
private bool _hasNewCrb;
private TimerState _taState;
private TimerState _tbState;
private int _newCra;
private int _newCrb;
private bool _flagLatch;
[SaveState.DoNotSave] private bool _flagInput;
[SaveState.DoNotSave] private bool _taUnderflow;
private readonly Port _port;
[SaveState.DoNotSave] private int _todlo;
[SaveState.DoNotSave] private int _todhi;
[SaveState.DoNotSave] private readonly int _todNum;
[SaveState.DoNotSave] private readonly int _todDen;
private int _todCounter;
private Cia(int todNum, int todDen)
{
_todNum = todNum;
_todDen = todDen;
}
public Cia(int todNum, int todDen, int[] keyboard, int[] joysticks) : this(todNum, todDen)
{
_port = new JoystickKeyboardPort(joysticks, keyboard);
}
public Cia(int todNum, int todDen, Func<int> readIec) : this(todNum, todDen)
{
_port = new IecPort(readIec);
}
public void HardReset()
{
_pra = 0;
_prb = 0;
_ddra = 0;
_ddrb = 0;
_ta = 0xFFFF;
_tb = 0xFFFF;
_latcha = 1;
_latchb = 1;
_tod10Ths = 0;
_todSec = 0;
_todMin = 0;
_todHr = 0;
_alm10Ths = 0;
_almSec = 0;
_almMin = 0;
_almHr = 0;
_sdr = 0;
_icr = 0;
_cra = 0;
_crb = 0;
_intMask = 0;
_todLatch = false;
_taCntPhi2 = false;
_taCntCnt = false;
_tbCntPhi2 = false;
_tbCntTa = false;
_tbCntCnt = false;
_taIrqNextCycle = false;
_tbIrqNextCycle = false;
_taState = TimerState.Stop;
_tbState = TimerState.Stop;
}
private void CheckIrqs()
{
if (_taIrqNextCycle)
{
_taIrqNextCycle = false;
TriggerInterrupt(1);
}
if (_tbIrqNextCycle)
{
_tbIrqNextCycle = false;
TriggerInterrupt(2);
}
}
public void ExecutePhase()
{
_taUnderflow = false;
if (DelayedInterrupts)
{
CheckIrqs();
}
if (_taPrb6NegativeNextCycle)
{
_prb &= 0xBF;
_taPrb6NegativeNextCycle = false;
}
if (_tbPrb7NegativeNextCycle)
{
_prb &= 0x7F;
_tbPrb7NegativeNextCycle = false;
}
switch (_taState)
{
case TimerState.WaitThenCount:
_taState = TimerState.Count;
Ta_Idle();
break;
case TimerState.Stop:
Ta_Idle();
break;
case TimerState.LoadThenStop:
_taState = TimerState.Stop;
_ta = _latcha;
Ta_Idle();
break;
case TimerState.LoadThenCount:
_taState = TimerState.Count;
_ta = _latcha;
Ta_Idle();
break;
case TimerState.LoadThenWaitThenCount:
_taState = TimerState.WaitThenCount;
if (_ta == 1)
{
Ta_Interrupt();
_taUnderflow = true;
}
else
{
_ta = _latcha;
}
Ta_Idle();
break;
case TimerState.Count:
Ta_Count();
break;
case TimerState.CountThenStop:
_taState = TimerState.Stop;
Ta_Count();
break;
}
switch (_tbState)
{
case TimerState.WaitThenCount:
_tbState = TimerState.Count;
Tb_Idle();
break;
case TimerState.Stop:
Tb_Idle();
break;
case TimerState.LoadThenStop:
_tbState = TimerState.Stop;
_tb = _latchb;
Tb_Idle();
break;
case TimerState.LoadThenCount:
_tbState = TimerState.Count;
_tb = _latchb;
Tb_Idle();
break;
case TimerState.LoadThenWaitThenCount:
_tbState = TimerState.WaitThenCount;
if (_tb == 1)
{
Tb_Interrupt();
}
else
{
_tb = _latchb;
}
Tb_Idle();
break;
case TimerState.Count:
Tb_Count();
break;
case TimerState.CountThenStop:
_tbState = TimerState.Stop;
Tb_Count();
break;
}
CountTod();
if (!_todLatch)
{
_latch10Ths = _tod10Ths;
_latchSec = _todSec;
_latchMin = _todMin;
_latchHr = _todHr;
}
_flagInput = ReadFlag();
if (!_flagInput && _flagLatch)
{
TriggerInterrupt(16);
}
_flagLatch = _flagInput;
if (!DelayedInterrupts)
{
CheckIrqs();
}
}
private void Ta_Count()
{
if (_taCntPhi2)
{
if (_ta <= 0 || --_ta == 0)
{
if (_taState != TimerState.Stop)
{
Ta_Interrupt();
}
_taUnderflow = true;
}
}
Ta_Idle();
}
private void Ta_Interrupt()
{
_ta = _latcha;
_taIrqNextCycle = true;
_icr |= 1;
if ((_cra & 0x08) != 0)
{
_cra &= 0xFE;
_newCra &= 0xFE;
_taState = TimerState.LoadThenStop;
}
else
{
_taState = TimerState.LoadThenCount;
}
if ((_cra & 0x02) != 0)
{
if ((_cra & 0x04) != 0)
{
_taPrb6NegativeNextCycle = true;
_prb |= 0x40;
}
else
{
_prb ^= 0x40;
}
_ddrb |= 0x40;
}
}
private void Ta_Idle()
{
if (_hasNewCra)
{
switch (_taState)
{
case TimerState.Stop:
case TimerState.LoadThenStop:
if ((_newCra & 0x01) != 0)
{
_taState = (_newCra & 0x10) != 0
? TimerState.LoadThenWaitThenCount
: TimerState.WaitThenCount;
}
else
{
if ((_newCra & 0x10) != 0)
{
_taState = TimerState.LoadThenStop;
}
}
break;
case TimerState.Count:
if ((_newCra & 0x01) != 0)
{
if ((_newCra & 0x10) != 0)
{
_taState = TimerState.LoadThenWaitThenCount;
}
}
else
{
_taState = (_newCra & 0x10) != 0
? TimerState.LoadThenStop
: TimerState.CountThenStop;
}
break;
case TimerState.LoadThenCount:
case TimerState.WaitThenCount:
if ((_newCra & 0x01) != 0)
{
if ((_newCra & 0x08) != 0)
{
_newCra &= 0xFE;
_taState = TimerState.Stop;
}
else if ((_newCra & 0x10) != 0)
{
_taState = TimerState.LoadThenWaitThenCount;
}
}
else
{
_taState = TimerState.Stop;
}
break;
}
_cra = _newCra & 0xEF;
_hasNewCra = false;
}
}
private void Tb_Count()
{
if (_tbCntPhi2 || (_tbCntTa && _taUnderflow))
{
if (_tb <= 0 || --_tb == 0)
{
if (_tbState != TimerState.Stop)
{
Tb_Interrupt();
}
}
}
Tb_Idle();
}
private void Tb_Interrupt()
{
_tb = _latchb;
_tbIrqNextCycle = true;
_icr |= 2;
if ((_crb & 0x08) != 0)
{
_crb &= 0xFE;
_newCrb &= 0xFE;
_tbState = TimerState.LoadThenStop;
}
else
{
_tbState = TimerState.LoadThenCount;
}
if ((_cra & 0x02) != 0)
{
if ((_cra & 0x04) != 0)
{
_tbPrb7NegativeNextCycle = true;
_prb |= 0x80;
}
else
{
_prb ^= 0x80;
}
_ddrb |= 0x80;
}
}
private void Tb_Idle()
{
if (_hasNewCrb)
{
switch (_tbState)
{
case TimerState.Stop:
case TimerState.LoadThenStop:
if ((_newCrb & 0x01) != 0)
{
_tbState = (_newCrb & 0x10) != 0
? TimerState.LoadThenWaitThenCount
: TimerState.WaitThenCount;
}
else
{
if ((_newCrb & 0x10) != 0)
{
_tbState = TimerState.LoadThenStop;
}
}
break;
case TimerState.Count:
if ((_newCrb & 0x01) != 0)
{
if ((_newCrb & 0x10) != 0)
{
_tbState = TimerState.LoadThenWaitThenCount;
}
}
else
{
_tbState = (_newCrb & 0x10) != 0
? TimerState.LoadThenStop
: TimerState.CountThenStop;
}
break;
case TimerState.LoadThenCount:
case TimerState.WaitThenCount:
if ((_newCrb & 0x01) != 0)
{
if ((_newCrb & 0x08) != 0)
{
_newCrb &= 0xFE;
_tbState = TimerState.Stop;
}
else if ((_newCrb & 0x10) != 0)
{
_tbState = TimerState.LoadThenWaitThenCount;
}
}
else
{
_tbState = TimerState.Stop;
}
break;
}
_crb = _newCrb & 0xEF;
_hasNewCrb = false;
}
}
private void TriggerInterrupt(int bit)
{
_icr |= bit;
if ((_intMask & bit) == 0) return;
_icr |= 0x80;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -1,85 +1,85 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public class LatchedPort
{
public byte Direction;
public byte Latch;
public LatchedPort()
{
Direction = 0x00;
Latch = 0x00;
}
// data works like this in these types of systems:
//
// directionA directionB result
// 0 0 1
// 1 0 latchA
// 0 1 latchB
// 1 1 latchA && latchB
//
// however because this uses transistor logic, there are cases where wired-ands
// cause the pull-up resistors not to be enough to keep the bus bit set to 1 when
// both the direction and latch are 1 (the keyboard and joystick port 2 can do this.)
// the class does not handle this case as it must be handled differently in every occurrence.
public byte ReadInput(byte bus)
{
return (byte)((Latch & Direction) | ((Direction ^ 0xFF) & bus));
}
public byte ReadOutput()
{
return (byte)((Latch & Direction) | (Direction ^ 0xFF));
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
sealed public class LatchedBooleanPort
{
public bool Direction;
public bool Latch;
public LatchedBooleanPort()
{
Direction = false;
Latch = false;
}
// data dir bus out
// 0 0 0 0
// 0 0 1 1
// 0 1 0 0
// 0 1 1 0
// 1 0 0 0
// 1 0 1 1
// 1 1 0 1
// 1 1 1 1
public bool ReadInput(bool bus)
{
return (Direction && Latch) || (!Direction && bus);
}
public bool ReadOutput()
{
return (Latch || !Direction);
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed class LatchedPort
{
public int Direction;
public int Latch;
public LatchedPort()
{
Direction = 0x00;
Latch = 0x00;
}
// data works like this in these types of systems:
//
// directionA directionB result
// 0 0 1
// 1 0 latchA
// 0 1 latchB
// 1 1 latchA && latchB
//
// however because this uses transistor logic, there are cases where wired-ands
// cause the pull-up resistors not to be enough to keep the bus bit set to 1 when
// both the direction and latch are 1 (the keyboard and joystick port 2 can do this.)
// the class does not handle this case as it must be handled differently in every occurrence.
public int ReadInput(int bus)
{
return (Latch & Direction) | ((Direction ^ 0xFF) & bus);
}
public int ReadOutput()
{
return (Latch & Direction) | (Direction ^ 0xFF);
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
public sealed class LatchedBooleanPort
{
public bool Direction;
public bool Latch;
public LatchedBooleanPort()
{
Direction = false;
Latch = false;
}
// data dir bus out
// 0 0 0 0
// 0 0 1 1
// 0 1 0 0
// 0 1 1 0
// 1 0 0 0
// 1 0 1 1
// 1 1 0 1
// 1 1 1 1
public bool ReadInput(bool bus)
{
return (Direction && Latch) || (!Direction && bus);
}
public bool ReadOutput()
{
return (Latch || !Direction);
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -1,220 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.Emulation.Cores.Components.M6502;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// an extension of the 6502 processor
sealed public class MOS6510
{
// ------------------------------------
MOS6502X cpu;
int lagCycles;
bool pinNMILast;
LatchedPort port;
bool thisNMI;
public Func<int, byte> PeekMemory;
public Action<int, byte> PokeMemory;
public Func<bool> ReadAEC;
public Func<bool> ReadIRQ;
public Func<bool> ReadNMI;
public Func<bool> ReadRDY;
public Func<int, byte> ReadMemory;
public Func<byte> ReadPort;
public Action<int, byte> WriteMemory;
public Action<int, byte> WriteMemoryPort;
// ------------------------------------
public MOS6510()
{
cpu = new MOS6502X();
// configure cpu r/w
cpu.DummyReadMemory = Read;
cpu.ReadMemory = Read;
cpu.WriteMemory = Write;
// perform hard reset
HardReset();
}
public void HardReset()
{
// configure CPU defaults
cpu.Reset();
cpu.FlagI = true;
cpu.BCD_Enabled = true;
if (ReadMemory != null)
cpu.PC = (ushort)(ReadMemory(0x0FFFC) | (ReadMemory(0x0FFFD) << 8));
// configure data port defaults
port = new LatchedPort();
port.Direction = 0x00;
port.Latch = 0xFF;
// NMI is high on startup (todo: verify)
pinNMILast = true;
}
// ------------------------------------
public void ExecutePhase1()
{
cpu.IRQ = !ReadIRQ();
}
public void ExecutePhase2()
{
cpu.RDY = ReadRDY();
// the 6502 core expects active high
// so we reverse the polarity here
thisNMI = ReadNMI();
if (!thisNMI && pinNMILast)
cpu.NMI = true;
if (ReadAEC())
{
cpu.ExecuteOne();
pinNMILast = thisNMI;
}
else
{
lagCycles++;
}
}
public int LagCycles
{
get
{
return lagCycles;
}
set
{
lagCycles = value;
}
}
internal bool AtInstructionStart()
{
return cpu.AtInstructionStart();
}
// ------------------------------------
public ushort PC
{
get
{
return cpu.PC;
}
set
{
cpu.PC = value;
}
}
public byte A
{
get { return cpu.A; } set { cpu.A = value; }
}
public byte X
{
get { return cpu.X; } set { cpu.X = value; }
}
public byte Y
{
get { return cpu.Y; } set { cpu.Y = value; }
}
public byte S
{
get { return cpu.S; } set { cpu.S = value; }
}
public bool FlagC { get { return cpu.FlagC; } }
public bool FlagZ { get { return cpu.FlagZ; } }
public bool FlagI { get { return cpu.FlagI; } }
public bool FlagD { get { return cpu.FlagD; } }
public bool FlagB { get { return cpu.FlagB; } }
public bool FlagV { get { return cpu.FlagV; } }
public bool FlagN { get { return cpu.FlagN; } }
public bool FlagT { get { return cpu.FlagT; } }
public byte Peek(long addr)
{
if (addr == 0x0000)
return port.Direction;
else if (addr == 0x0001)
return PortData;
else
return PeekMemory((int)addr);
}
public void Poke(long addr, byte val)
{
if (addr == 0x0000)
port.Direction = val;
else if (addr == 0x0001)
port.Latch = val;
else
PokeMemory((int)addr, val);
}
public byte PortData
{
get
{
return port.ReadInput(ReadPort());
}
set
{
port.Latch = value;
}
}
public byte Read(ushort addr)
{
if (addr == 0x0000)
return port.Direction;
else if (addr == 0x0001)
return PortData;
else
return ReadMemory(addr);
}
public void SyncState(Serializer ser)
{
cpu.SyncState(ser);
SaveState.SyncObject(ser, this);
}
public void Write(ushort addr, byte val)
{
if (addr == 0x0000)
{
port.Direction = val;
WriteMemoryPort(addr, val);
}
else if (addr == 0x0001)
{
port.Latch = val;
WriteMemoryPort(addr, val);
}
else
{
WriteMemory(addr, val);
}
}
}
}

View File

@ -1,570 +0,0 @@
using System;
#if false
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// via
public class MOS6522 : Timer
{
private const uint acrShiftModeDisabled = 0;
private const uint acrShiftModeInT1 = 1;
private const uint acrShiftModeInClock = 2;
private const uint acrShiftModeInExtClock = 3;
private const uint acrShiftModeOutFree = 4;
private const uint acrShiftModeOutT1 = 5;
private const uint acrShiftModeOutClock = 6;
private const uint acrShiftModeOutExtClock = 7;
private const uint pcrControlInNegative = 0;
private const uint pcrControlInNegativeIndep = 1;
private const uint pcrControlInPositive = 2;
private const uint pcrControlInPositiveIndep = 3;
private const uint pcrControlHandshake = 4;
private const uint pcrControlPulse = 5;
private const uint pcrControlLow = 6;
private const uint pcrControlHigh = 7;
private const uint tControlLoad = 0;
private const uint tControlContinuous = 1;
private const uint tControlLoadPB = 2;
private const uint tControlContinuousPB = 3;
private const uint tControlPulseCounter = 4;
private static byte[] portBit = new byte[] { 0x80, 0x40 };
private static byte[] portMask = new byte[] { 0x7F, 0xBF };
private bool caPulse;
private bool cbPulse;
private bool[] enableIrqCA;
private bool[] enableIrqCB;
private bool enableIrqSR;
private bool[] enableIrqT;
private bool[] irqCA;
private bool[] irqCB;
private bool irqSR;
private bool[] irqT;
private bool[] lastca;
private bool[] lastcb;
private byte lastpb;
private byte paLatch;
private bool paLatchEnable;
private byte paOut;
private byte pbLatch;
private bool pbLatchEnable;
private byte pbOut;
private readonly bool[] pbPulse;
private readonly uint[] pcrControlA;
private readonly uint[] pcrControlB;
private byte sr;
private uint srControl;
private readonly uint[] tControl;
public Func<bool> ReadCA0;
public Func<bool> ReadCA1;
public Func<bool> ReadCB0;
public Func<bool> ReadCB1;
public Action<bool> WriteCA0;
public Action<bool> WriteCA1;
public Action<bool> WriteCB0;
public Action<bool> WriteCB1;
public MOS6522()
{
enableIrqCA = new bool[2];
enableIrqCB = new bool[2];
enableIrqT = new bool[2];
irqCA = new bool[2];
irqCB = new bool[2];
irqT = new bool[2];
lastca = new bool[2];
lastcb = new bool[2];
pbPulse = new bool[2];
pcrControlA = new uint[2];
pcrControlB = new uint[2];
tControl = new uint[2];
}
public void HardReset()
{
caPulse = false;
cbPulse = false;
enableIrqCA[0] = false;
enableIrqCA[1] = false;
enableIrqCB[0] = false;
enableIrqCB[1] = false;
enableIrqSR = false;
enableIrqT[0] = false;
enableIrqT[1] = false;
irqCA[0] = false;
irqCA[1] = false;
irqCB[0] = false;
irqCB[1] = false;
irqSR = false;
irqT[0] = false;
irqT[1] = false;
lastca[0] = ReadCA0();
lastca[1] = ReadCA1();
lastcb[0] = ReadCB0();
lastcb[1] = ReadCB1();
pbPulse[0] = false;
pbPulse[1] = false;
pcrControlA[0] = 0;
pcrControlA[1] = 0;
pcrControlB[0] = 0;
pcrControlB[1] = 0;
tControl[0] = 0;
tControl[1] = 0;
HardResetInternal();
}
// ------------------------------------
public void ExecutePhase1()
{
}
public void ExecutePhase2()
{
bool ca0 = ReadCA0();
bool ca1 = ReadCA1();
bool cb0 = ReadCB0();
bool cb1 = ReadCB1();
bool ca0Trans = (lastca[0] != ca0);
bool ca1Trans = (lastca[1] != ca1);
bool cb0Trans = (lastcb[0] != cb0);
bool cb1Trans = (lastcb[1] != cb1);
// edge triggered interrupts
switch (pcrControlA[0])
{
case pcrControlInNegative:
if (lastca[0] && !ca0)
irqCA[0] = true;
break;
case pcrControlInPositive:
if (!lastca[0] && ca0)
irqCA[0] = true;
break;
}
switch (pcrControlB[0])
{
case pcrControlInNegative:
if (lastcb[0] && !cb0)
irqCB[0] = true;
break;
case pcrControlInPositive:
if (!lastcb[0] && cb0)
irqCB[0] = true;
break;
}
switch (pcrControlA[1])
{
case pcrControlInNegative:
case pcrControlInNegativeIndep:
if (lastca[1] && !ca1)
irqCA[1] = true;
break;
case pcrControlInPositive:
case pcrControlInPositiveIndep:
if (!lastca[1] && ca1)
irqCA[1] = true;
break;
case pcrControlHandshake:
if (lastca[0] != ca0)
WriteCA1(true);
break;
case pcrControlPulse:
if (caPulse)
caPulse = false;
else
WriteCA1(true);
break;
case pcrControlLow:
WriteCA1(false);
break;
case pcrControlHigh:
WriteCA1(true);
break;
}
switch (pcrControlB[1])
{
case pcrControlInNegative:
case pcrControlInNegativeIndep:
if (lastcb[1] && !cb1)
irqCB[1] = true;
break;
case pcrControlInPositive:
case pcrControlInPositiveIndep:
if (!lastcb[1] && cb1)
irqCB[1] = true;
break;
case pcrControlHandshake:
if (lastcb[0] != cb0)
WriteCB1(true);
break;
case pcrControlPulse:
if (cbPulse)
cbPulse = false;
else
WriteCB1(true);
break;
case pcrControlLow:
WriteCB1(false);
break;
case pcrControlHigh:
WriteCB1(true);
break;
}
// run timers
for (uint i = 0; i < 2; i++)
{
switch (tControl[i])
{
case tControlLoad:
if (timer[i] > 0)
{
timer[i]--;
if (timer[i] == 0)
irqT[i] = true;
}
break;
case tControlContinuous:
if (timer[i] > 0)
{
timer[i]--;
if (timer[i] == 0)
{
irqT[i] = true;
timer[i] = timerLatch[i];
}
}
break;
case tControlLoadPB:
if (!pbPulse[i])
WritePortB((byte)(ReadPortB() & portMask[i]));
if (timer[i] > 0)
{
timer[i]--;
if (timer[i] == 0)
{
irqT[i] = true;
if (irqT[i])
WritePortB((byte)(ReadPortB() | portBit[i]));
}
}
break;
case tControlContinuousPB:
if (timer[i] > 0)
{
timer[i]--;
if (timer[i] == 0)
{
irqT[i] = true;
timer[i] = timerLatch[i];
WritePortB((byte)(ReadPortB() ^ portBit[i]));
}
}
break;
case tControlPulseCounter:
if ((lastpb & 0x40) != 0 && (ReadPortB() & 0x40) == 0)
{
if (timer[i] > 0)
{
timer[i]--;
if (timer[i] == 0)
{
irqT[i] = true;
}
}
}
break;
}
}
lastca[0] = ca0;
lastca[1] = ca1;
lastcb[0] = cb0;
lastcb[1] = cb1;
lastpb = ReadPortB();
pinIRQ = !((irqCA[0] & enableIrqCA[0]) |
(irqCA[1] & enableIrqCA[1]) |
(irqCB[0] & enableIrqCB[0]) |
(irqCB[1] & enableIrqCB[1]) |
(irqSR & enableIrqSR) |
(irqT[0] & enableIrqT[0]) |
(irqT[1] & enableIrqT[1])
);
}
// ------------------------------------
public byte Peek(int addr)
{
return ReadRegister((ushort)(addr & 0xF));
}
public void Poke(int addr, byte val)
{
WriteRegister((ushort)(addr & 0xF), val);
}
public byte Read(ushort addr)
{
//Console.WriteLine("via R: reg" + C64Util.ToHex(addr, 4));
addr &= 0xF;
switch (addr)
{
case 0x0:
irqCB[0] = false;
irqCB[1] = false;
if (pbLatchEnable)
return Port.ExternalWrite(pbLatch, ReadPortB(), ReadDirB());
else
return ReadPortB();
case 0x1:
if (pcrControlA[0] != pcrControlInNegativeIndep && pcrControlA[0] != pcrControlInPositiveIndep)
irqCA[0] = false;
if (pcrControlA[1] != pcrControlInNegativeIndep && pcrControlA[1] != pcrControlInPositiveIndep)
irqCA[1] = false;
if (paLatchEnable)
return Port.ExternalWrite(paLatch, ReadPortA(), ReadDirA());
else
return ReadPortA();
case 0x4:
irqT[0] = false;
return ReadRegister(addr);
case 0x8:
irqT[1] = false;
return ReadRegister(addr);
default:
return ReadRegister(addr);
}
}
private byte ReadRegister(ushort addr)
{
switch (addr)
{
case 0x0:
return ReadPortB();
case 0x1:
return ReadPortA();
case 0x2:
return ReadDirB();
case 0x3:
return ReadDirA();
case 0x4:
return (byte)(timer[0] & 0xFF);
case 0x5:
return (byte)(timer[0] >> 8);
case 0x6:
return (byte)(timerLatch[0] & 0xFF);
case 0x7:
return (byte)(timerLatch[0] >> 8);
case 0x8:
return (byte)(timer[1] & 0xFF);
case 0x9:
return (byte)(timer[1] >> 8);
case 0xA:
return sr;
case 0xB:
return (byte)(
(paLatchEnable ? 0x01 : 0x00) |
(pbLatchEnable ? 0x02 : 0x00) |
(byte)((srControl & 0x7) << 2) |
(byte)((tControl[1] & 0x4) << 3) |
(byte)((tControl[0] & 0x3) << 6)
);
case 0xC:
return (byte)(
(byte)((pcrControlA[0] & 0x2) >> 1) |
(byte)((pcrControlA[1] & 0x3) << 1) |
(byte)((pcrControlB[0] & 0x2) << 3) |
(byte)((pcrControlB[1] & 0x3) << 5)
);
case 0xD: //IFR
return (byte)(
(irqCA[1] ? 0x01 : 0x00) |
(irqCA[0] ? 0x02 : 0x00) |
(irqSR ? 0x04 : 0x00) |
(irqCB[1] ? 0x08 : 0x00) |
(irqCB[0] ? 0x10 : 0x00) |
(irqT[1] ? 0x20 : 0x00) |
(irqT[0] ? 0x40 : 0x00) |
(pinIRQ ? 0x00 : 0x80)
);
case 0xE: //IER
return (byte)(
(enableIrqCA[1] ? 0x01 : 0x00) |
(enableIrqCA[0] ? 0x02 : 0x00) |
(enableIrqSR ? 0x04 : 0x00) |
(enableIrqCB[1] ? 0x08 : 0x00) |
(enableIrqCB[0] ? 0x10 : 0x00) |
(enableIrqT[1] ? 0x20 : 0x00) |
(enableIrqT[0] ? 0x40 : 0x00) |
(0x00)
);
default:
return 0x00;
}
}
public void Write(ushort addr, byte val)
{
byte result;
bool intEnable;
//Console.WriteLine("via W: reg" + C64Util.ToHex(addr, 4) + " val" + C64Util.ToHex(val, 2));
addr &= 0xF;
switch (addr)
{
case 0x0:
irqCB[0] = false;
irqCB[1] = false;
pbOut = val;
WritePortB(val);
break;
case 0x1:
if (pcrControlA[0] != pcrControlInNegativeIndep && pcrControlA[0] != pcrControlInPositiveIndep)
irqCA[0] = false;
if (pcrControlA[1] != pcrControlInNegativeIndep && pcrControlA[1] != pcrControlInPositiveIndep)
irqCA[1] = false;
paOut = val;
WritePortA(val);
break;
case 0x2:
WriteDirB(val);
WritePortB(pbOut);
break;
case 0x3:
WriteDirA(val);
WritePortA(paOut);
break;
case 0x4:
case 0x6:
WriteRegister(0x6, val);
break;
case 0x5:
WriteRegister(0x7, val);
WriteRegister(0x4, ReadRegister(0x6));
WriteRegister(0x5, ReadRegister(0x7));
irqT[0] = false;
pbPulse[0] = false;
break;
case 0x9:
timer[1] = timerLatch[1];
irqT[1] = false;
pbPulse[1] = false;
break;
case 0xE:
intEnable = ((val & 0x80) != 0);
result = ReadRegister(addr);
if (intEnable)
result |= (byte)(val & 0x7F);
else
result &= (byte)((val & 0x7F) ^ 0x7F);
WriteRegister(0xE, result);
break;
default:
WriteRegister(addr, val);
break;
}
}
private void WriteRegister(ushort addr, byte val)
{
switch (addr)
{
case 0x0:
WritePortB(val);
break;
case 0x1:
WritePortA(val);
break;
case 0x2:
WriteDirB(val);
break;
case 0x3:
WriteDirA(val);
break;
case 0x4:
timer[0] &= 0xFF00;
timer[0] |= val;
break;
case 0x5:
timer[0] &= 0x00FF;
timer[0] |= (uint)val << 8;
break;
case 0x6:
timerLatch[0] &= 0xFF00;
timerLatch[0] |= val;
break;
case 0x7:
timerLatch[0] &= 0x00FF;
timerLatch[0] |= (uint)val << 8;
break;
case 0x8:
timerLatch[1] &= 0xFF00;
timerLatch[1] |= val;
break;
case 0x9:
timer[1] &= 0x00FF;
timer[1] |= (uint)val << 8;
break;
case 0xA:
sr = val;
break;
case 0xB:
paLatchEnable = ((val & 0x01) != 0);
pbLatchEnable = ((val & 0x02) != 0);
srControl = (((uint)val >> 2) & 0x7);
tControl[1] = (((uint)val >> 3) & 0x4);
tControl[0] = (((uint)val >> 6) & 0x3);
break;
case 0xC:
pcrControlA[0] = ((uint)val << 1) & 0x2;
pcrControlA[1] = ((uint)val >> 1) & 0x7;
pcrControlB[0] = ((uint)val >> 3) & 0x2;
pcrControlB[1] = ((uint)val >> 5) & 0x7;
break;
case 0xD:
irqCA[1] = ((val & 0x01) != 0);
irqCA[0] = ((val & 0x02) != 0);
irqSR = ((val & 0x04) != 0);
irqCB[1] = ((val & 0x08) != 0);
irqCB[0] = ((val & 0x10) != 0);
irqT[1] = ((val & 0x20) != 0);
irqT[0] = ((val & 0x40) != 0);
break;
case 0xE:
enableIrqCA[1] = ((val & 0x01) != 0);
enableIrqCA[0] = ((val & 0x02) != 0);
enableIrqSR = ((val & 0x04) != 0);
enableIrqCB[1] = ((val & 0x08) != 0);
enableIrqCB[0] = ((val & 0x10) != 0);
enableIrqT[1] = ((val & 0x20) != 0);
enableIrqT[0] = ((val & 0x40) != 0);
break;
default:
break;
}
}
// ------------------------------------
}
}
#endif

View File

@ -1,90 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class MOS6526_2
{
public void ExecutePhase1()
{
proc_a();
proc_b();
if (--tod_cycles <= 0)
{
//tod_cycles += tod_period;
tod();
}
}
public void ExecutePhase2()
{
}
public void HardReset()
{
reset();
}
public byte PortAData
{
get
{
return portA.ReadOutput();
}
}
public byte PortAMask
{
get;
set;
}
public byte PortADirection
{
get
{
return portA.Direction;
}
}
public byte PortALatch
{
get
{
return portA.Latch;
}
}
public byte PortBData
{
get
{
return portB.ReadOutput();
}
}
public byte PortBDirection
{
get
{
return portB.Direction;
}
}
public byte PortBLatch
{
get
{
return portB.Latch;
}
}
public byte PortBMask
{
get;
set;
}
}
}

View File

@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class MOS6526_2
{
public Func<bool> ReadCNT;
public Func<bool> ReadFlag;
public bool ReadIRQBuffer() {
return (idr & 0x80) == 0;
}
public Func<byte> ReadPortA = (() => { return 0xFF; });
public Func<byte> ReadPortB = (() => { return 0xFF; });
public Func<bool> ReadSP;
}
}

View File

@ -1,250 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class MOS6526_2
{
int read_data;
public byte Peek(int addr)
{
addr &= 0xF;
switch (addr)
{
case 0x0:
return (byte)(portA.ReadInput(ReadPortA()) & PortAMask);
case 0x1:
return (byte)(portB.ReadInput(ReadPortB()) & PortBMask);
case 0x2:
return (byte)portA.Direction;
case 0x3:
return (byte)portB.Direction;
case 0x4:
return (byte)(a.getTimer() & 0xFF);
case 0x5:
return (byte)((a.getTimer() >> 8) & 0xFF);
case 0x6:
return (byte)(b.getTimer() & 0xFF);
case 0x7:
return (byte)((b.getTimer() >> 8) & 0xFF);
case 0x8:
case 0x9:
case 0xA:
case 0xB:
return (byte)(tod_clock[addr - 0x08] & 0xFF);
case 0xC:
return (byte)(sdr & 0xFF);
case 0xD:
return (byte)(idr & 0xFF);
case 0xE:
return (byte)((cra & 0xEE) | (a.state & 1));
case 0xF:
return (byte)((crb & 0xEE) | (b.state & 1));
}
return 0xFF;
}
public void Poke(int addr, byte val)
{
// TODO
}
public byte Read(int addr)
{
return Read(addr, 0xFF);
}
public byte Read(int addr, byte mask)
{
addr &= 0xF;
switch (addr)
{
case 0x0:
return (byte)(portA.ReadInput(ReadPortA()) & PortAMask);
case 0x1:
read_data = portB.ReadInput(ReadPortB()) & PortBMask;
if ((cra & 0x02) != 0)
{
read_data &= 0xBF;
if (((cra & 0x04) != 0) ? (a.getPbToggle()) : ((a.state & CiaTimer.TIMER_OUT) != 0))
{
read_data |= 0x40;
}
}
if ((crb & 0x02) != 0)
{
read_data &= 0x7F;
if (((crb & 0x04) != 0) ? (b.getPbToggle()) : ((b.state & CiaTimer.TIMER_OUT) != 0))
{
read_data |= 0x80;
}
}
return (byte)(read_data & 0xFF);
case 0x2:
return (byte)portA.Direction;
case 0x3:
return (byte)portB.Direction;
case 0x4:
return (byte)(a.getTimer() & 0xFF);
case 0x5:
return (byte)((a.getTimer() >> 8) & 0xFF);
case 0x6:
return (byte)(b.getTimer() & 0xFF);
case 0x7:
return (byte)((b.getTimer() >> 8) & 0xFF);
case 0x8:
case 0x9:
case 0xA:
case 0xB:
if (!tod_latched)
{
tod_latch[0] = tod_clock[0];
tod_latch[1] = tod_clock[1];
tod_latch[2] = tod_clock[2];
tod_latch[3] = tod_clock[3];
}
if (addr == 0x8)
{
tod_latched = false;
}
else if (addr == 0xB)
{
tod_latched = true;
}
return (byte)(tod_latch[addr - 0x08] & 0xFF);
case 0xC:
return (byte)(sdr & 0xFF);
case 0xD:
int_clear();
return (byte)(icr & 0xFF);
case 0xE:
return (byte)((cra & 0xEE) | (a.state & 1));
case 0xF:
return (byte)((crb & 0xEE) | (b.state & 1));
}
return 0xFF;
}
public bool ReadCNTBuffer()
{
return true;
}
public bool ReadPCBuffer()
{
return true;
}
public bool ReadSPBuffer()
{
return true;
}
public void Write(int addr, byte val)
{
Write(addr, val, 0xFF);
}
public void Write(int addr, byte val, byte mask)
{
addr &= 0xF;
switch (addr)
{
case 0x0:
portA.Latch = val;
pra = val;
break;
case 0x1:
portB.Latch = val;
prb = val;
break;
case 0x2:
portA.Direction = val;
ddra = val;
break;
case 0x3:
portB.Direction = val;
ddrb = val;
break;
case 0x4:
a.setLatchLow(val);
ta = ((int)val | (ta & 0xFF00));
break;
case 0x5:
a.setLatchHigh(val);
ta = (((int)val << 8) | (ta & 0xFF));
break;
case 0x6:
b.setLatchLow(val);
tb = ((int)val | (tb & 0xFF00));
break;
case 0x7:
b.setLatchHigh(val);
tb = (((int)val << 8) | (tb & 0xFF));
break;
case 0x8:
case 0x9:
case 0xA:
case 0xB:
if (addr == 0xB)
{
val &= 0x9F;
if ((val & 0x1F) == 0x12 && (crb & 0x80) == 0)
{
val ^= 0x80;
}
}
if ((crb & 0x80) != 0)
{
tod_alarm[addr - 0x8] = val;
}
else
{
if (addr == 0x8)
{
tod_stopped = false;
}
else if (addr == 0xB)
{
tod_stopped = true;
}
}
tod_clock[addr - 0x8] = val;
break;
case 0xC:
if ((cra & 0x40) != 0)
{
sdr_buffered = true;
}
break;
case 0xD:
if ((val & 0x80) != 0)
{
int_setEnabled(val);
}
else
{
int_clearEnabled(val);
}
break;
case 0xE:
if ((val & 0x1) != 0 && (cra & 0x1) == 0)
{
a.setPbToggle(true);
}
a.setControlRegister(val);
break;
case 0xF:
if ((val & 0x1) != 0 && (crb & 0x1) == 0)
{
b.setPbToggle(true);
}
b.setControlRegister((val | (val & 0x40) >> 1));
break;
}
}
}
}

View File

@ -1,448 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Common;
#pragma warning disable 0169
#pragma warning disable 0414
#pragma warning disable 0649
// thanks to these fine folks for their research on this buggy as hell chip:
// Simon White (s_a_white@email.com)
// Antti S. Lankila "alankila"
// Andreas Boose (viceteam@t-online.de)
// Alexander Bluhm (mam96ehy@studserv.uni-leipzig.de)
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class MOS6526_2
{
sealed private class CiaTimer
{
public const int TIMER_CR_START = 0x01;
public const int TIMER_STEP = 0x04;
public const int TIMER_CR_ONESHOT = 0x08;
public const int TIMER_CR_FLOAD = 0x10;
public const int TIMER_PHI2IN = 0x20;
public const int TIMER_MASK = TIMER_CR_START | TIMER_CR_ONESHOT | TIMER_CR_FLOAD | TIMER_PHI2IN;
public const int TIMER_COUNT2 = 0x100;
public const int TIMER_COUNT3 = 0x200;
public const int TIMER_ONESHOT0 = 0x800;
public const int TIMER_ONESHOT = 0x80000;
public const int TIMER_LOAD1 = 0x1000;
public const int TIMER_LOAD = 0x100000;
public const int TIMER_OUT = 0x40000000 << 1;
const int WANTED = TIMER_CR_START | TIMER_PHI2IN | TIMER_COUNT2 | TIMER_COUNT3;
const int UNWANTED = TIMER_OUT | TIMER_CR_FLOAD | TIMER_LOAD1 | TIMER_LOAD;
const int UNWANTED1 = TIMER_CR_START | TIMER_PHI2IN;
const int UNWANTED2 = TIMER_CR_START | TIMER_STEP;
int adj;
bool toggle;
public int state;
int lastControlValue;
int timer;
int latch;
bool pbToggle;
int ciaEventPauseTime;
bool phi1tod;
bool phi1run;
int cycleSkippingEvent = -1;
int nextTick = 0;
Action serialPort;
Action underFlow;
public CiaTimer(Action serialPortCallback, Action underFlowCallback)
{
this.serialPort = serialPortCallback;
this.underFlow = underFlowCallback;
}
public void clock()
{
if (timer != 0 && (state & TIMER_COUNT3) != 0)
{
timer--;
}
adj = state & (TIMER_CR_START | TIMER_CR_ONESHOT | TIMER_PHI2IN);
if ((state & (TIMER_CR_START | TIMER_PHI2IN)) == (TIMER_CR_START | TIMER_PHI2IN))
{
adj |= TIMER_COUNT2;
}
if ((state & TIMER_COUNT2) != 0 || (state & (TIMER_STEP | TIMER_CR_START)) == (TIMER_STEP | TIMER_CR_START))
{
adj |= TIMER_COUNT3;
}
adj |= (state & (TIMER_CR_FLOAD | TIMER_CR_ONESHOT | TIMER_LOAD1 | TIMER_ONESHOT0)) << 8;
state = adj;
if (timer == 0 && (state & TIMER_COUNT3) != 0)
{
state |= TIMER_LOAD | TIMER_OUT;
if ((state & (TIMER_ONESHOT | TIMER_ONESHOT0)) != 0)
{
state &= ~(TIMER_CR_START | TIMER_COUNT2);
}
toggle = (lastControlValue & 0x06) == 0x06;
pbToggle = toggle && !pbToggle;
serialPort();
underFlow();
}
if ((state & TIMER_LOAD) != 0)
{
timer = latch;
state &= ~TIMER_COUNT3;
}
}
public bool getPbToggle()
{
return pbToggle;
}
public int getTimer()
{
return timer;
}
public void reschedule()
{
if ((state & UNWANTED) != 0)
{
return;
}
if ((state & TIMER_COUNT3) != 0)
{
if ((timer & 0xFFFF) > 2 && (state & UNWANTED) == WANTED)
{
ciaEventPauseTime = 1;
cycleSkippingEvent = (timer - 1) & 0xFFFF;
return;
}
nextTick = 1;
return;
}
else
{
if ((state & UNWANTED1) == UNWANTED1 || (state & UNWANTED2) == UNWANTED2)
{
nextTick = 1;
return;
}
ciaEventPauseTime = -1;
return;
}
}
public void reset()
{
timer = 0xFFFF;
latch = 0xFFFF;
pbToggle = false;
state = 0;
ciaEventPauseTime = 0;
cycleSkippingEvent = -1;
}
public void setControlRegister(int cr)
{
state &= ~TIMER_MASK;
state |= cr & TIMER_MASK ^ TIMER_PHI2IN;
lastControlValue = cr;
}
public void setLatchLow(int low)
{
latch = ((latch & 0xFF00) | (low & 0xFF));
if ((state & TIMER_LOAD) != 0)
{
timer = ((timer & 0xFF00) | (low & 0xFF));
}
}
public void setLatchHigh(int high)
{
latch = ((latch & 0xFF) | ((high & 0xFF) << 8));
if ((state & TIMER_LOAD) != 0 || (state & TIMER_CR_START) == 0)
{
timer = latch;
}
}
public void setPbToggle(bool st)
{
pbToggle = st;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
const int INT_NONE = 0x00;
const int INT_UNDERFLOW_A = 0x01;
const int INT_UNDERFLOW_B = 0x02;
const int INT_ALARM = 0x04;
const int INT_SP = 0x08;
const int INT_FLAG = 0x10;
int pra;
int prb;
int ddra;
int ddrb;
int ta;
int tb;
int tod_ten;
int tod_sec;
int tod_min;
int tod_hr;
int sdr;
int icr;
int idr;
int cra;
int crb;
int sdr_out;
bool sdr_buffered;
int sdr_count;
bool tod_latched;
bool tod_stopped;
int[] tod_clock = new int[4];
int[] tod_alarm = new int[4];
int[] tod_latch = new int[4];
int tod_cycles = -1;
int tod_period = -1;
bool postTimerBEvent;
int idr_old;
bool int_delayed;
int bcd_internal;
CiaTimer a;
CiaTimer b;
LatchedPort portA;
LatchedPort portB;
public MOS6526_2(Common.DisplayType region)
{
a = new CiaTimer(serialPortA, underFlowA);
b = new CiaTimer(serialPortB, underFlowB);
portA = new LatchedPort();
portB = new LatchedPort();
switch (region)
{
case Common.DisplayType.NTSC:
tod_period = 14318181 / 140;
break;
case Common.DisplayType.PAL:
tod_period = 17734472 / 180;
break;
}
}
int bcdToByte(int input)
{
return 10 * ((input & 0xF0) >> 4) + (input & 0x0F);
}
int byteToBcd(int input)
{
return (((input / 10) << 4) + (input % 10)) & 0xFF;
}
int int_clear()
{
int_delayed = false;
idr_old = idr;
idr = 0;
return idr_old;
}
void int_clearEnabled(int i)
{
icr &= ~i;
}
void int_reset()
{
int_delayed = false;
}
void int_set(int i)
{
idr |= i;
if ((icr & idr) != 0 && (idr & 0x80) == 0)
{
int_delayed = true;
}
}
void int_setEnabled(int i)
{
icr |= i & ~0x80;
}
void proc_a()
{
if (int_delayed)
{
int_delayed = false;
idr |= 0x80;
}
if (postTimerBEvent)
{
postTimerBEvent = false;
b.state |= CiaTimer.TIMER_STEP;
}
a.clock();
}
void proc_b()
{
b.clock();
}
void reset()
{
a.reset();
b.reset();
sdr_out = 0;
sdr_count = 0;
sdr_buffered = false;
icr = 0;
idr = 0;
idr_old = 0;
int_reset();
portA.Latch = 0xFF;
portB.Latch = 0xFF;
portA.Direction = 0xFF;
portB.Direction = 0xFF;
PortAMask = 0xFF;
PortBMask = 0xFF;
}
void serialPortA()
{
if ((cra & 0x40) != 0)
{
if (sdr_count != 0)
{
if (--sdr_count == 0)
{
triggerInterruptSP();
}
}
if (sdr_count == 0 && sdr_buffered)
{
sdr_out = sdr;
sdr_buffered = false;
sdr_count = 16;
}
}
}
void serialPortB()
{
// nop
}
void tod()
{
tod_cycles += tod_period;
if (!tod_stopped)
{
int todpos = 0;
int t = bcdToByte(tod_clock[todpos]) + 1;
tod_clock[todpos++] = byteToBcd(t % 10);
if (t >= 10)
{
t = bcdToByte(tod_clock[todpos]) + 1;
tod_clock[todpos++] = byteToBcd(t % 60);
if (t >= 60)
{
t = bcdToByte(tod_clock[todpos]) + 1;
tod_clock[todpos++] = byteToBcd(t % 60);
if (t >= 60)
{
int pm = tod_clock[todpos] & 0x80;
t = bcdToByte(tod_clock[todpos] & 0x1F);
if (t == 0x11)
{
pm ^= 0x80;
}
if (t == 0x12)
{
t = 1;
}
else if (++t == 10)
{
t = 0x10;
}
t &= 0x1F;
tod_clock[todpos] = t | pm;
}
}
}
if (tod_clock[0] == tod_alarm[0] &&
tod_clock[1] == tod_alarm[1] &&
tod_clock[2] == tod_alarm[2] &&
tod_clock[3] == tod_alarm[3])
{
triggerInterruptAlarm();
}
}
}
void triggerInterruptAlarm()
{
int_set(INT_ALARM);
}
void triggerInterruptSP()
{
int_set(INT_SP);
}
void triggerInterruptUnderFlowA()
{
int_set(INT_UNDERFLOW_A);
}
void triggerInterruptUnderFlowB()
{
int_set(INT_UNDERFLOW_B);
}
void underFlowA()
{
triggerInterruptUnderFlowA();
if ((crb & 0x41) == 0x41)
{
if ((b.state & CiaTimer.TIMER_CR_START) != 0)
{
postTimerBEvent = true;
}
}
}
void underFlowB()
{
triggerInterruptUnderFlowB();
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -1,786 +0,0 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// MOS technology 6526 "CIA"
//
// emulation notes:
// * CS, R/W and RS# pins are not emulated. (not needed)
// * A low RES pin is emulated via HardReset().
sealed public class MOS6526
{
// ------------------------------------
enum InMode
{
Phase2,
CNT,
TimerAUnderflow,
TimerAUnderflowCNT
}
enum OutMode
{
Pulse,
Toggle
}
enum RunMode
{
Continuous,
Oneshot
}
enum SPMode
{
Input,
Output
}
static byte[] PBOnBit = new byte[] { 0x40, 0x80 };
static byte[] PBOnMask = new byte[] { 0xBF, 0x7F };
// ------------------------------------
public Func<bool> ReadCNT;
public Func<bool> ReadFlag;
public Func<bool> ReadSP;
// ------------------------------------
bool alarmSelect;
bool cntPos;
bool enableIntAlarm;
bool enableIntFlag;
bool enableIntSP;
bool[] enableIntTimer;
bool intAlarm;
bool intFlag;
bool intSP;
bool[] intTimer;
bool pinCnt;
bool pinCntLast;
bool pinPC;
bool pinSP;
byte sr;
int[] timerDelay;
InMode[] timerInMode;
OutMode[] timerOutMode;
bool[] timerPortEnable;
bool[] timerPulse;
RunMode[] timerRunMode;
SPMode timerSPMode;
byte[] tod;
byte[] todAlarm;
bool todAlarmPM;
int todCounter;
bool todIn;
bool todPM;
bool oldFlag;
// ------------------------------------
int todStepsNum;
int todStepsDen;
// todStepsNum/todStepsDen is the number of clock cycles it takes the external clock source to advance one cycle
// (50 or 60 Hz depending on AC frequency in use).
// By default the CIA assumes 60 Hz and will thus count incorrectly when fed with 50 Hz.
public MOS6526(int todStepsNum, int todStepsDen)
{
this.todStepsNum = todStepsNum;
this.todStepsDen = todStepsDen;
enableIntTimer = new bool[2];
intTimer = new bool[2];
timerDelay = new int[2];
timerInMode = new InMode[2];
timerOutMode = new OutMode[2];
timerPortEnable = new bool[2];
timerPulse = new bool[2];
timerRunMode = new RunMode[2];
tod = new byte[4];
todAlarm = new byte[4];
portA = new LatchedPort();
portB = new LatchedPort();
timer = new int[2];
timerLatch = new int[2];
timerOn = new bool[2];
underflow = new bool[2];
pinSP = true;
}
// ------------------------------------
public void ExecutePhase1()
{
// unsure if the timer actually operates in ph1
pinIRQ = !(
(intTimer[0] && enableIntTimer[0]) ||
(intTimer[1] && enableIntTimer[1]) ||
(intAlarm && enableIntAlarm) ||
(intSP && enableIntSP) ||
(intFlag && enableIntFlag)
);
}
public void ExecutePhase2()
{
{
bool sumCnt = ReadCNT();
cntPos |= (!pinCntLast && sumCnt);
pinCntLast = sumCnt;
pinPC = true;
TODRun();
if (timerPulse[0])
{
portA.Latch &= PBOnMask[0];
}
if (timerPulse[1])
{
portB.Latch &= PBOnMask[1];
}
if (timerDelay[0] == 0)
TimerRun(0);
else
timerDelay[0]--;
if (timerDelay[1] == 0)
TimerRun(1);
else
timerDelay[1]--;
intAlarm |= (
tod[0] == todAlarm[0] &&
tod[1] == todAlarm[1] &&
tod[2] == todAlarm[2] &&
tod[3] == todAlarm[3] &&
todPM == todAlarmPM);
cntPos = false;
underflow[0] = false;
underflow[1] = false;
bool newFlag = ReadFlag();
intFlag |= oldFlag && !newFlag;
oldFlag = newFlag;
}
}
public void HardReset()
{
HardResetInternal();
alarmSelect = false;
cntPos = false;
enableIntAlarm = false;
enableIntFlag = false;
enableIntSP = false;
enableIntTimer[0] = false;
enableIntTimer[1] = false;
intAlarm = false;
intFlag = false;
intSP = false;
intTimer[0] = false;
intTimer[1] = false;
sr = 0;
timerDelay[0] = 0;
timerDelay[1] = 0;
timerInMode[0] = InMode.Phase2;
timerInMode[1] = InMode.Phase2;
timerOn[0] = false;
timerOn[1] = false;
timerOutMode[0] = OutMode.Pulse;
timerOutMode[1] = OutMode.Pulse;
timerPortEnable[0] = false;
timerPortEnable[1] = false;
timerPulse[0] = false;
timerPulse[1] = false;
timerRunMode[0] = RunMode.Continuous;
timerRunMode[1] = RunMode.Continuous;
timerSPMode = SPMode.Input;
tod[0] = 0;
tod[1] = 0;
tod[2] = 0;
tod[3] = 0x12;
todAlarm[0] = 0;
todAlarm[1] = 0;
todAlarm[2] = 0;
todAlarm[3] = 0;
todCounter = 0;
todIn = false;
todPM = false;
pinCnt = false;
pinPC = true;
}
// ------------------------------------
private byte BCDAdd(byte i, byte j, out bool overflow)
{
{
int lo;
int hi;
int result;
lo = (i & 0x0F) + (j & 0x0F);
hi = (i & 0x70) + (j & 0x70);
if (lo > 0x09)
{
hi += 0x10;
lo += 0x06;
}
if (hi > 0x50)
{
hi += 0xA0;
}
overflow = hi >= 0x60;
result = (hi & 0x70) + (lo & 0x0F);
return (byte)(result & 0xFF);
}
}
private void TimerRun(int index)
{
{
if (timerOn[index])
{
int t = timer[index];
bool u = false;
{
switch (timerInMode[index])
{
case InMode.CNT:
// CNT positive
if (cntPos)
{
t--;
u = (t == 0);
intTimer[index] |= (t == 0);
}
break;
case InMode.Phase2:
// every clock
t--;
u = (t == 0);
intTimer[index] |= (t == 0);
break;
case InMode.TimerAUnderflow:
// every underflow[0]
if (underflow[0])
{
t--;
u = (t == 0);
intTimer[index] |= (t == 0);
}
break;
case InMode.TimerAUnderflowCNT:
// every underflow[0] while CNT high
if (underflow[0] && pinCnt)
{
t--;
u = (t == 0);
intTimer[index] |= (t == 0);
}
break;
}
// underflow?
if (u)
{
timerDelay[index] = 1;
t = timerLatch[index];
if (timerRunMode[index] == RunMode.Oneshot)
timerOn[index] = false;
if (timerPortEnable[index])
{
// force port B bit to output
portB.Direction |= PBOnBit[index];
switch (timerOutMode[index])
{
case OutMode.Pulse:
timerPulse[index] = true;
portB.Latch |= PBOnBit[index];
break;
case OutMode.Toggle:
portB.Latch ^= PBOnBit[index];
break;
}
}
}
underflow[index] = u;
timer[index] = t;
}
}
}
}
private void TODRun()
{
{
bool todV;
if (todCounter <= 0)
{
todCounter += todStepsNum*(todIn ? 6 : 5);
tod[0] = BCDAdd(tod[0], 1, out todV);
if (tod[0] >= 10)
{
tod[0] = 0;
tod[1] = BCDAdd(tod[1], 1, out todV);
if (todV)
{
tod[1] = 0;
tod[2] = BCDAdd(tod[2], 1, out todV);
if (todV)
{
tod[2] = 0;
tod[3] = BCDAdd(tod[3], 1, out todV);
if (tod[3] > 12)
{
tod[3] = 1;
}
else if (tod[3] == 12)
{
todPM = !todPM;
}
}
}
}
}
todCounter -= todStepsDen;
}
}
// ------------------------------------
public byte Peek(long addr)
{
return ReadRegister((int)addr & 0xF);
}
public void Poke(long addr, byte val)
{
WriteRegister((int)(addr & 0xF), val);
}
public byte Peek(int addr)
{
return ReadRegister((int)addr & 0xF);
}
public void Poke(int addr, byte val)
{
WriteRegister((int)(addr & 0xF), val);
}
public byte Read(int addr)
{
return Read(addr, 0xFF);
}
public byte Read(int addr, byte mask)
{
addr &= 0xF;
byte val;
switch (addr)
{
case 0x01:
val = ReadRegister(addr);
pinPC = false;
break;
case 0x0D:
val = ReadRegister(addr);
intTimer[0] = false;
intTimer[1] = false;
intAlarm = false;
intSP = false;
intFlag = false;
pinIRQ = true;
break;
default:
val = ReadRegister(addr);
break;
}
val &= mask;
return val;
}
public bool ReadCNTBuffer()
{
return pinCnt;
}
public bool ReadPCBuffer()
{
return pinPC;
}
private byte ReadRegister(int addr)
{
byte val = 0x00; //unused pin value
int timerVal;
switch (addr)
{
case 0x0:
val = (byte)(portA.ReadInput(ReadPortA()) & PortAMask);
break;
case 0x1:
val = (byte)(portB.ReadInput(ReadPortB()) & PortBMask);
break;
case 0x2:
val = portA.Direction;
break;
case 0x3:
val = portB.Direction;
break;
case 0x4:
timerVal = ReadTimerValue(0);
val = (byte)(timerVal & 0xFF);
break;
case 0x5:
timerVal = ReadTimerValue(0);
val = (byte)(timerVal >> 8);
break;
case 0x6:
timerVal = ReadTimerValue(1);
val = (byte)(timerVal & 0xFF);
break;
case 0x7:
timerVal = ReadTimerValue(1);
val = (byte)(timerVal >> 8);
break;
case 0x8:
val = tod[0];
break;
case 0x9:
val = tod[1];
break;
case 0xA:
val = tod[2];
break;
case 0xB:
val = tod[3];
break;
case 0xC:
val = sr;
break;
case 0xD:
val = (byte)(
(intTimer[0] ? 0x01 : 0x00) |
(intTimer[1] ? 0x02 : 0x00) |
(intAlarm ? 0x04 : 0x00) |
(intSP ? 0x08 : 0x00) |
(intFlag ? 0x10 : 0x00) |
(!pinIRQ ? 0x80 : 0x00)
);
break;
case 0xE:
val = (byte)(
(timerOn[0] ? 0x01 : 0x00) |
(timerPortEnable[0] ? 0x02 : 0x00) |
(todIn ? 0x80 : 0x00));
if (timerOutMode[0] == OutMode.Toggle)
val |= 0x04;
if (timerRunMode[0] == RunMode.Oneshot)
val |= 0x08;
if (timerInMode[0] == InMode.CNT)
val |= 0x20;
if (timerSPMode == SPMode.Output)
val |= 0x40;
break;
case 0xF:
val = (byte)(
(timerOn[1] ? 0x01 : 0x00) |
(timerPortEnable[1] ? 0x02 : 0x00) |
(alarmSelect ? 0x80 : 0x00));
if (timerOutMode[1] == OutMode.Toggle)
val |= 0x04;
if (timerRunMode[1] == RunMode.Oneshot)
val |= 0x08;
switch (timerInMode[1])
{
case InMode.CNT:
val |= 0x20;
break;
case InMode.TimerAUnderflow:
val |= 0x40;
break;
case InMode.TimerAUnderflowCNT:
val |= 0x60;
break;
}
break;
}
return val;
}
public bool ReadSPBuffer()
{
return pinSP;
}
private int ReadTimerValue(int index)
{
if (timerOn[index])
{
if (timer[index] == 0)
return timerLatch[index];
else
return timer[index];
}
else
{
return timer[index];
}
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
public void Write(int addr, byte val)
{
Write(addr, val, 0xFF);
}
public void Write(int addr, byte val, byte mask)
{
addr &= 0xF;
val &= mask;
val |= (byte)(ReadRegister(addr) & ~mask);
switch (addr)
{
case 0x1:
WriteRegister(addr, val);
pinPC = false;
break;
case 0x5:
WriteRegister(addr, val);
if (!timerOn[0])
timer[0] = timerLatch[0];
break;
case 0x7:
WriteRegister(addr, val);
if (!timerOn[1])
timer[1] = timerLatch[1];
break;
case 0xE:
WriteRegister(addr, val);
if ((val & 0x10) != 0)
timer[0] = timerLatch[0];
break;
case 0xF:
WriteRegister(addr, val);
if ((val & 0x10) != 0)
timer[1] = timerLatch[1];
break;
default:
WriteRegister(addr, val);
break;
}
}
public void WriteRegister(int addr, byte val)
{
bool intReg;
switch (addr)
{
case 0x0:
portA.Latch = val;
break;
case 0x1:
portB.Latch = val;
break;
case 0x2:
portA.Direction = val;
break;
case 0x3:
portB.Direction = val;
break;
case 0x4:
timerLatch[0] &= 0xFF00;
timerLatch[0] |= val;
break;
case 0x5:
timerLatch[0] &= 0x00FF;
timerLatch[0] |= val << 8;
break;
case 0x6:
timerLatch[1] &= 0xFF00;
timerLatch[1] |= val;
break;
case 0x7:
timerLatch[1] &= 0x00FF;
timerLatch[1] |= val << 8;
break;
case 0x8:
if (alarmSelect)
todAlarm[0] = (byte)(val & 0xF);
else
tod[0] = (byte)(val & 0xF);
break;
case 0x9:
if (alarmSelect)
todAlarm[1] = (byte)(val & 0x7F);
else
tod[1] = (byte)(val & 0x7F);
break;
case 0xA:
if (alarmSelect)
todAlarm[2] = (byte)(val & 0x7F);
else
tod[2] = (byte)(val & 0x7F);
break;
case 0xB:
if (alarmSelect)
{
todAlarm[3] = (byte)(val & 0x1F);
todAlarmPM = ((val & 0x80) != 0);
}
else
{
tod[3] = (byte)(val & 0x1F);
todPM = ((val & 0x80) != 0);
}
break;
case 0xC:
sr = val;
break;
case 0xD:
intReg = ((val & 0x80) != 0);
if ((val & 0x01) != 0)
enableIntTimer[0] = intReg;
if ((val & 0x02) != 0)
enableIntTimer[1] = intReg;
if ((val & 0x04) != 0)
enableIntAlarm = intReg;
if ((val & 0x08) != 0)
enableIntSP = intReg;
if ((val & 0x10) != 0)
enableIntFlag = intReg;
break;
case 0xE:
if ((val & 0x01) != 0 && !timerOn[0])
timerDelay[0] = 2;
timerOn[0] = ((val & 0x01) != 0);
timerPortEnable[0] = ((val & 0x02) != 0);
timerOutMode[0] = ((val & 0x04) != 0) ? OutMode.Toggle : OutMode.Pulse;
timerRunMode[0] = ((val & 0x08) != 0) ? RunMode.Oneshot : RunMode.Continuous;
timerInMode[0] = ((val & 0x20) != 0) ? InMode.CNT : InMode.Phase2;
timerSPMode = ((val & 0x40) != 0) ? SPMode.Output : SPMode.Input;
todIn = ((val & 0x80) != 0);
break;
case 0xF:
if ((val & 0x01) != 0 && !timerOn[1])
timerDelay[1] = 2;
timerOn[1] = ((val & 0x01) != 0);
timerPortEnable[1] = ((val & 0x02) != 0);
timerOutMode[1] = ((val & 0x04) != 0) ? OutMode.Toggle : OutMode.Pulse;
timerRunMode[1] = ((val & 0x08) != 0) ? RunMode.Oneshot : RunMode.Continuous;
switch (val & 0x60)
{
case 0x00: timerInMode[1] = InMode.Phase2; break;
case 0x20: timerInMode[1] = InMode.CNT; break;
case 0x40: timerInMode[1] = InMode.TimerAUnderflow; break;
case 0x60: timerInMode[1] = InMode.TimerAUnderflowCNT; break;
}
alarmSelect = ((val & 0x80) != 0);
break;
}
}
// ------------------------------------
public byte PortAMask = 0xFF;
public byte PortBMask = 0xFF;
bool pinIRQ;
LatchedPort portA;
LatchedPort portB;
int[] timer;
int[] timerLatch;
bool[] timerOn;
bool[] underflow;
public Func<byte> ReadPortA = (() => { return 0xFF; });
public Func<byte> ReadPortB = (() => { return 0xFF; });
void HardResetInternal()
{
timer[0] = 0xFFFF;
timer[1] = 0xFFFF;
timerLatch[0] = timer[0];
timerLatch[1] = timer[1];
pinIRQ = true;
}
public byte PortAData
{
get
{
return portA.ReadOutput();
}
}
public byte PortADirection
{
get
{
return portA.Direction;
}
}
public byte PortALatch
{
get
{
return portA.Latch;
}
}
public byte PortBData
{
get
{
return portB.ReadOutput();
}
}
public byte PortBDirection
{
get
{
return portB.Direction;
}
}
public byte PortBLatch
{
get
{
return portB.Latch;
}
}
public bool ReadIRQBuffer() { return pinIRQ; }
}
}

View File

@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// vic ntsc old
// TODO is everything right? it's mostly a copy from the other NTSC chip with tweaks wherever it was neccessary to fix something
static public class MOS6567R56A
{
static int cycles = 64;
static int scanwidth = cycles * 8;
static int lines = 262;
static int vblankstart = 0x00D % lines;
static int vblankend = 0x018 % lines;
static int hblankoffset = 20;
static int hblankstart = (0x18C + hblankoffset) % scanwidth;
static int hblankend = (0x1F0 + hblankoffset) % scanwidth;
static int[] timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, scanwidth, -1, -1);
static int[] fetch = Vic.TimingBuilder_Fetch(timing, 0x174);
static int[] ba = Vic.TimingBuilder_BA(fetch);
static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend);
static int[][] pipeline = new int[][]
{
timing,
fetch,
ba,
act
};
static public Vic Create()
{
return new Vic(
cycles, lines,
pipeline,
14318181 / 14,
hblankstart, hblankend,
vblankstart, vblankend
);
}
}
}

View File

@ -1,41 +0,0 @@
using System.Drawing;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// vic ntsc
static public class MOS6567R8
{
static int cycles = 65;
static int scanwidth = cycles * 8;
static int lines = 263;
static int vblankstart = 0x00D % lines;
static int vblankend = 0x018 % lines;
static int hblankoffset = 20;
static int hblankstart = (0x18C + hblankoffset) % scanwidth - 8; // -8 because the VIC repeats internal pixel cycles around 0x18C
static int hblankend = (0x1F0 + hblankoffset) % scanwidth - 8;
static int[] timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, scanwidth, 0x18C, 8);
static int[] fetch = Vic.TimingBuilder_Fetch(timing, 0x174);
static int[] ba = Vic.TimingBuilder_BA(fetch);
static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend);
static int[][] pipeline = new int[][]
{
timing,
fetch,
ba,
act
};
static public Vic Create()
{
return new Vic(
cycles, lines,
pipeline,
14318181 / 14,
hblankstart, hblankend,
vblankstart, vblankend
);
}
}
}

View File

@ -1,41 +0,0 @@
using System.Drawing;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// vic pal
static public class MOS6569
{
static int cycles = 63;
static int scanwidth = cycles * 8;
static int lines = 312;
static int vblankstart = 0x12C % lines;
static int vblankend = 0x00F % lines;
static int hblankoffset = 20;
static int hblankstart = (0x17C + hblankoffset) % scanwidth;
static int hblankend = (0x1E0 + hblankoffset) % scanwidth;
static int[] timing = Vic.TimingBuilder_XRaster(0x194, 0x1F8, scanwidth, -1, -1);
static int[] fetch = Vic.TimingBuilder_Fetch(timing, 0x164);
static int[] ba = Vic.TimingBuilder_BA(fetch);
static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend);
static int[][] pipeline = new int[][]
{
timing,
fetch,
ba,
act
};
static public Vic Create()
{
return new Vic(
cycles, lines,
pipeline,
17734472 / 18,
hblankstart, hblankend,
vblankstart, vblankend
);
}
}
}

View File

@ -1,44 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// pal n / drean - TODO correct?
class MOS6572
{
static int cycles = 65;
static int scanwidth = cycles * 8;
static int lines = 312;
static int vblankstart = 0x12C % lines;
static int vblankend = 0x00F % lines;
static int hblankoffset = 20;
static int hblankstart = (0x18C + hblankoffset) % scanwidth - 8; // -8 because the VIC repeats internal pixel cycles around 0x18C
static int hblankend = (0x1F0 + hblankoffset) % scanwidth - 8;
static int[] timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, scanwidth, 0x18C, 8);
static int[] fetch = Vic.TimingBuilder_Fetch(timing, 0x174);
static int[] ba = Vic.TimingBuilder_BA(fetch);
static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend);
static int[][] pipeline = new int[][]
{
timing,
fetch,
ba,
act
};
static public Vic Create()
{
return new Vic(
cycles, lines,
pipeline,
14328225 / 14,
hblankstart, hblankend,
vblankstart, vblankend
);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,353 +0,0 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// emulates the PLA
// which handles all bank switching
sealed public class MOSPLA
{
// ------------------------------------
public Func<int, byte> PeekBasicRom;
public Func<int, byte> PeekCartridgeLo;
public Func<int, byte> PeekCartridgeHi;
public Func<int, byte> PeekCharRom;
public Func<int, byte> PeekCia0;
public Func<int, byte> PeekCia1;
public Func<int, byte> PeekColorRam;
public Func<int, byte> PeekExpansionLo;
public Func<int, byte> PeekExpansionHi;
public Func<int, byte> PeekKernalRom;
public Func<int, byte> PeekMemory;
public Func<int, byte> PeekSid;
public Func<int, byte> PeekVic;
public Action<int, byte> PokeCartridgeLo;
public Action<int, byte> PokeCartridgeHi;
public Action<int, byte> PokeCia0;
public Action<int, byte> PokeCia1;
public Action<int, byte> PokeColorRam;
public Action<int, byte> PokeExpansionLo;
public Action<int, byte> PokeExpansionHi;
public Action<int, byte> PokeMemory;
public Action<int, byte> PokeSid;
public Action<int, byte> PokeVic;
public Func<bool> ReadAEC;
public Func<bool> ReadBA;
public Func<int, byte> ReadBasicRom;
public Func<int, byte> ReadCartridgeLo;
public Func<int, byte> ReadCartridgeHi;
public Func<bool> ReadCharen;
public Func<int, byte> ReadCharRom;
public Func<int, byte> ReadCia0;
public Func<int, byte> ReadCia1;
public Func<int, byte> ReadColorRam;
public Func<int, byte> ReadExpansionLo;
public Func<int, byte> ReadExpansionHi;
public Func<bool> ReadExRom;
public Func<bool> ReadGame;
public Func<bool> ReadHiRam;
public Func<int, byte> ReadKernalRom;
public Func<bool> ReadLoRam;
public Func<int, byte> ReadMemory;
public Func<int, byte> ReadSid;
public Func<int, byte> ReadVic;
public Action<int, byte> WriteCartridgeLo;
public Action<int, byte> WriteCartridgeHi;
public Action<int, byte> WriteCia0;
public Action<int, byte> WriteCia1;
public Action<int, byte> WriteColorRam;
public Action<int, byte> WriteExpansionLo;
public Action<int, byte> WriteExpansionHi;
public Action<int, byte> WriteMemory;
public Action<int, byte> WriteSid;
public Action<int, byte> WriteVic;
// ------------------------------------
enum PLABank
{
None,
RAM,
BasicROM,
KernalROM,
CharROM,
IO,
CartridgeLo,
CartridgeHi,
Vic,
Sid,
ColorRam,
Cia0,
Cia1,
Expansion0,
Expansion1
}
// ------------------------------------
bool p24;
bool p25;
bool p26;
bool p27;
bool p28;
bool loram;
bool hiram;
bool game;
bool exrom;
bool charen;
bool a15;
bool a14;
bool a13;
bool a12;
private PLABank Bank(int addr, bool read)
{
loram = ReadLoRam();
hiram = ReadHiRam();
game = ReadGame();
a15 = (addr & 0x08000) != 0;
a14 = (addr & 0x04000) != 0;
a13 = (addr & 0x02000) != 0;
a12 = (addr & 0x01000) != 0;
// upper memory regions 8000-FFFF
exrom = ReadExRom();
if (a15)
{
// io/character access
if (a14 && !a13 && a12)
{
// character rom, banked in at D000-DFFF
charen = ReadCharen();
if (read && !charen && (((hiram || loram) && game) || (hiram && !exrom && !game)))
return PLABank.CharROM;
// io block, banked in at D000-DFFF
if ((charen && (hiram || loram)) || (exrom && !game))
{
if (addr < 0xD400)
return PLABank.Vic;
if (addr < 0xD800)
return PLABank.Sid;
if (addr < 0xDC00)
return PLABank.ColorRam;
if (addr < 0xDD00)
return PLABank.Cia0;
if (addr < 0xDE00)
return PLABank.Cia1;
if (addr < 0xDF00)
return PLABank.Expansion0;
return PLABank.Expansion1;
}
}
// cartridge high, banked either at A000-BFFF or E000-FFFF depending
if (a13 && !game && ((hiram && !a14 && read && !exrom) || (a14 && exrom)))
return PLABank.CartridgeHi;
// cartridge low, banked at 8000-9FFF
if (!a14 && !a13 && ((loram && hiram && read && !exrom) || (exrom && !game)))
return PLABank.CartridgeLo;
// kernal rom, banked at E000-FFFF
if (hiram && a14 && a13 && read && (game || (!exrom && !game)))
return PLABank.KernalROM;
// basic rom, banked at A000-BFFF
if (loram && hiram && !a14 && a13 && read && game)
return PLABank.BasicROM;
}
// ultimax mode ram exclusion
if (exrom && !game)
{
p24 = !a15 && !a14 && a12; //00x1 1000-1FFF, 3000-3FFF
p25 = !a15 && !a14 && a13; //001x 2000-3FFF
p26 = !a15 && a14; //01xx 4000-7FFF
p27 = a15 && !a14 && a13; //101x A000-BFFF
p28 = a15 && a14 && !a13 && !a12; //1100 C000-CFFF
if (p24 || p25 || p26 || p27 || p28)
return PLABank.None;
}
return PLABank.RAM;
}
public byte Peek(int addr)
{
switch (Bank(addr, true))
{
case PLABank.BasicROM:
return PeekBasicRom(addr);
case PLABank.CartridgeHi:
return PeekCartridgeHi(addr);
case PLABank.CartridgeLo:
return PeekCartridgeLo(addr);
case PLABank.CharROM:
return PeekCharRom(addr);
case PLABank.Cia0:
return PeekCia0(addr);
case PLABank.Cia1:
return PeekCia1(addr);
case PLABank.ColorRam:
return PeekColorRam(addr);
case PLABank.Expansion0:
return PeekExpansionLo(addr);
case PLABank.Expansion1:
return PeekExpansionHi(addr);
case PLABank.KernalROM:
return PeekKernalRom(addr);
case PLABank.RAM:
return PeekMemory(addr);
case PLABank.Sid:
return PeekSid(addr);
case PLABank.Vic:
return PeekVic(addr);
}
return 0xFF;
}
public void Poke(int addr, byte val)
{
switch (Bank(addr, false))
{
case PLABank.CartridgeHi:
PokeCartridgeHi(addr, val);
break;
case PLABank.CartridgeLo:
PokeCartridgeLo(addr, val);
break;
case PLABank.Cia0:
PokeCia0(addr, val);
break;
case PLABank.Cia1:
PokeCia1(addr, val);
break;
case PLABank.ColorRam:
PokeColorRam(addr, val);
break;
case PLABank.Expansion0:
PokeExpansionLo(addr, val);
break;
case PLABank.Expansion1:
PokeExpansionHi(addr, val);
break;
case PLABank.RAM:
PokeMemory(addr, val);
break;
case PLABank.Sid:
PokeSid(addr, val);
break;
case PLABank.Vic:
PokeVic(addr, val);
break;
}
}
public byte Read(int addr)
{
switch (Bank(addr, true))
{
case PLABank.BasicROM:
return ReadBasicRom(addr);
case PLABank.CartridgeHi:
return ReadCartridgeHi(addr);
case PLABank.CartridgeLo:
return ReadCartridgeLo(addr);
case PLABank.CharROM:
return ReadCharRom(addr);
case PLABank.Cia0:
return ReadCia0(addr);
case PLABank.Cia1:
return ReadCia1(addr);
case PLABank.ColorRam:
return ReadColorRam(addr);
case PLABank.Expansion0:
return ReadExpansionLo(addr);
case PLABank.Expansion1:
return ReadExpansionHi(addr);
case PLABank.KernalROM:
return ReadKernalRom(addr);
case PLABank.RAM:
return ReadMemory(addr);
case PLABank.Sid:
return ReadSid(addr);
case PLABank.Vic:
return ReadVic(addr);
}
return 0xFF;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
public byte VicRead(int addr)
{
game = ReadGame();
exrom = ReadExRom();
a14 = (addr & 0x04000) == 0;
a13 = (addr & 0x02000) != 0;
a12 = (addr & 0x01000) != 0;
// read char rom at 1000-1FFF and 9000-9FFF
if (a14 && !a13 && a12 && (game || !exrom))
return ReadCharRom(addr);
// read cartridge rom in ultimax mode
if (a13 && a12 && exrom && !game)
return ReadCartridgeHi(addr);
return ReadMemory(addr);
}
public void Write(int addr, byte val)
{
switch (Bank(addr, false))
{
case PLABank.CartridgeHi:
WriteCartridgeHi(addr, val);
if (ReadGame() || !ReadExRom())
{
WriteMemory(addr, val);
}
break;
case PLABank.CartridgeLo:
WriteCartridgeLo(addr, val);
if (ReadGame() || !ReadExRom())
{
WriteMemory(addr, val);
}
break;
case PLABank.Cia0:
WriteCia0(addr, val);
break;
case PLABank.Cia1:
WriteCia1(addr, val);
break;
case PLABank.ColorRam:
WriteColorRam(addr, val);
break;
case PLABank.Expansion0:
WriteExpansionLo(addr, val);
return;
case PLABank.Expansion1:
WriteExpansionHi(addr, val);
return;
case PLABank.RAM:
WriteMemory(addr, val);
break;
case PLABank.Sid:
WriteSid(addr, val);
break;
case PLABank.Vic:
WriteVic(addr, val);
break;
}
}
}
}

View File

@ -1,38 +0,0 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
// the functions on this port are at the point of
// view of an external device.
sealed public class SerialPort
{
public Func<bool> ReadAtnOut;
public Func<bool> ReadClockOut;
public Func<bool> ReadDataOut;
public SerialPort()
{
}
public void HardReset()
{
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
public bool WriteClockIn()
{
return true;
}
public bool WriteDataIn()
{
return true;
}
}
}

View File

@ -7,48 +7,44 @@ using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Sid
public sealed partial class Sid
{
sealed class Envelope
private sealed class Envelope
{
const int stateAttack = 0;
const int stateDecay = 1;
const int stateRelease = 2;
[SaveState.DoNotSave] private const int StateAttack = 0;
[SaveState.DoNotSave] private const int StateDecay = 1;
[SaveState.DoNotSave] private const int StateRelease = 2;
int attack;
int decay;
bool delay;
int envCounter;
int expCounter;
int expPeriod;
bool freeze;
int lfsr;
bool gate;
int rate;
int release;
int state;
int sustain;
private int _attack;
private int _decay;
private bool _delay;
private int _envCounter;
private int _expCounter;
private int _expPeriod;
private bool _freeze;
private int _lfsr;
private bool _gate;
private int _rate;
private int _release;
private int _state;
private int _sustain;
static int[] adsrTable = new int[]
{
private static readonly int[] AdsrTable = {
0x7F00, 0x0006, 0x003C, 0x0330,
0x20C0, 0x6755, 0x3800, 0x500E,
0x1212, 0x0222, 0x1848, 0x59B8,
0x3840, 0x77E2, 0x7625, 0x0A93
};
static int[] expCounterTable = new int[]
{
private static readonly int[] ExpCounterTable = {
0xFF, 0x5D, 0x36, 0x1A, 0x0E, 0x06, 0x00
};
static int[] expPeriodTable = new int[]
{
private static readonly int[] ExpPeriodTable = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x1E, 0x01
};
static int[] sustainTable = new int[]
{
private static readonly int[] SustainTable = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
@ -60,94 +56,93 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public void ExecutePhase2()
{
if (!_delay)
{
if (!delay)
{
envCounter--;
delay = true;
UpdateExpCounter();
}
if (lfsr != rate)
{
int feedback = ((lfsr >> 14) ^ (lfsr >> 13)) & 0x1;
lfsr = ((lfsr << 1) & 0x7FFF) | feedback;
return;
}
lfsr = 0x7FFF;
if (state == stateAttack || ++expCounter == expPeriod)
{
expCounter = 0;
if (freeze)
return;
switch (state)
{
case stateAttack:
envCounter++;
if (envCounter == 0xFF)
{
state = stateDecay;
rate = adsrTable[decay];
}
break;
case stateDecay:
if (envCounter == sustainTable[sustain])
{
return;
}
if (expPeriod != 1)
{
delay = false;
return;
}
envCounter--;
break;
case stateRelease:
if (expPeriod != 1)
{
delay = false;
return;
}
envCounter--;
break;
}
envCounter &= 0xFF;
UpdateExpCounter();
}
_envCounter--;
_delay = true;
UpdateExpCounter();
}
if (_lfsr != _rate)
{
var feedback = ((_lfsr >> 14) ^ (_lfsr >> 13)) & 0x1;
_lfsr = ((_lfsr << 1) & 0x7FFF) | feedback;
return;
}
_lfsr = 0x7FFF;
if (_state != StateAttack && ++_expCounter != _expPeriod)
{
return;
}
_expCounter = 0;
if (_freeze)
return;
switch (_state)
{
case StateAttack:
_envCounter++;
if (_envCounter == 0xFF)
{
_state = StateDecay;
_rate = AdsrTable[_decay];
}
break;
case StateDecay:
if (_envCounter == SustainTable[_sustain])
{
return;
}
if (_expPeriod != 1)
{
_delay = false;
return;
}
_envCounter--;
break;
case StateRelease:
if (_expPeriod != 1)
{
_delay = false;
return;
}
_envCounter--;
break;
}
_envCounter &= 0xFF;
UpdateExpCounter();
}
public void HardReset()
{
attack = 0;
decay = 0;
delay = true;
envCounter = 0;
expCounter = 0;
expPeriod = expPeriodTable[0];
freeze = false;
gate = false;
lfsr = 0x7FFF;
rate = adsrTable[release];
release = 0;
state = stateRelease;
sustain = 0;
_attack = 0;
_decay = 0;
_delay = true;
_envCounter = 0;
_expCounter = 0;
_expPeriod = ExpPeriodTable[0];
_freeze = false;
_gate = false;
_lfsr = 0x7FFF;
_rate = AdsrTable[_release];
_release = 0;
_state = StateRelease;
_sustain = 0;
}
private void UpdateExpCounter()
{
{
for (int i = 0; i < 7; i++)
for (var i = 0; i < 7; i++)
{
if (envCounter == expCounterTable[i])
expPeriod = expPeriodTable[i];
if (_envCounter == ExpCounterTable[i])
_expPeriod = ExpPeriodTable[i];
}
if (envCounter == 0)
freeze = true;
if (_envCounter == 0)
_freeze = true;
}
}
@ -157,13 +152,13 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return attack;
return _attack;
}
set
{
attack = (value & 0xF);
if (state == stateAttack)
rate = adsrTable[attack];
_attack = value & 0xF;
if (_state == StateAttack)
_rate = AdsrTable[_attack];
}
}
@ -171,13 +166,13 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return decay;
return _decay;
}
set
{
decay = (value & 0xF);
if (state == stateDecay)
rate = adsrTable[decay];
_decay = value & 0xF;
if (_state == StateDecay)
_rate = AdsrTable[_decay];
}
}
@ -185,24 +180,24 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return gate;
return _gate;
}
set
{
bool nextGate = value;
if (nextGate && !gate)
var nextGate = value;
if (nextGate && !_gate)
{
state = stateAttack;
rate = adsrTable[attack];
delay = true;
freeze = false;
_state = StateAttack;
_rate = AdsrTable[_attack];
_delay = true;
_freeze = false;
}
else if (!nextGate && gate)
else if (!nextGate && _gate)
{
state = stateRelease;
rate = adsrTable[release];
_state = StateRelease;
_rate = AdsrTable[_release];
}
gate = nextGate;
_gate = nextGate;
}
}
@ -210,7 +205,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return envCounter;
return _envCounter;
}
}
@ -218,13 +213,13 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return release;
return _release;
}
set
{
release = (value & 0xF);
if (state == stateRelease)
rate = adsrTable[release];
_release = value & 0xF;
if (_state == StateRelease)
_rate = AdsrTable[_release];
}
}
@ -232,11 +227,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return sustain;
return _sustain;
}
set
{
sustain = (value & 0xF);
_sustain = value & 0xF;
}
}

View File

@ -5,32 +5,22 @@ using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Sid
public sealed partial class Sid
{
public byte Peek(long addr)
public int Peek(int addr)
{
return ReadRegister((int)(addr & 0x1F));
return ReadRegister(addr & 0x1F);
}
public void Poke(long addr, byte val)
public void Poke(int addr, int val)
{
WriteRegister((int)(addr & 0x1F), val);
WriteRegister(addr & 0x1F, val);
}
public byte Peek(int addr)
{
return ReadRegister((int)(addr & 0x1F));
}
public void Poke(int addr, byte val)
{
WriteRegister((int)(addr & 0x1F), val);
}
public byte Read(int addr)
public int Read(int addr)
{
addr &= 0x1F;
byte result = 0x00;
var result = 0x00;
switch (addr)
{
case 0x19:
@ -44,116 +34,94 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
return result;
}
private byte ReadRegister(int addr)
private int ReadRegister(int addr)
{
byte result = 0x00;
var result = 0x00;
switch (addr)
{
case 0x00: result = (byte)voices[0].FrequencyLo; break;
case 0x01: result = (byte)voices[0].FrequencyHi; break;
case 0x02: result = (byte)voices[0].PulseWidthLo; break;
case 0x03: result = (byte)voices[0].PulseWidthHi; break;
case 0x00: result = _voice0.FrequencyLo; break;
case 0x01: result = _voice0.FrequencyHi; break;
case 0x02: result = _voice0.PulseWidthLo; break;
case 0x03: result = _voice0.PulseWidthHi; break;
case 0x04:
result = (byte)(
(envelopes[0].Gate ? 0x01 : 0x00) |
(voices[0].Sync ? 0x02 : 0x00) |
(voices[0].RingMod ? 0x04 : 0x00) |
(voices[0].Test ? 0x08 : 0x00) |
(byte)(voices[0].Waveform << 4)
);
result = (_envelope0.Gate ? 0x01 : 0x00) |
(_voice0.Sync ? 0x02 : 0x00) |
(_voice0.RingMod ? 0x04 : 0x00) |
(_voice0.Test ? 0x08 : 0x00) |
(_voice0.Waveform << 4);
break;
case 0x05:
result = (byte)(
(envelopes[0].Attack << 4) |
(envelopes[0].Decay)
);
result = (_envelope0.Attack << 4) |
_envelope0.Decay;
break;
case 0x06:
result = (byte)(
(envelopes[0].Sustain << 4) |
(envelopes[0].Release)
);
result = (_envelope0.Sustain << 4) |
_envelope0.Release;
break;
case 0x07: result = (byte)voices[1].FrequencyLo; break;
case 0x08: result = (byte)voices[1].FrequencyHi; break;
case 0x09: result = (byte)voices[1].PulseWidthLo; break;
case 0x0A: result = (byte)voices[1].PulseWidthHi; break;
case 0x07: result = _voice1.FrequencyLo; break;
case 0x08: result = _voice1.FrequencyHi; break;
case 0x09: result = _voice1.PulseWidthLo; break;
case 0x0A: result = _voice1.PulseWidthHi; break;
case 0x0B:
result = (byte)(
(envelopes[1].Gate ? 0x01 : 0x00) |
(voices[1].Sync ? 0x02 : 0x00) |
(voices[1].RingMod ? 0x04 : 0x00) |
(voices[1].Test ? 0x08 : 0x00) |
(byte)(voices[1].Waveform << 4)
);
result = (_envelope1.Gate ? 0x01 : 0x00) |
(_voice1.Sync ? 0x02 : 0x00) |
(_voice1.RingMod ? 0x04 : 0x00) |
(_voice1.Test ? 0x08 : 0x00) |
(_voice1.Waveform << 4);
break;
case 0x0C:
result = (byte)(
(envelopes[1].Attack << 4) |
(envelopes[1].Decay)
);
result = (_envelope1.Attack << 4) |
_envelope1.Decay;
break;
case 0x0D:
result = (byte)(
(envelopes[1].Sustain << 4) |
(envelopes[1].Release)
);
result = (_envelope1.Sustain << 4) |
_envelope1.Release;
break;
case 0x0E: result = (byte)voices[2].FrequencyLo; break;
case 0x0F: result = (byte)voices[2].FrequencyHi; break;
case 0x10: result = (byte)voices[2].PulseWidthLo; break;
case 0x11: result = (byte)voices[2].PulseWidthHi; break;
case 0x0E: result = _voice2.FrequencyLo; break;
case 0x0F: result = _voice2.FrequencyHi; break;
case 0x10: result = _voice2.PulseWidthLo; break;
case 0x11: result = _voice2.PulseWidthHi; break;
case 0x12:
result = (byte)(
(envelopes[2].Gate ? 0x01 : 0x00) |
(voices[2].Sync ? 0x02 : 0x00) |
(voices[2].RingMod ? 0x04 : 0x00) |
(voices[2].Test ? 0x08 : 0x00) |
(byte)(voices[2].Waveform << 4)
);
result = (_envelope2.Gate ? 0x01 : 0x00) |
(_voice2.Sync ? 0x02 : 0x00) |
(_voice2.RingMod ? 0x04 : 0x00) |
(_voice2.Test ? 0x08 : 0x00) |
(_voice2.Waveform << 4);
break;
case 0x13:
result = (byte)(
(envelopes[2].Attack << 4) |
(envelopes[2].Decay)
);
result = (_envelope2.Attack << 4) |
_envelope2.Decay;
break;
case 0x14:
result = (byte)(
(envelopes[2].Sustain << 4) |
(envelopes[2].Release)
);
result = (_envelope2.Sustain << 4) |
_envelope2.Release;
break;
case 0x15: result = (byte)(filterFrequency & 0x7); break;
case 0x16: result = (byte)((filterFrequency >> 3) & 0xFF); break;
case 0x15: result = _filterFrequency & 0x7; break;
case 0x16: result = (_filterFrequency >> 3) & 0xFF; break;
case 0x17:
result = (byte)(
(filterEnable[0] ? 0x01 : 0x00) |
(filterEnable[1] ? 0x02 : 0x00) |
(filterEnable[2] ? 0x04 : 0x00) |
(byte)(filterResonance << 4)
);
result = (_filterEnable[0] ? 0x01 : 0x00) |
(_filterEnable[1] ? 0x02 : 0x00) |
(_filterEnable[2] ? 0x04 : 0x00) |
(_filterResonance << 4);
break;
case 0x18:
result = (byte)(
(byte)volume |
(filterSelectLoPass ? 0x10 : 0x00) |
(filterSelectBandPass ? 0x20 : 0x00) |
(filterSelectHiPass ? 0x40 : 0x00) |
(disableVoice3 ? 0x80 : 0x00)
);
result = _volume |
(_filterSelectLoPass ? 0x10 : 0x00) |
(_filterSelectBandPass ? 0x20 : 0x00) |
(_filterSelectHiPass ? 0x40 : 0x00) |
(_disableVoice3 ? 0x80 : 0x00);
break;
case 0x19: result = (byte)potX; break;
case 0x1A: result = (byte)potY; break;
case 0x1B: result = (byte)(voiceOutput[2] >> 4); break;
case 0x1C: result = (byte)(envelopeOutput[2]); break;
case 0x19: result = _potX; break;
case 0x1A: result = _potY; break;
case 0x1B: result = _voiceOutput2 >> 4; break;
case 0x1C: result = _envelopeOutput2; break;
}
return result;
}
public void Write(int addr, byte val)
public void Write(int addr, int val)
{
addr &= 0x1F;
switch (addr)
@ -174,51 +142,51 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
}
}
private void WriteRegister(int addr, byte val)
private void WriteRegister(int addr, int val)
{
switch (addr)
{
case 0x00: voices[0].FrequencyLo = val; break;
case 0x01: voices[0].FrequencyHi = val; break;
case 0x02: voices[0].PulseWidthLo = val; break;
case 0x03: voices[0].PulseWidthHi = val; break;
case 0x04: voices[0].Control = val; envelopes[0].Gate = ((val & 0x01) != 0); break;
case 0x05: envelopes[0].Attack = (val >> 4); envelopes[0].Decay = (val & 0xF); break;
case 0x06: envelopes[0].Sustain = (val >> 4); envelopes[0].Release = (val & 0xF); break;
case 0x07: voices[1].FrequencyLo = val; break;
case 0x08: voices[1].FrequencyHi = val; break;
case 0x09: voices[1].PulseWidthLo = val; break;
case 0x0A: voices[1].PulseWidthHi = val; break;
case 0x0B: voices[1].Control = val; envelopes[1].Gate = ((val & 0x01) != 0); break;
case 0x0C: envelopes[1].Attack = (val >> 4); envelopes[1].Decay = (val & 0xF); break;
case 0x0D: envelopes[1].Sustain = (val >> 4); envelopes[1].Release = (val & 0xF); break;
case 0x0E: voices[2].FrequencyLo = val; break;
case 0x0F: voices[2].FrequencyHi = val; break;
case 0x10: voices[2].PulseWidthLo = val; break;
case 0x11: voices[2].PulseWidthHi = val; break;
case 0x12: voices[2].Control = val; envelopes[2].Gate = ((val & 0x01) != 0); break;
case 0x13: envelopes[2].Attack = (val >> 4); envelopes[2].Decay = (val & 0xF); break;
case 0x14: envelopes[2].Sustain = (val >> 4); envelopes[2].Release = (val & 0xF); break;
case 0x15: filterFrequency &= 0x3FF; filterFrequency |= (val & 0x7); break;
case 0x16: filterFrequency &= 0x7; filterFrequency |= val << 3; break;
case 0x00: _voice0.FrequencyLo = val; break;
case 0x01: _voice0.FrequencyHi = val; break;
case 0x02: _voice0.PulseWidthLo = val; break;
case 0x03: _voice0.PulseWidthHi = val; break;
case 0x04: _voice0.Control = val; _envelope0.Gate = (val & 0x01) != 0; break;
case 0x05: _envelope0.Attack = val >> 4; _envelope0.Decay = val & 0xF; break;
case 0x06: _envelope0.Sustain = val >> 4; _envelope0.Release = val & 0xF; break;
case 0x07: _voice1.FrequencyLo = val; break;
case 0x08: _voice1.FrequencyHi = val; break;
case 0x09: _voice1.PulseWidthLo = val; break;
case 0x0A: _voice1.PulseWidthHi = val; break;
case 0x0B: _voice1.Control = val; _envelope1.Gate = (val & 0x01) != 0; break;
case 0x0C: _envelope1.Attack = val >> 4; _envelope1.Decay = val & 0xF; break;
case 0x0D: _envelope1.Sustain = val >> 4; _envelope1.Release = val & 0xF; break;
case 0x0E: _voice2.FrequencyLo = val; break;
case 0x0F: _voice2.FrequencyHi = val; break;
case 0x10: _voice2.PulseWidthLo = val; break;
case 0x11: _voice2.PulseWidthHi = val; break;
case 0x12: _voice2.Control = val; _envelope2.Gate = (val & 0x01) != 0; break;
case 0x13: _envelope2.Attack = val >> 4; _envelope2.Decay = val & 0xF; break;
case 0x14: _envelope2.Sustain = val >> 4; _envelope2.Release = val & 0xF; break;
case 0x15: _filterFrequency &= 0x3FF; _filterFrequency |= val & 0x7; break;
case 0x16: _filterFrequency &= 0x7; _filterFrequency |= val << 3; break;
case 0x17:
filterEnable[0] = ((val & 0x1) != 0);
filterEnable[1] = ((val & 0x2) != 0);
filterEnable[2] = ((val & 0x4) != 0);
filterResonance = val >> 4;
_filterEnable[0] = (val & 0x1) != 0;
_filterEnable[1] = (val & 0x2) != 0;
_filterEnable[2] = (val & 0x4) != 0;
_filterResonance = val >> 4;
break;
case 0x18:
volume = (val & 0xF);
filterSelectLoPass = ((val & 0x10) != 0);
filterSelectBandPass = ((val & 0x20) != 0);
filterSelectHiPass = ((val & 0x40) != 0);
disableVoice3 = ((val & 0x40) != 0);
_volume = val & 0xF;
_filterSelectLoPass = (val & 0x10) != 0;
_filterSelectBandPass = (val & 0x20) != 0;
_filterSelectHiPass = (val & 0x40) != 0;
_disableVoice3 = (val & 0x40) != 0;
break;
case 0x19:
potX = val;
_potX = val;
break;
case 0x1A:
potY = val;
_potY = val;
break;
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed partial class Sid : ISoundProvider, ISyncSoundProvider
{
public int MaxVolume
{
get { return short.MaxValue; }
set { }
}
public void DiscardSamples()
{
_outputBufferIndex = 0;
}
public void GetSamples(short[] samples)
{
Flush();
var length = Math.Min(samples.Length, _outputBufferIndex);
for (var i = 0; i < length; i++)
{
samples[i] = _outputBuffer[i];
}
_outputBufferIndex = 0;
}
public void GetSamples(out short[] samples, out int nsamp)
{
Flush();
samples = _outputBuffer;
nsamp = _outputBufferIndex >> 1;
_outputBufferIndex = 0;
}
}
}

View File

@ -7,60 +7,60 @@ using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Sid
public sealed partial class Sid
{
sealed class Voice
private sealed class Voice
{
int accBits;
int accNext;
int accumulator;
bool controlTestPrev;
int controlWavePrev;
int delay;
int floatOutputTTL;
int frequency;
bool msbRising;
int noise;
int noNoise;
int noNoiseOrNoise;
int noPulse;
int output;
int pulse;
int pulseWidth;
bool ringMod;
int ringMsbMask;
int shiftRegister;
int shiftRegisterReset;
bool sync;
bool test;
int[] wave;
int waveform;
int waveformIndex;
int[][] waveTable;
private int _accBits;
private int _accNext;
private int _accumulator;
private bool _controlTestPrev;
private int _controlWavePrev;
private int _delay;
private int _floatOutputTtl;
private int _frequency;
private bool _msbRising;
private int _noise;
private int _noNoise;
private int _noNoiseOrNoise;
private int _noPulse;
private int _output;
private int _pulse;
private int _pulseWidth;
private bool _ringMod;
private int _ringMsbMask;
private int _shiftRegister;
private int _shiftRegisterReset;
private bool _sync;
private bool _test;
[SaveState.DoNotSave] private int[] _wave;
private int _waveform;
private int _waveformIndex;
[SaveState.DoNotSave] private readonly int[][] _waveTable;
public Voice(int[][] newWaveTable)
{
waveTable = newWaveTable;
_waveTable = newWaveTable;
HardReset();
}
public void HardReset()
{
accumulator = 0;
delay = 0;
floatOutputTTL = 0;
frequency = 0;
msbRising = false;
noNoise = 0xFFF;
noPulse = 0xFFF;
output = 0x000;
pulse = 0xFFF;
pulseWidth = 0;
ringMsbMask = 0;
sync = false;
test = false;
wave = waveTable[0];
waveform = 0;
_accumulator = 0;
_delay = 0;
_floatOutputTtl = 0;
_frequency = 0;
_msbRising = false;
_noNoise = 0xFFF;
_noPulse = 0xFFF;
_output = 0x000;
_pulse = 0xFFF;
_pulseWidth = 0;
_ringMsbMask = 0;
_sync = false;
_test = false;
_wave = _waveTable[0];
_waveform = 0;
ResetShiftReg();
}
@ -69,24 +69,24 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
{
if (test)
if (_test)
{
if (shiftRegisterReset != 0 && --shiftRegisterReset == 0)
if (_shiftRegisterReset != 0 && --_shiftRegisterReset == 0)
{
ResetShiftReg();
}
pulse = 0xFFF;
_pulse = 0xFFF;
}
else
{
accNext = (accumulator + frequency) & 0xFFFFFF;
accBits = ~accumulator & accNext;
accumulator = accNext;
msbRising = ((accBits & 0x800000) != 0);
_accNext = (_accumulator + _frequency) & 0xFFFFFF;
_accBits = ~_accumulator & _accNext;
_accumulator = _accNext;
_msbRising = (_accBits & 0x800000) != 0;
if ((accBits & 0x080000) != 0)
delay = 2;
else if (delay != 0 && --delay == 0)
if ((_accBits & 0x080000) != 0)
_delay = 2;
else if (_delay != 0 && --_delay == 0)
ClockShiftReg();
}
}
@ -98,8 +98,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
{
shiftRegister = ((shiftRegister << 1) |
(((shiftRegister >> 22) ^ (shiftRegister >> 17)) & 0x1)
_shiftRegister = ((_shiftRegister << 1) |
(((_shiftRegister >> 22) ^ (_shiftRegister >> 17)) & 0x1)
) & 0x7FFFFF;
SetNoise();
}
@ -109,8 +109,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
{
shiftRegister = 0x7FFFFF;
shiftRegisterReset = 0;
_shiftRegister = 0x7FFFFF;
_shiftRegisterReset = 0;
SetNoise();
}
}
@ -119,16 +119,16 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
{
noise =
((shiftRegister & 0x100000) >> 9) |
((shiftRegister & 0x040000) >> 8) |
((shiftRegister & 0x004000) >> 5) |
((shiftRegister & 0x000800) >> 3) |
((shiftRegister & 0x000200) >> 2) |
((shiftRegister & 0x000020) << 1) |
((shiftRegister & 0x000004) << 3) |
((shiftRegister & 0x000001) << 4);
noNoiseOrNoise = noNoise | noise;
_noise =
((_shiftRegister & 0x100000) >> 9) |
((_shiftRegister & 0x040000) >> 8) |
((_shiftRegister & 0x004000) >> 5) |
((_shiftRegister & 0x000800) >> 3) |
((_shiftRegister & 0x000200) >> 2) |
((_shiftRegister & 0x000020) << 1) |
((_shiftRegister & 0x000004) << 3) |
((_shiftRegister & 0x000001) << 4);
_noNoiseOrNoise = _noNoise | _noise;
}
}
@ -136,17 +136,17 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
{
output &=
_output &=
0xBB5DA |
((output & 0x800) << 9) |
((output & 0x400) << 8) |
((output & 0x200) << 5) |
((output & 0x100) << 3) |
((output & 0x040) >> 1) |
((output & 0x020) >> 3) |
((output & 0x010) >> 4);
noise &= output;
noNoiseOrNoise = noNoise | noise;
((_output & 0x800) << 9) |
((_output & 0x400) << 8) |
((_output & 0x200) << 5) |
((_output & 0x100) << 3) |
((_output & 0x040) >> 1) |
((_output & 0x020) >> 3) |
((_output & 0x010) >> 4);
_noise &= _output;
_noNoiseOrNoise = _noNoise | _noise;
}
}
@ -156,47 +156,35 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
set
{
controlWavePrev = waveform;
controlTestPrev = test;
_controlWavePrev = _waveform;
_controlTestPrev = _test;
sync = ((value & 0x02) != 0);
ringMod = ((value & 0x04) != 0);
test = ((value & 0x08) != 0);
waveform = (value >> 4) & 0x0F;
wave = waveTable[waveform & 0x07];
ringMsbMask = ((~value >> 5) & (value >> 2) & 0x1) << 23;
noNoise = ((waveform & 0x8) != 0) ? 0x000 : 0xFFF;
noNoiseOrNoise = noNoise | noise;
noPulse = ((waveform & 0x4) != 0) ? 0x000 : 0xFFF;
_sync = (value & 0x02) != 0;
_ringMod = (value & 0x04) != 0;
_test = (value & 0x08) != 0;
_waveform = (value >> 4) & 0x0F;
_wave = _waveTable[_waveform & 0x07];
_ringMsbMask = ((~value >> 5) & (value >> 2) & 0x1) << 23;
_noNoise = (_waveform & 0x8) != 0 ? 0x000 : 0xFFF;
_noNoiseOrNoise = _noNoise | _noise;
_noPulse = (_waveform & 0x4) != 0 ? 0x000 : 0xFFF;
if (!controlTestPrev && test)
if (!_controlTestPrev && _test)
{
accumulator = 0;
delay = 0;
shiftRegisterReset = 0x8000;
_accumulator = 0;
_delay = 0;
_shiftRegisterReset = 0x8000;
}
else if (controlTestPrev && !test)
else if (_controlTestPrev && !_test)
{
shiftRegister = ((shiftRegister << 1) |
((~shiftRegister >> 17) & 0x1)
_shiftRegister = ((_shiftRegister << 1) |
((~_shiftRegister >> 17) & 0x1)
) & 0x7FFFFF;
SetNoise();
}
if (waveform == 0 && controlWavePrev != 0)
floatOutputTTL = 0x28000;
}
}
public int Frequency
{
get
{
return frequency;
}
set
{
frequency = value;
if (_waveform == 0 && _controlWavePrev != 0)
_floatOutputTtl = 0x28000;
}
}
@ -204,12 +192,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return (frequency & 0xFF);
return _frequency & 0xFF;
}
set
{
frequency &= 0xFF00;
frequency |= value & 0x00FF;
_frequency &= 0xFF00;
_frequency |= value & 0x00FF;
}
}
@ -217,20 +205,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return (frequency >> 8);
return _frequency >> 8;
}
set
{
frequency &= 0x00FF;
frequency |= (value & 0x00FF) << 8;
}
}
public int Oscillator
{
get
{
return output;
_frequency &= 0x00FF;
_frequency |= (value & 0x00FF) << 8;
}
}
@ -238,32 +218,20 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
{
if (waveform != 0)
if (_waveform != 0)
{
waveformIndex = (accumulator ^ (ringModSource.accumulator & ringMsbMask)) >> 12;
output = wave[waveformIndex] & (noPulse | pulse) & noNoiseOrNoise;
if (waveform > 8)
_waveformIndex = (_accumulator ^ (ringModSource._accumulator & _ringMsbMask)) >> 12;
_output = _wave[_waveformIndex] & (_noPulse | _pulse) & _noNoiseOrNoise;
if (_waveform > 8)
WriteShiftReg();
}
else
{
if (floatOutputTTL != 0 && --floatOutputTTL == 0)
output = 0x000;
if (_floatOutputTtl != 0 && --_floatOutputTtl == 0)
_output = 0x000;
}
pulse = ((accumulator >> 12) >= pulseWidth) ? 0xFFF : 0x000;
return output;
}
}
public int PulseWidth
{
get
{
return pulseWidth;
}
set
{
pulseWidth = value;
_pulse = _accumulator >> 12 >= _pulseWidth ? 0xFFF : 0x000;
return _output;
}
}
@ -271,12 +239,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return (pulseWidth & 0xFF);
return _pulseWidth & 0xFF;
}
set
{
pulseWidth &= 0x0F00;
pulseWidth |= value & 0x00FF;
_pulseWidth &= 0x0F00;
_pulseWidth |= value & 0x00FF;
}
}
@ -284,12 +252,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return (pulseWidth >> 8);
return _pulseWidth >> 8;
}
set
{
pulseWidth &= 0x00FF;
pulseWidth |= (value & 0x000F) << 8;
_pulseWidth &= 0x00FF;
_pulseWidth |= (value & 0x000F) << 8;
}
}
@ -297,7 +265,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return ringMod;
return _ringMod;
}
}
@ -305,21 +273,21 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return sync;
return _sync;
}
}
public void Synchronize(Voice target, Voice source)
{
if (msbRising && target.sync && !(sync && source.msbRising))
target.accumulator = 0;
if (_msbRising && target._sync && !(_sync && source._msbRising))
target._accumulator = 0;
}
public bool Test
{
get
{
return test;
return _test;
}
}
@ -327,7 +295,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return waveform;
return _waveform;
}
}
@ -336,7 +304,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
_wave = _waveTable[_waveform & 0x07];
}
}
}

View File

@ -3,164 +3,182 @@
using BizHawk.Common;
using BizHawk.Emulation.Common;
#pragma warning disable 649 //adelikat: Disable dumb warnings until this file is complete
#pragma warning disable 169 //adelikat: Disable dumb warnings until this file is complete
#pragma warning disable 219 //adelikat: Disable dumb warnings until this file is complete
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Sid
public sealed partial class Sid
{
/*
Commodore SID 6581/8580 core.
// ------------------------------------
Many thanks to:
- Michael Huth for die shots of the 6569R3 chip (to get ideas how to implement)
http://mail.lipsia.de/~enigma/neu/6581.html
- Kevtris for figuring out ADSR tables
http://blog.kevtris.org/?p=13
- Mixer for a lot of useful SID info
http://www.sid.fi/sidwiki/doku.php?id=sid-knowledge
- Documentation collected by the libsidplayfp team
https://sourceforge.net/projects/sidplay-residfp/
*/
public SpeexResampler resampler;
// ------------------------------------
static int[] syncNextTable = new int[] { 1, 2, 0 };
static int[] syncPrevTable = new int[] { 2, 0, 1 };
private int _cachedCycles;
private bool _disableVoice3;
private int _envelopeOutput0;
private int _envelopeOutput1;
private int _envelopeOutput2;
private readonly Envelope[] _envelopes;
[SaveState.DoNotSave] private readonly Envelope _envelope0;
[SaveState.DoNotSave] private readonly Envelope _envelope1;
[SaveState.DoNotSave] private readonly Envelope _envelope2;
private readonly bool[] _filterEnable;
private int _filterFrequency;
private int _filterResonance;
private bool _filterSelectBandPass;
private bool _filterSelectLoPass;
private bool _filterSelectHiPass;
private int _mixer;
[SaveState.DoNotSave] private readonly short[] _outputBuffer;
[SaveState.DoNotSave] private int _outputBufferIndex;
private int _potCounter;
private int _potX;
private int _potY;
private short _sample;
private int _voiceOutput0;
private int _voiceOutput1;
private int _voiceOutput2;
private readonly Voice[] _voices;
[SaveState.DoNotSave] private readonly Voice _voice0;
[SaveState.DoNotSave] private readonly Voice _voice1;
[SaveState.DoNotSave] private readonly Voice _voice2;
private int _volume;
int cachedCycles;
bool disableVoice3;
int[] envelopeOutput;
Envelope[] envelopes;
bool[] filterEnable;
int filterFrequency;
int filterResonance;
bool filterSelectBandPass;
bool filterSelectLoPass;
bool filterSelectHiPass;
int mixer;
int potCounter;
int potX;
int potY;
short sample;
int[] voiceOutput;
Voice[] voices;
int volume;
int[][] waveformTable;
public Func<int> ReadPotX;
public Func<int> ReadPotY;
public Func<byte> ReadPotX;
public Func<byte> ReadPotY;
[SaveState.DoNotSave] private readonly int _cpuCyclesNum;
[SaveState.DoNotSave] private int _sampleCyclesNum;
[SaveState.DoNotSave] private readonly int _sampleCyclesDen;
[SaveState.DoNotSave] private readonly int _sampleRate;
public Sid(int[][] newWaveformTable, uint sampleRate, uint cyclesNum, uint cyclesDen)
{
waveformTable = newWaveformTable;
public Sid(int[][] newWaveformTable, int sampleRate, int cyclesNum, int cyclesDen)
{
_sampleRate = sampleRate;
_cpuCyclesNum = cyclesNum;
_sampleCyclesDen = cyclesDen*sampleRate;
envelopes = new Envelope[3];
for (int i = 0; i < 3; i++)
envelopes[i] = new Envelope();
envelopeOutput = new int[3];
_envelopes = new Envelope[3];
for (var i = 0; i < 3; i++)
_envelopes[i] = new Envelope();
_envelope0 = _envelopes[0];
_envelope1 = _envelopes[1];
_envelope2 = _envelopes[2];
voices = new Voice[3];
for (int i = 0; i < 3; i++)
voices[i] = new Voice(newWaveformTable);
voiceOutput = new int[3];
_voices = new Voice[3];
for (var i = 0; i < 3; i++)
_voices[i] = new Voice(newWaveformTable);
_voice0 = _voices[0];
_voice1 = _voices[1];
_voice2 = _voices[2];
filterEnable = new bool[3];
for (int i = 0; i < 3; i++)
filterEnable[i] = false;
_filterEnable = new bool[3];
for (var i = 0; i < 3; i++)
_filterEnable[i] = false;
resampler = new SpeexResampler(0, cyclesNum, sampleRate * cyclesDen, cyclesNum, sampleRate * cyclesDen, null, null);
}
public void Dispose()
{
if (resampler != null)
{
resampler.Dispose();
resampler = null;
}
_outputBuffer = new short[sampleRate];
}
// ------------------------------------
public void HardReset()
{
for (int i = 0; i < 3; i++)
for (var i = 0; i < 3; i++)
{
envelopes[i].HardReset();
voices[i].HardReset();
_envelopes[i].HardReset();
_voices[i].HardReset();
}
potCounter = 0;
potX = 0;
potY = 0;
_potCounter = 0;
_potX = 0;
_potY = 0;
}
// ------------------------------------
public void ExecutePhase2()
public void ExecutePhase()
{
cachedCycles++;
_cachedCycles++;
// potentiometer values refresh every 512 cycles
if (potCounter == 0)
if (_potCounter == 0)
{
potCounter = 512;
potX = ReadPotX();
potY = ReadPotY();
Flush(); //this is here unrelated to the pots, just to keep the buffer somewhat loaded
_potCounter = 512;
_potX = ReadPotX();
_potY = ReadPotY();
}
potCounter--;
_potCounter--;
}
public void Flush()
{
while (cachedCycles > 0)
{
while (_cachedCycles > 0)
{
_cachedCycles--;
// process voices and envelopes
voices[0].ExecutePhase2();
voices[1].ExecutePhase2();
voices[2].ExecutePhase2();
envelopes[0].ExecutePhase2();
envelopes[1].ExecutePhase2();
envelopes[2].ExecutePhase2();
_voice0.ExecutePhase2();
_voice1.ExecutePhase2();
_voice2.ExecutePhase2();
_envelope0.ExecutePhase2();
_envelope1.ExecutePhase2();
_envelope2.ExecutePhase2();
// process sync
for (int i = 0; i < 3; i++)
voices[i].Synchronize(voices[syncNextTable[i]], voices[syncPrevTable[i]]);
_voice0.Synchronize(_voice1, _voice2);
_voice1.Synchronize(_voice2, _voice0);
_voice2.Synchronize(_voice0, _voice1);
// get output
voiceOutput[0] = voices[0].Output(voices[2]);
voiceOutput[1] = voices[1].Output(voices[0]);
voiceOutput[2] = voices[2].Output(voices[1]);
envelopeOutput[0] = envelopes[0].Level;
envelopeOutput[1] = envelopes[1].Level;
envelopeOutput[2] = envelopes[2].Level;
// get output
_voiceOutput0 = _voice0.Output(_voice2);
_voiceOutput1 = _voice1.Output(_voice0);
_voiceOutput2 = _voice2.Output(_voice1);
_envelopeOutput0 = _envelope0.Level;
_envelopeOutput1 = _envelope1.Level;
_envelopeOutput2 = _envelope2.Level;
mixer = ((voiceOutput[0] * envelopeOutput[0]) >> 7);
mixer += ((voiceOutput[1] * envelopeOutput[1]) >> 7);
mixer += ((voiceOutput[2] * envelopeOutput[2]) >> 7);
mixer = (mixer * volume) >> 4;
_sampleCyclesNum += _sampleCyclesDen;
if (_sampleCyclesNum >= _cpuCyclesNum)
{
_sampleCyclesNum -= _cpuCyclesNum;
_mixer = (_voiceOutput0 * _envelopeOutput0) >> 7;
_mixer += (_voiceOutput1 * _envelopeOutput1) >> 7;
_mixer += (_voiceOutput2 * _envelopeOutput2) >> 7;
_mixer = (_mixer * _volume) >> 4;
_mixer -= _volume << 8;
sample = (short)mixer;
resampler.EnqueueSample(sample, sample);
cachedCycles--;
}
}
if (_mixer > 0x7FFF)
{
_mixer = 0x7FFF;
}
if (_mixer < -0x8000)
{
_mixer = -0x8000;
}
// ----------------------------------
_sample = unchecked((short)_mixer);
if (_outputBufferIndex < _sampleRate)
{
_outputBuffer[_outputBufferIndex++] = _sample;
_outputBuffer[_outputBufferIndex++] = _sample;
}
}
}
}
public void SyncState(Serializer ser)
// ----------------------------------
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
ser.BeginSection("env0");
envelopes[0].SyncState(ser);
ser.EndSection();
ser.BeginSection("wav0");
voices[0].SyncState(ser);
ser.EndSection();
ser.BeginSection("env1");
envelopes[1].SyncState(ser);
ser.EndSection();
ser.BeginSection("wav1");
voices[1].SyncState(ser);
ser.EndSection();
ser.BeginSection("env2");
envelopes[2].SyncState(ser);
ser.EndSection();
ser.BeginSection("wav2");
voices[2].SyncState(ser);
ser.EndSection();
}
}
}

View File

@ -1,73 +0,0 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public class UserPort
{
public Func<bool> ReadCounter1;
public Func<bool> ReadCounter2;
public Func<bool> ReadHandshake;
public Func<bool> ReadSerial1;
public Func<bool> ReadSerial2;
public UserPort()
{
}
virtual public void HardReset()
{
// note: this will not disconnect any attached media
}
virtual public bool ReadAtn()
{
return true;
}
virtual public bool ReadCounter1Buffer()
{
return true;
}
virtual public bool ReadCounter2Buffer()
{
return true;
}
virtual public byte ReadData()
{
return 0xFF;
}
virtual public bool ReadFlag2()
{
return true;
}
virtual public bool ReadPA2()
{
return true;
}
virtual public bool ReadReset()
{
return true;
}
virtual public bool ReadSerial1Buffer()
{
return true;
}
virtual public bool ReadSerial2Buffer()
{
return true;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Common;
using BizHawk.Emulation.Cores.Computers.Commodore64.Media;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed partial class Via
{
private abstract class Port
{
public abstract int ReadPra(int pra, int ddra);
public abstract int ReadPrb(int prb, int ddrb);
public abstract int ReadExternalPra();
public abstract int ReadExternalPrb();
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
private sealed class DisconnectedPort : Port
{
public override int ReadPra(int pra, int ddra)
{
return (pra | ~ddra) & 0xFF;
}
public override int ReadPrb(int prb, int ddrb)
{
return (prb | ~ddrb) & 0xFF;
}
public override int ReadExternalPra()
{
return 0xFF;
}
public override int ReadExternalPrb()
{
return 0xFF;
}
}
private sealed class DriverPort : Port
{
private readonly Func<int> _readPrA;
private readonly Func<int> _readPrB;
public DriverPort(Func<int> readPrA, Func<int> readPrB)
{
_readPrA = readPrA;
_readPrB = readPrB;
}
public override int ReadPra(int pra, int ddra)
{
return _readPrA();
}
public override int ReadPrb(int prb, int ddrb)
{
return (prb & ddrb) | (_readPrB() & ~ddrb);
}
public override int ReadExternalPra()
{
return _readPrA();
}
public override int ReadExternalPrb()
{
return _readPrB();
}
}
private sealed class IecPort : Port
{
private readonly Func<bool> _readClock;
private readonly Func<bool> _readData;
private readonly Func<bool> _readAtn;
private readonly int _driveNumber;
public IecPort(Func<bool> readClock, Func<bool> readData, Func<bool> readAtn, int driveNumber)
{
_readClock = readClock;
_readData = readData;
_readAtn = readAtn;
_driveNumber = (driveNumber & 0x3) << 5;
}
public override int ReadPra(int pra, int ddra)
{
return (pra | ~ddra) & 0xFF;
}
public override int ReadPrb(int prb, int ddrb)
{
return (prb & ddrb) |
(~ddrb & 0xE5 & (
(_readClock() ? 0x04 : 0x00) |
(_readData() ? 0x01 : 0x00) |
(_readAtn() ? 0x80 : 0x00) |
_driveNumber)
);
}
public override int ReadExternalPra()
{
return 0xFF;
}
public override int ReadExternalPrb()
{
return
(_readClock() ? 0x04 : 0x00) |
(_readData() ? 0x01 : 0x00) |
(_readAtn() ? 0x80 : 0x00) |
_driveNumber;
}
}
}
}

View File

@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed partial class Via
{
public int Peek(int addr)
{
return ReadRegister(addr & 0xF);
}
public void Poke(int addr, int val)
{
WriteRegister(addr & 0xF, val);
}
public int Read(int addr)
{
addr &= 0xF;
switch (addr)
{
case 0x0:
_ifr &= 0xE7;
if (_acrPbLatchEnable)
{
return _pbLatch;
}
break;
case 0x1:
_ifr &= 0xFC;
if (_acrPaLatchEnable)
{
return _paLatch;
}
break;
case 0x4:
_ifr &= 0xBF;
break;
case 0x8:
_ifr &= 0xDF;
break;
case 0xA:
_ifr &= 0xFB;
break;
case 0xF:
if (_acrPaLatchEnable)
{
return _paLatch;
}
break;
}
return ReadRegister(addr);
}
private int ReadRegister(int addr)
{
switch (addr)
{
case 0x0:
return _port.ReadPrb(_prb, _ddrb);
case 0x1:
return _port.ReadPra(_pra, _ddra);
case 0x2:
return _ddrb;
case 0x3:
return _ddra;
case 0x4:
return _t1C & 0xFF;
case 0x5:
return (_t1C >> 8) & 0xFF;
case 0x6:
return _t1L & 0xFF;
case 0x7:
return (_t1L >> 8) & 0xFF;
case 0x8:
return _t2C & 0xFF;
case 0x9:
return (_t2C >> 8) & 0xFF;
case 0xA:
return _sr;
case 0xB:
return _acr;
case 0xC:
return _pcr;
case 0xD:
return _ifr;
case 0xE:
return _ier | 0x80;
case 0xF:
return _port.ReadPra(_pra, _ddra);
}
return 0xFF;
}
public void Write(int addr, int val)
{
addr &= 0xF;
switch (addr)
{
case 0x0:
_ifr &= 0xE7;
WriteRegister(addr, val);
break;
case 0x1:
_ifr &= 0xFC;
WriteRegister(addr, val);
break;
case 0x4:
case 0x6:
_t1L = (_t1L & 0xFF00) | (val & 0xFF);
break;
case 0x5:
_t1L = (_t1L & 0xFF) | ((val & 0xFF) << 8);
_ifr &= 0xBF;
_t1C = _t1L;
break;
case 0x7:
_t1L = (_t1L & 0xFF) | ((val & 0xFF) << 8);
break;
case 0x8:
_t2L = (_t2L & 0xFF00) | (val & 0xFF);
break;
case 0x9:
_t2L = (_t2L & 0xFF) | ((val & 0xFF) << 8);
_ifr &= 0xDF;
_t2C = _t2L;
break;
case 0xA:
_ifr &= 0xFB;
WriteRegister(addr, val);
break;
case 0xD:
_ifr &= ~val;
break;
case 0xE:
if ((val & 0x80) != 0)
_ier |= val & 0x7F;
else
_ier &= ~val;
break;
default:
WriteRegister(addr, val);
break;
}
}
private void WriteRegister(int addr, int val)
{
addr &= 0xF;
switch (addr)
{
case 0x0:
_prb = val & 0xFF;
break;
case 0x1:
case 0xF:
_pra = val & 0xFF;
break;
case 0x2:
_ddrb = val & 0xFF;
break;
case 0x3:
_ddra = val & 0xFF;
break;
case 0x4:
_t1C = (_t1C & 0xFF00) | (val & 0xFF);
break;
case 0x5:
_t1C = (_t1C & 0xFF) | ((val & 0xFF) << 8);
break;
case 0x6:
_t1L = (_t1L & 0xFF00) | (val & 0xFF);
break;
case 0x7:
_t1L = (_t1L & 0xFF) | ((val & 0xFF) << 8);
break;
case 0x8:
_t2C = (_t2C & 0xFF00) | (val & 0xFF);
break;
case 0x9:
_t2C = (_t2C & 0xFF) | ((val & 0xFF) << 8);
break;
case 0xA:
_sr = val & 0xFF;
break;
case 0xB:
_acr = val & 0xFF;
_acrPaLatchEnable = (val & 0x01) != 0;
_acrPbLatchEnable = (val & 0x02) != 0;
_acrSrControl = (val & 0x1C);
_acrT2Control = (val & 0x20);
_acrT1Control = (val & 0xC0);
break;
case 0xC:
_pcr = val & 0xFF;
_pcrCa1IntControl = _pcr & 0x01;
_pcrCa2Control = _pcr & 0x0E;
_pcrCb1IntControl = (_pcr & 0x10) >> 4;
_pcrCb2Control = (_pcr & 0xE0) >> 4;
break;
case 0xD:
_ifr = val & 0xFF;
break;
case 0xE:
_ier = val & 0xFF;
break;
}
}
[SaveState.DoNotSave]
public int DdrA
{
get { return _ddra; }
}
[SaveState.DoNotSave]
public int DdrB
{
get { return _ddrb; }
}
[SaveState.DoNotSave]
public int PrA
{
get { return _pra; }
}
[SaveState.DoNotSave]
public int PrB
{
get { return _prb; }
}
[SaveState.DoNotSave]
public int EffectivePrA
{
get { return _pra | ~_ddra; }
}
[SaveState.DoNotSave]
public int EffectivePrB
{
get { return _prb | ~_ddrb; }
}
[SaveState.DoNotSave]
public int ActualPrA
{
get { return _acrPaLatchEnable ? _paLatch : _port.ReadPra(_pra, _ddra); }
}
[SaveState.DoNotSave]
public int ActualPrB
{
get { return _acrPbLatchEnable ? _pbLatch : _port.ReadPrb(_prb, _ddrb); }
}
}
}

View File

@ -0,0 +1,248 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Common;
using BizHawk.Emulation.Cores.Computers.Commodore64.Media;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed partial class Via
{
private const int PCR_INT_CONTROL_NEGATIVE_EDGE = 0x00;
private const int PCR_INT_CONTROL_POSITIVE_EDGE = 0x01;
private const int PCR_CONTROL_INPUT_NEGATIVE_ACTIVE_EDGE = 0x00;
private const int PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE = 0x02;
private const int PCR_CONTROL_INPUT_POSITIVE_ACTIVE_EDGE = 0x04;
private const int PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE = 0x06;
private const int PCR_CONTROL_HANDSHAKE_OUTPUT = 0x08;
private const int PCR_CONTROL_PULSE_OUTPUT = 0x0A;
private const int PCR_CONTROL_LOW_OUTPUT = 0x0C;
private const int PCR_CONTROL_HIGH_OUTPUT = 0x0E;
private const int ACR_SR_CONTROL_DISABLED = 0x00;
private const int ACR_SR_CONTROL_SHIFT_IN_T2_ONCE = 0x04;
private const int ACR_SR_CONTROL_SHIFT_IN_PHI2 = 0x08;
private const int ACR_SR_CONTROL_SHIFT_IN_CLOCK = 0x0C;
private const int ACR_SR_CONTROL_SHIFT_OUT_T2 = 0x10;
private const int ACR_SR_CONTROL_SHIFT_OUT_T2_ONCE = 0x14;
private const int ACR_SR_CONTROL_SHIFT_OUT_PHI2 = 0x18;
private const int ACR_SR_CONTROL_SHIFT_OUT_CLOCK = 0x1C;
private const int ACR_T2_CONTROL_TIMED = 0x00;
private const int ACR_T2_CONTROL_COUNT_ON_PB6 = 0x20;
private const int ACR_T1_CONTROL_INTERRUPT_ON_LOAD = 0x00;
private const int ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS = 0x40;
private const int ACR_T1_CONTROL_INTERRUPT_ON_LOAD_AND_ONESHOT_PB7 = 0x80;
private const int ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7 = 0xC0;
private int _pra;
private int _ddra;
private int _prb;
private int _ddrb;
private int _t1C;
private int _t1L;
private int _t2C;
private int _t2L;
private int _sr;
private int _acr;
private int _pcr;
private int _ifr;
private int _ier;
private readonly Port _port;
private int _paLatch;
private int _pbLatch;
private int _pcrCa1IntControl;
private int _pcrCa2Control;
private int _pcrCb1IntControl;
private int _pcrCb2Control;
private bool _acrPaLatchEnable;
private bool _acrPbLatchEnable;
private int _acrSrControl;
private int _acrT1Control;
private int _acrT2Control;
private bool _ca1L;
private bool _ca2L;
private bool _cb1L;
private bool _cb2L;
private int _shiftCount;
public bool Ca1;
public bool Ca2;
public bool Cb1;
public bool Cb2;
public Via()
{
_port = new DisconnectedPort();
}
public Via(Func<int> readPrA, Func<int> readPrB)
{
_port = new DriverPort(readPrA, readPrB);
}
public Via(Func<bool> readClock, Func<bool> readData, Func<bool> readAtn, int driveNumber)
{
_port = new IecPort(readClock, readData, readAtn, driveNumber);
_ca1L = true;
}
[SaveState.DoNotSave]
public bool Irq
{
get { return (_ifr & 0x80) == 0; }
}
public void HardReset()
{
_pra = 0;
_prb = 0;
_ddra = 0;
_ddrb = 0;
_t1C = 0;
_t1L = 0;
_t2C = 0;
_t2L = 0;
_sr = 0;
_acr = 0;
_pcr = 0;
_ifr = 0;
_ier = 0;
_paLatch = 0;
_pbLatch = 0;
_pcrCa1IntControl = 0;
_pcrCa2Control = 0;
_pcrCb1IntControl = 0;
_pcrCb2Control = 0;
_acrPaLatchEnable = false;
_acrPbLatchEnable = false;
_acrSrControl = 0;
_acrT1Control = 0;
_acrT2Control = 0;
_ca1L = false;
_cb1L = false;
Ca1 = false;
Ca2 = false;
Cb1 = false;
Cb2 = false;
}
private bool ProcessC2(bool c2, int control)
{
switch (control)
{
case PCR_CONTROL_INPUT_NEGATIVE_ACTIVE_EDGE:
return c2;
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE:
return c2;
case PCR_CONTROL_INPUT_POSITIVE_ACTIVE_EDGE:
return c2;
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE:
return c2;
case PCR_CONTROL_HANDSHAKE_OUTPUT:
return c2;
case PCR_CONTROL_PULSE_OUTPUT:
return c2;
case PCR_CONTROL_LOW_OUTPUT:
return false;
case PCR_CONTROL_HIGH_OUTPUT:
return true;
}
return c2;
}
public void ExecutePhase()
{
_t1C--;
if (_t1C < 0)
{
if (_acrT1Control == ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS ||
_acrT1Control == ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7)
{
_t1C = _t1L;
}
_ifr |= 0x40;
}
if (_acrT2Control == ACR_T2_CONTROL_TIMED)
{
_t2C--;
if (_t2C < 0)
{
_ifr |= 0x20;
_t2C = _t2L;
}
}
Ca2 = ProcessC2(Ca2, _pcrCa2Control);
Cb2 = ProcessC2(Cb2, _pcrCb2Control);
// unknown behavior
if (_acrT1Control != ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS &&
_acrT1Control != ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7)
{
// unknown ACR T1 control
}
if (_acrT2Control != ACR_T2_CONTROL_TIMED)
{
// unknown ACR T2 control
}
// interrupt generation
if ((_pcrCb1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Cb1 && !_cb1L) ||
(_pcrCb1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Cb1 && _cb1L))
{
_ifr |= 0x01;
if (_acrPbLatchEnable)
{
_pbLatch = _port.ReadExternalPrb();
}
}
if ((_pcrCa1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Ca1 && !_ca1L) ||
(_pcrCa1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Ca1 && _ca1L))
{
_ifr |= 0x02;
if (_acrPaLatchEnable)
{
_paLatch = _port.ReadExternalPra();
}
}
switch (_acrSrControl)
{
case ACR_SR_CONTROL_DISABLED:
_ifr &= 0xFB;
break;
default:
break;
}
if ((_ifr & _ier & 0x7F) != 0)
{
_ifr |= 0x80;
}
else
{
_ifr &= 0x7F;
}
_ca1L = Ca1;
_ca2L = Ca2;
_cb1L = Cb1;
_cb2L = Cb2;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -1,259 +1,259 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Vic
public sealed partial class Vic
{
const int baResetCounter = 7;
const int pipelineUpdateVc = 1;
const int pipelineChkSprCrunch = 2;
const int pipelineUpdateMcBase = 4;
const int pipelineChkBrdL1 = 8;
const int pipelineChkBrdL0 = 16;
const int pipelineChkSprDma = 32;
const int pipelineChkBrdR0 = 64;
const int pipelineChkSprExp = 128;
const int pipelineChkBrdR1 = 256;
const int pipelineChkSprDisp = 512;
const int pipelineUpdateRc = 1024;
const int pipelineHBlankL = 0x10000000;
const int pipelineHBlankR = 0x20000000;
const int pipelineHoldX = 0x40000000;
const int rasterIrqLine0Cycle = 1;
const int rasterIrqLineXCycle = 0;
private const int BaResetCounter = 4;
private const int PipelineUpdateVc = 0x00000001; // vc/rc rule 2
private const int PipelineSpriteCrunch = 0x00000002;
private const int PipelineUpdateMcBase = 0x00000004;
private const int PipelineBorderLeft1 = 0x00000008;
private const int PipelineBorderLeft0 = 0x00000010;
private const int PipelineSpriteDma = 0x00000020; // sprite rule 3
private const int PipelineBorderRight0 = 0x00000040;
private const int PipelineSpriteExpansion = 0x00000080; // sprite rule 2
private const int PipelineBorderRight1 = 0x00000100;
private const int PipelineSpriteDisplay = 0x00000200; // sprite rule 4
private const int PipelineUpdateRc = 0x00000400; // vc/rc rule 5
private const int PipelineHoldX = 0x40000000;
private const int RasterIrqLine0Cycle = 2;
private const int RasterIrqLineXCycle = 1;
private const int FetchTypeSprite = 0x0000;
private const int FetchTypeRefresh = 0x0100;
private const int FetchTypeColor = 0x0200;
private const int FetchTypeGraphics = 0x0300;
private const int FetchTypeIdle = 0x0400;
private const int FetchTypeNone = 0x0500;
private const int BaTypeNone = 0x0888;
private const int BaTypeCharacter = 0x1000;
private const int BaTypeMaskSprite0 = 0x000F;
private const int BaTypeMaskSprite1 = 0x00F0;
private const int BaTypeMaskSprite2 = 0x0F00;
private const int AddressMask = 0x3FFF;
private const int AddressMaskEc = 0x39FF;
private const int AddressMaskRefresh = 0x3F00;
private int parseaddr;
private int parsecycleBAsprite0;
private int parsecycleBAsprite1;
private int parsecycleBAsprite2;
private int parsecycleFetchSpriteIndex;
private int parsefetch;
private int parsefetchType;
private int parseba;
private int parseact;
[SaveState.DoNotSave] private int _parseAddr;
[SaveState.DoNotSave] private int _parseCycleBaSprite0;
[SaveState.DoNotSave] private int _parseCycleBaSprite1;
[SaveState.DoNotSave] private int _parseCycleBaSprite2;
[SaveState.DoNotSave] private int _parseCycleFetchSpriteIndex;
[SaveState.DoNotSave] private int _parseFetch;
[SaveState.DoNotSave] private int _parseFetchType;
[SaveState.DoNotSave] private int _parseBa;
[SaveState.DoNotSave] private int _parseAct;
private bool _parseIsSprCrunch;
private void ParseCycle()
{
parseaddr = 0x3FFF;
parsefetch = pipeline[1][cycleIndex];
parseba = pipeline[2][cycleIndex];
parseact = pipeline[3][cycleIndex];
// initialization
_parseAddr = AddressMask;
_parseFetch = _fetchPipeline[_cycleIndex];
_parseBa = _baPipeline[_cycleIndex];
_parseAct = _actPipeline[_cycleIndex];
// apply X location
rasterX = pipeline[0][cycleIndex];
rasterXHold = ((parseact & pipelineHoldX) != 0);
_rasterX = _rasterXPipeline[_cycleIndex];
_rasterXHold = (_parseAct & PipelineHoldX) != 0;
// perform fetch
parsefetchType = parsefetch & 0xFF00;
if (parsefetchType == 0x100)
_parseFetchType = _parseFetch & 0xFF00;
switch (_parseFetchType)
{
// fetch R
refreshCounter = (refreshCounter - 1) & 0xFF;
parseaddr = (0x3F00 | refreshCounter);
ReadMemory(parseaddr);
}
else if (parsefetchType == 0x200)
{
delayC = xScroll;
if (!idle)
{
if (badline)
{
parseaddr = (pointerVM | vc);
dataC = ReadMemory(parseaddr);
dataC |= ((int)ReadColorRam(parseaddr) & 0xF) << 8;
bufferC[vmli] = dataC;
}
else
{
dataC = bufferC[vmli];
}
}
else
{
dataC = 0;
bufferC[vmli] = dataC;
}
srC <<= 12;
srC |= dataC;
}
else if (parsefetchType == 0x300)
{
// fetch G
if (idle)
parseaddr = 0x3FFF;
else
{
if (bitmapMode)
parseaddr = (rc | (vc << 3) | ((pointerCB & 0x4) << 11));
else
parseaddr = (rc | ((dataC & 0xFF) << 3) | (pointerCB << 11));
}
if (extraColorMode)
parseaddr &= 0x39FF;
dataG = ReadMemory(parseaddr);
sr |= dataG << (7 - xScroll);
srSync |= 0xAA << (7 - xScroll);
if (!idle)
{
bufferG[vmli] = dataG;
vmli = (vmli + 1) & 0x3F;
vc = (vc + 1) & 0x3FF;
}
}
else if (parsefetchType == 0x400)
{
// fetch I
parseaddr = (extraColorMode ? 0x39FF : 0x3FFF);
dataG = ReadMemory(parseaddr);
}
else if (parsefetchType == 0x500)
{
// fetch none
}
else
{
parsecycleFetchSpriteIndex = (parsefetch & 0x7);
if ((parsefetch & 0xF0) == 0) // sprite rule 5
{
// fetch P
parseaddr = (0x3F8 | pointerVM | parsecycleFetchSpriteIndex);
sprites[parsecycleFetchSpriteIndex].pointer = ReadMemory(parseaddr);
sprites[parsecycleFetchSpriteIndex].shiftEnable = false;
}
else
{
// fetch S
var spr = sprites[parsecycleFetchSpriteIndex];
if (spr.dma)
case FetchTypeColor:
// fetch C
if (!_idle)
{
if (_badline)
{
_parseAddr = _pointerVm | _vc;
_dataC = ReadMemory(_parseAddr);
_dataC |= (ReadColorRam(_parseAddr) & 0xF) << 8;
_bufferC[_vmli] = _dataC;
}
else
{
_dataC = _bufferC[_vmli];
}
}
else
{
_dataC = 0;
_bufferC[_vmli] = _dataC;
}
_srColorSync |= 0x01 << (7 - _xScroll);
break;
case FetchTypeGraphics:
// fetch G
if (!_idle)
{
if (_bitmapMode)
_parseAddr = _rc | (_vc << 3) | ((_pointerCb & 0x4) << 11);
else
_parseAddr = _rc | ((_dataC & 0xFF) << 3) | (_pointerCb << 11);
}
if (_extraColorModeBuffer)
_parseAddr &= AddressMaskEc;
_dataG = ReadMemory(_parseAddr);
_sr |= _dataG << (7 - _xScroll);
_srSync |= 0xAA << (7 - _xScroll);
if (!_idle)
{
parseaddr = (spr.mc | (spr.pointer << 6));
spr.sr |= (int)(ReadMemory(parseaddr)) << ((0x30 - (parsefetch & 0x30)) >> 1);
spr.mc++;
spr.loaded |= 0x800000;
_bufferG[_vmli] = _dataG;
_vmli = (_vmli + 1) & 0x3F;
_vc = (_vc + 1) & 0x3FF;
}
break;
case FetchTypeNone:
// fetch none
break;
case FetchTypeRefresh:
// fetch R
_refreshCounter = (_refreshCounter - 1) & 0xFF;
_parseAddr = AddressMaskRefresh | _refreshCounter;
ReadMemory(_parseAddr);
break;
case FetchTypeIdle:
// fetch I
ReadMemory(AddressMask);
break;
default:
_parseCycleFetchSpriteIndex = _parseFetch & 0x7;
if ((_parseFetch & 0xF0) == 0) // sprite rule 5
{
// fetch P
_parseAddr = 0x3F8 | _pointerVm | _parseCycleFetchSpriteIndex;
_sprites[_parseCycleFetchSpriteIndex].Pointer = ReadMemory(_parseAddr);
_sprites[_parseCycleFetchSpriteIndex].ShiftEnable = false;
}
else
{
// fetch S
var spr = _sprites[_parseCycleFetchSpriteIndex];
if (spr.Dma)
{
_parseAddr = spr.Mc | (spr.Pointer << 6);
spr.Sr |= ReadMemory(_parseAddr) << ((0x30 - (_parseFetch & 0x30)) >> 1);
spr.Mc++;
spr.Loaded |= 0x800000;
}
else if ((_parseFetch & 0xF0) == 0x20)
{
ReadMemory(AddressMask);
}
}
}
}
// perform BA flag manipulation
if (parseba == 0x0000)
{
pinBA = true;
}
else if (parseba == 0x1000)
{
pinBA = !badline;
}
else
{
parsecycleBAsprite0 = (parseba & 0x000F);
parsecycleBAsprite1 = (parseba & 0x00F0) >> 4;
parsecycleBAsprite2 = (parseba & 0x0F00) >> 8;
if ((parsecycleBAsprite0 < 8 && sprites[parsecycleBAsprite0].dma) ||
(parsecycleBAsprite1 < 8 && sprites[parsecycleBAsprite1].dma) ||
(parsecycleBAsprite2 < 8 && sprites[parsecycleBAsprite2].dma))
pinBA = false;
else
pinBA = true;
break;
}
// perform actions
borderCheckLEnable = ((parseact & (pipelineChkBrdL0 | pipelineChkBrdL1)) != 0);
borderCheckREnable = ((parseact & (pipelineChkBrdR0 | pipelineChkBrdR1)) != 0);
hblankCheckEnableL = ((parseact & pipelineHBlankL) != 0);
hblankCheckEnableR = ((parseact & pipelineHBlankR) != 0);
_borderCheckLEnable = (_parseAct & (PipelineBorderLeft0 | PipelineBorderLeft1)) != 0;
_borderCheckREnable = (_parseAct & (PipelineBorderRight0 | PipelineBorderRight1)) != 0;
foreach (var spr in sprites)
foreach (var spr in _sprites) // sprite rule 1
{
if (!spr.yExpand)
spr.yCrunch = true;
if (!spr.YExpand)
spr.YCrunch = true;
}
if ((parseact & pipelineChkSprExp) != 0)
{
foreach (var spr in sprites)
{
if (spr.yExpand)
spr.yCrunch ^= true;
}
}
if ((parseact & pipelineChkSprDma) != 0)
{
foreach (var spr in sprites)
{
if (spr.enable && spr.y == (rasterLine & 0xFF) && !spr.dma)
{
spr.dma = true;
spr.mcbase = 0;
spr.yCrunch = !spr.yExpand;
}
}
}
if ((parseact & pipelineChkSprDisp) != 0)
{
foreach (var spr in sprites)
{
spr.mc = spr.mcbase;
if (spr.dma && spr.y == (rasterLine & 0xFF))
{
spr.display = true;
}
else if (!spr.dma)
{
spr.display = false;
}
}
}
if ((parseact & pipelineChkSprCrunch) != 0)
{
// not sure if anything has to go here,
// some sources say yes, some say no...
}
if ((parseact & pipelineUpdateMcBase) != 0)
{
foreach (var spr in sprites)
{
if (spr.yCrunch)
{
spr.mcbase = spr.mc;
if (spr.mcbase == 63)
{
if (!spr.yCrunch)
{
}
spr.dma = false;
}
}
if ((_parseAct & PipelineUpdateMcBase) != 0) // VIC addendum sprite rule 7
{
foreach (var spr in _sprites)
{
if (spr.YCrunch)
{
spr.Mcbase = spr.Mc;
if (spr.Mcbase == 63)
{
spr.Dma = false;
}
}
}
}
}
if ((parseact & pipelineUpdateRc) != 0) // VC/RC rule 5
if ((_parseAct & PipelineSpriteDma) != 0) // sprite rule 3
{
if (rc == 7)
foreach (var spr in _sprites)
{
idle = true;
vcbase = vc;
if (spr.Enable && spr.Y == (_rasterLine & 0xFF) && !spr.Dma)
{
spr.Dma = true;
spr.Mcbase = 0;
spr.YCrunch = true;
}
}
if (!idle)
rc = (rc + 1) & 0x7;
}
if ((parseact & pipelineUpdateVc) != 0) // VC/RC rule 2
if ((_parseAct & PipelineSpriteExpansion) != 0) // sprite rule 2
{
foreach (var spr in _sprites)
{
if (spr.Dma && spr.YExpand)
spr.YCrunch ^= true;
}
}
if ((_parseAct & PipelineSpriteDisplay) != 0) // VIC addendum on sprite rule 4
{
vc = vcbase;
vmli = 0;
if (badline)
rc = 0;
foreach (var spr in _sprites)
{
spr.Mc = spr.Mcbase;
if (spr.Dma)
{
if (spr.Enable && spr.Y == (_rasterLine & 0xFF))
spr.Display = true;
}
else
{
spr.Display = false;
}
}
}
cycleIndex++;
_parseIsSprCrunch = (_parseAct & PipelineSpriteCrunch) != 0; // VIC addendum sprite rule 7
if ((_parseAct & PipelineUpdateVc) != 0) // VC/RC rule 2
{
_vc = _vcbase;
_srColorIndexLatch = 0;
_vmli = 0;
if (_badline)
_rc = 0;
}
if ((_parseAct & PipelineUpdateRc) != 0) // VC/RC rule 5
{
if (_rc == 7)
{
_idle = true;
_vcbase = _vc;
}
if (!_idle || _badline)
{
_rc = (_rc + 1) & 0x7;
_idle = false;
}
}
// perform BA flag manipulation
_pinBa = true;
switch (_parseBa)
{
case BaTypeNone:
break;
case BaTypeCharacter:
_pinBa = !_badline;
break;
default:
_parseCycleBaSprite0 = _parseBa & BaTypeMaskSprite0;
_parseCycleBaSprite1 = (_parseBa & BaTypeMaskSprite1) >> 4;
_parseCycleBaSprite2 = (_parseBa & BaTypeMaskSprite2) >> 8;
if ((_parseCycleBaSprite0 < 8 && _sprites[_parseCycleBaSprite0].Dma) ||
(_parseCycleBaSprite1 < 8 && _sprites[_parseCycleBaSprite1].Dma) ||
(_parseCycleBaSprite2 < 8 && _sprites[_parseCycleBaSprite2].Dma))
_pinBa = false;
break;
}
_cycleIndex++;
}
}
}

View File

@ -1,56 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Vic
public sealed partial class Vic
{
public byte Peek(long addr)
public int Peek(int addr)
{
return ReadRegister((int)(addr & 0x3F));
return ReadRegister(addr & 0x3F);
}
public void Poke(long addr, byte val)
public void Poke(int addr, int val)
{
WriteRegister((int)(addr & 0x3F), val);
WriteRegister(addr & 0x3F, val);
}
public byte Peek(int addr)
public int Read(int addr)
{
return ReadRegister((int)(addr & 0x3F));
}
public void Poke(int addr, byte val)
{
WriteRegister((int)(addr & 0x3F), val);
}
public byte Read(int addr)
{
byte result;
addr &= 0x3F;
switch (addr)
{
case 0x1E:
case 0x1F:
// reading clears these
result = ReadRegister(addr);
WriteRegister(addr, 0);
break;
_spriteSpriteCollisionClearPending = true;
return ReadRegister(addr);
case 0x1F:
_spriteBackgroundCollisionClearPending = true;
return ReadRegister(addr);
default:
result = ReadRegister((addr & 0x3F));
break;
return ReadRegister(addr);
}
return result;
}
private byte ReadRegister(int addr)
private int ReadRegister(int addr)
{
byte result = 0xFF; //unused bit value
switch (addr)
{
case 0x00:
@ -61,8 +41,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
case 0x0A:
case 0x0C:
case 0x0E:
result = (byte)(sprites[addr >> 1].x & 0xFF);
break;
return _sprites[addr >> 1].X & 0xFF;
case 0x01:
case 0x03:
case 0x05:
@ -71,185 +50,124 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
case 0x0B:
case 0x0D:
case 0x0F:
result = (byte)(sprites[addr >> 1].y & 0xFF);
break;
return _sprites[addr >> 1].Y & 0xFF;
case 0x10:
result = (byte)(
((sprites[0].x >> 8) & 0x01) |
((sprites[1].x >> 7) & 0x02) |
((sprites[2].x >> 6) & 0x04) |
((sprites[3].x >> 5) & 0x08) |
((sprites[4].x >> 4) & 0x10) |
((sprites[5].x >> 3) & 0x20) |
((sprites[6].x >> 2) & 0x40) |
((sprites[7].x >> 1) & 0x80)
);
break;
return ((_sprite0.X >> 8) & 0x01) |
((_sprite1.X >> 7) & 0x02) |
((_sprite2.X >> 6) & 0x04) |
((_sprite3.X >> 5) & 0x08) |
((_sprite4.X >> 4) & 0x10) |
((_sprite5.X >> 3) & 0x20) |
((_sprite6.X >> 2) & 0x40) |
((_sprite7.X >> 1) & 0x80);
case 0x11:
result = (byte)(
(yScroll & 0x7) |
(rowSelect ? 0x08 : 0x00) |
(displayEnable ? 0x10 : 0x00) |
(bitmapMode ? 0x20 : 0x00) |
(extraColorMode ? 0x40 : 0x00) |
((rasterLine & 0x100) >> 1)
);
break;
return (_yScroll & 0x7) |
(_rowSelect ? 0x08 : 0x00) |
(_displayEnable ? 0x10 : 0x00) |
(_bitmapMode ? 0x20 : 0x00) |
(_extraColorMode ? 0x40 : 0x00) |
((_rasterLine & 0x100) >> 1);
case 0x12:
result = (byte)(rasterLine & 0xFF);
break;
return _rasterLine & 0xFF;
case 0x13:
result = (byte)(lightPenX & 0xFF);
break;
return _lightPenX & 0xFF;
case 0x14:
result = (byte)(lightPenY & 0xFF);
break;
return _lightPenY & 0xFF;
case 0x15:
result = (byte)(
(sprites[0].enable ? 0x01 : 0x00) |
(sprites[1].enable ? 0x02 : 0x00) |
(sprites[2].enable ? 0x04 : 0x00) |
(sprites[3].enable ? 0x08 : 0x00) |
(sprites[4].enable ? 0x10 : 0x00) |
(sprites[5].enable ? 0x20 : 0x00) |
(sprites[6].enable ? 0x40 : 0x00) |
(sprites[7].enable ? 0x80 : 0x00)
);
break;
return (_sprite0.Enable ? 0x01 : 0x00) |
(_sprite1.Enable ? 0x02 : 0x00) |
(_sprite2.Enable ? 0x04 : 0x00) |
(_sprite3.Enable ? 0x08 : 0x00) |
(_sprite4.Enable ? 0x10 : 0x00) |
(_sprite5.Enable ? 0x20 : 0x00) |
(_sprite6.Enable ? 0x40 : 0x00) |
(_sprite7.Enable ? 0x80 : 0x00);
case 0x16:
result &= 0xC0;
result |= (byte)(
(xScroll & 0x7) |
(columnSelect ? 0x08 : 0x00) |
(multicolorMode ? 0x10 : 0x00)
);
break;
return 0xC0 | (_xScroll & 0x7) |
(_columnSelect ? 0x08 : 0x00) |
(_multicolorMode ? 0x10 : 0x00);
case 0x17:
result = (byte)(
(sprites[0].yExpand ? 0x01 : 0x00) |
(sprites[1].yExpand ? 0x02 : 0x00) |
(sprites[2].yExpand ? 0x04 : 0x00) |
(sprites[3].yExpand ? 0x08 : 0x00) |
(sprites[4].yExpand ? 0x10 : 0x00) |
(sprites[5].yExpand ? 0x20 : 0x00) |
(sprites[6].yExpand ? 0x40 : 0x00) |
(sprites[7].yExpand ? 0x80 : 0x00)
);
break;
return (_sprite0.YExpand ? 0x01 : 0x00) |
(_sprite1.YExpand ? 0x02 : 0x00) |
(_sprite2.YExpand ? 0x04 : 0x00) |
(_sprite3.YExpand ? 0x08 : 0x00) |
(_sprite4.YExpand ? 0x10 : 0x00) |
(_sprite5.YExpand ? 0x20 : 0x00) |
(_sprite6.YExpand ? 0x40 : 0x00) |
(_sprite7.YExpand ? 0x80 : 0x00);
case 0x18:
result &= 0x01;
result |= (byte)(
((pointerVM & 0x3C00) >> 6) |
((pointerCB & 0x7) << 1)
);
break;
return 0x01 | ((_pointerVm & 0x3C00) >> 6) |
((_pointerCb & 0x7) << 1);
case 0x19:
result &= 0x70;
result |= (byte)(
(intRaster ? 0x01 : 0x00) |
(intSpriteDataCollision ? 0x02 : 0x00) |
(intSpriteCollision ? 0x04 : 0x00) |
(intLightPen ? 0x08 : 0x00) |
(pinIRQ ? 0x00 : 0x80)
);
break;
return 0x70 | (_intRaster ? 0x01 : 0x00) |
(_intSpriteDataCollision ? 0x02 : 0x00) |
(_intSpriteCollision ? 0x04 : 0x00) |
(_intLightPen ? 0x08 : 0x00) |
(_pinIrq ? 0x00 : 0x80);
case 0x1A:
result &= 0xF0;
result |= (byte)(
(enableIntRaster ? 0x01 : 0x00) |
(enableIntSpriteDataCollision ? 0x02 : 0x00) |
(enableIntSpriteCollision ? 0x04 : 0x00) |
(enableIntLightPen ? 0x08 : 0x00)
);
break;
return 0xF0 | (_enableIntRaster ? 0x01 : 0x00) |
(_enableIntSpriteDataCollision ? 0x02 : 0x00) |
(_enableIntSpriteCollision ? 0x04 : 0x00) |
(_enableIntLightPen ? 0x08 : 0x00);
case 0x1B:
result = (byte)(
(sprites[0].priority ? 0x01 : 0x00) |
(sprites[1].priority ? 0x02 : 0x00) |
(sprites[2].priority ? 0x04 : 0x00) |
(sprites[3].priority ? 0x08 : 0x00) |
(sprites[4].priority ? 0x10 : 0x00) |
(sprites[5].priority ? 0x20 : 0x00) |
(sprites[6].priority ? 0x40 : 0x00) |
(sprites[7].priority ? 0x80 : 0x00)
);
break;
return (_sprite0.Priority ? 0x01 : 0x00) |
(_sprite1.Priority ? 0x02 : 0x00) |
(_sprite2.Priority ? 0x04 : 0x00) |
(_sprite3.Priority ? 0x08 : 0x00) |
(_sprite4.Priority ? 0x10 : 0x00) |
(_sprite5.Priority ? 0x20 : 0x00) |
(_sprite6.Priority ? 0x40 : 0x00) |
(_sprite7.Priority ? 0x80 : 0x00);
case 0x1C:
result = (byte)(
(sprites[0].multicolor ? 0x01 : 0x00) |
(sprites[1].multicolor ? 0x02 : 0x00) |
(sprites[2].multicolor ? 0x04 : 0x00) |
(sprites[3].multicolor ? 0x08 : 0x00) |
(sprites[4].multicolor ? 0x10 : 0x00) |
(sprites[5].multicolor ? 0x20 : 0x00) |
(sprites[6].multicolor ? 0x40 : 0x00) |
(sprites[7].multicolor ? 0x80 : 0x00)
);
break;
return (_sprite0.Multicolor ? 0x01 : 0x00) |
(_sprite1.Multicolor ? 0x02 : 0x00) |
(_sprite2.Multicolor ? 0x04 : 0x00) |
(_sprite3.Multicolor ? 0x08 : 0x00) |
(_sprite4.Multicolor ? 0x10 : 0x00) |
(_sprite5.Multicolor ? 0x20 : 0x00) |
(_sprite6.Multicolor ? 0x40 : 0x00) |
(_sprite7.Multicolor ? 0x80 : 0x00);
case 0x1D:
result = (byte)(
(sprites[0].xExpand ? 0x01 : 0x00) |
(sprites[1].xExpand ? 0x02 : 0x00) |
(sprites[2].xExpand ? 0x04 : 0x00) |
(sprites[3].xExpand ? 0x08 : 0x00) |
(sprites[4].xExpand ? 0x10 : 0x00) |
(sprites[5].xExpand ? 0x20 : 0x00) |
(sprites[6].xExpand ? 0x40 : 0x00) |
(sprites[7].xExpand ? 0x80 : 0x00)
);
break;
return (_sprite0.XExpand ? 0x01 : 0x00) |
(_sprite1.XExpand ? 0x02 : 0x00) |
(_sprite2.XExpand ? 0x04 : 0x00) |
(_sprite3.XExpand ? 0x08 : 0x00) |
(_sprite4.XExpand ? 0x10 : 0x00) |
(_sprite5.XExpand ? 0x20 : 0x00) |
(_sprite6.XExpand ? 0x40 : 0x00) |
(_sprite7.XExpand ? 0x80 : 0x00);
case 0x1E:
result = (byte)(
(sprites[0].collideSprite ? 0x01 : 0x00) |
(sprites[1].collideSprite ? 0x02 : 0x00) |
(sprites[2].collideSprite ? 0x04 : 0x00) |
(sprites[3].collideSprite ? 0x08 : 0x00) |
(sprites[4].collideSprite ? 0x10 : 0x00) |
(sprites[5].collideSprite ? 0x20 : 0x00) |
(sprites[6].collideSprite ? 0x40 : 0x00) |
(sprites[7].collideSprite ? 0x80 : 0x00)
);
break;
return (_sprite0.CollideSprite ? 0x01 : 0x00) |
(_sprite1.CollideSprite ? 0x02 : 0x00) |
(_sprite2.CollideSprite ? 0x04 : 0x00) |
(_sprite3.CollideSprite ? 0x08 : 0x00) |
(_sprite4.CollideSprite ? 0x10 : 0x00) |
(_sprite5.CollideSprite ? 0x20 : 0x00) |
(_sprite6.CollideSprite ? 0x40 : 0x00) |
(_sprite7.CollideSprite ? 0x80 : 0x00);
case 0x1F:
result = (byte)(
(sprites[0].collideData ? 0x01 : 0x00) |
(sprites[1].collideData ? 0x02 : 0x00) |
(sprites[2].collideData ? 0x04 : 0x00) |
(sprites[3].collideData ? 0x08 : 0x00) |
(sprites[4].collideData ? 0x10 : 0x00) |
(sprites[5].collideData ? 0x20 : 0x00) |
(sprites[6].collideData ? 0x40 : 0x00) |
(sprites[7].collideData ? 0x80 : 0x00)
);
break;
return (_sprite0.CollideData ? 0x01 : 0x00) |
(_sprite1.CollideData ? 0x02 : 0x00) |
(_sprite2.CollideData ? 0x04 : 0x00) |
(_sprite3.CollideData ? 0x08 : 0x00) |
(_sprite4.CollideData ? 0x10 : 0x00) |
(_sprite5.CollideData ? 0x20 : 0x00) |
(_sprite6.CollideData ? 0x40 : 0x00) |
(_sprite7.CollideData ? 0x80 : 0x00);
case 0x20:
result &= 0xF0;
result |= (byte)(borderColor & 0x0F);
break;
return 0xF0 | _borderColor & 0x0F;
case 0x21:
result &= 0xF0;
result |= (byte)(backgroundColor0 & 0x0F);
break;
return 0xF0 | _backgroundColor0 & 0x0F;
case 0x22:
result &= 0xF0;
result |= (byte)(backgroundColor1 & 0x0F);
break;
return 0xF0 | _backgroundColor1 & 0x0F;
case 0x23:
result &= 0xF0;
result |= (byte)(backgroundColor2 & 0x0F);
break;
return 0xF0 | _backgroundColor2 & 0x0F;
case 0x24:
result &= 0xF0;
result |= (byte)(backgroundColor3 & 0x0F);
break;
return 0xF0 | _backgroundColor3 & 0x0F;
case 0x25:
result &= 0xF0;
result |= (byte)(spriteMulticolor0 & 0x0F);
break;
return 0xF0 | _spriteMulticolor0 & 0x0F;
case 0x26:
result &= 0xF0;
result |= (byte)(spriteMulticolor1 & 0x0F);
break;
return 0xF0 | _spriteMulticolor1 & 0x0F;
case 0x27:
case 0x28:
case 0x29:
@ -258,37 +176,44 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
case 0x2C:
case 0x2D:
case 0x2E:
result &= 0xF0;
result |= (byte)(sprites[addr - 0x27].color & 0xF);
break;
default:
// not connected
break;
return 0xF0 | _sprites[addr - 0x27].Color & 0xF;
default:
return 0xFF;
}
return result;
}
public void Write(int addr, byte val)
public void Write(int addr, int val)
{
addr &= 0x3F;
switch (addr)
{
case 0x17:
// vic-ii addendum rule 7
foreach (var spr in _sprites)
{
if ((val & 1) == 0 && !spr.YCrunch)
{
if (_parseIsSprCrunch)
{
spr.Mc = (0x2a & spr.Mcbase & spr.Mc) | (0x15 & (spr.Mcbase | spr.Mc));
}
spr.YCrunch = true;
}
}
WriteRegister(addr, val);
break;
case 0x19:
// interrupts are cleared by writing a 1
if ((val & 0x01) != 0)
intRaster = false;
_intRaster = false;
if ((val & 0x02) != 0)
intSpriteDataCollision = false;
_intSpriteDataCollision = false;
if ((val & 0x04) != 0)
intSpriteCollision = false;
_intSpriteCollision = false;
if ((val & 0x08) != 0)
intLightPen = false;
_intLightPen = false;
UpdatePins();
break;
case 0x1A:
WriteRegister(addr, val);
break;
case 0x1E:
case 0x1F:
// can't write to these
@ -318,7 +243,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
}
}
private void WriteRegister(int addr, byte val)
private void WriteRegister(int addr, int val)
{
switch (addr)
{
@ -330,8 +255,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
case 0x0A:
case 0x0C:
case 0x0E:
sprites[addr >> 1].x &= 0x100;
sprites[addr >> 1].x |= val;
_sprites[addr >> 1].X &= 0x100;
_sprites[addr >> 1].X |= val;
break;
case 0x01:
case 0x03:
@ -341,154 +266,154 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
case 0x0B:
case 0x0D:
case 0x0F:
sprites[addr >> 1].y = val;
_sprites[addr >> 1].Y = val;
break;
case 0x10:
sprites[0].x = (sprites[0].x & 0xFF) | ((val & 0x01) << 8);
sprites[1].x = (sprites[1].x & 0xFF) | ((val & 0x02) << 7);
sprites[2].x = (sprites[2].x & 0xFF) | ((val & 0x04) << 6);
sprites[3].x = (sprites[3].x & 0xFF) | ((val & 0x08) << 5);
sprites[4].x = (sprites[4].x & 0xFF) | ((val & 0x10) << 4);
sprites[5].x = (sprites[5].x & 0xFF) | ((val & 0x20) << 3);
sprites[6].x = (sprites[6].x & 0xFF) | ((val & 0x40) << 2);
sprites[7].x = (sprites[7].x & 0xFF) | ((val & 0x80) << 1);
_sprite0.X = (_sprite0.X & 0xFF) | ((val & 0x01) << 8);
_sprite1.X = (_sprite1.X & 0xFF) | ((val & 0x02) << 7);
_sprite2.X = (_sprite2.X & 0xFF) | ((val & 0x04) << 6);
_sprite3.X = (_sprite3.X & 0xFF) | ((val & 0x08) << 5);
_sprite4.X = (_sprite4.X & 0xFF) | ((val & 0x10) << 4);
_sprite5.X = (_sprite5.X & 0xFF) | ((val & 0x20) << 3);
_sprite6.X = (_sprite6.X & 0xFF) | ((val & 0x40) << 2);
_sprite7.X = (_sprite7.X & 0xFF) | ((val & 0x80) << 1);
break;
case 0x11:
yScroll = (val & 0x07);
rowSelect = ((val & 0x08) != 0);
displayEnable = ((val & 0x10) != 0);
bitmapMode = ((val & 0x20) != 0);
extraColorMode = ((val & 0x40) != 0);
rasterInterruptLine &= 0xFF;
rasterInterruptLine |= (val & 0x80) << 1;
_yScroll = val & 0x07;
_rowSelect = (val & 0x08) != 0;
_displayEnable = (val & 0x10) != 0;
_bitmapMode = (val & 0x20) != 0;
_extraColorMode = (val & 0x40) != 0;
_rasterInterruptLine &= 0xFF;
_rasterInterruptLine |= (val & 0x80) << 1;
UpdateBorder();
UpdateVideoMode();
break;
case 0x12:
rasterInterruptLine &= 0x100;
rasterInterruptLine |= val;
_rasterInterruptLine &= 0x100;
_rasterInterruptLine |= val;
break;
case 0x13:
lightPenX = val;
_lightPenX = val;
break;
case 0x14:
lightPenY = val;
_lightPenY = val;
break;
case 0x15:
sprites[0].enable = ((val & 0x01) != 0);
sprites[1].enable = ((val & 0x02) != 0);
sprites[2].enable = ((val & 0x04) != 0);
sprites[3].enable = ((val & 0x08) != 0);
sprites[4].enable = ((val & 0x10) != 0);
sprites[5].enable = ((val & 0x20) != 0);
sprites[6].enable = ((val & 0x40) != 0);
sprites[7].enable = ((val & 0x80) != 0);
_sprite0.Enable = (val & 0x01) != 0;
_sprite1.Enable = (val & 0x02) != 0;
_sprite2.Enable = (val & 0x04) != 0;
_sprite3.Enable = (val & 0x08) != 0;
_sprite4.Enable = (val & 0x10) != 0;
_sprite5.Enable = (val & 0x20) != 0;
_sprite6.Enable = (val & 0x40) != 0;
_sprite7.Enable = (val & 0x80) != 0;
break;
case 0x16:
xScroll = (val & 0x07);
columnSelect = ((val & 0x08) != 0);
multicolorMode = ((val & 0x10) != 0);
_xScroll = val & 0x07;
_columnSelect = (val & 0x08) != 0;
_multicolorMode = (val & 0x10) != 0;
UpdateBorder();
UpdateVideoMode();
break;
case 0x17:
sprites[0].yExpand = ((val & 0x01) != 0);
sprites[1].yExpand = ((val & 0x02) != 0);
sprites[2].yExpand = ((val & 0x04) != 0);
sprites[3].yExpand = ((val & 0x08) != 0);
sprites[4].yExpand = ((val & 0x10) != 0);
sprites[5].yExpand = ((val & 0x20) != 0);
sprites[6].yExpand = ((val & 0x40) != 0);
sprites[7].yExpand = ((val & 0x80) != 0);
_sprite0.YExpand = (val & 0x01) != 0;
_sprite1.YExpand = (val & 0x02) != 0;
_sprite2.YExpand = (val & 0x04) != 0;
_sprite3.YExpand = (val & 0x08) != 0;
_sprite4.YExpand = (val & 0x10) != 0;
_sprite5.YExpand = (val & 0x20) != 0;
_sprite6.YExpand = (val & 0x40) != 0;
_sprite7.YExpand = (val & 0x80) != 0;
break;
case 0x18:
pointerVM = ((val << 6) & 0x3C00);
pointerCB = ((val >> 1) & 0x7);
_pointerVm = (val << 6) & 0x3C00;
_pointerCb = (val >> 1) & 0x7;
break;
case 0x19:
intRaster = ((val & 0x01) != 0);
intSpriteDataCollision = ((val & 0x02) != 0);
intSpriteCollision = ((val & 0x04) != 0);
intLightPen = ((val & 0x08) != 0);
_intRaster = (val & 0x01) != 0;
_intSpriteDataCollision = (val & 0x02) != 0;
_intSpriteCollision = (val & 0x04) != 0;
_intLightPen = (val & 0x08) != 0;
UpdatePins();
break;
case 0x1A:
enableIntRaster = ((val & 0x01) != 0);
enableIntSpriteDataCollision = ((val & 0x02) != 0);
enableIntSpriteCollision = ((val & 0x04) != 0);
enableIntLightPen = ((val & 0x08) != 0);
_enableIntRaster = (val & 0x01) != 0;
_enableIntSpriteDataCollision = (val & 0x02) != 0;
_enableIntSpriteCollision = (val & 0x04) != 0;
_enableIntLightPen = (val & 0x08) != 0;
UpdatePins();
break;
case 0x1B:
sprites[0].priority = ((val & 0x01) != 0);
sprites[1].priority = ((val & 0x02) != 0);
sprites[2].priority = ((val & 0x04) != 0);
sprites[3].priority = ((val & 0x08) != 0);
sprites[4].priority = ((val & 0x10) != 0);
sprites[5].priority = ((val & 0x20) != 0);
sprites[6].priority = ((val & 0x40) != 0);
sprites[7].priority = ((val & 0x80) != 0);
_sprite0.Priority = (val & 0x01) != 0;
_sprite1.Priority = (val & 0x02) != 0;
_sprite2.Priority = (val & 0x04) != 0;
_sprite3.Priority = (val & 0x08) != 0;
_sprite4.Priority = (val & 0x10) != 0;
_sprite5.Priority = (val & 0x20) != 0;
_sprite6.Priority = (val & 0x40) != 0;
_sprite7.Priority = (val & 0x80) != 0;
break;
case 0x1C:
sprites[0].multicolor = ((val & 0x01) != 0);
sprites[1].multicolor = ((val & 0x02) != 0);
sprites[2].multicolor = ((val & 0x04) != 0);
sprites[3].multicolor = ((val & 0x08) != 0);
sprites[4].multicolor = ((val & 0x10) != 0);
sprites[5].multicolor = ((val & 0x20) != 0);
sprites[6].multicolor = ((val & 0x40) != 0);
sprites[7].multicolor = ((val & 0x80) != 0);
_sprite0.Multicolor = (val & 0x01) != 0;
_sprite1.Multicolor = (val & 0x02) != 0;
_sprite2.Multicolor = (val & 0x04) != 0;
_sprite3.Multicolor = (val & 0x08) != 0;
_sprite4.Multicolor = (val & 0x10) != 0;
_sprite5.Multicolor = (val & 0x20) != 0;
_sprite6.Multicolor = (val & 0x40) != 0;
_sprite7.Multicolor = (val & 0x80) != 0;
break;
case 0x1D:
sprites[0].xExpand = ((val & 0x01) != 0);
sprites[1].xExpand = ((val & 0x02) != 0);
sprites[2].xExpand = ((val & 0x04) != 0);
sprites[3].xExpand = ((val & 0x08) != 0);
sprites[4].xExpand = ((val & 0x10) != 0);
sprites[5].xExpand = ((val & 0x20) != 0);
sprites[6].xExpand = ((val & 0x40) != 0);
sprites[7].xExpand = ((val & 0x80) != 0);
_sprite0.XExpand = (val & 0x01) != 0;
_sprite1.XExpand = (val & 0x02) != 0;
_sprite2.XExpand = (val & 0x04) != 0;
_sprite3.XExpand = (val & 0x08) != 0;
_sprite4.XExpand = (val & 0x10) != 0;
_sprite5.XExpand = (val & 0x20) != 0;
_sprite6.XExpand = (val & 0x40) != 0;
_sprite7.XExpand = (val & 0x80) != 0;
break;
case 0x1E:
sprites[0].collideSprite = ((val & 0x01) != 0);
sprites[1].collideSprite = ((val & 0x02) != 0);
sprites[2].collideSprite = ((val & 0x04) != 0);
sprites[3].collideSprite = ((val & 0x08) != 0);
sprites[4].collideSprite = ((val & 0x10) != 0);
sprites[5].collideSprite = ((val & 0x20) != 0);
sprites[6].collideSprite = ((val & 0x40) != 0);
sprites[7].collideSprite = ((val & 0x80) != 0);
_sprite0.CollideSprite = (val & 0x01) != 0;
_sprite1.CollideSprite = (val & 0x02) != 0;
_sprite2.CollideSprite = (val & 0x04) != 0;
_sprite3.CollideSprite = (val & 0x08) != 0;
_sprite4.CollideSprite = (val & 0x10) != 0;
_sprite5.CollideSprite = (val & 0x20) != 0;
_sprite6.CollideSprite = (val & 0x40) != 0;
_sprite7.CollideSprite = (val & 0x80) != 0;
break;
case 0x1F:
sprites[0].collideData = ((val & 0x01) != 0);
sprites[1].collideData = ((val & 0x02) != 0);
sprites[2].collideData = ((val & 0x04) != 0);
sprites[3].collideData = ((val & 0x08) != 0);
sprites[4].collideData = ((val & 0x10) != 0);
sprites[5].collideData = ((val & 0x20) != 0);
sprites[6].collideData = ((val & 0x40) != 0);
sprites[7].collideData = ((val & 0x80) != 0);
_sprite0.CollideData = (val & 0x01) != 0;
_sprite1.CollideData = (val & 0x02) != 0;
_sprite2.CollideData = (val & 0x04) != 0;
_sprite3.CollideData = (val & 0x08) != 0;
_sprite4.CollideData = (val & 0x10) != 0;
_sprite5.CollideData = (val & 0x20) != 0;
_sprite6.CollideData = (val & 0x40) != 0;
_sprite7.CollideData = (val & 0x80) != 0;
break;
case 0x20:
borderColor = (val & 0xF);
_borderColor = val & 0xF;
break;
case 0x21:
backgroundColor0 = (val & 0xF);
_backgroundColor0 = val & 0xF;
break;
case 0x22:
backgroundColor1 = (val & 0xF);
_backgroundColor1 = val & 0xF;
break;
case 0x23:
backgroundColor2 = (val & 0xF);
_backgroundColor2 = val & 0xF;
break;
case 0x24:
backgroundColor3 = (val & 0xF);
_backgroundColor3 = val & 0xF;
break;
case 0x25:
spriteMulticolor0 = (val & 0xF);
_spriteMulticolor0 = val & 0xF;
break;
case 0x26:
spriteMulticolor1 = (val & 0xF);
_spriteMulticolor1 = val & 0xF;
break;
case 0x27:
case 0x28:
@ -498,9 +423,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
case 0x2C:
case 0x2D:
case 0x2E:
sprites[addr - 0x27].color = (val & 0xF);
break;
default:
_sprites[addr - 0x27].Color = val & 0xF;
break;
}
}

View File

@ -1,261 +1,308 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Vic
public sealed partial class Vic
{
private int delayC;
private int ecmPixel;
private int pixel;
private int pixelCounter;
private int pixelData;
private int pixelOwner;
private int sprData;
private int sprIndex;
private int sprPixel;
private int srC = 0;
private int srSync = 0;
private int videoMode;
[SaveState.DoNotSave] private int _borderPixel;
[SaveState.DoNotSave] private int _bufferPixel;
[SaveState.DoNotSave] private int _ecmPixel;
[SaveState.DoNotSave] private int _pixel;
[SaveState.DoNotSave] private int _pixelCounter;
[SaveState.DoNotSave] private int _pixelData;
[SaveState.DoNotSave] private int _pixelOwner;
[SaveState.DoNotSave] private int _sprData;
[SaveState.DoNotSave] private int _sprIndex;
[SaveState.DoNotSave] private int _sprPixel;
private int _srSync;
private int _srColorSync;
private int _srColorIndexLatch;
private int _videoMode;
private int _borderOnShiftReg;
private void Render()
[SaveState.DoNotSave] private const int VideoMode000 = 0;
[SaveState.DoNotSave] private const int VideoMode001 = 1;
[SaveState.DoNotSave] private const int VideoMode010 = 2;
[SaveState.DoNotSave] private const int VideoMode011 = 3;
[SaveState.DoNotSave] private const int VideoMode100 = 4;
[SaveState.DoNotSave] private const int VideoModeInvalid = -1;
[SaveState.DoNotSave] private const int SrMask1 = 0x20000;
[SaveState.DoNotSave] private const int SrMask2 = SrMask1 << 1;
[SaveState.DoNotSave] private const int SrMask3 = SrMask1 | SrMask2;
[SaveState.DoNotSave] private const int SrColorMask = 0x8000;
[SaveState.DoNotSave] private const int SrSpriteMask = SrSpriteMask2;
[SaveState.DoNotSave] private const int SrSpriteMask1 = 0x400000;
[SaveState.DoNotSave] private const int SrSpriteMask2 = SrSpriteMask1 << 1;
[SaveState.DoNotSave] private const int SrSpriteMask3 = SrSpriteMask1 | SrSpriteMask2;
[SaveState.DoNotSave] private const int SrSpriteMaskMc = SrSpriteMask3;
private void Render()
{
if (hblankCheckEnableL)
{
if (rasterX == hblankEnd)
hblank = false;
}
else if (hblankCheckEnableR)
{
if (rasterX == hblankStart)
hblank = true;
}
if (_rasterX == _hblankEndCheckXRaster)
_hblank = false;
if (_rasterX == _hblankStartCheckXRaster)
_hblank = true;
renderEnabled = (!hblank && !vblank);
pixelCounter = -1;
while (pixelCounter++ < 3)
_renderEnabled = !_hblank && !_vblank;
_pixelCounter = -1;
while (_pixelCounter++ < 3)
{
if (delayC > 0)
delayC--;
else
displayC = (srC >> 12) & 0xFFF;
if ((_srColorSync & SrColorMask) != 0)
{
_displayC = _bufferC[_srColorIndexLatch];
_srColorIndexLatch = (_srColorIndexLatch + 1) & 0x3F;
}
#region PRE-RENDER BORDER
#region PRE-RENDER BORDER
if (borderCheckLEnable && (rasterX == borderL))
// check left border
if (_borderCheckLEnable && (_rasterX == _borderL))
{
if (rasterLine == borderB)
borderOnVertical = true;
if (rasterLine == borderT && displayEnable)
borderOnVertical = false;
if (!borderOnVertical)
borderOnMain = false;
if (_rasterLine == _borderB)
_borderOnVertical = true;
if (_cycle == _totalCycles && _rasterLine == _borderT && _displayEnable)
_borderOnVertical = false;
if (!_borderOnVertical)
_borderOnMain = false;
}
#endregion
#region CHARACTER GRAPHICS
switch (videoMode)
// check right border
if (_borderCheckREnable && (_rasterX == _borderR))
_borderOnMain = true;
#endregion
#region CHARACTER GRAPHICS
switch (_videoMode)
{
case 0:
pixelData = sr & srMask2;
pixel = (pixelData != 0) ? (displayC >> 8) : backgroundColor0;
case VideoMode000:
_pixelData = _sr & SrMask2;
_pixel = _pixelData != 0 ? _displayC >> 8 : _backgroundColor0;
break;
case 1:
if ((displayC & 0x800) != 0)
case VideoMode001:
if ((_displayC & 0x800) != 0)
{
// multicolor 001
if ((srSync & srMask2) != 0)
pixelData = sr & srMask3;
// multicolor 001
if ((_srSync & SrMask2) != 0)
_pixelData = _sr & SrMask3;
if (pixelData == 0)
pixel = backgroundColor0;
else if (pixelData == srMask1)
pixel = backgroundColor1;
else if (pixelData == srMask2)
pixel = backgroundColor2;
else
pixel = (displayC & 0x700) >> 8;
switch (_pixelData)
{
case 0:
_pixel = _backgroundColor0;
break;
case SrMask1:
_pixel = _backgroundColor1;
break;
case SrMask2:
_pixel = _backgroundColor2;
break;
default:
_pixel = (_displayC & 0x700) >> 8;
break;
}
}
else
{
// standard 001
pixelData = sr & srMask2;
pixel = (pixelData != 0) ? (displayC >> 8) : backgroundColor0;
_pixelData = _sr & SrMask2;
_pixel = _pixelData != 0 ? _displayC >> 8 : _backgroundColor0;
}
break;
case 2:
pixelData = sr & srMask2;
pixel = (pixelData != 0) ? (displayC >> 4) : (displayC);
case VideoMode010:
_pixelData = _sr & SrMask2;
_pixel = _pixelData != 0 ? _displayC >> 4 : _displayC;
break;
case 3:
if ((srSync & srMask2) != 0)
pixelData = sr & srMask3;
case VideoMode011:
if ((_srSync & SrMask2) != 0)
_pixelData = _sr & SrMask3;
if (pixelData == 0)
pixel = backgroundColor0;
else if (pixelData == srMask1)
pixel = (displayC >> 4);
else if (pixelData == srMask2)
pixel = displayC;
else
pixel = (displayC >> 8);
break;
case 4:
pixelData = sr & srMask2;
if (pixelData != 0)
switch (_pixelData)
{
pixel = displayC >> 8;
case 0:
_pixel = _backgroundColor0;
break;
case SrMask1:
_pixel = _displayC >> 4;
break;
case SrMask2:
_pixel = _displayC;
break;
default:
_pixel = _displayC >> 8;
break;
}
break;
case VideoMode100:
_pixelData = _sr & SrMask2;
if (_pixelData != 0)
{
_pixel = _displayC >> 8;
}
else
{
ecmPixel = (displayC) & 0xC0;
if (ecmPixel == 0x00)
pixel = backgroundColor0;
else if (ecmPixel == 0x40)
pixel = backgroundColor1;
else if (ecmPixel == 0x80)
pixel = backgroundColor2;
else
pixel = backgroundColor3;
_ecmPixel = (_displayC & 0xC0) >> 6;
switch (_ecmPixel)
{
case 0:
_pixel = _backgroundColor0;
break;
case 1:
_pixel = _backgroundColor1;
break;
case 2:
_pixel = _backgroundColor2;
break;
default:
_pixel = _backgroundColor3;
break;
}
}
break;
break;
default:
pixelData = 0;
pixel = 0;
_pixelData = 0;
_pixel = 0;
break;
}
pixel &= 0xF;
sr <<= 1;
srSync <<= 1;
#endregion
_pixel &= 0xF;
_sr <<= 1;
_srSync <<= 1;
_srColorSync <<= 1;
#endregion
#region SPRITES
// render sprites
pixelOwner = -1;
sprIndex = 0;
foreach (var spr in sprites)
#region SPRITES
// render sprites
_pixelOwner = -1;
_sprIndex = 0;
foreach (var spr in _sprites)
{
sprData = 0;
sprPixel = pixel;
_sprData = 0;
_sprPixel = _pixel;
if (spr.x == rasterX)
if (spr.X == _rasterX)
{
spr.shiftEnable = spr.display;
spr.xCrunch = !spr.xExpand;
spr.multicolorCrunch = false;
spr.ShiftEnable = spr.Display;
spr.XCrunch = !spr.XExpand;
spr.MulticolorCrunch = false;
}
else
{
spr.xCrunch |= !spr.xExpand;
spr.XCrunch |= !spr.XExpand;
}
if (spr.shiftEnable) // sprite rule 6
if (spr.ShiftEnable) // sprite rule 6
{
if (spr.multicolor)
if (spr.Multicolor)
{
sprData = (spr.sr & srSpriteMaskMC);
if (spr.multicolorCrunch && spr.xCrunch && !rasterXHold)
_sprData = spr.Sr & SrSpriteMaskMc;
if (spr.MulticolorCrunch && spr.XCrunch && !_rasterXHold)
{
if (spr.loaded == 0)
if (spr.Loaded == 0)
{
spr.shiftEnable = false;
spr.ShiftEnable = false;
}
spr.sr <<= 2;
spr.loaded >>= 2;
spr.Sr <<= 2;
spr.Loaded >>= 2;
}
spr.multicolorCrunch ^= spr.xCrunch;
spr.MulticolorCrunch ^= spr.XCrunch;
}
else
{
sprData = (spr.sr & srSpriteMask);
if (spr.xCrunch && !rasterXHold)
_sprData = spr.Sr & SrSpriteMask;
if (spr.XCrunch && !_rasterXHold)
{
if (spr.loaded == 0)
if (spr.Loaded == 0)
{
spr.shiftEnable = false;
spr.ShiftEnable = false;
}
spr.sr <<= 1;
spr.loaded >>= 1;
spr.Sr <<= 1;
spr.Loaded >>= 1;
}
}
spr.xCrunch ^= spr.xExpand;
spr.XCrunch ^= spr.XExpand;
if (sprData != 0)
if (_sprData != 0)
{
// sprite-sprite collision
if (pixelOwner < 0)
if (_pixelOwner < 0)
{
if (sprData == srSpriteMask1)
sprPixel = spriteMulticolor0;
else if (sprData == srSpriteMask2)
sprPixel = spr.color;
else if (sprData == srSpriteMask3)
sprPixel = spriteMulticolor1;
pixelOwner = sprIndex;
switch (_sprData)
{
case SrSpriteMask1:
_sprPixel = _spriteMulticolor0;
break;
case SrSpriteMask2:
_sprPixel = spr.Color;
break;
case SrSpriteMask3:
_sprPixel = _spriteMulticolor1;
break;
}
_pixelOwner = _sprIndex;
}
else
{
if (!borderOnVertical)
if (!_borderOnVertical)
{
spr.collideSprite = true;
sprites[pixelOwner].collideSprite = true;
spr.CollideSprite = true;
_sprites[_pixelOwner].CollideSprite = true;
}
}
// sprite-data collision
if (!borderOnVertical && (pixelData >= srMask2))
if (!_borderOnVertical && (_pixelData >= SrMask2))
{
spr.collideData = true;
spr.CollideData = true;
}
// sprite priority logic
if (spr.priority)
if (spr.Priority)
{
pixel = (pixelData >= srMask2) ? pixel : sprPixel;
_pixel = _pixelData >= SrMask2 ? _pixel : _sprPixel;
}
else
{
pixel = sprPixel;
_pixel = _sprPixel;
}
}
}
sprIndex++;
_sprIndex++;
}
#endregion
#endregion
#region POST-RENDER BORDER
if (borderCheckREnable && (rasterX == borderR))
borderOnMain = true;
#region POST-RENDER BORDER
// border doesn't work with the background buffer
if (borderOnMain || borderOnVertical)
pixel = borderColor;
// border doesn't work with the background buffer
_borderPixel = _pixBorderBuffer[_pixBufferBorderIndex];
_pixBorderBuffer[_pixBufferBorderIndex] = _borderColor;
#endregion
// plot pixel if within viewing area
if (renderEnabled)
if (_renderEnabled)
{
buf[bufOffset] = palette[pixBuffer[pixBufferIndex]];
bufOffset++;
if (bufOffset == bufLength)
bufOffset = 0;
_bufferPixel = (_borderOnShiftReg & 0x80000) != 0 ? _borderPixel : _pixBuffer[_pixBufferIndex];
_buf[_bufOffset] = Palette[_bufferPixel];
_bufOffset++;
if (_bufOffset == _bufLength)
_bufOffset = 0;
}
pixBuffer[pixBufferIndex] = pixel;
pixBufferIndex++;
_borderOnShiftReg <<= 1;
_borderOnShiftReg |= (_borderOnVertical || _borderOnMain) ? 1 : 0;
_pixBuffer[_pixBufferIndex] = _pixel;
_pixBufferIndex++;
_pixBufferBorderIndex++;
if (!rasterXHold)
rasterX++;
bitmapColumn++;
if (!_rasterXHold)
_rasterX++;
}
if (pixBufferIndex >= pixBufferSize)
pixBufferIndex = 0;
if (_pixBufferBorderIndex >= PixBorderBufferSize)
_pixBufferBorderIndex = 0;
if (_pixBufferIndex >= PixBufferSize)
_pixBufferIndex = 0;
}
}
}

View File

@ -7,54 +7,54 @@ using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Vic
public sealed partial class Vic
{
sealed class Sprite
private sealed class Sprite
{
public bool collideData;
public bool collideSprite;
public int color;
public bool display;
public bool dma;
public bool enable;
public int loaded;
public int mc;
public int mcbase;
public bool multicolor;
public bool multicolorCrunch;
public int pointer;
public bool priority;
public bool shiftEnable;
public int sr;
public int x;
public bool xCrunch;
public bool xExpand;
public int y;
public bool yCrunch;
public bool yExpand;
public bool CollideData;
public bool CollideSprite;
public int Color;
public bool Display;
public bool Dma;
public bool Enable;
public int Loaded;
public int Mc;
public int Mcbase;
public bool Multicolor;
public bool MulticolorCrunch;
public int Pointer;
public bool Priority;
public bool ShiftEnable;
public int Sr;
public int X;
public bool XCrunch;
public bool XExpand;
public int Y;
public bool YCrunch;
public bool YExpand;
public void HardReset()
{
collideData = false;
collideSprite = false;
color = 0;
display = false;
dma = false;
enable = false;
mc = 0;
mcbase = 0;
multicolor = false;
multicolorCrunch = false;
pointer = 0;
priority = false;
shiftEnable = false;
sr = 0;
x = 0;
xCrunch = false;
xExpand = false;
y = 0;
yCrunch = false;
yExpand = false;
CollideData = false;
CollideSprite = false;
Color = 0;
Display = false;
Dma = false;
Enable = false;
Mc = 0;
Mcbase = 0;
Multicolor = false;
MulticolorCrunch = false;
Pointer = 0;
Priority = false;
ShiftEnable = false;
Sr = 0;
X = 0;
XCrunch = false;
XExpand = false;
Y = 0;
YCrunch = false;
YExpand = false;
}
public void SyncState(Serializer ser)

View File

@ -8,182 +8,169 @@ using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Vic
public sealed partial class Vic
{
private int backgroundColor0;
private int backgroundColor1;
private int backgroundColor2;
private int backgroundColor3;
private int baCount;
private bool badline;
private bool badlineEnable;
private int bitmapColumn;
private bool bitmapMode;
private int borderB;
private bool borderCheckLEnable;
private bool borderCheckREnable;
private int borderColor;
private int borderL;
private bool borderOnMain;
private bool borderOnVertical;
private int borderR;
private int borderT;
private int[] bufferC;
private int[] bufferG;
private int cycle;
private int cycleIndex;
private bool columnSelect;
private int dataC;
private int dataG;
private bool displayEnable;
private int displayC;
private bool enableIntLightPen;
private bool enableIntRaster;
private bool enableIntSpriteCollision;
private bool enableIntSpriteDataCollision;
private bool extraColorMode;
private bool hblank;
private bool idle;
private bool intLightPen;
private bool intRaster;
private bool intSpriteCollision;
private bool intSpriteDataCollision;
private int hblankEnd;
private int hblankStart;
private bool hblankCheckEnableL;
private bool hblankCheckEnableR;
private int lastRasterLine;
private int lightPenX;
private int lightPenY;
private bool multicolorMode;
private bool pinAEC = true;
private bool pinBA = true;
private bool pinIRQ = true;
private int pointerCB;
private int pointerVM;
private int rasterInterruptLine;
private int rasterLine;
private int rasterX;
private bool rasterXHold;
private int rc;
private int refreshCounter;
private bool renderEnabled;
private bool rowSelect;
private int spriteMulticolor0;
private int spriteMulticolor1;
private Sprite[] sprites;
private int sr;
private int srMask;
private int srMask1;
private int srMask2;
private int srMask3;
private int srMaskMC;
private int srSpriteMask;
private int srSpriteMask1;
private int srSpriteMask2;
private int srSpriteMask3;
private int srSpriteMaskMC;
private bool vblank;
private int vblankEnd;
private int vblankStart;
private int vc;
private int vcbase;
private int vmli;
private int xScroll;
private int yScroll;
private int _backgroundColor0;
private int _backgroundColor1;
private int _backgroundColor2;
private int _backgroundColor3;
private int _baCount;
private bool _badline;
private bool _badlineEnable;
private bool _bitmapMode;
private int _borderB;
private bool _borderCheckLEnable;
private bool _borderCheckREnable;
private int _borderColor;
private int _borderL;
private bool _borderOnMain;
private bool _borderOnVertical;
private int _borderR;
private int _borderT;
private readonly int[] _bufferC;
private readonly int[] _bufferG;
private int _cycle;
private int _cycleIndex;
private bool _columnSelect;
private int _dataC;
private int _dataG;
private bool _displayEnable;
private int _displayC;
private bool _enableIntLightPen;
private bool _enableIntRaster;
private bool _enableIntSpriteCollision;
private bool _enableIntSpriteDataCollision;
private bool _extraColorMode;
private bool _extraColorModeBuffer;
[SaveState.DoNotSave] private bool _hblank;
private bool _idle;
private bool _intLightPen;
private bool _intRaster;
private bool _intSpriteCollision;
private bool _intSpriteDataCollision;
private int _lightPenX;
private int _lightPenY;
private bool _multicolorMode;
private bool _pinAec = true;
private bool _pinBa = true;
private bool _pinIrq = true;
private int _pointerCb;
private int _pointerVm;
private int _rasterInterruptLine;
private bool _rasterInterruptTriggered;
private int _rasterLine;
private int _rasterX;
private bool _rasterXHold;
private int _rc;
private int _refreshCounter;
private bool _renderEnabled;
private bool _rowSelect;
private bool _spriteBackgroundCollisionClearPending;
private bool _spriteSpriteCollisionClearPending;
private int _spriteMulticolor0;
private int _spriteMulticolor1;
[SaveState.DoNotSave] private readonly Sprite _sprite0;
[SaveState.DoNotSave] private readonly Sprite _sprite1;
[SaveState.DoNotSave] private readonly Sprite _sprite2;
[SaveState.DoNotSave] private readonly Sprite _sprite3;
[SaveState.DoNotSave] private readonly Sprite _sprite4;
[SaveState.DoNotSave] private readonly Sprite _sprite5;
[SaveState.DoNotSave] private readonly Sprite _sprite6;
[SaveState.DoNotSave] private readonly Sprite _sprite7;
private readonly Sprite[] _sprites;
private int _sr;
[SaveState.DoNotSave] private bool _vblank;
[SaveState.DoNotSave] private int _vblankEnd;
[SaveState.DoNotSave] private int _vblankStart;
private int _vc;
private int _vcbase;
private int _vmli;
private int _xScroll;
private int _yScroll;
public void HardReset()
{
// *** SHIFT REGISTER BITMASKS ***
srMask1 = 0x20000;
srMask2 = srMask1 << 1;
srMask3 = srMask1 | srMask2;
srMask = srMask2;
srMaskMC = srMask3;
srSpriteMask1 = 0x400000;
srSpriteMask2 = srSpriteMask1 << 1;
srSpriteMask3 = srSpriteMask1 | srSpriteMask2;
srSpriteMask = srSpriteMask2;
srSpriteMaskMC = srSpriteMask3;
_pinAec = true;
_pinBa = true;
_pinIrq = true;
pinAEC = true;
pinBA = true;
pinIRQ = true;
_bufOffset = 0;
bufOffset = 0;
backgroundColor0 = 0;
backgroundColor1 = 0;
backgroundColor2 = 0;
backgroundColor3 = 0;
baCount = baResetCounter;
badline = false;
badlineEnable = false;
bitmapMode = false;
borderCheckLEnable = false;
borderCheckREnable = false;
borderColor = 0;
borderOnMain = true;
borderOnVertical = true;
columnSelect = false;
displayEnable = false;
enableIntLightPen = false;
enableIntRaster = false;
enableIntSpriteCollision = false;
enableIntSpriteDataCollision = false;
extraColorMode = false;
hblank = true;
idle = true;
intLightPen = false;
intRaster = false;
intSpriteCollision = false;
intSpriteDataCollision = false;
lastRasterLine = 0;
lightPenX = 0;
lightPenY = 0;
multicolorMode = false;
pointerCB = 0;
pointerVM = 0;
rasterInterruptLine = 0;
rasterLine = 0;
rasterX = 0;
rc = 7;
refreshCounter = 0xFF;
rowSelect = false;
spriteMulticolor0 = 0;
spriteMulticolor1 = 0;
sr = 0;
vblank = true;
vc = 0;
vcbase = 0;
vmli = 0;
xScroll = 0;
yScroll = 0;
_backgroundColor0 = 0;
_backgroundColor1 = 0;
_backgroundColor2 = 0;
_backgroundColor3 = 0;
_baCount = BaResetCounter;
_badline = false;
_badlineEnable = false;
_bitmapMode = false;
_borderCheckLEnable = false;
_borderCheckREnable = false;
_borderColor = 0;
_borderOnMain = true;
_borderOnVertical = true;
_columnSelect = false;
_displayEnable = false;
_enableIntLightPen = false;
_enableIntRaster = false;
_enableIntSpriteCollision = false;
_enableIntSpriteDataCollision = false;
_extraColorMode = false;
_idle = true;
_intLightPen = false;
_intRaster = false;
_intSpriteCollision = false;
_intSpriteDataCollision = false;
_lightPenX = 0;
_lightPenY = 0;
_multicolorMode = false;
_pointerCb = 0;
_pointerVm = 0;
_rasterInterruptLine = 0;
_rasterLine = 0;
_rasterX = 0;
_rc = 7;
_refreshCounter = 0xFF;
_rowSelect = false;
_spriteBackgroundCollisionClearPending = false;
_spriteSpriteCollisionClearPending = false;
_spriteMulticolor0 = 0;
_spriteMulticolor1 = 0;
_sr = 0;
_vc = 0;
_vcbase = 0;
_vmli = 0;
_xScroll = 0;
_yScroll = 0;
_cycle = 0;
// reset sprites
for (int i = 0; i < 8; i++)
sprites[i].HardReset();
for (var i = 0; i < 8; i++)
_sprites[i].HardReset();
// clear C buffer
for (int i = 0; i < 40; i++)
for (var i = 0; i < 40; i++)
{
bufferC[i] = 0;
bufferG[i] = 0;
_bufferC[i] = 0;
_bufferG[i] = 0;
}
pixBuffer = new int[pixBufferSize];
UpdateBorder();
_pixBuffer = new int[PixBufferSize];
_pixBorderBuffer = new int[PixBorderBufferSize];
_pixBufferIndex = 0;
_pixBufferBorderIndex = 0;
UpdateBorder();
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
for (int i = 0; i < 8; i++)
{
ser.BeginSection("sprite" + i.ToString());
SaveState.SyncObject(ser, sprites[i]);
ser.EndSection();
}
}
if (ser.IsReader)
{
UpdateBorder();
UpdatePins();
UpdateVideoMode();
}
}
}
}

View File

@ -1,126 +1,131 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Vic
public sealed partial class Vic
{
const int BORDER_LEFT_38 = 0x01F;
const int BORDER_LEFT_40 = 0x018;
const int BORDER_RIGHT_38 = 0x14F;
const int BORDER_RIGHT_40 = 0x158;
[SaveState.DoNotSave] private const int BorderLeft38 = 0x023;
[SaveState.DoNotSave] private const int BorderLeft40 = 0x01C;
[SaveState.DoNotSave] private const int BorderRight38 = 0x153;
[SaveState.DoNotSave] private const int BorderRight40 = 0x15C;
[SaveState.DoNotSave] private const int BorderTop25 = 0x033;
[SaveState.DoNotSave] private const int BorderTop24 = 0x037;
[SaveState.DoNotSave] private const int BorderBottom25 = 0x0FB;
[SaveState.DoNotSave] private const int BorderBottom24 = 0x0F7;
[SaveState.DoNotSave] private const int FirstDmaLine = 0x030;
[SaveState.DoNotSave] private const int LastDmaLine = 0x0F7;
// The special actions taken by the Vic are in the same order and interval on all chips, just different offsets.
static private int[] TimingBuilder_Cycle14Act = new int[]
{
pipelineUpdateVc, 0,
pipelineChkSprCrunch, 0,
pipelineUpdateMcBase, 0,
};
static private int[] TimingBuilder_Cycle55Act = new int[]
{
pipelineChkSprDma, 0,
pipelineChkSprDma | pipelineChkSprExp, 0,
0, 0,
pipelineChkSprDisp, pipelineUpdateRc
// The special actions taken by the Vic are in the same order and interval on all chips, just different offsets.
[SaveState.DoNotSave]
private static readonly int[] TimingBuilderCycle14Act = {
PipelineUpdateVc, 0,
PipelineSpriteCrunch, 0,
PipelineUpdateMcBase, 0,
};
// This builds a table of special actions to take on each half-cycle. Cycle14 is the X-raster position where
// pre-display operations happen, and Cycle55 is the X-raster position where post-display operations happen.
static public int[] TimingBuilder_Act(int[] timing, int cycle14, int cycle55, int hblankStart, int hblankEnd)
public static int[] TimingBuilder_Act(int[] timing, int cycle14, int sprite0Ba, int sprDisp)
{
List<int> result = new List<int>();
var result = new List<int>();
int length = timing.Length;
for (int i = 0; i < length; i++)
var length = timing.Length;
for (var i = 0; i < length; i++)
{
while (i < result.Count)
i++;
if (timing[i] == cycle14)
result.AddRange(TimingBuilder_Cycle14Act);
else if (timing[i] == cycle55)
result.AddRange(TimingBuilder_Cycle55Act);
result.AddRange(TimingBuilderCycle14Act);
else
result.Add(0);
}
for (int i = 0; i < length; i++)
for (var i = 0; i < length; i++)
{
// pipeline raster X delay
if (timing[(i + 1) % length] == timing[i])
result[i] |= pipelineHoldX;
result[i] |= PipelineHoldX;
// pipeline border checks
if (timing[i] == (BORDER_LEFT_40 & 0xFFC))
result[i] |= pipelineChkBrdL1;
if (timing[i] == (BORDER_LEFT_38 & 0xFFC))
result[i] |= pipelineChkBrdL0;
if (timing[i] == (BORDER_RIGHT_38 & 0xFFC))
result[i] |= pipelineChkBrdR0;
if (timing[i] == (BORDER_RIGHT_40 & 0xFFC))
result[i] |= pipelineChkBrdR1;
if (timing[i] == (hblankStart & 0xFFC))
result[i] |= pipelineHBlankR;
if (timing[i] == (hblankEnd & 0xFFC))
result[i] |= pipelineHBlankL;
if (timing[i] == (BorderLeft40 & 0xFFC))
result[i] |= PipelineBorderLeft1;
if (timing[i] == (BorderLeft38 & 0xFFC))
result[i] |= PipelineBorderLeft0;
if (timing[i] == (BorderRight38 & 0xFFC))
result[i] |= PipelineBorderRight0;
if (timing[i] == (BorderRight40 & 0xFFC))
result[i] |= PipelineBorderRight1;
// right side timing
if (timing[i] == 0x0158)
result[i] |= PipelineSpriteExpansion;
if (timing[i] == 0x0168)
result[i] |= PipelineUpdateRc;
if (timing[i] == sprite0Ba || timing[i] == sprite0Ba + 8)
result[i] |= PipelineSpriteDma;
if (timing[i] == sprDisp)
result[i] |= PipelineSpriteDisplay;
}
return result.ToArray();
}
// This builds a table of how the BA pin is supposed to act on each half-cycle.
static public int[] TimingBuilder_BA(int[] fetch)
public static int[] TimingBuilder_BA(int[] fetch)
{
int baRestart = 7;
int start = 0;
int length = fetch.Length;
int[] result = new int[length];
int[] spriteBA = new int[8];
int charBA = 0;
const int baRestart = 7;
var start = 0;
var length = fetch.Length;
var result = new int[length];
var spriteBa = new int[8];
var charBa = 0;
while (true)
{
if (fetch[start] == 0)
if (fetch[start] == FetchTypeSprite)
break;
start++;
}
while (true)
{
if (fetch[start] == 0x200)
if (fetch[start] == FetchTypeColor)
break;
start--;
}
if (start < 0)
start += length;
int offset = start;
var offset = start;
while (true)
{
int ba = 0x0888;
var ba = BaTypeNone;
if (fetch[offset] == 0x200)
charBA = baRestart;
else if ((fetch[offset] & 0xFF00) == 0x0000)
spriteBA[fetch[offset] & 0x007] = baRestart;
if (fetch[offset] == FetchTypeColor)
charBa = baRestart;
else if ((fetch[offset] & 0xFF00) == FetchTypeSprite)
spriteBa[fetch[offset] & 0x007] = baRestart;
for (int i = 0; i < 8; i++)
for (var i = 0; i < 8; i++)
{
if (spriteBA[i] > 0)
if (spriteBa[i] > 0)
{
ba <<= 4;
ba |= i;
spriteBA[i]--;
spriteBa[i]--;
}
}
ba &= 0x0FFF;
if (charBA > 0)
if (charBa > 0)
{
ba = 0x1000;
charBA--;
ba = BaTypeCharacter;
charBa--;
}
result[offset] = ba;
@ -133,7 +138,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
break;
}
for (int i = 0; i < length; i += 2)
for (var i = 0; i < length; i += 2)
{
result[i] = result[i + 1];
}
@ -142,34 +147,33 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
}
// This builds a table of the fetch operations to take on each half-cycle.
static public int[] TimingBuilder_Fetch(int[] timing, int sprite)
public static int[] TimingBuilder_Fetch(int[] timing, int sprite)
{
int length = timing.Length;
int[] result = new int[length];
int offset;
int index = -1;
int refreshCounter = 0;
bool spriteActive = false;
int spriteIndex = 0;
int spritePhase = 0;
int charCounter = 0;
var length = timing.Length;
var result = new int[length];
var index = -1;
var refreshCounter = 0;
var spriteActive = false;
var spriteIndex = 0;
var spritePhase = 0;
var charCounter = 0;
for (int i = 0; i < length; i++)
for (var i = 0; i < length; i++)
{
result[i++] = 0x500;
result[i] = 0x100;
}
result[i++] = FetchTypeIdle;
result[i] = FetchTypeNone;
}
while (true)
while (true)
{
index++;
if (index >= length)
index -= length;
offset = timing[index];
var offset = timing[index];
if (charCounter > 0)
{
result[index] = (charCounter & 1) == 0 ? 0x200 : 0x300;
result[index] = (charCounter & 1) == 0 ? FetchTypeColor : FetchTypeGraphics;
charCounter--;
if (charCounter == 0)
break;
@ -177,7 +181,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
if (refreshCounter > 0)
{
result[index] = (refreshCounter & 1) == 0 ? 0x500 : 0x100;
result[index] = (refreshCounter & 1) == 0 ? FetchTypeNone : FetchTypeRefresh;
refreshCounter--;
if (refreshCounter == 0)
charCounter = 80;
@ -209,10 +213,15 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
}
// This uses the vBlank values to determine the height of the visible screen.
static public int TimingBuilder_ScreenHeight(int vblankStart, int vblankEnd, int lines)
private static int TimingBuilder_ScreenHeight(int vblankStart, int vblankEnd, int lines)
{
int offset = vblankEnd;
int result = 0;
if (vblankStart < 0 || vblankEnd < 0)
{
return lines;
}
var offset = vblankEnd;
var result = 0;
while (true)
{
if (offset >= lines)
@ -225,11 +234,16 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
}
// This uses the hBlank values to determine the width of the visible screen.
static public int TimingBuilder_ScreenWidth(int[] timing, int hblankStart, int hblankEnd)
private static int TimingBuilder_ScreenWidth(IList<int> timing, int hblankStart, int hblankEnd)
{
int length = timing.Length;
int result = 0;
int offset = 0;
if (hblankStart < 0 || hblankEnd < 0)
{
return timing.Count * 4;
}
var length = timing.Count;
var result = 0;
var offset = 0;
while (timing[offset] != hblankEnd) { offset = (offset + 1) % length; }
while (timing[offset] != hblankStart) { offset = (offset + 1) % length; result++; }
@ -241,15 +255,15 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
// Y-raster is incremented. Width is the position where the X-raster is reset to zero. Count
// is the width of a rasterline in pixels. DelayOffset is the X-raster position where lag begins
// (specifically on an NTSC 6567R8) and DelayAmount is the number of positions to lag.
static public int[] TimingBuilder_XRaster(int start, int width, int count, int delayOffset, int delayAmount)
public static int[] TimingBuilder_XRaster(int start, int width, int count, int delayOffset, int delayAmount)
{
List<int> result = new List<int>();
int rasterX = start;
bool delayed = false;
var result = new List<int>();
var rasterX = start;
var delayed = false;
count >>= 2;
delayAmount >>= 2;
for (int i = 0; i < count; i++)
for (var i = 0; i < count; i++)
{
result.Add(rasterX);

View File

@ -5,19 +5,24 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Vic : IVideoProvider
public sealed partial class Vic : IVideoProvider
{
private int[] buf;
private int bufHeight;
private int bufLength;
private int bufOffset;
private int bufWidth;
private int pixBufferSize = 12;
private int[] pixBuffer;
private int pixBufferIndex;
[SaveState.DoNotSave] private static readonly int BgColor = Colors.ARGB(0, 0, 0);
[SaveState.DoNotSave] private int[] _buf;
[SaveState.DoNotSave] private int _bufHeight;
[SaveState.DoNotSave] private int _bufLength;
private int _bufOffset;
[SaveState.DoNotSave] private int _bufWidth;
[SaveState.DoNotSave] private const int PixBufferSize = 24;
[SaveState.DoNotSave] private const int PixBorderBufferSize = 12;
private int[] _pixBuffer;
private int _pixBufferIndex;
private int[] _pixBorderBuffer;
private int _pixBufferBorderIndex;
// palette
private int[] palette =
// palette
[SaveState.DoNotSave]
private static readonly int[] Palette =
{
Colors.ARGB(0x00, 0x00, 0x00),
Colors.ARGB(0xFF, 0xFF, 0xFF),
@ -37,34 +42,33 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
Colors.ARGB(0x95, 0x95, 0x95)
};
public int BackgroundColor
[SaveState.DoNotSave]
public int BackgroundColor
{
get { return Colors.ARGB(0, 0, 0); }
get { return BgColor; }
}
public int BufferHeight
[SaveState.DoNotSave]
public int BufferHeight
{
get { return bufHeight; }
get { return _bufHeight; }
}
public int BufferWidth
[SaveState.DoNotSave]
public int BufferWidth
{
get { return bufWidth; }
get { return _bufWidth; }
}
public int[] GetVideoBuffer()
{
return buf;
return _buf;
}
public int VirtualWidth
{
get { return bufWidth; }
}
[SaveState.DoNotSave]
public int VirtualWidth { get; private set; }
public int VirtualHeight
{
get { return bufHeight; }
}
[SaveState.DoNotSave]
public int VirtualHeight { get; private set; }
}
}

View File

@ -1,56 +1,178 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
sealed public partial class Vic
public sealed partial class Vic
{
public Func<int, byte> ReadColorRam;
public Func<int, byte> ReadMemory;
/*
Commodore VIC-II 6567/6569/6572 core.
public bool ReadAECBuffer() { return pinAEC; }
public bool ReadBABuffer() { return pinBA; }
public bool ReadIRQBuffer() { return pinIRQ; }
Many thanks to:
- Christian Bauer for the VIC-II document.
http://www.zimmers.net/cbmpics/cbm/c64/vic-ii.txt
- VICE team for the addendum to the above document.
http://vice-emu.sourceforge.net/plain/VIC-Addendum.txt
- Whoever scanned the CSG 6567 preliminary datasheet.
http://www.classiccmp.org/cini/pdf/Commodore/ds_6567.pdf
- Michael Huth for die shots of the 6569R3 chip (to get ideas how to implement)
http://mail.lipsia.de/~enigma/neu/6581.html
*/
private int cyclesPerSec;
private int irqShift;
private int[][] pipeline;
private int totalCycles;
private int totalLines;
public Func<int, int> ReadColorRam;
public Func<int, int> ReadMemory;
public Vic(int newCycles, int newLines, int[][] newPipeline, int newCyclesPerSec, int hblankStart, int hblankEnd, int vblankStart, int vblankEnd)
public bool ReadAec() { return _pinAec; }
public bool ReadBa() { return _pinBa; }
public bool ReadIrq() { return _pinIrq; }
[SaveState.DoNotSave] private readonly int _cyclesPerSec;
[SaveState.DoNotSave] private readonly int[] _rasterXPipeline;
[SaveState.DoNotSave] private readonly int[] _fetchPipeline;
[SaveState.DoNotSave] private readonly int[] _baPipeline;
[SaveState.DoNotSave] private readonly int[] _actPipeline;
[SaveState.DoNotSave] private readonly int _totalCycles;
[SaveState.DoNotSave] private readonly int _totalLines;
private int _cyclesExecuted;
[SaveState.DoNotSave] private int _hblankStartCheckXRaster;
[SaveState.DoNotSave] private int _hblankEndCheckXRaster;
[SaveState.DoNotSave] private int _pixelRatioNum;
[SaveState.DoNotSave] private int _pixelRatioDen;
public Vic(int newCycles, int newLines, IList<int[]> newPipeline, int newCyclesPerSec, int hblankStart, int hblankEnd, int vblankStart, int vblankEnd, C64.BorderType borderType, int pixelRatioNum, int pixelRatioDen)
{
{
this.hblankStart = hblankStart;
this.hblankEnd = hblankEnd;
this.vblankStart = vblankStart;
this.vblankEnd = vblankEnd;
Debug.WriteLine("C64 VIC timings:");
Debug.WriteLine("RX FTCH BA ACT");
for (var i = 0; i < newPipeline[0].Length; i++)
{
Debug.WriteLine("{0:x4} {1:x4} {2:x4} {3:x8}", newPipeline[0][i], newPipeline[1][i], newPipeline[2][i], newPipeline[3][i]);
}
totalCycles = newCycles;
totalLines = newLines;
pipeline = newPipeline;
cyclesPerSec = newCyclesPerSec;
_pixelRatioNum = pixelRatioNum;
_pixelRatioDen = pixelRatioDen;
bufWidth = TimingBuilder_ScreenWidth(pipeline[0], hblankStart, hblankEnd);
bufHeight = TimingBuilder_ScreenHeight(vblankStart, vblankEnd, newLines);
_rasterXPipeline = newPipeline[0];
_fetchPipeline = newPipeline[1];
_baPipeline = newPipeline[2];
_actPipeline = newPipeline[3];
_totalCycles = newCycles;
_totalLines = newLines;
_cyclesPerSec = newCyclesPerSec;
buf = new int[bufWidth * bufHeight];
bufLength = buf.Length;
ConfigureBlanking(newLines, hblankStart, hblankEnd, vblankStart, vblankEnd, borderType);
sprites = new Sprite[8];
for (int i = 0; i < 8; i++)
sprites[i] = new Sprite();
_sprites = new Sprite[8];
for (var i = 0; i < 8; i++)
_sprites[i] = new Sprite();
_sprite0 = _sprites[0];
_sprite1 = _sprites[1];
_sprite2 = _sprites[2];
_sprite3 = _sprites[3];
_sprite4 = _sprites[4];
_sprite5 = _sprites[5];
_sprite6 = _sprites[6];
_sprite7 = _sprites[7];
bufferC = new int[40];
bufferG = new int[40];
}
}
_bufferC = new int[40];
_bufferG = new int[40];
}
public int CyclesPerFrame
private void ConfigureBlanking(int lines, int hblankStart, int hblankEnd, int vblankStart, int vblankEnd,
C64.BorderType borderType)
{
var newHblankStart = hblankStart;
var newHblankEnd = hblankEnd;
var newVblankStart = vblankStart;
var newVblankEnd = vblankEnd;
var hBorderSize = 16; // must be a multiple of 4
var vBorderSize = hBorderSize*_pixelRatioNum/_pixelRatioDen; // to keep top and bottom in proportion
var maxWidth = _rasterXPipeline.Max();
switch (borderType)
{
case C64.BorderType.Full:
newHblankStart = -1;
newHblankEnd = -1;
_hblank = false;
newVblankStart = -1;
newVblankEnd = -1;
_vblank = false;
break;
case C64.BorderType.Normal:
newHblankStart = hblankStart;
newHblankEnd = hblankEnd;
newVblankStart = vblankStart;
newVblankEnd = vblankEnd;
_vblank = true;
_hblank = true;
break;
case C64.BorderType.SmallProportional:
_vblank = true;
_hblank = true;
newHblankStart = 0x158 + PixBufferSize + hBorderSize;
newHblankEnd = 0x018 + PixBufferSize - hBorderSize;
newVblankStart = 0xFA + vBorderSize;
newVblankEnd = 0x32 - vBorderSize;
break;
case C64.BorderType.SmallFixed:
_vblank = true;
_hblank = true;
newHblankStart = 0x158 + PixBufferSize + hBorderSize;
newHblankEnd = 0x018 + PixBufferSize - hBorderSize;
newVblankStart = 0xFA + hBorderSize;
newVblankEnd = 0x32 - hBorderSize;
break;
}
// wrap values
newHblankStart = WrapValue(0, maxWidth, newHblankStart);
newHblankEnd = WrapValue(0, maxWidth, newHblankEnd);
newVblankStart = WrapValue(0, lines, newVblankStart);
newVblankEnd = WrapValue(0, lines, newVblankEnd);
// calculate output dimensions
_hblankStartCheckXRaster = newHblankStart & 0xFFC;
_hblankEndCheckXRaster = newHblankEnd & 0xFFC;
_vblankStart = newVblankStart;
_vblankEnd = newVblankEnd;
_bufWidth = TimingBuilder_ScreenWidth(_rasterXPipeline, newHblankStart, newHblankEnd);
_bufHeight = TimingBuilder_ScreenHeight(newVblankStart, newVblankEnd, lines);
_buf = new int[_bufWidth * _bufHeight];
_bufLength = _buf.Length;
VirtualWidth = _bufWidth*_pixelRatioNum/_pixelRatioDen;
VirtualHeight = _bufHeight;
}
private int WrapValue(int min, int max, int val)
{
if (min == max)
{
return min;
}
var width = Math.Abs(min - max);
while (val > max)
{
val -= width;
}
while (val < min)
{
val += width;
}
return val;
}
public int CyclesPerFrame
{
get
{
return (totalCycles * totalLines);
return _totalCycles * _totalLines;
}
}
@ -58,155 +180,193 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
get
{
return cyclesPerSec;
return _cyclesPerSec;
}
}
public void ExecutePhase1()
public void ExecutePhase()
{
{
// raster IRQ compare
if ((cycle == rasterIrqLineXCycle && rasterLine > 0) || (cycle == rasterIrqLine0Cycle && rasterLine == 0))
{
if (rasterLine != lastRasterLine)
if (rasterLine == rasterInterruptLine)
intRaster = true;
lastRasterLine = rasterLine;
}
// phi1
// display enable compare
if (rasterLine == 0)
badlineEnable = false;
// advance cycle and optionally raster line
_cycle++;
if (_cycle > _totalCycles)
{
// border check
if (_rasterLine == _borderB)
_borderOnVertical = true;
if (_rasterLine == _borderT && _displayEnable)
_borderOnVertical = false;
if (rasterLine == 0x030)
badlineEnable |= displayEnable;
// vblank check
if (_rasterLine == _vblankStart)
_vblank = true;
if (_rasterLine == _vblankEnd)
_vblank = false;
// badline compare
if (badlineEnable && rasterLine >= 0x030 && rasterLine < 0x0F7 && ((rasterLine & 0x7) == yScroll))
{
badline = true;
}
else
{
badline = false;
}
// reset to beginning of rasterline
_cycleIndex = 0;
_cycle = 1;
_rasterLine++;
// go into display state on a badline
if (badline)
idle = false;
if (_rasterLine == _totalLines)
{
// reset to rasterline 0
_rasterLine = 0;
_vcbase = 0;
_vc = 0;
_badlineEnable = false;
_refreshCounter = 0xFF;
_cyclesExecuted = 0;
}
}
ParseCycle();
// bg collision clear
if (_spriteBackgroundCollisionClearPending)
{
foreach (var spr in _sprites)
{
spr.CollideData = false;
}
_spriteBackgroundCollisionClearPending = false;
}
Render();
// sprite collision clear
if (_spriteSpriteCollisionClearPending)
{
foreach (var spr in _sprites)
{
spr.CollideSprite = false;
}
_spriteSpriteCollisionClearPending = false;
}
// if the BA counter is nonzero, allow CPU bus access
UpdateBA();
pinAEC = false;
// phi2
// must always come last
//UpdatePins();
}
}
// start of rasterline
if ((_cycle == RasterIrqLineXCycle && _rasterLine > 0) || (_cycle == RasterIrqLine0Cycle && _rasterLine == 0))
{
_rasterInterruptTriggered = false;
public void ExecutePhase2()
{
if (_rasterLine == LastDmaLine)
_badlineEnable = false;
}
{
ParseCycle();
// rasterline IRQ compare
if (_rasterLine != _rasterInterruptLine)
{
_rasterInterruptTriggered = false;
}
else
{
if (!_rasterInterruptTriggered)
{
_rasterInterruptTriggered = true;
_intRaster = true;
}
}
// advance cycle and optionally raster line
cycle++;
if (cycle == totalCycles)
{
if (rasterLine == borderB)
borderOnVertical = true;
if (rasterLine == borderT && displayEnable)
borderOnVertical = false;
// check top and bottom border
if (_rasterLine == _borderB)
{
_borderOnVertical = true;
}
if (_displayEnable && _rasterLine == _borderT)
{
_borderOnVertical = false;
}
if (rasterLine == vblankStart)
vblank = true;
if (rasterLine == vblankEnd)
vblank = false;
// display enable compare
if (_rasterLine == FirstDmaLine)
_badlineEnable |= _displayEnable;
cycleIndex = 0;
cycle = 0;
rasterLine++;
if (rasterLine == totalLines)
{
rasterLine = 0;
vcbase = 0;
vc = 0;
}
}
// badline compare
if (_badlineEnable)
{
if ((_rasterLine & 0x7) == _yScroll)
{
_badline = true;
Render();
UpdateBA();
pinAEC = (baCount > 0);
// go into display state on a badline
_idle = false;
}
else
{
_badline = false;
}
}
else
{
_badline = false;
}
// must always come last
UpdatePins();
}
}
// render
ParseCycle();
Render();
ParseCycle();
Render();
_extraColorModeBuffer = _extraColorMode;
private void UpdateBA()
{
if (pinBA)
baCount = baResetCounter;
else if (baCount > 0)
baCount--;
// if the BA counter is nonzero, allow CPU bus access
if (_pinBa)
_baCount = BaResetCounter;
else if (_baCount > 0)
_baCount--;
_pinAec = _pinBa || _baCount > 0;
// must always come last
UpdatePins();
_cyclesExecuted++;
}
private void UpdateBorder()
{
borderL = columnSelect ? 0x018 : 0x01F;
borderR = columnSelect ? 0x158 : 0x14F;
//borderL = columnSelect ? 28 : 35;
//borderR = columnSelect ? 348 : 339;
borderT = rowSelect ? 0x033 : 0x037;
borderB = rowSelect ? 0x0FB : 0x0F7;
_borderL = _columnSelect ? BorderLeft40 : BorderLeft38;
_borderR = _columnSelect ? BorderRight40 : BorderRight38;
_borderT = _rowSelect ? BorderTop25 : BorderTop24;
_borderB = _rowSelect ? BorderBottom25 : BorderBottom24;
}
private void UpdatePins()
{
bool irqTemp = !(
(enableIntRaster & intRaster) |
(enableIntSpriteDataCollision & intSpriteDataCollision) |
(enableIntSpriteCollision & intSpriteCollision) |
(enableIntLightPen & intLightPen));
var irqTemp = !(
(_enableIntRaster & _intRaster) |
(_enableIntSpriteDataCollision & _intSpriteDataCollision) |
(_enableIntSpriteCollision & _intSpriteCollision) |
(_enableIntLightPen & _intLightPen));
irqShift <<= 1;
irqShift |= (irqTemp ? 0x1 : 0x0);
pinIRQ = (irqShift & 0x1) != 0;
_pinIrq = irqTemp;
}
private void UpdateVideoMode()
{
if (!extraColorMode && !bitmapMode && !multicolorMode)
if (!_extraColorMode && !_bitmapMode && !_multicolorMode)
{
videoMode = 0;
_videoMode = VideoMode000;
return;
}
else if (!extraColorMode && !bitmapMode && multicolorMode)
{
videoMode = 1;
return;
}
else if (!extraColorMode && bitmapMode && !multicolorMode)
{
videoMode = 2;
return;
}
else if (!extraColorMode && bitmapMode && multicolorMode)
{
videoMode = 3;
return;
}
else if (extraColorMode && !bitmapMode && !multicolorMode)
{
videoMode = 4;
return;
}
videoMode = -1;
if (!_extraColorMode && !_bitmapMode && _multicolorMode)
{
_videoMode = VideoMode001;
return;
}
if (!_extraColorMode && _bitmapMode && !_multicolorMode)
{
_videoMode = VideoMode010;
return;
}
if (!_extraColorMode && _bitmapMode && _multicolorMode)
{
_videoMode = VideoMode011;
return;
}
if (_extraColorMode && !_bitmapMode && !_multicolorMode)
{
_videoMode = VideoMode100;
return;
}
_videoMode = VideoModeInvalid;
}
}
}

View File

@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
public static class D64
{
private static int[] densityTable =
private static readonly int[] densityTable =
{
3, 3, 3, 3, 3,
3, 3, 3, 3, 3,
@ -17,7 +18,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
0, 0, 0, 0, 0
};
private static int[] gcrDecodeTable =
private static readonly int[] gcrDecodeTable =
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //00xxx
0xFF, 0x08, 0x00, 0x01, 0xFF, 0x0C, 0x04, 0x05, //01xxx
@ -25,7 +26,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
0xFF, 0x09, 0x0A, 0x0B, 0xFF, 0x0D, 0x0E, 0xFF //11xxx
};
private static int[] gcrEncodeTable =
private static readonly int[] gcrEncodeTable =
{
Convert.ToByte("01010", 2), // 0
Convert.ToByte("01011", 2), // 1
@ -45,7 +46,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
Convert.ToByte("10101", 2) // F
};
private static int[] sectorsPerTrack =
private static readonly int[] sectorsPerTrack =
{
21, 21, 21, 21, 21,
21, 21, 21, 21, 21,
@ -57,49 +58,41 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
17, 17, 17, 17, 17
};
private static int[] standardTrackLengthBytes =
private static readonly int[] standardTrackLengthBytes =
{
6250, 6666, 7142, 7692
};
private static byte Checksum(byte[] source)
{
int count = source.Length;
var count = source.Length;
byte result = 0;
for (int i = 0; i < count; i++)
for (var i = 0; i < count; i++)
result ^= source[i];
return result;
}
private static byte[] ConvertSectorFromGCR(byte[] source, out int bitsWritten)
private static byte[] ConvertSectorToGcr(byte[] source, byte sectorNo, byte trackNo, byte formatA, byte formatB, out int bitsWritten)
{
bitsWritten = 0;
return new byte[] { };
}
private static byte[] ConvertSectorToGCR(byte[] source, byte sectorNo, byte trackNo, byte formatA, byte formatB, out int bitsWritten)
{
MemoryStream mem = new MemoryStream();
BinaryWriter writer = new BinaryWriter(mem);
byte[] writtenData;
byte headerChecksum = (byte)(sectorNo ^ trackNo ^ formatA ^ formatB);
var mem = new MemoryStream();
var writer = new BinaryWriter(mem);
var headerChecksum = (byte)(sectorNo ^ trackNo ^ formatA ^ formatB);
// assemble written data for GCR encoding
writtenData = new byte[260];
var writtenData = new byte[260];
Array.Copy(source, 0, writtenData, 1, 256);
writtenData[0] = 0x07;
writtenData[0x101] = Checksum(source);
writtenData[0x102] = 0x00;
writtenData[0x103] = 0x00;
bitsWritten = 0;
writer.Write(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }); // sync
writer.Write(EncodeGCR(new byte[] { 0x08, headerChecksum, sectorNo, trackNo, formatA, formatB, 0x0F, 0x0F })); // header
writer.Write(EncodeGcr(new byte[] { 0x08, headerChecksum, sectorNo, trackNo, formatA, formatB, 0x0F, 0x0F })); // header
writer.Write(new byte[] { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }); // gap
writer.Write(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }); // sync
writer.Write(EncodeGCR(writtenData)); // data
writer.Write(EncodeGcr(writtenData)); // data
writer.Write(new byte[] { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }); // gap
bitsWritten = (int)mem.Length * 8;
@ -108,71 +101,46 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
return mem.ToArray();
}
private static byte[] DecodeGCR(byte[] source)
{
// 5 GCR encoded bytes -> 4 bytes
int[] gcr = new int[8];
byte[] data = new byte[4];
int count = source.Length;
int outputValue;
MemoryStream mem = new MemoryStream();
BinaryWriter writer = new BinaryWriter(mem);
for (int i = 0; i < count; i += 5)
{
Array.Copy(source, i, data, 0, 5);
// -------- -------- -------- -------- --------
// 11100000 32222211 44443333 66555554 77777666
gcr[0] = ((data[0])) & 0x1F;
gcr[1] = ((data[0] >> 5) | data[1] << 3) & 0x1F;
gcr[2] = ((data[1] >> 2)) & 0x1F;
gcr[3] = ((data[1] >> 7) | data[2] << 1) & 0x1F;
gcr[4] = ((data[2] >> 4) | data[3] << 4) & 0x1F;
gcr[5] = ((data[3] >> 1)) & 0x1F;
gcr[6] = ((data[3] >> 6) | data[4] << 2) & 0x1F;
gcr[7] = ((data[4] >> 3)) & 0x1F;
outputValue = gcrDecodeTable[gcr[0]] | (gcrDecodeTable[gcr[1]] << 4);
writer.Write((byte)(outputValue & 0xFF));
outputValue = gcrDecodeTable[gcr[2]] | (gcrDecodeTable[gcr[3]] << 4);
writer.Write((byte)(outputValue & 0xFF));
outputValue = gcrDecodeTable[gcr[4]] | (gcrDecodeTable[gcr[5]] << 4);
writer.Write((byte)(outputValue & 0xFF));
outputValue = gcrDecodeTable[gcr[6]] | (gcrDecodeTable[gcr[7]] << 4);
writer.Write((byte)(outputValue & 0xFF));
}
writer.Flush();
return mem.ToArray();
}
private static byte[] EncodeGCR(byte[] source)
private static byte[] EncodeGcr(byte[] source)
{
// 4 bytes -> 5 GCR encoded bytes
int[] gcr = new int[8];
byte[] data = new byte[4];
int count = source.Length;
int outputValue;
MemoryStream mem = new MemoryStream();
BinaryWriter writer = new BinaryWriter(mem);
var gcr = new int[8];
var data = new byte[4];
var count = source.Length;
var mem = new MemoryStream();
var writer = new BinaryWriter(mem);
for (int i = 0; i < count; i += 4)
for (var i = 0; i < count; i += 4)
{
Array.Copy(source, i, data, 0, 4);
gcr[0] = gcrEncodeTable[data[0] & 0xF];
gcr[1] = gcrEncodeTable[data[0] >> 4];
gcr[2] = gcrEncodeTable[data[1] & 0xF];
gcr[3] = gcrEncodeTable[data[1] >> 4];
gcr[4] = gcrEncodeTable[data[2] & 0xF];
gcr[5] = gcrEncodeTable[data[2] >> 4];
gcr[6] = gcrEncodeTable[data[3] & 0xF];
gcr[7] = gcrEncodeTable[data[3] >> 4];
Array.Copy(source, i, data, 0, 4);
gcr[0] = gcrEncodeTable[data[0] >> 4];
gcr[1] = gcrEncodeTable[data[0] & 0xF];
gcr[2] = gcrEncodeTable[data[1] >> 4];
gcr[3] = gcrEncodeTable[data[1] & 0xF];
gcr[4] = gcrEncodeTable[data[2] >> 4];
gcr[5] = gcrEncodeTable[data[2] & 0xF];
gcr[6] = gcrEncodeTable[data[3] >> 4];
gcr[7] = gcrEncodeTable[data[3] & 0xF];
// -------- -------- -------- -------- --------
// 00000111 11222223 33334444 45555566 66677777
var outputValue = (gcr[0] << 3) | (gcr[1] >> 2);
writer.Write((byte)(outputValue & 0xFF));
outputValue = (gcr[1] << 6) | (gcr[2] << 1) | (gcr[3] >> 4);
writer.Write((byte)(outputValue & 0xFF));
outputValue = (gcr[3] << 4) | (gcr[4] >> 1);
writer.Write((byte)(outputValue & 0xFF));
outputValue = (gcr[4] << 7) | (gcr[5] << 2) | (gcr[6] >> 3);
writer.Write((byte)(outputValue & 0xFF));
outputValue = (gcr[6] << 5) | (gcr[7]);
writer.Write((byte)(outputValue & 0xFF));
/*
// -------- -------- -------- -------- --------
// 11100000 32222211 44443333 66555554 77777666
outputValue = (gcr[0]) | (gcr[1] << 5);
var outputValue = (gcr[0]) | (gcr[1] << 5);
writer.Write((byte)(outputValue & 0xFF));
outputValue = (gcr[1] >> 3) | (gcr[2] << 2) | (gcr[3] << 7);
writer.Write((byte)(outputValue & 0xFF));
@ -182,17 +150,21 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
writer.Write((byte)(outputValue & 0xFF));
outputValue = (gcr[6] >> 2) | (gcr[7] << 3);
writer.Write((byte)(outputValue & 0xFF));
}
writer.Flush();
*/
}
writer.Flush();
return mem.ToArray();
}
public static Disk Read(byte[] source)
{
MemoryStream mem = new MemoryStream(source);
BinaryReader reader = new BinaryReader(mem);
Disk result = new Disk();
int trackCount = 0;
var mem = new MemoryStream(source);
var reader = new BinaryReader(mem);
var trackDatas = new List<byte[]>();
var trackLengths = new List<int>();
var trackNumbers = new List<int>();
var trackDensities = new List<int>();
int trackCount;
switch (source.Length)
{
@ -208,42 +180,40 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
case 197376: // 40 tracks with errors
trackCount = 40;
break;
default:
throw new Exception("Not able to identify capacity of the D64 file.");
}
for (int i = 0; i < trackCount; i++)
for (var i = 0; i < trackCount; i++)
{
Track track = new Track();
int sectors = sectorsPerTrack[i];
MemoryStream trackMem = new MemoryStream();
var sectors = sectorsPerTrack[i];
var trackLengthBits = 0;
using (var trackMem = new MemoryStream())
{
for (var j = 0; j < sectors; j++)
{
int bitsWritten;
var sectorData = reader.ReadBytes(256);
var diskData = ConvertSectorToGcr(sectorData, (byte)j, (byte)(i + 1), 0xA0, 0xA0, out bitsWritten);
trackMem.Write(diskData, 0, diskData.Length);
trackLengthBits += bitsWritten;
}
var density = densityTable[i];
for (int j = 0; j < sectors; j++)
{
int bitsWritten;
byte[] sectorData = reader.ReadBytes(256);
byte[] diskData = ConvertSectorToGCR(sectorData, (byte)j, (byte)i, (byte)0x00, (byte)0x00, out bitsWritten);
trackMem.Write(diskData, 0, diskData.Length);
}
track.density = densityTable[i];
// we pad the tracks with extra gap bytes to meet MNIB standards
while (trackMem.Length < standardTrackLengthBytes[density])
{
trackMem.WriteByte(0x55);
}
// we pad the tracks with extra gap bytes to meet MNIB standards
while (trackMem.Length < standardTrackLengthBytes[track.density])
{
trackMem.WriteByte(0x55);
}
track.data = trackMem.ToArray();
track.bits = (int)trackMem.Length;
track.index = i;
result.tracks.Add(track);
trackMem.Dispose();
trackDatas.Add(trackMem.ToArray());
trackLengths.Add(trackLengthBits);
trackNumbers.Add(i * 2);
trackDensities.Add(densityTable[i]);
}
}
result.valid = (result.tracks.Count > 0);
return result;
}
public static byte[] Write(Disk source)
{
return null;
return new Disk(trackDatas, trackNumbers, trackDensities, trackLengths, 84);
}
}
}

View File

@ -1,19 +1,116 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
public class Track
public sealed class Disk
{
public int bits;
public byte[] data;
public int density;
public int index;
}
public const int FLUX_BITS_PER_ENTRY = 32;
public const int FLUX_BITS_PER_TRACK = 16000000 / 5;
public const int FLUX_ENTRIES_PER_TRACK = FLUX_BITS_PER_TRACK/FLUX_BITS_PER_ENTRY;
public class Disk
{
private class Track
{
public int Index;
public bool Present;
public int[] FluxData;
}
public List<Track> tracks = new List<Track>();
public bool valid;
}
[SaveState.DoNotSave] private readonly Track[] _tracks;
[SaveState.DoNotSave] public bool Valid;
/// <summary>
/// Create a blank, unformatted disk.
/// </summary>
public Disk(int trackCapacity)
{
_tracks = new Track[trackCapacity];
FillMissingTracks();
Valid = true;
}
/// <summary>
/// Create an expanded representation of a magnetic disk.
/// </summary>
/// <param name="trackData">Raw bit data.</param>
/// <param name="trackNumbers">Track numbers for the raw bit data.</param>
/// <param name="trackDensities">Density zones for the raw bit data.</param>
/// <param name="trackLengths">Length, in bits, of each raw bit data.</param>
/// <param name="trackCapacity">Total number of tracks on the media.</param>
public Disk(IList<byte[]> trackData, IList<int> trackNumbers, IList<int> trackDensities, IList<int> trackLengths, int trackCapacity)
{
_tracks = new Track[trackCapacity];
for (var i = 0; i < trackData.Count; i++)
{
var track = new Track
{
Index = trackNumbers[i],
Present = true,
FluxData = ConvertToFluxTransitions(trackDensities[i], trackData[i], 0)
};
_tracks[trackNumbers[i]] = track;
}
FillMissingTracks();
Valid = true;
}
private int[] ConvertToFluxTransitions(int density, byte[] bytes, int fluxBitOffset)
{
var result = new int[FLUX_ENTRIES_PER_TRACK];
var length = bytes.Length;
var lengthBits = length*8;
var offsets = new List<long>();
const long bitsNum = FLUX_ENTRIES_PER_TRACK * FLUX_BITS_PER_ENTRY;
long bitsDen = lengthBits;
for (var i = 0; i < length; i++)
{
var byteData = bytes[i];
for (var j = 0; j < 8; j++)
{
var offset = fluxBitOffset + ((i * 8 + j) * bitsNum / bitsDen);
var byteOffset = (int)(offset / FLUX_BITS_PER_ENTRY);
var bitOffset = (int)(offset % FLUX_BITS_PER_ENTRY);
offsets.Add(offset);
result[byteOffset] |= ((byteData & 0x80) != 0 ? 1 : 0) << bitOffset;
byteData <<= 1;
}
}
return result;
}
private void FillMissingTracks()
{
for (var i = 0; i < _tracks.Length; i++)
{
if (_tracks[i] == null)
{
_tracks[i] = new Track
{
Index = i,
FluxData = new int[FLUX_ENTRIES_PER_TRACK]
};
}
}
}
public int[] GetDataForTrack(int halftrack)
{
return _tracks[halftrack].FluxData;
}
public IEnumerable<int> GetPresentTracks()
{
return _tracks.Where(t => t.Present).Select(t => t.Index);
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -1,59 +1,62 @@
using System.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
public static class G64
{
public static Disk Read(byte[] source)
{
MemoryStream mem = new MemoryStream(source);
BinaryReader reader = new BinaryReader(mem);
Disk result = new Disk();
string id = new string(reader.ReadChars(8));
var mem = new MemoryStream(source);
var reader = new BinaryReader(mem);
var id = new string(reader.ReadChars(8));
var trackDatas = new List<byte[]>();
var trackLengths = new List<int>();
var trackNumbers = new List<int>();
var trackDensities = new List<int>();
if (id == @"GCR-1541")
if (id == @"GCR-1541")
{
int trackCount;
int[] trackOffsetTable = new int[84];
int[] trackSpeedTable = new int[84];
reader.ReadByte(); //version
trackCount = reader.ReadByte();
int trackCount = reader.ReadByte();
reader.ReadInt16(); //max track size in bytes
for (int i = 0; i < 84; i++)
var trackOffsetTable = new int[trackCount];
var trackSpeedTable = new int[trackCount];
for (var i = 0; i < trackCount; i++)
trackOffsetTable[i] = reader.ReadInt32();
for (int i = 0; i < 84; i++)
for (var i = 0; i < trackCount; i++)
trackSpeedTable[i] = reader.ReadInt32();
for (int i = 0; i < 84; i++)
for (var i = 0; i < trackCount; i++)
{
if (trackOffsetTable[i] > 0)
{
int trackLength;
byte[] trackData;
Track track = new Track();
mem.Position = trackOffsetTable[i];
trackLength = reader.ReadInt16();
trackData = reader.ReadBytes(trackLength);
track.bits = trackLength * 8;
track.data = trackData;
track.density = trackSpeedTable[i];
track.index = i;
result.tracks.Add(track);
int trackLength = reader.ReadInt16();
var trackData = reader.ReadBytes(trackLength);
trackDatas.Add(trackData);
trackLengths.Add(trackLength * 8);
trackDensities.Add(trackSpeedTable[i]);
trackNumbers.Add(i);
}
}
if (trackSpeedTable.Any(ts => ts > 3 || ts < 0))
{
throw new Exception("Byte-level speeds are not yet supported in the G64 loader.");
}
return new Disk(trackDatas, trackNumbers, trackDensities, trackLengths, 84);
}
result.valid = (result.tracks.Count > 0);
return result;
return new Disk(84);
}
public static byte[] Write(Disk source)
{
return null;
}
}
}
}

View File

@ -1,26 +1,28 @@
using BizHawk.Emulation.Cores.Computers.Commodore64.MOS;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
public static class PRG
public static class Prg
{
static public void Load(MOSPLA pla, byte[] prgFile)
public static void Load(Chip90611401 pla, byte[] prgFile)
{
int length = prgFile.Length;
if (length > 2)
{
int addr = (prgFile[0] | (prgFile[1] << 8));
int offset = 2;
unchecked
{
while (offset < length)
{
pla.Write(addr, prgFile[offset]);
offset++;
addr++;
}
}
}
var length = prgFile.Length;
if (length <= 2)
{
return;
}
var addr = prgFile[0] | (prgFile[1] << 8);
var offset = 2;
unchecked
{
while (offset < length)
{
pla.Write(addr, prgFile[offset]);
offset++;
addr++;
}
}
}
}
}

View File

@ -0,0 +1,101 @@
using System;
using System.Text;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
/**
* This class represents a tape. Only TAP-style tapes are supported for now.
*/
public class Tape
{
[SaveState.DoNotSave] private readonly byte[] _tapeData;
[SaveState.DoNotSave] private readonly byte _version;
[SaveState.DoNotSave] private readonly int _start;
[SaveState.DoNotSave] private readonly int _end;
private int _pos, _cycle;
private bool _data;
public Tape(byte version, byte[] tapeData, int start, int end)
{
_version = version;
_tapeData = tapeData;
_start = start;
_end = end;
Rewind();
}
public void ExecuteCycle()
{
if (_cycle == 0)
{
if (_pos >= _end)
{
_data = true;
return;
}
_cycle = _tapeData[_pos++] * 8;
if (_cycle == 0)
{
if (_version == 0)
{
_cycle = 256 * 8; // unspecified overflow condition
}
else
{
_cycle = (int)(BitConverter.ToUInt32(_tapeData, _pos - 1) >> 8);
_pos += 3;
if (_cycle == 0)
{
throw new Exception("Bad tape data");
}
}
}
_cycle++;
}
// Send a single negative pulse at the end of a cycle
_data = --_cycle != 0;
}
// Rewinds the tape back to start
public void Rewind()
{
_pos = _start;
_cycle = 0;
}
// Reads from tape, this will tell the caller if the flag pin should be raised
public bool Read()
{
return _data;
}
// Try to construct a tape file from file data. Returns null if not a tape file, throws exceptions for bad tape files.
// (Note that some error conditions aren't caught right here.)
public static Tape Load(byte[] tapeFile)
{
Tape result = null;
if (Encoding.ASCII.GetString(tapeFile, 0, 12) == "C64-TAPE-RAW")
{
var version = tapeFile[12];
if (version > 1) throw new Exception("This tape has an unsupported version");
var size = BitConverter.ToUInt32(tapeFile, 16);
if (size + 20 != tapeFile.Length)
{
throw new Exception("Tape file header specifies a length that doesn't match the file size");
}
result = new Tape(version, tapeFile, 20, tapeFile.Length);
}
return result;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
@ -9,204 +10,204 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
internal static class SaveState
{
static private Encoding encoding = Encoding.Unicode;
public class DoNotSave : Attribute
{
}
static public void SyncObject(Serializer ser, object obj)
private static readonly Encoding Encoding = Encoding.Unicode;
public static void SyncObject(Serializer ser, object obj)
{
BindingFlags defaultFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
MemberInfo[] members = obj.GetType().GetMembers(defaultFlags);
const BindingFlags defaultFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
var objType = obj.GetType();
var members = objType.GetMembers(defaultFlags);
Bit refBit;
Boolean refBool;
Byte refByte;
ByteBuffer refByteBuffer;
Int16 refInt16;
Int32 refInt32;
IntBuffer refIntBuffer;
Int32 refPointX;
Int32 refPointY;
SByte refSByte;
UInt16 refUInt16;
UInt32 refUInt32;
Int32 refRectHeight;
Int32 refRectWidth;
foreach (MemberInfo member in members)
foreach (var member in members)
{
object currentValue = null;
bool fail = false;
FieldInfo fieldInfo = null;
PropertyInfo propInfo = null;
Type valueType = null;
if (member.GetCustomAttributes(true).Any(a => a is DoNotSave))
{
continue;
}
if (member.MemberType == MemberTypes.Field)
object currentValue = null;
var fail = false;
var fieldInfo = member as FieldInfo;
Type valueType = null;
if ((member.MemberType == MemberTypes.Field) && member.ReflectedType != null)
{
fieldInfo = member.ReflectedType.GetField(member.Name, defaultFlags);
valueType = fieldInfo.FieldType;
currentValue = fieldInfo.GetValue(obj);
}
else
{
fail = true;
}
valueType = fieldInfo.FieldType;
currentValue = fieldInfo.GetValue(obj);
}
if (!fail)
{
if (valueType.IsArray)
{
}
if (currentValue != null)
{
ByteBuffer refByteBuffer;
int refInt32;
IntBuffer refIntBuffer;
int refPointX;
int refPointY;
switch (valueType.Name)
{
case "Action`1":
case "Action`2":
break;
case "Bit":
var refBit = (Bit)currentValue;
ser.Sync(member.Name, ref refBit);
currentValue = refBit;
break;
case "Boolean":
var refBool = (bool)currentValue;
ser.Sync(member.Name, ref refBool);
currentValue = refBool;
break;
case "Boolean[]":
{
var tmp = (bool[])currentValue;
ser.Sync(member.Name, ref tmp, false);
currentValue = tmp;
}
break;
case "Byte":
var refByte = (byte)currentValue;
ser.Sync(member.Name, ref refByte);
currentValue = refByte;
break;
case "Byte[]":
refByteBuffer = new ByteBuffer((byte[])currentValue);
ser.Sync(member.Name, ref refByteBuffer);
currentValue = refByteBuffer.Arr;
break;
case "ByteBuffer":
refByteBuffer = (ByteBuffer)currentValue;
ser.Sync(member.Name, ref refByteBuffer);
currentValue = refByteBuffer;
break;
case "Func`1":
case "Func`2":
break;
case "Int16":
var refInt16 = (short)currentValue;
ser.Sync(member.Name, ref refInt16);
currentValue = refInt16;
break;
case "Int32":
refInt32 = (int)currentValue;
ser.Sync(member.Name, ref refInt32);
currentValue = refInt32;
break;
case "Int32[]":
refIntBuffer = new IntBuffer((int[])currentValue);
ser.Sync(member.Name, ref refIntBuffer);
currentValue = refIntBuffer.Arr;
break;
case "IntBuffer":
refIntBuffer = (IntBuffer)currentValue;
ser.Sync(member.Name, ref refIntBuffer);
currentValue = refIntBuffer;
break;
case "Point":
refPointX = ((Point)currentValue).X;
refPointY = ((Point)currentValue).Y;
ser.Sync(member.Name + "_X", ref refPointX);
ser.Sync(member.Name + "_Y", ref refPointY);
currentValue = new Point(refPointX, refPointY);
break;
case "Rectangle":
refPointX = ((Rectangle)currentValue).X;
refPointY = ((Rectangle)currentValue).Y;
var refRectWidth = ((Rectangle)currentValue).Width;
var refRectHeight = ((Rectangle)currentValue).Height;
ser.Sync(member.Name + "_X", ref refPointX);
ser.Sync(member.Name + "_Y", ref refPointY);
ser.Sync(member.Name + "_Height", ref refRectHeight);
ser.Sync(member.Name + "_Width", ref refRectWidth);
currentValue = new Rectangle(refPointX, refPointY, refRectWidth, refRectHeight);
break;
case "SByte":
var refSByte = (sbyte)currentValue;
ser.Sync(member.Name, ref refSByte);
currentValue = refSByte;
break;
case "String":
var refString = (string)currentValue;
var refVal = new ByteBuffer(Encoding.GetBytes(refString));
ser.Sync(member.Name, ref refVal);
currentValue = Encoding.GetString(refVal.Arr);
break;
case "UInt16":
var refUInt16 = (ushort)currentValue;
ser.Sync(member.Name, ref refUInt16);
currentValue = refUInt16;
break;
case "UInt32":
var refUInt32 = (uint)currentValue;
ser.Sync(member.Name, ref refUInt32);
currentValue = refUInt32;
break;
default:
var t = currentValue.GetType();
if (t.IsEnum)
{
refInt32 = (int)currentValue;
ser.Sync(member.Name, ref refInt32);
currentValue = refInt32;
}
else if (t.IsArray)
{
var currentValueArray = (Array) currentValue;
for (var i = 0; i < currentValueArray.Length; i++)
{
ser.BeginSection(string.Format("{0}_{1}", member.Name, i));
SyncObject(ser, currentValueArray.GetValue(i));
ser.EndSection();
}
}
else if (t.IsValueType)
{
fail = true;
}
else if (t.IsClass)
{
fail = true;
foreach (var method in t.GetMethods().Where(method => method.Name == "SyncState"))
{
ser.BeginSection(fieldInfo.Name);
method.Invoke(currentValue, new object[] { ser });
ser.EndSection();
fail = false;
break;
}
}
else
{
fail = true;
}
break;
}
}
if (currentValue != null)
{
switch (valueType.Name)
{
case "Bit":
refBit = (Bit)currentValue;
ser.Sync(member.Name, ref refBit);
currentValue = refBit;
break;
case "Boolean":
refBool = (Boolean)currentValue;
ser.Sync(member.Name, ref refBool);
currentValue = refBool;
break;
case "Boolean[]":
{
bool[] tmp = (bool[])currentValue;
ser.Sync(member.Name, ref tmp, false);
currentValue = tmp;
}
break;
case "Byte":
refByte = (Byte)currentValue;
ser.Sync(member.Name, ref refByte);
currentValue = refByte;
break;
case "Byte[]":
refByteBuffer = new ByteBuffer((byte[])currentValue);
ser.Sync(member.Name, ref refByteBuffer);
currentValue = refByteBuffer.Arr;
break;
case "ByteBuffer":
refByteBuffer = (ByteBuffer)currentValue;
ser.Sync(member.Name, ref refByteBuffer);
currentValue = refByteBuffer;
break;
case "Func`1":
break;
case "Int16":
refInt16 = (Int16)currentValue;
ser.Sync(member.Name, ref refInt16);
currentValue = refInt16;
break;
case "Int32":
refInt32 = (Int32)currentValue;
ser.Sync(member.Name, ref refInt32);
currentValue = refInt32;
break;
case "Int32[]":
refIntBuffer = new IntBuffer((int[])currentValue);
ser.Sync(member.Name, ref refIntBuffer);
currentValue = refIntBuffer.Arr;
break;
case "IntBuffer":
refIntBuffer = (IntBuffer)currentValue;
ser.Sync(member.Name, ref refIntBuffer);
currentValue = refIntBuffer;
break;
case "Point":
refPointX = ((Point)currentValue).X;
refPointY = ((Point)currentValue).Y;
ser.Sync(member.Name + "_X", ref refPointX);
ser.Sync(member.Name + "_Y", ref refPointY);
currentValue = new Point(refPointX, refPointY);
break;
case "Rectangle":
refPointX = ((Rectangle)currentValue).X;
refPointY = ((Rectangle)currentValue).Y;
refRectWidth = ((Rectangle)currentValue).Width;
refRectHeight = ((Rectangle)currentValue).Height;
ser.Sync(member.Name + "_X", ref refPointX);
ser.Sync(member.Name + "_Y", ref refPointY);
ser.Sync(member.Name + "_Height", ref refRectHeight);
ser.Sync(member.Name + "_Width", ref refRectWidth);
currentValue = new Rectangle(refPointX, refPointY, refRectWidth, refRectHeight);
break;
case "SByte":
refSByte = (SByte)currentValue;
ser.Sync(member.Name, ref refSByte);
currentValue = refSByte;
break;
case "String":
{
var refString = (String)currentValue;
var refVal = new ByteBuffer(encoding.GetBytes(refString));
ser.Sync(member.Name, ref refVal);
currentValue = encoding.GetString(refVal.Arr);
}
break;
case "UInt16":
refUInt16 = (UInt16)currentValue;
ser.Sync(member.Name, ref refUInt16);
currentValue = refUInt16;
break;
case "UInt32":
refUInt32 = (UInt32)currentValue;
ser.Sync(member.Name, ref refUInt32);
currentValue = refUInt32;
break;
default:
{
Type t = currentValue.GetType();
if (t.IsEnum)
{
refInt32 = (Int32)currentValue;
ser.Sync(member.Name, ref refInt32);
currentValue = refInt32;
}
else if (t.IsValueType)
{
fail = true;
}
else if (t.IsClass)
{
fail = true;
foreach (var method in t.GetMethods())
{
if (method.Name == "SyncState")
{
ser.BeginSection(fieldInfo.Name);
method.Invoke(currentValue, new object[] { (Serializer)ser });
ser.EndSection();
fail = false;
break;
}
}
}
else
{
fail = true;
}
}
break;
}
}
if (member.MemberType == MemberTypes.Property)
{
if (propInfo.CanWrite && !fail)
{
MethodInfo setMethod = propInfo.GetSetMethod();
setMethod.Invoke(obj, new object[] { currentValue });
}
}
if (member.MemberType == MemberTypes.Field)
{
fieldInfo.SetValue(obj, currentValue);
}
}
}
if (!fail)
{
if (member.MemberType == MemberTypes.Property)
{
var propInfo = member as PropertyInfo;
if (propInfo.CanWrite)
{
var setMethod = propInfo.GetSetMethod();
if (setMethod != null)
{
setMethod.Invoke(obj, new[] { currentValue });
}
}
}
else if (member.MemberType == MemberTypes.Field)
{
fieldInfo.SetValue(obj, currentValue);
}
}
}
}
}
}

View File

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Cores.Computers.Commodore64.Media;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
public sealed partial class Drive1541
{
private const long LEHMER_RNG_PRIME = 48271;
private int _diskDensityCounter; // density .. 16
private int _diskSupplementaryCounter; // 0 .. 16
private bool _diskFluxReversalDetected;
private int _diskBitsLeft;
private int _diskByteOffset;
private int _diskBits;
private int _diskCycle;
private int _diskDensity;
private bool _previousCa1;
private int _countsBeforeRandomTransition;
private int _rngCurrent;
// Lehmer RNG
private void AdvanceRng()
{
if (_rngCurrent == 0)
_rngCurrent = 1;
_rngCurrent = (int)(_rngCurrent * LEHMER_RNG_PRIME % int.MaxValue);
}
private void ExecuteFlux()
{
for (_diskCycle = 0; _diskCycle < 16; _diskCycle++)
{
// rotate disk
if (_motorEnabled)
{
if (_diskBitsLeft <= 0)
{
_diskByteOffset++;
if (_diskByteOffset == Disk.FLUX_ENTRIES_PER_TRACK)
{
_diskByteOffset = 0;
}
_diskBits = _trackImageData[_diskByteOffset];
_diskBitsLeft = Disk.FLUX_BITS_PER_ENTRY;
}
if ((_diskBits & 1) != 0)
{
_countsBeforeRandomTransition = 0;
_diskFluxReversalDetected = true;
}
_diskBits >>= 1;
_diskBitsLeft--;
}
// random flux transition readings for unformatted data
if (_countsBeforeRandomTransition > 0)
{
_countsBeforeRandomTransition--;
if (_countsBeforeRandomTransition == 0)
{
_diskFluxReversalDetected = true;
AdvanceRng();
// This constant is what VICE uses. TODO: Determine accuracy.
_countsBeforeRandomTransition = (_rngCurrent % 367) + 33;
}
}
// flux transition circuitry
if (_diskFluxReversalDetected)
{
_diskDensityCounter = _diskDensity;
_diskSupplementaryCounter = 0;
_diskFluxReversalDetected = false;
if (_countsBeforeRandomTransition == 0)
{
AdvanceRng();
// This constant is what VICE uses. TODO: Determine accuracy.
_countsBeforeRandomTransition = (_rngCurrent & 0x1F) + 289;
}
}
// counter circuitry
if (_diskDensityCounter >= 16)
{
_diskDensityCounter = _diskDensity;
_diskSupplementaryCounter++;
if ((_diskSupplementaryCounter & 0x3) == 0x2)
{
_byteReady = false;
_bitsRemainingInLatchedByte--;
if (_bitsRemainingInLatchedByte <= 0)
{
_bitsRemainingInLatchedByte = 8;
// SOE (sync output enabled)
_byteReady = Via1.Ca2;
}
_bitHistory = (_bitHistory << 1) | ((_diskSupplementaryCounter & 0xC) == 0x0 ? 1 : 0);
_sync = false;
if (Via1.Cb2 && (_bitHistory & 0x3FF) == 0x3FF)
{
_sync = true;
_bitsRemainingInLatchedByte = 8;
_byteReady = false;
}
// negative transition activates SO pin on CPU
_previousCa1 = Via1.Ca1;
Via1.Ca1 = !_byteReady;
if (_previousCa1 && !Via1.Ca1)
{
// cycle 6 is roughly 400ns
_overflowFlagDelaySr |= _diskCycle > 6 ? 4 : 2;
}
}
}
if (_diskSupplementaryCounter >= 16)
{
_diskSupplementaryCounter = 0;
}
_diskDensityCounter++;
}
}
}
}

View File

@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
public sealed partial class Drive1541 : IDebuggable
{
IDictionary<string, RegisterValue> IDebuggable.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 }
};
}
void IDebuggable.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;
}
}
bool IDebuggable.CanStep(StepType type)
{
switch (type)
{
case StepType.Into:
case StepType.Over:
case StepType.Out:
return DebuggerStep != null;
default:
return false;
}
}
void IDebuggable.Step(StepType type)
{
switch (type)
{
case StepType.Into:
StepInto();
break;
case StepType.Out:
StepOut();
break;
case StepType.Over:
StepOver();
break;
}
}
private void StepInto()
{
while (_cpu.AtInstructionStart())
{
DebuggerStep();
}
while (!_cpu.AtInstructionStart())
{
DebuggerStep();
}
}
private void StepOver()
{
var instruction = CpuPeek(_cpu.PC);
if (instruction == Jsr)
{
var destination = _cpu.PC + JsrSize;
while (_cpu.PC != destination)
{
StepInto();
}
}
else
{
StepInto();
}
}
private void StepOut()
{
var instructionsBeforeBailout = 1000000;
var instr = CpuPeek(_cpu.PC);
_jsrCount = instr == Jsr ? 1 : 0;
while (--instructionsBeforeBailout > 0)
{
StepInto();
instr = CpuPeek(_cpu.PC);
if (instr == Jsr)
{
_jsrCount++;
}
else if ((instr == Rts || instr == Rti) && _jsrCount <= 0)
{
StepInto();
_jsrCount = 0;
break;
}
else if (instr == Rts || instr == Rti)
{
_jsrCount--;
}
}
}
[SaveState.DoNotSave]
private int _jsrCount;
[SaveState.DoNotSave]
private const byte Jsr = 0x20;
[SaveState.DoNotSave]
private const byte Rti = 0x40;
[SaveState.DoNotSave]
private const byte Rts = 0x60;
[SaveState.DoNotSave]
private const byte JsrSize = 3;
[SaveState.DoNotSave]
public IMemoryCallbackSystem MemoryCallbacks { get; private set; }
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
public sealed partial class Drive1541 : IDisassemblable
{
IEnumerable<string> IDisassemblable.AvailableCpus
{
get { yield return "Disk Drive 6502"; }
}
string IDisassemblable.Cpu
{
get { return "Disk Drive 6502"; }
set
{
}
}
string IDisassemblable.PCRegisterName
{
get { return "PC"; }
}
string IDisassemblable.Disassemble(MemoryDomain m, uint addr, out int length)
{
return Components.M6502.MOS6502X.Disassemble((ushort)addr, out length, CpuPeek);
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
public sealed partial class Drive1541
{
[SaveState.DoNotSave] private int _tempStep;
[SaveState.DoNotSave] private int _tempPrB1;
private int _overflowFlagDelaySr;
private int ReadVia1PrA()
{
return _bitHistory & 0xFF;
}
private int ReadVia1PrB()
{
return (_motorStep & 0x03) | (_motorEnabled ? 0x04 : 0x00) | (_sync ? 0x00 : 0x80);
}
private void ExecuteMotor()
{
_tempPrB1 = Via1.EffectivePrB;
_tempStep = _tempPrB1 & 0x3;
_diskDensity = (_tempPrB1 & 0x60) >> 5;
_motorEnabled = (_tempPrB1 & 0x04) != 0;
_ledEnabled = (_tempPrB1 & 0x08) != 0;
// motor track stepping
if (_tempStep != _motorStep)
{
if (_tempStep == ((_motorStep - 1) & 0x3))
_trackNumber--;
else if (_tempStep == ((_motorStep + 1) & 0x3))
_trackNumber++;
if (_trackNumber < 0)
_trackNumber = 0;
else if (_trackNumber > 83)
_trackNumber = 83;
_motorStep = _tempStep;
UpdateMediaData();
}
}
}
}

View File

@ -0,0 +1,309 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.M6502;
using BizHawk.Emulation.Cores.Computers.Commodore64.Media;
using BizHawk.Emulation.Cores.Computers.Commodore64.MOS;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
public sealed partial class Drive1541 : SerialPortDevice
{
private Disk _disk;
private int _bitHistory;
private int _bitsRemainingInLatchedByte;
private bool _sync;
private bool _byteReady;
[SaveState.DoNotSave] private readonly int _driveCpuClockNum;
private int _trackNumber;
private bool _motorEnabled;
private bool _ledEnabled;
private int _motorStep;
private int _via0PortBtemp;
private readonly MOS6502X _cpu;
private readonly int[] _ram;
public readonly Via Via0;
public readonly Via Via1;
private readonly int _cpuClockNum;
private int _ratioDifference;
private int _driveLightOffTime;
[SaveState.DoNotSave] private int[] _trackImageData = new int[1];
public Func<int> ReadIec = () => 0xFF;
public Action DebuggerStep;
public readonly Chip23128 DriveRom;
public Drive1541(int clockNum, int clockDen)
{
DriveRom = new Chip23128();
_cpu = new MOS6502X
{
ReadMemory = CpuRead,
WriteMemory = CpuWrite,
DummyReadMemory = CpuRead,
PeekMemory = CpuPeek,
NMI = false
};
_ram = new int[0x800];
Via0 = new Via(ViaReadClock, ViaReadData, ViaReadAtn, 8);
Via1 = new Via(ReadVia1PrA, ReadVia1PrB);
_cpuClockNum = clockNum;
_driveCpuClockNum = clockDen*1000000; // 1mhz
}
private byte CpuPeek(ushort addr)
{
return unchecked((byte)Peek(addr));
}
private byte CpuRead(ushort addr)
{
return unchecked((byte) Read(addr));
}
private void CpuWrite(ushort addr, byte val)
{
Write(addr, val);
}
private bool ViaReadClock()
{
var inputClock = ReadMasterClk();
var outputClock = ReadDeviceClk();
return !(inputClock && outputClock);
}
private bool ViaReadData()
{
var inputData = ReadMasterData();
var outputData = ReadDeviceData();
return !(inputData && outputData);
}
private bool ViaReadAtn()
{
var inputAtn = ReadMasterAtn();
return !inputAtn;
}
public override void ExecutePhase()
{
if (_cpuClockNum > _driveCpuClockNum)
{
_ratioDifference += _cpuClockNum - _driveCpuClockNum;
if (_ratioDifference > _cpuClockNum)
{
_ratioDifference -= _cpuClockNum;
return;
}
}
else if (_cpuClockNum <= _driveCpuClockNum)
{
_ratioDifference += _driveCpuClockNum - _cpuClockNum;
while (_ratioDifference > _driveCpuClockNum)
{
_ratioDifference -= _driveCpuClockNum;
ExecutePhaseInternal();
}
}
ExecutePhaseInternal();
}
private void ExecutePhaseInternal()
{
Via0.Ca1 = ViaReadAtn();
// clock output from 325572-01 drives CPU clock (phi0)
ExecuteMotor();
ExecuteFlux();
Via0.ExecutePhase();
Via1.ExecutePhase();
// SO pin pipeline
if ((_overflowFlagDelaySr & 0x01) != 0)
{
_cpu.SetOverflow();
}
_overflowFlagDelaySr >>= 1;
_cpu.IRQ = !(Via0.Irq && Via1.Irq); // active low IRQ line
_cpu.ExecuteOne();
_via0PortBtemp = Via0.EffectivePrB;
_ledEnabled = (_via0PortBtemp & 0x08) != 0;
if (_ledEnabled)
{
_driveLightOffTime = 1000000;
}
else if (_driveLightOffTime > 0)
{
_driveLightOffTime--;
}
}
public override void HardReset()
{
Via0.HardReset();
Via1.HardReset();
_trackNumber = 34;
for (var i = 0; i < _ram.Length; i++)
{
_ram[i] = 0x00;
}
_diskDensity = 0;
_diskFluxReversalDetected = false;
_diskByteOffset = 0;
_diskBitsLeft = 0;
_diskBits = 0;
_driveLightOffTime = 0;
_diskDensityCounter = 0;
_diskSupplementaryCounter = 0;
_diskCycle = 0;
_previousCa1 = false;
_countsBeforeRandomTransition = 0;
SoftReset();
UpdateMediaData();
}
public void SoftReset()
{
_cpu.NESSoftReset();
_overflowFlagDelaySr = 0;
}
public void InsertMedia(Disk disk)
{
_disk = disk;
UpdateMediaData();
}
private void UpdateMediaData()
{
if (_disk != null)
{
_trackImageData = _disk.GetDataForTrack(_trackNumber);
_diskBits = _trackImageData[_diskByteOffset] >> (Disk.FLUX_BITS_PER_ENTRY - _diskBitsLeft);
}
}
public void RemoveMedia()
{
_trackImageData = new int[1];
}
public int Peek(int addr)
{
switch (addr & 0xFC00)
{
case 0x1800:
return Via0.Peek(addr);
case 0x1C00:
return Via1.Peek(addr);
}
if ((addr & 0x8000) != 0)
return DriveRom.Peek(addr & 0x3FFF);
if ((addr & 0x1F00) < 0x800)
return _ram[addr & 0x7FF];
return (addr >> 8) & 0xFF;
}
public int PeekVia0(int addr)
{
return Via0.Peek(addr);
}
public int PeekVia1(int addr)
{
return Via1.Peek(addr);
}
public void Poke(int addr, int val)
{
switch (addr & 0xFC00)
{
case 0x1800:
Via0.Poke(addr, val);
break;
case 0x1C00:
Via1.Poke(addr, val);
break;
default:
if ((addr & 0x8000) == 0 && (addr & 0x1F00) < 0x800)
_ram[addr & 0x7FF] = val & 0xFF;
break;
}
}
public void PokeVia0(int addr, int val)
{
Via0.Poke(addr, val);
}
public void PokeVia1(int addr, int val)
{
Via1.Poke(addr, val);
}
public int Read(int addr)
{
switch (addr & 0xFC00)
{
case 0x1800:
return Via0.Read(addr);
case 0x1C00:
return Via1.Read(addr);
}
if ((addr & 0x8000) != 0)
return DriveRom.Read(addr & 0x3FFF);
if ((addr & 0x1F00) < 0x800)
return _ram[addr & 0x7FF];
return (addr >> 8) & 0xFF;
}
public void Write(int addr, int val)
{
switch (addr & 0xFC00)
{
case 0x1800:
Via0.Write(addr, val);
break;
case 0x1C00:
Via1.Write(addr, val);
break;
default:
if ((addr & 0x8000) == 0 && (addr & 0x1F00) < 0x800)
_ram[addr & 0x7FF] = val & 0xFF;
break;
}
}
public override bool ReadDeviceClk()
{
var viaOutputClock = (Via0.DdrB & 0x08) != 0 && (Via0.PrB & 0x08) != 0;
return !viaOutputClock;
}
public override bool ReadDeviceData()
{
var viaOutputData = (Via0.DdrB & 0x02) != 0 && (Via0.PrB & 0x02) != 0;
var viaInputAtn = ViaReadAtn();
var viaOutputAtna = (Via0.DdrB & 0x10) != 0 && (Via0.PrB & 0x10) != 0;
return !(viaOutputAtna ^ viaInputAtn) && !viaOutputData;
}
public override bool ReadDeviceLight()
{
return _driveLightOffTime > 0;
}
}
}

View File

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Computers.Commodore64.Cassette;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
public sealed class SerialPort : IDriveLight
{
public Func<bool> ReadMasterAtn = () => true;
public Func<bool> ReadMasterClk = () => true;
public Func<bool> ReadMasterData = () => true;
private SerialPortDevice _device;
private bool _connected;
public void HardReset()
{
if (_connected)
{
_device.HardReset();
}
}
public void ExecutePhase()
{
if (_connected)
{
_device.ExecutePhase();
}
}
public void ExecuteDeferred(int cycles)
{
if (_connected)
{
_device.ExecuteDeferred(cycles);
}
}
public bool ReadDeviceClock()
{
return !_connected || _device.ReadDeviceClk();
}
public bool ReadDeviceData()
{
return !_connected || _device.ReadDeviceData();
}
public bool ReadDeviceLight()
{
return _connected && _device.ReadDeviceLight();
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
public void Connect(SerialPortDevice device)
{
_connected = device != null;
_device = device;
if (_device == null)
{
return;
}
_device.ReadMasterAtn = () => ReadMasterAtn();
_device.ReadMasterClk = () => ReadMasterClk();
_device.ReadMasterData = () => ReadMasterData();
}
public bool DriveLightEnabled { get { return true; } }
public bool DriveLightOn { get { return ReadDeviceLight(); } }
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
public abstract class SerialPortDevice
{
public Func<bool> ReadMasterAtn;
public Func<bool> ReadMasterClk;
public Func<bool> ReadMasterData;
public virtual void ExecutePhase()
{
}
public virtual void ExecuteDeferred(int cycles)
{
}
public virtual void HardReset()
{
}
public virtual bool ReadDeviceClk()
{
return true;
}
public virtual bool ReadDeviceData()
{
return true;
}
public virtual bool ReadDeviceLight()
{
return false;
}
public virtual void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.User
{
public sealed class UserPort
{
public Func<bool> ReadCounter1;
public Func<bool> ReadCounter2;
public Func<bool> ReadHandshake;
public Func<bool> ReadSerial1;
public Func<bool> ReadSerial2;
private bool _connected;
private UserPortDevice _device;
public void Connect(UserPortDevice device)
{
_device = device;
_connected = _device != null;
if (_device != null)
{
_device.ReadCounter1 = () => ReadCounter1();
_device.ReadCounter2 = () => ReadCounter2();
_device.ReadHandshake = () => ReadHandshake();
_device.ReadSerial1 = () => ReadSerial1();
_device.ReadSerial2 = () => ReadSerial2();
}
}
public void Disconnect()
{
_connected = false;
_device = null;
}
public void HardReset()
{
if (_connected)
{
_device.HardReset();
}
}
public bool ReadAtn()
{
return !_connected || _device.ReadAtn();
}
public int ReadData()
{
return !_connected ? 0xFF : _device.ReadData();
}
public bool ReadFlag2()
{
return !_connected || _device.ReadFlag2();
}
public bool ReadPa2()
{
return !_connected || _device.ReadPa2();
}
public bool ReadReset()
{
return !_connected || _device.ReadReset();
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

Some files were not shown because too many files have changed in this diff Show More