Merge pull request from TASVideos/c64-via-timer-fixes

C64 via timer fixes (and more)
This commit is contained in:
Tony Konzel 2016-03-15 10:08:17 -05:00
commit 05914e1490
36 changed files with 2068 additions and 1031 deletions

View File

@ -788,7 +788,7 @@ namespace BizHawk.Client.Common
nextEmulator = new Atari7800(nextComm, game, rom.RomData, gamedbpath);
break;
case "C64":
var c64 = new C64(nextComm, game, rom.RomData, rom.Extension, GetCoreSettings<C64>(), GetCoreSyncSettings<C64>());
var c64 = new C64(nextComm, game, rom.RomData, GetCoreSettings<C64>(), GetCoreSyncSettings<C64>());
nextEmulator = c64;
break;
case "GBA":

View File

@ -208,20 +208,25 @@
</Compile>
<Compile Include="Computers\Commodore64\C64.Motherboard.cs" />
<Compile Include="Computers\Commodore64\C64.MotherboardInterface.cs" />
<Compile Include="Computers\Commodore64\C64Format.cs" />
<Compile Include="Computers\Commodore64\C64Util.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Mapper0000.cs" />
<Compile Include="Computers\Commodore64\C64.Input.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Mapper0001.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Mapper0005.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Mapper000A.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Mapper000B.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Mapper000F.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Mapper0011.cs" />
<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\Cartridge\Mapper002B.cs" />
<Compile Include="Computers\Commodore64\Cassette\CassettePortDevice.cs" />
<Compile Include="Computers\Commodore64\Cassette\TapeDrive.cs" />
<Compile Include="Computers\Commodore64\C64FormatFinder.cs" />
<Compile Include="Computers\Commodore64\Media\DiskBuilder.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\CartridgeDevice.cs" />
<Compile Include="Computers\Commodore64\Cartridge\CartridgePort.cs" />
@ -231,6 +236,7 @@
<Compile Include="Computers\Commodore64\MOS\Chip4864.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6510.IDebuggable.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6510.IDisassemblable.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6522.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6581R3.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip6581R4AR.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip8580R5.cs" />
@ -263,6 +269,7 @@
<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\Drive1541.Registers.cs" />
<Compile Include="Computers\Commodore64\Serial\SerialPort.cs" />
<Compile Include="Computers\Commodore64\Serial\SerialPortDevice.cs" />
<Compile Include="Computers\Commodore64\User\UserPort.cs" />

View File

@ -534,7 +534,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502
void FetchDummy()
{
DummyReadMemory(PC);
DummyReadMemory(PC);
}
public void Execute(int cycles)
@ -1152,45 +1152,59 @@ namespace BizHawk.Emulation.Cores.Components.M6502
}
void RelBranch_Stage3()
{
FetchDummy();
alu_temp = (byte)PC + (int)(sbyte)opcode2;
PC &= 0xFF00;
PC |= (ushort)((alu_temp & 0xFF));
if (alu_temp.Bit(8))
{
//we need to carry the add, and then we'll be ready to fetch the next instruction
opcode = VOP_RelativeStuff2;
mi = -1;
}
else
{
//to pass cpu_interrupts_v2/5-branch_delays_irq we need to handle a quirk here
//if we decide to interrupt in the next cycle, this condition will cause it to get deferred by one instruction
if (!interrupt_pending)
branch_irq_hack = true;
}
}
rdy_freeze = !RDY;
if (RDY)
{
FetchDummy();
alu_temp = (byte)PC + (int)(sbyte)opcode2;
PC &= 0xFF00;
PC |= (ushort)((alu_temp & 0xFF));
if (alu_temp.Bit(8))
{
//we need to carry the add, and then we'll be ready to fetch the next instruction
opcode = VOP_RelativeStuff2;
mi = -1;
}
else
{
//to pass cpu_interrupts_v2/5-branch_delays_irq we need to handle a quirk here
//if we decide to interrupt in the next cycle, this condition will cause it to get deferred by one instruction
if (!interrupt_pending)
branch_irq_hack = true;
}
}
}
void RelBranch_Stage4()
{
FetchDummy();
if (alu_temp.Bit(31))
PC = (ushort)(PC - 0x100);
else PC = (ushort)(PC + 0x100);
}
rdy_freeze = !RDY;
if (RDY)
{
FetchDummy();
if (alu_temp.Bit(31))
PC = (ushort)(PC - 0x100);
else PC = (ushort)(PC + 0x100);
}
}
void NOP()
{
}
void DecS()
rdy_freeze = !RDY;
}
void DecS()
{
S--;
}
rdy_freeze = !RDY;
if (RDY)
{
S--;
}
}
void IncS()
{
S++;
}
rdy_freeze = !RDY;
if (RDY)
{
S++;
}
}
void JSR()
{
rdy_freeze = !RDY;
@ -2041,35 +2055,50 @@ namespace BizHawk.Emulation.Cores.Components.M6502
}
void Imp_ASL_A()
{
FetchDummy();
FlagC = (A & 0x80) != 0;
A = (byte)(A << 1);
NZ_A();
}
rdy_freeze = !RDY;
if (RDY)
{
FetchDummy();
FlagC = (A & 0x80) != 0;
A = (byte)(A << 1);
NZ_A();
}
}
void Imp_ROL_A()
{
FetchDummy();
temp8 = A;
A = (byte)((A << 1) | (P & 1));
FlagC = (temp8 & 0x80) != 0;
NZ_A();
}
rdy_freeze = !RDY;
if (RDY)
{
FetchDummy();
temp8 = A;
A = (byte)((A << 1) | (P & 1));
FlagC = (temp8 & 0x80) != 0;
NZ_A();
}
}
void Imp_ROR_A()
{
FetchDummy();
temp8 = A;
A = (byte)((A >> 1) | ((P & 1) << 7));
FlagC = (temp8 & 1) != 0;
NZ_A();
}
rdy_freeze = !RDY;
if (RDY)
{
FetchDummy();
temp8 = A;
A = (byte)((A >> 1) | ((P & 1) << 7));
FlagC = (temp8 & 1) != 0;
NZ_A();
}
}
void Imp_LSR_A()
{
FetchDummy();
FlagC = (A & 1) != 0;
A = (byte)(A >> 1);
NZ_A();
}
rdy_freeze = !RDY;
if (RDY)
{
FetchDummy();
FlagC = (A & 1) != 0;
A = (byte)(A >> 1);
NZ_A();
}
}
void JMP_abs()
{
rdy_freeze = !RDY;
@ -2082,8 +2111,6 @@ namespace BizHawk.Emulation.Cores.Components.M6502
void IncPC()
{
PC++;
}
void ZP_RMW_Stage3()
{
@ -2097,21 +2124,18 @@ namespace BizHawk.Emulation.Cores.Components.M6502
void ZP_RMW_Stage5()
{
WriteMemory(opcode2, (byte)alu_temp);
}
void ZP_RMW_INC()
{
WriteMemory(opcode2, (byte)alu_temp);
alu_temp = (byte)((alu_temp + 1) & 0xFF);
P = (byte)((P & 0x7D) | TableNZ[alu_temp]);
}
void ZP_RMW_DEC()
{
WriteMemory(opcode2, (byte)alu_temp);
alu_temp = (byte)((alu_temp - 1) & 0xFF);
P = (byte)((P & 0x7D) | TableNZ[alu_temp]);
}
void ZP_RMW_ASL()
{
@ -2120,7 +2144,6 @@ namespace BizHawk.Emulation.Cores.Components.M6502
FlagC = (value8 & 0x80) != 0;
alu_temp = value8 = (byte)(value8 << 1);
P = (byte)((P & 0x7D) | TableNZ[value8]);
}
void ZP_RMW_SRE()
{
@ -2163,7 +2186,6 @@ namespace BizHawk.Emulation.Cores.Components.M6502
alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7));
FlagC = (temp8 & 1) != 0;
P = (byte)((P & 0x7D) | TableNZ[value8]);
}
void ZP_RMW_ROL()
{
@ -2172,7 +2194,6 @@ namespace BizHawk.Emulation.Cores.Components.M6502
alu_temp = value8 = (byte)((value8 << 1) | (P & 1));
FlagC = (temp8 & 0x80) != 0;
P = (byte)((P & 0x7D) | TableNZ[value8]);
}
void ZP_RMW_SLO()
{
@ -2198,7 +2219,6 @@ namespace BizHawk.Emulation.Cores.Components.M6502
FlagC = (temp8 & 0x80) != 0;
A &= value8;
NZ_A();
}
void AbsIdx_Stage3_Y()
{

View File

@ -4,7 +4,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public partial class C64 : IDriveLight
{
public bool DriveLightEnabled { get { return true; } }
public bool DriveLightOn { get; private set; }
public bool DriveLightEnabled { get { return _board != null && (_board.CartPort.DriveLightEnabled || _board.Serial.DriveLightEnabled); } }
public bool DriveLightOn { get { return _board != null && (_board.CartPort.DriveLightOn || _board.Serial.DriveLightOn);} }
}
}

View File

@ -1,121 +1,121 @@
using BizHawk.Emulation.Common;
using Newtonsoft.Json;
using System;
using System.ComponentModel;
using System.Drawing;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
// 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 C64Settings GetSettings()
{
return Settings.Clone();
}
public C64SyncSettings GetSyncSettings()
{
return SyncSettings.Clone();
}
public bool PutSettings(C64Settings o)
{
Settings = o;
return false;
}
public bool PutSyncSettings(C64SyncSettings o)
{
SyncSettings = o;
return false;
}
internal C64Settings Settings { get; private set; }
internal C64SyncSettings SyncSettings { get; private set; }
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();
}
public C64Settings()
{
BizHawk.Common.SettingsUtil.SetDefaultValues(this);
}
}
public class C64SyncSettings
{
[DisplayName("VIC type")]
[Description("Set the type of video chip to use")]
[DefaultValue(VicType.Pal)]
public VicType VicType { get; set; }
[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();
}
public C64SyncSettings()
{
BizHawk.Common.SettingsUtil.SetDefaultValues(this);
}
}
public enum VicType
{
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
}
}
using BizHawk.Emulation.Common;
using Newtonsoft.Json;
using System;
using System.ComponentModel;
using System.Drawing;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
// 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 C64Settings GetSettings()
{
return Settings.Clone();
}
public C64SyncSettings GetSyncSettings()
{
return SyncSettings.Clone();
}
public bool PutSettings(C64Settings o)
{
Settings = o;
return false;
}
public bool PutSyncSettings(C64SyncSettings o)
{
SyncSettings = o;
return false;
}
internal C64Settings Settings { get; private set; }
internal C64SyncSettings SyncSettings { get; private set; }
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();
}
public C64Settings()
{
BizHawk.Common.SettingsUtil.SetDefaultValues(this);
}
}
public class C64SyncSettings
{
[DisplayName("VIC type")]
[Description("Set the type of video chip to use")]
[DefaultValue(VicType.Pal)]
public VicType VicType { get; set; }
[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();
}
public C64SyncSettings()
{
BizHawk.Common.SettingsUtil.SetDefaultValues(this);
}
}
public enum VicType
{
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, Commodore1541II
}
}
}

View File

@ -130,6 +130,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
switch (diskDriveType)
{
case C64.DiskDriveType.Commodore1541:
case C64.DiskDriveType.Commodore1541II:
DiskDrive = new Drive1541(ClockNumerator, ClockDenominator);
Serial.Connect(DiskDrive);
break;
@ -156,9 +157,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
public void Execute()
{
_vicBank = (0x3 - (Cia1.EffectivePrA & 0x3)) << 14;
_vicBank = (0x3 - ((Cia1.PrA | ~Cia1.DdrA) & 0x3)) << 14;
Vic.ExecutePhase();
CartPort.ExecutePhase();
Cassette.ExecutePhase();
Serial.ExecutePhase();
Sid.ExecutePhase();
@ -190,10 +192,13 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
Cassette.HardReset();
Serial.HardReset();
Cpu.HardReset();
CartPort.HardReset();
}
public void Init()
{
{
CartPort.ReadOpenBus = ReadOpenBus;
Cassette.ReadDataOutput = CassPort_ReadDataOutput;
Cassette.ReadMotor = CassPort_ReadMotor;

View File

@ -10,7 +10,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
private int _lastReadVicAddress = 0x3FFF;
private int _lastReadVicData = 0xFF;
private int _vicBank = 0xC000;
private int _tempCia1Cra;
private bool CassPort_ReadDataOutput()
{
@ -173,5 +172,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
_lastReadVicData = Pla.VicRead(_lastReadVicAddress);
return _lastReadVicData;
}
private int ReadOpenBus()
{
return _lastReadVicData;
}
}
}

View File

@ -20,9 +20,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
)]
[ServiceNotApplicable(typeof(ISettable<,>))]
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, object settings, object syncSettings)
{
PutSyncSettings((C64SyncSettings)syncSettings ?? new C64SyncSettings());
PutSettings((C64Settings)settings ?? new C64Settings());
@ -30,14 +30,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
ServiceProvider = new BasicServiceProvider(this);
InputCallbacks = new InputCallbackSystem();
_inputFileInfo = new InputFileInfo
{
Data = rom,
Extension = romextension
};
CoreComm = comm;
Init(SyncSettings.VicType, Settings.BorderType, SyncSettings.SidType, SyncSettings.TapeDriveType, SyncSettings.DiskDriveType);
Roms = new List<byte[]> { rom };
Init(SyncSettings.VicType, Settings.BorderType, SyncSettings.SidType, SyncSettings.TapeDriveType, SyncSettings.DiskDriveType);
_cyclesPerFrame = _board.Vic.CyclesPerFrame;
SetupMemoryDomains(_board.DiskDrive != null);
_memoryCallbacks = new MemoryCallbackSystem();
@ -56,13 +51,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
}
((BasicServiceProvider) ServiceProvider).Register<IVideoProvider>(_board.Vic);
((BasicServiceProvider) ServiceProvider).Register<IDriveLight>(_board.Serial);
((BasicServiceProvider) ServiceProvider).Register<IDriveLight>(this);
}
// internal variables
private int _frame;
[SaveState.DoNotSave] private readonly int _cyclesPerFrame;
[SaveState.DoNotSave] private InputFileInfo _inputFileInfo;
private bool _driveLed;
// bizhawk I/O
@ -94,6 +88,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
// controller
[SaveState.DoNotSave] public ControllerDefinition ControllerDefinition { get { return C64ControllerDefinition; } }
[SaveState.DoNotSave] public IController Controller { get { return _board.Controller; } set { _board.Controller = value; } }
[SaveState.DoNotSave] public IEnumerable<byte[]> Roms { get; private set; }
[SaveState.DoNotSave]
private static readonly ControllerDefinition C64ControllerDefinition = new ControllerDefinition
@ -122,7 +117,19 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
public void Dispose()
{
}
if (_board != null)
{
if (_board.TapeDrive != null)
{
_board.TapeDrive.RemoveMedia();
}
if (_board.DiskDrive != null)
{
_board.DiskDrive.RemoveMedia();
}
_board = null;
}
}
private int _frameCycles;
@ -149,17 +156,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
_board.Execute();
_frameCycles++;
// load PRG file if needed
if (_loadPrg)
{
// check to see if cpu PC is at the BASIC warm start vector
if (_board.Cpu.Pc != 0 && _board.Cpu.Pc == ((_board.Ram.Peek(0x0303) << 8) | _board.Ram.Peek(0x0302)))
{
Prg.Load(_board.Pla, _inputFileInfo.Data);
_loadPrg = false;
}
}
if (_frameCycles != _cyclesPerFrame)
{
return;
@ -181,7 +177,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
}
private Motherboard _board;
private bool _loadPrg;
private byte[] GetFirmware(int length, params string[] names)
{
@ -194,29 +189,42 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
private void Init(VicType initRegion, BorderType borderType, SidType sidType, TapeDriveType tapeDriveType, DiskDriveType diskDriveType)
{
// Force certain drive types to be available depending on ROM type
switch (_inputFileInfo.Extension.ToUpper())
foreach (var rom in Roms)
{
case @".D64":
case @".G64":
if (diskDriveType == DiskDriveType.None)
{
diskDriveType = DiskDriveType.Commodore1541;
}
break;
case @".TAP":
if (tapeDriveType == TapeDriveType.None)
{
tapeDriveType = TapeDriveType.Commodore1530;
}
break;
}
switch (C64FormatFinder.GetFormat(rom))
{
case C64Format.D64:
case C64Format.G64:
case C64Format.X64:
if (diskDriveType == DiskDriveType.None)
diskDriveType = DiskDriveType.Commodore1541;
break;
case C64Format.T64:
case C64Format.TAP:
if (tapeDriveType == TapeDriveType.None)
{
tapeDriveType = TapeDriveType.Commodore1530;
}
break;
case C64Format.CRT:
// Nothing required.
break;
case C64Format.Unknown:
if (rom.Length >= 0xFE00)
{
throw new Exception("The image format is not known, and too large to be used as a PRG.");
}
if (diskDriveType == DiskDriveType.None)
diskDriveType = DiskDriveType.Commodore1541;
break;
default:
throw new Exception("The image format is not yet supported by the Commodore 64 core.");
}
}
_board = new Motherboard(this, initRegion, borderType, sidType, tapeDriveType, diskDriveType);
InitRoms(diskDriveType);
_board.Init();
InitMedia();
// configure video
CoreComm.VsyncDen = _board.Vic.CyclesPerFrame;
@ -225,65 +233,86 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
private void InitMedia()
{
switch (_inputFileInfo.Extension.ToUpper())
{
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);
}
break;
case @".TAP":
var tape = Tape.Load(_inputFileInfo.Data);
if (tape != null)
{
_board.TapeDrive.Insert(tape);
}
break;
case @".PRG":
if (_inputFileInfo.Data.Length > 2)
_loadPrg = true;
break;
}
}
foreach (var rom in Roms)
{
switch (C64FormatFinder.GetFormat(rom))
{
case C64Format.D64:
var d64 = D64.Read(rom);
if (d64 != null)
{
_board.DiskDrive.InsertMedia(d64);
}
break;
case C64Format.G64:
var g64 = G64.Read(rom);
if (g64 != null)
{
_board.DiskDrive.InsertMedia(g64);
}
break;
case C64Format.CRT:
var cart = CartridgeDevice.Load(rom);
if (cart != null)
{
_board.CartPort.Connect(cart);
}
break;
case C64Format.TAP:
var tape = Tape.Load(rom);
if (tape != null)
{
_board.TapeDrive.Insert(tape);
}
break;
case C64Format.Unknown:
var prgDisk = new DiskBuilder
{
Entries = new List<DiskBuilder.Entry>
{
new DiskBuilder.Entry
{
Closed = true,
Data = rom,
Locked = false,
Name = "PRG",
RecordLength = 0,
Type = DiskBuilder.FileType.Program
}
}
}.Build();
if (prgDisk != null)
{
_board.DiskDrive.InsertMedia(prgDisk);
}
break;
}
}
}
private void InitRoms(DiskDriveType diskDriveType)
{
var basicRom = GetFirmware(0x2000, "Basic");
var charRom = GetFirmware(0x1000, "Chargen");
var kernalRom = GetFirmware(0x2000, "Kernal");
_board.BasicRom.Flash(GetFirmware(0x2000, "Basic"));
_board.KernalRom.Flash(GetFirmware(0x2000, "Kernal"));
_board.CharRom.Flash(GetFirmware(0x1000, "Chargen"));
_board.BasicRom.Flash(basicRom);
_board.KernalRom.Flash(kernalRom);
_board.CharRom.Flash(charRom);
if (diskDriveType == DiskDriveType.Commodore1541)
switch (diskDriveType)
{
var diskRom = GetFirmware(0x4000, "Drive1541II", "Drive1541");
_board.DiskDrive.DriveRom.Flash(diskRom);
}
}
case DiskDriveType.Commodore1541:
_board.DiskDrive.DriveRom.Flash(GetFirmware(0x4000, "Drive1541"));
break;
case DiskDriveType.Commodore1541II:
_board.DiskDrive.DriveRom.Flash(GetFirmware(0x4000, "Drive1541II"));
break;
}
}
// ------------------------------------
public void HardReset()
{
_board.HardReset();
InitMedia();
_board.HardReset();
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public enum C64Format
{
Unknown,
D64,
D71,
D81,
X64,
G64,
T64,
TAP,
CRT,
P64,
P00,
D82,
D80,
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public static class C64FormatFinder
{
public static C64Format GetFormat(byte[] data)
{
if (data == null || data.Length < 0x10)
return C64Format.Unknown;
using (var mem = new MemoryStream(data))
{
var reader = new BinaryReader(mem);
var header = Encoding.GetEncoding(437).GetString(reader.ReadBytes(0x10));
if (header.StartsWith("C64 CARTRIDGE "))
return C64Format.CRT;
if (header.StartsWith("GCR-1541"))
return C64Format.G64;
if (header.StartsWith("C64S tape image "))
return C64Format.T64;
if (header.StartsWith("C64-TAPE-RAW"))
return C64Format.TAP;
if (header.StartsWith("C64File"))
return C64Format.P00;
if (header.StartsWith("P64-1541"))
return C64Format.P64;
if (data[0] == 0x43 && data[1] == 0x15 && data[2] == 0x41 && data[3] == 0x64)
return C64Format.X64;
if (data.Length == 174848 || data.Length == 175531 || data.Length == 196608 || data.Length == 197376)
return C64Format.D64;
if (data.Length == 349696 || data.Length == 351062)
return C64Format.D71;
if (data.Length == 533248)
return C64Format.D80;
if (data.Length == 819200 || data.Length == 822400)
return C64Format.D81;
if (data.Length == 1066496)
return C64Format.D82;
}
return C64Format.Unknown;
}
}
}

View File

@ -3,102 +3,116 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
public abstract partial class CartridgeDevice
public abstract partial class CartridgeDevice : IDriveLight
{
public Func<int> ReadOpenBus;
public static CartridgeDevice Load(byte[] crtFile)
{
var mem = new MemoryStream(crtFile);
var reader = new BinaryReader(mem);
if (new string(reader.ReadChars(16)) != "C64 CARTRIDGE ")
using (var mem = new MemoryStream(crtFile))
{
return null;
}
var reader = new BinaryReader(mem);
var chipAddress = new List<int>();
var chipBank = new List<int>();
var chipData = new List<int[]>();
var chipType = new List<int>();
if (new string(reader.ReadChars(16)) != "C64 CARTRIDGE ")
{
return null;
}
var headerLength = ReadCRTInt(reader);
var version = ReadCRTShort(reader);
var mapper = ReadCRTShort(reader);
var exrom = reader.ReadByte() != 0;
var game = reader.ReadByte() != 0;
var chipAddress = new List<int>();
var chipBank = new List<int>();
var chipData = new List<int[]>();
var chipType = new List<int>();
// reserved
reader.ReadBytes(6);
var headerLength = ReadCRTInt(reader);
var version = ReadCRTShort(reader);
var mapper = ReadCRTShort(reader);
var exrom = reader.ReadByte() != 0;
var game = reader.ReadByte() != 0;
// cartridge name
reader.ReadBytes(0x20);
// reserved
reader.ReadBytes(6);
// skip extra header bytes
if (headerLength > 0x40)
{
reader.ReadBytes(headerLength - 0x40);
}
// cartridge name
reader.ReadBytes(0x20);
// read chips
while (reader.PeekChar() >= 0)
{
if (new string(reader.ReadChars(4)) != "CHIP")
{
break;
}
// skip extra header bytes
if (headerLength > 0x40)
{
reader.ReadBytes(headerLength - 0x40);
}
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);
}
// read chips
while (reader.PeekChar() >= 0)
{
if (new string(reader.ReadChars(4)) != "CHIP")
{
break;
}
if (chipData.Count <= 0)
{
return null;
}
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);
}
CartridgeDevice result;
switch (mapper)
{
case 0x0000:
result = new Mapper0000(chipAddress, chipData, game, exrom);
break;
case 0x0005:
result = new Mapper0005(chipAddress, chipBank, chipData);
break;
case 0x000B:
result = new Mapper000B(chipAddress, 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();
if (chipData.Count <= 0)
{
return null;
}
return result;
}
CartridgeDevice result;
switch (mapper)
{
case 0x0000: // Standard Cartridge
result = new Mapper0000(chipAddress, chipData, game, exrom);
break;
case 0x0001: // Action Replay (4.2 and up)
result = new Mapper0001(chipAddress, chipBank, chipData);
break;
case 0x0005: // Ocean
result = new Mapper0005(chipAddress, chipBank, chipData);
break;
case 0x000A: // Epyx FastLoad
result = new Mapper000A(chipData);
break;
case 0x000B: // Westermann Learning
result = new Mapper000B(chipAddress, chipData);
break;
case 0x000F: // C64 Game System / System 3
result = new Mapper000F(chipAddress, chipBank, chipData);
break;
case 0x0011: // Dinamic
result = new Mapper0011(chipAddress, chipBank, chipData);
break;
case 0x0012: // Zaxxon / Super Zaxxon
result = new Mapper0012(chipAddress, chipBank, chipData);
break;
case 0x0013: // Domark
result = new Mapper0013(chipAddress, chipBank, chipData);
break;
case 0x0020: // EasyFlash
result = new Mapper0020(chipAddress, chipBank, chipData);
break;
case 0x002B: // Prophet 64
result = new Mapper002B(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)
{
@ -127,11 +141,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
[SaveState.DoNotSave]
protected bool validCartridge;
public virtual void ExecutePhase1()
{
}
public virtual void ExecutePhase2()
public virtual void ExecutePhase()
{
}
@ -180,22 +190,22 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
public virtual int Peek8000(int addr)
{
return 0xFF;
return ReadOpenBus();
}
public virtual int PeekA000(int addr)
{
return 0xFF;
return ReadOpenBus();
}
public virtual int PeekDE00(int addr)
{
return 0xFF;
return ReadOpenBus();
}
public virtual int PeekDF00(int addr)
{
return 0xFF;
return ReadOpenBus();
}
public virtual void Poke8000(int addr, int val)
@ -216,22 +226,22 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
public virtual int Read8000(int addr)
{
return 0xFF;
return ReadOpenBus();
}
public virtual int ReadA000(int addr)
{
return 0xFF;
return ReadOpenBus();
}
public virtual int ReadDE00(int addr)
{
return 0xFF;
return ReadOpenBus();
}
public virtual int ReadDF00(int addr)
{
return 0xFF;
return ReadOpenBus();
}
[SaveState.DoNotSave]
@ -272,5 +282,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
public virtual void WriteDF00(int addr, int val)
{
}
public bool DriveLightEnabled { get; protected set; }
public bool DriveLightOn { get; protected set; }
}
}

View File

@ -1,9 +1,13 @@
using BizHawk.Common;
using System;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
public sealed class CartridgePort
public sealed class CartridgePort : IDriveLight
{
public Func<int> ReadOpenBus;
private CartridgeDevice _cartridgeDevice;
private bool _connected;
@ -81,6 +85,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
_connected = true;
_cartridgeDevice = newCartridgeDevice;
newCartridgeDevice.ReadOpenBus = ReadOpenBus;
}
public void Disconnect()
@ -89,6 +94,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
_connected = false;
}
public void ExecutePhase()
{
if (_connected)
_cartridgeDevice.ExecutePhase();
}
public void HardReset()
{
// note: this will not disconnect any attached media
@ -120,5 +131,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
SaveState.SyncObject(ser, this);
}
public bool DriveLightEnabled { get { return _connected && _cartridgeDevice.DriveLightEnabled; } }
public bool DriveLightOn { get { return _connected && _cartridgeDevice.DriveLightOn; } }
}
}

View File

@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
public abstract partial class CartridgeDevice
{
private sealed class Mapper0001 : CartridgeDevice
{
[SaveState.SaveWithName("RAM")] private readonly int[] _ram = new int[0x2000];
[SaveState.SaveWithName("RAMEnabled")] private bool _ramEnabled;
[SaveState.DoNotSave] private readonly int[] _rom = new int[0x8000];
[SaveState.SaveWithName("ROMOffset")] private int _romOffset;
[SaveState.SaveWithName("CartEnabled")] private bool _cartEnabled;
public Mapper0001(IList<int> newAddresses, IList<int> newBanks, IList<int[]> newData)
{
pinExRom = false;
pinGame = false;
for (var i = 0; i < newData.Count; i++)
{
if (newAddresses[i] == 0x8000)
Array.Copy(newData[i], 0, _rom, 0x2000 * newBanks[i], 0x2000);
}
_romOffset = 0;
_cartEnabled = true;
}
public override void HardReset()
{
base.HardReset();
pinExRom = false;
pinGame = false;
for (var i = 0; i < 0x2000; i++)
_ram[i] = 0x00;
_romOffset = 0;
_cartEnabled = true;
}
public override int Peek8000(int addr)
{
return GetLoRom(addr);
}
public override int PeekA000(int addr)
{
return Peek8000(addr);
}
public override int PeekDF00(int addr)
{
return GetIo2(addr);
}
public override void Poke8000(int addr, int val)
{
SetLoRom(addr, val);
}
public override void PokeA000(int addr, int val)
{
Poke8000(addr, val);
}
public override void PokeDE00(int addr, int val)
{
SetState(val);
}
public override void PokeDF00(int addr, int val)
{
SetIo2(addr, val);
}
public override int Read8000(int addr)
{
return GetLoRom(addr);
}
public override int ReadA000(int addr)
{
return GetHiRom(addr);
}
public override int ReadDF00(int addr)
{
return GetIo2(addr);
}
public override void Write8000(int addr, int val)
{
SetLoRom(addr, val);
}
public override void WriteA000(int addr, int val)
{
SetLoRom(addr, val);
}
public override void WriteDE00(int addr, int val)
{
SetState(val);
}
public override void WriteDF00(int addr, int val)
{
SetIo2(addr, val);
}
private void SetState(int val)
{
pinGame = (val & 0x01) == 0;
pinExRom = (val & 0x02) != 0;
_cartEnabled = (val & 0x04) == 0;
_romOffset = (val & 0x18) << 10;
_ramEnabled = (val & 0x20) == 0;
}
private int GetLoRom(int addr)
{
return _ramEnabled
? _ram[addr & 0x1FFF]
: _rom[(addr & 0x1FFF) | _romOffset];
}
private int GetHiRom(int addr)
{
return _rom[(addr & 0x1FFF) | _romOffset];
}
private void SetLoRom(int addr, int val)
{
_ram[addr & 0x1FFF] = val;
}
private int GetIo2(int addr)
{
if (!_cartEnabled)
return ReadOpenBus();
return _ramEnabled
? _ram[(addr & 0xFF) | 0x1F00]
: _rom[(addr & 0xFF) | _romOffset | 0x1F00];
}
private void SetIo2(int addr, int val)
{
_ram[addr & 0x1FFF] = val & 0xFF;
}
}
}
}

View File

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
// Epyx Fastload. Uppermost page is always visible at DFxx.
// They use a capacitor that is discharged by accesses to DExx
// to pull down EXROM. Also, accesses to LOROM while it is active
// discharge the capacitor.
// Thanks to VICE team for the info: http://vice-emu.sourceforge.net/vice_15.html
public abstract partial class CartridgeDevice
{
private class Mapper000A : CartridgeDevice
{
// This constant differs depending on whose research you reference. TODO: Verify.
[SaveState.DoNotSave]
private const int RESET_CAPACITOR_CYCLES = 512;
[SaveState.SaveWithName("CapacitorCycles")]
private int _capacitorCycles;
[SaveState.DoNotSave]
private readonly int[] _rom;
public Mapper000A(IList<int[]> newData)
{
_rom = new int[0x2000];
Array.Copy(newData.First(), _rom, 0x2000);
pinGame = true;
}
public override void ExecutePhase()
{
pinExRom = !(_capacitorCycles > 0);
if (!pinExRom)
{
_capacitorCycles--;
}
}
public override void HardReset()
{
_capacitorCycles = RESET_CAPACITOR_CYCLES;
base.HardReset();
}
public override int Peek8000(int addr)
{
return _rom[addr & 0x1FFF];
}
public override int PeekDE00(int addr)
{
return 0x00;
}
public override int PeekDF00(int addr)
{
return _rom[(addr & 0xFF) | 0x1F00];
}
public override int Read8000(int addr)
{
_capacitorCycles = RESET_CAPACITOR_CYCLES;
return _rom[addr & 0x1FFF];
}
public override int ReadDE00(int addr)
{
_capacitorCycles = RESET_CAPACITOR_CYCLES;
return 0x00;
}
public override int ReadDF00(int addr)
{
return _rom[(addr & 0xFF) | 0x1F00];
}
}
}
}

View File

@ -53,6 +53,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
public Mapper0020(IList<int> newAddresses, IList<int> newBanks, IList<int[]> newData)
{
DriveLightEnabled = true;
var count = newAddresses.Count;
// force ultimax mode (the cart SHOULD set this
@ -194,6 +195,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
pinExRom = (val & 0x02) == 0;
_boardLed = (val & 0x80) != 0;
_internalRomState = 0;
DriveLightOn = _boardLed;
}
public override void Write8000(int addr, int val)
@ -289,6 +291,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
SaveState.SyncDelta("MediaStateA", ser, _originalMediaA, ref _banksA);
SaveState.SyncDelta("MediaStateB", ser, _originalMediaB, ref _banksB);
base.SyncState(ser);
DriveLightOn = _boardLed;
}
}
}

View File

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
{
// Prophet 64 cartridge. Because we can.
// 32 banks of 8KB.
// DFxx = status register, xxABBBBB. A=enable cart, B=bank
// Thanks to VICE team for the info: http://vice-emu.sourceforge.net/vice_15.html
public abstract partial class CartridgeDevice
{
private class Mapper002B : CartridgeDevice
{
[SaveState.DoNotSave]
private readonly int[] _rom;
[SaveState.SaveWithName("RomOffset")]
private int _romOffset;
[SaveState.SaveWithName("RomEnabled")]
private bool _romEnabled;
public Mapper002B(IList<int> newAddresses, IList<int> newBanks, IList<int[]> newData)
{
pinExRom = false;
pinGame = true;
_rom = new int[0x40000];
Array.Copy(newData.First(), _rom, 0x2000);
pinGame = true;
for (var i = 0; i < newData.Count; i++)
{
if (newAddresses[i] == 0x8000)
{
Array.Copy(newData[i], 0, _rom, newBanks[i] * 0x2000, 0x2000);
}
}
}
public override void HardReset()
{
_romEnabled = true;
_romOffset = 0;
}
public override int Peek8000(int addr)
{
return _romOffset | (addr & 0x1FFF);
}
public override int PeekDF00(int addr)
{
// For debugging only. The processor does not see this.
return ((_romOffset >> 13) & 0x1F) | (_romEnabled ? 0x20 : 0x00);
}
public override void PokeDF00(int addr, int val)
{
_romOffset = (val & 0x1F) << 13;
_romEnabled = (val & 0x20) != 0;
}
public override int Read8000(int addr)
{
return _romOffset | (addr & 0x1FFF);
}
public override int ReadDF00(int addr)
{
return 0x00;
}
public override void WriteDF00(int addr, int val)
{
_romOffset = (val & 0x1F) << 13;
_romEnabled = (val & 0x20) != 0;
}
}
}
}

View File

@ -37,5 +37,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cassette
{
_tape = tape;
}
public void RemoveMedia()
{
_tape = null;
}
}
}

View File

@ -1,8 +0,0 @@
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public struct InputFileInfo
{
public byte[] Data;
public string Extension;
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public static class Chip6522
{
public static Via Create(Func<int> readPrA, Func<int> readPrB)
{
return new Via(readPrA, readPrB);
}
public static Via Create(Func<bool> readClock, Func<bool> readData, Func<bool> readAtn, int driveNumber)
{
return new Via(readClock, readData, readAtn, driveNumber);
}
}
}

View File

@ -173,6 +173,22 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_icr |= 0x80;
}
break;
case 0xE:
var oldCra = _cra;
WriteRegister(addr, val);
// Toggle output begins high when timer starts.
if ((_cra & 0x05) == 0x05 && (oldCra & 0x01) == 0)
_prb |= 0x40;
break;
case 0xF:
var oldCrb = _crb;
WriteRegister(addr, val);
// Toggle output begins high when timer starts.
if ((_crb & 0x05) == 0x05 && (oldCrb & 0x01) == 0)
_prb |= 0x80;
break;
default:
WriteRegister(addr, val);
break;

View File

@ -1,69 +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);
}
}
}
}
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

@ -164,7 +164,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_prb &= 0x7F;
_tbPrb7NegativeNextCycle = false;
}
switch (_taState)
{
@ -268,6 +268,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
CheckIrqs();
}
if ((_cra & 0x02) != 0)
_ddra |= 0x40;
if ((_crb & 0x02) != 0)
_ddrb |= 0x80;
}
private void Ta_Count()
@ -412,9 +417,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_tbState = TimerState.LoadThenCount;
}
if ((_cra & 0x02) != 0)
if ((_crb & 0x02) != 0)
{
if ((_cra & 0x04) != 0)
if ((_crb & 0x04) != 0)
{
_tbPrb7NegativeNextCycle = true;
_prb |= 0x80;
@ -423,7 +428,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
_prb ^= 0x80;
}
_ddrb |= 0x80;
}
}

View File

@ -1,85 +1,85 @@
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);
}
}
}
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

@ -102,10 +102,18 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
case 0x0:
_ifr &= 0xE7;
if (_pcrCb2Control == PCR_CONTROL_HANDSHAKE_OUTPUT || _pcrCb2Control == PCR_CONTROL_PULSE_OUTPUT)
{
_handshakeCb2NextClock = true;
}
WriteRegister(addr, val);
break;
case 0x1:
_ifr &= 0xFC;
if (_pcrCa2Control == PCR_CONTROL_HANDSHAKE_OUTPUT || _pcrCa2Control == PCR_CONTROL_PULSE_OUTPUT)
{
_handshakeCa2NextClock = true;
}
WriteRegister(addr, val);
break;
case 0x4:
@ -116,9 +124,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_t1L = (_t1L & 0xFF) | ((val & 0xFF) << 8);
_ifr &= 0xBF;
_t1C = _t1L;
_t1CLoaded = true;
_t1Delayed = 1;
break;
case 0x7:
_t1L = (_t1L & 0xFF) | ((val & 0xFF) << 8);
_ifr &= 0xBF;
break;
case 0x8:
_t2L = (_t2L & 0xFF00) | (val & 0xFF);
@ -126,7 +137,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
case 0x9:
_t2L = (_t2L & 0xFF) | ((val & 0xFF) << 8);
_ifr &= 0xDF;
_t2C = _t2L;
if (_acrT2Control == ACR_T2_CONTROL_TIMED)
{
_t2C = _t2L;
_t2CLoaded = true;
}
_t2Delayed = 1;
break;
case 0xA:
_ifr &= 0xFB;

View File

@ -9,70 +9,126 @@ 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;
[SaveState.DoNotSave] private const int PCR_INT_CONTROL_NEGATIVE_EDGE = 0x00;
[SaveState.DoNotSave] private const int PCR_INT_CONTROL_POSITIVE_EDGE = 0x01;
[SaveState.DoNotSave] private const int PCR_CONTROL_INPUT_NEGATIVE_ACTIVE_EDGE = 0x00;
[SaveState.DoNotSave] private const int PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE = 0x02;
[SaveState.DoNotSave] private const int PCR_CONTROL_INPUT_POSITIVE_ACTIVE_EDGE = 0x04;
[SaveState.DoNotSave] private const int PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE = 0x06;
[SaveState.DoNotSave] private const int PCR_CONTROL_HANDSHAKE_OUTPUT = 0x08;
[SaveState.DoNotSave] private const int PCR_CONTROL_PULSE_OUTPUT = 0x0A;
[SaveState.DoNotSave] private const int PCR_CONTROL_LOW_OUTPUT = 0x0C;
[SaveState.DoNotSave] private const int PCR_CONTROL_HIGH_OUTPUT = 0x0E;
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_DISABLED = 0x00;
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_IN_T2_ONCE = 0x04;
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_IN_PHI2 = 0x08;
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_IN_CLOCK = 0x0C;
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_OUT_T2 = 0x10;
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_OUT_T2_ONCE = 0x14;
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_OUT_PHI2 = 0x18;
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_OUT_CLOCK = 0x1C;
[SaveState.DoNotSave] private const int ACR_T2_CONTROL_TIMED = 0x00;
[SaveState.DoNotSave] private const int ACR_T2_CONTROL_COUNT_ON_PB6 = 0x20;
[SaveState.DoNotSave] private const int ACR_T1_CONTROL_INTERRUPT_ON_LOAD = 0x00;
[SaveState.DoNotSave] private const int ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS = 0x40;
[SaveState.DoNotSave] private const int ACR_T1_CONTROL_INTERRUPT_ON_LOAD_AND_ONESHOT_PB7 = 0x80;
[SaveState.DoNotSave] private const int ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7 = 0xC0;
[SaveState.SaveWithName("PortOutputA")]
private int _pra;
[SaveState.SaveWithName("PortDirectionA")]
private int _ddra;
[SaveState.SaveWithName("PortOutputB")]
private int _prb;
[SaveState.SaveWithName("PortDirectionB")]
private int _ddrb;
[SaveState.SaveWithName("Timer1Counter")]
private int _t1C;
[SaveState.SaveWithName("Timer1Latch")]
private int _t1L;
[SaveState.SaveWithName("Timer2Counter")]
private int _t2C;
[SaveState.SaveWithName("Timer2Latch")]
private int _t2L;
[SaveState.SaveWithName("ShiftRegister")]
private int _sr;
[SaveState.SaveWithName("AuxiliaryControlRegister")]
private int _acr;
[SaveState.SaveWithName("PeripheralControlRegister")]
private int _pcr;
[SaveState.SaveWithName("InterruptFlagRegister")]
private int _ifr;
[SaveState.SaveWithName("InterruptEnableRegister")]
private int _ier;
[SaveState.SaveWithName("Port")]
private readonly Port _port;
[SaveState.SaveWithName("PortLatchA")]
private int _paLatch;
[SaveState.SaveWithName("PortLatchB")]
private int _pbLatch;
[SaveState.SaveWithName("CA1InterruptControl")]
private int _pcrCa1IntControl;
[SaveState.SaveWithName("CA2Control")]
private int _pcrCa2Control;
[SaveState.SaveWithName("CB1InterruptControl")]
private int _pcrCb1IntControl;
[SaveState.SaveWithName("CB2Control")]
private int _pcrCb2Control;
[SaveState.SaveWithName("PortLatchEnableA")]
private bool _acrPaLatchEnable;
[SaveState.SaveWithName("PortLatchEnableB")]
private bool _acrPbLatchEnable;
[SaveState.SaveWithName("ShiftRegisterControl")]
private int _acrSrControl;
[SaveState.SaveWithName("Timer1Control")]
private int _acrT1Control;
[SaveState.SaveWithName("Timer2Control")]
private int _acrT2Control;
[SaveState.SaveWithName("PreviousCA1")]
private bool _ca1L;
[SaveState.SaveWithName("PreviousCA2")]
private bool _ca2L;
[SaveState.SaveWithName("PreviousCB1")]
private bool _cb1L;
[SaveState.SaveWithName("PreviousCB2")]
private bool _cb2L;
[SaveState.SaveWithName("PreviousPB6")]
private bool _pb6L;
private int _shiftCount;
[SaveState.SaveWithName("ResetCa2NextClock")]
private bool _resetCa2NextClock;
[SaveState.SaveWithName("ResetCb2NextClock")]
private bool _resetCb2NextClock;
[SaveState.SaveWithName("HandshakeCa2NextClock")]
private bool _handshakeCa2NextClock;
[SaveState.SaveWithName("HandshakeCb2NextClock")]
private bool _handshakeCb2NextClock;
[SaveState.SaveWithName("CA1")]
public bool Ca1;
[SaveState.SaveWithName("CA2")]
public bool Ca2;
[SaveState.SaveWithName("CB1")]
public bool Cb1;
[SaveState.SaveWithName("CB2")]
public bool Cb2;
[SaveState.SaveWithName("PB6")]
private bool _pb6;
[SaveState.SaveWithName("InterruptNextClock")]
private int _interruptNextClock;
[SaveState.SaveWithName("T1Loaded")]
private bool _t1CLoaded;
[SaveState.SaveWithName("T2Loaded")]
private bool _t2CLoaded;
[SaveState.SaveWithName("T1Delayed")]
private int _t1Delayed;
[SaveState.SaveWithName("T2Delayed")]
private int _t2Delayed;
public Via()
{
@ -128,78 +184,186 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
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;
_pb6L = false;
_pb6 = false;
_resetCa2NextClock = false;
_resetCb2NextClock = false;
_handshakeCa2NextClock = false;
_handshakeCb2NextClock = false;
_interruptNextClock = 0;
_t1CLoaded = false;
_t2CLoaded = false;
}
public void ExecutePhase()
{
_t1C--;
if (_t1C < 0)
// Process delayed interrupts
_ifr |= _interruptNextClock;
_interruptNextClock = 0;
// Process 'pulse' and 'handshake' outputs on CA2 and CB2
if (_resetCa2NextClock)
{
if (_acrT1Control == ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS ||
_acrT1Control == ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7)
{
_t1C = _t1L;
}
_ifr |= 0x40;
Ca2 = true;
_resetCa2NextClock = false;
}
else if (_handshakeCa2NextClock)
{
Ca2 = false;
_resetCa2NextClock = _pcrCa2Control == PCR_CONTROL_PULSE_OUTPUT;
_handshakeCa2NextClock = false;
}
if (_acrT2Control == ACR_T2_CONTROL_TIMED)
if (_resetCb2NextClock)
{
_t2C--;
if (_t2C < 0)
Cb2 = true;
_resetCb2NextClock = false;
}
else if (_handshakeCb2NextClock)
{
Cb2 = false;
_resetCb2NextClock = _pcrCb2Control == PCR_CONTROL_PULSE_OUTPUT;
_handshakeCb2NextClock = false;
}
// Count timers
if (_t1Delayed > 0)
{
_t1Delayed--;
}
else
{
_t1C--;
if (_t1C < 0)
{
_ifr |= 0x20;
_t2C = _t2L;
if (_t1CLoaded)
{
_interruptNextClock |= 0x40;
_t1CLoaded = false;
}
switch (_acrT1Control)
{
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS:
_t1C = _t1L;
_t1CLoaded = true;
break;
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7:
_t1C = _t1L;
_prb ^= 0x80;
_t1CLoaded = true;
break;
}
_t1C &= 0xFFFF;
}
}
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)
if (_t2Delayed > 0)
{
// unknown ACR T1 control
_t2Delayed--;
}
else
{
switch (_acrT2Control)
{
case ACR_T2_CONTROL_TIMED:
_t2C--;
if (_t2C < 0)
{
if (_t2CLoaded)
{
_interruptNextClock |= 0x20;
_t2CLoaded = false;
}
_t2C = _t2L;
}
break;
case ACR_T2_CONTROL_COUNT_ON_PB6:
_pb6L = _pb6;
_pb6 = (_port.ReadExternalPrb() & 0x40) != 0;
if (!_pb6 && _pb6L)
{
_t2C--;
if (_t2C < 0)
{
_ifr |= 0x20;
_t2C = 0xFFFF;
}
}
break;
}
}
if (_acrT2Control != ACR_T2_CONTROL_TIMED)
// Process CA2
switch (_pcrCa2Control)
{
// unknown ACR T2 control
case PCR_CONTROL_INPUT_NEGATIVE_ACTIVE_EDGE:
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE:
if (_ca2L && !Ca2)
_ifr |= 0x01;
break;
case PCR_CONTROL_INPUT_POSITIVE_ACTIVE_EDGE:
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE:
if (!_ca2L && Ca2)
_ifr |= 0x01;
break;
case PCR_CONTROL_HANDSHAKE_OUTPUT:
if (_ca1L && !Ca1)
{
Ca2 = true;
_ifr |= 0x01;
}
break;
case PCR_CONTROL_PULSE_OUTPUT:
break;
case PCR_CONTROL_LOW_OUTPUT:
Ca2 = false;
break;
case PCR_CONTROL_HIGH_OUTPUT:
Ca2 = true;
break;
}
// Process CB2
switch (_pcrCb2Control)
{
case PCR_CONTROL_INPUT_NEGATIVE_ACTIVE_EDGE:
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE:
if (_cb2L && !Cb2)
_ifr |= 0x08;
break;
case PCR_CONTROL_INPUT_POSITIVE_ACTIVE_EDGE:
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE:
if (!_cb2L && Cb2)
_ifr |= 0x08;
break;
case PCR_CONTROL_HANDSHAKE_OUTPUT:
if (_cb1L && !Cb1)
{
Cb2 = true;
_ifr |= 0x08;
}
break;
case PCR_CONTROL_PULSE_OUTPUT:
break;
case PCR_CONTROL_LOW_OUTPUT:
Cb2 = false;
break;
case PCR_CONTROL_HIGH_OUTPUT:
Cb2 = true;
break;
}
// interrupt generation
if ((_pcrCb1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Cb1 && !_cb1L) ||
(_pcrCb1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Cb1 && _cb1L))
{
_ifr |= 0x01;
_ifr |= 0x10;
if (_acrPbLatchEnable)
{
_pbLatch = _port.ReadExternalPrb();

View File

@ -6,7 +6,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
public static class D64
{
private static readonly int[] densityTable =
private static readonly int[] DensityTable =
{
3, 3, 3, 3, 3,
3, 3, 3, 3, 3,
@ -18,7 +18,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
0, 0, 0, 0, 0
};
private static readonly 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
@ -26,7 +26,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
0xFF, 0x09, 0x0A, 0x0B, 0xFF, 0x0D, 0x0E, 0xFF //11xxx
};
private static readonly int[] gcrEncodeTable =
private static readonly int[] GcrEncodeTable =
{
Convert.ToByte("01010", 2), // 0
Convert.ToByte("01011", 2), // 1
@ -46,7 +46,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
Convert.ToByte("10101", 2) // F
};
private static readonly int[] sectorsPerTrack =
private static readonly int[] SectorsPerTrack =
{
21, 21, 21, 21, 21,
21, 21, 21, 21, 21,
@ -58,7 +58,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
17, 17, 17, 17, 17
};
private static readonly int[] standardTrackLengthBytes =
private static readonly int[] StandardTrackLengthBytes =
{
6250, 6666, 7142, 7692
};
@ -76,30 +76,32 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
private static byte[] ConvertSectorToGcr(byte[] source, byte sectorNo, byte trackNo, byte formatA, byte formatB, out int bitsWritten)
{
var mem = new MemoryStream();
var writer = new BinaryWriter(mem);
var headerChecksum = (byte)(sectorNo ^ trackNo ^ formatA ^ formatB);
using (var mem = new MemoryStream())
{
var writer = new BinaryWriter(mem);
var headerChecksum = (byte)(sectorNo ^ trackNo ^ formatA ^ formatB);
// assemble written data for GCR encoding
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;
// assemble written data for GCR encoding
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;
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(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(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(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(new byte[] { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }); // gap
bitsWritten = (int)mem.Length * 8;
bitsWritten = (int)mem.Length * 8;
writer.Flush();
return mem.ToArray();
}
writer.Flush();
return mem.ToArray();
}
}
private static byte[] EncodeGcr(byte[] source)
{
@ -107,113 +109,101 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
var gcr = new int[8];
var data = new byte[4];
var count = source.Length;
var mem = new MemoryStream();
var writer = new BinaryWriter(mem);
using (var mem = new MemoryStream())
{
var writer = new BinaryWriter(mem);
for (var i = 0; i < count; i += 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];
for (var i = 0; i < count; i += 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
// -------- -------- -------- -------- --------
// 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
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));
outputValue = (gcr[3] >> 1) | (gcr[4] << 4);
writer.Write((byte)(outputValue & 0xFF));
outputValue = (gcr[4] >> 4) | (gcr[5] << 1) | (gcr[6] << 6);
writer.Write((byte)(outputValue & 0xFF));
outputValue = (gcr[6] >> 2) | (gcr[7] << 3);
writer.Write((byte)(outputValue & 0xFF));
*/
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));
}
writer.Flush();
return mem.ToArray();
}
writer.Flush();
return mem.ToArray();
}
}
public static Disk Read(byte[] source)
{
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;
using (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)
{
case 174848: // 35 tracks no errors
trackCount = 35;
break;
case 175531: // 35 tracks with errors
trackCount = 35;
break;
case 196608: // 40 tracks no errors
trackCount = 40;
break;
case 197376: // 40 tracks with errors
trackCount = 40;
break;
default:
throw new Exception("Not able to identify capacity of the D64 file.");
}
for (var i = 0; i < trackCount; i++)
{
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];
// we pad the tracks with extra gap bytes to meet MNIB standards
while (trackMem.Length < standardTrackLengthBytes[density])
{
trackMem.WriteByte(0x55);
}
trackDatas.Add(trackMem.ToArray());
trackLengths.Add(trackLengthBits);
trackNumbers.Add(i * 2);
trackDensities.Add(densityTable[i]);
switch (source.Length)
{
case 174848: // 35 tracks no errors
trackCount = 35;
break;
case 175531: // 35 tracks with errors
trackCount = 35;
break;
case 196608: // 40 tracks no errors
trackCount = 40;
break;
case 197376: // 40 tracks with errors
trackCount = 40;
break;
default:
throw new Exception("Not able to identify capacity of the D64 file.");
}
}
return new Disk(trackDatas, trackNumbers, trackDensities, trackLengths, 84);
}
for (var i = 0; i < trackCount; i++)
{
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];
// we pad the tracks with extra gap bytes to meet MNIB standards
while (trackMem.Length < StandardTrackLengthBytes[density])
{
trackMem.WriteByte(0x55);
}
trackDatas.Add(trackMem.ToArray());
trackLengths.Add(trackLengthBits);
trackNumbers.Add(i * 2);
trackDensities.Add(DensityTable[i]);
}
}
return new Disk(trackDatas, trackNumbers, trackDensities, 84);
}
}
}
}

View File

@ -13,17 +13,19 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
[SaveState.DoNotSave] private int[][] _tracks;
[SaveState.DoNotSave] private readonly int[] _originalMedia;
[SaveState.DoNotSave] public bool Valid;
[SaveState.SaveWithName("DiskIsWriteProtected")] public bool WriteProtected;
/// <summary>
/// Create a blank, unformatted disk.
/// </summary>
public Disk(int trackCapacity)
{
{
WriteProtected = false;
_tracks = new int[trackCapacity][];
FillMissingTracks();
_originalMedia = SerializeTracks(_tracks);
Valid = true;
}
}
/// <summary>
/// Create an expanded representation of a magnetic disk.
@ -33,8 +35,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
/// <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)
public Disk(IList<byte[]> trackData, IList<int> trackNumbers, IList<int> trackDensities, int trackCapacity)
{
WriteProtected = true;
_tracks = new int[trackCapacity][];
for (var i = 0; i < trackData.Count; i++)
{
@ -72,7 +75,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
paddedBytes[i] = 0xAA;
}
var result = new int[FluxEntriesPerTrack];
var length = paddedLength;
var lengthBits = (paddedLength * 8) - 7;
var offsets = new List<long>();
var remainingBits = lengthBits;
@ -104,6 +106,17 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
private void FillMissingTracks()
{
// Fill half tracks (should assist with EA "fat-track" protections)
for (var i = 1; i < _tracks.Length; i += 2)
{
if (_tracks[i] == null && _tracks[i - 1] != null)
{
_tracks[i] = new int[FluxEntriesPerTrack];
Array.Copy(_tracks[i - 1], _tracks[i], FluxEntriesPerTrack);
}
}
// Fill vacant tracks
for (var i = 0; i < _tracks.Length; i++)
{
if (_tracks[i] == null)

View File

@ -0,0 +1,308 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
public class DiskBuilder
{
public enum FileType
{
Deleted = 0,
Sequential = 1,
Program = 2,
User = 3,
Relative = 4
}
protected class BamEntry
{
public int Data { get; private set; }
public int Sectors { get; private set; }
public BamEntry(int sectors)
{
Data = 0;
for (var i = 0; i < sectors; i++)
{
Data >>= 1;
Data |= 0x800000;
}
Data |= (sectors << 24);
Sectors = sectors;
}
private int GetBit(int sector)
{
if (sector < 0 || sector >= Sectors)
{
return 0;
}
return 0x800000 >> sector;
}
public void Allocate(int sector)
{
var bit = GetBit(sector);
if (bit != 0 && (Data & bit) != 0)
{
Data &= ~bit;
Data -= 0x1000000;
}
}
public void Free(int sector)
{
var bit = GetBit(sector);
if (bit != 0 && (Data & bit) == 0)
{
Data |= bit;
Data += 0x1000000;
}
}
public int SectorsRemaining
{
get { return (Data >> 24) & 0xFF; }
}
public bool this[int sector]
{
get { return (Data & (1 << sector)) != 0; }
set
{
if (value)
Free(sector);
else
Allocate(sector);
}
}
public byte[] GetBytes()
{
return GetBytesEnumerable().ToArray();
}
private IEnumerable<byte> GetBytesEnumerable()
{
yield return unchecked((byte)(Data >> 24));
yield return unchecked((byte)(Data >> 16));
yield return unchecked((byte)(Data >> 8));
yield return unchecked((byte)Data);
}
public IEnumerable<bool> Entries
{
get
{
var d = Data;
for (var i = 0; i < Sectors; i++)
{
d <<= 1;
yield return (d & 0x1000000) != 0;
}
}
}
}
protected class LocatedEntry
{
public Entry Entry { get; set; }
public int DirectoryTrack { get; set; }
public int DirectorySector { get; set; }
public int Track { get; set; }
public int Sector { get; set; }
public int SideTrack { get; set; }
public int SideSector { get; set; }
public int LengthInSectors { get; set; }
}
public class Entry
{
public FileType Type { get; set; }
public bool Locked { get; set; }
public bool Closed { get; set; }
public string Name { get; set; }
public int RecordLength { get; set; }
public byte[] Data { get; set; }
}
private static readonly int[] SectorsPerTrack =
{
21, 21, 21, 21, 21,
21, 21, 21, 21, 21,
21, 21, 21, 21, 21,
21, 21, 19, 19, 19,
19, 19, 19, 19, 18,
18, 18, 18, 18, 18,
17, 17, 17, 17, 17,
17, 17, 17, 17, 17
};
public List<Entry> Entries { get; set; }
public int VersionType { get; set; }
public string Title { get; set; }
public DiskBuilder()
{
Entries = new List<Entry>();
VersionType = 0x41;
}
public Disk Build()
{
const int tracks = 35;
var trackByteOffsets = new int[tracks];
var bam = new BamEntry[tracks];
var diskFull = false;
for (var i = 0; i < tracks; i++)
{
bam[i] = new BamEntry(SectorsPerTrack[i]);
if (i > 0)
{
trackByteOffsets[i] = trackByteOffsets[i - 1] + (SectorsPerTrack[i - 1] * 256);
}
}
var bytes = new byte[trackByteOffsets[tracks - 1] + (SectorsPerTrack[tracks - 1] *256)];
var currentTrack = 16;
var currentSector = 0;
var interleaveStart = 0;
var sectorInterleave = 3;
var directory = new List<LocatedEntry>();
Func<int, int, int> GetOutputOffset = (t, s) => trackByteOffsets[t] + (s*256);
foreach (var entry in Entries)
{
var sourceOffset = 0;
var dataLength = entry.Data == null ? 0 : entry.Data.Length;
var lengthInSectors = dataLength / 254;
var dataRemaining = dataLength;
var directoryEntry = new LocatedEntry
{
Entry = entry,
LengthInSectors = lengthInSectors + 1,
Track = currentTrack,
Sector = currentSector
};
directory.Add(directoryEntry);
while (!diskFull)
{
var outputOffset = GetOutputOffset(currentTrack, currentSector);
if (dataRemaining > 254)
{
Array.Copy(entry.Data, sourceOffset, bytes, outputOffset + 2, 254);
dataRemaining -= 254;
sourceOffset += 254;
}
else
{
if (dataRemaining > 0)
{
Array.Copy(entry.Data, sourceOffset, bytes, outputOffset + 2, dataRemaining);
bytes[outputOffset + 0] = 0;
bytes[outputOffset + 1] = (byte)(dataRemaining + 1);
dataRemaining = 0;
}
}
bam[currentTrack].Allocate(currentSector);
currentSector += sectorInterleave;
if (currentSector >= SectorsPerTrack[currentTrack])
{
interleaveStart++;
if (interleaveStart >= sectorInterleave)
{
interleaveStart = 0;
if (currentTrack >= 17)
{
currentTrack++;
if (currentTrack >= 35)
{
diskFull = true;
break;
}
}
else
{
currentTrack--;
if (currentTrack < 0)
currentTrack = 18;
}
}
currentSector = interleaveStart;
}
if (dataRemaining <= 0)
break;
bytes[outputOffset + 0] = (byte)(currentTrack + 1);
bytes[outputOffset + 1] = (byte) currentSector;
}
if (diskFull)
break;
}
// write Directory
var directoryOffset = -(0x20);
currentTrack = 17;
currentSector = 1;
var directoryOutputOffset = GetOutputOffset(currentTrack, currentSector);
var fileIndex = 0;
bam[currentTrack].Allocate(currentSector);
foreach (var entry in directory)
{
directoryOffset += 0x20;
if (directoryOffset == 0x100)
{
directoryOffset = 0;
currentSector += 3;
bytes[directoryOutputOffset] = (byte) currentTrack;
bytes[directoryOutputOffset + 1] = (byte) currentSector;
directoryOutputOffset = GetOutputOffset(currentTrack, currentSector);
bam[currentTrack].Allocate(currentSector);
}
bytes[directoryOutputOffset + directoryOffset + 0x00] = 0x00;
bytes[directoryOutputOffset + directoryOffset + 0x01] = 0x00;
bytes[directoryOutputOffset + directoryOffset + 0x02] = (byte)((int)entry.Entry.Type | (entry.Entry.Locked ? 0x40 : 0x00) | (entry.Entry.Closed ? 0x80 : 0x00));
bytes[directoryOutputOffset + directoryOffset + 0x03] = (byte)(entry.Track + 1);
bytes[directoryOutputOffset + directoryOffset + 0x04] = (byte)entry.Sector;
for (var i = 0x05; i <= 0x14; i++)
bytes[directoryOutputOffset + directoryOffset + i] = 0xA0;
var fileNameBytes = Encoding.ASCII.GetBytes(entry.Entry.Name ?? string.Format("FILE{0:D3}", fileIndex));
Array.Copy(fileNameBytes, 0, bytes, directoryOutputOffset + directoryOffset + 0x05, Math.Min(fileNameBytes.Length, 0x10));
bytes[directoryOutputOffset + directoryOffset + 0x1E] = (byte)(entry.LengthInSectors & 0xFF);
bytes[directoryOutputOffset + directoryOffset + 0x1F] = (byte)((entry.LengthInSectors >> 8) & 0xFF);
fileIndex++;
}
bytes[directoryOutputOffset + 0x00] = 0x00;
bytes[directoryOutputOffset + 0x01] = 0xFF;
// write BAM
var bamOutputOffset = GetOutputOffset(17, 0);
bytes[bamOutputOffset + 0x00] = 18;
bytes[bamOutputOffset + 0x01] = 1;
bytes[bamOutputOffset + 0x02] = (byte)VersionType;
for (var i = 0; i < 35; i++)
{
Array.Copy(bam[i].GetBytes(), 0, bytes, bamOutputOffset + 4 + (i * 4), 4);
}
for (var i = 0x90; i <= 0xAA; i++)
{
bytes[bamOutputOffset + i] = 0xA0;
}
var titleBytes = Encoding.ASCII.GetBytes(Title ?? "UNTITLED");
Array.Copy(titleBytes, 0, bytes, bamOutputOffset + 0x90, Math.Min(titleBytes.Length, 0x10));
return D64.Read(bytes);
}
}
}

View File

@ -9,54 +9,56 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
public static Disk Read(byte[] source)
{
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>();
using (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")
{
reader.ReadByte(); //version
int trackCount = reader.ReadByte();
reader.ReadInt16(); //max track size in bytes
reader.ReadByte(); //version
int trackCount = reader.ReadByte();
reader.ReadInt16(); //max track size in bytes
var trackOffsetTable = new int[trackCount];
var trackSpeedTable = new int[trackCount];
var trackOffsetTable = new int[trackCount];
var trackSpeedTable = new int[trackCount];
for (var i = 0; i < trackCount; i++)
trackOffsetTable[i] = reader.ReadInt32();
for (var i = 0; i < trackCount; i++)
trackOffsetTable[i] = reader.ReadInt32();
for (var i = 0; i < trackCount; i++)
trackSpeedTable[i] = reader.ReadInt32();
for (var i = 0; i < trackCount; i++)
trackSpeedTable[i] = reader.ReadInt32();
for (var i = 0; i < trackCount; i++)
{
if (trackOffsetTable[i] > 0)
{
mem.Position = trackOffsetTable[i];
int trackLength = reader.ReadInt16();
var trackData = reader.ReadBytes(trackLength);
for (var i = 0; i < trackCount; i++)
{
if (trackOffsetTable[i] > 0)
{
mem.Position = trackOffsetTable[i];
int trackLength = reader.ReadInt16();
var trackData = reader.ReadBytes(trackLength);
trackDatas.Add(trackData);
trackLengths.Add(trackLength * 8);
trackDensities.Add(trackSpeedTable[i]);
trackNumbers.Add(i);
}
}
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.");
}
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);
}
return new Disk(trackDatas, trackNumbers, trackDensities, 84);
}
return new Disk(84);
}
return new Disk(84);
}
}
}
}

