NES - start mapper 96 (prg mapping working)
This commit is contained in:
parent
b96e014327
commit
f72905f602
|
@ -124,6 +124,7 @@
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Consoles\Nintendo\NES\Boards\BANDAI-FCG-1.cs" />
|
<Compile Include="Consoles\Nintendo\NES\Boards\BANDAI-FCG-1.cs" />
|
||||||
|
<Compile Include="Consoles\Nintendo\NES\Boards\BANDAI_74_161_02_74.cs" />
|
||||||
<Compile Include="Consoles\Nintendo\NES\Boards\BANDAI_74_161_161_32.cs" />
|
<Compile Include="Consoles\Nintendo\NES\Boards\BANDAI_74_161_161_32.cs" />
|
||||||
<Compile Include="Consoles\Nintendo\NES\Boards\BxROM.cs" />
|
<Compile Include="Consoles\Nintendo\NES\Boards\BxROM.cs" />
|
||||||
<Compile Include="Consoles\Nintendo\NES\Boards\Camerica.cs" />
|
<Compile Include="Consoles\Nintendo\NES\Boards\Camerica.cs" />
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
|
{
|
||||||
|
class BANDAI_74_161_02_74 : NES.NESBoardBase
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Here are Disch's original notes:
|
||||||
|
========================
|
||||||
|
= Mapper 096 =
|
||||||
|
========================
|
||||||
|
|
||||||
|
|
||||||
|
Example Games:
|
||||||
|
--------------------------
|
||||||
|
Oeka Kids - Anpanman no Hiragana Daisuki
|
||||||
|
Oeka Kids - Anpanman to Oekaki Shiyou!!
|
||||||
|
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
---------------------------
|
||||||
|
These games use the Oeka Kids tablet -- so you'll need to add support for that if you really want to test
|
||||||
|
these.
|
||||||
|
|
||||||
|
These games use 32k of CHR-RAM, which is swappable in a very unique fashion. Be sure to read the CHR Setup
|
||||||
|
section in detail.
|
||||||
|
|
||||||
|
|
||||||
|
Registers:
|
||||||
|
---------------------------
|
||||||
|
I'm unsure whether or not this mapper suffers from bus conflicts. Use caution!
|
||||||
|
|
||||||
|
|
||||||
|
$8000-FFFF: [.... .CPP]
|
||||||
|
C = CHR Block select (see CHR Setup)
|
||||||
|
P = PRG Page select (32k @ $8000)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CHR Setup:
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
This mapper is tricky!!!
|
||||||
|
|
||||||
|
Firstly, this mapper divides the 32k CHR-RAM into two 16k blocks (above 'C' bit selects which block is used).
|
||||||
|
The selected pages (including the fixed page) are taken from only the currently selected 16k block.
|
||||||
|
|
||||||
|
$0000 $0400 $0800 $0C00 $1000 $1400 $1800 $1C00
|
||||||
|
+-------------------------------+-------------------------------+
|
||||||
|
| **See below** | { 3 } |
|
||||||
|
+-------------------------------+-------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
But that's the easy part. This mapper does a very, very cool trick which watches the PPU address lines to
|
||||||
|
effectively "split" the nametable into 4 smaller sections -- thereby assigning a different CHR-RAM page to
|
||||||
|
each section. This allows **every single tile in the NT** to have a unique tile graphic!
|
||||||
|
|
||||||
|
Long story short:
|
||||||
|
|
||||||
|
A nametable spans from $2000-$23BF ($23C0-$23FF are the attribute table).
|
||||||
|
The mapper breaks the NT up like so:
|
||||||
|
|
||||||
|
$2000-20FF = use CHR page 0
|
||||||
|
$2100-21FF = use CHR page 1
|
||||||
|
$2200-22FF = use CHR page 2
|
||||||
|
$2300-23BF = use CHR page 3
|
||||||
|
|
||||||
|
the other nametables at $2400, $2800, $2C00 are broken up in the same fashion.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Long story long:
|
||||||
|
|
||||||
|
PPU Address lines are modified as the PPU fetches tiles, and also when the game manually changes the PPU
|
||||||
|
address (via the second write to $2006 --- or by the increment after read/writing $2007). The mapper
|
||||||
|
monitors every change to the PPU Address lines, and when it lies within a certain range, it swaps the
|
||||||
|
appropriate CHR page in.
|
||||||
|
|
||||||
|
It will only swap CHR when the address falls between $2000-2FFF (or mirrored regions like $6000-6FFF,
|
||||||
|
$A000-AFFF, $E000-EFFF). $3xxx will not trigger a swap.
|
||||||
|
|
||||||
|
When in that range, it checks to make sure the address is not attribute tables ((Addr AND $03FF) < $03C0).
|
||||||
|
Note I'm not 100% sure if the mapper really does this or not. It's very possible that attribute fetches will
|
||||||
|
also swap CHR... this would not really disrupt anything other than making the game be more careful about its
|
||||||
|
PPU writes.
|
||||||
|
|
||||||
|
When all that checks out, bits 8 and 9 (Addr AND $0300) select the 4k CHR page to swap in to $0000.
|
||||||
|
|
||||||
|
|
||||||
|
Note that the mapper does not distinguish between PPU driven line changes and game driven line changes.
|
||||||
|
This means that games can manually swap the CHR page by doing specific writes to $2006:
|
||||||
|
|
||||||
|
|
||||||
|
LDA #$20
|
||||||
|
STA $2006
|
||||||
|
STA $2006 ; Addr set to $20xx -- CHR page 0 selected
|
||||||
|
|
||||||
|
LDA #$21
|
||||||
|
STA $2006
|
||||||
|
STA $2006 ; Addr set to $21xx -- CHR page 1 selected
|
||||||
|
|
||||||
|
And in fact, games would HAVE to do that to select CHR, since that's the only way to fill CHR RAM with the
|
||||||
|
desired data. So make sure your emu supports this.
|
||||||
|
*/
|
||||||
|
int chr_block;
|
||||||
|
int prg_bank_mask_32k;
|
||||||
|
byte prg_bank_32k;
|
||||||
|
|
||||||
|
public override bool Configure(NES.EDetectionOrigin origin)
|
||||||
|
{
|
||||||
|
//analyze board type
|
||||||
|
switch (Cart.board_type)
|
||||||
|
{
|
||||||
|
case "MAPPER096":
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
chr_block = 0;
|
||||||
|
prg_bank_mask_32k = Cart.prg_size / 32 - 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SyncState(Serializer ser)
|
||||||
|
{
|
||||||
|
base.SyncState(ser);
|
||||||
|
ser.Sync("chr_block", ref chr_block);
|
||||||
|
ser.Sync("prg_bank_mask_16k", ref prg_bank_mask_32k);
|
||||||
|
ser.Sync("prg_bank_16k", ref prg_bank_32k);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WritePRG(int addr, byte value)
|
||||||
|
{
|
||||||
|
prg_bank_mask_32k = (byte)(value & 0x03);
|
||||||
|
chr_block = (value >> 2) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte ReadPRG(int addr)
|
||||||
|
{
|
||||||
|
int bank_32k = prg_bank_32k & prg_bank_mask_32k;
|
||||||
|
return ROM[(bank_32k * 0x8000) + addr];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte ReadPPU(int addr)
|
||||||
|
{
|
||||||
|
if (addr < 0x2000)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
return base.ReadPPU(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue