304 lines
6.2 KiB
C#
304 lines
6.2 KiB
C#
using System.Collections.Generic;
|
|
using BizHawk.Common;
|
|
using BizHawk.Common.NumberExtensions;
|
|
|
|
// https://wiki.nesdev.com/w/index.php/INES_Mapper_105
|
|
namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|
{
|
|
//AKA mapper 105
|
|
internal sealed class NES_EVENT : NesBoardBase
|
|
{
|
|
//configuration
|
|
int prg_bank_mask_16k;
|
|
|
|
//regenerable state
|
|
int[] prg_banks_16k = new int[2];
|
|
|
|
//state
|
|
MMC1.MMC1_SerialController scnt;
|
|
bool c000_swappable, prg_32k_mode;
|
|
bool irq_enable;
|
|
int prg_a, prg_b;
|
|
int init_sequence;
|
|
bool chip_select;
|
|
bool wram_disable;
|
|
|
|
int irq_count;
|
|
int irq_destination;
|
|
bool irq_pending;
|
|
|
|
[MapperProp]
|
|
public bool Dipswitch1 = false;
|
|
|
|
[MapperProp]
|
|
public bool Dipswitch2 = true;
|
|
|
|
[MapperProp]
|
|
public bool Dipswitch3 = false;
|
|
|
|
[MapperProp]
|
|
public bool Dipswitch4 = false;
|
|
|
|
private List<bool> Switches =>
|
|
new List<bool>
|
|
{
|
|
{ Dipswitch1 },
|
|
{ Dipswitch2 },
|
|
{ Dipswitch3 },
|
|
{ Dipswitch4 }
|
|
};
|
|
|
|
public int IrqDestination
|
|
{
|
|
get
|
|
{
|
|
SyncIRQDestination();
|
|
return irq_destination;
|
|
}
|
|
}
|
|
|
|
private void SyncIRQDestination()
|
|
{
|
|
//0b001D_CBAx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx
|
|
|
|
int val = 0;
|
|
for (int i = 0; i < Switches.Count; i++)
|
|
{
|
|
val <<= 1;
|
|
if (Switches[i])
|
|
{
|
|
val |= 1;
|
|
}
|
|
}
|
|
|
|
irq_destination = 0x20000000 | (val << 25);
|
|
}
|
|
|
|
public override void SyncState(Serializer ser)
|
|
{
|
|
base.SyncState(ser);
|
|
|
|
scnt.SyncState(ser);
|
|
ser.Sync(nameof(c000_swappable), ref c000_swappable);
|
|
ser.Sync("prg_16k_mode", ref prg_32k_mode);
|
|
ser.Sync(nameof(irq_enable), ref irq_enable);
|
|
ser.Sync(nameof(irq_pending), ref irq_pending);
|
|
ser.Sync(nameof(irq_count), ref irq_count);
|
|
ser.Sync(nameof(prg_a), ref prg_a);
|
|
ser.Sync(nameof(prg_b), ref prg_b);
|
|
ser.Sync(nameof(init_sequence), ref init_sequence);
|
|
ser.Sync(nameof(chip_select), ref chip_select);
|
|
ser.Sync(nameof(wram_disable), ref wram_disable);
|
|
ser.Sync(nameof(prg_banks_16k), ref prg_banks_16k, false);
|
|
if (ser.IsReader) Sync();
|
|
}
|
|
|
|
public override bool Configure(EDetectionOrigin origin)
|
|
{
|
|
switch (Cart.BoardType)
|
|
{
|
|
case "MAPPER105":
|
|
break;
|
|
case "NES-EVENT":
|
|
AssertPrg(256); AssertChr(0); AssertVram(8); AssertWram(8);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
prg_bank_mask_16k = Cart.PrgSize / 16 - 1;
|
|
init_sequence = 0;
|
|
|
|
SetMirrorType(EMirrorType.Vertical);
|
|
|
|
scnt = new MMC1.MMC1_SerialController
|
|
{
|
|
WriteRegister = SerialWriteRegister,
|
|
Reset = SerialReset
|
|
};
|
|
|
|
InitValues();
|
|
|
|
return true;
|
|
}
|
|
|
|
void SerialReset()
|
|
{
|
|
prg_32k_mode = true;
|
|
c000_swappable = true;
|
|
}
|
|
|
|
void Sync()
|
|
{
|
|
SyncIRQDestination();
|
|
SyncIRQ();
|
|
|
|
if (init_sequence != 2)
|
|
{
|
|
// prg banks locked to first 32k of first 128k chip
|
|
prg_banks_16k[0] = 0;
|
|
prg_banks_16k[1] = 1;
|
|
}
|
|
else
|
|
{
|
|
if (!chip_select)
|
|
{
|
|
//use prg banks in first 128k as indicated by prg_a reg
|
|
prg_banks_16k[0] = prg_a * 2;
|
|
prg_banks_16k[1] = prg_a * 2 + 1;
|
|
}
|
|
else
|
|
{
|
|
if (!prg_32k_mode)
|
|
{
|
|
//use prg banks in second 128k (add 8*16k as offset) in 32k mode, as determined by prg_b reg
|
|
prg_banks_16k[0] = ((prg_b & ~1) & 7) + 8;
|
|
prg_banks_16k[1] = ((prg_b & ~1) & 7) + 9;
|
|
}
|
|
else
|
|
{
|
|
//((these arent tested, i think...))
|
|
//"use second 128k"
|
|
if (!c000_swappable)
|
|
{
|
|
prg_banks_16k[0] = 8;
|
|
prg_banks_16k[1] = prg_b + 8;
|
|
}
|
|
else
|
|
{
|
|
prg_banks_16k[0] = prg_b + 8;
|
|
prg_banks_16k[1] = 15; //last bank of second 128k
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
prg_banks_16k[0] &= prg_bank_mask_16k;
|
|
prg_banks_16k[1] &= prg_bank_mask_16k;
|
|
}
|
|
|
|
void SerialWriteRegister(int addr, int value)
|
|
{
|
|
switch (addr)
|
|
{
|
|
case 0: //8000-9FFF
|
|
switch (value & 3)
|
|
{
|
|
case 0: SetMirrorType(EMirrorType.OneScreenA); break;
|
|
case 1: SetMirrorType(EMirrorType.OneScreenB); break;
|
|
case 2: SetMirrorType(EMirrorType.Vertical); break;
|
|
case 3: SetMirrorType(EMirrorType.Horizontal); break;
|
|
}
|
|
c000_swappable = value.Bit(2);
|
|
prg_32k_mode = value.Bit(3);
|
|
Sync();
|
|
break;
|
|
case 1: //A000-BFFF
|
|
{
|
|
irq_enable = !value.Bit(4);
|
|
|
|
//Acknowledge IRQ
|
|
if (!irq_enable)
|
|
{
|
|
irq_count = 0;
|
|
irq_pending = false;
|
|
}
|
|
|
|
if (init_sequence == 0 && irq_enable)
|
|
{
|
|
init_sequence = 1;
|
|
}
|
|
else if (init_sequence == 1 && !irq_enable)
|
|
{
|
|
init_sequence = 2;
|
|
}
|
|
|
|
chip_select = value.Bit(3);
|
|
prg_a = (value >> 1) & 3;
|
|
Sync();
|
|
break;
|
|
}
|
|
case 2: //C000-DFFF
|
|
//unused
|
|
break;
|
|
case 3: //E000-FFFF
|
|
prg_b = value & 0xF;
|
|
wram_disable = value.Bit(4);
|
|
Sync();
|
|
break;
|
|
}
|
|
//board.NES.LogLine("mapping.. chr_mode={0}, chr={1},{2}", chr_mode, chr_0, chr_1);
|
|
//board.NES.LogLine("mapping.. prg_mode={0}, prg_slot{1}, prg={2}", prg_mode, prg_slot, prg);
|
|
}
|
|
|
|
public override void WriteWram(int addr, byte value)
|
|
{
|
|
if (!wram_disable)
|
|
{
|
|
base.WriteWram(addr, value);
|
|
}
|
|
}
|
|
|
|
public override byte ReadWram(int addr)
|
|
{
|
|
return wram_disable ? NES.DB : base.ReadWram(addr);
|
|
}
|
|
|
|
public override void NesSoftReset()
|
|
{
|
|
InitValues();
|
|
base.NesSoftReset();
|
|
}
|
|
|
|
private void InitValues()
|
|
{
|
|
AutoMapperProps.Apply(this);
|
|
|
|
irq_enable = false;
|
|
init_sequence = 0;
|
|
irq_count = 0;
|
|
|
|
Sync();
|
|
}
|
|
|
|
public override void WritePrg(int addr, byte value)
|
|
{
|
|
scnt.Write(addr, value);
|
|
}
|
|
|
|
|
|
public override byte ReadPrg(int addr)
|
|
{
|
|
int bank_16k = addr >> 14;
|
|
int ofs = addr & ((1 << 14) - 1);
|
|
bank_16k = prg_banks_16k[bank_16k];
|
|
addr = (bank_16k << 14) | ofs;
|
|
return Rom[addr];
|
|
}
|
|
|
|
public override void ClockCpu()
|
|
{
|
|
if (irq_enable)
|
|
{
|
|
ClockIRQ();
|
|
}
|
|
}
|
|
|
|
private void ClockIRQ()
|
|
{
|
|
irq_count++;
|
|
if (irq_count >= irq_destination)
|
|
{
|
|
irq_enable = false;
|
|
irq_pending = true;
|
|
}
|
|
|
|
SyncIRQ();
|
|
}
|
|
|
|
private void SyncIRQ()
|
|
{
|
|
SyncIRQ(irq_pending);
|
|
}
|
|
}
|
|
} |