Merge pull request #599 from TASVideos/c64-via-timer-fixes
C64 via timer fixes (and more)
This commit is contained in:
commit
05914e1490
BizHawk.Client.Common
BizHawk.Emulation.Cores
BizHawk.Emulation.Cores.csproj
CPUs/MOS 6502X
Computers/Commodore64
|
@ -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":
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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);} }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; } }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,5 +37,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cassette
|
|||
{
|
||||
_tape = tape;
|
||||
}
|
||||
|
||||
public void RemoveMedia()
|
||||
{
|
||||
_tape = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
namespace BizHawk.Emulation.Cores.Computers.Commodore64
|
||||
{
|
||||
public struct InputFileInfo
|
||||
{
|
||||
public byte[] Data;
|
||||
public string Extension;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; } }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue