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)
(→‎Verilog Code: restructured UNROM versions' code)
Line 144: Line 144:
// Modifications:
// Modifications:
// 10/6/12: Corrected prg bank switching 27/36Mcells (75%), 25/34 pins.
// 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%)
//
//
// Options:
// Options:
// 8KB WRAM: control available at the cost of 2 Mcells 29/36 (81%)
// 8KB WRAM: control available at the cost of 2 Mcells and
// and 2 pins (WRAM /CE and /WE) 27/34 pins available.
// 2 pins for WRAM /CE and /WE. Leaving 27/34 pins used.
// 1MB PRG-ROM: shaving off PRG ROM A20 saves 1Mcell and 1pin.
// 1MB PRG-ROM: shaving off PRG ROM A20 saves 1Mcell and 1pin.
//
//
Line 185: Line 187:
//$00 CHR bank: used for CNROM, and one screen mirroring
//$00 CHR bank: used for CNROM, and one screen mirroring
reg [1:0] chr_bank; //D1-0 set CHR RAM A14-13
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.
//$01 Inner bank: prg bank for A/B/UxROM etc.
Line 193: Line 194:
reg [1:0] prg_size; //D5-4 0: 32KB, 1: 64KB, 2:128KB, 3:256KB
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] 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
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
//$81 Outer bank: sets the upper PRG ROM bits to select current game
Line 208: Line 209:
always @ (negedge write_reg_val)
always @ (negedge write_reg_val)
begin
begin
if (reg_sel[1] == 0)
//if (reg_sel[1] == 0)
M <= data[4]; //this is written to for both $0x registers
// M <= data[4]; //this is written to for both $0x registers
if (reg_sel[1] == 0  &  mirror[1] == 0)
//$0x registers while in mirror mode 0 & 1 only
mirror[0] <= data[4];
else if (reg_sel == 2)
//$80 register
mirror[0] <= data[0];
 


case (reg_sel)
case (reg_sel)
0:  chr_bank  <= data[1:0];
0:  chr_bank  <= data[1:0];
1:  prg_inner_bank <= data[3:0];
1:  prg_inner_bank <= data[3:0];
      2: {prg_size, prg_mode, mirror_mode} <= data[5:0]; 
      2: {prg_size, prg_mode, mirror[1]} <= data[5:1]; 
      //2: {prg_size, prg_mode, mirror} <= data[5:0]; 
3:  prg_outer_bank <= data[5:0];
3:  prg_outer_bank <= data[5:0];
endcase
endcase
Line 220: Line 229:




//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
//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)
always @ (m2, chr_bank, prg_ce, prg_rw, prg_inner_bank, prg_size, prg_mode, mirror, prg_outer_bank, chr_addr, prg_addr)
begin
begin
Line 246: Line 250:


//mirroring
//mirroring
case (mirror_mode)
case (mirror)
0: ciram_a10 = M;
0: ciram_a10 = mirror[0];
1: ciram_a10 = M;
1: ciram_a10 = mirror[0];
2: ciram_a10 = chr_addr[10]; //vert
2: ciram_a10 = chr_addr[10]; //vert
3: ciram_a10 = chr_addr[11]; //horiz
3: ciram_a10 = chr_addr[11]; //horiz
Line 288: Line 292:
end
end


// the UNROM support was rewritten on 2012-10-06 by tepples, who
else if (prg_mode == 2) // mode 2 standard UNROM 16KB banks last fixed
// 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
begin
pa[14] = prg_addr[14]; //handle it like 32KB mode 0
//32KB
pa[17:15] = prg_outer_bank[2:0];
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


//switchable bank of 32KB UNROM (for sake of completeness only)
else //mode 3 UNROM #180 variant first 16KB bank fixed to first bank
else if (prg_size == 0)
begin
begin
pa[17:15] = prg_outer_bank[2:0]; //set to 32KB
//32KB
pa[14] = prg_inner_bank[0];
if (prg_size == 0)
end
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


//switchable bank of 64KB UNROM
//128KB
else if (prg_size == 1)  
else if (prg_size == 2)  
begin
begin
pa[17:16] = prg_outer_bank[2:1]; //set to 64KB
pa[17] = prg_outer_bank[2]; //set to 128KB
pa[15:14] = prg_inner_bank[1:0];
end


//switchable bank of 128KB UNROM
if (prg_addr[14] == 1) //$C000-FFFF
else if (prg_size == 2)  
pa[16:14] = prg_inner_bank[2:0];
begin
else
pa[17] = prg_outer_bank[2]; //set to 128KB
//first bank fixed to first bank
pa[16:14] = prg_inner_bank[2:0];
pa[16:14] = 3'b000;
end
end


//switchable bank of 256KB UOROM
//256KB
else  
else  
begin
begin
pa[17:14] = prg_inner_bank[3:0];
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
end



Revision as of 01:40, 7 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
Mirroring modes
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)
PRG bank mode
Mode Effect
0, 1 Current 32 KiB bank in $8000-$FFFF
2 Fixed bottom half of outer bank in $8000-$BFFF and current bank in $C000-$FFFF
3 Current bank in $8000-$BFFF and 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
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 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.
//		Reduced mirror[0] freeing 1 Mcell (26/36 73%)
//		Corrected fixed banks for both UxROM versions (26/36 73%)
//	
//	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.
//
//	Possible features with remaining logic:
//	Upto 32KB of WRAM and/or finer CHR bankswitching.
//
//	NOTE:  BEWARE, This implementation is UNTESETED!
//--------------------------------------------------------------------------------


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
	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
);


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 standard, 3: UNROM #180
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
	//if (reg_sel[1] == 0)
	//	M <= data[4];	//this is written to for both $0x registers
	if (reg_sel[1] == 0  &  mirror[1] == 0)	
		//$0x registers while in mirror mode 0 & 1 only
		mirror[0] <= data[4];
	else if (reg_sel == 2)				
		//$80 register
		mirror[0] <= data[0];


	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];  	
	       	//2: {prg_size, prg_mode, mirror} <= data[5:0];  	
		3:  prg_outer_bank <= data[5:0];
	endcase
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
	
	//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 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

	else //mode 3 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

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 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 1MB by shaving off PRG ROM A20 would save 1 Macrocell and 1 pin, if desired.