NEShawk/Mapper 105 - Add IRQs
This commit is contained in:
parent
51f29c5337
commit
b0ff8d586c
|
@ -1,4 +1,117 @@
|
|||
using BizHawk.Common;
|
||||
#region Disch's Notes
|
||||
/*
|
||||
* Here are Disch's original notes:
|
||||
========================
|
||||
= Mapper 105 =
|
||||
========================
|
||||
|
||||
aka
|
||||
--------------------------
|
||||
NES-EVENT
|
||||
|
||||
|
||||
Example Game:
|
||||
--------------------------
|
||||
Nintendo World Championships 1990
|
||||
|
||||
|
||||
Notes:
|
||||
---------------------------
|
||||
This mapper is an MMC1 with crazy wiring and a huge 30-bit CPU cycle driven IRQ counter. Registers are all
|
||||
internal and not directly accessable -- and the latch must be written to 1 bit at a time -- just like on a
|
||||
normal MMC1. For details on how regs are written to, see mapper 001.
|
||||
|
||||
This mapper has 8k CHR-RAM, and it is not swappable.
|
||||
|
||||
|
||||
Registers:
|
||||
---------------------------
|
||||
|
||||
Note that like a normal MMC1, registers are internal and not accessed directly.
|
||||
|
||||
|
||||
$8000-9FFF: [.... PSMM] Same as MMC1 (but CHR mode bit isn't used)
|
||||
|
||||
$A000-BFFF: [...I OAA.]
|
||||
I = IRQ control / initialization toggle
|
||||
O = PRG Mode/Chip select
|
||||
A = PRG Reg 'A'
|
||||
|
||||
$C000-DFFF: [.... ....] Unused
|
||||
|
||||
$E000-FFFF: [...W BBBB]
|
||||
W = WRAM disable (same as MMC1)
|
||||
B = PRG Reg 'B'
|
||||
|
||||
|
||||
|
||||
Powerup / Reset / Initialization:
|
||||
---------------------------
|
||||
|
||||
On powerup and reset, the first 32k of PRG (from the first PRG chip) is selected at $8000 *no matter what*.
|
||||
PRG cannot be swapped until the mapper has been "initialized" by setting the 'I' bit to 0, then to '1'. This
|
||||
toggling will "unlock" PRG swapping on the mapper.
|
||||
|
||||
Note 'I' also controls the IRQ counter (see below)
|
||||
|
||||
|
||||
PRG Setup:
|
||||
---------------------------
|
||||
|
||||
There are 2 PRG chips, each 128k. The 'O' bit selects between the chips, and also determines which PRG Reg
|
||||
is used to select the page.
|
||||
|
||||
O=0: Use first PRG chip (first 128k), use 'A' PRG Reg, 32k swap
|
||||
O=1: Use second PRG chip (second 128k), use 'B' PRG Reg, MMC1 style swap
|
||||
|
||||
In addition, if the mapper has not been "unlocked", the first 32k of the first chip is always selected
|
||||
regardless (as if $A000 contained $00).
|
||||
|
||||
Modes as listed below:
|
||||
|
||||
$8000 $A000 $C000 $E000
|
||||
+-------------------------------+
|
||||
Uninitialized: | { 0 } | <-- use first 128k
|
||||
+-------------------------------+
|
||||
O=0: | $A000 | <-- use first 128k
|
||||
+-------------------------------+
|
||||
O=1, P=0: | <$E000> | <-- use second 128k
|
||||
+-------------------------------+
|
||||
O=1, P=1, S=0: | { 0 } | $E000 | <-- use second 128k
|
||||
+---------------+---------------+
|
||||
O=1, P=1, S=1: | $E000 | {$07} | <-- use second 128k
|
||||
+---------------+---------------+
|
||||
|
||||
|
||||
|
||||
|
||||
IRQ Counter:
|
||||
---------------------------
|
||||
|
||||
The 'I' bit in $A000 controls the IRQ counter. When cleared, the IRQ counter counts up every cycle. When
|
||||
set, the IRQ counter is reset to 0 and stays there (does not count), and the pending IRQ is acknowledged.
|
||||
|
||||
The cart has 4 dipswitches which control how high the counter must reach for an IRQ to be generated.
|
||||
|
||||
The IRQ counter is 30 bits wide.. when it reaches the following value, an IRQ is fired:
|
||||
|
||||
[1D CBAx xxxx xxxx xxxx xxxx xxxx xxxx]
|
||||
^ ^^^
|
||||
| |||
|
||||
either 0 or 1, depending on the corresponding dipswitch.
|
||||
|
||||
So if all dipswitches are open (use '0' above), the counter must reach $20000000.
|
||||
If all dipswitches are closed (use '1' above), the counter must reach $3E000000.
|
||||
etc
|
||||
|
||||
In the official tournament, 'C' was closed, and the others were open, so the counter had to reach $2800000.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.NumberExtensions;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||
|
@ -14,13 +127,69 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
//state
|
||||
MMC1.MMC1_SerialController scnt;
|
||||
bool slot_mode, prg_mode;
|
||||
bool irq_control;
|
||||
int prg_a,prg_b;
|
||||
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
|
||||
{
|
||||
get
|
||||
{
|
||||
return 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 Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
@ -30,17 +199,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
public override void SyncState(Serializer ser)
|
||||
{
|
||||
base.SyncState(ser);
|
||||
|
||||
|
||||
scnt.SyncState(ser);
|
||||
ser.Sync("slot_mode", ref slot_mode);
|
||||
ser.Sync("prg_mode", ref prg_mode);
|
||||
ser.Sync("irq_control", ref irq_control);
|
||||
ser.Sync("c000_swappable", ref c000_swappable);
|
||||
ser.Sync("prg_16k_mode", ref prg_32k_mode);
|
||||
ser.Sync("irq_enable", ref irq_enable);
|
||||
ser.Sync("irq_pending", ref irq_pending);
|
||||
ser.Sync("irq_count", ref irq_count);
|
||||
ser.Sync("prg_a", ref prg_a);
|
||||
ser.Sync("prg_b", ref prg_b);
|
||||
ser.Sync("init_sequence", ref init_sequence);
|
||||
ser.Sync("chip_select", ref chip_select);
|
||||
ser.Sync("wram_disable", ref wram_disable);
|
||||
|
||||
ser.Sync("prg_banks_16k", ref prg_banks_16k);
|
||||
if (ser.IsReader) Sync();
|
||||
}
|
||||
|
||||
|
@ -58,6 +229,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
}
|
||||
|
||||
prg_bank_mask_16k = Cart.prg_size / 16 - 1;
|
||||
init_sequence = 0;
|
||||
|
||||
SetMirrorType(EMirrorType.Vertical);
|
||||
|
||||
|
@ -65,55 +237,57 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
scnt.WriteRegister = SerialWriteRegister;
|
||||
scnt.Reset = SerialReset;
|
||||
|
||||
Sync();
|
||||
InitValues();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SerialReset()
|
||||
{
|
||||
prg_mode = true;
|
||||
slot_mode = true;
|
||||
prg_32k_mode = true;
|
||||
c000_swappable = true;
|
||||
}
|
||||
|
||||
void Sync()
|
||||
{
|
||||
SyncIRQDestination();
|
||||
SyncIRQ();
|
||||
|
||||
if (init_sequence != 2)
|
||||
{
|
||||
//"use first 128k"
|
||||
// prg banks locked to first 32k of first 128k chip
|
||||
prg_banks_16k[0] = 0;
|
||||
prg_banks_16k[1] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (chip_select == false)
|
||||
if (!chip_select)
|
||||
{
|
||||
//"use first 128k"
|
||||
prg_banks_16k[0] = prg_a*2;
|
||||
prg_banks_16k[1] = prg_a*2 + 1;
|
||||
//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_mode == false)
|
||||
if (!prg_32k_mode)
|
||||
{
|
||||
//"use second 128k"
|
||||
prg_banks_16k[0] = (prg_b>>1) + 8;
|
||||
prg_banks_16k[1] = (prg_b>>1) + 8;
|
||||
//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...))
|
||||
if (slot_mode == false)
|
||||
//((these arent tested, i think...))
|
||||
//"use second 128k"
|
||||
if (!c000_swappable)
|
||||
{
|
||||
//"use second 128k"
|
||||
prg_banks_16k[0] = 8;
|
||||
prg_banks_16k[1] = prg_b + 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
//"use second 128k"
|
||||
prg_banks_16k[0] = prg_b + 8;
|
||||
prg_banks_16k[1] = 8 + 7;
|
||||
prg_banks_16k[1] = 15; //last bank of second 128k
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,18 +314,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
case 2: SetMirrorType(EMirrorType.Vertical); break;
|
||||
case 3: SetMirrorType(EMirrorType.Horizontal); break;
|
||||
}
|
||||
slot_mode = value.Bit(2);
|
||||
prg_mode = value.Bit(3);
|
||||
c000_swappable = value.Bit(2);
|
||||
prg_32k_mode = value.Bit(3);
|
||||
Sync();
|
||||
break;
|
||||
case 1: //A000-BFFF
|
||||
{
|
||||
bool last_irq_control = irq_control;
|
||||
irq_control = value.Bit(4);
|
||||
if (init_sequence == 0)
|
||||
if (irq_control == false) init_sequence = 1; else { }
|
||||
else if (init_sequence == 1)
|
||||
if (irq_control == true) init_sequence = 2;
|
||||
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();
|
||||
|
@ -170,6 +356,36 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
//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);
|
||||
|
@ -185,7 +401,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue