Commodore 64: Add new CIA emulation (disconnected for this commit but it is a drop-in replacement). Fixed RAM writes underneath CPU IO port space ($00/$01)
This commit is contained in:
parent
977f7ff0dd
commit
1b367c1873
|
@ -113,6 +113,9 @@
|
|||
<Compile Include="Computers\Commodore64\Media\D64.cs" />
|
||||
<Compile Include="Computers\Commodore64\Media\Disk.cs" />
|
||||
<Compile Include="Computers\Commodore64\Media\G64.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6526-2.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6526-2.Interface.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6526-2.PortIO.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6526.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6567.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6581.cs" />
|
||||
|
@ -555,6 +558,7 @@
|
|||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6526-2.Registers.cs" />
|
||||
<None Include="Consoles\Atari\docs\stella.pdf" />
|
||||
<None Include="Consoles\Coleco\docs\colecovision tech1.pdf" />
|
||||
<None Include="Consoles\Coleco\docs\colecovision tech2.pdf" />
|
||||
|
|
|
@ -136,6 +136,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
|
|||
cpu.ReadRDY = vic.ReadBABuffer;
|
||||
cpu.ReadMemory = pla.Read;
|
||||
cpu.WriteMemory = pla.Write;
|
||||
cpu.WriteMemoryPort = Cpu_WriteMemoryPort;
|
||||
|
||||
pla.PeekBasicRom = basicRom.Peek;
|
||||
pla.PeekCartridgeHi = cartPort.PeekHiRom;
|
||||
|
|
|
@ -66,6 +66,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
|
|||
return data;
|
||||
}
|
||||
|
||||
void Cpu_WriteMemoryPort(int addr, byte val)
|
||||
{
|
||||
pla.WriteMemory(addr, bus);
|
||||
}
|
||||
|
||||
bool Glue_ReadIRQ()
|
||||
{
|
||||
return cia0.ReadIRQBuffer() & vic.ReadIRQBuffer() & cartPort.ReadIRQBuffer();
|
||||
|
|
|
@ -108,6 +108,31 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
|
|||
//disk.Execute();
|
||||
board.Execute();
|
||||
|
||||
#if false
|
||||
if (board.cpu.PC == 0xE16F && (board.cpu.ReadPort() & 0x7) == 7)
|
||||
{
|
||||
// HUGE kernal hack to load files
|
||||
// the only purpose for this is to be able to run the Lorenz
|
||||
// test suite!
|
||||
|
||||
int fileNameLength = board.ram.Peek(0xB7);
|
||||
int fileNameOffset = board.ram.Peek(0xBB) | ((int)board.ram.Peek(0xBC) << 8);
|
||||
byte[] fileNameRaw = new byte[fileNameLength];
|
||||
for (int i = 0; i < fileNameLength; i++)
|
||||
{
|
||||
fileNameRaw[i] = board.ram.Peek(fileNameOffset + i);
|
||||
}
|
||||
var enc = System.Text.Encoding.ASCII;
|
||||
string fileName = enc.GetString(fileNameRaw);
|
||||
string filePath = Path.Combine(@"E:\Programming\Visual Studio 2013\Vice\testprogs\general\Lorenz-2.15\src\", fileName + ".prg");
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
PRG.Load(board.pla, File.ReadAllBytes(filePath));
|
||||
}
|
||||
board.cpu.PC = 0xE1B5;
|
||||
}
|
||||
#endif
|
||||
|
||||
// load PRG file if needed
|
||||
if (loadPrg)
|
||||
{
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
public Func<int, byte> ReadMemory;
|
||||
public Func<byte> ReadPort;
|
||||
public Action<int, byte> WriteMemory;
|
||||
public Action<int, byte> WriteMemoryPort;
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
|
@ -196,10 +197,19 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
if (addr == 0x0000)
|
||||
{
|
||||
port.Direction = val;
|
||||
WriteMemoryPort(addr, val);
|
||||
}
|
||||
else if (addr == 0x0001)
|
||||
{
|
||||
port.Latch = val;
|
||||
WriteMemory(addr, val);
|
||||
WriteMemoryPort(addr, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteMemory(addr, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
||||
{
|
||||
sealed public partial class MOS6526_2
|
||||
{
|
||||
public void ExecutePhase1()
|
||||
{
|
||||
proc_a();
|
||||
proc_b();
|
||||
if (--tod_cycles <= 0)
|
||||
{
|
||||
//tod_cycles += tod_period;
|
||||
tod();
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecutePhase2()
|
||||
{
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
public byte PortAData
|
||||
{
|
||||
get
|
||||
{
|
||||
return portA.ReadOutput();
|
||||
}
|
||||
}
|
||||
|
||||
public byte PortAMask
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public byte PortADirection
|
||||
{
|
||||
get
|
||||
{
|
||||
return portA.Direction;
|
||||
}
|
||||
}
|
||||
|
||||
public byte PortALatch
|
||||
{
|
||||
get
|
||||
{
|
||||
return portA.Latch;
|
||||
}
|
||||
}
|
||||
|
||||
public byte PortBData
|
||||
{
|
||||
get
|
||||
{
|
||||
return portB.ReadOutput();
|
||||
}
|
||||
}
|
||||
|
||||
public byte PortBDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
return portB.Direction;
|
||||
}
|
||||
}
|
||||
|
||||
public byte PortBLatch
|
||||
{
|
||||
get
|
||||
{
|
||||
return portB.Latch;
|
||||
}
|
||||
}
|
||||
|
||||
public byte PortBMask
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
||||
{
|
||||
sealed public partial class MOS6526_2
|
||||
{
|
||||
public Func<bool> ReadCNT;
|
||||
public Func<bool> ReadFlag;
|
||||
public bool ReadIRQBuffer() {
|
||||
return (idr & 0x80) == 0;
|
||||
}
|
||||
public Func<byte> ReadPortA = (() => { return 0xFF; });
|
||||
public Func<byte> ReadPortB = (() => { return 0xFF; });
|
||||
public Func<bool> ReadSP;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
||||
{
|
||||
sealed public partial class MOS6526_2
|
||||
{
|
||||
int read_data;
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
addr &= 0xF;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x0:
|
||||
return (byte)(portA.ReadInput(ReadPortA()) & PortAMask);
|
||||
case 0x1:
|
||||
return (byte)(portB.ReadInput(ReadPortB()) & PortBMask);
|
||||
case 0x2:
|
||||
return (byte)portA.Direction;
|
||||
case 0x3:
|
||||
return (byte)portB.Direction;
|
||||
case 0x4:
|
||||
return (byte)(a.getTimer() & 0xFF);
|
||||
case 0x5:
|
||||
return (byte)((a.getTimer() >> 8) & 0xFF);
|
||||
case 0x6:
|
||||
return (byte)(b.getTimer() & 0xFF);
|
||||
case 0x7:
|
||||
return (byte)((b.getTimer() >> 8) & 0xFF);
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
case 0xA:
|
||||
case 0xB:
|
||||
return (byte)(tod_clock[addr - 0x08] & 0xFF);
|
||||
case 0xC:
|
||||
return (byte)(sdr & 0xFF);
|
||||
case 0xD:
|
||||
return (byte)(idr & 0xFF);
|
||||
case 0xE:
|
||||
return (byte)((cra & 0xEE) | (a.state & 1));
|
||||
case 0xF:
|
||||
return (byte)((crb & 0xEE) | (b.state & 1));
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
public byte Read(int addr)
|
||||
{
|
||||
return Read(addr, 0xFF);
|
||||
}
|
||||
|
||||
public byte Read(int addr, byte mask)
|
||||
{
|
||||
addr &= 0xF;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x0:
|
||||
return (byte)(portA.ReadInput(ReadPortA()) & PortAMask);
|
||||
case 0x1:
|
||||
read_data = portB.ReadInput(ReadPortB()) & PortBMask;
|
||||
if ((cra & 0x02) != 0)
|
||||
{
|
||||
read_data &= 0xBF;
|
||||
if (((cra & 0x04) != 0) ? (a.getPbToggle()) : ((a.state & CiaTimer.TIMER_OUT) != 0))
|
||||
{
|
||||
read_data |= 0x40;
|
||||
}
|
||||
}
|
||||
if ((crb & 0x02) != 0)
|
||||
{
|
||||
read_data &= 0x7F;
|
||||
if (((crb & 0x04) != 0) ? (b.getPbToggle()) : ((b.state & CiaTimer.TIMER_OUT) != 0))
|
||||
{
|
||||
read_data |= 0x80;
|
||||
}
|
||||
}
|
||||
return (byte)(read_data & 0xFF);
|
||||
case 0x2:
|
||||
return (byte)portA.Direction;
|
||||
case 0x3:
|
||||
return (byte)portB.Direction;
|
||||
case 0x4:
|
||||
return (byte)(a.getTimer() & 0xFF);
|
||||
case 0x5:
|
||||
return (byte)((a.getTimer() >> 8) & 0xFF);
|
||||
case 0x6:
|
||||
return (byte)(b.getTimer() & 0xFF);
|
||||
case 0x7:
|
||||
return (byte)((b.getTimer() >> 8) & 0xFF);
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
case 0xA:
|
||||
case 0xB:
|
||||
if (!tod_latched)
|
||||
{
|
||||
tod_latch[0] = tod_clock[0];
|
||||
tod_latch[1] = tod_clock[1];
|
||||
tod_latch[2] = tod_clock[2];
|
||||
tod_latch[3] = tod_clock[3];
|
||||
}
|
||||
if (addr == 0x8)
|
||||
{
|
||||
tod_latched = false;
|
||||
}
|
||||
else if (addr == 0xB)
|
||||
{
|
||||
tod_latched = true;
|
||||
}
|
||||
return (byte)(tod_latch[addr - 0x08] & 0xFF);
|
||||
case 0xC:
|
||||
return (byte)(sdr & 0xFF);
|
||||
case 0xD:
|
||||
int_clear();
|
||||
return (byte)(icr & 0xFF);
|
||||
case 0xE:
|
||||
return (byte)((cra & 0xEE) | (a.state & 1));
|
||||
case 0xF:
|
||||
return (byte)((crb & 0xEE) | (b.state & 1));
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public bool ReadCNTBuffer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ReadPCBuffer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ReadSPBuffer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Write(int addr, byte val)
|
||||
{
|
||||
Write(addr, val, 0xFF);
|
||||
}
|
||||
|
||||
public void Write(int addr, byte val, byte mask)
|
||||
{
|
||||
addr &= 0xF;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x0:
|
||||
portA.Latch = val;
|
||||
pra = val;
|
||||
break;
|
||||
case 0x1:
|
||||
portB.Latch = val;
|
||||
prb = val;
|
||||
break;
|
||||
case 0x2:
|
||||
portA.Direction = val;
|
||||
ddra = val;
|
||||
break;
|
||||
case 0x3:
|
||||
portB.Direction = val;
|
||||
ddrb = val;
|
||||
break;
|
||||
case 0x4:
|
||||
a.setLatchLow(val);
|
||||
ta = ((int)val | (ta & 0xFF00));
|
||||
break;
|
||||
case 0x5:
|
||||
a.setLatchHigh(val);
|
||||
ta = (((int)val << 8) | (ta & 0xFF));
|
||||
break;
|
||||
case 0x6:
|
||||
b.setLatchLow(val);
|
||||
tb = ((int)val | (tb & 0xFF00));
|
||||
break;
|
||||
case 0x7:
|
||||
b.setLatchHigh(val);
|
||||
tb = (((int)val << 8) | (tb & 0xFF));
|
||||
break;
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
case 0xA:
|
||||
case 0xB:
|
||||
if (addr == 0xB)
|
||||
{
|
||||
val &= 0x9F;
|
||||
if ((val & 0x1F) == 0x12 && (crb & 0x80) == 0)
|
||||
{
|
||||
val ^= 0x80;
|
||||
}
|
||||
}
|
||||
if ((crb & 0x80) != 0)
|
||||
{
|
||||
tod_alarm[addr - 0x8] = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (addr == 0x8)
|
||||
{
|
||||
tod_stopped = false;
|
||||
}
|
||||
else if (addr == 0xB)
|
||||
{
|
||||
tod_stopped = true;
|
||||
}
|
||||
}
|
||||
tod_clock[addr - 0x8] = val;
|
||||
break;
|
||||
case 0xC:
|
||||
if ((cra & 0x40) != 0)
|
||||
{
|
||||
sdr_buffered = true;
|
||||
}
|
||||
break;
|
||||
case 0xD:
|
||||
if ((val & 0x80) != 0)
|
||||
{
|
||||
int_setEnabled(val);
|
||||
}
|
||||
else
|
||||
{
|
||||
int_clearEnabled(val);
|
||||
}
|
||||
break;
|
||||
case 0xE:
|
||||
if ((val & 0x1) != 0 && (cra & 0x1) == 0)
|
||||
{
|
||||
a.setPbToggle(true);
|
||||
}
|
||||
a.setControlRegister(val);
|
||||
break;
|
||||
case 0xF:
|
||||
if ((val & 0x1) != 0 && (crb & 0x1) == 0)
|
||||
{
|
||||
b.setPbToggle(true);
|
||||
}
|
||||
b.setControlRegister((val | (val & 0x40) >> 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,444 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BizHawk.Common;
|
||||
|
||||
// thanks to these fine folks for their research on this buggy as hell chip:
|
||||
// Simon White (s_a_white@email.com)
|
||||
// Antti S. Lankila "alankila"
|
||||
// Andreas Boose (viceteam@t-online.de)
|
||||
// Alexander Bluhm (mam96ehy@studserv.uni-leipzig.de)
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
||||
{
|
||||
sealed public partial class MOS6526_2
|
||||
{
|
||||
sealed private class CiaTimer
|
||||
{
|
||||
public const int TIMER_CR_START = 0x01;
|
||||
public const int TIMER_STEP = 0x04;
|
||||
public const int TIMER_CR_ONESHOT = 0x08;
|
||||
public const int TIMER_CR_FLOAD = 0x10;
|
||||
public const int TIMER_PHI2IN = 0x20;
|
||||
public const int TIMER_MASK = TIMER_CR_START | TIMER_CR_ONESHOT | TIMER_CR_FLOAD | TIMER_PHI2IN;
|
||||
public const int TIMER_COUNT2 = 0x100;
|
||||
public const int TIMER_COUNT3 = 0x200;
|
||||
public const int TIMER_ONESHOT0 = 0x800;
|
||||
public const int TIMER_ONESHOT = 0x80000;
|
||||
public const int TIMER_LOAD1 = 0x1000;
|
||||
public const int TIMER_LOAD = 0x100000;
|
||||
public const int TIMER_OUT = 0x40000000 << 1;
|
||||
|
||||
const int WANTED = TIMER_CR_START | TIMER_PHI2IN | TIMER_COUNT2 | TIMER_COUNT3;
|
||||
const int UNWANTED = TIMER_OUT | TIMER_CR_FLOAD | TIMER_LOAD1 | TIMER_LOAD;
|
||||
const int UNWANTED1 = TIMER_CR_START | TIMER_PHI2IN;
|
||||
const int UNWANTED2 = TIMER_CR_START | TIMER_STEP;
|
||||
|
||||
int adj;
|
||||
bool toggle;
|
||||
public int state;
|
||||
int lastControlValue;
|
||||
int timer;
|
||||
int latch;
|
||||
bool pbToggle;
|
||||
int ciaEventPauseTime;
|
||||
bool phi1tod;
|
||||
bool phi1run;
|
||||
int cycleSkippingEvent = -1;
|
||||
int nextTick = 0;
|
||||
|
||||
Action serialPort;
|
||||
Action underFlow;
|
||||
|
||||
public CiaTimer(Action serialPortCallback, Action underFlowCallback)
|
||||
{
|
||||
this.serialPort = serialPortCallback;
|
||||
this.underFlow = underFlowCallback;
|
||||
}
|
||||
|
||||
public void clock()
|
||||
{
|
||||
if (timer != 0 && (state & TIMER_COUNT3) != 0)
|
||||
{
|
||||
timer--;
|
||||
}
|
||||
|
||||
adj = state & (TIMER_CR_START | TIMER_CR_ONESHOT | TIMER_PHI2IN);
|
||||
if ((state & (TIMER_CR_START | TIMER_PHI2IN)) == (TIMER_CR_START | TIMER_PHI2IN))
|
||||
{
|
||||
adj |= TIMER_COUNT2;
|
||||
}
|
||||
if ((state & TIMER_COUNT2) != 0 || (state & (TIMER_STEP | TIMER_CR_START)) == (TIMER_STEP | TIMER_CR_START))
|
||||
{
|
||||
adj |= TIMER_COUNT3;
|
||||
}
|
||||
adj |= (state & (TIMER_CR_FLOAD | TIMER_CR_ONESHOT | TIMER_LOAD1 | TIMER_ONESHOT0)) << 8;
|
||||
state = adj;
|
||||
|
||||
if (timer == 0 && (state & TIMER_COUNT3) != 0)
|
||||
{
|
||||
state |= TIMER_LOAD | TIMER_OUT;
|
||||
|
||||
if ((state & (TIMER_ONESHOT | TIMER_ONESHOT0)) != 0)
|
||||
{
|
||||
state &= ~(TIMER_CR_START | TIMER_COUNT2);
|
||||
}
|
||||
|
||||
toggle = (lastControlValue & 0x06) == 0x06;
|
||||
pbToggle = toggle && !pbToggle;
|
||||
serialPort();
|
||||
underFlow();
|
||||
}
|
||||
|
||||
if ((state & TIMER_LOAD) != 0)
|
||||
{
|
||||
timer = latch;
|
||||
state &= ~TIMER_COUNT3;
|
||||
}
|
||||
}
|
||||
|
||||
public bool getPbToggle()
|
||||
{
|
||||
return pbToggle;
|
||||
}
|
||||
|
||||
public int getTimer()
|
||||
{
|
||||
return timer;
|
||||
}
|
||||
|
||||
public void reschedule()
|
||||
{
|
||||
if ((state & UNWANTED) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((state & TIMER_COUNT3) != 0)
|
||||
{
|
||||
if ((timer & 0xFFFF) > 2 && (state & UNWANTED) == WANTED)
|
||||
{
|
||||
ciaEventPauseTime = 1;
|
||||
cycleSkippingEvent = (timer - 1) & 0xFFFF;
|
||||
return;
|
||||
}
|
||||
nextTick = 1;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((state & UNWANTED1) == UNWANTED1 || (state & UNWANTED2) == UNWANTED2)
|
||||
{
|
||||
nextTick = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
ciaEventPauseTime = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
timer = 0xFFFF;
|
||||
latch = 0xFFFF;
|
||||
pbToggle = false;
|
||||
state = 0;
|
||||
ciaEventPauseTime = 0;
|
||||
cycleSkippingEvent = -1;
|
||||
}
|
||||
|
||||
public void setControlRegister(int cr)
|
||||
{
|
||||
state &= ~TIMER_MASK;
|
||||
state |= cr & TIMER_MASK ^ TIMER_PHI2IN;
|
||||
lastControlValue = cr;
|
||||
}
|
||||
|
||||
public void setLatchLow(int low)
|
||||
{
|
||||
latch = ((latch & 0xFF00) | (low & 0xFF));
|
||||
if ((state & TIMER_LOAD) != 0)
|
||||
{
|
||||
timer = ((timer & 0xFF00) | (low & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
public void setLatchHigh(int high)
|
||||
{
|
||||
latch = ((latch & 0xFF) | ((high & 0xFF) << 8));
|
||||
if ((state & TIMER_LOAD) != 0 || (state & TIMER_CR_START) == 0)
|
||||
{
|
||||
timer = latch;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPbToggle(bool st)
|
||||
{
|
||||
pbToggle = st;
|
||||
}
|
||||
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
SaveState.SyncObject(ser, this);
|
||||
}
|
||||
}
|
||||
|
||||
const int INT_NONE = 0x00;
|
||||
const int INT_UNDERFLOW_A = 0x01;
|
||||
const int INT_UNDERFLOW_B = 0x02;
|
||||
const int INT_ALARM = 0x04;
|
||||
const int INT_SP = 0x08;
|
||||
const int INT_FLAG = 0x10;
|
||||
|
||||
int pra;
|
||||
int prb;
|
||||
int ddra;
|
||||
int ddrb;
|
||||
int ta;
|
||||
int tb;
|
||||
int tod_ten;
|
||||
int tod_sec;
|
||||
int tod_min;
|
||||
int tod_hr;
|
||||
int sdr;
|
||||
int icr;
|
||||
int idr;
|
||||
int cra;
|
||||
int crb;
|
||||
|
||||
int sdr_out;
|
||||
bool sdr_buffered;
|
||||
int sdr_count;
|
||||
bool tod_latched;
|
||||
bool tod_stopped;
|
||||
int[] tod_clock = new int[4];
|
||||
int[] tod_alarm = new int[4];
|
||||
int[] tod_latch = new int[4];
|
||||
int tod_cycles = -1;
|
||||
int tod_period = -1;
|
||||
bool postTimerBEvent;
|
||||
int idr_old;
|
||||
bool int_delayed;
|
||||
|
||||
int bcd_internal;
|
||||
|
||||
CiaTimer a;
|
||||
CiaTimer b;
|
||||
LatchedPort portA;
|
||||
LatchedPort portB;
|
||||
|
||||
public MOS6526_2(Region region)
|
||||
{
|
||||
a = new CiaTimer(serialPortA, underFlowA);
|
||||
b = new CiaTimer(serialPortB, underFlowB);
|
||||
portA = new LatchedPort();
|
||||
portB = new LatchedPort();
|
||||
switch (region)
|
||||
{
|
||||
case Region.NTSC:
|
||||
tod_period = 14318181 / 140;
|
||||
break;
|
||||
case Region.PAL:
|
||||
tod_period = 17734472 / 180;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int bcdToByte(int input)
|
||||
{
|
||||
return 10 * ((input & 0xF0) >> 4) + (input & 0x0F);
|
||||
}
|
||||
|
||||
int byteToBcd(int input)
|
||||
{
|
||||
return (((input / 10) << 4) + (input % 10)) & 0xFF;
|
||||
}
|
||||
|
||||
int int_clear()
|
||||
{
|
||||
int_delayed = false;
|
||||
idr_old = idr;
|
||||
idr = 0;
|
||||
return idr_old;
|
||||
}
|
||||
|
||||
void int_clearEnabled(int i)
|
||||
{
|
||||
icr &= ~i;
|
||||
}
|
||||
|
||||
void int_reset()
|
||||
{
|
||||
int_delayed = false;
|
||||
}
|
||||
|
||||
void int_set(int i)
|
||||
{
|
||||
idr |= i;
|
||||
if ((icr & idr) != 0 && (idr & 0x80) == 0)
|
||||
{
|
||||
int_delayed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void int_setEnabled(int i)
|
||||
{
|
||||
icr |= i & ~0x80;
|
||||
}
|
||||
|
||||
void proc_a()
|
||||
{
|
||||
if (int_delayed)
|
||||
{
|
||||
int_delayed = false;
|
||||
idr |= 0x80;
|
||||
}
|
||||
if (postTimerBEvent)
|
||||
{
|
||||
postTimerBEvent = false;
|
||||
b.state |= CiaTimer.TIMER_STEP;
|
||||
}
|
||||
a.clock();
|
||||
}
|
||||
|
||||
void proc_b()
|
||||
{
|
||||
b.clock();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
a.reset();
|
||||
b.reset();
|
||||
sdr_out = 0;
|
||||
sdr_count = 0;
|
||||
sdr_buffered = false;
|
||||
icr = 0;
|
||||
idr = 0;
|
||||
idr_old = 0;
|
||||
int_reset();
|
||||
portA.Latch = 0xFF;
|
||||
portB.Latch = 0xFF;
|
||||
portA.Direction = 0xFF;
|
||||
portB.Direction = 0xFF;
|
||||
PortAMask = 0xFF;
|
||||
PortBMask = 0xFF;
|
||||
}
|
||||
|
||||
void serialPortA()
|
||||
{
|
||||
if ((cra & 0x40) != 0)
|
||||
{
|
||||
if (sdr_count != 0)
|
||||
{
|
||||
if (--sdr_count == 0)
|
||||
{
|
||||
triggerInterruptSP();
|
||||
}
|
||||
}
|
||||
if (sdr_count == 0 && sdr_buffered)
|
||||
{
|
||||
sdr_out = sdr;
|
||||
sdr_buffered = false;
|
||||
sdr_count = 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void serialPortB()
|
||||
{
|
||||
// nop
|
||||
}
|
||||
|
||||
void tod()
|
||||
{
|
||||
tod_cycles += tod_period;
|
||||
if (!tod_stopped)
|
||||
{
|
||||
int todpos = 0;
|
||||
int t = bcdToByte(tod_clock[todpos]) + 1;
|
||||
tod_clock[todpos++] = byteToBcd(t % 10);
|
||||
if (t >= 10)
|
||||
{
|
||||
t = bcdToByte(tod_clock[todpos]) + 1;
|
||||
tod_clock[todpos++] = byteToBcd(t % 60);
|
||||
if (t >= 60)
|
||||
{
|
||||
t = bcdToByte(tod_clock[todpos]) + 1;
|
||||
tod_clock[todpos++] = byteToBcd(t % 60);
|
||||
if (t >= 60)
|
||||
{
|
||||
int pm = tod_clock[todpos] & 0x80;
|
||||
t = bcdToByte(tod_clock[todpos] & 0x1F);
|
||||
if (t == 0x11)
|
||||
{
|
||||
pm ^= 0x80;
|
||||
}
|
||||
if (t == 0x12)
|
||||
{
|
||||
t = 1;
|
||||
}
|
||||
else if (++t == 10)
|
||||
{
|
||||
t = 0x10;
|
||||
}
|
||||
t &= 0x1F;
|
||||
tod_clock[todpos] = t | pm;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tod_clock[0] == tod_alarm[0] &&
|
||||
tod_clock[1] == tod_alarm[1] &&
|
||||
tod_clock[2] == tod_alarm[2] &&
|
||||
tod_clock[3] == tod_alarm[3])
|
||||
{
|
||||
triggerInterruptAlarm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void triggerInterruptAlarm()
|
||||
{
|
||||
int_set(INT_ALARM);
|
||||
}
|
||||
|
||||
void triggerInterruptSP()
|
||||
{
|
||||
int_set(INT_SP);
|
||||
}
|
||||
|
||||
void triggerInterruptUnderFlowA()
|
||||
{
|
||||
int_set(INT_UNDERFLOW_A);
|
||||
}
|
||||
|
||||
void triggerInterruptUnderFlowB()
|
||||
{
|
||||
int_set(INT_UNDERFLOW_B);
|
||||
}
|
||||
|
||||
void underFlowA()
|
||||
{
|
||||
triggerInterruptUnderFlowA();
|
||||
if ((crb & 0x41) == 0x41)
|
||||
{
|
||||
if ((b.state & CiaTimer.TIMER_CR_START) != 0)
|
||||
{
|
||||
postTimerBEvent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void underFlowB()
|
||||
{
|
||||
triggerInterruptUnderFlowB();
|
||||
}
|
||||
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
SaveState.SyncObject(ser, this);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue