Action 53 mapper: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(→‎Verilog Code: I don't know Verilog, but I think this is what I mean)
(→‎External links: link the build tool)
(46 intermediate revisions by 7 users not shown)
Line 1: Line 1:
This is a sketch of a mapper that allows making a multicart of games that use multiple discrete mappers.
{{Infobox_iNES_mapper
As a random sample, it'd support 1943: The Battle of Midway/Valhalla (UNROM), 3-D Battles of Worldrunner (UNROM), Battle City (NROM-128), Battle Kid: Fortress of Peril (UOROM), Battle Tank (CNROM), Battleship (CNROM), Battletoads (AOROM), and Gekitotsu Yonku Battle (UNROM).
|name=Action 53
|company=InfiniteNESLives
|mapper=28
|complexity=CPLD
|boards=
|prgmax=8M (512K/256K inner)
|prgpage=32K or 16K/16K
|chrmax=32K
|chrpage=8K
|busconflicts=No
|mirroring=H, V, or 1-screen switchable
}}
The '''Action 53 mapper''' allows making a multicart of games that use multiple discrete mappers. It was assigned to [[iNES Mapper 028]].
 
Examples:
* ''[[Action 53|STREEMERZ: Action 53 Function 16 Volume One]]''
* ''[[Action 53|Double Action 53]]''
* ''[[Action 53|Action 53 Vol. 3: Revenge of the Twins]]''
 
 


== Registers ==
== Registers ==
;$5000-$5FFF: Register select
;$5000-$5FFF: Register select
;$8000-$FFFF: Register value
;$8000-$FFFF: Register value
There are no bus conflicts.


=== $5000: Register select ===
=== $5000: Register select ===
Line 14: Line 35:
</pre>
</pre>
In a multicart, registers $00 and $01 change the bank within a game, and registers $80 and $81 remain constant throughout a given game's execution.
In a multicart, registers $00 and $01 change the bank within a game, and registers $80 and $81 remain constant throughout a given game's execution.
Games ported from [[SxROM|SGROM or SNROM]] may rewrite register $80, usually to change mirroring.
Games ported from SUROM may change register $81's value to access the first or second 256 KiB of the ROM.


=== $00: CHR bank ===
=== $00: CHR bank ===
Line 20: Line 43:
   M  BB
   M  BB
   |  ++- Set CHR RAM A14-A13
   |  ++- Set CHR RAM A14-A13
   +------ Set CIRAM A10 if H/V mirroring is disabled
   +------ Set mirroring mode bit 0 if H/V mirroring is disabled
</pre>
</pre>


Line 28: Line 51:
   M BBBB
   M BBBB
   | ++++- Set current PRG ROM bank
   | ++++- Set current PRG ROM bank
   +------ Set CIRAM A10 if H/V mirroring is disabled
   +------ Set mirroring mode bit 0 if H/V mirroring is disabled
</pre>
</pre>
If the current mirroring mode is one of the 1-screen modes (0 or 1), writes to registers $00 and $01 change bit 0 of the mirroring mode to D4 of the written value.
(This simulates the mirroring control of [[AxROM]].)
If the current mirroring mode is vertical or horizontal (2 or 3), D4 is ignored.


=== $80: Mode ===
=== $80: Mode ===
Line 41: Line 67:
{| class="tabular"
{| class="tabular"
|+ Mirroring modes
|+ Mirroring modes
! Mode || Effect
! Mode || Effect || A10 output || Effect of write to register $00 or $01
|-
|-
| 0 || 1-screen lower bank
| 0 || 1-screen lower bank || 0 || D4 changes bit 0 of the mirroring mode
|-
|-
| 1 || 1-screen upper bank
| 1 || 1-screen upper bank || 1 || D4 changes bit 0 of the mirroring mode
|-
|-
| 2 || Vertical (and ignore writes to bit 4 of registers $00 and $01)
| 2 || Vertical || PPU A10 || D4 is ignored
|-
|-
| 3 || Horizontal (and ignore writes to bit 4 of registers $00 and $01)
| 3 || Horizontal || PPU A11 || D4 is ignored
|}
|}
While the mirroring mode is 0 or 1 (1-screen), bit 0 can be written in three places: bit 0 of $80, bit 4 of $00, or bit 4 of $01.
[[File:Multi-discrete PRG bank.png|frame|right|Logic table, with o = $81 value and i = $01 value]]
{| class="tabular"
{| class="tabular"
|+ PRG bank mode
|+ PRG bank mode
! Mode || Effect
! Mode || Simulates || Effect
|-
|-
| 0, 1 || Current 32 KiB bank in $8000-$FFFF
| 0, 1 || BNROM/AOROM || Current 32 KiB bank in $8000-$FFFF
|-
|-
| 2 || Fixed bottom half of outer bank in $8000-$BFFF and current bank in $C000-$FFFF
| 2 || UNROM (#180) || Fixed bottom half of outer bank in $8000-$BFFF<br>Current bank in $C000-$FFFF
|-
|-
| 3 || Current bank in $8000-$BFFF and fixed top half of outer bank in $C000-$FFFF
| 3 || UNROM (#2) || Current bank in $8000-$BFFF<br>Fixed top half of outer bank in $C000-$FFFF
|}
|}
When the fixed bank ($8000-$BFFF in mode 2 or $C000-$FFFF in mode 3) is accessed, it treats accesses to the fixed bank the same way as accesses in mode 0 with 32K: the outer bank bits are passed straight through.
When the fixed bank ($8000-$BFFF in mode 2 or $C000-$FFFF in mode 3) is accessed, it treats accesses to the fixed bank the same way as accesses in mode 0 with 32K: the outer bank bits are passed straight through.
Line 67: Line 96:
{| class="tabular"
{| class="tabular"
|+ PRG outer bank size
|+ PRG outer bank size
! Mode || Effect
! Size || Effect
|-
|-
| 0 || A20-A15 controlled by outer bank (32 KiB)
| 0 || A15 and up controlled by outer bank (32 KiB)
|-
|-
| 1 || A20-A16 controlled by outer bank (64 KiB)
| 1 || A16 and up controlled by outer bank (64 KiB)
|-
|-
| 2 || A20-A17 controlled by outer bank (128 KiB)
| 2 || A17 and up controlled by outer bank (128 KiB)
|-
|-
| 3 || A20-A18 controlled by outer bank (256 KiB)
| 3 || A18 and up controlled by outer bank (256 KiB)
|}
|}
Again, when a fixed bank is being accessed, this is temporarily forced to 32K, allowing all outer bank bits to come through.


Here are some examples of how bank modes work, assuming the outer bank is set to $12 and inner bank is $07, along with which bits from the inner bank are used:
These are the outputs on A22-A14 in each of the 12 combinations of mode and size, with '''o''' used for outer bank bits and '''i''' used for inner bank bits:
 
{| class="tabular"
{| class="tabular"
! Mode value || PRG bank mode || Outer bank size || Bank in $8000-$BFFF || Bank in $C000-$FFFF
! Mode value || PRG bank mode || Outer bank size || Bank in $8000 || Bank in $C000
|-
|-
| $00-$07 || 32 KiB || 32 KiB || $12 bottom (xxxx) || $12 top (xxxx)
| $00-$07 || 32 KiB     || 32 KiB || <code>oooooooo<span style="background:#FF6">0</span></code> || <code>oooooooo<span style="background:#FF6">1</span></code>
|-
|-
| $08-$0B || Fixed $8000 || 32 KiB || $12 bottom (xxxx) || $12 top (xxx1)
| $08-$0B || Fixed $8000 || 32 KiB || <code>oooooooo<span style="background:#FF6">0</span></code> || <code>ooooooooi</code>
|-
|-
| $0C-$0F || Fixed $C000 || 32 KiB || $12 top (xxx1) || $12 top (xxxx)
| $0C-$0F || Fixed $C000 || 32 KiB || <code>ooooooooi</code> || <code>oooooooo<span style="background:#FF6">1</span></code>
|-
|-
| $10-$17 || 32 KiB || 64 KiB || $13 bottom (xxx1) || $13 top (xxx1)
| $10-$17 || 32 KiB     || 64 KiB || <code>oooooooi<span style="background:#FF6">0</span></code> || <code>oooooooi<span style="background:#FF6">1</span></code>
|-
|-
| $18-$1B || Fixed $8000 || 64 KiB || $12 bottom (xxxx) || $13 top (xx11)
| $18-$1B || Fixed $8000 || 64 KiB || <code>oooooooo<span style="background:#FF6">0</span></code> || <code>oooooooii</code>
|-
|-
| $1C-$1F || Fixed $C000 || 64 KiB || $13 top (xx11) || $12 top (xxxx)
| $1C-$1F || Fixed $C000 || 64 KiB || <code>oooooooii</code> || <code>oooooooo<span style="background:#FF6">1</span></code>
|-
|-
| $20-$27 || 32 KiB || 128 KiB || $13 bottom (xx11) || $13 top (xx11)
| $20-$27 || 32 KiB     || 128 KiB || <code>ooooooii<span style="background:#FF6">0</span></code> || <code>ooooooii<span style="background:#FF6">1</span></code>
|-
|-
| $28-$2B || Fixed $8000 || 128 KiB || $12 bottom (xxxx) || $13 top (x111)
| $28-$2B || Fixed $8000 || 128 KiB || <code>oooooooo<span style="background:#FF6">0</span></code> || <code>ooooooiii</code>
|-
|-
| $2C-$2F || Fixed $C000 || 128 KiB || $13 top (x111) || $12 top (xxxx)
| $2C-$2F || Fixed $C000 || 128 KiB || <code>ooooooiii</code> || <code>oooooooo<span style="background:#FF6">1</span></code>
|-
|-
| $30-$37 || 32 KiB || 256 KiB || $17 bottom (x111) || $17 top (x111)
| $30-$37 || 32 KiB     || 256 KiB || <code>oooooiii<span style="background:#FF6">0</span></code> || <code>oooooiii<span style="background:#FF6">1</span></code>
|-
|-
| $38-$3B || Fixed $8000 || 256 KiB || $12 bottom (xxxx) || $13 top (0111)
| $38-$3B || Fixed $8000 || 256 KiB || <code>oooooooo<span style="background:#FF6">0</span></code> || <code>oooooiiii</code>
|-
|-
| $3C-$3F || Fixed $C000 || 256 KiB || $13 top (0111) || $12 top (xxxx)
| $3C-$3F || Fixed $C000 || 256 KiB || <code>oooooiiii</code> || <code>oooooooo<span style="background:#FF6">1</span></code>
|}
|}
For all of these cases, the "o"s come from the '''topmost''' outer bank bits and the "i"s come from the '''bottommost''' inner bank bits.


=== $81: Outer bank ===
=== $81: Outer bank ===
<pre>
<pre>
7654 3210
7654 3210
  BB BBBB
BBBB Bbbb
  ++-++++- Set outer PRG ROM bank
++++-++++- Set outer PRG ROM bank
</pre>
</pre>
When the outer bank size is set greater than 32K, the least significant bits are ignored.
Bits 7 through 3 always control PRG ROM A22 through A18. Bits 2-0 control A17-A15 only when the outer bank size is small enough to require them.
Many implementations recognize only the lower 4 or 6 bits for two reasons: memory cost and the practical limit of 2 MiB PRG ROM in an [[iNES]] ROM image.
The PowerPak uses only the low 4 bits, as it has only 512 KiB of RAM for PRG ROM.
And by the 2010s, 5-volt 8-bit parallel flash memories larger than 2Mx8 had become hard to find.
An implementation supporting [[NES 2.0#Byte 9 (Upper bits of ROM size)|NES 2.0 large ROMs]] should [[Oversize|recognize all bits]] for a maximum of 8 MiB.
One document about this mapper describes a register at $4444 with unknown purpose. The released hardware ''does not'' respond to this address.<ref name="everynes">[//problemkaputt.de/everynes.htm#mapper28action53homebrewxin1 Everynes] mentions $4444 write. It's presumably a breakpoint on the developer's debugging rig, analogous to <code>ld b, b</code> in emulators of consoles using an 8080 family CPU.</ref>
== Power up state ==
At power on, the last 16 KiB of the ROM is mapped into $C000-$FFFF.
The rest of the state is unspecified.
The mapper state is unchanged on reset.
''Non-normative:''
Once a program boots, it may set reg $81 = $FF and reg $80 = $02 to get into oversize-BNROM mode in the last bank.
''Non-normative:''
If desired, games in a multicart can be patched with an appropriate reset stub to allow returning to the menu.
The Action 53 build tool does this semi-automatically for NROM games.


== Configurations ==
== Configurations ==
Line 125: Line 177:
;AOROM ([[iNES Mapper 007|#7]]): Outer bank size 1-3, PRG mode 0, mirroring 1-screen, select $01
;AOROM ([[iNES Mapper 007|#7]]): Outer bank size 1-3, PRG mode 0, mirroring 1-screen, select $01


== Reference implementations ==
See [[Action 53 mapper/Reference implementations]] for functions in Python and 6502 assembly language that calculate the bank number output to PRG ROM A20-A14 as described above. These may be used to verify emulator or hardware implementations.


== Verilog Code ==
Supported in the following emulators:
<pre>
* FCEUX (2.2.2; bugs prior to 2.3.0)
//--------------------------------------------------------------------------------
* Bizhawk [https://github.com/TASVideos/BizHawk/commit/c217768871617ba9d6343896a47123f7027e392d c217768]
// Tepples' Multi-discrete mapper
* Nintendulator
// Designed for use with a multicart that supports many discrete mappers
* Mesen
// such as NROM, AOROM, BNROM, CNROM, UxROM, UxROM variant #180
// written for Xilinx 9500XL series CPLD XC9536XL
//
// verilog code by: infiniteneslives (paul@InfiniteNesLives.com)
// Created: 6 Oct 2012
// Modified: 6 Oct 2012
//
// Description:  Designed to support 32KB of CHR-RAM, and 2MB PRG-ROM
// see nesdev wiki for details:
// http://wiki.nesdev.org/w/index.php/User:Tepples/Multi-discrete_mapper
//
// Modifications:
// 10/6/12: Corrected prg bank switching 27/36Mcells (75%), 25/34 pins.
//
// Options:
// 8KB WRAM: control available at the cost of 2 Mcells 29/36 (81%)
// and 2 pins (WRAM /CE and /WE) 27/34 pins available.
// 1MB PRG-ROM: shaving off PRG ROM A20 saves 1Mcell and 1pin.
//
// Possible features with remaining logic:
// Upto 32KB of WRAM and/or finer CHR bankswitching.
//
// NOTE:  BEWARE, This implementation is UNTESETED!
//--------------------------------------------------------------------------------


== <span id="Verilog_Code">Hardware implementations</span> ==


module action53 (
Two [[Action 53 mapper/Verilog|implementations in Verilog]] are designed for use on a CPLD.
input m2, //clock input negedge triggered
One has been tested on a PowerPak as [//forums.nesdev.org/viewtopic.php?p=102718#p102718 MAP1C.MAP].
input prg_rw, //PRG R/W signal from NES
input prg_ce, //PRG /CE signal from NES
input [7:0] data, //PRG Data bus input
input [14:12] prg_addr, //PRG A14-12 input
input [11:10] chr_addr, //CHR A11-10 input
output reg[20:14] pa, //PRG ROM A20-14 outputs
output reg[14:13] ca, //CHR RAM A14-13 outputs
output reg p_ce, //PRG-ROM /CE signal output
output reg ciram_a10, //CIRAM A10 output
output  reg w_ce_n, //WRAM /CE signal output
output  reg w_we //WRAM /WE signal output
// output reg debug //debug pin output
);


The MiSTer FPGA computer includes an [https://github.com/Kitrinx/NES_MiSTer/blob/7d1a94b38817a1eede8bfbbb5bfbc7b76b823824/rtl/mappers/generic.sv#L958-L1164 implementation in SystemVerilog].


wire write_reg_sel, write_reg_val;
== Implementation notes ==
//active high when writing to $5000->reg_sel, $8000->reg_val
Input pins: 2 power, 16 signal
assign write_reg_sel = (prg_addr[14] & ~prg_addr[13] & prg_addr[12] & m2 & prg_ce & ~prg_rw);
* Power and ground
assign write_reg_val = (~prg_ce & prg_rw);
* CPU D7, D5-D0 (D6 used only in 4-8 MB version)
 
* CPU A14-A12, /PRGSEL, M2, R/W
reg [1:0] reg_sel; //register select S: supervisor=1/user=0 (D7), R: register select (D0)
* PPU A12-A10
//In a multicart, registers $00 and $01 change the bank within a game, and registers $80 and $81
Output pins: 12 signal
//remain constant throughout a given game's execution.
* CHR RAM A14-A13
 
* CIRAM A10
//$00 CHR bank: used for CNROM, and one screen mirroring
* PRG ROM A20-A14, /CE
reg [1:0] chr_bank; //D1-0 set CHR RAM A14-13
* Optional PRG RAM enable ($6000-$7FFF)
reg M; //set CIRAM A10 if H/V disabled via mirroring mode below
 
//$01 Inner bank: prg bank for A/B/UxROM etc.
reg [3:0] prg_inner_bank; //D4-0 set PRG ROM A14/15 and up for single game in game use
 
//$80 Mode: used by multicart supervisor software
reg [1:0] prg_size; //D5-4 0: 32KB, 1: 64KB, 2:128KB, 3:256KB
reg [1:0] prg_mode; //D3-2 0,1: current 32KB @ $8000, 2: UNROM standard, 3: UNROM #180
reg [1:0] mirror_mode; //D1-0 0: 1scn lower, 1: 1scn upper, 2: vert, 3: horiz
 
//$81 Outer bank: sets the upper PRG ROM bits to select current game
reg [5:0] prg_outer_bank; //sets PRG ROM A15-20
 
//REGISTER SELECT:  chooses which of the above registers is written to
always @ (negedge write_reg_sel)
begin
reg_sel[1] <= data[7];
reg_sel[0] <= data[0];
end
 
//REGISTER VALUE WRITING
always @ (negedge write_reg_val)
begin
if (reg_sel[1] == 0)
M <= data[4]; //this is written to for both $0x registers
 
case (reg_sel)
0:  chr_bank  <= data[1:0];
1:  prg_inner_bank <= data[3:0];
      2: {prg_size, prg_mode, mirror_mode} <= data[5:0]; 
3:  prg_outer_bank <= data[5:0];
endcase
end


 
A CPLD requires one macrocell per bit of state and one for each output pin controlled by a multiplexer, plus possibly a couple more for more complex operations.
 
Depending on maximum PRG ROM size (512 KiB to 8 MiB), this mapper requires 18 to 22 bits of state and 7 multiplexed outputs, which fits comfortably in a 32- or 36-cell CPLD.
//allow for simple handling of both UNROM versions
wire pa14_eff;
assign pa14_eff = prg_addr[14] ^ prg_mode[0];  //only used in modes 2 and 3 so don't care about 1 & 0
//now using pa14_eff allows us to ignore the mapper #180 special case
 
//OUTPUTS combinational logic
always @ (m2, chr_bank, pa14_eff, prg_ce, prg_rw, M, prg_inner_bank, prg_size, prg_mode, mirror_mode, prg_outer_bank, chr_addr, prg_addr)
begin
//wram control
if ((prg_addr[14:13] == 3) & m2 & prg_ce)
w_ce_n = 0; //enabled
else
w_ce_n = 1; //disabled
//wram write control
w_we = prg_rw;
 
//prevent bus conflicts for writes to $8000-FFFF
if (prg_ce == 0 & prg_rw == 1)
p_ce = 0; //enabled
else
p_ce = 1; //disabled
 
//mirroring
case (mirror_mode)
0: ciram_a10 = M;
1: ciram_a10 = M;
2: ciram_a10 = chr_addr[10]; //vert
3: ciram_a10 = chr_addr[11]; //horiz
endcase
//chr bankswitching
ca = chr_bank;
 
//prg bankswitching
pa[20:18] = prg_outer_bank[5:3];  //always controlled directly (independent of prg_mode)
 
//prg bank selecting must control PRG ROM A17-14
 
if (prg_mode[1] == 0) //modes 0 and 1 NROM, A/BNROM 32KB banks
begin
pa[14] = prg_addr[14]; //32KB banks
 
//32KB NROM
if (prg_size == 0)
pa[17:15] = prg_outer_bank[2:0]; //set to 32KB
 
//64KB A/BNROM
else if (prg_size[1] == 0)
begin
pa[17:16] = prg_outer_bank[2:1]; //set to 64KB
pa[15] = prg_inner_bank[0];
end
 
//128KB A/BNROM
else if (prg_size == 2)
begin
pa[17] = prg_outer_bank[2]; //set to 128KB
pa[16:15] = prg_inner_bank[1:0];
end
 
//256KB A/BNROM
else
pa[17:15] = prg_inner_bank[2:0];
end
 
// the UNROM support was rewritten on 2012-10-06 by tepples, who
// doesn't really know any Verilog, so it might not even compile
else if (pa14_eff == 0) // modes 2 & 3 (UNROM 180 & UNROM 2) fixed bank
begin
pa[14] = prg_addr[14]; //handle it like 32KB mode 0
pa[17:15] = prg_outer_bank[2:0];
end
 
//switchable bank of 32KB UNROM (for sake of completeness only)
else if (prg_size == 0)
begin
pa[17:15] = prg_outer_bank[2:0]; //set to 32KB
pa[14] = prg_inner_bank[0];
end
 
//switchable bank of 64KB UNROM
else if (prg_size == 1)
begin
pa[17:16] = prg_outer_bank[2:1]; //set to 64KB
pa[15:14] = prg_inner_bank[1:0];
end
 
//switchable bank of 128KB UNROM
else if (prg_size == 2)
begin
pa[17] = prg_outer_bank[2]; //set to 128KB
pa[16:14] = prg_inner_bank[2:0];
end
 
//switchable bank of 256KB UOROM
else
begin
pa[17:14] = prg_inner_bank[3:0];
end
 
end
endmodule
 
</pre>
 
== Implementation notes ==
A CPLD requires at least one macrocell per bit of state, plus more macrocells for more complex operations.
This mapper requires 20 bits of state, which leaves plenty of breathing room in a 36-cell CPLD for mapper logic.
* Register select: 2 bits
* Register select: 2 bits
* Register $00: 6 bits (D4 is directed to register 0)
* Register $00: 2 bits (D4 is directed to register $80)
* Register $01: 4 bits (D4 is directed to register 0)
* Register $01: 4 bits (D4 is directed to register $80)
* Register $80: 6 bits
* Register $80: 6 bits
* Register $81: 2 bits (D4 is directed to register 0)
* Register $81: 4, 6, or 8 bits
* A17-A14: 4 multiplexed outputs
* CIRAM A10: 1 multiplexed output
* PRG ROM /CE: 1 multiplexed output
* PRG RAM /CE: 1 multiplexed output


After synthesizing and laying fitting within a XC9536XL CPLD 27/36 Macrocells were consumed (75%).
After synthesizing a 2 MiB design and laying fitting within a XC9536XL CPLD, 27/36 Macrocells were consumed (75%).
Additionally this design requires 25/34 available pins on the XC9536XL.
Additionally this design requires 25/34 available pins on the XC9536XL.


Adding WRAM control requires 2 Macrocells and 2 pins.
Adding WRAM control requires 2 Macrocells and 2 pins.


Lowering to 1MB by shaving off PRG ROM A20 would save 1 Macrocell and 1 pin, if desired.
Lowering to 1 MB by shaving off PRG ROM A20 would save 1 Macrocell and 1 pin, if desired.
 
== See also ==
*[[Action 53 manual]]
 
== Notes ==
<references />
 
== External links ==
*[https://github.com/pinobatch/action53 action53 github] - build tool to create multi-game ROM
*[https://forums.nesdev.org/viewtopic.php?p=215345#p215345 test28], a comprehensive test ROM
*[https://forums.nesdev.org/viewtopic.php?p=101111#p101111 Forum post announcing reservation of #28]
*[https://forums.nesdev.org/viewtopic.php?p=101970#p101970 Forum post announcing implementation in NESICIDE]


[[Category:Multicart mappers]]
[[Category:Multicart mappers]]
[[Category:Mappers using $4020-$5FFF]]
[[Category:Mappers with single-screen mirroring]]

Revision as of 14:38, 28 October 2022


Action 53
Company InfiniteNESLives
Complexity CPLD
Boards
PRG ROM capacity 8M (512K/256K inner)
PRG ROM window 32K or 16K/16K
PRG RAM capacity None
CHR capacity 32K
CHR window 8K
Nametable mirroring H, V, or 1-screen switchable
Bus conflicts No
IRQ No
Audio No
iNES mappers 028

The Action 53 mapper allows making a multicart of games that use multiple discrete mappers. It was assigned to iNES Mapper 028.

Examples:


Registers

$5000-$5FFF
Register select
$8000-$FFFF
Register value

There are no bus conflicts.

$5000: Register select

7654 3210
S       R
|       +- Select register
+--------- 0: User registers; 1: Supervisor registers

In a multicart, registers $00 and $01 change the bank within a game, and registers $80 and $81 remain constant throughout a given game's execution. Games ported from SGROM or SNROM may rewrite register $80, usually to change mirroring. Games ported from SUROM may change register $81's value to access the first or second 256 KiB of the ROM.

$00: CHR bank

7654 3210
   M   BB
   |   ++- Set CHR RAM A14-A13
   +------ Set mirroring mode bit 0 if H/V mirroring is disabled

$01: Inner bank

7654 3210
   M BBBB
   | ++++- Set current PRG ROM bank
   +------ Set mirroring mode bit 0 if H/V mirroring is disabled

If the current mirroring mode is one of the 1-screen modes (0 or 1), writes to registers $00 and $01 change bit 0 of the mirroring mode to D4 of the written value. (This simulates the mirroring control of AxROM.) If the current mirroring mode is vertical or horizontal (2 or 3), D4 is ignored.

$80: Mode

7654 3210
  SS PPMM
  || ||++- Nametable mirroring mode
  || ++--- PRG bank mode
  ++------ PRG outer bank size
Mirroring modes
Mode Effect A10 output Effect of write to register $00 or $01
0 1-screen lower bank 0 D4 changes bit 0 of the mirroring mode
1 1-screen upper bank 1 D4 changes bit 0 of the mirroring mode
2 Vertical PPU A10 D4 is ignored
3 Horizontal PPU A11 D4 is ignored

While the mirroring mode is 0 or 1 (1-screen), bit 0 can be written in three places: bit 0 of $80, bit 4 of $00, or bit 4 of $01.

Logic table, with o = $81 value and i = $01 value
PRG bank mode
Mode Simulates Effect
0, 1 BNROM/AOROM Current 32 KiB bank in $8000-$FFFF
2 UNROM (#180) Fixed bottom half of outer bank in $8000-$BFFF
Current bank in $C000-$FFFF
3 UNROM (#2) Current bank in $8000-$BFFF
Fixed top half of outer bank in $C000-$FFFF

When the fixed bank ($8000-$BFFF in mode 2 or $C000-$FFFF in mode 3) is accessed, it treats accesses to the fixed bank the same way as accesses in mode 0 with 32K: the outer bank bits are passed straight through. For example, this would allow the fixed $C000 bank in mode 3 128K to be set to 16K bank 7 (as in mapper 2) or 1, 3, or 5. In mode 2 128K, the fixed $8000 bank could be configured as 16K bank 0 (as in mapper 180) or 2, 4, or 6.

PRG outer bank size
Size Effect
0 A15 and up controlled by outer bank (32 KiB)
1 A16 and up controlled by outer bank (64 KiB)
2 A17 and up controlled by outer bank (128 KiB)
3 A18 and up controlled by outer bank (256 KiB)

Again, when a fixed bank is being accessed, this is temporarily forced to 32K, allowing all outer bank bits to come through.

These are the outputs on A22-A14 in each of the 12 combinations of mode and size, with o used for outer bank bits and i used for inner bank bits:

Mode value PRG bank mode Outer bank size Bank in $8000 Bank in $C000
$00-$07 32 KiB 32 KiB oooooooo0 oooooooo1
$08-$0B Fixed $8000 32 KiB oooooooo0 ooooooooi
$0C-$0F Fixed $C000 32 KiB ooooooooi oooooooo1
$10-$17 32 KiB 64 KiB oooooooi0 oooooooi1
$18-$1B Fixed $8000 64 KiB oooooooo0 oooooooii
$1C-$1F Fixed $C000 64 KiB oooooooii oooooooo1
$20-$27 32 KiB 128 KiB ooooooii0 ooooooii1
$28-$2B Fixed $8000 128 KiB oooooooo0 ooooooiii
$2C-$2F Fixed $C000 128 KiB ooooooiii oooooooo1
$30-$37 32 KiB 256 KiB oooooiii0 oooooiii1
$38-$3B Fixed $8000 256 KiB oooooooo0 oooooiiii
$3C-$3F Fixed $C000 256 KiB oooooiiii oooooooo1

For all of these cases, the "o"s come from the topmost outer bank bits and the "i"s come from the bottommost inner bank bits.

$81: Outer bank

7654 3210
BBBB Bbbb
++++-++++- Set outer PRG ROM bank

When the outer bank size is set greater than 32K, the least significant bits are ignored.

Bits 7 through 3 always control PRG ROM A22 through A18. Bits 2-0 control A17-A15 only when the outer bank size is small enough to require them.

Many implementations recognize only the lower 4 or 6 bits for two reasons: memory cost and the practical limit of 2 MiB PRG ROM in an iNES ROM image. The PowerPak uses only the low 4 bits, as it has only 512 KiB of RAM for PRG ROM. And by the 2010s, 5-volt 8-bit parallel flash memories larger than 2Mx8 had become hard to find. An implementation supporting NES 2.0 large ROMs should recognize all bits for a maximum of 8 MiB.

One document about this mapper describes a register at $4444 with unknown purpose. The released hardware does not respond to this address.[1]

Power up state

At power on, the last 16 KiB of the ROM is mapped into $C000-$FFFF. The rest of the state is unspecified. The mapper state is unchanged on reset.

Non-normative: Once a program boots, it may set reg $81 = $FF and reg $80 = $02 to get into oversize-BNROM mode in the last bank.

Non-normative: If desired, games in a multicart can be patched with an appropriate reset stub to allow returning to the menu. The Action 53 build tool does this semi-automatically for NROM games.

Configurations

NROM-128 (#0)
Outer bank size 0, PRG mode 2 or 3, mirroring H or V, select $01
NROM-256 (#0)
Outer bank size 0, PRG mode 0, mirroring H or V, select $01
CNROM (#3)
Outer bank size 0, PRG mode 0, mirroring H or V, select $00
BNROM (#34)
Outer bank size 1-3, PRG mode 0, mirroring H or V, select $01
BNROM oversize (#34 as emulated)
Outer bank size 0, PRG mode 0, mirroring H or V, select $81, and modify bus-conflict-avoidance table for position within multicart
UNROM (common) (#2)
Outer bank size 1-3, PRG mode 3, mirroring H or V, select $01
UNROM (Crazy Climber and MGC 2011) (#180)
Outer bank size 1-3, PRG mode 2, mirroring H or V, select $01
AOROM (#7)
Outer bank size 1-3, PRG mode 0, mirroring 1-screen, select $01

Reference implementations

See Action 53 mapper/Reference implementations for functions in Python and 6502 assembly language that calculate the bank number output to PRG ROM A20-A14 as described above. These may be used to verify emulator or hardware implementations.

Supported in the following emulators:

  • FCEUX (2.2.2; bugs prior to 2.3.0)
  • Bizhawk c217768
  • Nintendulator
  • Mesen

Hardware implementations

Two implementations in Verilog are designed for use on a CPLD. One has been tested on a PowerPak as MAP1C.MAP.

The MiSTer FPGA computer includes an implementation in SystemVerilog.

Implementation notes

Input pins: 2 power, 16 signal

  • Power and ground
  • CPU D7, D5-D0 (D6 used only in 4-8 MB version)
  • CPU A14-A12, /PRGSEL, M2, R/W
  • PPU A12-A10

Output pins: 12 signal

  • CHR RAM A14-A13
  • CIRAM A10
  • PRG ROM A20-A14, /CE
  • Optional PRG RAM enable ($6000-$7FFF)

A CPLD requires one macrocell per bit of state and one for each output pin controlled by a multiplexer, plus possibly a couple more for more complex operations. Depending on maximum PRG ROM size (512 KiB to 8 MiB), this mapper requires 18 to 22 bits of state and 7 multiplexed outputs, which fits comfortably in a 32- or 36-cell CPLD.

  • Register select: 2 bits
  • Register $00: 2 bits (D4 is directed to register $80)
  • Register $01: 4 bits (D4 is directed to register $80)
  • Register $80: 6 bits
  • Register $81: 4, 6, or 8 bits
  • A17-A14: 4 multiplexed outputs
  • CIRAM A10: 1 multiplexed output
  • PRG ROM /CE: 1 multiplexed output
  • PRG RAM /CE: 1 multiplexed output

After synthesizing a 2 MiB design and laying fitting within a XC9536XL CPLD, 27/36 Macrocells were consumed (75%). Additionally this design requires 25/34 available pins on the XC9536XL.

Adding WRAM control requires 2 Macrocells and 2 pins.

Lowering to 1 MB by shaving off PRG ROM A20 would save 1 Macrocell and 1 pin, if desired.

See also

Notes

  1. Everynes mentions $4444 write. It's presumably a breakpoint on the developer's debugging rig, analogous to ld b, b in emulators of consoles using an 8080 family CPU.

External links