Atari M6532: Turned the timer into an object, and now uses a prescaler, replacing the previous 32 bit int implementation.

Fixed a typo with the interrupt flag. 
These changes fix freezes with "Berzerk (1982) (Atari)" and "Omega Race (1983) (CBS Electronics)"
This commit is contained in:
pjgat09 2012-04-04 01:18:41 +00:00
parent 3591f7ca47
commit b5927b1212
3 changed files with 74 additions and 54 deletions

View File

@ -165,7 +165,7 @@ namespace BizHawk
tia.execute(1);
tia.execute(1);
m6532.tick();
m6532.timer.tick();
cpu.ExecuteOne();
//if (cpu.PendingCycles <= 0)
//{

View File

@ -9,32 +9,60 @@ namespace BizHawk.Emulation.Consoles.Atari
{
Atari2600 core;
public int timerCyclesRemaining = 0;
public int timerShift = 0;
bool interruptEnabled = false;
bool interruptFlag = false;
public byte ddra = 0x00;
public byte ddrb = 0x00;
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.). We shall see
timerCyclesRemaining = 0;
interruptEnabled = false;
interruptFlag = false;
}
public void tick()
{
timerCyclesRemaining--;
if (timerCyclesRemaining == 0 && interruptEnabled)
{
interruptFlag = true;
}
// 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)
@ -86,37 +114,28 @@ namespace BizHawk.Emulation.Consoles.Atari
else if ((registerAddr & 0x5) == 0x4)
{
// Bit 0x0080 contains interrupt enable/disable
interruptEnabled = (addr & 0x0080) != 0;
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 (timerCyclesRemaining != 0)
if (!(timer.prescalerCount == 0 && timer.value == 0))
{
interruptFlag = false;
}
// If there is still time on the timer (or its 0), return the lowest byte
if (timerCyclesRemaining >= 0)
{
return (byte)(((timerCyclesRemaining) >> timerShift) & 0xFF);
}
else
{
return (byte)(timerCyclesRemaining & 0xFF);
timer.interruptFlag = false;
}
return timer.value;
}
else if ((registerAddr & 0x5) == 0x5)
{
// Read interrupt flag
if (interruptEnabled && interruptFlag)
if (timer.interruptEnabled && timer.interruptFlag)
{
return 0x00;
return 0x80;
}
else
{
return 0x80;
return 0x00;
}
}
}
@ -143,7 +162,7 @@ namespace BizHawk.Emulation.Consoles.Atari
ushort registerAddr = (ushort)(addr & 0x0007);
// Bit 0x0080 contains interrupt enable/disable
interruptEnabled = (addr & 0x0080) != 0;
timer.interruptEnabled = (addr & 0x0080) != 0;
// The interrupt flag will be reset whenever the Timer is access by a read or a write
// (M6532 datasheet)
@ -151,30 +170,34 @@ namespace BizHawk.Emulation.Consoles.Atari
if (registerAddr == 0x04)
{
// Write to Timer/1
timerShift = 0;
timerCyclesRemaining = value << timerShift;
interruptFlag = false;
timer.prescalerShift = 0;
timer.value = value;
timer.prescalerCount = 1 << timer.prescalerShift;
timer.interruptFlag = false;
}
else if (registerAddr == 0x05)
{
// Write to Timer/8
timerShift = 3;
timerCyclesRemaining = value << timerShift;
interruptFlag = false;
timer.prescalerShift = 3;
timer.value = value;
timer.prescalerCount = 1 << timer.prescalerShift;
timer.interruptFlag = false;
}
else if (registerAddr == 0x06)
{
// Write to Timer/64
timerShift = 6;
timerCyclesRemaining = value << timerShift;
interruptFlag = false;
timer.prescalerShift = 6;
timer.value = value;
timer.prescalerCount = 1 << timer.prescalerShift;
timer.interruptFlag = false;
}
else if (registerAddr == 0x07)
{
// Write to Timer/1024
timerShift = 10;
timerCyclesRemaining = value << timerShift;
interruptFlag = false;
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
@ -209,10 +232,7 @@ namespace BizHawk.Emulation.Consoles.Atari
{
ser.Sync("ddra", ref ddra);
ser.Sync("ddrb", ref ddrb);
ser.Sync("interruptEnabled", ref interruptEnabled);
ser.Sync("interruptFlag", ref interruptFlag);
ser.Sync("timerCyclesRemaining", ref timerCyclesRemaining);
ser.Sync("timerShift", ref timerShift);
timer.SyncState(ser);
}
}
}

View File

@ -879,7 +879,7 @@ namespace BizHawk.Emulation.Consoles.Atari
// Add a cycle to the cpu every 3 TIA clocks (corrects timer error in M6532)
if (count % 3 == 0)
{
core.m6532.tick();
core.m6532.timer.tick();
}
}
}