2012-03-08 08:12:44 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Consoles.Atari
|
|
|
|
|
{
|
|
|
|
|
// Emulates the M6532 RIOT Chip
|
|
|
|
|
public partial class M6532
|
|
|
|
|
{
|
2012-03-21 20:28:26 +00:00
|
|
|
|
Atari2600 core;
|
2012-03-14 21:57:10 +00:00
|
|
|
|
|
2012-03-21 20:28:26 +00:00
|
|
|
|
public byte ddra = 0x00;
|
|
|
|
|
public byte ddrb = 0x00;
|
2012-03-15 06:09:10 +00:00
|
|
|
|
|
2012-04-04 01:18:41 +00:00
|
|
|
|
public struct timerData
|
2012-03-08 08:12:44 +00:00
|
|
|
|
{
|
2012-04-04 01:18:41 +00:00
|
|
|
|
public int prescalerCount;
|
|
|
|
|
public byte prescalerShift;
|
2012-03-10 00:26:54 +00:00
|
|
|
|
|
2012-04-04 01:18:41 +00:00
|
|
|
|
public byte value;
|
2012-03-08 08:12:44 +00:00
|
|
|
|
|
2012-04-04 01:18:41 +00:00
|
|
|
|
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)
|
2012-03-21 20:28:26 +00:00
|
|
|
|
{
|
2012-04-04 01:18:41 +00:00
|
|
|
|
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);
|
2012-03-21 20:28:26 +00:00
|
|
|
|
}
|
2012-04-04 01:18:41 +00:00
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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;
|
2012-03-21 01:02:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-03-08 08:12:44 +00:00
|
|
|
|
public byte ReadMemory(ushort addr)
|
|
|
|
|
{
|
2012-03-21 20:28:26 +00:00
|
|
|
|
// Register Select (?)
|
|
|
|
|
bool RS = (addr & 0x0200) != 0;
|
2012-03-08 08:12:44 +00:00
|
|
|
|
|
2012-03-21 20:28:26 +00:00
|
|
|
|
if (!RS)
|
2012-03-08 08:12:44 +00:00
|
|
|
|
{
|
2012-03-21 20:28:26 +00:00
|
|
|
|
// Read Ram
|
|
|
|
|
ushort maskedAddr = (ushort)(addr & 0x007f);
|
|
|
|
|
return core.ram[maskedAddr];
|
2012-03-08 08:12:44 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2012-03-21 20:28:26 +00:00
|
|
|
|
ushort registerAddr = (ushort)(addr & 0x0007);
|
|
|
|
|
if (registerAddr == 0x00)
|
2012-03-10 00:26:54 +00:00
|
|
|
|
{
|
2012-03-21 20:28:26 +00:00
|
|
|
|
// Read Output reg A
|
|
|
|
|
// Combine readings from player 1 and player 2
|
|
|
|
|
byte temp = (byte)(core.ReadControls1() & 0xF0 | ((core.ReadControls2() >> 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();
|
|
|
|
|
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
|
2012-04-04 01:18:41 +00:00
|
|
|
|
timer.interruptEnabled = (addr & 0x0080) != 0;
|
2012-03-10 00:26:54 +00:00
|
|
|
|
|
2012-03-21 20:28:26 +00:00
|
|
|
|
// 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)
|
2012-04-04 01:18:41 +00:00
|
|
|
|
if (!(timer.prescalerCount == 0 && timer.value == 0))
|
2012-03-10 00:26:54 +00:00
|
|
|
|
{
|
2012-04-04 01:18:41 +00:00
|
|
|
|
timer.interruptFlag = false;
|
2012-03-10 00:26:54 +00:00
|
|
|
|
}
|
2012-03-21 20:28:26 +00:00
|
|
|
|
|
2012-04-04 01:18:41 +00:00
|
|
|
|
return timer.value;
|
2012-03-10 00:26:54 +00:00
|
|
|
|
}
|
2012-03-21 20:28:26 +00:00
|
|
|
|
else if ((registerAddr & 0x5) == 0x5)
|
2012-03-10 00:26:54 +00:00
|
|
|
|
{
|
2012-03-21 20:28:26 +00:00
|
|
|
|
// Read interrupt flag
|
2012-04-04 01:18:41 +00:00
|
|
|
|
if (timer.interruptEnabled && timer.interruptFlag)
|
2012-03-12 18:44:29 +00:00
|
|
|
|
{
|
2012-04-04 01:18:41 +00:00
|
|
|
|
return 0x80;
|
2012-03-12 18:44:29 +00:00
|
|
|
|
}
|
2012-03-21 20:28:26 +00:00
|
|
|
|
else
|
2012-03-14 21:57:10 +00:00
|
|
|
|
{
|
2012-04-04 01:18:41 +00:00
|
|
|
|
return 0x00;
|
2012-03-14 21:57:10 +00:00
|
|
|
|
}
|
2012-03-10 00:26:54 +00:00
|
|
|
|
}
|
2012-03-08 08:12:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0x3A;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void WriteMemory(ushort addr, byte value)
|
|
|
|
|
{
|
2012-03-21 20:28:26 +00:00
|
|
|
|
// Register Select (?)
|
|
|
|
|
bool RS = (addr & 0x0200) != 0;
|
2012-03-08 08:12:44 +00:00
|
|
|
|
|
2012-03-21 20:28:26 +00:00
|
|
|
|
// If the RS bit is not set, this is a ram write
|
|
|
|
|
if (!RS)
|
2012-03-08 08:12:44 +00:00
|
|
|
|
{
|
2012-03-21 20:28:26 +00:00
|
|
|
|
ushort maskedAddr = (ushort)(addr & 0x007f);
|
|
|
|
|
core.ram[maskedAddr] = value;
|
2012-03-08 08:12:44 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2012-03-21 20:28:26 +00:00
|
|
|
|
// If bit 0x0010 is set, and bit 0x0004 is set, this is a timer write
|
|
|
|
|
if ((addr & 0x0014) == 0x0014)
|
2012-03-10 00:26:54 +00:00
|
|
|
|
{
|
2012-03-21 20:28:26 +00:00
|
|
|
|
ushort registerAddr = (ushort)(addr & 0x0007);
|
2012-03-10 00:26:54 +00:00
|
|
|
|
|
2012-03-21 20:28:26 +00:00
|
|
|
|
// Bit 0x0080 contains interrupt enable/disable
|
2012-04-04 01:18:41 +00:00
|
|
|
|
timer.interruptEnabled = (addr & 0x0080) != 0;
|
2012-03-10 00:26:54 +00:00
|
|
|
|
|
2012-03-21 20:28:26 +00:00
|
|
|
|
// The interrupt flag will be reset whenever the Timer is access by a read or a write
|
|
|
|
|
// (M6532 datasheet)
|
2012-03-10 00:26:54 +00:00
|
|
|
|
|
2012-03-21 20:28:26 +00:00
|
|
|
|
if (registerAddr == 0x04)
|
|
|
|
|
{
|
|
|
|
|
// Write to Timer/1
|
2012-04-04 01:18:41 +00:00
|
|
|
|
timer.prescalerShift = 0;
|
|
|
|
|
timer.value = value;
|
|
|
|
|
timer.prescalerCount = 1 << timer.prescalerShift;
|
|
|
|
|
timer.interruptFlag = false;
|
2012-03-21 20:28:26 +00:00
|
|
|
|
}
|
|
|
|
|
else if (registerAddr == 0x05)
|
|
|
|
|
{
|
|
|
|
|
// Write to Timer/8
|
2012-04-04 01:18:41 +00:00
|
|
|
|
timer.prescalerShift = 3;
|
|
|
|
|
timer.value = value;
|
|
|
|
|
timer.prescalerCount = 1 << timer.prescalerShift;
|
|
|
|
|
timer.interruptFlag = false;
|
2012-03-21 20:28:26 +00:00
|
|
|
|
}
|
|
|
|
|
else if (registerAddr == 0x06)
|
|
|
|
|
{
|
|
|
|
|
// Write to Timer/64
|
2012-04-04 01:18:41 +00:00
|
|
|
|
timer.prescalerShift = 6;
|
|
|
|
|
timer.value = value;
|
|
|
|
|
timer.prescalerCount = 1 << timer.prescalerShift;
|
|
|
|
|
timer.interruptFlag = false;
|
2012-03-21 20:28:26 +00:00
|
|
|
|
}
|
|
|
|
|
else if (registerAddr == 0x07)
|
|
|
|
|
{
|
|
|
|
|
// Write to Timer/1024
|
2012-04-04 01:18:41 +00:00
|
|
|
|
timer.prescalerShift = 10;
|
|
|
|
|
timer.value = value;
|
|
|
|
|
timer.prescalerCount = 1 << timer.prescalerShift;
|
|
|
|
|
timer.interruptFlag = false;
|
2012-03-21 20:28:26 +00:00
|
|
|
|
}
|
2012-03-15 06:09:10 +00:00
|
|
|
|
}
|
2012-03-21 20:28:26 +00:00
|
|
|
|
// If bit 0x0004 is not set, bit 0x0010 is ignored and
|
|
|
|
|
// these are register writes
|
|
|
|
|
else if ((addr & 0x0004) == 0)
|
2012-03-10 00:26:54 +00:00
|
|
|
|
{
|
2012-03-21 20:28:26 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
2012-03-10 00:26:54 +00:00
|
|
|
|
}
|
2012-03-08 08:12:44 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-03-30 02:34:47 +00:00
|
|
|
|
|
|
|
|
|
public void SyncState(Serializer ser)
|
|
|
|
|
{
|
|
|
|
|
ser.Sync("ddra", ref ddra);
|
|
|
|
|
ser.Sync("ddrb", ref ddrb);
|
2012-04-04 01:18:41 +00:00
|
|
|
|
timer.SyncState(ser);
|
2012-03-30 02:34:47 +00:00
|
|
|
|
}
|
2012-03-08 08:12:44 +00:00
|
|
|
|
}
|
|
|
|
|
}
|