2013-11-04 00:36:15 +00:00
|
|
|
|
using BizHawk.Common;
|
|
|
|
|
|
2013-11-13 03:32:25 +00:00
|
|
|
|
namespace BizHawk.Emulation.Cores.Atari.Atari2600
|
2012-03-31 20:53:14 +00:00
|
|
|
|
{
|
2012-04-29 21:01:06 +00:00
|
|
|
|
/*
|
|
|
|
|
4A50 (no name)
|
|
|
|
|
-----
|
|
|
|
|
|
|
|
|
|
Upon review, I don't think this method is terribly workable on real
|
|
|
|
|
hardware. There's so many problems that I kinda gave up trying to
|
|
|
|
|
count them all. Seems that this is more of a "pony" method than something
|
|
|
|
|
actually usable. ("pony" referring to "I want this, and that, and that, and
|
|
|
|
|
a pony too!")
|
|
|
|
|
|
|
|
|
|
One major problem is that it specifies that memory can be read and written
|
|
|
|
|
to at the same address, but this is nearly impossible to detect on a 2600
|
|
|
|
|
cartridge. You'd almost have to try and figure out what opcodes are being
|
|
|
|
|
run, and what cycle it's on somehow, all just by watching address and
|
|
|
|
|
data bus state. Not very practical.
|
|
|
|
|
|
|
|
|
|
The other problem is just the sheer volume of things it is supposed to do.
|
|
|
|
|
There's just tons and tons of unnecessary things like attempting to detect
|
|
|
|
|
BIT instructions, handling page wraps and other silly things.
|
|
|
|
|
|
|
|
|
|
This all supposidly fit into a Xilinx XC9536XL but I am not sure how the
|
|
|
|
|
chip could handle the RAM issue above at all. It almost needs to see R/W
|
|
|
|
|
and M2 (clock) to be able to properly do most of the things it's doing.
|
|
|
|
|
*/
|
|
|
|
|
|
2014-04-02 21:27:14 +00:00
|
|
|
|
internal class m4A50 : MapperBase
|
2012-03-31 20:53:14 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
private readonly ByteBuffer _myRam = new ByteBuffer(32768);
|
2012-03-31 20:53:14 +00:00
|
|
|
|
|
2014-04-03 19:58:47 +00:00
|
|
|
|
private int _myLastData = 0xFF;
|
|
|
|
|
private int _myLastAddress = 0xFFFF;
|
2013-02-24 20:17:12 +00:00
|
|
|
|
|
2014-04-03 19:58:47 +00:00
|
|
|
|
private bool _myIsRomHigh = true;
|
|
|
|
|
private bool _myIsRomLow = true;
|
|
|
|
|
private bool _myIsRomMiddle = true;
|
2013-02-24 20:17:12 +00:00
|
|
|
|
|
2014-04-03 19:58:47 +00:00
|
|
|
|
private int _mySliceHigh;
|
|
|
|
|
private int _mySliceLow;
|
|
|
|
|
private int _mySliceMiddle;
|
2013-03-11 01:46:12 +00:00
|
|
|
|
|
2012-10-20 00:14:28 +00:00
|
|
|
|
public override byte ReadMemory(ushort addr)
|
|
|
|
|
{
|
|
|
|
|
byte val = 0;
|
|
|
|
|
if (addr < 0x1000)
|
|
|
|
|
{
|
|
|
|
|
val = base.ReadMemory(addr);
|
2014-04-03 19:58:47 +00:00
|
|
|
|
CheckBankSwitch(addr, val);
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if ((addr & 0x1800) == 0x1000) // 2K region from 0x1000 - 0x17ff
|
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
val = _myIsRomLow ? core.rom[(addr & 0x7ff) + _mySliceLow]
|
|
|
|
|
: _myRam[(addr & 0x7ff) + _mySliceLow];
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
else if (((addr & 0x1fff) >= 0x1800) && // 1.5K region from 0x1800 - 0x1dff
|
|
|
|
|
((addr & 0x1fff) <= 0x1dff))
|
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
val = _myIsRomMiddle ? core.rom[(addr & 0x7ff) + _mySliceMiddle]
|
|
|
|
|
: _myRam[(addr & 0x7ff) + _mySliceMiddle];
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
else if ((addr & 0x1f00) == 0x1e00) // 256B region from 0x1e00 - 0x1eff
|
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
val = _myIsRomHigh ? core.rom[(addr & 0xff) + _mySliceHigh]
|
|
|
|
|
: _myRam[(addr & 0xff) + _mySliceHigh];
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
else if ((addr & 0x1f00) == 0x1f00) // 256B region from 0x1f00 - 0x1fff
|
|
|
|
|
{
|
|
|
|
|
val = core.rom[(addr & 0xff) + (core.rom.Length - 256)];
|
2014-04-03 19:58:47 +00:00
|
|
|
|
if (((_myLastData & 0xe0) == 0x60) && ((_myLastAddress >= 0x1000) ||
|
|
|
|
|
(_myLastAddress < 0x200)))
|
|
|
|
|
{
|
|
|
|
|
_mySliceHigh = (_mySliceHigh & 0xf0ff) | ((addr & 0x8) << 8) |
|
|
|
|
|
((addr & 0x70) << 4);
|
|
|
|
|
}
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myLastData = val;
|
|
|
|
|
_myLastAddress = addr & 0x1fff;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void WriteMemory(ushort addr, byte value)
|
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
if (addr < 0x1000) // Hotspots below 0x1000
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
|
|
|
|
base.WriteMemory(addr, value);
|
2014-04-03 19:58:47 +00:00
|
|
|
|
CheckBankSwitch(addr, value);
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
if (addr < 0x1800) // 2K region at 0x1000 - 0x17ff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
if (!_myIsRomLow)
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myRam[(addr & 0x7ff) + _mySliceLow] = value;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if (((addr & 0x1fff) >= 0x1800) && // 1.5K region at 0x1800 - 0x1dff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
((addr & 0x1fff) <= 0x1dff))
|
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
if (!_myIsRomMiddle)
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myRam[(addr & 0x7ff) + _mySliceMiddle] = value;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((addr & 0x1f00) == 0x1e00) // 256B region at 0x1e00 - 0x1eff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
if (!_myIsRomHigh)
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myRam[(addr & 0xff) + _mySliceHigh] = value;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((addr & 0x1f00) == 0x1f00) // 256B region at 0x1f00 - 0x1fff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
if (((_myLastData & 0xe0) == 0x60) &&
|
|
|
|
|
((_myLastAddress >= 0x1000) || (_myLastAddress < 0x200)))
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_mySliceHigh = (_mySliceHigh & 0xf0ff) | ((addr & 0x8) << 8) |
|
2012-10-20 00:14:28 +00:00
|
|
|
|
((addr & 0x70) << 4);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myLastData = value;
|
|
|
|
|
_myLastAddress = addr & 0x1fff;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-03 19:58:47 +00:00
|
|
|
|
private void CheckBankSwitch(ushort address, byte value)
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
if (((_myLastData & 0xe0) == 0x60) && // Switch lower/middle/upper bank
|
|
|
|
|
((_myLastAddress >= 0x1000) || (_myLastAddress < 0x200)))
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
if ((address & 0x0f00) == 0x0c00) // Enable 256B of ROM at 0x1e00 - 0x1eff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomHigh = true;
|
|
|
|
|
_mySliceHigh = (address & 0xff) << 8;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((address & 0x0f00) == 0x0d00) // Enable 256B of RAM at 0x1e00 - 0x1eff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomHigh = false;
|
|
|
|
|
_mySliceHigh = (address & 0x7f) << 8;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((address & 0x0f40) == 0x0e00) // Enable 2K of ROM at 0x1000 - 0x17ff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomLow = true;
|
|
|
|
|
_mySliceLow = (address & 0x1f) << 11;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((address & 0x0f40) == 0x0e40) // Enable 2K of RAM at 0x1000 - 0x17ff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomLow = false;
|
|
|
|
|
_mySliceLow = (address & 0xf) << 11;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((address & 0x0f40) == 0x0f00) // Enable 1.5K of ROM at 0x1800 - 0x1dff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomMiddle = true;
|
|
|
|
|
_mySliceMiddle = (address & 0x1f) << 11;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
else if ((address & 0x0f50) == 0x0f40) // Enable 1.5K of RAM at 0x1800 - 0x1dff
|
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomMiddle = false;
|
|
|
|
|
_mySliceMiddle = (address & 0xf) << 11;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((address & 0x0f00) == 0x0400) // Toggle bit A11 of lower block address
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_mySliceLow = _mySliceLow ^ 0x800;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((address & 0x0f00) == 0x0500) // Toggle bit A12 of lower block address
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_mySliceLow = _mySliceLow ^ 0x1000;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((address & 0x0f00) == 0x0800) // Toggle bit A11 of middle block address
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_mySliceMiddle = _mySliceMiddle ^ 0x800;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((address & 0x0f00) == 0x0900) // Toggle bit A12 of middle block address
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_mySliceMiddle = _mySliceMiddle ^ 0x1000;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Zero-page hotspots for upper page
|
2014-04-03 19:58:47 +00:00
|
|
|
|
// 0xf4, 0xf6, 0xfc, 0xfe for ROM
|
|
|
|
|
// 0xf5, 0xf7, 0xfd, 0xff for RAM
|
|
|
|
|
// 0x74 - 0x7f (0x80 bytes lower)
|
|
|
|
|
if ((address & 0xf75) == 0x74) // Enable 256B of ROM at 0x1e00 - 0x1eff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomHigh = true;
|
|
|
|
|
_mySliceHigh = value << 8;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((address & 0xf75) == 0x75) // Enable 256B of RAM at 0x1e00 - 0x1eff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomHigh = false;
|
|
|
|
|
_mySliceHigh = (value & 0x7f) << 8;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Zero-page hotspots for lower and middle blocks
|
2014-04-03 19:58:47 +00:00
|
|
|
|
// 0xf8, 0xf9, 0xfa, 0xfb
|
|
|
|
|
// 0x78, 0x79, 0x7a, 0x7b (0x80 bytes lower)
|
2012-10-20 00:14:28 +00:00
|
|
|
|
else if ((address & 0xf7c) == 0x78)
|
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
if ((value & 0xf0) == 0) // Enable 2K of ROM at 0x1000 - 0x17ff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomLow = true;
|
|
|
|
|
_mySliceLow = (value & 0xf) << 11;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((value & 0xf0) == 0x40) // Enable 2K of RAM at 0x1000 - 0x17ff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomLow = false;
|
|
|
|
|
_mySliceLow = (value & 0xf) << 11;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((value & 0xf0) == 0x90) // Enable 1.5K of ROM at 0x1800 - 0x1dff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomMiddle = true;
|
|
|
|
|
_mySliceMiddle = ((value & 0xf) | 0x10) << 11;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
2014-04-03 19:58:47 +00:00
|
|
|
|
else if ((value & 0xf0) == 0xc0) // Enable 1.5K of RAM at 0x1800 - 0x1dff
|
2012-10-20 00:14:28 +00:00
|
|
|
|
{
|
2014-04-03 19:58:47 +00:00
|
|
|
|
_myIsRomMiddle = false;
|
|
|
|
|
_mySliceMiddle = (value & 0xf) << 11;
|
2012-10-20 00:14:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-03-31 20:53:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|