2016-11-04 00:49:19 +00:00
|
|
|
|
using System;
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Cores.Sega.MasterSystem
|
2011-01-11 02:55:51 +00:00
|
|
|
|
{
|
2012-10-13 20:15:28 +00:00
|
|
|
|
public partial class SMS
|
|
|
|
|
{
|
|
|
|
|
// The Sega memory mapper layout looks like so:
|
|
|
|
|
// $0000-$03FF - ROM (unpaged)
|
|
|
|
|
// $0400-$3FFF - ROM mapper slot 0
|
|
|
|
|
// $4000-$7FFF - ROM mapper slot 1
|
|
|
|
|
// $8000-$BFFF - ROM mapper slot 2 - OR - SaveRAM
|
|
|
|
|
// $C000-$DFFF - System RAM
|
|
|
|
|
// $E000-$FFFF - System RAM (mirror)
|
|
|
|
|
// $FFFC - SaveRAM mapper control
|
|
|
|
|
// $FFFD - Mapper slot 0 control
|
|
|
|
|
// $FFFE - Mapper slot 1 control
|
|
|
|
|
// $FFFF - Mapper slot 2 control
|
|
|
|
|
|
|
|
|
|
const ushort BankSizeMask = 0x3FFF;
|
|
|
|
|
const ushort RamSizeMask = 0x1FFF;
|
|
|
|
|
|
2014-03-05 05:09:20 +00:00
|
|
|
|
bool BiosMapped { get { return (Port3E & 0x40) == 0x40; } }
|
|
|
|
|
|
2015-10-30 05:00:57 +00:00
|
|
|
|
byte ReadMemorySega(ushort address)
|
2012-10-13 20:15:28 +00:00
|
|
|
|
{
|
2014-03-22 04:46:01 +00:00
|
|
|
|
byte ret = 0xFF;
|
2014-03-05 05:09:20 +00:00
|
|
|
|
|
|
|
|
|
if (address < 0xC000)
|
2012-10-13 20:15:28 +00:00
|
|
|
|
{
|
2014-03-05 05:09:20 +00:00
|
|
|
|
if ((Port3E & 0x48) == 0x48) // cart and bios disabled, return empty bus
|
|
|
|
|
ret = 0xFF;
|
|
|
|
|
else if (BiosMapped && BiosRom != null)
|
|
|
|
|
ret = BiosRom[address & 0x1FFF];
|
|
|
|
|
else if (address < 1024)
|
|
|
|
|
ret = RomData[address];
|
|
|
|
|
else if (address < 0x4000)
|
|
|
|
|
ret = RomData[(RomBank0 * BankSize) + address];
|
|
|
|
|
else if (address < 0x8000)
|
|
|
|
|
ret = RomData[(RomBank1 * BankSize) + (address & BankSizeMask)];
|
|
|
|
|
else
|
2012-10-13 20:15:28 +00:00
|
|
|
|
{
|
2014-03-05 05:09:20 +00:00
|
|
|
|
switch (SaveRamBank)
|
|
|
|
|
{
|
|
|
|
|
case 0: ret = RomData[(RomBank2 * BankSize) + (address & BankSizeMask)]; break;
|
2014-03-22 04:46:01 +00:00
|
|
|
|
case 1: if (SaveRAM != null) ret = SaveRAM[(address & BankSizeMask) % SaveRAM.Length]; break;
|
2016-11-04 00:49:19 +00:00
|
|
|
|
case 2: if (SaveRAM != null) ret = SaveRAM[(BankSize + (address & BankSizeMask)) % SaveRAM.Length]; break;
|
2014-03-05 05:09:20 +00:00
|
|
|
|
default:
|
|
|
|
|
ret = SystemRam[address & RamSizeMask];
|
|
|
|
|
break;
|
|
|
|
|
}
|
2012-10-13 20:15:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-10-13 20:39:13 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ret = SystemRam[address & RamSizeMask];
|
|
|
|
|
}
|
2012-10-13 20:15:28 +00:00
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-30 05:00:57 +00:00
|
|
|
|
CDLog_MapResults MapMemorySega(ushort address, bool write)
|
|
|
|
|
{
|
|
|
|
|
if (address < 0xC000)
|
|
|
|
|
{
|
|
|
|
|
if ((Port3E & 0x48) == 0x48) // cart and bios disabled, return empty bus
|
|
|
|
|
return new CDLog_MapResults();
|
|
|
|
|
else if (BiosMapped && BiosRom != null)
|
|
|
|
|
return new CDLog_MapResults(); //bios tracking of CDL is not supported
|
|
|
|
|
else if (address < 1024)
|
|
|
|
|
return new CDLog_MapResults() { Type = CDLog_AddrType.ROM, Address = address };
|
|
|
|
|
else if (address < 0x4000)
|
|
|
|
|
return new CDLog_MapResults() { Type = CDLog_AddrType.ROM, Address = (RomBank0 * BankSize) + address };
|
|
|
|
|
else if (address < 0x8000)
|
2015-11-01 16:44:36 +00:00
|
|
|
|
return new CDLog_MapResults() { Type = CDLog_AddrType.ROM, Address = (RomBank1 * BankSize) + (address & BankSizeMask) };
|
2015-10-30 05:00:57 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
switch (SaveRamBank)
|
|
|
|
|
{
|
|
|
|
|
case 0: return new CDLog_MapResults() { Type = CDLog_AddrType.ROM, Address = (RomBank2 * BankSize) + (address & BankSizeMask) };
|
|
|
|
|
case 1:
|
|
|
|
|
if (SaveRAM != null) return new CDLog_MapResults() { Type = CDLog_AddrType.SaveRAM, Address = (address & BankSizeMask) % SaveRAM.Length };
|
|
|
|
|
else return new CDLog_MapResults();
|
|
|
|
|
case 2:
|
|
|
|
|
if (SaveRAM != null) return new CDLog_MapResults() { Type = CDLog_AddrType.SaveRAM, Address = (BankSize + (address & BankSizeMask)) & BankSizeMask };
|
|
|
|
|
else return new CDLog_MapResults();
|
|
|
|
|
default:
|
|
|
|
|
return new CDLog_MapResults() { Type = CDLog_AddrType.MainRAM, Address = address & RamSizeMask };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return new CDLog_MapResults() { Type = CDLog_AddrType.MainRAM, Address = address & RamSizeMask };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-01 16:44:36 +00:00
|
|
|
|
void WriteMemorySega(ushort address, byte value)
|
|
|
|
|
{
|
|
|
|
|
if (address >= 0xC000)
|
|
|
|
|
SystemRam[address & RamSizeMask] = value;
|
|
|
|
|
|
|
|
|
|
else if (address >= 0x8000)
|
|
|
|
|
{
|
|
|
|
|
if (SaveRAM != null)
|
|
|
|
|
{
|
|
|
|
|
SaveRamModified = true;
|
|
|
|
|
switch (SaveRamBank)
|
|
|
|
|
{
|
|
|
|
|
case 1: SaveRAM[(address & BankSizeMask) % SaveRAM.Length] = value; return;
|
2016-11-04 00:49:19 +00:00
|
|
|
|
case 2: SaveRAM[(BankSize + (address & BankSizeMask)) % SaveRAM.Length] = value; return;
|
2015-11-01 16:44:36 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else System.Console.WriteLine("Game attempt to use SRAM but SRAM not present");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (address >= 0xFFFC)
|
|
|
|
|
{
|
|
|
|
|
if (address == 0xFFFC)
|
|
|
|
|
{
|
|
|
|
|
if ((value & 8) != 0)
|
|
|
|
|
SaveRamBank = (byte)((value & 4) == 0 ? 1 : 2); // SaveRAM selected
|
|
|
|
|
else
|
|
|
|
|
SaveRamBank = 0; // ROM bank selected
|
2016-11-04 00:49:19 +00:00
|
|
|
|
|
2015-11-01 16:44:36 +00:00
|
|
|
|
}
|
|
|
|
|
else if (address == 0xFFFD) RomBank0 = (byte)(value % RomBanks);
|
|
|
|
|
else if (address == 0xFFFE) RomBank1 = (byte)(value % RomBanks);
|
|
|
|
|
else if (address == 0xFFFF) RomBank2 = (byte)(value % RomBanks);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-13 20:15:28 +00:00
|
|
|
|
void InitSegaMapper()
|
|
|
|
|
{
|
2015-10-30 05:00:57 +00:00
|
|
|
|
ReadMemory = ReadMemorySega;
|
|
|
|
|
WriteMemory = WriteMemorySega;
|
|
|
|
|
MapMemory = MapMemorySega;
|
|
|
|
|
WriteMemorySega(0xFFFC, 0);
|
|
|
|
|
WriteMemorySega(0xFFFD, 0);
|
|
|
|
|
WriteMemorySega(0xFFFE, 1);
|
|
|
|
|
WriteMemorySega(0xFFFF, 2);
|
2012-10-13 20:15:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-05 05:09:20 +00:00
|
|
|
|
// Mapper when loading a BIOS as a ROM (simulating no cart loaded)
|
2012-10-13 20:15:28 +00:00
|
|
|
|
|
|
|
|
|
byte ReadMemoryBIOS(ushort address)
|
|
|
|
|
{
|
2014-03-05 05:09:20 +00:00
|
|
|
|
if ((Port3E & 0x08) != 0 && address < 0xC000)
|
|
|
|
|
return 0xFF;
|
2012-10-13 20:15:28 +00:00
|
|
|
|
|
|
|
|
|
if (address < 1024)
|
|
|
|
|
return RomData[address];
|
2014-03-05 05:09:20 +00:00
|
|
|
|
if (address < 0x4000)
|
2012-10-13 20:15:28 +00:00
|
|
|
|
return RomData[(RomBank0 * BankSize) + address];
|
2014-03-05 05:09:20 +00:00
|
|
|
|
if (address < 0x8000)
|
2012-10-13 20:15:28 +00:00
|
|
|
|
return RomData[(RomBank1 * BankSize) + (address & BankSizeMask)];
|
2014-03-05 05:09:20 +00:00
|
|
|
|
if (address < 0xC000)
|
2012-10-13 20:15:28 +00:00
|
|
|
|
return RomData[(RomBank2 * BankSize) + (address & BankSizeMask)];
|
|
|
|
|
|
|
|
|
|
return SystemRam[address & RamSizeMask];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WriteMemoryBIOS(ushort address, byte value)
|
|
|
|
|
{
|
|
|
|
|
if (address >= 0xC000)
|
|
|
|
|
SystemRam[address & RamSizeMask] = value;
|
|
|
|
|
|
|
|
|
|
if (address >= 0xFFFC)
|
|
|
|
|
{
|
|
|
|
|
if (address == 0xFFFD) RomBank0 = (byte)(value % RomBanks);
|
|
|
|
|
else if (address == 0xFFFE) RomBank1 = (byte)(value % RomBanks);
|
|
|
|
|
else if (address == 0xFFFF) RomBank2 = (byte)(value % RomBanks);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InitBiosMapper()
|
|
|
|
|
{
|
2015-10-30 05:00:57 +00:00
|
|
|
|
ReadMemory = ReadMemoryBIOS;
|
|
|
|
|
WriteMemory = WriteMemoryBIOS;
|
|
|
|
|
WriteMemorySega(0xFFFC, 0);
|
|
|
|
|
WriteMemorySega(0xFFFD, 0);
|
|
|
|
|
WriteMemorySega(0xFFFE, 1);
|
|
|
|
|
WriteMemorySega(0xFFFF, 2);
|
2012-10-13 20:15:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-05 17:45:01 +00:00
|
|
|
|
}
|