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,45 +58,32 @@ namespace BizHawk
|
||||||
// ROM
|
// ROM
|
||||||
public byte ReadMemory(ushort addr)
|
public byte ReadMemory(ushort addr)
|
||||||
{
|
{
|
||||||
ushort maskedAddr = (ushort)(addr & 0x1FFF);
|
addr = (ushort)(addr & 0x1FFF);
|
||||||
ushort pageNum = (ushort)(maskedAddr >> 6);
|
if ((addr & 0x1080) == 0)
|
||||||
if (pageNum < 0x40)
|
|
||||||
{
|
{
|
||||||
// if Page 0x02, or a mirror
|
return tia.ReadMemory(addr);
|
||||||
if (pageNum % 4 > 1)
|
}
|
||||||
{
|
else if ((addr & 0x1080) == 0x0080)
|
||||||
return m6532.ReadMemory(maskedAddr);
|
{
|
||||||
}
|
return m6532.ReadMemory(addr);
|
||||||
// if Page 0x01 or 0x02, or a mirror
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return tia.ReadMemory(maskedAddr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// ROM data
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Console.WriteLine("ROM read");
|
return rom[addr & 0x0FFF];
|
||||||
return rom[maskedAddr & 0x0FFF];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteMemory(ushort addr, byte value)
|
public void WriteMemory(ushort addr, byte value)
|
||||||
{
|
{
|
||||||
ushort maskedAddr = (ushort)(addr & 0x1FFF);
|
addr = (ushort)(addr & 0x1FFF);
|
||||||
ushort pageNum = (ushort)(maskedAddr >> 6);
|
if ((addr & 0x1080) == 0)
|
||||||
if (pageNum < 0x40)
|
|
||||||
{
|
{
|
||||||
if (pageNum % 4 > 1)
|
tia.WriteMemory(addr, value);
|
||||||
{
|
}
|
||||||
m6532.WriteMemory(maskedAddr, value);
|
else if ((addr & 0x1080) == 0x0080)
|
||||||
}
|
{
|
||||||
else
|
m6532.WriteMemory(addr, value);
|
||||||
{
|
|
||||||
tia.WriteMemory(maskedAddr, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// ROM data
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("ROM write(?): " + addr.ToString("x"));
|
Console.WriteLine("ROM write(?): " + addr.ToString("x"));
|
||||||
|
@ -106,7 +93,7 @@ namespace BizHawk
|
||||||
public void HardReset()
|
public void HardReset()
|
||||||
{
|
{
|
||||||
cpu = new MOS6507();
|
cpu = new MOS6507();
|
||||||
cpu.debug = true;
|
//cpu.debug = true;
|
||||||
cpu.ReadMemory = ReadMemory;
|
cpu.ReadMemory = ReadMemory;
|
||||||
cpu.WriteMemory = WriteMemory;
|
cpu.WriteMemory = WriteMemory;
|
||||||
|
|
||||||
|
@ -114,7 +101,7 @@ namespace BizHawk
|
||||||
//tia = new TIA(this, frameBuffer);
|
//tia = new TIA(this, frameBuffer);
|
||||||
tia = new TIA(this);
|
tia = new TIA(this);
|
||||||
// Setup 6532
|
// Setup 6532
|
||||||
m6532 = new M6532(cpu, ram, this);
|
m6532 = new M6532(this);
|
||||||
|
|
||||||
//setup the system state here. for instance..
|
//setup the system state here. for instance..
|
||||||
// Read from the reset vector for where to start
|
// Read from the reset vector for where to start
|
||||||
|
@ -125,15 +112,6 @@ namespace BizHawk
|
||||||
public void FrameAdvance(bool render)
|
public void FrameAdvance(bool render)
|
||||||
{
|
{
|
||||||
Frame++;
|
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;
|
tia.frameComplete = false;
|
||||||
while (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!)
|
//if (render == false) return;
|
||||||
//this should generate the framebuffer as it goes.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte ReadControls1()
|
public byte ReadControls1()
|
||||||
|
@ -184,5 +147,38 @@ namespace BizHawk
|
||||||
if (Controller["P1 Button"]) value &= 0xF7;
|
if (Controller["P1 Button"]) value &= 0xF7;
|
||||||
return value;
|
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,104 +8,118 @@ namespace BizHawk.Emulation.Consoles.Atari
|
||||||
// Emulates the M6532 RIOT Chip
|
// Emulates the M6532 RIOT Chip
|
||||||
public partial class M6532
|
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;
|
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 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;
|
this.core = core;
|
||||||
|
|
||||||
// Apparently this will break for some games (Solaris and H.E.R.O.). We shall see
|
// Apparently starting the timer at 0 will break for some games (Solaris and H.E.R.O.). We shall see
|
||||||
timerFinishedCycles = 0;
|
timerCyclesRemaining = 0;
|
||||||
|
interruptEnabled = false;
|
||||||
|
interruptFlag = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tick()
|
public void tick()
|
||||||
{
|
{
|
||||||
totalCycles++;
|
timerCyclesRemaining--;
|
||||||
|
if (timerCyclesRemaining == 0 && interruptEnabled)
|
||||||
|
{
|
||||||
|
interruptFlag = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte ReadMemory(ushort addr)
|
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);
|
// Read Ram
|
||||||
Console.WriteLine("6532 ram read: " + maskedAddr.ToString("x"));
|
ushort maskedAddr = (ushort)(addr & 0x007f);
|
||||||
return ram[maskedAddr];
|
return core.ram[maskedAddr];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
maskedAddr = (ushort)(addr & 0x0007);
|
ushort registerAddr = (ushort)(addr & 0x0007);
|
||||||
if (maskedAddr == 0x04 || maskedAddr == 0x06)
|
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;
|
// 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
|
||||||
|
interruptEnabled = (addr & 0x0080) != 0;
|
||||||
|
|
||||||
interruptTriggered = false;
|
// 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
|
||||||
// If the timer has not finished, shift the value down for the game
|
// (M6532 Datasheet)
|
||||||
if (totalCycles < timerFinishedCycles)
|
if (timerCyclesRemaining != 0)
|
||||||
{
|
{
|
||||||
return (byte)(((timerCurrentValue) >> timerShift) & 0xFF);
|
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);
|
||||||
}
|
}
|
||||||
// Other wise, return the last 8 bits from how long ago it triggered
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
interruptTriggered = true;
|
return (byte)(timerCyclesRemaining & 0xFF);
|
||||||
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
|
|
||||||
{
|
|
||||||
bool temp = resetOccured;
|
|
||||||
resetOccured = false;
|
|
||||||
return (byte)(0x0A | (temp ? 0x00 : 0x01));
|
|
||||||
}
|
|
||||||
else if (maskedAddr == 0x03) // SWBCNT
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (maskedAddr == 0x05) // interrupt
|
else if ((registerAddr & 0x5) == 0x5)
|
||||||
|
{
|
||||||
|
// Read interrupt flag
|
||||||
|
if (interruptEnabled && interruptFlag)
|
||||||
{
|
{
|
||||||
if ((timerFinishedCycles - totalCycles >= 0)|| (interruptEnabled && interruptTriggered))
|
return 0x00;
|
||||||
{
|
}
|
||||||
return 0x00;
|
else
|
||||||
}
|
{
|
||||||
else
|
return 0x80;
|
||||||
{
|
|
||||||
return 0x80;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,39 +129,81 @@ namespace BizHawk.Emulation.Consoles.Atari
|
||||||
|
|
||||||
public void WriteMemory(ushort addr, byte value)
|
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);
|
ushort maskedAddr = (ushort)(addr & 0x007f);
|
||||||
Console.WriteLine("6532 ram write: " + maskedAddr.ToString("x"));
|
core.ram[maskedAddr] = value;
|
||||||
ram[maskedAddr] = value;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
maskedAddr = (ushort)(addr & 0x0007);
|
// If bit 0x0010 is set, and bit 0x0004 is set, this is a timer write
|
||||||
if ((addr & 0x14) == 0x14)
|
if ((addr & 0x0014) == 0x0014)
|
||||||
{
|
{
|
||||||
int[] shift = new int[] {0,3,6,10};
|
ushort registerAddr = (ushort)(addr & 0x0007);
|
||||||
timerShift = shift[addr & 0x03];
|
|
||||||
|
|
||||||
// Store the number of cycles for the timer
|
// Bit 0x0080 contains interrupt enable/disable
|
||||||
timerStartValue = value << timerShift;
|
interruptEnabled = (addr & 0x0080) != 0;
|
||||||
|
|
||||||
// Calculate when the timer will be finished
|
// The interrupt flag will be reset whenever the Timer is access by a read or a write
|
||||||
timerFinishedCycles = timerStartValue + totalCycles;
|
// (M6532 datasheet)
|
||||||
|
|
||||||
Console.WriteLine("6532 timer write: " + maskedAddr.ToString("x"));
|
if (registerAddr == 0x04)
|
||||||
|
{
|
||||||
interruptEnabled = ((addr & 0x08) != 0);
|
// Write to Timer/1
|
||||||
|
timerShift = 0;
|
||||||
|
timerCyclesRemaining = value << timerShift;
|
||||||
|
interruptFlag = false;
|
||||||
|
}
|
||||||
|
else if (registerAddr == 0x05)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if ((addr & 0x04) == 0 && (maskedAddr & 0x03) == 0x02)
|
// If bit 0x0004 is not set, bit 0x0010 is ignored and
|
||||||
|
// these are register writes
|
||||||
|
else if ((addr & 0x0004) == 0)
|
||||||
{
|
{
|
||||||
swchb = value;
|
ushort registerAddr = (ushort)(addr & 0x0007);
|
||||||
}
|
|
||||||
else
|
if (registerAddr == 0x00)
|
||||||
{
|
{
|
||||||
Console.WriteLine("6532 register write: " + maskedAddr.ToString("x"));
|
// 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)
|
public byte ReadMemory(ushort addr)
|
||||||
{
|
{
|
||||||
ushort maskedAddr = (ushort)(addr & 0x000F);
|
ushort maskedAddr = (ushort)(addr & 0x000F);
|
||||||
Console.WriteLine("TIA read: " + maskedAddr.ToString("x"));
|
|
||||||
if (maskedAddr == 0x00) // CXM0P
|
if (maskedAddr == 0x00) // CXM0P
|
||||||
{
|
{
|
||||||
return (byte)((((player0.missile.collisions & CXP1) != 0) ? 0x80 : 0x00) | (((player0.missile.collisions & CXP0) != 0) ? 0x40 : 0x00));
|
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)
|
public void WriteMemory(ushort addr, byte value)
|
||||||
{
|
{
|
||||||
ushort maskedAddr = (ushort)(addr & 0x3f);
|
ushort maskedAddr = (ushort)(addr & 0x3f);
|
||||||
Console.WriteLine("TIA write: " + maskedAddr.ToString("x"));
|
|
||||||
|
|
||||||
if (maskedAddr == 0x00) // VSYNC
|
if (maskedAddr == 0x00) // VSYNC
|
||||||
{
|
{
|
||||||
|
@ -698,7 +696,6 @@ namespace BizHawk.Emulation.Consoles.Atari
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// When VSYNC is disabled, this will be the first line of the new frame
|
// When VSYNC is disabled, this will be the first line of the new frame
|
||||||
Console.WriteLine("TIA VSYNC Off");
|
|
||||||
|
|
||||||
// write to frame buffer
|
// write to frame buffer
|
||||||
outputFrame();
|
outputFrame();
|
||||||
|
|
Loading…
Reference in New Issue