Merge pull request #1607 from TASVideos/c64-refactor

C64: General improvements (disk writing, CIA/VIA timers, 6502X decimal mode fixes)
This commit is contained in:
Tony Konzel 2019-07-22 09:29:22 -05:00 committed by GitHub
commit 58513ea22f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1375 additions and 1190 deletions

View File

@ -524,13 +524,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502
bool interrupt_pending; bool interrupt_pending;
bool branch_irq_hack; //see Uop.RelBranch_Stage3 for more details bool branch_irq_hack; //see Uop.RelBranch_Stage3 for more details
bool Interrupted bool Interrupted => RDY && (NMI || (IRQ && !FlagI));
{
get
{
return NMI || (IRQ && !FlagI);
}
}
void FetchDummy() void FetchDummy()
{ {
@ -668,15 +662,21 @@ namespace BizHawk.Emulation.Cores.Components.M6502
} }
void PushP_Reset() void PushP_Reset()
{ {
ea = ResetVector; rdy_freeze = !RDY;
S--; if (RDY)
FlagI = true; {
ea = ResetVector;
_link.DummyReadMemory((ushort)(S-- + 0x100));
FlagI = true;
}
} }
void PushDummy() void PushDummy()
{ {
S--; rdy_freeze = !RDY;
if (RDY)
{
_link.DummyReadMemory((ushort)(S-- + 0x100));
}
} }
void FetchPCLVector() void FetchPCLVector()
{ {
@ -803,18 +803,24 @@ namespace BizHawk.Emulation.Cores.Components.M6502
} }
void Imp_SEI() void Imp_SEI()
{ {
// not affected by RDY
iflag_pending = true;
rdy_freeze = !RDY; rdy_freeze = !RDY;
if (RDY) if (RDY)
{ {
FetchDummy(); iflag_pending = true; FetchDummy();
} }
} }
void Imp_CLI() void Imp_CLI()
{ {
// not affected by RDY
iflag_pending = false;
rdy_freeze = !RDY; rdy_freeze = !RDY;
if (RDY) if (RDY)
{ {
FetchDummy(); iflag_pending = false; FetchDummy();
} }
} }
void Imp_SEC() void Imp_SEC()
@ -924,19 +930,19 @@ namespace BizHawk.Emulation.Cores.Components.M6502
} }
void IndIdx_READ_Stage5() void IndIdx_READ_Stage5()
{ {
rdy_freeze = !RDY; if (!alu_temp.Bit(8))
if (RDY)
{ {
if (!alu_temp.Bit(8)) mi++;
ExecuteOneRetry();
return;
}
else
{
rdy_freeze = !RDY;
if (RDY)
{ {
mi++; _link.ReadMemory((ushort) ea);
ExecuteOneRetry(); ea = (ushort) (ea + 0x100);
return;
}
else
{
_link.ReadMemory((ushort)ea);
ea = (ushort)(ea + 0x100);
} }
} }
} }
@ -1192,13 +1198,17 @@ namespace BizHawk.Emulation.Cores.Components.M6502
void NOP() void NOP()
{ {
rdy_freeze = !RDY; rdy_freeze = !RDY;
if (RDY)
{
FetchDummy();
}
} }
void DecS() void DecS()
{ {
rdy_freeze = !RDY; rdy_freeze = !RDY;
if (RDY) if (RDY)
{ {
S--; _link.DummyReadMemory((ushort) (0x100 | --S));
} }
} }
void IncS() void IncS()
@ -1206,7 +1216,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502
rdy_freeze = !RDY; rdy_freeze = !RDY;
if (RDY) if (RDY)
{ {
S++; _link.DummyReadMemory((ushort) (0x100 | S++));
} }
} }
void JSR() void JSR()
@ -1659,34 +1669,32 @@ namespace BizHawk.Emulation.Cores.Components.M6502
} }
void _Adc() void _Adc()
{ {
//TODO - an extra cycle penalty on 65C02 only
value8 = (byte)alu_temp;
if (FlagD && BCD_Enabled)
{ {
//TODO - an extra cycle penalty? tempint = (A & 0x0F) + (value8 & 0x0F) + (FlagC ? 0x01 : 0x00);
value8 = (byte)alu_temp; if (tempint > 0x09)
if (FlagD && BCD_Enabled) tempint += 0x06;
{ tempint = (tempint & 0x0F) + (A & 0xF0) + (value8 & 0xF0) + (tempint > 0x0F ? 0x10 : 0x00);
lo = (A & 0x0F) + (value8 & 0x0F) + (FlagC ? 1 : 0); FlagV = (~(A ^ value8) & (A ^ tempint) & 0x80) != 0;
hi = (A & 0xF0) + (value8 & 0xF0); FlagZ = ((A + value8 + (FlagC ? 1 : 0)) & 0xFF) == 0;
if (lo > 0x09) FlagN = (tempint & 0x80) != 0;
{ if ((tempint & 0x1F0) > 0x090)
hi += 0x10; tempint += 0x060;
lo += 0x06; FlagC = tempint > 0xFF;
} A = (byte)(tempint & 0xFF);
if (hi > 0x90) hi += 0x60; }
FlagV = (~(A ^ value8) & (A ^ hi) & 0x80) != 0; else
FlagC = hi > 0xFF; {
A = (byte)((lo & 0x0F) | (hi & 0xF0)); tempint = value8 + A + (FlagC ? 1 : 0);
} FlagV = (~(A ^ value8) & (A ^ tempint) & 0x80) != 0;
else FlagC = tempint > 0xFF;
{ A = (byte)tempint;
tempint = value8 + A + (FlagC ? 1 : 0);
FlagV = (~(A ^ value8) & (A ^ tempint) & 0x80) != 0;
FlagC = tempint > 0xFF;
A = (byte)tempint;
}
NZ_A(); NZ_A();
} }
} }
void Unsupported() void Unsupported()
{ {
@ -1850,7 +1858,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502
rdy_freeze = !RDY; rdy_freeze = !RDY;
if (RDY) if (RDY)
{ {
_link.ReadMemory(opcode2); //dummy? _link.DummyReadMemory(opcode2);
alu_temp = (opcode2 + X) & 0xFF; alu_temp = (opcode2 + X) & 0xFF;
} }
@ -2254,19 +2262,18 @@ namespace BizHawk.Emulation.Cores.Components.M6502
} }
void AbsIdx_READ_Stage4() void AbsIdx_READ_Stage4()
{ {
rdy_freeze = !RDY; if (!alu_temp.Bit(8))
if (RDY)
{ {
if (!alu_temp.Bit(8)) mi++;
ExecuteOneRetry();
}
else
{
rdy_freeze = !RDY;
if (RDY)
{ {
mi++; alu_temp = _link.ReadMemory((ushort) ea);
ExecuteOneRetry(); ea = (ushort) (ea + 0x100);
return;
}
else
{
alu_temp = _link.ReadMemory((ushort)ea);
ea = (ushort)(ea + 0x100);
} }
} }
@ -2697,7 +2704,6 @@ namespace BizHawk.Emulation.Cores.Components.M6502
mi = 0; mi = 0;
iflag_pending = FlagI; iflag_pending = FlagI;
ExecuteOneRetry(); ExecuteOneRetry();
return;
} }
void End_BranchSpecial() void End_BranchSpecial()
{ {
@ -2968,12 +2974,9 @@ namespace BizHawk.Emulation.Cores.Components.M6502
public void ExecuteOne() public void ExecuteOne()
{ {
// total cycles now incraments every time a cycle is called to accurately count during RDY // total cycles now increments every time a cycle is called to accurately count during RDY
TotalExecutedCycles++; TotalExecutedCycles++;
if (!rdy_freeze) interrupt_pending |= Interrupted;
{
interrupt_pending |= Interrupted;
}
rdy_freeze = false; rdy_freeze = false;
//i tried making ExecuteOneRetry not re-entrant by having it set a flag instead, then exit from the call below, check the flag, and GOTO if it was flagged, but it wasnt faster //i tried making ExecuteOneRetry not re-entrant by having it set a flag instead, then exit from the call below, check the flag, and GOTO if it was flagged, but it wasnt faster

View File

@ -21,6 +21,32 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
_board.Cpu.TraceCallback = null; _board.Cpu.TraceCallback = null;
} }
if (controller.IsPressed("Power"))
{
if (!_powerPressed)
{
_powerPressed = true;
HardReset();
}
}
else
{
_powerPressed = false;
}
if (controller.IsPressed("Reset"))
{
if (!_resetPressed)
{
_resetPressed = true;
SoftReset();
}
}
else
{
_resetPressed = false;
}
if (controller.IsPressed("Next Disk") && !_nextPressed) if (controller.IsPressed("Next Disk") && !_nextPressed)
{ {
_nextPressed = true; _nextPressed = true;

View File

@ -38,8 +38,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
public readonly Drive1541 DiskDrive; public readonly Drive1541 DiskDrive;
// state // state
//public int address;
public int Bus;
public bool InputRead; public bool InputRead;
public bool Irq; public bool Irq;
public bool Nmi; public bool Nmi;
@ -100,23 +98,23 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
{ {
case C64.VicType.Ntsc: case C64.VicType.Ntsc:
Vic = Chip6567R8.Create(borderType); Vic = Chip6567R8.Create(borderType);
Cia0 = Chip6526.Create(C64.CiaType.Ntsc, Input_ReadKeyboard, Input_ReadJoysticks); Cia0 = Chip6526.CreateCia0(C64.CiaType.Ntsc, Input_ReadKeyboard, Input_ReadJoysticks);
Cia1 = Chip6526.Create(C64.CiaType.Ntsc, Cia1_ReadPortA); Cia1 = Chip6526.CreateCia1(C64.CiaType.Ntsc, Cia1_ReadPortA, () => 0xFF);
break; break;
case C64.VicType.Pal: case C64.VicType.Pal:
Vic = Chip6569.Create(borderType); Vic = Chip6569.Create(borderType);
Cia0 = Chip6526.Create(C64.CiaType.Pal, Input_ReadKeyboard, Input_ReadJoysticks); Cia0 = Chip6526.CreateCia0(C64.CiaType.Pal, Input_ReadKeyboard, Input_ReadJoysticks);
Cia1 = Chip6526.Create(C64.CiaType.Pal, Cia1_ReadPortA); Cia1 = Chip6526.CreateCia1(C64.CiaType.Pal, Cia1_ReadPortA, () => 0xFF);
break; break;
case C64.VicType.NtscOld: case C64.VicType.NtscOld:
Vic = Chip6567R56A.Create(borderType); Vic = Chip6567R56A.Create(borderType);
Cia0 = Chip6526.Create(C64.CiaType.NtscRevA, Input_ReadKeyboard, Input_ReadJoysticks); Cia0 = Chip6526.CreateCia0(C64.CiaType.NtscRevA, Input_ReadKeyboard, Input_ReadJoysticks);
Cia1 = Chip6526.Create(C64.CiaType.NtscRevA, Cia1_ReadPortA); Cia1 = Chip6526.CreateCia1(C64.CiaType.NtscRevA, Cia1_ReadPortA, () => 0xFF);
break; break;
case C64.VicType.Drean: case C64.VicType.Drean:
Vic = Chip6572.Create(borderType); Vic = Chip6572.Create(borderType);
Cia0 = Chip6526.Create(C64.CiaType.Pal, Input_ReadKeyboard, Input_ReadJoysticks); Cia0 = Chip6526.CreateCia0(C64.CiaType.Pal, Input_ReadKeyboard, Input_ReadJoysticks);
Cia1 = Chip6526.Create(C64.CiaType.Pal, Cia1_ReadPortA); Cia1 = Chip6526.CreateCia1(C64.CiaType.Pal, Cia1_ReadPortA, () => 0xFF);
break; break;
} }
User = new UserPort(); User = new UserPort();
@ -146,6 +144,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
BasicRom = new Chip23128(); BasicRom = new Chip23128();
CharRom = new Chip23128(); CharRom = new Chip23128();
KernalRom = new Chip23128(); KernalRom = new Chip23128();
if (Cpu != null)
Cpu.DebuggerStep = Execute;
if (DiskDrive != null)
DiskDrive.DebuggerStep = Execute;
} }
public int ClockNumerator { get; } public int ClockNumerator { get; }
@ -156,7 +159,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
{ {
_vicBank = (0x3 - ((Cia1.PrA | ~Cia1.DdrA) & 0x3)) << 14; _vicBank = (0x3 - ((Cia1.PrA | ~Cia1.DdrA) & 0x3)) << 14;
Vic.ExecutePhase(); Vic.ExecutePhase1();
CartPort.ExecutePhase(); CartPort.ExecutePhase();
Cassette.ExecutePhase(); Cassette.ExecutePhase();
Serial.ExecutePhase(); Serial.ExecutePhase();
@ -164,6 +167,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
Cia0.ExecutePhase(); Cia0.ExecutePhase();
Cia1.ExecutePhase(); Cia1.ExecutePhase();
Cpu.ExecutePhase(); Cpu.ExecutePhase();
Vic.ExecutePhase2();
} }
public void Flush() public void Flush()
@ -174,7 +178,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
// ----------------------------------------- // -----------------------------------------
public void HardReset() public void HardReset()
{ {
Bus = 0xFF; _lastReadVicAddress = 0x3FFF;
_lastReadVicData = 0xFF;
InputRead = false; InputRead = false;
Cia0.HardReset(); Cia0.HardReset();
@ -191,6 +196,25 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
CartPort.HardReset(); CartPort.HardReset();
} }
public void SoftReset()
{
// equivalent to a hard reset EXCEPT cpu, color ram, memory
_lastReadVicAddress = 0x3FFF;
_lastReadVicData = 0xFF;
InputRead = false;
Cia0.HardReset();
Cia1.HardReset();
Serial.HardReset();
Sid.HardReset();
Vic.HardReset();
User.HardReset();
Cassette.HardReset();
Serial.HardReset();
Cpu.SoftReset();
CartPort.HardReset();
}
public void Init() public void Init()
{ {
CartPort.ReadOpenBus = ReadOpenBus; CartPort.ReadOpenBus = ReadOpenBus;
@ -210,6 +234,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
Cpu.ReadMemory = Pla.Read; Cpu.ReadMemory = Pla.Read;
Cpu.WriteMemory = Pla.Write; Cpu.WriteMemory = Pla.Write;
Cpu.WriteMemoryPort = Cpu_WriteMemoryPort; Cpu.WriteMemoryPort = Cpu_WriteMemoryPort;
Cpu.ReadBus = ReadOpenBus;
Pla.PeekBasicRom = BasicRom.Peek; Pla.PeekBasicRom = BasicRom.Peek;
Pla.PeekCartridgeHi = CartPort.PeekHiRom; Pla.PeekCartridgeHi = CartPort.PeekHiRom;
@ -234,8 +259,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
Pla.PokeMemory = Ram.Poke; Pla.PokeMemory = Ram.Poke;
Pla.PokeSid = Sid.Poke; Pla.PokeSid = Sid.Poke;
Pla.PokeVic = Vic.Poke; Pla.PokeVic = Vic.Poke;
Pla.ReadAec = Vic.ReadAec;
Pla.ReadBa = Vic.ReadBa;
Pla.ReadBasicRom = BasicRom.Read; Pla.ReadBasicRom = BasicRom.Read;
Pla.ReadCartridgeHi = CartPort.ReadHiRom; Pla.ReadCartridgeHi = CartPort.ReadHiRom;
Pla.ReadCartridgeLo = CartPort.ReadLoRom; Pla.ReadCartridgeLo = CartPort.ReadLoRom;
@ -343,7 +366,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
ser.EndSection(); ser.EndSection();
} }
ser.Sync(nameof(Bus), ref Bus);
ser.Sync(nameof(InputRead), ref InputRead); ser.Sync(nameof(InputRead), ref InputRead);
ser.Sync(nameof(Irq), ref Irq); ser.Sync(nameof(Irq), ref Irq);
ser.Sync(nameof(Nmi), ref Nmi); ser.Sync(nameof(Nmi), ref Nmi);

View File

@ -18,48 +18,18 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
return (Cpu.PortData & 0x20) != 0; return (Cpu.PortData & 0x20) != 0;
} }
/*
private bool Cia0_ReadCnt()
{
return User.ReadCounter1() && Cia0.ReadCntBuffer();
}
private int Cia0_ReadPortA()
{
return cia0InputLatchA;
}
private int Cia0_ReadPortB()
{
return cia0InputLatchB;
}
private bool Cia0_ReadSP()
{
return User.ReadSerial1() && Cia0.ReadSpBuffer();
}
private bool Cia1_ReadSP()
{
return User.ReadSerial2() && Cia1.ReadSpBuffer();
}
private bool Cia1_ReadCnt()
{
return User.ReadCounter2() && Cia1.ReadCntBuffer();
}
*/
private int Cia1_ReadPortA() private int Cia1_ReadPortA()
{ {
// the low bits are actually the VIC memory address. // the low bits are actually the VIC memory address.
return (SerPort_ReadDataOut() && Serial.ReadDeviceData() ? 0x80 : 0x00) | return (SerPort_ReadDataOut() && Serial.ReadDeviceData() ? 0x80 : 0x00) |
(SerPort_ReadClockOut() && Serial.ReadDeviceClock() ? 0x40 : 0x00); (SerPort_ReadClockOut() && Serial.ReadDeviceClock() ? 0x40 : 0x00) |
0x3F;
} }
private int Cia1_ReadPortB() private int Cia1_ReadPortB()
{ {
return 0xFF; // Ordinarily these are connected to the userport.
return 0x00;
} }
private int Cpu_ReadPort() private int Cpu_ReadPort()
@ -73,9 +43,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
return data; return data;
} }
private void Cpu_WriteMemoryPort(int addr, int val) private void Cpu_WriteMemoryPort(int addr)
{ {
Pla.WriteMemory(addr, Bus); Pla.WriteMemory(addr, ReadOpenBus());
} }
private bool Glue_ReadIRQ() private bool Glue_ReadIRQ()
@ -114,7 +84,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
private int Pla_ReadColorRam(int addr) private int Pla_ReadColorRam(int addr)
{ {
var result = Bus; var result = ReadOpenBus();
result &= 0xF0; result &= 0xF0;
result |= ColorRam.Read(addr); result |= ColorRam.Read(addr);
return result; return result;
@ -142,16 +112,19 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
private bool SerPort_ReadAtnOut() private bool SerPort_ReadAtnOut()
{ {
// inverted PA3 (input NOT pulled up)
return !((Cia1.DdrA & 0x08) != 0 && (Cia1.PrA & 0x08) != 0); return !((Cia1.DdrA & 0x08) != 0 && (Cia1.PrA & 0x08) != 0);
} }
private bool SerPort_ReadClockOut() private bool SerPort_ReadClockOut()
{ {
// inverted PA4 (input NOT pulled up)
return !((Cia1.DdrA & 0x10) != 0 && (Cia1.PrA & 0x10) != 0); return !((Cia1.DdrA & 0x10) != 0 && (Cia1.PrA & 0x10) != 0);
} }
private bool SerPort_ReadDataOut() private bool SerPort_ReadDataOut()
{ {
// inverted PA5 (input NOT pulled up)
return !((Cia1.DdrA & 0x20) != 0 && (Cia1.PrA & 0x20) != 0); return !((Cia1.DdrA & 0x20) != 0 && (Cia1.PrA & 0x20) != 0);
} }

View File

@ -34,6 +34,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
_cyclesPerFrame = _board.Vic.CyclesPerFrame; _cyclesPerFrame = _board.Vic.CyclesPerFrame;
_memoryCallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); _memoryCallbacks = new MemoryCallbackSystem(new[] { "System Bus" });
InitMedia(_roms[_currentDisk]);
HardReset(); HardReset();
switch (SyncSettings.VicType) switch (SyncSettings.VicType)
@ -137,7 +138,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
"Key Commodore", "Key Left Shift", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Comma", "Key Period", "Key Slash", "Key Right Shift", "Key Cursor Up/Down", "Key Cursor Left/Right", "Key Commodore", "Key Left Shift", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Comma", "Key Period", "Key Slash", "Key Right Shift", "Key Cursor Up/Down", "Key Cursor Left/Right",
"Key Space", "Key Space",
"Key F1", "Key F3", "Key F5", "Key F7", "Key F1", "Key F3", "Key F5", "Key F7",
"Previous Disk", "Next Disk" "Previous Disk", "Next Disk",
"Power", "Reset"
} }
}; };
@ -147,6 +149,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
private int _frame; private int _frame;
private readonly ITraceable _tracer; private readonly ITraceable _tracer;
// Power stuff
private bool _powerPressed;
private bool _resetPressed;
// Disk stuff // Disk stuff
private bool _nextPressed; private bool _nextPressed;
@ -201,7 +207,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
{ {
_board.InputRead = false; _board.InputRead = false;
_board.PollInput(); _board.PollInput();
_board.Cpu.LagCycles = 0;
} }
_board.Execute(); _board.Execute();
@ -358,8 +363,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
private void HardReset() private void HardReset()
{ {
InitMedia(_roms[_currentDisk]);
_board.HardReset(); _board.HardReset();
} }
private void SoftReset()
{
_board.SoftReset();
}
} }
} }

View File

@ -27,7 +27,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
return C64Format.G64; return C64Format.G64;
} }
if (header.StartsWith("C64S tape image ")) if (header.StartsWith("C64S tape image ") || header.StartsWith("C64 tape image f"))
{ {
return C64Format.T64; return C64Format.T64;
} }

View File

@ -97,7 +97,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private void StepOver() private void StepOver()
{ {
var instruction = CpuPeek(_cpu.PC); var instruction = Peek(_cpu.PC);
if (instruction == Jsr) if (instruction == Jsr)
{ {
@ -116,13 +116,13 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private void StepOut() private void StepOut()
{ {
var instructionsBeforeBailout = 1000000; var instructionsBeforeBailout = 1000000;
var instr = CpuPeek(_cpu.PC); var instr = Peek(_cpu.PC);
_jsrCount = instr == Jsr ? 1 : 0; _jsrCount = instr == Jsr ? 1 : 0;
while (--instructionsBeforeBailout > 0) while (--instructionsBeforeBailout > 0)
{ {
StepInto(); StepInto();
instr = CpuPeek(_cpu.PC); instr = Peek(_cpu.PC);
if (instr == Jsr) if (instr == Jsr)
{ {
_jsrCount++; _jsrCount++;

View File

@ -27,7 +27,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public string Disassemble(MemoryDomain m, uint addr, out int length) public string Disassemble(MemoryDomain m, uint addr, out int length)
{ {
return MOS6502X.Disassemble((ushort)addr, out length, CpuPeek); return MOS6502X.Disassemble((ushort) addr, out length, a => unchecked((byte) Peek(a)));
} }
} }
} }

View File

@ -11,9 +11,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{ {
// ------------------------------------ // ------------------------------------
private readonly MOS6502X<CpuLink> _cpu; private readonly MOS6502X<CpuLink> _cpu;
private bool _pinNmiLast;
private LatchedPort _port; private LatchedPort _port;
private bool _thisNmi; private int _irqDelay;
private int _nmiDelay;
private struct CpuLink : IMOS6502XLink private struct CpuLink : IMOS6502XLink
{ {
@ -41,15 +41,14 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public Func<bool> ReadIrq; public Func<bool> ReadIrq;
public Func<bool> ReadNmi; public Func<bool> ReadNmi;
public Func<bool> ReadRdy; public Func<bool> ReadRdy;
public Func<int> ReadBus;
public Func<int, int> ReadMemory; public Func<int, int> ReadMemory;
public Func<int> ReadPort; public Func<int> ReadPort;
public Action<int, int> WriteMemory; public Action<int, int> WriteMemory;
public Action<int, int> WriteMemoryPort; public Action<int> WriteMemoryPort;
public Action DebuggerStep; public Action DebuggerStep;
// ------------------------------------
public Chip6510() public Chip6510()
{ {
// configure cpu r/w // configure cpu r/w
@ -67,25 +66,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
set { _cpu.TraceCallback = value; } set { _cpu.TraceCallback = value; }
} }
public void SetOverflow()
{
}
private byte CpuPeek(ushort addr)
{
return unchecked((byte)Peek(addr));
}
private byte CpuRead(ushort addr)
{
return unchecked((byte)Read(addr));
}
private void CpuWrite(ushort addr, byte val)
{
Write(addr, val);
}
public void HardReset() public void HardReset()
{ {
_cpu.NESSoftReset(); _cpu.NESSoftReset();
@ -94,81 +74,27 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
Direction = 0x00, Direction = 0x00,
Latch = 0xFF Latch = 0xFF
}; };
_pinNmiLast = true;
} }
// ------------------------------------ public void SoftReset()
{
_cpu.NESSoftReset();
_port.Direction = 0x00;
_port.Latch = 0xFF;
}
public void ExecutePhase() public void ExecutePhase()
{ {
_irqDelay >>= 1;
_nmiDelay >>= 1;
_irqDelay |= ReadIrq() ? 0x0 : 0x2;
_nmiDelay |= ReadNmi() ? 0x0 : 0x2;
_cpu.RDY = ReadRdy(); _cpu.RDY = ReadRdy();
_cpu.IRQ = (_irqDelay & 1) != 0;
if (ReadAec()) _cpu.NMI |= (_nmiDelay & 3) == 2;
{ _cpu.ExecuteOne();
_cpu.IRQ = !ReadIrq();
_pinNmiLast = _thisNmi;
_thisNmi = ReadNmi();
_cpu.NMI |= _pinNmiLast && !_thisNmi;
_cpu.ExecuteOne();
}
else
{
LagCycles++;
}
} }
public int LagCycles;
internal bool AtInstructionStart()
{
return _cpu.AtInstructionStart();
}
// ------------------------------------
public ushort Pc
{
get
{
return _cpu.PC;
}
set
{
_cpu.PC = value;
}
}
public int A
{
get { return _cpu.A; }
set { _cpu.A = unchecked((byte)value); }
}
public int X
{
get { return _cpu.X; }
set { _cpu.X = unchecked((byte)value); }
}
public int Y
{
get { return _cpu.Y; }
set { _cpu.Y = unchecked((byte)value); }
}
public int S
{
get { return _cpu.S; }
set { _cpu.S = unchecked((byte)value); }
}
public bool FlagC => _cpu.FlagC;
public bool FlagZ => _cpu.FlagZ;
public bool FlagI => _cpu.FlagI;
public bool FlagD => _cpu.FlagD;
public bool FlagB => _cpu.FlagB;
public bool FlagV => _cpu.FlagV;
public bool FlagN => _cpu.FlagN;
public bool FlagT => _cpu.FlagT;
public int Peek(int addr) public int Peek(int addr)
{ {
switch (addr) switch (addr)
@ -209,7 +135,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
case 0x0001: case 0x0001:
return PortData; return PortData;
default: default:
return ReadMemory(addr); if (ReadAec())
return ReadMemory(addr);
else
return ReadBus();
} }
} }
@ -219,14 +148,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_cpu.SyncState(ser); _cpu.SyncState(ser);
ser.EndSection(); ser.EndSection();
ser.Sync(nameof(_pinNmiLast), ref _pinNmiLast);
ser.BeginSection(nameof(_port)); ser.BeginSection(nameof(_port));
_port.SyncState(ser); _port.SyncState(ser);
ser.EndSection(); ser.EndSection();
ser.Sync(nameof(_thisNmi), ref _thisNmi); ser.Sync(nameof(_irqDelay), ref _irqDelay);
ser.Sync(nameof(LagCycles), ref LagCycles); ser.Sync(nameof(_nmiDelay), ref _nmiDelay);
} }
public void Write(int addr, int val) public void Write(int addr, int val)
@ -235,14 +162,15 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{ {
case 0x0000: case 0x0000:
_port.Direction = val; _port.Direction = val;
WriteMemoryPort(addr, val); WriteMemoryPort(addr);
break; break;
case 0x0001: case 0x0001:
_port.Latch = val; _port.Latch = val;
WriteMemoryPort(addr, val); WriteMemoryPort(addr);
break; break;
default: default:
WriteMemory(addr, val); if (ReadAec())
WriteMemory(addr, val);
break; break;
} }
} }

View File

@ -9,27 +9,27 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
// * A low RES pin is emulated via HardReset(). // * A low RES pin is emulated via HardReset().
public static class Chip6526 public static class Chip6526
{ {
public static Cia Create(C64.CiaType type, Func<int> readIec) public static Cia CreateCia1(C64.CiaType type, Func<int> readIec, Func<int> readUserPort)
{ {
switch (type) switch (type)
{ {
case C64.CiaType.Ntsc: case C64.CiaType.Ntsc:
return new Cia(14318181, 14 * 60, readIec) return new Cia(14318181, 14 * 60, readIec, readUserPort)
{ {
DelayedInterrupts = true DelayedInterrupts = true
}; };
case C64.CiaType.NtscRevA: case C64.CiaType.NtscRevA:
return new Cia(14318181, 14 * 60, readIec) return new Cia(14318181, 14 * 60, readIec, readUserPort)
{ {
DelayedInterrupts = false DelayedInterrupts = false
}; };
case C64.CiaType.Pal: case C64.CiaType.Pal:
return new Cia(17734472, 18 * 50, readIec) return new Cia(17734472, 18 * 50, readIec, readUserPort)
{ {
DelayedInterrupts = true DelayedInterrupts = true
}; };
case C64.CiaType.PalRevA: case C64.CiaType.PalRevA:
return new Cia(17734472, 18 * 50, readIec) return new Cia(17734472, 18 * 50, readIec, readUserPort)
{ {
DelayedInterrupts = false DelayedInterrupts = false
}; };
@ -38,7 +38,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
} }
} }
public static Cia Create(C64.CiaType type, Func<bool[]> keyboard, Func<bool[]> joysticks) public static Cia CreateCia0(C64.CiaType type, Func<bool[]> keyboard, Func<bool[]> joysticks)
{ {
switch (type) switch (type)
{ {

View File

@ -31,8 +31,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public Action<int, int> PokeMemory; public Action<int, int> PokeMemory;
public Action<int, int> PokeSid; public Action<int, int> PokeSid;
public Action<int, int> PokeVic; public Action<int, int> PokeVic;
public Func<bool> ReadAec;
public Func<bool> ReadBa;
public Func<int, int> ReadBasicRom; public Func<int, int> ReadBasicRom;
public Func<int, int> ReadCartridgeLo; public Func<int, int> ReadCartridgeLo;
public Func<int, int> ReadCartridgeHi; public Func<int, int> ReadCartridgeHi;

View File

@ -131,10 +131,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private sealed class IecPort : IPort private sealed class IecPort : IPort
{ {
private readonly Func<int> _readIec; private readonly Func<int> _readIec;
private readonly Func<int> _readUserPort;
public IecPort(Func<int> readIec) public IecPort(Func<int> readIec, Func<int> readUserPort)
{ {
_readIec = readIec; _readIec = readIec;
_readUserPort = readUserPort;
} }
public int ReadPra(int pra, int ddra, int prb, int ddrb) public int ReadPra(int pra, int ddra, int prb, int ddrb)
@ -144,7 +146,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public int ReadPrb(int pra, int ddra, int prb, int ddrb) public int ReadPrb(int pra, int ddra, int prb, int ddrb)
{ {
return (prb | ~ddrb) & 0xFF; return (prb | ~ddrb) | (~ddrb & _readUserPort());
} }
} }
} }

View File

@ -252,15 +252,16 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
case 0xE: case 0xE:
_hasNewCra = true; _hasNewCra = true;
_newCra = val; _newCra = val;
_taCntPhi2 = ((val & 0x20) == 0); _taCntPhi2 = (val & 0x20) == 0;
_taCntCnt = ((val & 0x20) == 0x20); _taCntCnt = (val & 0x20) == 0x20;
break; break;
case 0xF: case 0xF:
_hasNewCrb = true; _hasNewCrb = true;
_newCrb = val; _newCrb = val;
_tbCntPhi2 = ((val & 0x60) == 0); _tbCntPhi2 = (val & 0x60) == 0;
_tbCntTa = ((val & 0x40) == 0x40); _tbCntCnt = (val & 0x60) == 0x20;
_tbCntCnt = ((val & 0x20) == 0x20); _tbCntTa = (val & 0x60) == 0x40;
_tbCntTaCnt = (val & 0x60) == 0x60;
break; break;
} }
} }

View File

@ -33,6 +33,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
} }
public Func<bool> ReadFlag = () => true; public Func<bool> ReadFlag = () => true;
public Func<bool> ReadCnt = () => true;
public Func<bool> ReadSp = () => true;
public bool DelayedInterrupts = true; public bool DelayedInterrupts = true;
private int _pra; private int _pra;
@ -79,6 +81,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private bool _flagLatch; private bool _flagLatch;
private bool _flagInput; private bool _flagInput;
private bool _taUnderflow; private bool _taUnderflow;
private bool _lastCnt;
private bool _thisCnt;
private bool _tbCntTaCnt;
private readonly IPort _port; private readonly IPort _port;
private int _todlo; private int _todlo;
@ -98,438 +103,416 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_port = new JoystickKeyboardPort(joysticks, keyboard); _port = new JoystickKeyboardPort(joysticks, keyboard);
} }
public Cia(int todNum, int todDen, Func<int> readIec) : this(todNum, todDen) public Cia(int todNum, int todDen, Func<int> readIec, Func<int> readUserPort) : this(todNum, todDen)
{ {
_port = new IecPort(readIec); _port = new IecPort(readIec, readUserPort);
} }
public void HardReset() public void HardReset()
{ {
_pra = 0; _pra = 0;
_prb = 0; _prb = 0;
_ddra = 0; _ddra = 0;
_ddrb = 0; _ddrb = 0;
_ta = 0xFFFF; _ta = 0xFFFF;
_tb = 0xFFFF; _tb = 0xFFFF;
_latcha = 1; _latcha = 1;
_latchb = 1; _latchb = 1;
_tod10Ths = 0; _tod10Ths = 0;
_todSec = 0; _todSec = 0;
_todMin = 0; _todMin = 0;
_todHr = 0; _todHr = 0;
_alm10Ths = 0; _alm10Ths = 0;
_almSec = 0; _almSec = 0;
_almMin = 0; _almMin = 0;
_almHr = 0; _almHr = 0;
_sdr = 0; _sdr = 0;
_icr = 0; _icr = 0;
_cra = 0; _cra = 0;
_crb = 0; _crb = 0;
_intMask = 0; _intMask = 0;
_todLatch = false; _todLatch = false;
_taCntPhi2 = false; _taCntPhi2 = false;
_taCntCnt = false; _taCntCnt = false;
_tbCntPhi2 = false; _tbCntPhi2 = false;
_tbCntTa = false; _tbCntTa = false;
_tbCntCnt = false; _tbCntCnt = false;
_taIrqNextCycle = false; _taIrqNextCycle = false;
_tbIrqNextCycle = false; _tbIrqNextCycle = false;
_taState = TimerState.Stop; _taState = TimerState.Stop;
_tbState = TimerState.Stop; _tbState = TimerState.Stop;
} _lastCnt = true;
}
public void ExecutePhase()
{
_thisCnt = ReadCnt();
_taUnderflow = false;
private void CheckIrqs()
{
if (_taIrqNextCycle) if (_taIrqNextCycle)
{ {
_taIrqNextCycle = false; _taIrqNextCycle = false;
TriggerInterrupt(1); TriggerInterrupt(1);
} }
if (_tbIrqNextCycle) if (_tbIrqNextCycle)
{ {
_tbIrqNextCycle = false; _tbIrqNextCycle = false;
TriggerInterrupt(2); TriggerInterrupt(2);
} }
}
public void ExecutePhase() if (_taPrb6NegativeNextCycle)
{ {
_taUnderflow = false; _prb &= 0xBF;
_taPrb6NegativeNextCycle = false;
}
if (_tbPrb7NegativeNextCycle)
{
_prb &= 0x7F;
_tbPrb7NegativeNextCycle = false;
}
switch (_taState)
{
case TimerState.WaitThenCount:
_taState = TimerState.Count;
Ta_Idle();
break;
case TimerState.Stop:
Ta_Idle();
break;
case TimerState.LoadThenStop:
_taState = TimerState.Stop;
_ta = _latcha;
Ta_Idle();
break;
case TimerState.LoadThenCount:
_taState = TimerState.Count;
_ta = _latcha;
Ta_Idle();
break;
case TimerState.LoadThenWaitThenCount:
_taState = TimerState.WaitThenCount;
if (_ta == 1)
{
Ta_Interrupt();
_taUnderflow = true;
}
else
{
_ta = _latcha;
}
Ta_Idle();
break;
case TimerState.Count:
Ta_Count();
break;
case TimerState.CountThenStop:
_taState = TimerState.Stop;
Ta_Count();
break;
}
switch (_tbState)
{
case TimerState.WaitThenCount:
_tbState = TimerState.Count;
Tb_Idle();
break;
case TimerState.Stop:
Tb_Idle();
break;
case TimerState.LoadThenStop:
_tbState = TimerState.Stop;
_tb = _latchb;
Tb_Idle();
break;
case TimerState.LoadThenCount:
_tbState = TimerState.Count;
_tb = _latchb;
Tb_Idle();
break;
case TimerState.LoadThenWaitThenCount:
_tbState = TimerState.WaitThenCount;
if (_tb == 1)
{
Tb_Interrupt();
}
else
{
_tb = _latchb;
}
Tb_Idle();
break;
case TimerState.Count:
Tb_Count();
break;
case TimerState.CountThenStop:
_tbState = TimerState.Stop;
Tb_Count();
break;
}
CountTod();
if (!_todLatch)
{
_latch10Ths = _tod10Ths;
_latchSec = _todSec;
_latchMin = _todMin;
_latchHr = _todHr;
}
_flagInput = ReadFlag();
if (!_flagInput && _flagLatch)
{
TriggerInterrupt(16);
}
_flagLatch = _flagInput;
if ((_cra & 0x02) != 0)
_ddra |= 0x40;
if ((_crb & 0x02) != 0)
_ddrb |= 0x80;
_lastCnt = _thisCnt;
}
private void Ta_Count()
{
if (_taCntPhi2 || (_taCntCnt && !_lastCnt && _thisCnt))
{
if (_ta <= 0 || --_ta == 0)
{
if (_taState != TimerState.Stop)
{
Ta_Interrupt();
}
_taUnderflow = true;
}
}
Ta_Idle();
}
private void Ta_Interrupt()
{
_ta = _latcha;
if (DelayedInterrupts) if (DelayedInterrupts)
{ _taIrqNextCycle = true;
CheckIrqs();
}
if (_taPrb6NegativeNextCycle)
{
_prb &= 0xBF;
_taPrb6NegativeNextCycle = false;
}
if (_tbPrb7NegativeNextCycle)
{
_prb &= 0x7F;
_tbPrb7NegativeNextCycle = false;
}
switch (_taState)
{
case TimerState.WaitThenCount:
_taState = TimerState.Count;
Ta_Idle();
break;
case TimerState.Stop:
Ta_Idle();
break;
case TimerState.LoadThenStop:
_taState = TimerState.Stop;
_ta = _latcha;
Ta_Idle();
break;
case TimerState.LoadThenCount:
_taState = TimerState.Count;
_ta = _latcha;
Ta_Idle();
break;
case TimerState.LoadThenWaitThenCount:
_taState = TimerState.WaitThenCount;
if (_ta == 1)
{
Ta_Interrupt();
_taUnderflow = true;
}
else
{
_ta = _latcha;
}
Ta_Idle();
break;
case TimerState.Count:
Ta_Count();
break;
case TimerState.CountThenStop:
_taState = TimerState.Stop;
Ta_Count();
break;
}
switch (_tbState)
{
case TimerState.WaitThenCount:
_tbState = TimerState.Count;
Tb_Idle();
break;
case TimerState.Stop:
Tb_Idle();
break;
case TimerState.LoadThenStop:
_tbState = TimerState.Stop;
_tb = _latchb;
Tb_Idle();
break;
case TimerState.LoadThenCount:
_tbState = TimerState.Count;
_tb = _latchb;
Tb_Idle();
break;
case TimerState.LoadThenWaitThenCount:
_tbState = TimerState.WaitThenCount;
if (_tb == 1)
{
Tb_Interrupt();
}
else
{
_tb = _latchb;
}
Tb_Idle();
break;
case TimerState.Count:
Tb_Count();
break;
case TimerState.CountThenStop:
_tbState = TimerState.Stop;
Tb_Count();
break;
}
CountTod();
if (!_todLatch)
{
_latch10Ths = _tod10Ths;
_latchSec = _todSec;
_latchMin = _todMin;
_latchHr = _todHr;
}
_flagInput = ReadFlag();
if (!_flagInput && _flagLatch)
{
TriggerInterrupt(16);
}
_flagLatch = _flagInput;
if (!DelayedInterrupts)
{
CheckIrqs();
}
if ((_cra & 0x02) != 0)
{
_ddra |= 0x40;
}
if ((_crb & 0x02) != 0)
{
_ddrb |= 0x80;
}
}
private void Ta_Count()
{
if (_taCntPhi2)
{
if (_ta <= 0 || --_ta == 0)
{
if (_taState != TimerState.Stop)
{
Ta_Interrupt();
}
_taUnderflow = true;
}
}
Ta_Idle();
}
private void Ta_Interrupt()
{
_ta = _latcha;
_taIrqNextCycle = true;
_icr |= 1;
if ((_cra & 0x08) != 0)
{
_cra &= 0xFE;
_newCra &= 0xFE;
_taState = TimerState.LoadThenStop;
}
else else
{ TriggerInterrupt(1);
_taState = TimerState.LoadThenCount;
}
if ((_cra & 0x02) != 0) _icr |= 1;
{
if ((_cra & 0x04) != 0)
{
_taPrb6NegativeNextCycle = true;
_prb |= 0x40;
}
else
{
_prb ^= 0x40;
}
_ddrb |= 0x40; if ((_cra & 0x08) != 0)
} {
} _cra &= 0xFE;
_newCra &= 0xFE;
_taState = TimerState.LoadThenStop;
}
else
{
_taState = TimerState.LoadThenCount;
}
private void Ta_Idle() if ((_cra & 0x02) != 0)
{ {
if (_hasNewCra) if ((_cra & 0x04) != 0)
{ {
switch (_taState) _taPrb6NegativeNextCycle = true;
{ _prb |= 0x40;
case TimerState.Stop: }
case TimerState.LoadThenStop: else
if ((_newCra & 0x01) != 0) {
{ _prb ^= 0x40;
_taState = (_newCra & 0x10) != 0 }
? TimerState.LoadThenWaitThenCount _ddrb |= 0x40;
: TimerState.WaitThenCount; }
} }
else
{
if ((_newCra & 0x10) != 0)
{
_taState = TimerState.LoadThenStop;
}
}
break; private void Ta_Idle()
case TimerState.Count: {
if ((_newCra & 0x01) != 0) if (_hasNewCra)
{ {
if ((_newCra & 0x10) != 0) switch (_taState)
{ {
_taState = TimerState.LoadThenWaitThenCount; case TimerState.Stop:
} case TimerState.LoadThenStop:
} if ((_newCra & 0x01) != 0)
else {
{ _taState = (_newCra & 0x10) != 0
_taState = (_newCra & 0x10) != 0 ? TimerState.LoadThenWaitThenCount
? TimerState.LoadThenStop : TimerState.WaitThenCount;
: TimerState.CountThenStop; }
} else
{
if ((_newCra & 0x10) != 0)
{
_taState = TimerState.LoadThenStop;
}
}
break;
case TimerState.Count:
if ((_newCra & 0x01) != 0)
{
if ((_newCra & 0x10) != 0)
{
_taState = TimerState.LoadThenWaitThenCount;
}
}
else
{
_taState = (_newCra & 0x10) != 0
? TimerState.LoadThenStop
: TimerState.CountThenStop;
}
break;
case TimerState.LoadThenCount:
case TimerState.WaitThenCount:
if ((_newCra & 0x01) != 0)
{
if ((_newCra & 0x08) != 0)
{
_newCra &= 0xFE;
_taState = TimerState.Stop;
}
else if ((_newCra & 0x10) != 0)
{
_taState = TimerState.LoadThenWaitThenCount;
}
}
else
{
_taState = TimerState.Stop;
}
break;
}
_cra = _newCra & 0xEF;
_hasNewCra = false;
}
}
break; private void Tb_Count()
case TimerState.LoadThenCount: {
case TimerState.WaitThenCount: if (_tbCntPhi2 || (_tbCntTa && _taUnderflow) || (_tbCntTaCnt && _taUnderflow && _thisCnt) || (_tbCntCnt && !_lastCnt && _thisCnt))
if ((_newCra & 0x01) != 0) {
{ if (_tb <= 0 || --_tb == 0)
if ((_newCra & 0x08) != 0) {
{ if (_tbState != TimerState.Stop)
_newCra &= 0xFE; {
_taState = TimerState.Stop; Tb_Interrupt();
} }
else if ((_newCra & 0x10) != 0) }
{ }
_taState = TimerState.LoadThenWaitThenCount; Tb_Idle();
} }
}
else
{
_taState = TimerState.Stop;
}
break; private void Tb_Interrupt()
} {
_tb = _latchb;
_cra = _newCra & 0xEF; if (DelayedInterrupts)
_hasNewCra = false; _tbIrqNextCycle = true;
}
}
private void Tb_Count()
{
if (_tbCntPhi2 || (_tbCntTa && _taUnderflow))
{
if (_tb <= 0 || --_tb == 0)
{
if (_tbState != TimerState.Stop)
{
Tb_Interrupt();
}
}
}
Tb_Idle();
}
private void Tb_Interrupt()
{
_tb = _latchb;
_tbIrqNextCycle = true;
_icr |= 2;
if ((_crb & 0x08) != 0)
{
_crb &= 0xFE;
_newCrb &= 0xFE;
_tbState = TimerState.LoadThenStop;
}
else else
{ TriggerInterrupt(2);
_tbState = TimerState.LoadThenCount;
}
if ((_crb & 0x02) != 0) _icr |= 2;
{
if ((_crb & 0x04) != 0)
{
_tbPrb7NegativeNextCycle = true;
_prb |= 0x80;
}
else
{
_prb ^= 0x80;
}
}
}
private void Tb_Idle() if ((_crb & 0x08) != 0)
{ {
if (_hasNewCrb) _crb &= 0xFE;
{ _newCrb &= 0xFE;
switch (_tbState) _tbState = TimerState.LoadThenStop;
{ }
case TimerState.Stop: else
case TimerState.LoadThenStop: {
if ((_newCrb & 0x01) != 0) _tbState = TimerState.LoadThenCount;
{ }
_tbState = (_newCrb & 0x10) != 0
? TimerState.LoadThenWaitThenCount
: TimerState.WaitThenCount;
}
else
{
if ((_newCrb & 0x10) != 0)
{
_tbState = TimerState.LoadThenStop;
}
}
break; if ((_crb & 0x02) != 0)
case TimerState.Count: {
if ((_newCrb & 0x01) != 0) if ((_crb & 0x04) != 0)
{ {
if ((_newCrb & 0x10) != 0) _tbPrb7NegativeNextCycle = true;
{ _prb |= 0x80;
_tbState = TimerState.LoadThenWaitThenCount; }
} else
} {
else _prb ^= 0x80;
{ }
_tbState = (_newCrb & 0x10) != 0 }
? TimerState.LoadThenStop }
: TimerState.CountThenStop;
}
break; private void Tb_Idle()
case TimerState.LoadThenCount: {
case TimerState.WaitThenCount: if (_hasNewCrb)
if ((_newCrb & 0x01) != 0) {
{ switch (_tbState)
if ((_newCrb & 0x08) != 0) {
{ case TimerState.Stop:
_newCrb &= 0xFE; case TimerState.LoadThenStop:
_tbState = TimerState.Stop; if ((_newCrb & 0x01) != 0)
} {
else if ((_newCrb & 0x10) != 0) _tbState = (_newCrb & 0x10) != 0
{ ? TimerState.LoadThenWaitThenCount
_tbState = TimerState.LoadThenWaitThenCount; : TimerState.WaitThenCount;
} }
} else
else {
{ if ((_newCrb & 0x10) != 0)
_tbState = TimerState.Stop; {
} _tbState = TimerState.LoadThenStop;
}
}
break;
case TimerState.Count:
if ((_newCrb & 0x01) != 0)
{
if ((_newCrb & 0x10) != 0)
{
_tbState = TimerState.LoadThenWaitThenCount;
}
}
else
{
_tbState = (_newCrb & 0x10) != 0
? TimerState.LoadThenStop
: TimerState.CountThenStop;
}
break;
case TimerState.LoadThenCount:
case TimerState.WaitThenCount:
if ((_newCrb & 0x01) != 0)
{
if ((_newCrb & 0x08) != 0)
{
_newCrb &= 0xFE;
_tbState = TimerState.Stop;
}
else if ((_newCrb & 0x10) != 0)
{
_tbState = TimerState.LoadThenWaitThenCount;
}
}
else
{
_tbState = TimerState.Stop;
}
break;
}
_crb = _newCrb & 0xEF;
_hasNewCrb = false;
}
}
break; private void TriggerInterrupt(int bit)
} {
_icr |= bit;
_crb = _newCrb & 0xEF; if ((_intMask & bit) == 0) return;
_hasNewCrb = false; _icr |= 0x80;
} }
}
private void TriggerInterrupt(int bit)
{
_icr |= bit;
if ((_intMask & bit) == 0)
{
return;
}
_icr |= 0x80;
}
public void SyncState(Serializer ser) public void SyncState(Serializer ser)
{ {
@ -578,6 +561,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
ser.Sync(nameof(_flagLatch), ref _flagLatch); ser.Sync(nameof(_flagLatch), ref _flagLatch);
ser.Sync(nameof(_todCounter), ref _todCounter); ser.Sync(nameof(_todCounter), ref _todCounter);
ser.Sync(nameof(_lastCnt), ref _lastCnt);
ser.Sync(nameof(_thisCnt), ref _thisCnt);
} }
} }
} }

View File

@ -222,7 +222,7 @@
_filterSelectLoPass = (val & 0x10) != 0; _filterSelectLoPass = (val & 0x10) != 0;
_filterSelectBandPass = (val & 0x20) != 0; _filterSelectBandPass = (val & 0x20) != 0;
_filterSelectHiPass = (val & 0x40) != 0; _filterSelectHiPass = (val & 0x40) != 0;
_disableVoice3 = (val & 0x40) != 0; _disableVoice3 = (val & 0x80) != 0;
break; break;
case 0x19: case 0x19:
_potX = val; _potX = val;

View File

@ -47,10 +47,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_outputBuffer = new short[_outputBufferIndex * 2]; _outputBuffer = new short[_outputBufferIndex * 2];
for (int i = 0; i < _outputBufferIndex; i++) for (int i = 0; i < _outputBufferIndex; i++)
{ {
_mixer = _outputBuffer_not_filtered[i] + _outputBuffer_filtered[i]; _mixer = _outputBufferNotFiltered[i] + _outputBufferFiltered[i];
_mixer = _mixer >> 7; _mixer = _mixer >> 7;
_mixer = (_mixer * _volume_at_sample_time[i]) >> 4; _mixer = (_mixer * _volumeAtSampleTime[i]) >> 4;
_mixer -= _volume_at_sample_time[i] << 8; _mixer -= _volumeAtSampleTime[i] << 8;
if (_mixer > 0x7FFF) if (_mixer > 0x7FFF)
{ {
@ -69,9 +69,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
samples = _outputBuffer; samples = _outputBuffer;
nsamp = _outputBufferIndex; nsamp = _outputBufferIndex;
last_filtered_value = _outputBuffer_filtered[_outputBufferIndex - 1]; _lastFilteredValue = _outputBufferFiltered[_outputBufferIndex - 1];
_outputBufferIndex = 0; _outputBufferIndex = 0;
filter_index = 0; _filterIndex = 0;
} }
} }

View File

@ -39,12 +39,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private bool _filterSelectHiPass; private bool _filterSelectHiPass;
private int _mixer; private int _mixer;
private short[] _outputBuffer; private short[] _outputBuffer;
private int[] _outputBuffer_filtered; private readonly int[] _outputBufferFiltered;
private int[] _outputBuffer_not_filtered; private readonly int[] _outputBufferNotFiltered;
private int[] _volume_at_sample_time; private readonly int[] _volumeAtSampleTime;
private int _outputBufferIndex; private int _outputBufferIndex;
private int filter_index; private int _filterIndex;
private int last_filtered_value; private int _lastFilteredValue;
private int _potCounter; private int _potCounter;
private int _potX; private int _potX;
private int _potY; private int _potY;
@ -61,7 +61,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public Func<int> ReadPotX; public Func<int> ReadPotX;
public Func<int> ReadPotY; public Func<int> ReadPotY;
public RealFFT fft; private RealFFT _fft;
private double[] _fftBuffer = new double[0];
private readonly int _cpuCyclesNum; private readonly int _cpuCyclesNum;
private int _sampleCyclesNum; private int _sampleCyclesNum;
@ -92,9 +93,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
for (var i = 0; i < 3; i++) for (var i = 0; i < 3; i++)
_filterEnable[i] = false; _filterEnable[i] = false;
_outputBuffer_filtered = new int[sampleRate]; _outputBufferFiltered = new int[sampleRate];
_outputBuffer_not_filtered = new int[sampleRate]; _outputBufferNotFiltered = new int[sampleRate];
_volume_at_sample_time = new int[sampleRate]; _volumeAtSampleTime = new int[sampleRate];
} }
// ------------------------------------ // ------------------------------------
@ -109,6 +110,15 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_potCounter = 0; _potCounter = 0;
_potX = 0; _potX = 0;
_potY = 0; _potY = 0;
_filterEnable[0] = false;
_filterEnable[1] = false;
_filterEnable[2] = false;
_filterFrequency = 0;
_filterSelectBandPass = false;
_filterSelectHiPass = false;
_filterSelectLoPass = false;
_filterResonance = 0;
_volume = 0;
} }
// ------------------------------------ // ------------------------------------
@ -190,9 +200,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
if (_outputBufferIndex < _sampleRate) if (_outputBufferIndex < _sampleRate)
{ {
_outputBuffer_not_filtered[_outputBufferIndex] = temp_not_filtered; _outputBufferNotFiltered[_outputBufferIndex] = temp_not_filtered;
_outputBuffer_filtered[_outputBufferIndex] = temp_filtered; _outputBufferFiltered[_outputBufferIndex] = temp_filtered;
_volume_at_sample_time[_outputBufferIndex] = _volume; _volumeAtSampleTime[_outputBufferIndex] = _volume;
_outputBufferIndex++; _outputBufferIndex++;
} }
} }
@ -202,7 +212,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{ {
if (_filterEnable[0] | _filterEnable[1] | _filterEnable[2]) if (_filterEnable[0] | _filterEnable[1] | _filterEnable[2])
{ {
if ((_outputBufferIndex - filter_index) >= 16) if ((_outputBufferIndex - _filterIndex) >= 16)
{ {
filter_operator(); filter_operator();
} }
@ -210,22 +220,22 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{ {
// the length is too short for the FFT to reliably act on the output // the length is too short for the FFT to reliably act on the output
// instead, clamp it to the previous output. // instead, clamp it to the previous output.
for (int i = filter_index; i < _outputBufferIndex; i++) for (int i = _filterIndex; i < _outputBufferIndex; i++)
{ {
_outputBuffer_filtered[i] = last_filtered_value; _outputBufferFiltered[i] = _lastFilteredValue;
} }
} }
} }
filter_index = _outputBufferIndex; _filterIndex = _outputBufferIndex;
if (_outputBufferIndex>0) if (_outputBufferIndex>0)
last_filtered_value = _outputBuffer_filtered[_outputBufferIndex - 1]; _lastFilteredValue = _outputBufferFiltered[_outputBufferIndex - 1];
} }
// if the filter is off, keep updating the filter index to the most recent Flush // if the filter is off, keep updating the filter index to the most recent Flush
if (!(_filterEnable[0] | _filterEnable[1] | _filterEnable[2])) if (!(_filterEnable[0] | _filterEnable[1] | _filterEnable[2]))
{ {
filter_index = _outputBufferIndex; _filterIndex = _outputBufferIndex;
} }
} }
@ -236,7 +246,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
double attenuation; double attenuation;
int nsamp = _outputBufferIndex - filter_index; int nsamp = _outputBufferIndex - _filterIndex;
// pass the list of filtered samples to the FFT // pass the list of filtered samples to the FFT
// but needs to be a power of 2, so find the next highest power of 2 and re-sample // but needs to be a power of 2, so find the next highest power of 2 and re-sample
@ -251,18 +261,20 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
} }
} }
fft = new RealFFT(nsamp_2); _fft = new RealFFT(nsamp_2);
double[] temp_buffer = new double[nsamp_2]; // eventually this will settle on a single buffer size and stop reallocating
if (_fftBuffer.Length < nsamp_2)
Array.Resize(ref _fftBuffer, nsamp_2);
// linearly interpolate the original sample set into the new denser sample set // linearly interpolate the original sample set into the new denser sample set
for (double i = 0; i < nsamp_2; i++) for (double i = 0; i < nsamp_2; i++)
{ {
temp_buffer[(int)i] = _outputBuffer_filtered[(int)Math.Floor((i / (nsamp_2-1) * (nsamp - 1))) + filter_index]; _fftBuffer[(int)i] = _outputBufferFiltered[(int)Math.Floor((i / (nsamp_2-1) * (nsamp - 1))) + _filterIndex];
} }
// now we have everything we need to perform the FFT // now we have everything we need to perform the FFT
fft.ComputeForward(temp_buffer); _fft.ComputeForward(_fftBuffer);
// for each element in the frequency list, attenuate it according to the specs // for each element in the frequency list, attenuate it according to the specs
for (int i = 1; i < nsamp_2; i++) for (int i = 1; i < nsamp_2; i++)
@ -273,7 +285,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
// let's assume that frequencies near the peak are doubled in strength at max resonance // let's assume that frequencies near the peak are doubled in strength at max resonance
if ((1.2 > freq / loc_filterFrequency) && (freq / loc_filterFrequency > 0.8 )) if ((1.2 > freq / loc_filterFrequency) && (freq / loc_filterFrequency > 0.8 ))
{ {
temp_buffer[i] = temp_buffer[i] * (1 + (double)_filterResonance/15); _fftBuffer[i] = _fftBuffer[i] * (1 + (double)_filterResonance/15);
} }
// low pass filter // low pass filter
@ -282,7 +294,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
//attenuated at 12db per octave //attenuated at 12db per octave
attenuation = Math.Log(freq / loc_filterFrequency, 2); attenuation = Math.Log(freq / loc_filterFrequency, 2);
attenuation = 12 * attenuation; attenuation = 12 * attenuation;
temp_buffer[i] = temp_buffer[i] * Math.Pow(2, -attenuation / 10); _fftBuffer[i] = _fftBuffer[i] * Math.Pow(2, -attenuation / 10);
} }
// High pass filter // High pass filter
@ -291,7 +303,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
//attenuated at 12db per octave //attenuated at 12db per octave
attenuation = Math.Log(loc_filterFrequency / freq, 2); attenuation = Math.Log(loc_filterFrequency / freq, 2);
attenuation = 12 * attenuation; attenuation = 12 * attenuation;
temp_buffer[i] = temp_buffer[i] * Math.Pow(2, -attenuation / 10); _fftBuffer[i] = _fftBuffer[i] * Math.Pow(2, -attenuation / 10);
} }
// Band pass filter // Band pass filter
@ -300,23 +312,23 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
//attenuated at 6db per octave //attenuated at 6db per octave
attenuation = Math.Log(freq / loc_filterFrequency, 2); attenuation = Math.Log(freq / loc_filterFrequency, 2);
attenuation = 6 * attenuation; attenuation = 6 * attenuation;
temp_buffer[i] = temp_buffer[i] * Math.Pow(2, -Math.Abs(attenuation) / 10); _fftBuffer[i] = _fftBuffer[i] * Math.Pow(2, -Math.Abs(attenuation) / 10);
} }
} }
// now transform back into time space and reassemble the attenuated frequency components // now transform back into time space and reassemble the attenuated frequency components
fft.ComputeReverse(temp_buffer); _fft.ComputeReverse(_fftBuffer);
int temp = nsamp - 1; int temp = nsamp - 1;
//re-sample back down to the original number of samples //re-sample back down to the original number of samples
for (double i = 0; i < nsamp; i++) for (double i = 0; i < nsamp; i++)
{ {
_outputBuffer_filtered[(int)i + filter_index] = (int)(temp_buffer[(int)Math.Ceiling((i / (nsamp - 1) * (nsamp_2 - 1)))]/(nsamp_2/2)); _outputBufferFiltered[(int)i + _filterIndex] = (int)(_fftBuffer[(int)Math.Ceiling((i / (nsamp - 1) * (nsamp_2 - 1)))]/(nsamp_2/2));
if (_outputBuffer_filtered[(int)i + filter_index] < 0) if (_outputBufferFiltered[(int)i + _filterIndex] < 0)
{ {
_outputBuffer_filtered[(int)i + filter_index] = 0; _outputBufferFiltered[(int)i + _filterIndex] = 0;
} }
// the FFT is only an approximate model and fails at low sample rates // the FFT is only an approximate model and fails at low sample rates
@ -324,7 +336,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
// thus smoothing out the FT samples // thus smoothing out the FT samples
if (i<16) if (i<16)
_outputBuffer_filtered[(int)i + filter_index] = (int)((last_filtered_value * Math.Pow(15 - i,1) + _outputBuffer_filtered[(int)i + filter_index] * Math.Pow(i,1))/ Math.Pow(15,1)); _outputBufferFiltered[(int)i + _filterIndex] = (int)((_lastFilteredValue * Math.Pow(15 - i,1) + _outputBufferFiltered[(int)i + _filterIndex] * Math.Pow(i,1))/ Math.Pow(15,1));
} }
} }
// ---------------------------------- // ----------------------------------

View File

@ -56,7 +56,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public int ReadPra(int pra, int ddra) public int ReadPra(int pra, int ddra)
{ {
return _readPrA(); return (pra | ~ddra) & ReadExternalPra();
} }
public int ReadPrb(int prb, int ddrb) public int ReadPrb(int prb, int ddrb)
@ -98,7 +98,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public int ReadPra(int pra, int ddra) public int ReadPra(int pra, int ddra)
{ {
return (pra | ~ddra) & 0xFF; return (pra | ~ddra) & ReadExternalPra();
} }
public int ReadPrb(int prb, int ddrb) public int ReadPrb(int prb, int ddrb)

View File

@ -18,18 +18,16 @@
switch (addr) switch (addr)
{ {
case 0x0: case 0x0:
_ifr &= 0xE7; if (_pcrCb2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE && _pcrCb2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE)
_ifr &= 0xE7;
if (_acrPbLatchEnable) if (_acrPbLatchEnable)
{
return _pbLatch; return _pbLatch;
}
break; break;
case 0x1: case 0x1:
_ifr &= 0xFC; if (_pcrCa2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE && _pcrCa2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE)
_ifr &= 0xFC;
if (_acrPaLatchEnable) if (_acrPaLatchEnable)
{
return _paLatch; return _paLatch;
}
break; break;
case 0x4: case 0x4:
_ifr &= 0xBF; _ifr &= 0xBF;
@ -39,6 +37,7 @@
break; break;
case 0xA: case 0xA:
_ifr &= 0xFB; _ifr &= 0xFB;
_srCount = 8;
break; break;
case 0xF: case 0xF:
if (_acrPaLatchEnable) if (_acrPaLatchEnable)
@ -58,7 +57,8 @@
case 0x0: case 0x0:
return _port.ReadPrb(_prb, _ddrb); return _port.ReadPrb(_prb, _ddrb);
case 0x1: case 0x1:
return _port.ReadPra(_pra, _ddra); case 0xF:
return _port.ReadExternalPra();
case 0x2: case 0x2:
return _ddrb; return _ddrb;
case 0x3: case 0x3:
@ -85,8 +85,6 @@
return _ifr; return _ifr;
case 0xE: case 0xE:
return _ier | 0x80; return _ier | 0x80;
case 0xF:
return _port.ReadPra(_pra, _ddra);
} }
return 0xFF; return 0xFF;
@ -98,20 +96,17 @@
switch (addr) switch (addr)
{ {
case 0x0: case 0x0:
_ifr &= 0xE7; if (_pcrCb2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE && _pcrCb2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE)
if (_pcrCb2Control == PCR_CONTROL_HANDSHAKE_OUTPUT || _pcrCb2Control == PCR_CONTROL_PULSE_OUTPUT) _ifr &= 0xE7;
{ if (_pcrCb2Control == PCR_CONTROL_PULSE_OUTPUT)
_handshakeCb2NextClock = true; _handshakeCb2NextClock = true;
}
WriteRegister(addr, val); WriteRegister(addr, val);
break; break;
case 0x1: case 0x1:
_ifr &= 0xFC; if (_pcrCa2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE && _pcrCa2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE)
if (_pcrCa2Control == PCR_CONTROL_HANDSHAKE_OUTPUT || _pcrCa2Control == PCR_CONTROL_PULSE_OUTPUT) _ifr &= 0xFC;
{ if (_pcrCa2Control == PCR_CONTROL_PULSE_OUTPUT)
_handshakeCa2NextClock = true; _handshakeCa2NextClock = true;
}
WriteRegister(addr, val); WriteRegister(addr, val);
break; break;
case 0x4: case 0x4:
@ -124,6 +119,7 @@
_t1C = _t1L; _t1C = _t1L;
_t1CLoaded = true; _t1CLoaded = true;
_t1Delayed = 1; _t1Delayed = 1;
_resetPb7NextClock = _acrT1Control == ACR_T1_CONTROL_INTERRUPT_ON_LOAD_AND_PULSE_PB7;
break; break;
case 0x7: case 0x7:
_t1L = (_t1L & 0xFF) | ((val & 0xFF) << 8); _t1L = (_t1L & 0xFF) | ((val & 0xFF) << 8);
@ -145,6 +141,7 @@
break; break;
case 0xA: case 0xA:
_ifr &= 0xFB; _ifr &= 0xFB;
_srCount = 8;
WriteRegister(addr, val); WriteRegister(addr, val);
break; break;
case 0xD: case 0xD:

View File

@ -29,6 +29,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private const int ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS = 0x40; 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_INTERRUPT_ON_LOAD_AND_ONESHOT_PB7 = 0x80;
private const int ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7 = 0xC0; private const int ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7 = 0xC0;
private const int ACR_T1_CONTROL_INTERRUPT_ON_LOAD_AND_PULSE_PB7 = 0x80;
private int _pra; private int _pra;
private int _ddra; private int _ddra;
@ -57,6 +58,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private int _acrSrControl; private int _acrSrControl;
private int _acrT1Control; private int _acrT1Control;
private int _acrT2Control; private int _acrT2Control;
private int _srCount;
private bool _ca1L; private bool _ca1L;
private bool _ca2L; private bool _ca2L;
@ -66,6 +68,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private bool _resetCa2NextClock; private bool _resetCa2NextClock;
private bool _resetCb2NextClock; private bool _resetCb2NextClock;
private bool _resetPb7NextClock;
private bool _setPb7NextClock;
private bool _handshakeCa2NextClock; private bool _handshakeCa2NextClock;
private bool _handshakeCb2NextClock; private bool _handshakeCb2NextClock;
@ -106,11 +110,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_prb = 0; _prb = 0;
_ddra = 0; _ddra = 0;
_ddrb = 0; _ddrb = 0;
_t1C = 0; _t1C = 0xFFFF;
_t1L = 0; _t1L = 0xFFFF;
_t2C = 0; _t2C = 0xFFFF;
_t2L = 0; _t2L = 0xFFFF;
_sr = 0; _sr = 0xFF;
_acr = 0; _acr = 0;
_pcr = 0; _pcr = 0;
_ifr = 0; _ifr = 0;
@ -126,15 +130,16 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_acrSrControl = 0; _acrSrControl = 0;
_acrT1Control = 0; _acrT1Control = 0;
_acrT2Control = 0; _acrT2Control = 0;
_ca1L = false; _ca1L = true;
_cb1L = false; _cb1L = true;
Ca1 = false; Ca1 = true;
Ca2 = false; Ca2 = true;
Cb1 = false; Cb1 = true;
Cb2 = false; Cb2 = true;
_srCount = 0;
_pb6L = false; _pb6L = true;
_pb6 = false; _pb6 = true;
_resetCa2NextClock = false; _resetCa2NextClock = false;
_resetCb2NextClock = false; _resetCb2NextClock = false;
_handshakeCa2NextClock = false; _handshakeCa2NextClock = false;
@ -142,15 +147,20 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_interruptNextClock = 0; _interruptNextClock = 0;
_t1CLoaded = false; _t1CLoaded = false;
_t2CLoaded = false; _t2CLoaded = false;
_resetPb7NextClock = false;
_setPb7NextClock = false;
} }
public void ExecutePhase() public void ExecutePhase()
{ {
var _shiftIn = false;
var _shiftOut = false;
// Process delayed interrupts // Process delayed interrupts
_ifr |= _interruptNextClock; _ifr |= _interruptNextClock;
_interruptNextClock = 0; _interruptNextClock = 0;
// Process 'pulse' and 'handshake' outputs on CA2 and CB2 // Process 'pulse' and 'handshake' outputs on PB7, CA2 and CB2
if (_resetCa2NextClock) if (_resetCa2NextClock)
{ {
Ca2 = true; Ca2 = true;
@ -174,6 +184,17 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_resetCb2NextClock = _pcrCb2Control == PCR_CONTROL_PULSE_OUTPUT; _resetCb2NextClock = _pcrCb2Control == PCR_CONTROL_PULSE_OUTPUT;
_handshakeCb2NextClock = false; _handshakeCb2NextClock = false;
} }
if (_resetPb7NextClock)
{
_prb &= 0x7F;
_resetPb7NextClock = false;
}
else if (_setPb7NextClock)
{
_prb |= 0x80;
_setPb7NextClock = false;
}
// Count timers // Count timers
if (_t1Delayed > 0) if (_t1Delayed > 0)
@ -183,7 +204,19 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
else else
{ {
_t1C--; _t1C--;
if (_t1C < 0) if (_t1C == 0)
{
switch (_acrT1Control)
{
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7:
_prb ^= 0x80;
break;
case ACR_T1_CONTROL_INTERRUPT_ON_LOAD_AND_PULSE_PB7:
_prb |= 0x80;
break;
}
}
else if (_t1C < 0)
{ {
if (_t1CLoaded) if (_t1CLoaded)
{ {
@ -194,12 +227,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
switch (_acrT1Control) switch (_acrT1Control)
{ {
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS: case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS:
_t1C = _t1L;
_t1CLoaded = true;
break;
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7: case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7:
_t1C = _t1L; _t1C = _t1L;
_prb ^= 0x80;
_t1CLoaded = true; _t1CLoaded = true;
break; break;
} }
@ -234,11 +263,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
if (!_pb6 && _pb6L) if (!_pb6 && _pb6L)
{ {
_t2C--; _t2C--;
if (_t2C < 0) if (_t2C == 0)
{
_ifr |= 0x20; _ifr |= 0x20;
_t2C = 0xFFFF; _t2C &= 0xFFFF;
}
} }
break; break;
} }
@ -304,44 +331,60 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
break; break;
} }
// interrupt generation // interrupt generation
if ((_pcrCb1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Cb1 && !_cb1L) ||
(_pcrCb1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Cb1 && _cb1L)) if (_acrSrControl == ACR_SR_CONTROL_DISABLED)
{ {
_ifr |= 0x10; _ifr &= 0xFB;
if (_acrPbLatchEnable) _srCount = 0;
{
_pbLatch = _port.ReadExternalPrb();
}
} }
if ((_pcrCa1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Ca1 && !_ca1L) || /*
(_pcrCa1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Ca1 && _ca1L)) As long as the CA1 interrupt flag is set, the data on the peripheral pins can change
{ without affecting the data in the latches. This input latching can be used with any of the CA2
_ifr |= 0x02; input or output modes.
if (_acrPaLatchEnable) It is important to note that on the PA port, the processor always reads the data on the
{ peripheral pins (as reflected in the latches). For output pins, the processor still reads the
latches. This may or may not reflect the data currently in the ORA. Proper system operation
requires careful planning on the part of the system designer if input latching is combined
with output pins on the peripheral ports.
*/
if ((_pcrCa1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Ca1 && !_ca1L) ||
(_pcrCa1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Ca1 && _ca1L))
{
if (_acrPaLatchEnable && (_ifr & 0x02) == 0)
_paLatch = _port.ReadExternalPra(); _paLatch = _port.ReadExternalPra();
} _ifr |= 0x02;
} }
switch (_acrSrControl) /*
Input latching on the PB port is controlled in the same manner as that described for the PA port.
However, with the peripheral B port the input latch will store either the voltage on the pin or the contents
of the Output Register (ORB) depending on whether the pin is programmed to act as an input or an
output. As with the PA port, the processor always reads the input latches.
*/
if ((_pcrCb1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Cb1 && !_cb1L) ||
(_pcrCb1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Cb1 && _cb1L))
{
if (_acrPbLatchEnable && (_ifr & 0x10) == 0)
_pbLatch = _port.ReadPrb(_prb, _ddrb);
if (_acrSrControl == ACR_SR_CONTROL_DISABLED)
_shiftIn = true;
_ifr |= 0x10;
}
if (_shiftIn)
{ {
case ACR_SR_CONTROL_DISABLED: _sr <<= 1;
_ifr &= 0xFB; _sr |= Cb2 ? 1 : 0;
break;
default:
break;
} }
if ((_ifr & _ier & 0x7F) != 0) if ((_ifr & _ier & 0x7F) != 0)
{
_ifr |= 0x80; _ifr |= 0x80;
}
else else
{
_ifr &= 0x7F; _ifr &= 0x7F;
}
_ca1L = Ca1; _ca1L = Ca1;
_ca2L = Ca2; _ca2L = Ca2;
@ -399,6 +442,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
ser.Sync("T2Loaded", ref _t2CLoaded); ser.Sync("T2Loaded", ref _t2CLoaded);
ser.Sync("T1Delayed", ref _t1Delayed); ser.Sync("T1Delayed", ref _t1Delayed);
ser.Sync("T2Delayed", ref _t2Delayed); ser.Sync("T2Delayed", ref _t2Delayed);
ser.Sync("ResetPb7NextClock", ref _resetPb7NextClock);
ser.Sync("SetPb7NextClock", ref _setPb7NextClock);
ser.Sync("ShiftRegisterCount", ref _srCount);
} }
} }
} }

