BizHawk/BizHawk.Emulation/Consoles/Atari/2600/M6532.cs

238 lines
5.6 KiB
C#
Raw Normal View History

using BizHawk.Common;
namespace BizHawk.Emulation.Consoles.Atari
{
// Emulates the M6532 RIOT Chip
2013-04-16 00:42:57 +00:00
public class M6532
{
public byte ddra = 0x00;
public byte ddrb = 0x00;
2013-04-16 00:42:57 +00:00
private readonly Atari2600 core;
public struct timerData
{
public int prescalerCount;
public byte prescalerShift;
public byte value;
public bool interruptEnabled;
public bool interruptFlag;
public void tick()
{
if (prescalerCount == 0)
{
value--;
prescalerCount = 1 << prescalerShift;
}
prescalerCount--;
if (prescalerCount == 0)
{
if (value == 0)
{
interruptFlag = true;
prescalerShift = 0;
}
}
}
public void SyncState(Serializer ser)
{
ser.Sync("prescalerCount", ref prescalerCount);
ser.Sync("prescalerShift", ref prescalerShift);
ser.Sync("value", ref value);
ser.Sync("interruptEnabled", ref interruptEnabled);
ser.Sync("interruptFlag", ref interruptFlag);
}
};
public timerData timer;
public M6532(Atari2600 core)
{
this.core = core;
// Apparently starting the timer at 0 will break for some games (Solaris and H.E.R.O.). To avoid that, we pick an
// arbitrary value to start with.
timer.value = 0x73;
timer.prescalerShift = 10;
timer.prescalerCount = 1 << timer.prescalerShift;
}
public byte ReadMemory(ushort addr, bool peek)
{
// Register Select (?)
bool RS = (addr & 0x0200) != 0;
if (!RS)
{
// Read Ram
ushort maskedAddr = (ushort)(addr & 0x007f);
return core.ram[maskedAddr];
}
else
{
ushort registerAddr = (ushort)(addr & 0x0007);
if (registerAddr == 0x00)
{
// Read Output reg A
// Combine readings from player 1 and player 2
byte temp = (byte)(core.ReadControls1(peek) & 0xF0 | ((core.ReadControls2(peek) >> 4) & 0x0F));
temp = (byte)(temp & ~ddra);
return temp;
}
else if (registerAddr == 0x01)
{
// Read DDRA
return ddra;
}
else if (registerAddr == 0x02)
{
// Read Output reg B
byte temp = core.ReadConsoleSwitches(peek);
temp = (byte)(temp & ~ddrb);
return temp;
/*
// TODO: Rewrite this!
bool temp = resetOccured;
resetOccured = false;
return (byte)(0x0A | (temp ? 0x00 : 0x01));
* */
}
else if (registerAddr == 0x03)
{
// Read DDRB
return ddrb;
}
else if ((registerAddr & 0x5) == 0x4)
{
// Bit 0x0080 contains interrupt enable/disable
timer.interruptEnabled = (addr & 0x0080) != 0;
// The interrupt flag will be reset whenever the Timer is access by a read or a write
// However, the reading of the timer at the same time the interrupt occurs will not reset the interrupt flag
// (M6532 Datasheet)
if (!(timer.prescalerCount == 0 && timer.value == 0))
{
timer.interruptFlag = false;
}
return timer.value;
}
else if ((registerAddr & 0x5) == 0x5)
{
// Read interrupt flag
if (timer.interruptEnabled && timer.interruptFlag)
{
return 0x80;
}
else
{
return 0x00;
}
}
}
return 0x3A;
}
public void WriteMemory(ushort addr, byte value)
{
// Register Select (?)
bool RS = (addr & 0x0200) != 0;
// If the RS bit is not set, this is a ram write
if (!RS)
{
ushort maskedAddr = (ushort)(addr & 0x007f);
core.ram[maskedAddr] = value;
}
else
{
// If bit 0x0010 is set, and bit 0x0004 is set, this is a timer write
if ((addr & 0x0014) == 0x0014)
{
ushort registerAddr = (ushort)(addr & 0x0007);
// Bit 0x0080 contains interrupt enable/disable
timer.interruptEnabled = (addr & 0x0080) != 0;
// The interrupt flag will be reset whenever the Timer is access by a read or a write
// (M6532 datasheet)
if (registerAddr == 0x04)
{
// Write to Timer/1
timer.prescalerShift = 0;
timer.value = value;
timer.prescalerCount = 1 << timer.prescalerShift;
timer.interruptFlag = false;
}
else if (registerAddr == 0x05)
{
// Write to Timer/8
timer.prescalerShift = 3;
timer.value = value;
timer.prescalerCount = 1 << timer.prescalerShift;
timer.interruptFlag = false;
}
else if (registerAddr == 0x06)
{
// Write to Timer/64
timer.prescalerShift = 6;
timer.value = value;
timer.prescalerCount = 1 << timer.prescalerShift;
timer.interruptFlag = false;
}
else if (registerAddr == 0x07)
{
// Write to Timer/1024
timer.prescalerShift = 10;
timer.value = value;
timer.prescalerCount = 1 << timer.prescalerShift;
timer.interruptFlag = false;
}
}
// If bit 0x0004 is not set, bit 0x0010 is ignored and
// these are register writes
else if ((addr & 0x0004) == 0)
{
ushort registerAddr = (ushort)(addr & 0x0007);
if (registerAddr == 0x00)
{
// Write Output reg A
}
else if (registerAddr == 0x01)
{
// Write DDRA
ddra = value;
}
else if (registerAddr == 0x02)
{
// Write Output reg B
}
else if (registerAddr == 0x03)
{
// Write DDRB
ddrb = value;
}
}
}
}
public void SyncState(Serializer ser)
{
ser.BeginSection("M6532");
ser.Sync("ddra", ref ddra);
ser.Sync("ddrb", ref ddrb);
timer.SyncState(ser);
ser.EndSection();
}
}
}