-Made Intellicart its own class.

-Separated cartridge logic into a separate ICart named Cartridge.cs.
-Made WriteMemory return a bool to match ICart.Write. It currently returns true if either the cart or the core responded.

TODO: Parse the vanilla Intellivision ROM, which will hopefully include the read / writability of the data segments. adelikat seems to think that I just need to send the bytes to $5000, but I'm not convinced.
This commit is contained in:
brandman211 2012-07-31 06:54:20 +00:00
parent a5262488c7
commit 80a0f8f75b
7 changed files with 386 additions and 600 deletions

View File

@ -114,6 +114,7 @@
<Compile Include="Consoles\GB\Input.cs" />
<Compile Include="Consoles\GB\MemoryMap.cs" />
<Compile Include="Consoles\GB\GB.cs" />
<Compile Include="Consoles\Intellivision\Cartridge.cs" />
<Compile Include="Consoles\Intellivision\Intellicart.cs" />
<Compile Include="Consoles\Intellivision\Intellivision.cs" />
<Compile Include="Consoles\Intellivision\MemoryMap.cs" />

View File

@ -14,7 +14,7 @@ namespace BizHawk.Emulation.CPUs.CP1610
public int PendingCycles;
public Func<ushort, ushort> ReadMemory;
public Action<ushort, ushort> WriteMemory;
public Func<ushort, ushort, bool> WriteMemory;
private static bool logging = true;
private static StreamWriter log;

View File