View File

@ -2,7 +2,7 @@
{ {
public sealed partial class Vic public sealed partial class Vic
{ {
private const int BaResetCounter = 4; private const int BaResetCounter = 3;
private const int PipelineUpdateVc = 0x00000001; // vc/rc rule 2 private const int PipelineUpdateVc = 0x00000001; // vc/rc rule 2
private const int PipelineSpriteCrunch = 0x00000002; private const int PipelineSpriteCrunch = 0x00000002;
private const int PipelineUpdateMcBase = 0x00000004; private const int PipelineUpdateMcBase = 0x00000004;
@ -42,6 +42,12 @@
private int _parseBa; private int _parseBa;
private int _parseAct; private int _parseAct;
private bool _parseIsSprCrunch; private bool _parseIsSprCrunch;
private int _parsePixelData;
private int _parsePixelDataIndex;
private int _parseSrData0;
private int _parseSrData1;
private int _parseSrShift;
private bool _parseSrColorEnable;
private void ParseCycle() private void ParseCycle()
{ {
@ -66,7 +72,7 @@
if (_badline) if (_badline)
{ {
_parseAddr = _pointerVm | _vc; _parseAddr = _pointerVm | _vc;
_dataC = ReadMemory(_parseAddr); _dataC = _baCount >= 0 ? 0xFF : ReadMemory(_parseAddr);
_dataC |= (ReadColorRam(_parseAddr) & 0xF) << 8; _dataC |= (ReadColorRam(_parseAddr) & 0xF) << 8;
_bufferC[_vmli] = _dataC; _bufferC[_vmli] = _dataC;
} }
@ -80,8 +86,6 @@
_dataC = 0; _dataC = 0;
_bufferC[_vmli] = _dataC; _bufferC[_vmli] = _dataC;
} }
_srColorSync |= 0x01 << (7 - _xScroll);
break; break;
case FetchTypeGraphics: case FetchTypeGraphics:
// fetch G // fetch G
@ -93,17 +97,105 @@
_parseAddr = _rc | ((_dataC & 0xFF) << 3) | (_pointerCb << 11); _parseAddr = _rc | ((_dataC & 0xFF) << 3) | (_pointerCb << 11);
} }
if (_extraColorModeBuffer) if (_extraColorMode)
_parseAddr &= AddressMaskEc; _parseAddr &= AddressMaskEc;
_dataG = ReadMemory(_parseAddr); _dataG = ReadMemory(_parseAddr);
_sr |= _dataG << (7 - _xScroll);
_srSync |= 0xAA << (7 - _xScroll); if (!_idle && _vcEnable)
if (!_idle)
{ {
_bufferG[_vmli] = _dataG;
_vmli = (_vmli + 1) & 0x3F; _vmli = (_vmli + 1) & 0x3F;
_vc = (_vc + 1) & 0x3FF; _vc = (_vc + 1) & 0x3FF;
} }
// graphics data shift register
_parseSrShift = 7 - _xScroll;
_srData1 &= ~(0xFF << _parseSrShift);
_srColorEnable &= ~(0xFF << _parseSrShift);
if (_multicolorMode && (_bitmapMode || (_dataC & 0x800) != 0))
{
_parseSrData0 = (_dataG & 0x55) | ((_dataG & 0x55) << 1);
_parseSrData1 = (_dataG & 0xAA) | ((_dataG & 0xAA) >> 1);
}
else
{
_parseSrData0 = _parseSrData1 = _dataG;
}
_srData1 |= _parseSrData1 << _parseSrShift;
if (_bitmapMode && !_multicolorMode)
_parseSrData1 ^= 0xFF;
// graphics color shift register
_srColor0 &= ~(0xFF << _parseSrShift);
_srColor1 &= ~(0xFF << _parseSrShift);
_srColor2 &= ~(0xFF << _parseSrShift);
_srColor3 &= ~(0xFF << _parseSrShift);
for (_parsePixelDataIndex = 7; _parsePixelDataIndex >= 0; _parsePixelDataIndex--)
{
_parsePixelData = ((_parseSrData0 & 0x80) >> 7) | ((_parseSrData1 & 0x80) >> 6);
switch (_videoMode)
{
case VideoMode000:
case VideoMode001:
if (_parsePixelData == 3)
{
_pixel = _idle ? 0 : (_multicolorMode ? _dataC & 0x700 : _dataC) >> 8;
_parseSrColorEnable = true;
}
else
{
_pixel = _parsePixelData;
_parseSrColorEnable = false;
}
break;
case VideoMode010:
case VideoMode011:
_parseSrColorEnable = _parsePixelData != 0;
switch (_parsePixelData)
{
case 0:
_pixel = 0;
break;
case 1:
_pixel = _idle ? 0 : _dataC >> 4;
break;
case 2:
_pixel = _idle ? 0 : _dataC;
break;
default:
_pixel = _idle ? 0 : _dataC >> 8;
break;
}
break;
case VideoMode100:
if (_parsePixelData != 0)
{
_pixel = _idle ? 0 : _dataC >> 8;
_parseSrColorEnable = true;
}
else
{
_pixel = (_dataC & 0xC0) >> 6;
_parseSrColorEnable = false;
}
break;
default:
_parsePixelData = 0;
_pixel = 0;
_parseSrColorEnable = true;
break;
}
_parseSrData0 <<= 1;
_parseSrData1 <<= 1;
_srColor0 |= (_pixel & 1) << (_parseSrShift + _parsePixelDataIndex);
_srColor1 |= ((_pixel >> 1) & 1) << (_parseSrShift + _parsePixelDataIndex);
_srColor2 |= ((_pixel >> 2) & 1) << (_parseSrShift + _parsePixelDataIndex);
_srColor3 |= ((_pixel >> 3) & 1) << (_parseSrShift + _parsePixelDataIndex);
_srColorEnable |= (_parseSrColorEnable ? 1 : 0) << (_parseSrShift + _parsePixelDataIndex);
}
break; break;
case FetchTypeNone: case FetchTypeNone:
// fetch none // fetch none
@ -135,7 +227,7 @@
{ {
_parseAddr = spr.Mc | (spr.Pointer << 6); _parseAddr = spr.Mc | (spr.Pointer << 6);
spr.Sr |= ReadMemory(_parseAddr) << ((0x30 - (_parseFetch & 0x30)) >> 1); spr.Sr |= ReadMemory(_parseAddr) << ((0x30 - (_parseFetch & 0x30)) >> 1);
spr.Mc++; spr.Mc = (spr.Mc + 1) & 0x3F;
spr.Loaded |= 0x800000; spr.Loaded |= 0x800000;
} }
else if ((_parseFetch & 0xF0) == 0x20) else if ((_parseFetch & 0xF0) == 0x20)
@ -181,7 +273,7 @@
{ {
spr.Dma = true; spr.Dma = true;
spr.Mcbase = 0; spr.Mcbase = 0;
spr.YCrunch = true; spr.YCrunch = spr.YExpand;
} }
} }
} }
@ -221,7 +313,6 @@
if ((_parseAct & PipelineUpdateVc) != 0) // VC/RC rule 2 if ((_parseAct & PipelineUpdateVc) != 0) // VC/RC rule 2
{ {
_vc = _vcbase; _vc = _vcbase;
_srColorIndexLatch = 0;
_vmli = 0; _vmli = 0;
if (_badline) if (_badline)
{ {
@ -245,22 +336,21 @@
} }
// perform BA flag manipulation // perform BA flag manipulation
_pinBa = true;
switch (_parseBa) switch (_parseBa)
{ {
case BaTypeNone: case BaTypeNone:
_ba = true;
break; break;
case BaTypeCharacter: case BaTypeCharacter:
_pinBa = !_badline; _ba = !_badline;
break; break;
default: default:
_parseCycleBaSprite0 = _parseBa & BaTypeMaskSprite0; _parseCycleBaSprite0 = _parseBa & BaTypeMaskSprite0;
_parseCycleBaSprite1 = (_parseBa & BaTypeMaskSprite1) >> 4; _parseCycleBaSprite1 = (_parseBa & BaTypeMaskSprite1) >> 4;
_parseCycleBaSprite2 = (_parseBa & BaTypeMaskSprite2) >> 8; _parseCycleBaSprite2 = (_parseBa & BaTypeMaskSprite2) >> 8;
if ((_parseCycleBaSprite0 < 8 && _sprites[_parseCycleBaSprite0].Dma) || _ba = !((_parseCycleBaSprite0 < 8 && _sprites[_parseCycleBaSprite0].Dma) ||
(_parseCycleBaSprite1 < 8 && _sprites[_parseCycleBaSprite1].Dma) || (_parseCycleBaSprite1 < 8 && _sprites[_parseCycleBaSprite1].Dma) ||
(_parseCycleBaSprite2 < 8 && _sprites[_parseCycleBaSprite2].Dma)) (_parseCycleBaSprite2 < 8 && _sprites[_parseCycleBaSprite2].Dma));
_pinBa = false;
break; break;
} }

View File

@ -99,11 +99,11 @@
return 0x01 | ((_pointerVm & 0x3C00) >> 6) | return 0x01 | ((_pointerVm & 0x3C00) >> 6) |
((_pointerCb & 0x7) << 1); ((_pointerCb & 0x7) << 1);
case 0x19: case 0x19:
return 0x70 | (_rasterInterruptTriggered ? 0x01 : 0x00) | return 0x70 | (_intRaster ? 0x01 : 0x00) |
(_intSpriteDataCollision ? 0x02 : 0x00) | (_intSpriteDataCollision ? 0x02 : 0x00) |
(_intSpriteCollision ? 0x04 : 0x00) | (_intSpriteCollision ? 0x04 : 0x00) |
(_intLightPen ? 0x08 : 0x00) | (_intLightPen ? 0x08 : 0x00) |
(_pinIrq ? 0x00 : 0x80); ((_irqBuffer & 1) << 7);
case 0x1A: case 0x1A:
return 0xF0 | (_enableIntRaster ? 0x01 : 0x00) | return 0xF0 | (_enableIntRaster ? 0x01 : 0x00) |
(_enableIntSpriteDataCollision ? 0x02 : 0x00) | (_enableIntSpriteDataCollision ? 0x02 : 0x00) |
@ -181,10 +181,12 @@
return 0xFF; return 0xFF;
} }
} }
public void Write(int addr, int val) public void Write(int addr, int val)
{ {
addr &= 0x3F; addr &= 0x3F;
val &= 0xFF;
switch (addr) switch (addr)
{ {
case 0x17: case 0x17:
@ -207,18 +209,13 @@
case 0x19: case 0x19:
// interrupts are cleared by writing a 1 // interrupts are cleared by writing a 1
if ((val & 0x01) != 0) if ((val & 0x01) != 0)
{
_intRaster = false; _intRaster = false;
_rasterInterruptTriggered = false;
}
if ((val & 0x02) != 0) if ((val & 0x02) != 0)
_intSpriteDataCollision = false; _intSpriteDataCollision = false;
if ((val & 0x04) != 0) if ((val & 0x04) != 0)
_intSpriteCollision = false; _intSpriteCollision = false;
if ((val & 0x08) != 0) if ((val & 0x08) != 0)
_intLightPen = false; _intLightPen = false;
UpdatePins();
break; break;
case 0x1E: case 0x1E:
case 0x1F: case 0x1F:
@ -341,14 +338,12 @@
_intSpriteDataCollision = (val & 0x02) != 0; _intSpriteDataCollision = (val & 0x02) != 0;
_intSpriteCollision = (val & 0x04) != 0; _intSpriteCollision = (val & 0x04) != 0;
_intLightPen = (val & 0x08) != 0; _intLightPen = (val & 0x08) != 0;
UpdatePins();
break; break;
case 0x1A: case 0x1A:
_enableIntRaster = (val & 0x01) != 0; _enableIntRaster = (val & 0x01) != 0;
_enableIntSpriteDataCollision = (val & 0x02) != 0; _enableIntSpriteDataCollision = (val & 0x02) != 0;
_enableIntSpriteCollision = (val & 0x04) != 0; _enableIntSpriteCollision = (val & 0x04) != 0;
_enableIntLightPen = (val & 0x08) != 0; _enableIntLightPen = (val & 0x08) != 0;
UpdatePins();
break; break;
case 0x1B: case 0x1B:
_sprite0.Priority = (val & 0x01) != 0; _sprite0.Priority = (val & 0x01) != 0;

View File

@ -4,17 +4,19 @@
{ {
private int _borderPixel; private int _borderPixel;
private int _bufferPixel; private int _bufferPixel;
private int _ecmPixel;
private int _pixel; private int _pixel;
private int _pixelCounter; private int _pixelCounter;
private int _pixelData;
private int _pixelOwner; private int _pixelOwner;
private Sprite _spr;
private int _sprData; private int _sprData;
private int _sprIndex; private int _sprIndex;
private int _sprPixel; private int _sprPixel;
private int _srSync; private int _srColor0;
private int _srColorSync; private int _srColor1;
private int _srColorIndexLatch; private int _srColor2;
private int _srColor3;
private int _srData1;
private int _srColorEnable;
private int _videoMode; private int _videoMode;
private int _borderOnShiftReg; private int _borderOnShiftReg;
@ -25,10 +27,7 @@
private const int VideoMode100 = 4; private const int VideoMode100 = 4;
private const int VideoModeInvalid = -1; private const int VideoModeInvalid = -1;
private const int SrMask1 = 0x20000; private const int SrMask1 = 0x40000;
private const int SrMask2 = SrMask1 << 1;
private const int SrMask3 = SrMask1 | SrMask2;
private const int SrColorMask = 0x8000;
private const int SrSpriteMask = SrSpriteMask2; private const int SrSpriteMask = SrSpriteMask2;
private const int SrSpriteMask1 = 0x400000; private const int SrSpriteMask1 = 0x400000;
private const int SrSpriteMask2 = SrSpriteMask1 << 1; private const int SrSpriteMask2 = SrSpriteMask1 << 1;
@ -43,16 +42,9 @@
_hblank = true; _hblank = true;
_renderEnabled = !_hblank && !_vblank; _renderEnabled = !_hblank && !_vblank;
_pixelCounter = -1; _pixelCounter = 4;
while (_pixelCounter++ < 3) while (--_pixelCounter >= 0)
{ {
if ((_srColorSync & SrColorMask) != 0)
{
_displayC = _bufferC[_srColorIndexLatch];
_srColorIndexLatch = (_srColorIndexLatch + 1) & 0x3F;
}
#region PRE-RENDER BORDER #region PRE-RENDER BORDER
// check left border // check left border
@ -72,154 +64,82 @@
#endregion #endregion
#region CHARACTER GRAPHICS // render graphics
switch (_videoMode) if ((_srColorEnable & SrMask1) != 0)
{ {
case VideoMode000: _pixel = ((_srColor0 & SrMask1) >> 18) |
_pixelData = _sr & SrMask2; ((_srColor1 & SrMask1) >> 17) |
_pixel = _pixelData != 0 ? _displayC >> 8 : _backgroundColor0; ((_srColor2 & SrMask1) >> 16) |
break; ((_srColor3 & SrMask1) >> 15);
case VideoMode001: }
if ((_displayC & 0x800) != 0) else
{ {
// multicolor 001 switch (((_srColor0 & SrMask1) >> 18) | ((_srColor1 & SrMask1) >> 17))
if ((_srSync & SrMask2) != 0) {
_pixelData = _sr & SrMask3; case 1:
_pixel = _idle ? 0 : _backgroundColor1;
switch (_pixelData) break;
{ case 2:
case 0: _pixel = _idle ? 0 : _backgroundColor2;
_pixel = _backgroundColor0; break;
break; case 3:
case SrMask1: _pixel = _idle ? 0 : _backgroundColor3;
_pixel = _backgroundColor1; break;
break; default:
case SrMask2: _pixel = _backgroundColor0;
_pixel = _backgroundColor2; break;
break; }
default:
_pixel = (_displayC & 0x700) >> 8;
break;
}
}
else
{
// standard 001
_pixelData = _sr & SrMask2;
_pixel = _pixelData != 0 ? _displayC >> 8 : _backgroundColor0;
}
break;
case VideoMode010:
_pixelData = _sr & SrMask2;
_pixel = _pixelData != 0 ? _displayC >> 4 : _displayC;
break;
case VideoMode011:
if ((_srSync & SrMask2) != 0)
_pixelData = _sr & SrMask3;
switch (_pixelData)
{
case 0:
_pixel = _backgroundColor0;
break;
case SrMask1:
_pixel = _displayC >> 4;
break;
case SrMask2:
_pixel = _displayC;
break;
default:
_pixel = _displayC >> 8;
break;
}
break;
case VideoMode100:
_pixelData = _sr & SrMask2;
if (_pixelData != 0)
{
_pixel = _displayC >> 8;
}
else
{
_ecmPixel = (_displayC & 0xC0) >> 6;
switch (_ecmPixel)
{
case 0:
_pixel = _backgroundColor0;
break;
case 1:
_pixel = _backgroundColor1;
break;
case 2:
_pixel = _backgroundColor2;
break;
default:
_pixel = _backgroundColor3;
break;
}
}
break;
default:
_pixelData = 0;
_pixel = 0;
break;
} }
_pixel &= 0xF;
_sr <<= 1;
_srSync <<= 1;
_srColorSync <<= 1;
#endregion
#region SPRITES
// render sprites // render sprites
_pixelOwner = -1; _pixelOwner = -1;
_sprIndex = 0; for (_sprIndex = 0; _sprIndex < 8; _sprIndex++)
foreach (var spr in _sprites)
{ {
_spr = _sprites[_sprIndex];
_sprData = 0; _sprData = 0;
_sprPixel = _pixel; _sprPixel = _pixel;
if (spr.X == _rasterX) if (_spr.X == _rasterX)
{ {
spr.ShiftEnable = spr.Display; _spr.ShiftEnable = _spr.Display;
spr.XCrunch = !spr.XExpand; _spr.XCrunch = !_spr.XExpand;
spr.MulticolorCrunch = false; _spr.MulticolorCrunch = false;
} }
else else
{ {
spr.XCrunch |= !spr.XExpand; _spr.XCrunch |= !_spr.XExpand;
} }
if (spr.ShiftEnable) // sprite rule 6 if (_spr.ShiftEnable) // sprite rule 6
{ {
if (spr.Multicolor) if (_spr.Multicolor)
{ {
_sprData = spr.Sr & SrSpriteMaskMc; _sprData = _spr.Sr & SrSpriteMaskMc;
if (spr.MulticolorCrunch && spr.XCrunch && !_rasterXHold) if (_spr.MulticolorCrunch && _spr.XCrunch && !_rasterXHold)
{ {
if (spr.Loaded == 0) if (_spr.Loaded == 0)
{ {
spr.ShiftEnable = false; _spr.ShiftEnable = false;
} }
spr.Sr <<= 2; _spr.Sr <<= 2;
spr.Loaded >>= 2; _spr.Loaded >>= 2;
} }
spr.MulticolorCrunch ^= spr.XCrunch; _spr.MulticolorCrunch ^= _spr.XCrunch;
} }
else else
{ {
_sprData = spr.Sr & SrSpriteMask; _sprData = _spr.Sr & SrSpriteMask;
if (spr.XCrunch && !_rasterXHold) if (_spr.XCrunch && !_rasterXHold)
{ {
if (spr.Loaded == 0) if (_spr.Loaded == 0)
{ {
spr.ShiftEnable = false; _spr.ShiftEnable = false;
} }
spr.Sr <<= 1; _spr.Sr <<= 1;
spr.Loaded >>= 1; _spr.Loaded >>= 1;
} }
} }
spr.XCrunch ^= spr.XExpand; _spr.XCrunch ^= _spr.XExpand;
if (_sprData != 0) if (_sprData != 0)
{ {
@ -232,7 +152,7 @@
_sprPixel = _spriteMulticolor0; _sprPixel = _spriteMulticolor0;
break; break;
case SrSpriteMask2: case SrSpriteMask2:
_sprPixel = spr.Color; _sprPixel = _spr.Color;
break; break;
case SrSpriteMask3: case SrSpriteMask3:
_sprPixel = _spriteMulticolor1; _sprPixel = _spriteMulticolor1;
@ -244,21 +164,23 @@
{ {
if (!_borderOnVertical) if (!_borderOnVertical)
{ {
spr.CollideSprite = true; _spr.CollideSprite = true;
_sprites[_pixelOwner].CollideSprite = true; _sprites[_pixelOwner].CollideSprite = true;
_intSpriteCollision = true;
} }
} }
// sprite-data collision // sprite-data collision
if (!_borderOnVertical && (_pixelData >= SrMask2)) if (!_borderOnVertical && (_srData1 & SrMask1) != 0)
{ {
spr.CollideData = true; _spr.CollideData = true;
_intSpriteDataCollision = true;
} }
// sprite priority logic // sprite priority logic
if (spr.Priority) if (_spr.Priority)
{ {
_pixel = _pixelData >= SrMask2 ? _pixel : _sprPixel; _pixel = (_srData1 & SrMask1) != 0 ? _pixel : _sprPixel;
} }
else else
{ {
@ -266,12 +188,8 @@
} }
} }
} }
_sprIndex++;
} }
#endregion
#region POST-RENDER BORDER #region POST-RENDER BORDER
// border doesn't work with the background buffer // border doesn't work with the background buffer
@ -297,6 +215,13 @@
if (!_rasterXHold) if (!_rasterXHold)
_rasterX++; _rasterX++;
_srColor0 <<= 1;
_srColor1 <<= 1;
_srColor2 <<= 1;
_srColor3 <<= 1;
_srData1 <<= 1;
_srColorEnable <<= 1;
} }
if (_pixBufferBorderIndex >= PixBorderBufferSize) if (_pixBufferBorderIndex >= PixBorderBufferSize)

View File

@ -12,6 +12,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public bool Display; public bool Display;
public bool Dma; public bool Dma;
public bool Enable; public bool Enable;
public int Index;
public int Loaded; public int Loaded;
public int Mc; public int Mc;
public int Mcbase; public int Mcbase;
@ -28,6 +29,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public bool YCrunch; public bool YCrunch;
public bool YExpand; public bool YExpand;
public Sprite(int index)
{
Index = index;
}
public void HardReset() public void HardReset()
{ {
CollideData = false; CollideData = false;

View File

@ -8,6 +8,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private int _backgroundColor1; private int _backgroundColor1;
private int _backgroundColor2; private int _backgroundColor2;
private int _backgroundColor3; private int _backgroundColor3;
private bool _ba;
private int _baCount; private int _baCount;
private bool _badline; private bool _badline;
private bool _badlineEnable; private bool _badlineEnable;
@ -22,20 +23,17 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private int _borderR; private int _borderR;
private int _borderT; private int _borderT;
private int[] _bufferC; private int[] _bufferC;
private int[] _bufferG;
private int _cycle; private int _cycle;
private int _cycleIndex; private int _cycleIndex;
private bool _columnSelect; private bool _columnSelect;
private int _dataC; private int _dataC;
private int _dataG; private int _dataG;
private bool _displayEnable; private bool _displayEnable;
private int _displayC;
private bool _enableIntLightPen; private bool _enableIntLightPen;
private bool _enableIntRaster; private bool _enableIntRaster;
private bool _enableIntSpriteCollision; private bool _enableIntSpriteCollision;
private bool _enableIntSpriteDataCollision; private bool _enableIntSpriteDataCollision;
private bool _extraColorMode; private bool _extraColorMode;
private bool _extraColorModeBuffer;
private bool _hblank; private bool _hblank;
private bool _idle; private bool _idle;
private bool _intLightPen; private bool _intLightPen;
@ -47,11 +45,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private bool _multicolorMode; private bool _multicolorMode;
private bool _pinAec = true; private bool _pinAec = true;
private bool _pinBa = true; private bool _pinBa = true;
private bool _pinIrq = true;
private int _pointerCb; private int _pointerCb;
private int _pointerVm; private int _pointerVm;
private int _rasterInterruptLine; private int _rasterInterruptLine;
private bool _rasterInterruptTriggered;
private int _rasterLine; private int _rasterLine;
private int _rasterX; private int _rasterX;
private bool _rasterXHold; private bool _rasterXHold;
@ -72,28 +68,23 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private readonly Sprite _sprite6; private readonly Sprite _sprite6;
private readonly Sprite _sprite7; private readonly Sprite _sprite7;
private readonly Sprite[] _sprites; private readonly Sprite[] _sprites;
private int _sr;
private bool _vblank; private bool _vblank;
private int _vblankEnd; private int _vblankEnd;
private int _vblankStart; private int _vblankStart;
private int _vc; private int _vc;
private int _vcbase; private int _vcbase;
private bool _vcEnable;
private int _vmli; private int _vmli;
private int _xScroll; private int _xScroll;
private int _yScroll; private int _yScroll;
public void HardReset() public void HardReset()
{ {
_pinAec = true;
_pinBa = true;
_pinIrq = true;
_bufOffset = 0;
_backgroundColor0 = 0; _backgroundColor0 = 0;
_backgroundColor1 = 0; _backgroundColor1 = 0;
_backgroundColor2 = 0; _backgroundColor2 = 0;
_backgroundColor3 = 0; _backgroundColor3 = 0;
_ba = true;
_baCount = BaResetCounter; _baCount = BaResetCounter;
_badline = false; _badline = false;
_badlineEnable = false; _badlineEnable = false;
@ -103,7 +94,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_borderColor = 0; _borderColor = 0;
_borderOnMain = true; _borderOnMain = true;
_borderOnVertical = true; _borderOnVertical = true;
_bufOffset = 0;
_columnSelect = false; _columnSelect = false;
_cycle = 0;
_cycleIndex = 0;
_dataC = 0;
_dataG = 0;
_displayEnable = false; _displayEnable = false;
_enableIntLightPen = false; _enableIntLightPen = false;
_enableIntRaster = false; _enableIntRaster = false;
@ -115,14 +111,18 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_intRaster = false; _intRaster = false;
_intSpriteCollision = false; _intSpriteCollision = false;
_intSpriteDataCollision = false; _intSpriteDataCollision = false;
_irqBuffer = 0;
_lightPenX = 0; _lightPenX = 0;
_lightPenY = 0; _lightPenY = 0;
_multicolorMode = false; _multicolorMode = false;
_pinAec = true;
_pinBa = true;
_pointerCb = 0; _pointerCb = 0;
_pointerVm = 0; _pointerVm = 0;
_rasterInterruptLine = 0; _rasterInterruptLine = 0;
_rasterLine = 0; _rasterLine = 0;
_rasterX = 0; _rasterX = 0;
_rasterXHold = false;
_rc = 7; _rc = 7;
_refreshCounter = 0xFF; _refreshCounter = 0xFF;
_rowSelect = false; _rowSelect = false;
@ -130,13 +130,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_spriteSpriteCollisionClearPending = false; _spriteSpriteCollisionClearPending = false;
_spriteMulticolor0 = 0; _spriteMulticolor0 = 0;
_spriteMulticolor1 = 0; _spriteMulticolor1 = 0;
_sr = 0;
_vc = 0; _vc = 0;
_vcbase = 0; _vcbase = 0;
_vcEnable = false;
_vmli = 0; _vmli = 0;
_xScroll = 0; _xScroll = 0;
_yScroll = 0; _yScroll = 0;
_cycle = 0;
// reset sprites // reset sprites
for (var i = 0; i < 8; i++) for (var i = 0; i < 8; i++)
@ -148,11 +147,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
for (var i = 0; i < 40; i++) for (var i = 0; i < 40; i++)
{ {
_bufferC[i] = 0; _bufferC[i] = 0;
_bufferG[i] = 0;
} }
_pixBuffer = new int[PixBufferSize];
_pixBorderBuffer = new int[PixBorderBufferSize];
_pixBufferIndex = 0; _pixBufferIndex = 0;
_pixBufferBorderIndex = 0; _pixBufferBorderIndex = 0;
UpdateBorder(); UpdateBorder();
@ -160,13 +156,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public void SyncState(Serializer ser) public void SyncState(Serializer ser)
{ {
ser.Sync(nameof(_cyclesExecuted), ref _cyclesExecuted); ser.Sync(nameof(_ba), ref _ba);
ser.Sync(nameof(_parseIsSprCrunch), ref _parseIsSprCrunch);
ser.Sync(nameof(_srSync), ref _srSync);
ser.Sync(nameof(_srColorSync), ref _srColorSync);
ser.Sync(nameof(_srColorIndexLatch), ref _srColorIndexLatch);
ser.Sync(nameof(_videoMode), ref _videoMode);
ser.Sync(nameof(_borderOnShiftReg), ref _borderOnShiftReg);
ser.Sync(nameof(_backgroundColor0), ref _backgroundColor0); ser.Sync(nameof(_backgroundColor0), ref _backgroundColor0);
ser.Sync(nameof(_backgroundColor1), ref _backgroundColor1); ser.Sync(nameof(_backgroundColor1), ref _backgroundColor1);
ser.Sync(nameof(_backgroundColor2), ref _backgroundColor2); ser.Sync(nameof(_backgroundColor2), ref _backgroundColor2);
@ -181,39 +171,42 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
ser.Sync(nameof(_borderColor), ref _borderColor); ser.Sync(nameof(_borderColor), ref _borderColor);
ser.Sync(nameof(_borderL), ref _borderL); ser.Sync(nameof(_borderL), ref _borderL);
ser.Sync(nameof(_borderOnMain), ref _borderOnMain); ser.Sync(nameof(_borderOnMain), ref _borderOnMain);
ser.Sync(nameof(_borderOnShiftReg), ref _borderOnShiftReg);
ser.Sync(nameof(_borderOnVertical), ref _borderOnVertical); ser.Sync(nameof(_borderOnVertical), ref _borderOnVertical);
ser.Sync(nameof(_borderR), ref _borderR); ser.Sync(nameof(_borderR), ref _borderR);
ser.Sync(nameof(_borderT), ref _borderT); ser.Sync(nameof(_borderT), ref _borderT);
ser.Sync(nameof(_bufferC), ref _bufferC, useNull: false); ser.Sync(nameof(_bufferC), ref _bufferC, useNull: false);
ser.Sync(nameof(_bufferG), ref _bufferG, useNull: false); ser.Sync(nameof(_bufOffset), ref _bufOffset);
ser.Sync(nameof(_cycle), ref _cycle); ser.Sync(nameof(_cycle), ref _cycle);
ser.Sync(nameof(_cycleIndex), ref _cycleIndex); ser.Sync(nameof(_cycleIndex), ref _cycleIndex);
ser.Sync(nameof(_columnSelect), ref _columnSelect); ser.Sync(nameof(_columnSelect), ref _columnSelect);
ser.Sync(nameof(_dataC), ref _dataC); ser.Sync(nameof(_dataC), ref _dataC);
ser.Sync(nameof(_dataG), ref _dataG); ser.Sync(nameof(_dataG), ref _dataG);
ser.Sync(nameof(_displayEnable), ref _displayEnable); ser.Sync(nameof(_displayEnable), ref _displayEnable);
ser.Sync(nameof(_displayC), ref _displayC);
ser.Sync(nameof(_enableIntLightPen), ref _enableIntLightPen); ser.Sync(nameof(_enableIntLightPen), ref _enableIntLightPen);
ser.Sync(nameof(_enableIntRaster), ref _enableIntRaster); ser.Sync(nameof(_enableIntRaster), ref _enableIntRaster);
ser.Sync(nameof(_enableIntSpriteCollision), ref _enableIntSpriteCollision); ser.Sync(nameof(_enableIntSpriteCollision), ref _enableIntSpriteCollision);
ser.Sync(nameof(_enableIntSpriteDataCollision), ref _enableIntSpriteDataCollision); ser.Sync(nameof(_enableIntSpriteDataCollision), ref _enableIntSpriteDataCollision);
ser.Sync(nameof(_extraColorMode), ref _extraColorMode); ser.Sync(nameof(_extraColorMode), ref _extraColorMode);
ser.Sync(nameof(_extraColorModeBuffer), ref _extraColorModeBuffer);
ser.Sync(nameof(_idle), ref _idle); ser.Sync(nameof(_idle), ref _idle);
ser.Sync(nameof(_intLightPen), ref _intLightPen); ser.Sync(nameof(_intLightPen), ref _intLightPen);
ser.Sync(nameof(_intRaster), ref _intRaster); ser.Sync(nameof(_intRaster), ref _intRaster);
ser.Sync(nameof(_intSpriteCollision), ref _intSpriteCollision); ser.Sync(nameof(_intSpriteCollision), ref _intSpriteCollision);
ser.Sync(nameof(_intSpriteDataCollision), ref _intSpriteDataCollision); ser.Sync(nameof(_intSpriteDataCollision), ref _intSpriteDataCollision);
ser.Sync(nameof(_irqBuffer), ref _irqBuffer);
ser.Sync(nameof(_lightPenX), ref _lightPenX); ser.Sync(nameof(_lightPenX), ref _lightPenX);
ser.Sync(nameof(_lightPenY), ref _lightPenY); ser.Sync(nameof(_lightPenY), ref _lightPenY);
ser.Sync(nameof(_multicolorMode), ref _multicolorMode); ser.Sync(nameof(_multicolorMode), ref _multicolorMode);
ser.Sync(nameof(_pinAec), ref _pinAec); ser.Sync(nameof(_pinAec), ref _pinAec);
ser.Sync(nameof(_pinBa), ref _pinBa); ser.Sync(nameof(_pinBa), ref _pinBa);
ser.Sync(nameof(_pinIrq), ref _pinIrq); ser.Sync(nameof(_parseIsSprCrunch), ref _parseIsSprCrunch);
ser.Sync(nameof(_pixBorderBuffer), ref _pixBorderBuffer, useNull: false);
ser.Sync(nameof(_pixBufferBorderIndex), ref _pixBufferBorderIndex);
ser.Sync(nameof(_pixBuffer), ref _pixBuffer, useNull: false);
ser.Sync(nameof(_pixBufferIndex), ref _pixBufferIndex);
ser.Sync(nameof(_pointerCb), ref _pointerCb); ser.Sync(nameof(_pointerCb), ref _pointerCb);
ser.Sync(nameof(_pointerVm), ref _pointerVm); ser.Sync(nameof(_pointerVm), ref _pointerVm);
ser.Sync(nameof(_rasterInterruptLine), ref _rasterInterruptLine); ser.Sync(nameof(_rasterInterruptLine), ref _rasterInterruptLine);
ser.Sync(nameof(_rasterInterruptTriggered), ref _rasterInterruptTriggered);
ser.Sync(nameof(_rasterLine), ref _rasterLine); ser.Sync(nameof(_rasterLine), ref _rasterLine);
ser.Sync(nameof(_rasterX), ref _rasterX); ser.Sync(nameof(_rasterX), ref _rasterX);
ser.Sync(nameof(_rasterXHold), ref _rasterXHold); ser.Sync(nameof(_rasterXHold), ref _rasterXHold);
@ -226,29 +219,24 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
ser.Sync(nameof(_spriteMulticolor0), ref _spriteMulticolor0); ser.Sync(nameof(_spriteMulticolor0), ref _spriteMulticolor0);
ser.Sync(nameof(_spriteMulticolor1), ref _spriteMulticolor1); ser.Sync(nameof(_spriteMulticolor1), ref _spriteMulticolor1);
for (int i = 0; i < _sprites.Length; i++) foreach (var sprite in _sprites)
{ {
ser.BeginSection("Sprite" + i); ser.BeginSection($"Sprite{sprite.Index}");
_sprites[i].SyncState(ser); sprite.SyncState(ser);
ser.EndSection(); ser.EndSection();
} }
ser.Sync(nameof(_sr), ref _sr);
ser.Sync(nameof(_vc), ref _vc); ser.Sync(nameof(_vc), ref _vc);
ser.Sync(nameof(_vcbase), ref _vcbase); ser.Sync(nameof(_vcbase), ref _vcbase);
ser.Sync(nameof(_vcEnable), ref _vcEnable);
ser.Sync(nameof(_videoMode), ref _videoMode);
ser.Sync(nameof(_vmli), ref _vmli); ser.Sync(nameof(_vmli), ref _vmli);
ser.Sync(nameof(_xScroll), ref _xScroll); ser.Sync(nameof(_xScroll), ref _xScroll);
ser.Sync(nameof(_yScroll), ref _yScroll); ser.Sync(nameof(_yScroll), ref _yScroll);
ser.Sync(nameof(_bufOffset), ref _bufOffset);
ser.Sync(nameof(_pixBuffer), ref _pixBuffer, useNull: false);
ser.Sync(nameof(_pixBufferIndex), ref _pixBufferIndex);
ser.Sync(nameof(_pixBorderBuffer), ref _pixBorderBuffer, useNull: false);
ser.Sync(nameof(_pixBufferBorderIndex), ref _pixBufferBorderIndex);
if (ser.IsReader) if (ser.IsReader)
{ {
UpdateBorder(); UpdateBorder();
UpdatePins();
UpdateVideoMode(); UpdateVideoMode();
} }
} }

View File

@ -13,8 +13,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private const int BorderTop24 = 0x037; private const int BorderTop24 = 0x037;
private const int BorderBottom25 = 0x0FB; private const int BorderBottom25 = 0x0FB;
private const int BorderBottom24 = 0x0F7; private const int BorderBottom24 = 0x0F7;
private const int FirstDmaLine = 0x030; private const int BadLineEnableRaster = 0x030;
private const int LastDmaLine = 0x0F7; private const int BadLineDisableRaster = 0x0F8;
// The special actions taken by the Vic are in the same order and interval on all chips, just different offsets. // The special actions taken by the Vic are in the same order and interval on all chips, just different offsets.
private static readonly int[] TimingBuilderCycle14Act = private static readonly int[] TimingBuilderCycle14Act =

View File

@ -26,7 +26,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public bool ReadAec() { return _pinAec; } public bool ReadAec() { return _pinAec; }
public bool ReadBa() { return _pinBa; } public bool ReadBa() { return _pinBa; }
public bool ReadIrq() { return _pinIrq; } public bool ReadIrq() { return (_irqBuffer & 1) == 0; }
private readonly int _cyclesPerSec; private readonly int _cyclesPerSec;
private readonly int[] _rasterXPipeline; private readonly int[] _rasterXPipeline;
@ -35,8 +35,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private readonly int[] _actPipeline; private readonly int[] _actPipeline;
private readonly int _totalCycles; private readonly int _totalCycles;
private readonly int _totalLines; private readonly int _totalLines;
private int _irqBuffer;
private int _cyclesExecuted;
private int _hblankStartCheckXRaster; private int _hblankStartCheckXRaster;
private int _hblankEndCheckXRaster; private int _hblankEndCheckXRaster;
@ -67,9 +67,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_sprites = new Sprite[8]; _sprites = new Sprite[8];
for (var i = 0; i < 8; i++) for (var i = 0; i < 8; i++)
{ _sprites[i] = new Sprite(i);
_sprites[i] = new Sprite();
}
_sprite0 = _sprites[0]; _sprite0 = _sprites[0];
_sprite1 = _sprites[1]; _sprite1 = _sprites[1];
@ -79,9 +77,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_sprite5 = _sprites[5]; _sprite5 = _sprites[5];
_sprite6 = _sprites[6]; _sprite6 = _sprites[6];
_sprite7 = _sprites[7]; _sprite7 = _sprites[7];
_bufferC = new int[40]; _bufferC = new int[40];
_bufferG = new int[40]; _pixBuffer = new int[PixBufferSize];
_pixBorderBuffer = new int[PixBorderBufferSize];
} }
private void ConfigureBlanking(int lines, int hblankStart, int hblankEnd, int vblankStart, int vblankEnd, private void ConfigureBlanking(int lines, int hblankStart, int hblankEnd, int vblankStart, int vblankEnd,
@ -190,7 +188,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public int CyclesPerSecond => _cyclesPerSec; public int CyclesPerSecond => _cyclesPerSec;
public void ExecutePhase() public void ExecutePhase1()
{ {
// phi1 // phi1
@ -226,9 +224,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_rasterLine = 0; _rasterLine = 0;
_vcbase = 0; _vcbase = 0;
_vc = 0; _vc = 0;
_badlineEnable = false;
_refreshCounter = 0xFF; _refreshCounter = 0xFF;
_cyclesExecuted = 0;
} }
} }
@ -251,30 +247,31 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
} }
_spriteSpriteCollisionClearPending = false; _spriteSpriteCollisionClearPending = false;
} }
// phi2
// start of rasterline // start of rasterline
if ((_cycle == RasterIrqLineXCycle && _rasterLine > 0) || (_cycle == RasterIrqLine0Cycle && _rasterLine == 0)) if ((_cycle == RasterIrqLineXCycle && _rasterLine > 0) || (_cycle == RasterIrqLine0Cycle && _rasterLine == 0))
{ {
//_rasterInterruptTriggered = false; if (_rasterLine == BadLineDisableRaster)
if (_rasterLine == LastDmaLine)
_badlineEnable = false; _badlineEnable = false;
// IRQ compares are done here // raster compares are done here
if (_rasterLine == _rasterInterruptLine) if (_rasterLine == _rasterInterruptLine)
{ {
_rasterInterruptTriggered = true; _intRaster = true;
// interrupt needs to be enabled to be set to true
if (_enableIntRaster)
{
_intRaster = true;
}
} }
} }
// render
ParseCycle();
UpdateBa();
UpdatePins();
Render();
}
public void ExecutePhase2()
{
// phi2
// check top and bottom border // check top and bottom border
if (_rasterLine == _borderB) if (_rasterLine == _borderB)
{ {
@ -286,12 +283,13 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
} }
// display enable compare // display enable compare
if (_rasterLine == FirstDmaLine) if (_rasterLine == BadLineEnableRaster)
{ {
_badlineEnable |= _displayEnable; _badlineEnable |= _displayEnable;
} }
// badline compare // badline compare
_vcEnable = !_idle;
if (_badlineEnable) if (_badlineEnable)
{ {
if ((_rasterLine & 0x7) == _yScroll) if ((_rasterLine & 0x7) == _yScroll)
@ -313,22 +311,16 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
// render // render
ParseCycle(); ParseCycle();
Render();
ParseCycle();
Render();
_extraColorModeBuffer = _extraColorMode;
// if the BA counter is nonzero, allow CPU bus access
if (_pinBa)
_baCount = BaResetCounter;
else if (_baCount > 0)
_baCount--;
_pinAec = _pinBa || _baCount > 0;
// must always come last
UpdatePins(); UpdatePins();
Render();
_cyclesExecuted++; }
private void UpdateBa()
{
if (_ba)
_baCount = BaResetCounter;
else if (_baCount >= 0)
_baCount--;
} }
private void UpdateBorder() private void UpdateBorder()
@ -341,13 +333,17 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private void UpdatePins() private void UpdatePins()
{ {
var irqTemp = !( // IRQ is treated as a delay line
(_enableIntRaster & _intRaster) |
(_enableIntSpriteDataCollision & _intSpriteDataCollision) |
(_enableIntSpriteCollision & _intSpriteCollision) |
(_enableIntLightPen & _intLightPen));
_pinIrq = irqTemp; var intIrq = (_enableIntRaster && _intRaster) ? 0x0002 : 0x0000;
var sdIrq = (_enableIntSpriteDataCollision & _intSpriteDataCollision) ? 0x0001 : 0x0000;
var ssIrq = (_enableIntSpriteCollision & _intSpriteCollision) ? 0x0001 : 0x0000;
var lpIrq = (_enableIntLightPen & _intLightPen) ? 0x0001 : 0x0000;
_irqBuffer >>= 1;
_irqBuffer |= intIrq | sdIrq | ssIrq | lpIrq;
_pinAec = _ba || _baCount >= 0;
_pinBa = _ba;
} }
private void UpdateVideoMode() private void UpdateVideoMode()

View File

@ -1,11 +1,30 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{ {
public static class D64 public static class D64
{ {
const int D64_DISK_ID_OFFSET = 0x165A2; // track 18, sector 0, 0xA2
private enum ErrorType
{
NoError = 0x01,
HeaderNotFound = 0x02,
NoSyncSequence = 0x03,
DataNotFound = 0x04,
DataChecksumError = 0x05,
WriteVerifyFormatError = 0x06,
WriteVerifyError = 0x07,
WriteProtectOn = 0x08,
HeaderChecksumError = 0x09,
WriteError = 0x0A,
IdMismatch = 0x0B,
DriveNotReady = 0x0F
}
private static readonly int[] DensityTable = private static readonly int[] DensityTable =
{ {
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
@ -63,6 +82,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
6250, 6666, 7142, 7692 6250, 6666, 7142, 7692
}; };
private static readonly int[] StandardSectorGapLength =
{
9, 19, 13, 10
};
private static byte Checksum(byte[] source) private static byte Checksum(byte[] source)
{ {
var count = source.Length; var count = source.Length;
@ -76,27 +100,36 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
return result; return result;
} }
private static byte[] ConvertSectorToGcr(byte[] source, byte sectorNo, byte trackNo, byte formatA, byte formatB, out int bitsWritten) private static byte[] ConvertSectorToGcr(byte[] source, byte sectorNo, byte trackNo, byte formatA, byte formatB, int gapLength, ErrorType errorType, out int bitsWritten)
{ {
using (var mem = new MemoryStream()) using (var mem = new MemoryStream())
{ {
var writer = new BinaryWriter(mem); var writer = new BinaryWriter(mem);
var headerChecksum = (byte)(sectorNo ^ trackNo ^ formatA ^ formatB);
if (errorType == ErrorType.IdMismatch)
{
formatA ^= 0xFF;
formatB ^= 0xFF;
}
var headerChecksum = (byte)(sectorNo ^ trackNo ^ formatA ^ formatB ^ (errorType == ErrorType.HeaderChecksumError ? 0xFF : 0x00));
// assemble written data for GCR encoding // assemble written data for GCR encoding
var writtenData = new byte[260]; var writtenData = new byte[260];
var syncBytes40 = Enumerable.Repeat((byte) (errorType == ErrorType.NoSyncSequence ? 0x00 : 0xFF), 5).ToArray();
Array.Copy(source, 0, writtenData, 1, 256); Array.Copy(source, 0, writtenData, 1, 256);
writtenData[0] = 0x07; writtenData[0] = (byte)(errorType == ErrorType.HeaderNotFound ? 0x00 : 0x07);
writtenData[0x101] = Checksum(source); writtenData[0x101] = (byte)(Checksum(source) ^ (errorType == ErrorType.DataChecksumError ? 0xFF : 0x00));
writtenData[0x102] = 0x00; writtenData[0x102] = 0x00;
writtenData[0x103] = 0x00; writtenData[0x103] = 0x00;
writer.Write(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }); // sync writer.Write(syncBytes40); // sync
writer.Write(EncodeGcr(new byte[] { 0x08, headerChecksum, sectorNo, trackNo, formatA, formatB, 0x0F, 0x0F })); // header writer.Write(EncodeGcr(new byte[] { (byte)(errorType == ErrorType.DataNotFound ? 0x00 : 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[] { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }); // gap
writer.Write(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }); // sync writer.Write(syncBytes40); // sync
writer.Write(EncodeGcr(writtenData)); // data writer.Write(EncodeGcr(writtenData)); // data
writer.Write(new byte[] { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }); // gap writer.Write(Enumerable.Repeat((byte)0x55, gapLength).ToArray()); // gap
bitsWritten = (int)mem.Length * 8; bitsWritten = (int)mem.Length * 8;
@ -148,64 +181,76 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
public static Disk Read(byte[] source) public static Disk Read(byte[] source)
{ {
using (var mem = new MemoryStream(source)) var formatB = source[D64_DISK_ID_OFFSET + 0x00];
{ var formatA = source[D64_DISK_ID_OFFSET + 0x01];
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) using (var mem = new MemoryStream(source))
{ {
case 174848: // 35 tracks no errors var reader = new BinaryReader(mem);
trackCount = 35; var trackDatas = new List<byte[]>();
break; var trackLengths = new List<int>();
case 175531: // 35 tracks with errors var trackNumbers = new List<int>();
trackCount = 35; var trackDensities = new List<int>();
break; var errorType = ErrorType.NoError;
case 196608: // 40 tracks no errors int trackCount;
trackCount = 40; int errorOffset = -1;
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++) switch (source.Length)
{ {
var sectors = SectorsPerTrack[i]; case 174848: // 35 tracks no errors
var trackLengthBits = 0; trackCount = 35;
using (var trackMem = new MemoryStream()) break;
{ case 175531: // 35 tracks with errors
for (var j = 0; j < sectors; j++) trackCount = 35;
{ errorOffset = 174848;
int bitsWritten; break;
var sectorData = reader.ReadBytes(256); case 196608: // 40 tracks no errors
var diskData = ConvertSectorToGcr(sectorData, (byte)j, (byte)(i + 1), 0xA0, 0xA0, out bitsWritten); trackCount = 40;
trackMem.Write(diskData, 0, diskData.Length); break;
trackLengthBits += bitsWritten; case 197376: // 40 tracks with errors
} trackCount = 40;
var density = DensityTable[i]; errorOffset = 196608;
break;
default:
throw new Exception("Not able to identify capacity of the D64 file.");
}
// we pad the tracks with extra gap bytes to meet MNIB standards for (var i = 0; i < trackCount; i++)
while (trackMem.Length < StandardTrackLengthBytes[density]) {
{ if (errorOffset >= 0)
trackMem.WriteByte(0x55); {
} errorType = (ErrorType) source[errorOffset];
errorOffset++;
}
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), formatA, formatB, StandardSectorGapLength[DensityTable[i]], errorType, out bitsWritten);
trackMem.Write(diskData, 0, diskData.Length);
trackLengthBits += bitsWritten;
}
var density = DensityTable[i];
trackDatas.Add(trackMem.ToArray()); // we pad the tracks with extra gap bytes to meet MNIB standards
trackLengths.Add(trackLengthBits); while (trackMem.Length < StandardTrackLengthBytes[density])
trackNumbers.Add(i * 2); {
trackDensities.Add(DensityTable[i]); trackMem.WriteByte(0x55);
} }
}
return new Disk(trackDatas, trackNumbers, trackDensities, 84); trackDatas.Add(trackMem.ToArray());
trackLengths.Add(trackLengthBits);
trackNumbers.Add(i * 2);
trackDensities.Add(DensityTable[i]);
}
}
return new Disk(trackDatas, trackNumbers, trackDensities, 84) {WriteProtected = false};
} }
} }
} }
} }

View File

@ -57,11 +57,86 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
throw new Exception("Byte-level speeds are not yet supported in the G64 loader."); throw new Exception("Byte-level speeds are not yet supported in the G64 loader.");
} }
return new Disk(trackDatas, trackNumbers, trackDensities, 84); return new Disk(trackDatas, trackNumbers, trackDensities, 84) {WriteProtected = true};
} }
return new Disk(84); return new Disk(84) {WriteProtected = false};
} }
} }
public static byte[] Write(IList<byte[]> trackData, IList<int> trackNumbers, IList<int> trackDensities)
{
const byte version = 0;
const byte trackCount = 84;
const int headerLength = 0xC;
const byte dataFillerValue = 0xFF;
var trackMaxLength = (ushort)Math.Max(7928, trackData.Max(d => d.Length));
using (var mem = new MemoryStream())
{
var writer = new BinaryWriter(mem);
// header ID
writer.Write("GCR-1541".ToCharArray());
// version #
writer.Write(version);
// tracks in the image
writer.Write(trackCount);
// maximum track size in bytes
writer.Write(trackMaxLength);
// combine track data
var offsets = new List<int>();
var densities = new List<int>();
using (var trackMem = new MemoryStream())
{
var trackMemWriter = new BinaryWriter(trackMem);
for (var i = 0; i < trackCount; i++)
{
if (trackNumbers.Contains(i))
{
var trackIndex = trackNumbers.IndexOf(i);
offsets.Add((int)trackMem.Length);
densities.Add(trackDensities[trackIndex]);
var data = trackData[trackIndex];
var buffer = Enumerable.Repeat(dataFillerValue, trackMaxLength).ToArray();
var dataBytes = data.Select(d => unchecked((byte)d)).ToArray();
Array.Copy(dataBytes, buffer, dataBytes.Length);
trackMemWriter.Write((ushort)dataBytes.Length);
trackMemWriter.Write(buffer);
}
else
{
offsets.Add(-1);
densities.Add(0);
}
}
trackMemWriter.Flush();
// offset table
foreach (var offset in offsets.Select(o => o >= 0 ? o + headerLength + trackCount * 8 : 0))
{
writer.Write(offset);
}
// speed zone data
foreach (var density in densities)
{
writer.Write(density);
}
// track data
writer.Write(trackMem.ToArray());
}
writer.Flush();
return mem.ToArray();
}
}
} }
} }

View File

@ -97,6 +97,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
result = new Tape(version, tapeFile, 20, tapeFile.Length); result = new Tape(version, tapeFile, 20, tapeFile.Length);
} }
else if (Encoding.ASCII.GetString(tapeFile, 0, 0x12) == "C64 tape image file")
{
throw new Exception("The T64 format is not yet supported.");
}
return result; return result;
} }

View File

@ -19,6 +19,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
private int _rngCurrent; private int _rngCurrent;
private int _clocks; private int _clocks;
private int _cpuClocks; private int _cpuClocks;
private int _diskWriteBitsRemaining;
private bool _diskWriteEnabled;
private int _diskWriteLatch;
private int _diskOutputBits;
private bool _diskWriteProtected;
// Lehmer RNG // Lehmer RNG
private void AdvanceRng() private void AdvanceRng()
@ -28,130 +33,170 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
_rngCurrent = 1; _rngCurrent = 1;
} }
_rngCurrent = (int)(_rngCurrent * LEHMER_RNG_PRIME % int.MaxValue); _rngCurrent = unchecked((int) ((_rngCurrent * LEHMER_RNG_PRIME) & int.MaxValue));
} }
private void ExecuteFlux() private void ExecuteFlux()
{ {
// This actually executes the main 16mhz clock // This actually executes the main 16mhz clock
while (_clocks > 0) while (_clocks > 0)
{ {
_clocks--; _clocks--;
// rotate disk // rotate disk
if (_motorEnabled) if (_motorEnabled)
{ {
if (_disk == null) if (_disk == null)
{ {
_diskBitsLeft = 1; _diskBitsLeft = 1;
_diskBits = 0; _diskBits = 0;
} }
else else
{ {
if (_diskBitsLeft <= 0) if (_diskBitsLeft <= 0)
{ {
_diskByteOffset++; if (_diskWriteEnabled)
if (_diskByteOffset == Disk.FluxEntriesPerTrack) _trackImageData[_diskByteOffset] = _diskOutputBits;
{
_diskByteOffset = 0;
}
_diskBits = _trackImageData[_diskByteOffset]; _diskByteOffset++;
_diskBitsLeft = Disk.FluxBitsPerEntry;
}
}
if ((_diskBits & 1) != 0) if (_diskByteOffset == Disk.FluxEntriesPerTrack)
{ _diskByteOffset = 0;
_countsBeforeRandomTransition = 0;
_diskFluxReversalDetected = true;
}
_diskBits >>= 1; if (!_diskWriteEnabled)
_diskBitsLeft--; _diskBits = _trackImageData[_diskByteOffset];
}
// random flux transition readings for unformatted data _diskOutputBits = 0;
if (_countsBeforeRandomTransition > 0) _diskBitsLeft = Disk.FluxBitsPerEntry;
{ }
_countsBeforeRandomTransition--; }
if (_countsBeforeRandomTransition == 0) _diskOutputBits >>= 1;
{
_diskFluxReversalDetected = true;
AdvanceRng();
// This constant is what VICE uses. TODO: Determine accuracy. if (_diskWriteEnabled && !_diskWriteProtected)
_countsBeforeRandomTransition = (_rngCurrent % 367) + 33; _countsBeforeRandomTransition = 0;
}
}
// flux transition circuitry if ((_diskBits & 1) != 0)
if (_diskFluxReversalDetected) {
{ _countsBeforeRandomTransition = 0;
_diskDensityCounter = _diskDensity; _diskFluxReversalDetected = true;
_diskSupplementaryCounter = 0; _diskOutputBits |= int.MinValue; // set bit 31
_diskFluxReversalDetected = false; }
if (_countsBeforeRandomTransition == 0) else
{ {
AdvanceRng(); _diskOutputBits &= int.MaxValue; // clear bit 31
}
// This constant is what VICE uses. TODO: Determine accuracy. _diskBits >>= 1;
_countsBeforeRandomTransition = (_rngCurrent & 0x1F) + 289; _diskBitsLeft--;
} }
}
// counter circuitry // random flux transition readings for unformatted data
if (_diskDensityCounter >= 16) if (_countsBeforeRandomTransition > 0)
{ {
_diskDensityCounter = _diskDensity; _countsBeforeRandomTransition--;
_diskSupplementaryCounter++; if (_countsBeforeRandomTransition == 0)
if ((_diskSupplementaryCounter & 0x3) == 0x2) {
{ _diskFluxReversalDetected = true;
_bitsRemainingInLatchedByte--; AdvanceRng();
_byteReady = false; // This constant is what VICE uses. TODO: Determine accuracy.
_bitHistory = (_bitHistory << 1) | ((_diskSupplementaryCounter & 0xC) == 0x0 ? 1 : 0); _countsBeforeRandomTransition = (_rngCurrent % 367) + 33;
_sync = false; }
if (Via1.Cb2 && (_bitHistory & 0x3FF) == 0x3FF) }
{
_sync = true;
_bitsRemainingInLatchedByte = 8;
_byteReady = false;
}
if (_bitsRemainingInLatchedByte <= 0) // flux transition circuitry
{ if (_diskFluxReversalDetected)
_bitsRemainingInLatchedByte = 8; {
if (!_diskWriteEnabled)
{
_diskDensityCounter = _diskDensity;
_diskSupplementaryCounter = 0;
}
_diskFluxReversalDetected = false;
if (_countsBeforeRandomTransition == 0)
{
AdvanceRng();
// This constant is what VICE uses. TODO: Determine accuracy.
_countsBeforeRandomTransition = (_rngCurrent & 0x1F) + 289;
}
}
// SOE (sync output enabled) // counter circuitry
_byteReady = Via1.Ca2; if (_diskDensityCounter >= 16)
} {
_diskDensityCounter = _diskDensity;
_diskSupplementaryCounter++;
// negative transition activates SO pin on CPU if ((_diskSupplementaryCounter & 0x3) == 0x2)
_previousCa1 = Via1.Ca1; {
Via1.Ca1 = !_byteReady; if (!_diskWriteEnabled)
if (_previousCa1 && !Via1.Ca1) _diskWriteBitsRemaining = 0;
{ _diskWriteEnabled = !Via1.Cb2;
// cycle 6 is roughly 400ns
_overflowFlagDelaySr |= _diskCycle > 6 ? 4 : 2;
}
}
}
if (_diskSupplementaryCounter >= 16) _diskWriteBitsRemaining--;
{ if (_diskWriteEnabled)
_diskSupplementaryCounter = 0; {
} _countsBeforeRandomTransition = 0;
_byteReady = false;
if (_diskWriteBitsRemaining <= 0)
{
_diskWriteLatch = Via1.EffectivePrA;
_diskWriteBitsRemaining = 8;
_byteReady = Via1.Ca2;
}
if ((_diskWriteLatch & 0x80) != 0)
{
_diskOutputBits |= int.MinValue; // set bit 31
}
_diskWriteLatch <<= 1;
}
else
{
_bitsRemainingInLatchedByte--;
_byteReady = false;
_bitHistory = (_bitHistory << 1) | ((_diskSupplementaryCounter & 0xC) == 0x0 ? 1 : 0);
_sync = false;
if (!_diskWriteEnabled && (_bitHistory & 0x3FF) == 0x3FF)
{
_sync = true;
_bitsRemainingInLatchedByte = 8;
_byteReady = false;
}
_cpuClocks--; if (_bitsRemainingInLatchedByte <= 0)
if (_cpuClocks <= 0) {
{ _bitsRemainingInLatchedByte = 8;
ExecuteSystem();
_cpuClocks = 16;
}
_diskDensityCounter++; // SOE (SO/Byte Ready enabled)
_diskCycle = (_diskCycle + 1) & 0xF; _byteReady = Via1.Ca2;
} }
} }
}
// negative transition activates SO pin on CPU
_previousCa1 = Via1.Ca1;
Via1.Ca1 = !_byteReady;
if (_previousCa1 && !Via1.Ca1)
{
// cycle 6 is roughly 400ns
_overflowFlagDelaySr |= _diskCycle > 6 ? 4 : 2;
}
}
if (_diskSupplementaryCounter >= 16)
{
_diskSupplementaryCounter = 0;
}
_cpuClocks--;
if (_cpuClocks <= 0)
{
ExecuteSystem();
_cpuClocks = 16;
}
_diskDensityCounter++;
_diskCycle = (_diskCycle + 1) & 0xF;
}
}
} }
} }

