Action 53 mapper/Verilog
//--------------------------------------------------------------------------------
// INL-ROM Action53 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
//
// mapper design by: Tepples
// verilog code by: infiniteneslives (paul@InfiniteNesLives.com)
// Created: 6 Oct 2012
// Modified: 27 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.
// Reduced mirror[0] freeing 1 Mcell (26/36 73%)
// Corrected fixed banks for both UxROM versions (26/36 73%)
//
// 10/27/12: Fixed several bugs, and tested to Tepples' Action53 interactive
// mapper behavior test of 21Oct12. Everything appears operational at
// this point. 29/36Mcells with 2MB and 8KB WRAM.
//
// 10/28/12: Corrected fix bank behavior, now fixes to current bank based
// on outer_bank and PRG A14 value. No change in logic consumption.
// Script output of mapper behavior test of 21Oct12 matches for 512KB ROM.
//
// Options:
// 8KB WRAM: control available at the cost of 2 Mcells and
// 2 pins for WRAM /CE and /WE. Leaving 27/34 pins used.
// 1MB PRG-ROM: shaving off PRG ROM A20 saves 1Mcell and 1pin.
// note: Hardwiring WRAM R/W and PRG A13 saves 1Mcell of logic each.
//
// Possible features with remaining logic:
// Upto 32KB of WRAM and/or finer CHR bankswitching.
//
//--------------------------------------------------------------------------------
module action53 (
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-12 input
input [11:10] chr_addr, //CHR A11-10 input for mirroring
output reg[20:13] pa, //PRG ROM A20-13 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
);
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
//$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 #180, 3: UNROM standard
reg [1:0] mirror; //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
case (reg_sel)
0: chr_bank <= data[1:0];
1: prg_inner_bank <= data[3:0];
2: {prg_size, prg_mode, mirror[1]} <= data[5:1];
3: prg_outer_bank <= data[5:0];
endcase
if (reg_sel[1] == 0 & mirror[1] == 0)
begin
//$0x registers while in mirror mode 0 & 1 only
mirror[0] <= data[4];
end
else if (reg_sel == 2)
begin
//$80 register
mirror[0] <= data[0];
end
end
//OUTPUTS combinational logic
always @ (m2, chr_bank, prg_ce, prg_rw, prg_inner_bank, prg_size, prg_mode, mirror, prg_outer_bank, chr_addr, prg_addr)
begin
//fix to 16KB PRG ROM banks
pa[13] = prg_addr[13];
//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)
0: ciram_a10 = mirror[0];
1: ciram_a10 = mirror[0];
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
else if (prg_mode == 2) //mode 2 UNROM #180 variant first 16KB bank fixed to first bank
begin
//32KB
if (prg_size == 0)
begin
pa[17:15] = prg_outer_bank[2:0]; //set to 32KB
if (prg_addr[14] == 1) //$C000-FFFF
pa[14] = prg_inner_bank[0];
else
//first bank fixed to first bank
pa[14] = 1'b0;
end
//64KB
else if (prg_size == 1)
begin
pa[17:16] = prg_outer_bank[2:1]; //set to 64KB
if (prg_addr[14] == 1) //$C000-FFFF
pa[15:14] = prg_inner_bank[1:0];
else
//first bank fixed to first bank
pa[15:14] = {prg_outer_bank[0], 1'b0};
end
//128KB
else if (prg_size == 2)
begin
pa[17] = prg_outer_bank[2]; //set to 128KB
if (prg_addr[14] == 1) //$C000-FFFF
pa[16:14] = prg_inner_bank[2:0];
else
//first bank fixed to first bank
pa[16:14] = {prg_outer_bank[1:0], 1'b0};
end
//256KB
else
begin
if (prg_addr[14] == 1) //$C000-FFFF
pa[17:14] = prg_inner_bank[3:0];
else
//first bank fixed to first bank
pa[17:14] = {prg_outer_bank[2:0], 1'b0};
end
end
else // mode 3 standard UNROM 16KB banks last fixed
begin
//32KB
if (prg_size == 0)
begin
pa[17:15] = prg_outer_bank[2:0]; //set to 32KB
if (prg_addr[14] == 0) //$8000-BFFF
pa[14] = prg_inner_bank[0];
else
//last bank fixed to last bank
pa[14] = 1'b1;
end
//64KB
else if (prg_size == 1)
begin
pa[17:16] = prg_outer_bank[2:1]; //set to 64KB
if (prg_addr[14] == 0) //$8000-BFFF
pa[15:14] = prg_inner_bank[1:0];
else
//last bank fixed to last bank
pa[15:14] = {prg_outer_bank[0], 1'b1};
end
//128KB
else if (prg_size == 2)
begin
pa[17] = prg_outer_bank[2]; //set to 128KB
if (prg_addr[14] == 0) //$8000-BFFF
pa[16:14] = prg_inner_bank[2:0];
else
//last bank fixed to last bank
pa[16:14] = {prg_outer_bank[1:0], 1'b1};
end
//256KB
else
begin
if (prg_addr[14] == 0) //$8000-BFFF
pa[17:14] = prg_inner_bank[3:0];
else
//last bank fixed to last bank
pa[17:14] = {prg_outer_bank[2:0], 1'b1};
end
end
end
endmodule
The following code was written by Kevin Horton for his FPGA NES and forms the basis of thefox’s PowerPak mapper:
// "Action 53" mapper by tepples
// rendered into verilog by k.horton
// 11022012
// V4 now with syntax errors fixed!
//
module action53 ( C_A, C_D, C_WR, CLK0, RST, P_A, // inputs
a53prg, a53chr, a53mir // outputs
);
input wire [15:0] C_A; // NES A0-15 (only A12-15 used)
input wire [7:0] C_D; // NES D0-7
input wire C_WR; // NES /WR signal
input wire CLK0; // NES M2 signal
input wire RST; // NES reset signal
input wire [13:0] P_A; // PPU A0-13 (only A10/11 used)
output reg [6:0] a53prg; // output PRG ROM (A14-A20 on ROM)
output reg [1:0] a53chr; // output CHR RAM (A13-A14 on RAM)
output reg a53mir; // CIRAM A10
reg [3:0] inner; // "inner" bank at 01h
reg [5:0] mode; // mode register at 80h
reg [5:0] outer; // "outer" bank at 81h
reg [1:0] selreg; // selector register
always @ (posedge CLK0)
begin
if ((C_A[15:12] == 4'h5) & C_WR) selreg <= {C_D[7], C_D[0]}; // select register
if (RST)
begin
mode[5:2] <= 0; // NROM mode, 32K mode
outer[5:0] <= 6'h3f; // last bank
end
else if (C_A[15] & C_WR)
begin
case (selreg)
2'h0: {mode[0], a53chr} <= {((mode[1]) ? mode[0] : C_D[4]), C_D[1:0]}; // CHR RAM bank
2'h1: {mode[0], inner} <= {((mode[1]) ? mode[0] : C_D[4]), C_D[3:0]}; // "inner" bank
2'h2: {mode} <= {C_D[5:0]}; // mode register
2'h3: {outer} <= {C_D[5:0]}; // "outer" bank
endcase
end
end
always
begin
// mirroring mode
casex(mode[1:0])
2'b0x : a53mir <= {mode[0]}; // 1 screen lower
2'b10 : a53mir <= {P_A[10]}; // vertical
2'b11 : a53mir <= {P_A[11]}; // horizontal
endcase
// PRG ROM bank size select
casex({mode[5:2], C_A[14]})
5'b00_0x_x : a53prg <= {outer[5:0], C_A[14]}; // 32K banks, (B)NROM mode
5'b01_0x_x : a53prg <= {outer[5:1], inner[0], C_A[14]}; // 64K banks, (B)NROM mode
5'b10_0x_x : a53prg <= {outer[5:2], inner[1:0], C_A[14]}; // 128K banks, (B)NROM mode
5'b11_0x_x : a53prg <= {outer[5:3], inner[2:0], C_A[14]}; // 256K banks, (B)NROM mode
5'b00_10_1,
5'b00_11_0 : a53prg <= {outer[5:0], inner[0]}; // 32K banks, UNROM mode
5'b01_10_1,
5'b01_11_0 : a53prg <= {outer[5:1], inner[1:0]}; // 64K banks, UNROM mode
5'b10_10_1,
5'b10_11_0 : a53prg <= {outer[5:2], inner[2:0]}; // 128K banks, UNROM mode
5'b11_10_1,
5'b11_11_0 : a53prg <= {outer[5:3], inner[3:0]}; // 256K banks, UNROM mode
default : a53prg <= {outer[5:0], C_A[14]}; // 16K fixed bank
endcase
end
endmodule