View File

@ -32,6 +32,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
private int _countsBeforeRandomTransition;
[SaveState.SaveWithName("CurrentRNG")]
private int _rngCurrent;
[SaveState.SaveWithName("Clocks")]
private int _clocks;
[SaveState.SaveWithName("CpuClocks")]
private int _cpuClocks;
// Lehmer RNG
private void AdvanceRng()
@ -43,20 +47,31 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
private void ExecuteFlux()
{
for (_diskCycle = 0; _diskCycle < 16; _diskCycle++)
// This actually executes the main 16mhz clock
while (_clocks > 0)
{
_clocks--;
// rotate disk
if (_motorEnabled)
{
if (_diskBitsLeft <= 0)
if (_disk == null)
{
_diskByteOffset++;
if (_diskByteOffset == Disk.FluxEntriesPerTrack)
_diskBitsLeft = 1;
_diskBits = 0;
}
else
{
if (_diskBitsLeft <= 0)
{
_diskByteOffset = 0;
_diskByteOffset++;
if (_diskByteOffset == Disk.FluxEntriesPerTrack)
{
_diskByteOffset = 0;
}
_diskBits = _trackImageData[_diskByteOffset];
_diskBitsLeft = Disk.FluxBitsPerEntry;
}
_diskBits = _trackImageData[_diskByteOffset];
_diskBitsLeft = Disk.FluxBitsPerEntry;
}
if ((_diskBits & 1) != 0)
{
@ -101,16 +116,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
_diskSupplementaryCounter++;
if ((_diskSupplementaryCounter & 0x3) == 0x2)
{
_byteReady = false;
_bitsRemainingInLatchedByte--;
if (_bitsRemainingInLatchedByte <= 0)
{
_bitsRemainingInLatchedByte = 8;
// SOE (sync output enabled)
_byteReady = Via1.Ca2;
}
_byteReady = false;
_bitHistory = (_bitHistory << 1) | ((_diskSupplementaryCounter & 0xC) == 0x0 ? 1 : 0);
_sync = false;
if (Via1.Cb2 && (_bitHistory & 0x3FF) == 0x3FF)
@ -120,6 +127,14 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
_byteReady = false;
}
if (_bitsRemainingInLatchedByte <= 0)
{
_bitsRemainingInLatchedByte = 8;
// SOE (sync output enabled)
_byteReady = Via1.Ca2;
}
// negative transition activates SO pin on CPU
_previousCa1 = Via1.Ca1;
Via1.Ca1 = !_byteReady;
@ -136,7 +151,15 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
_diskSupplementaryCounter = 0;
}
_cpuClocks--;
if (_cpuClocks <= 0)
{
ExecuteSystem();
_cpuClocks = 16;
}
_diskDensityCounter++;
_diskCycle = (_diskCycle + 1) & 0xF;
}
}
}

View File

@ -11,18 +11,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
[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;

View File

@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
public sealed partial class Drive1541
{
[SaveState.SaveWithName("OverflowFlagDelayShiftRegister")]
private int _overflowFlagDelaySr;
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;
}
private int ReadVia1PrA()
{
return _bitHistory & 0xFF;
}
private int ReadVia1PrB()
{
return (_motorStep & 0x03) | (_motorEnabled ? 0x04 : 0x00) | (_sync ? 0x00 : 0x80);
}
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

@ -33,8 +33,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
private bool _ledEnabled;
[SaveState.SaveWithName("MotorStep")]
private int _motorStep;
[SaveState.DoNotSave]
private int _via0PortBtemp;
[SaveState.SaveWithName("CPU")]
private readonly MOS6502X _cpu;
[SaveState.SaveWithName("RAM")]
@ -71,78 +69,34 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
};
_ram = new int[0x800];
Via0 = new Via(ViaReadClock, ViaReadData, ViaReadAtn, 8);
Via1 = new Via(ReadVia1PrA, ReadVia1PrB);
Via0 = Chip6522.Create(ViaReadClock, ViaReadData, ViaReadAtn, 8);
Via1 = Chip6522.Create(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;
_driveCpuClockNum = clockDen*16000000; // 16mhz
}
public override void ExecutePhase()
{
if (_cpuClockNum > _driveCpuClockNum)
_ratioDifference += _driveCpuClockNum;
while (_ratioDifference > _cpuClockNum)
{
_ratioDifference += _cpuClockNum - _driveCpuClockNum;
if (_ratioDifference > _cpuClockNum)
{
_ratioDifference -= _cpuClockNum;
return;
}
}
else if (_cpuClockNum <= _driveCpuClockNum)
{
_ratioDifference += _driveCpuClockNum - _cpuClockNum;
while (_ratioDifference > _driveCpuClockNum)
{
_ratioDifference -= _driveCpuClockNum;
ExecutePhaseInternal();
}
_ratioDifference -= _cpuClockNum;
_clocks++;
}
ExecutePhaseInternal();
}
private void ExecutePhaseInternal()
{
Via0.Ca1 = ViaReadAtn();
// clock output from 325572-01 drives CPU clock (phi0)
ExecuteMotor();
ExecuteFlux();
}
private void ExecuteSystem()
{
Via0.Ca1 = ViaReadAtn();
Via0.ExecutePhase();
Via1.ExecutePhase();
@ -156,12 +110,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
_cpu.IRQ = !(Via0.Irq && Via1.Irq); // active low IRQ line
_cpu.ExecuteOne();
_via0PortBtemp = Via0.EffectivePrB;
_ledEnabled = (_via0PortBtemp & 0x08) != 0;
if (_ledEnabled)
{
_driveLightOffTime = 1000000;
_driveLightOffTime = 25000;
}
else if (_driveLightOffTime > 0)
{
@ -218,113 +169,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
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;
_disk = null;
_trackImageData = null;
_diskBits = 0;
}
}
}

View File

@ -84,5 +84,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
public bool DriveLightEnabled { get { return true; } }
[SaveState.DoNotSave]
public bool DriveLightOn { get { return ReadDeviceLight(); } }
[SaveState.DoNotSave]
public bool IsConnected { get { return _connected; } }
}
}

View File

@ -9,11 +9,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
public abstract class SerialPortDevice
{
[SaveState.DoNotSave]
public Func<bool> ReadMasterAtn;
public Func<bool> ReadMasterAtn = () => true;
[SaveState.DoNotSave]
public Func<bool> ReadMasterClk;
public Func<bool> ReadMasterClk = () => true;
[SaveState.DoNotSave]
public Func<bool> ReadMasterData;
public Func<bool> ReadMasterData = () => true;
public virtual void ExecutePhase()
{

View File

@ -1,49 +1,49 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.User
{
public abstract class UserPortDevice
{
public Func<bool> ReadCounter1;
public Func<bool> ReadCounter2;
public Func<bool> ReadHandshake;
public Func<bool> ReadSerial1;
public Func<bool> ReadSerial2;
public virtual void HardReset()
{
// note: this will not disconnect any attached media
}
public virtual bool ReadAtn()
{
return true;
}
public virtual int ReadData()
{
return 0xFF;
}
public virtual bool ReadFlag2()
{
return true;
}
public virtual bool ReadPa2()
{
return true;
}
public virtual bool ReadReset()
{
return true;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.User
{
public abstract class UserPortDevice
{
public Func<bool> ReadCounter1;
public Func<bool> ReadCounter2;
public Func<bool> ReadHandshake;
public Func<bool> ReadSerial1;
public Func<bool> ReadSerial2;
public virtual void HardReset()
{
// note: this will not disconnect any attached media
}
public virtual bool ReadAtn()
{
return true;
}
public virtual int ReadData()
{
return 0xFF;
}
public virtual bool ReadFlag2()
{
return true;
}
public virtual bool ReadPa2()
{
return true;
}
public virtual bool ReadReset()
{
return true;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}