View File

@ -46,7 +46,7 @@
private int ReadVia1PrB() private int ReadVia1PrB()
{ {
return (_motorStep & 0x03) | (_motorEnabled ? 0x04 : 0x00) | (_sync ? 0x00 : 0x80); return (_motorStep & 0x03) | (_motorEnabled ? 0x04 : 0x00) | (_sync ? 0x00 : 0x80) | (_diskWriteProtected ? 0x00 : 0x10);
} }
public int Peek(int addr) public int Peek(int addr)
@ -163,8 +163,11 @@
public override bool ReadDeviceData() public override bool ReadDeviceData()
{ {
// PB1 (input not pulled up)
var viaOutputData = (Via0.DdrB & 0x02) != 0 && (Via0.PrB & 0x02) != 0; var viaOutputData = (Via0.DdrB & 0x02) != 0 && (Via0.PrB & 0x02) != 0;
// inverted from c64, input, not pulled up to PB7/CA1
var viaInputAtn = ViaReadAtn(); var viaInputAtn = ViaReadAtn();
// PB4 (input not pulled up)
var viaOutputAtna = (Via0.DdrB & 0x10) != 0 && (Via0.PrB & 0x10) != 0; var viaOutputAtna = (Via0.DdrB & 0x10) != 0 && (Via0.PrB & 0x10) != 0;
return !(viaOutputAtna ^ viaInputAtn) && !viaOutputData; return !(viaOutputAtna ^ viaInputAtn) && !viaOutputData;

View File

@ -117,6 +117,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
ser.Sync("Clocks", ref _clocks); ser.Sync("Clocks", ref _clocks);
ser.Sync("CpuClocks", ref _cpuClocks); ser.Sync("CpuClocks", ref _cpuClocks);
ser.Sync("OverflowFlagDelayShiftRegister", ref _overflowFlagDelaySr); ser.Sync("OverflowFlagDelayShiftRegister", ref _overflowFlagDelaySr);
ser.Sync("DiskWriteBitsRemaining", ref _diskWriteBitsRemaining);
ser.Sync("DiskWriteEnabled", ref _diskWriteEnabled);
ser.Sync("DiskWriteLatch", ref _diskWriteLatch);
ser.Sync("DiskOutputBits", ref _diskOutputBits);
ser.Sync("DiskWriteProtected", ref _diskWriteProtected);
} }
public override void ExecutePhase() public override void ExecutePhase()
@ -209,6 +214,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{ {
_trackImageData = _disk.GetDataForTrack(_trackNumber); _trackImageData = _disk.GetDataForTrack(_trackNumber);
_diskBits = _trackImageData[_diskByteOffset] >> (Disk.FluxBitsPerEntry - _diskBitsLeft); _diskBits = _trackImageData[_diskByteOffset] >> (Disk.FluxBitsPerEntry - _diskBitsLeft);
_diskWriteProtected = _disk.WriteProtected;
}
else
{
_diskWriteProtected = true;
} }
} }