Atari 2600: Modified how memory read/writes are handled, to better emulate the address bus. Cleaned up some comments. Turned off CPU debugging. Added functions to read console switches and player 2 buttons.
M6532: Mostly re-written, this time based on the datasheet. TIA: Removed console debug outputs.
This commit is contained in:
parent
dcaf724dae
commit
a56ddcdb15
|
@ -58,46 +58,33 @@ namespace BizHawk
|
|||
// ROM
|
||||
public byte ReadMemory(ushort addr)
|
||||
{
|
||||
ushort maskedAddr = (ushort)(addr & 0x1FFF);
|
||||
ushort pageNum = (ushort)(maskedAddr >> 6);
|
||||
if (pageNum < 0x40)
|
||||
addr = (ushort)(addr & 0x1FFF);
|
||||
if ((addr & 0x1080) == 0)
|
||||
{
|
||||
// if Page 0x02, or a mirror
|
||||
if (pageNum % 4 > 1)
|
||||
{
|
||||
return m6532.ReadMemory(maskedAddr);
|
||||
return tia.ReadMemory(addr);
|
||||
}
|
||||
else if ((addr & 0x1080) == 0x0080)
|
||||
{
|
||||
return m6532.ReadMemory(addr);
|
||||
}
|
||||
// if Page 0x01 or 0x02, or a mirror
|
||||
else
|
||||
{
|
||||
return tia.ReadMemory(maskedAddr);
|
||||
}
|
||||
}
|
||||
// ROM data
|
||||
else
|
||||
{
|
||||
//Console.WriteLine("ROM read");
|
||||
return rom[maskedAddr & 0x0FFF];
|
||||
return rom[addr & 0x0FFF];
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteMemory(ushort addr, byte value)
|
||||
{
|
||||
ushort maskedAddr = (ushort)(addr & 0x1FFF);
|
||||
ushort pageNum = (ushort)(maskedAddr >> 6);
|
||||
if (pageNum < 0x40)
|
||||
addr = (ushort)(addr & 0x1FFF);
|
||||
if ((addr & 0x1080) == 0)
|
||||
{
|
||||
if (pageNum % 4 > 1)
|
||||
tia.WriteMemory(addr, value);
|
||||
}
|
||||
else if ((addr & 0x1080) == 0x0080)
|
||||
{
|
||||
m6532.WriteMemory(maskedAddr, value);
|
||||
m6532.WriteMemory(addr, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
tia.WriteMemory(maskedAddr, value);
|
||||
}
|
||||
}
|
||||
// ROM data
|
||||
else
|
||||
{
|
||||
Console.WriteLine("ROM write(?): " + addr.ToString("x"));
|
||||
}
|
||||
|
@ -106,7 +93,7 @@ namespace BizHawk
|
|||
public void HardReset()
|
||||
{
|
||||
cpu = new MOS6507();
|
||||
cpu.debug = true;
|
||||
//cpu.debug = true;
|
||||
cpu.ReadMemory = ReadMemory;
|
||||
cpu.WriteMemory = WriteMemory;
|
||||
|
||||
|
@ -114,7 +101,7 @@ namespace BizHawk
|
|||
//tia = new TIA(this, frameBuffer);
|
||||
tia = new TIA(this);
|
||||
// Setup 6532
|
||||
m6532 = new M6532(cpu, ram, this);
|
||||
m6532 = new M6532(this);
|
||||
|
||||
//setup the system state here. for instance..
|
||||
// Read from the reset vector for where to start
|
||||
|
@ -125,15 +112,6 @@ namespace BizHawk
|
|||
public void FrameAdvance(bool render)
|
||||
{
|
||||
Frame++;
|
||||
if (resetSignal)
|
||||
{
|
||||
//cpu.PC = cpu.ReadWord(MOS6507.ResetVector);
|
||||
m6532.resetOccured = true;
|
||||
//m6532.swchb &= 0xFE;
|
||||
//cpu.FlagI = true;
|
||||
}
|
||||
//cpu.Execute(228);
|
||||
//cpu.Execute(2000);
|
||||
|
||||
tia.frameComplete = false;
|
||||
while (tia.frameComplete == false)
|
||||
|
@ -154,23 +132,8 @@ namespace BizHawk
|
|||
}
|
||||
|
||||
}
|
||||
resetSignal = Controller["Reset"];
|
||||
//clear the framebuffer (hack code)
|
||||
if (render == false) return;
|
||||
/*
|
||||
for (int i = 0; i < 160 * 192; i++)
|
||||
{
|
||||
if (i < 64*256)
|
||||
frameBuffer[i] = i % 256; //black
|
||||
if (i >= 64*256 && i < 128*256)
|
||||
frameBuffer[i] = (i % 256) << 8; //black
|
||||
if (i >= 128*256)
|
||||
frameBuffer[i] = (i % 256) << 16; //black
|
||||
}
|
||||
*/
|
||||
|
||||
//run one frame's worth of cpu cyclees (i.e. do the emulation!)
|
||||
//this should generate the framebuffer as it goes.
|
||||
//if (render == false) return;
|
||||
}
|
||||
|
||||
public byte ReadControls1()
|
||||
|
@ -184,5 +147,38 @@ namespace BizHawk
|
|||
if (Controller["P1 Button"]) value &= 0xF7;
|
||||
return value;
|
||||
}
|
||||
|
||||
public byte ReadControls2()
|
||||
{
|
||||
byte value = 0xFF;
|
||||
|
||||
if (Controller["P2 Up"]) value &= 0xEF;
|
||||
if (Controller["P2 Down"]) value &= 0xDF;
|
||||
if (Controller["P2 Left"]) value &= 0xBF;
|
||||
if (Controller["P2 Right"]) value &= 0x7F;
|
||||
if (Controller["P2 Button"]) value &= 0xF7;
|
||||
return value;
|
||||
}
|
||||
|
||||
public byte ReadConsoleSwitches()
|
||||
{
|
||||
byte value = 0xFF;
|
||||
|
||||
bool select = false;
|
||||
bool reset = Controller["Reset"];
|
||||
bool bw = false;
|
||||
bool p0difficulty = true;
|
||||
bool p1difficulty = true;
|
||||
|
||||
if (reset) value &= 0xFE;
|
||||
if (select) value &= 0xFD;
|
||||
if (bw) value &= 0xF7;
|
||||
if (p0difficulty) value &= 0xBF;
|
||||
if (p1difficulty) value &= 0x7F;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -8,97 +8,112 @@ namespace BizHawk.Emulation.Consoles.Atari
|
|||
// Emulates the M6532 RIOT Chip
|
||||
public partial class M6532
|
||||
{
|
||||
MOS6507 Cpu;
|
||||
public byte[] ram;
|
||||
public int timerStartValue;
|
||||
public int timerFinishedCycles;
|
||||
public int timerShift;
|
||||
|
||||
bool interruptEnabled = false;
|
||||
bool interruptTriggered = false;
|
||||
|
||||
Atari2600 core;
|
||||
|
||||
public byte swchb = 0x0B;
|
||||
public int timerCyclesRemaining = 0;
|
||||
public int timerShift = 0;
|
||||
|
||||
bool interruptEnabled = false;
|
||||
bool interruptFlag = false;
|
||||
|
||||
public byte ddra = 0x00;
|
||||
public byte ddrb = 0x00;
|
||||
|
||||
public bool resetOccured = false;
|
||||
public int totalCycles = 0;
|
||||
|
||||
public M6532(MOS6507 cpu, byte[] ram, Atari2600 core)
|
||||
public M6532(Atari2600 core)
|
||||
{
|
||||
Cpu = cpu;
|
||||
this.ram = ram;
|
||||
this.core = core;
|
||||
|
||||
// Apparently this will break for some games (Solaris and H.E.R.O.). We shall see
|
||||
timerFinishedCycles = 0;
|
||||
|
||||
// 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()
|
||||
{
|
||||
totalCycles++;
|
||||
timerCyclesRemaining--;
|
||||
if (timerCyclesRemaining == 0 && interruptEnabled)
|
||||
{
|
||||
interruptFlag = true;
|
||||
}
|
||||
}
|
||||
|
||||
public byte ReadMemory(ushort addr)
|
||||
{
|
||||
ushort maskedAddr;
|
||||
// Register Select (?)
|
||||
bool RS = (addr & 0x0200) != 0;
|
||||
|
||||
if ((addr & 0x1080) == 0x0080 && (addr & 0x0200) == 0x0000)
|
||||
if (!RS)
|
||||
{
|
||||
maskedAddr = (ushort)(addr & 0x007f);
|
||||
Console.WriteLine("6532 ram read: " + maskedAddr.ToString("x"));
|
||||
return ram[maskedAddr];
|
||||
// Read Ram
|
||||
ushort maskedAddr = (ushort)(addr & 0x007f);
|
||||
return core.ram[maskedAddr];
|
||||
}
|
||||
else
|
||||
{
|
||||
maskedAddr = (ushort)(addr & 0x0007);
|
||||
if (maskedAddr == 0x04 || maskedAddr == 0x06)
|
||||
ushort registerAddr = (ushort)(addr & 0x0007);
|
||||
if (registerAddr == 0x00)
|
||||
{
|
||||
Console.WriteLine("6532 timer read: " + maskedAddr.ToString("x"));
|
||||
// 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;
|
||||
|
||||
// Calculate the current value on the timer
|
||||
int timerCurrentValue = timerFinishedCycles - totalCycles;
|
||||
|
||||
interruptTriggered = false;
|
||||
|
||||
// If the timer has not finished, shift the value down for the game
|
||||
if (totalCycles < timerFinishedCycles)
|
||||
{
|
||||
return (byte)(((timerCurrentValue) >> timerShift) & 0xFF);
|
||||
}
|
||||
// Other wise, return the last 8 bits from how long ago it triggered
|
||||
else
|
||||
{
|
||||
interruptTriggered = true;
|
||||
return (byte)(timerCurrentValue & 0xFF);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("6532 register read: " + maskedAddr.ToString("x"));
|
||||
if (maskedAddr == 0x00) // SWCHA
|
||||
{
|
||||
return core.ReadControls1();
|
||||
//return 0xFF;
|
||||
}
|
||||
else if (maskedAddr == 0x01) // SWACNT
|
||||
{
|
||||
|
||||
}
|
||||
else if (maskedAddr == 0x02) // SWCHB
|
||||
{
|
||||
/*
|
||||
// TODO: Rewrite this!
|
||||
bool temp = resetOccured;
|
||||
resetOccured = false;
|
||||
return (byte)(0x0A | (temp ? 0x00 : 0x01));
|
||||
* */
|
||||
}
|
||||
else if (maskedAddr == 0x03) // SWBCNT
|
||||
else if (registerAddr == 0x03)
|
||||
{
|
||||
// Read DDRB
|
||||
return ddrb;
|
||||
}
|
||||
else if ((registerAddr & 0x5) == 0x4)
|
||||
{
|
||||
// Bit 0x0080 contains interrupt enable/disable
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
else if (maskedAddr == 0x05) // interrupt
|
||||
else if ((registerAddr & 0x5) == 0x5)
|
||||
{
|
||||
if ((timerFinishedCycles - totalCycles >= 0)|| (interruptEnabled && interruptTriggered))
|
||||
// Read interrupt flag
|
||||
if (interruptEnabled && interruptFlag)
|
||||
{
|
||||
return 0x00;
|
||||
}
|
||||
|
@ -108,46 +123,87 @@ namespace BizHawk.Emulation.Consoles.Atari
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0x3A;
|
||||
}
|
||||
|
||||
public void WriteMemory(ushort addr, byte value)
|
||||
{
|
||||
ushort maskedAddr;
|
||||
// Register Select (?)
|
||||
bool RS = (addr & 0x0200) != 0;
|
||||
|
||||
if ((addr & 0x1080) == 0x0080 && (addr & 0x0200) == 0x0000)
|
||||
// If the RS bit is not set, this is a ram write
|
||||
if (!RS)
|
||||
{
|
||||
maskedAddr = (ushort)(addr & 0x007f);
|
||||
Console.WriteLine("6532 ram write: " + maskedAddr.ToString("x"));
|
||||
ram[maskedAddr] = value;
|
||||
ushort maskedAddr = (ushort)(addr & 0x007f);
|
||||
core.ram[maskedAddr] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
maskedAddr = (ushort)(addr & 0x0007);
|
||||
if ((addr & 0x14) == 0x14)
|
||||
// If bit 0x0010 is set, and bit 0x0004 is set, this is a timer write
|
||||
if ((addr & 0x0014) == 0x0014)
|
||||
{
|
||||
int[] shift = new int[] {0,3,6,10};
|
||||
timerShift = shift[addr & 0x03];
|
||||
ushort registerAddr = (ushort)(addr & 0x0007);
|
||||
|
||||
// Store the number of cycles for the timer
|
||||
timerStartValue = value << timerShift;
|
||||
// Bit 0x0080 contains interrupt enable/disable
|
||||
interruptEnabled = (addr & 0x0080) != 0;
|
||||
|
||||
// Calculate when the timer will be finished
|
||||
timerFinishedCycles = timerStartValue + totalCycles;
|
||||
// The interrupt flag will be reset whenever the Timer is access by a read or a write
|
||||
// (M6532 datasheet)
|
||||
|
||||
Console.WriteLine("6532 timer write: " + maskedAddr.ToString("x"));
|
||||
|
||||
interruptEnabled = ((addr & 0x08) != 0);
|
||||
}
|
||||
else if ((addr & 0x04) == 0 && (maskedAddr & 0x03) == 0x02)
|
||||
if (registerAddr == 0x04)
|
||||
{
|
||||
swchb = value;
|
||||
// Write to Timer/1
|
||||
timerShift = 0;
|
||||
timerCyclesRemaining = value << timerShift;
|
||||
interruptFlag = false;
|
||||
}
|
||||
else
|
||||
else if (registerAddr == 0x05)
|
||||
{
|
||||
Console.WriteLine("6532 register write: " + maskedAddr.ToString("x"));
|
||||
// Write to Timer/8
|
||||
timerShift = 3;
|
||||
timerCyclesRemaining = value << timerShift;
|
||||
interruptFlag = false;
|
||||
}
|
||||
else if (registerAddr == 0x06)
|
||||
{
|
||||
// Write to Timer/64
|
||||
timerShift = 6;
|
||||
timerCyclesRemaining = value << timerShift;
|
||||
interruptFlag = false;
|
||||
}
|
||||
else if (registerAddr == 0x07)
|
||||
{
|
||||
// Write to Timer/1024
|
||||
timerShift = 10;
|
||||
timerCyclesRemaining = value << timerShift;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -642,7 +642,6 @@ namespace BizHawk.Emulation.Consoles.Atari
|
|||
public byte ReadMemory(ushort addr)
|
||||
{
|
||||
ushort maskedAddr = (ushort)(addr & 0x000F);
|
||||
Console.WriteLine("TIA read: " + maskedAddr.ToString("x"));
|
||||
if (maskedAddr == 0x00) // CXM0P
|
||||
{
|
||||
return (byte)((((player0.missile.collisions & CXP1) != 0) ? 0x80 : 0x00) | (((player0.missile.collisions & CXP0) != 0) ? 0x40 : 0x00));
|
||||
|
@ -686,7 +685,6 @@ namespace BizHawk.Emulation.Consoles.Atari
|
|||
public void WriteMemory(ushort addr, byte value)
|
||||
{
|
||||
ushort maskedAddr = (ushort)(addr & 0x3f);
|
||||
Console.WriteLine("TIA write: " + maskedAddr.ToString("x"));
|
||||
|
||||
if (maskedAddr == 0x00) // VSYNC
|
||||
{
|
||||
|
@ -698,7 +696,6 @@ namespace BizHawk.Emulation.Consoles.Atari
|
|||
else
|
||||
{
|
||||
// When VSYNC is disabled, this will be the first line of the new frame
|
||||
Console.WriteLine("TIA VSYNC Off");
|
||||
|
||||
// write to frame buffer
|
||||
outputFrame();
|
||||
|
|
Loading…
Reference in New Issue