Action 53 mapper/Verilog

From NESdev Wiki
Jump to navigationJump to search

infiniteneslives

//--------------------------------------------------------------------------------
//	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

kevtris

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