Action 53 mapper/Verilog

From NESdev Wiki
Revision as of 23:21, 27 October 2012 by Infiniteneslives (talk | contribs) (several bug fixes and notes added/modified post test28.nes of 21Oct12)
Jump to navigationJump to search
//--------------------------------------------------------------------------------
//	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
//
//	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.
//	
//	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-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
);


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] = 2'b00;	
		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] = 3'b000;
		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] = 4'b0000;
		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] = 2'b11;	
		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] = 3'b111;
		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] = 4'b1111;
		end
	end

end	
endmodule