Action 53 mapper: Difference between revisions
m (cat) |
(Added verilog code for design implementation and notes about actual consumed logic.) |
||
Line 121: | Line 121: | ||
;UNROM (Crazy Climber and MGC 2011) ([[iNES Mapper 180|#180]]): Outer bank size 1-3, PRG mode 2, mirroring H or V, select $01 | ;UNROM (Crazy Climber and MGC 2011) ([[iNES Mapper 180|#180]]): Outer bank size 1-3, PRG mode 2, mirroring H or V, select $01 | ||
;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 | ||
== Verilog Code == | |||
<pre> | |||
//-------------------------------------------------------------------------------- | |||
// Tepples' Multi-discrete mapper | |||
// Designed for use with a multicart that supports many discrete mappers | |||
// 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 Sept 2012 | |||
// Modified: 6 Sept 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 | |||
// | |||
// NOTE: BEWARE, This implementation is UNTESETED! | |||
//-------------------------------------------------------------------------------- | |||
module mmc3 ( | |||
input m2, //clock input negedge triggered | |||
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-13 input | |||
input [11:10] chr_addr, //CHR A12-10 inputj | |||
output reg[20:14] pa, //PRG ROM A18-13 outputs | |||
output [14:13] ca, //CHR ROM/RAM A17-10 outputs | |||
output reg p_ce, //PRG-ROM /CE signal output | |||
output reg ciram_a10 //CIRAM A10 output | |||
// output reg debug, //debug pin output | |||
); | |||
wire write_reg_sel, write_reg_val; | |||
//active high when writing to $5000->reg_sel, $8000->reg_val | |||
assign write_reg_sel = (prg_addr[14] & ~prg_addr[13] & prg_addr[12] & m2 & prg_ce & ~prg_rw); | |||
assign write_reg_val = (~prg_ce & prg_rw); | |||
reg [1:0] reg_sel; //register select S: supervisor=1/user=0 (D7), R: register select (D0) | |||
//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. | |||
//$00 CHR bank: used for CNROM, and one screen mirroring | |||
reg [1:0] chr_bank; //D1-0 set CHR RAM A14-13 | |||
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 | |||
//chr bankswitching | |||
assign ca = chr_bank; | |||
//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 @ (pa14_eff, prg_ce, prg_rw, M, prg_inner_bank, prg_size, prg_mode, mirror_mode, prg_outer_bank, chr_addr, prg_addr) | |||
begin | |||
//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 | |||
pa[20:18] = prg_outer_bank[5:4]; //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[17:15] = prg_outer_bank[3:0]; //select bank | |||
pa[14] = prg_addr[14]; //32KB banks | |||
end | |||
else // modes 2 & 3 standard UNROM 16KB banks last fixed | |||
begin | |||
//64KB and useless 32KB | |||
if (prg_size[1] == 0) | |||
begin | |||
pa[17:16] = prg_outer_bank[3:2]; //set to 64KB | |||
if (pa14_eff == 0) //$8000-BFFF | |||
pa[15:14] = prg_inner_bank[1:0]; | |||
else | |||
pa[15:14] = 2'b11; //last bank fixed | |||
end | |||
//128KB | |||
else if (prg_size == 2) | |||
begin | |||
pa[17] = prg_outer_bank[3]; //set to 128KB | |||
if (pa14_eff == 0) //$8000-BFFF | |||
pa[16:14] = prg_inner_bank[2:0]; | |||
else | |||
pa[16:14] = 3'b111; //last bank fixed | |||
end | |||
//256KB | |||
else | |||
begin | |||
if (pa14_eff == 0) //$8000-BFFF | |||
pa[17:14] = prg_inner_bank[3:0]; | |||
else | |||
pa[17:14] = 4'b1111; //last bank fixed | |||
end | |||
end | |||
end | |||
endmodule | |||
</pre> | |||
== Implementation notes == | == Implementation notes == | ||
Line 130: | Line 290: | ||
* Register $80: 6 bits | * Register $80: 6 bits | ||
* Register $81: 2 bits (D4 is directed to register 0) | * Register $81: 2 bits (D4 is directed to register 0) | ||
After synthesizing and laying fitting within a XC9536XL CPLD 28/36 Macrocells were consumed (78%). | |||
Additionally this design requires 25/34 available pins on the XC9536XL. | |||
[[Category:Multicart mappers]] | [[Category:Multicart mappers]] |
Revision as of 09:21, 6 October 2012
This is a sketch of a mapper that allows making a multicart of games that use multiple discrete mappers. 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).
Registers
- $5000-$5FFF
- Register select
- $8000-$FFFF
- Register value
$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.
$00: CHR bank
7654 3210 M BB | ++- Set CHR RAM A14-A13 +------ Set CIRAM A10 if H/V mirroring is disabled
$01: Inner bank
7654 3210 M BBBB | ++++- Set current PRG ROM bank +------ Set CIRAM A10 if H/V mirroring is disabled
$80: Mode
7654 3210 SS PPMM || ||++- Nametable mirroring mode || ++--- PRG bank mode ++------ PRG outer bank size
Mode | Effect |
---|---|
0 | 1-screen lower bank |
1 | 1-screen upper bank |
2 | Vertical (and ignore writes to bit 4 of registers $00 and $01) |
3 | Horizontal (and ignore writes to bit 4 of registers $00 and $01) |
Mode | Effect |
---|---|
0, 1 | Current 32 KiB bank in $8000-$FFFF |
2 | Bottom half of outer bank in $8000-$BFFF and current bank in $C000-$FFFF |
3 | Current bank in $8000-$BFFF and top half of outer bank in $C000-$FFFF |
Mode | Effect |
---|---|
0 | A20-A15 controlled by outer bank (32 KiB) |
1 | A20-A16 controlled by outer bank (64 KiB) |
2 | A20-A17 controlled by outer bank (128 KiB) |
3 | A20-A18 controlled by outer bank (256 KiB) |
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:
Mode value | PRG bank mode | Outer bank size | Bank in $8000-$BFFF | Bank in $C000-$FFFF |
---|---|---|---|---|
$00-$07 | 32 KiB | 32 KiB | $12 bottom (xxxx) | $12 top (xxxx) |
$08-$0B | Fixed $8000 | 32 KiB | $12 bottom (xxxx) | $12 top (xxx1) |
$0C-$0F | Fixed $C000 | 32 KiB | $12 top (xxx1) | $12 top (xxxx) |
$10-$17 | 32 KiB | 64 KiB | $13 bottom (xxx1) | $13 top (xxx1) |
$18-$1B | Fixed $8000 | 64 KiB | $12 bottom (xxxx) | $13 top (xx11) |
$1C-$1F | Fixed $C000 | 64 KiB | $13 top (xx11) | $12 top (xxxx) |
$20-$27 | 32 KiB | 128 KiB | $13 bottom (xx11) | $13 top (xx11) |
$28-$2B | Fixed $8000 | 128 KiB | $12 bottom (xxxx) | $13 top (x111) |
$2C-$2F | Fixed $C000 | 128 KiB | $13 top (x111) | $12 top (xxxx) |
$30-$37 | 32 KiB | 256 KiB | $17 bottom (x111) | $17 top (x111) |
$38-$3B | Fixed $8000 | 256 KiB | $12 bottom (xxxx) | $13 top (0111) |
$3C-$3F | Fixed $C000 | 256 KiB | $13 top (0111) | $12 top (xxxx) |
$81: Outer bank
7654 3210 BB BBBB ++-++++- Set outer PRG ROM bank
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
Verilog Code
//-------------------------------------------------------------------------------- // Tepples' Multi-discrete mapper // Designed for use with a multicart that supports many discrete mappers // 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 Sept 2012 // Modified: 6 Sept 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 // // NOTE: BEWARE, This implementation is UNTESETED! //-------------------------------------------------------------------------------- module mmc3 ( input m2, //clock input negedge triggered 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-13 input input [11:10] chr_addr, //CHR A12-10 inputj output reg[20:14] pa, //PRG ROM A18-13 outputs output [14:13] ca, //CHR ROM/RAM A17-10 outputs output reg p_ce, //PRG-ROM /CE signal output output reg ciram_a10 //CIRAM A10 output // output reg debug, //debug pin output ); wire write_reg_sel, write_reg_val; //active high when writing to $5000->reg_sel, $8000->reg_val assign write_reg_sel = (prg_addr[14] & ~prg_addr[13] & prg_addr[12] & m2 & prg_ce & ~prg_rw); assign write_reg_val = (~prg_ce & prg_rw); reg [1:0] reg_sel; //register select S: supervisor=1/user=0 (D7), R: register select (D0) //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. //$00 CHR bank: used for CNROM, and one screen mirroring reg [1:0] chr_bank; //D1-0 set CHR RAM A14-13 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 //chr bankswitching assign ca = chr_bank; //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 @ (pa14_eff, prg_ce, prg_rw, M, prg_inner_bank, prg_size, prg_mode, mirror_mode, prg_outer_bank, chr_addr, prg_addr) begin //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 pa[20:18] = prg_outer_bank[5:4]; //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[17:15] = prg_outer_bank[3:0]; //select bank pa[14] = prg_addr[14]; //32KB banks end else // modes 2 & 3 standard UNROM 16KB banks last fixed begin //64KB and useless 32KB if (prg_size[1] == 0) begin pa[17:16] = prg_outer_bank[3:2]; //set to 64KB if (pa14_eff == 0) //$8000-BFFF pa[15:14] = prg_inner_bank[1:0]; else pa[15:14] = 2'b11; //last bank fixed end //128KB else if (prg_size == 2) begin pa[17] = prg_outer_bank[3]; //set to 128KB if (pa14_eff == 0) //$8000-BFFF pa[16:14] = prg_inner_bank[2:0]; else pa[16:14] = 3'b111; //last bank fixed end //256KB else begin if (pa14_eff == 0) //$8000-BFFF pa[17:14] = prg_inner_bank[3:0]; else pa[17:14] = 4'b1111; //last bank fixed end end end endmodule
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 $00: 6 bits (D4 is directed to register 0)
- Register $01: 4 bits (D4 is directed to register 0)
- Register $80: 6 bits
- Register $81: 2 bits (D4 is directed to register 0)
After synthesizing and laying fitting within a XC9536XL CPLD 28/36 Macrocells were consumed (78%). Additionally this design requires 25/34 available pins on the XC9536XL.