@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Consoles.Intellivision
{
public sealed class Cartridge : ICart
{
private ushort[] Data = new ushort[56320];
public int Parse(byte[] Rom)
{
// TODO: Actually parse the ROM.
return 1;
}
public ushort? Read(ushort addr)
{
// TODO: Check if address is RAM / ROM.
switch (addr & 0xF000)
{
case 0x0000:
if (addr <= 0x03FF)
break;
if (addr <= 0x04FF)
// OK on all but Intellivision 2.
return Data[addr & 0x00FF];
else if (addr <= 0x06FF)
return Data[addr & 0x02FF];
else if (addr <= 0x0CFF)
// OK if no Intellivoice.
return Data[addr & 0x08FF];
else
return Data[addr & 0x0BFF];
case 0x2000:
// OK if no ECS.
return Data[(addr & 0x0FFF) + 0x0C00];
case 0x4000:
if (addr <= 0x47FF)
// OK if no ECS.
return Data[(addr & 0x07FF) + 0x1C00];
else if (addr == 0x4800)
return Data[0x2400];
else
return Data[(addr & 0x0FFF) + 0x1C00];
case 0x5000:
case 0x6000:
if (addr <= 0x5014)
return Data[(addr & 0x0014) + 0x2C00];
else
return Data[(addr & 0x1FFF) + 0x2C00];
case 0x7000:
if (addr == 0x7000)
// OK if no ECS.
return Data[0x04C00];
else if (addr <= 0x77FF)
// OK if no ECS.
return Data[(addr & 0x07FF) + 0x4C00];
else
// OK if no ECS.
return Data[(addr & 0x0FFF) + 0x4C00];
case 0x8000:
// OK. Avoid STIC alias at $8000-$803F.
return Data[(addr & 0x0FFF) + 0x5C00];
case 0x9000:
case 0xA000:
case 0xB000:
if (addr <= 0xB7FF)
return Data[(addr & 0x27FF) + 0x6C00];
else
return Data[(addr & 0x2FFF) + 0x6C00];
case 0xC000:
// OK. Avoid STIC alias at $C000-$C03F.
return Data[(addr & 0x0FFF) + 0x9C00];
case 0xD000:
return Data[(addr & 0x0FFF) + 0xAC00];
case 0xE000:
// OK if no ECS.
return Data[(addr & 0x0FFF) + 0xBC00];
case 0xF000:
if (addr <= 0xF7FF)
return Data[(addr & 0x07FF) + 0xCC00];
else
return Data[(addr & 0x0FFF) + 0xCC00];
}
return null;
}
public bool Write(ushort addr, ushort value)
{
// TODO: Check if address is RAM / ROM.
switch (addr & 0xF000)
{
case 0x0000:
if (addr <= 0x03FF)
break;
if (addr <= 0x04FF)
{
// OK on all but Intellivision 2.
Data[addr & 0x00FF] = value;
return true;
}
else if (addr <= 0x06FF)
{
Data[addr & 0x02FF] = value;
return true;
}
else if (addr <= 0x0CFF)
{
// OK if no Intellivoice.
Data[addr & 0x08FF] = value;
return true;
}
else
{
Data[addr & 0x0BFF] = value;
return true;
}
case 0x2000:
// OK if no ECS.
Data[(addr & 0x0FFF) + 0x0C00] = value;
return true;
case 0x4000:
if (addr <= 0x47FF)
{
// OK if no ECS.
Data[(addr & 0x07FF) + 0x1C00] = value;
return true;
}
else if (addr == 0x4800)
{
// OK only if boot ROM at $7000.
Data[0x2400] = value;
return true;
}
else
{
Data[(addr & 0x0FFF) + 0x1C00] = value;
return true;
}
case 0x5000:
case 0x6000:
if (addr <= 0x5014)
{
// OK only if boot ROM at $4800 or $7000.
Data[(addr & 0x0014) + 0x2C00] = value;
return true;
}
else
{
Data[(addr & 0x1FFF) + 0x2C00] = value;
return true;
}
case 0x7000:
if (addr == 0x7000)
{
// RAM at $7000 confuses EXEC boot sequence.
Data[0x04C00] = value;
return true;
}
else if (addr <= 0x77FF)
{
// OK if no ECS.
Data[(addr & 0x07FF) + 0x4C00] = value;
return true;
}
else
{
// Do not map RAM here due to GRAM alias.
Data[(addr & 0x0FFF) + 0x4C00] = value;
return true;
}
case 0x8000:
// OK. Avoid STIC alias at $8000-$803F.
Data[(addr & 0x0FFF) + 0x5C00] = value;
return true;
case 0x9000:
case 0xA000:
case 0xB000:
if (addr <= 0xB7FF)
{
Data[(addr & 0x27FF) + 0x6C00] = value;
return true;
}
else
{
// Do not map RAM here due to GRAM alias.
Data[(addr & 0x2FFF) + 0x6C00] = value;
return true;
}
case 0xC000:
// OK. Avoid STIC alias at $C000-$C03F.
Data[(addr & 0x0FFF) + 0x9C00] = value;
return true;
case 0xD000:
Data[(addr & 0x0FFF) + 0xAC00] = value;
return true;
case 0xE000:
// OK if no ECS.
Data[(addr & 0x0FFF) + 0xBC00] = value;
return true;
case 0xF000:
if (addr <= 0xF7FF)
{
Data[(addr & 0x07FF) + 0xCC00] = value;
return true;
}
else
{
// Do not map RAM here due to GRAM alias.
Data[(addr & 0x0FFF) + 0xCC00] = value;
return true;
}
}
return false;
}
}
}

View File

@ -5,9 +5,9 @@ using System.Text;
namespace BizHawk.Emulation.Consoles.Intellivision
{
public sealed partial class Intellivision : ICart
public sealed class Intellicart : ICart
{
private ushort[] Intellicart = new ushort[65536];
private ushort[] Data = new ushort[65536];
private bool[][] MemoryAttributes = new bool[32][];
private ushort[][] FineAddresses = new ushort[32][];
@ -52,12 +52,12 @@ namespace BizHawk.Emulation.Consoles.Intellivision
return (ushort)((crc << 8) ^ CRC16_table[(crc >> 8) ^ data]);
}
public int Parse()
public int Parse(byte[] Rom)
{
int offset = 0;
// Check to see if the header is valid.
if (Rom[offset++] != 0xA8 || Rom[offset++] != (0xFF ^ Rom[offset++]))
throw new ArgumentException();
return -1;
ushort crc, expected;
// Parse for data segments.
for (int segment = 0; segment < Rom[1]; segment++)
@ -69,9 +69,8 @@ namespace BizHawk.Emulation.Consoles.Intellivision
crc = UpdateCRC16(crc, upper_end);
ushort start = (ushort)(upper_start << 8);
ushort end = (ushort)((upper_end << 8) | 0xFF);
// This range is invalid if it starts at a higher range than it ends.
if (end < start)
throw new ArgumentException();
throw new ArgumentException("Ranges can't start higher than they end.");
for (int addr = start; addr <= end; addr++)
{
ushort data;
@ -80,12 +79,11 @@ namespace BizHawk.Emulation.Consoles.Intellivision
crc = UpdateCRC16(crc, high);
crc = UpdateCRC16(crc, low);
data = (ushort)((high << 8) | low);
Intellicart[addr] = data;
Data[addr] = data;
}
expected = (ushort)((Rom[offset++] << 8) | Rom[offset++]);
// Check if there is an invalid CRC.
if (expected != crc)
throw new ArgumentException();
throw new ArgumentException("Invalid CRC.");
}
// Parse for memory attributes.
for (int range = 0; range < 32; range++)
@ -117,9 +115,8 @@ namespace BizHawk.Emulation.Consoles.Intellivision
int range_start = range * 2048;
ushort start = (ushort)((((Rom[index] >> 4) & 0x07) << 8) + range_start);
ushort end = (ushort)((((Rom[index]) & 0x07) << 8) + 0xFF + range_start);
// This range is invalid if it starts at a higher range than it ends.
if (end < start)
throw new ArgumentException();
throw new ArgumentException("Ranges can't start higher than they end.");
FineAddresses[range] = new ushort[2];
FineAddresses[range][0] = start;
FineAddresses[range][1] = end;
@ -130,20 +127,20 @@ namespace BizHawk.Emulation.Consoles.Intellivision
expected = (ushort)((Rom[offset++] << 8) | (Rom[offset++] & 0xFF));
// Check if there is an invalid CRC for the memory attributes / fine addresses.
if (expected != crc)
throw new ArgumentException();
throw new ArgumentException("Invalid CRC.");
return offset;
}
public ushort? ReadCart(ushort addr)
public ushort? Read(ushort addr)
{
int range = addr / 2048;
bool[] attributes = MemoryAttributes[range];
if (attributes[0] && addr >= FineAddresses[range][0] && addr <= FineAddresses[range][1])
return Intellicart[addr];
return Data[addr];
return null;
}
public bool WriteCart(ushort addr, ushort value)
public bool Write(ushort addr, ushort value)
{
int range = addr / 2048;
bool[] attributes = MemoryAttributes[range];
@ -154,7 +151,7 @@ namespace BizHawk.Emulation.Consoles.Intellivision
value &= 0xFF;
if (attributes[3])
throw new NotImplementedException("Bank-switched memory attribute not implemented.");
Intellicart[addr] = value;
Data[addr] = value;
return true;
}
return false;

View File

@ -11,6 +11,7 @@ namespace BizHawk.Emulation.Consoles.Intellivision
GameInfo Game;
CP1610 Cpu;
ICart Cart;
public void LoadExecutive_ROM()
{
@ -42,7 +43,12 @@ namespace BizHawk.Emulation.Consoles.Intellivision
Game = game;
LoadExecutive_ROM();
LoadGraphics_ROM();
Parse();
Cart = new Intellicart();
if (Cart.Parse(Rom) == -1)
{
Cart = new Cartridge();
Cart.Parse(Rom);
}
Cpu = new CP1610();
Cpu.ReadMemory = ReadMemory;

View File

@ -8,17 +8,6 @@ namespace BizHawk.Emulation.Consoles.Intellivision
public sealed partial class Intellivision
{
private const string INVALID = "Invalid memory address.";
private const string UNOCCUPIED = "Unoccupied memory address.";
private const string UNOCCUPIED_CART_READ = "Unoccupied memory address available to cartridge not mapped as"
+ " readable.";
private const string UNOCCUPIED_CART_WRITE = "Unoccupied memory address available to cartridge not mapped as"
+ " writable.";
private const string READ_ONLY_STIC = "This STIC Register alias is read-only.";
private const string READ_ONLY_EXEC = "Executive ROM is read-only.";
private const string READ_ONLY_GROM = "Graphics ROM is read-only.";
private const string WRITE_ONLY_STIC = "This STIC Register alias is write-only.";
private const string GARBAGE = "Memory address contains a garbage value, perhaps only in the Intellivision II.";
private const string INTV2_EXEC = "Additional EXEC ROM, Intellivision II only.";
private ushort[] STIC_Registers = new ushort[64];
private ushort[] Scratchpad_RAM = new ushort[240];
@ -27,12 +16,11 @@ namespace BizHawk.Emulation.Consoles.Intellivision
private ushort[] Executive_ROM = new ushort[4096]; // TODO: Intellivision II support?
private ushort[] Graphics_ROM = new ushort[2048];
private ushort[] Graphics_RAM = new ushort[512];
private ushort[] Cartridge = new ushort[56320]; // TODO: Resize as cartridge mapping grows.
public ushort ReadMemory(ushort addr)
{
ushort? cart = ReadCart(addr);
ushort core;
ushort? cart = Cart.Read(addr);
ushort? core = null;
switch (addr & 0xF000)
{
case 0x0000:
@ -43,7 +31,8 @@ namespace BizHawk.Emulation.Consoles.Intellivision
// TODO: OK only during VBlank Period 2.
core = STIC_Registers[addr & 0x003F];
else if (addr <= 0x00FF)
throw new ArgumentException(UNOCCUPIED);
// Unoccupied.
break;
else if (addr <= 0x01EF)
core = Scratchpad_RAM[addr & 0x00EF];
else if (addr <= 0x01FF)
@ -51,44 +40,15 @@ namespace BizHawk.Emulation.Consoles.Intellivision
else if (addr <= 0x035F)
core = System_RAM[addr & 0x015F];
else if (addr <= 0x03FF)
// TODO: Intellivision II support?
throw new ArgumentException(GARBAGE);
// TODO: Garbage values for Intellivision II.
break;
else if (addr <= 0x04FF)
// TODO: Actually map cartridge (on all but Intellivision II) RAM / ROM to decide which path to take.
if (false)
// TODO: Intellivision II support?
throw new ArgumentException(INTV2_EXEC);
else
core = Cartridge[addr & 0x00FF];
else if (addr <= 0x06FF)
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[addr & 0x02FF];
else if (addr <= 0x0CFF)
// TODO: Actually map cartridge (only if no Intellivoice) RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[addr & 0x08FF];
else
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[addr & 0x0BFF];
// TODO: Additional EXEC ROM for Intellivision II.
break;
break;
case 0x1000:
core = Executive_ROM[addr & 0x0FFF];
break;
case 0x2000:
// TODO: Actually map cartridge (only if no ECS) RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0FFF) + 0x0C00];
break;
case 0x3000:
if (addr <= 0x37FF)
// TODO: OK only during VBlank Period 2.
@ -109,667 +69,269 @@ namespace BizHawk.Emulation.Consoles.Intellivision
case 0x4000:
if (addr <= 0x403F)
{
// TODO: Actually map cartridge (only if no ECS) RAM / ROM to decide which path to take.
if (true)
{
if (addr == 0x4021)
// TODO: OK only during VBlank Period 1.
if (addr == 0x4021)
// TODO: Switch into Color Stack mode.
core = STIC_Registers[0x0021];
else
throw new ArgumentException(WRITE_ONLY_STIC);
}
else
core = Cartridge[(addr & 0x003F) + 0x1C00];
}
else if (addr <= 0x47FF)
{
// TODO: Actually map cartridge (only if no ECS) RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x07FF) + 0x1C00];
}
else if (addr == 0x4800)
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[0x2400];
}
else
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0FFF) + 0x1C00];
}
break;
case 0x5000:
case 0x6000:
if (addr <= 0x5014)
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0014) + 0x2C00];
}
else
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x1FFF) + 0x2C00];
// TODO: Switch into Color Stack mode.
core = STIC_Registers[0x0021];
}
break;
case 0x7000:
if (addr == 0x7000)
{
/*
TODO: Actually map cartridge (only if no ECS) RAM (confuses EXEC boot sequence) / ROM to decide
which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[0x04C00];
}
else if (addr <= 0x77FF)
{
// TODO: Actually map cartridge (only if no ECS) RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x07FF) + 0x4C00];
}
if (addr <= 0x77FF)
// Available to cartridges.
break;
else if (addr <= 0x79FF)
{
/*
TODO: Actually map cartridge (only if no ECS) RAM (Do not because of GRAM alias) / ROM to decide
which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x09FF) + 0x4C00];
}
// Write-only Graphics RAM.
break;
else if (addr <= 0x7BFF)
{
/*
TODO: Actually map cartridge (only if no ECS) RAM (Do not because of GRAM alias) / ROM to decide
which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0BFF) + 0x4C00];
}
// Write-only Graphics RAM.
break;
else if (addr <= 0x7DFF)
{
/*
TODO: Actually map cartridge (only if no ECS) RAM (Do not because of GRAM alias) / ROM to decide
which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0DFF) + 0x4C00];
}
// Write-only Graphics RAM.
break;
else
{
/*
TODO: Actually map cartridge (only if no ECS) RAM (Do not because of GRAM alias) / ROM to decide
which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0FFF) + 0x4C00];
}
break;
// Write-only Graphics RAM.
break;
case 0x8000:
if (addr <= 0x803F)
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (true)
{
if (addr == 0x8021)
// TODO: OK only during VBlank Period 1.
if (addr == 0x8021)
// TODO: Switch into Color Stack mode.
core = STIC_Registers[0x0021];
else
throw new ArgumentException(WRITE_ONLY_STIC);
}
else
core = Cartridge[(addr & 0x003F) + 0x5C00];
}
else
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0FFF) + 0x5C00];
// TODO: Switch into Color Stack mode.
core = STIC_Registers[0x0021];
}
break;
case 0x9000:
case 0xA000:
case 0xB000:
if (addr <= 0xB7FF)
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x27FF) + 0x6C00];
}
// Available to cartridges.
break;
else if (addr <= 0xB9FF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x29FF) + 0x6C00];
}
// Write-only Graphics RAM.
break;
else if (addr <= 0xBBFF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x2BFF) + 0x6C00];
}
// Write-only Graphics RAM.
break;
else if (addr <= 0xBDFF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x2DFF) + 0x6C00];
}
// Write-only Graphics RAM.
break;
else
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x2FFF) + 0x6C00];
}
break;
// Write-only Graphics RAM.
break;
case 0xC000:
if (addr <= 0xC03F)
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (true)
{
if (addr == 0xC021)
// TODO: OK only during VBlank Period 1.
if (addr == 0xC021)
// TODO: Switch into Color Stack mode.
core = STIC_Registers[0x0021];
else
throw new ArgumentException(WRITE_ONLY_STIC);
}
else
core = Cartridge[(addr & 0x003F) + 0x9C00];
// TODO: Switch into Color Stack mode.
core = STIC_Registers[0x0021];
}
else
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0FFF) + 0x9C00];
}
break;
case 0xD000:
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0FFF) + 0xAC00];
break;
case 0xE000:
// TODO: Actually map cartridge (only if no ECS) RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0FFF) + 0xBC00];
break;
case 0xF000:
if (addr <= 0xF7FF)
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x07FF) + 0xCC00];
}
// Available to cartridges.
break;
else if (addr <= 0xF9FF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x09FF) + 0xCC00];
}
// Write-only Graphics RAM.
break;
else if (addr <= 0xFBFF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0BFF) + 0xCC00];
}
// Write-only Graphics RAM.
break;
else if (addr <= 0xFDFF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0DFF) + 0xCC00];
}
// Write-only Graphics RAM.
break;
else
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_READ);
else
core = Cartridge[(addr & 0x0FFF) + 0xCC00];
}
break;
default:
throw new ArgumentException(INVALID);
// Write-only Graphics RAM.
break;
}
/*
TODO: Fix Intellicart hook.
if (cart != null)
return (ushort)cart;
*/
return core;
else if (core == null)
throw new ArgumentException(INVALID);
return (ushort)core;
}
public void WriteMemory(ushort addr, ushort value)
public bool WriteMemory(ushort addr, ushort value)
{
WriteCart(addr, value);
bool cart = Cart.Write(addr, value);
switch (addr & 0xF000)
{
case 0x0000:
if (addr <= 0x003F)
{
// TODO: OK only during VBlank Period 1.
STIC_Registers[addr] = value;
return true;
}
else if (addr <= 0x007F)
throw new ArgumentException(READ_ONLY_STIC);
// Read-only STIC.
break;
else if (addr <= 0x00FF)
throw new ArgumentException(UNOCCUPIED);
// Unoccupied.
break;
else if (addr <= 0x01EF)
{
Scratchpad_RAM[addr & 0x00EF] = value;
return true;
}
else if (addr <= 0x01FF)
{
PSG_Registers[addr & 0x000F] = value;
return true;
}
else if (addr <= 0x035F)
{
System_RAM[addr & 0x015F] = value;
return true;
}
else if (addr <= 0x03FF)
// TODO: Intellivision II support?
throw new ArgumentException(GARBAGE);
// Read-only garbage values for Intellivision II.
break;
else if (addr <= 0x04FF)
// TODO: Actually map cartridge (on all but Intellivision II) RAM / ROM to decide which path to take.
if (false)
// TODO: Intellivision II support?
throw new ArgumentException(INTV2_EXEC);
else
Cartridge[addr & 0x00FF] = value;
else if (addr <= 0x06FF)
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[addr & 0x02FF] = value;
else if (addr <= 0x0CFF)
// TODO: Actually map cartridge (only if no Intellivoice) RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[addr & 0x08FF] = value;
else
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[addr & 0x0BFF] = value;
// Read-only additional EXEC ROM for Intellivision II.
break;
break;
case 0x1000:
throw new ArgumentException(READ_ONLY_EXEC);
case 0x2000:
// TODO: Actually map cartridge (only if no ECS) RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x0FFF) + 0x0C00] = value;
// Read-only Executive ROM.
break;
case 0x3000:
if (addr <= 0x37FF)
throw new ArgumentException(READ_ONLY_GROM);
// Read-only Graphics ROM.
break;
else if (addr <= 0x39FF)
{
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else if (addr <= 0x3BFF)
{
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else if (addr <= 0x3DFF)
{
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else
{
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
break;
return true;
}
case 0x4000:
if (addr <= 0x403F)
{
// TODO: Actually map cartridge (only if no ECS) RAM / ROM to decide which path to take.
if (true)
// TODO: OK only during VBlank Period 1.
STIC_Registers[addr & 0x003F] = value;
else
Cartridge[(addr & 0x003F) + 0x1C00] = value;
}
else if (addr <= 0x47FF)
{
// TODO: Actually map cartridge (only if no ECS) RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x07FF) + 0x1C00] = value;
}
else if (addr == 0x4800)
{
// TODO: Actually map cartridge (only if boot ROM at $7000) RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[0x2400] = value;
}
else
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x0FFF) + 0x1C00] = value;
}
break;
case 0x5000:
case 0x6000:
if (addr <= 0x5014)
{
/*
TODO: Actually map (only if boot ROM at $4800 or $7000) cartridge RAM / ROM to decide which path to
take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x0014) + 0x2C00] = value;
}
else
{
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x1FFF) + 0x2C00] = value;
// TODO: OK only during VBlank Period 1.
STIC_Registers[addr & 0x003F] = value;
return true;
}
break;
case 0x7000:
if (addr == 0x7000)
{
/*
TODO: Actually map cartridge (only if no ECS) RAM (confuses EXEC boot sequence) / ROM to decide
which path to take.
*/
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[0x04C00] = value;
}
else if (addr <= 0x77FF)
{
// TODO: Actually map cartridge (only if no ECS) RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x07FF) + 0x4C00] = value;
}
if (addr <= 0x77FF)
// Available to cartridges.
break;
else if (addr <= 0x79FF)
{
/*
TODO: Actually map cartridge (only if no ECS) RAM (Do not because of GRAM alias) / ROM to decide
which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x09FF) + 0x4C00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else if (addr <= 0x7BFF)
{
/*
TODO: Actually map cartridge (only if no ECS) RAM (Do not because of GRAM alias) / ROM to decide
which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x0BFF) + 0x4C00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else if (addr <= 0x7DFF)
{
/*
TODO: Actually map cartridge (only if no ECS) RAM (Do not because of GRAM alias) / ROM to decide
which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x0DFF) + 0x4C00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else
{
/*
TODO: Actually map cartridge (only if no ECS) RAM (Do not because of GRAM alias) / ROM to decide
which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x0FFF) + 0x4C00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
break;
case 0x8000:
if (addr <= 0x803F)
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (true)
// TODO: OK only during VBlank Period 1.
STIC_Registers[addr & 0x003F] = value;
else
Cartridge[(addr & 0x003F) + 0x5C00] = value;
}
else
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x0FFF) + 0x5C00] = value;
// TODO: OK only during VBlank Period 1.
STIC_Registers[addr & 0x003F] = value;
return true;
}
break;
case 0x9000:
case 0xA000:
case 0xB000:
if (addr <= 0xB7FF)
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x27FF) + 0x6C00] = value;
}
// Available to cartridges.
break;
else if (addr <= 0xB9FF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x29FF) + 0x6C00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else if (addr <= 0xBBFF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x2BFF) + 0x6C00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else if (addr <= 0xBDFF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x2DFF) + 0x6C00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x2FFF) + 0x6C00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
break;
case 0xC000:
if (addr <= 0x803F)
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (true)
// TODO: OK only during VBlank Period 1.
STIC_Registers[addr & 0x003F] = value;
else
Cartridge[(addr & 0x003F) + 0x9C00] = value;
// TODO: OK only during VBlank Period 1.
STIC_Registers[addr & 0x003F] = value;
return true;
}
else
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x0FFF) + 0x9C00] = value;
}
break;
case 0xD000:
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x0FFF) + 0xAC00] = value;
break;
case 0xE000:
// TODO: Actually map cartridge (only if no ECS) RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x0FFF) + 0xBC00] = value;
break;
case 0xF000:
if (addr <= 0xF7FF)
{
// TODO: Actually map cartridge RAM / ROM to decide which path to take.
if (false)
throw new ArgumentException(UNOCCUPIED_CART_WRITE);
else
Cartridge[(addr & 0x07FF) + 0xCC00] = value;
}
// Available to cartridges.
break;
else if (addr <= 0xF9FF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x09FF) + 0xCC00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else if (addr <= 0xFBFF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x0BFF) + 0xCC00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else if (addr <= 0xFDFF)
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x0DFF) + 0xCC00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
else
{
/*
TODO: Actually map cartridge RAM (Do not because of GRAM alias) / ROM to decide which path to take.
*/
if (true)
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
else
Cartridge[(addr & 0x0FFF) + 0xCC00] = value;
// TODO: OK only during VBlank Period 2.
Graphics_RAM[addr & 0x01FF] = value;
return true;
}
break;
default:
throw new ArgumentException(INVALID);
}
return cart;
}
}
}

View File

@ -7,7 +7,8 @@ namespace BizHawk
{
public interface ICart
{
ushort? ReadCart(ushort addr);
bool WriteCart(ushort addr, ushort value);
int Parse(byte[] Rom);
ushort? Read(ushort addr);
bool Write(ushort addr, ushort value);
}
}