1
0
mirror of https://github.com/mist-devel/mist-board.git synced 2026-05-19 20:46:57 +00:00

NES: update from MiSTer repo

This commit is contained in:
Gyorgy Szombathelyi
2019-11-10 00:45:44 +01:00
parent be20434625
commit a5f7eec372
24 changed files with 12231 additions and 6988 deletions

View File

@@ -145,43 +145,43 @@ module NES_mist(
input UART_TX
);
wire [7:0] core_joy_A;
wire [7:0] core_joy_B;
wire [1:0] buttons;
wire [1:0] switches;
// the configuration string is returned to the io controller to allow
// it to control the menu on the OSD
parameter CONF_STR = {
"NES;NES;",
"O1,HQ2X(VGA-Only),OFF,ON;",
"O2,Scanlines,OFF,ON;",
"O3,Joystick swap,OFF,ON;",
"O4,Invert mirroring,OFF,ON;",
"O5,Hide overscan,OFF,ON;",
"O6,Palette,FCEUX,Unsaturated-V6;",
"T7,Reset;",
"V,v0.8;"
"O12,System Type,NTSC,PAL,Dendy;",
"O3,HQ2X(VGA-Only),OFF,ON;",
"O4,Scanlines,OFF,ON;",
"O5,Joystick swap,OFF,ON;",
"O6,Invert mirroring,OFF,ON;",
"O7,Hide overscan,OFF,ON;",
"O8,Palette,FCEUX,Unsaturated-V6;",
"T9,Reset;",
"V,v1.0;"
};
parameter CONF_STR_LEN = 8+25+20+24+27+24+32+9+7;
wire [7:0] status;
wire [31:0] status;
wire arm_reset = status[0];
wire smoothing_osd = status[1];
wire scanlines_osd = status[2];
wire joy_swap = status[3];
wire mirroring_osd = status[4];
wire overscan_osd = status[5];
wire palette2_osd = status[6];
wire reset_osd = status[7];
wire [1:0] system_type = status[2:1];
wire smoothing_osd = status[3];
wire scanlines_osd = status[4];
wire joy_swap = status[5];
wire mirroring_osd = status[6];
wire overscan_osd = status[7];
wire palette2_osd = status[8];
wire reset_osd = status[9];
wire scandoubler_disable;
wire ypbpr;
wire ps2_kbd_clk, ps2_kbd_data;
wire [7:0] core_joy_A;
wire [7:0] core_joy_B;
wire [1:0] buttons;
wire [1:0] switches;
user_io #(.STRLEN(CONF_STR_LEN)) user_io(
user_io #(.STRLEN($size(CONF_STR)>>3)) user_io(
.clk_sys(clk),
.conf_str(CONF_STR),
// the spi interface
@@ -241,18 +241,18 @@ wire [7:0] nes_joy_B = (reset_nes || osd_visible) ? 8'd0 :
wire [5:0] color;
wire joypad_strobe;
wire [1:0] joypad_clock;
wire [21:0] memory_addr;
wire [21:0] memory_addr_cpu, memory_addr_ppu;
wire memory_read_cpu, memory_read_ppu;
wire memory_write;
wire memory_write_cpu, memory_write_ppu;
wire [7:0] memory_din_cpu, memory_din_ppu;
wire [7:0] memory_dout;
wire [7:0] memory_dout_cpu, memory_dout_ppu;
reg [7:0] joypad_bits, joypad_bits2;
reg [7:0] powerpad_d3, powerpad_d4;
reg [1:0] last_joypad_clock;
wire [31:0] dbgadr;
wire [1:0] dbgctr;
reg [1:0] nes_ce;
wire [1:0] nes_ce;
always @(posedge clk) begin
if (reset_nes) begin
@@ -304,34 +304,49 @@ wire [7:0] nes_joy_B = (reset_nes || osd_visible) ? 8'd0 :
led_blink <= led_blink + 13'd1;
end
assign LED = downloading ? 0 : loader_fail ? led_blink[23] : 1;
assign LED = downloading ? 1'b0 : loader_fail ? led_blink[23] : 1'b1;
wire osd_visible;
wire reset_nes = (init_reset || buttons[1] || arm_reset || reset_osd || download_reset || loader_fail);
wire run_nes = (nes_ce == 3); // keep running even when reset, so that the reset can actually do its job!
wire ext_audio = 1;
wire int_audio = 1;
// NES is clocked at every 4th cycle.
always @(posedge clk)
nes_ce <= nes_ce + 1;
NES nes(clk, reset_nes, run_nes,
mapper_flags,
sample, color,
joypad_strobe, joypad_clock, {powerpad_d4[0],powerpad_d3[0],joypad_bits2[0],joypad_bits[0]},
0, //fds_swap
5'b11111, // enable all channels
memory_addr,
memory_read_cpu, memory_din_cpu,
memory_read_ppu, memory_din_ppu,
memory_write, memory_dout,
cycle, scanline,
int_audio,
ext_audio
);
NES nes(
.clk(clk),
.reset_nes(reset_nes),
.sys_type(system_type),
.nes_div(nes_ce),
.mapper_flags(mapper_flags),
.sample(sample),
.color(color),
.joypad_strobe(joypad_strobe),
.joypad_clock(joypad_clock),
.joypad_data({powerpad_d4[0],powerpad_d3[0],joypad_bits2[0],joypad_bits[0]}),
.mic(),
.fds_busy(),
.fds_eject(0),
.diskside_req(),
.diskside(),
.audio_channels(5'b11111), // enable all channels
.ex_sprites(),
.mask(),
.cpumem_addr(memory_addr_cpu),
.cpumem_read(memory_read_cpu),
.cpumem_din(memory_din_cpu),
.cpumem_write(memory_write_cpu),
.cpumem_dout(memory_dout_cpu),
.ppumem_addr(memory_addr_ppu),
.ppumem_read(memory_read_ppu),
.ppumem_write(memory_write_ppu),
.ppumem_din(memory_din_ppu),
.ppumem_dout(memory_dout_ppu),
.cycle(cycle),
.scanline(scanline),
.int_audio(int_audio),
.ext_audio(ext_audio)
);
assign SDRAM_CKE = 1'b1;
@@ -349,7 +364,7 @@ always @(posedge clk) begin
loader_write_data_mem <= loader_write_data;
end
if(nes_ce == 3) begin
if(nes_ce == 1) begin
loader_write_mem <= loader_write_triggered;
if(loader_write_triggered)
loader_write_triggered <= 1'b0;
@@ -358,48 +373,52 @@ end
sdram sdram (
// interface to the MT48LC16M16 chip
.sd_data ( SDRAM_DQ ),
.sd_addr ( SDRAM_A ),
.sd_dqm ( {SDRAM_DQMH, SDRAM_DQML} ),
.sd_cs ( SDRAM_nCS ),
.sd_ba ( SDRAM_BA ),
.sd_we ( SDRAM_nWE ),
.sd_ras ( SDRAM_nRAS ),
.sd_cas ( SDRAM_nCAS ),
.sd_data ( SDRAM_DQ ),
.sd_addr ( SDRAM_A ),
.sd_dqm ( {SDRAM_DQMH, SDRAM_DQML} ),
.sd_cs ( SDRAM_nCS ),
.sd_ba ( SDRAM_BA ),
.sd_we ( SDRAM_nWE ),
.sd_ras ( SDRAM_nRAS ),
.sd_cas ( SDRAM_nCAS ),
// system interface
.clk ( clk85 ),
.clkref ( nes_ce[1] ),
.init ( !clock_locked ),
.clk ( clk85 ),
.clkref ( nes_ce[1] ),
.init ( !clock_locked ),
// cpu/chipset interface
.addr ( downloading ? {3'b000, loader_addr_mem} : {3'b000, memory_addr} ),
.addrA ( downloading ? {3'b000, loader_addr_mem} : {3'b000, memory_addr_cpu} ),
.addrB ( {3'b000, memory_addr_ppu} ),
.we ( memory_write || loader_write_mem ),
.din ( downloading ? loader_write_data_mem : memory_dout ),
.oeA ( memory_read_cpu ),
.doutA ( memory_din_cpu ),
.oeB ( memory_read_ppu ),
.doutB ( memory_din_ppu )
.weA ( memory_write_cpu || loader_write_mem ),
.weB ( memory_write_ppu ),
.dinA ( downloading ? loader_write_data_mem : memory_dout_cpu ),
.dinB ( memory_dout_ppu ),
.oeA ( memory_read_cpu ),
.doutA ( memory_din_cpu ),
.oeB ( memory_read_ppu ),
.doutB ( memory_din_ppu )
);
wire downloading;
data_io data_io (
.sck ( SPI_SCK ),
.ss ( SPI_SS2 ),
.sdi ( SPI_DI ),
.sck ( SPI_SCK ),
.ss ( SPI_SS2 ),
.sdi ( SPI_DI ),
.downloading ( downloading ),
.size ( ),
.downloading ( downloading ),
.size ( ),
// ram interface
.clk ( clk ),
.wr ( loader_clk ),
.a ( ),
.d ( loader_input )
.clk ( clk ),
.wr ( loader_clk ),
.a ( ),
.d ( loader_input )
);
video video (

View File

@@ -24,34 +24,38 @@
module sdram (
// interface to the MT48LC16M16 chip
inout reg [15:0] sd_data, // 16 bit bidirectional data bus
output reg [12:0] sd_addr, // 13 bit multiplexed address bus
output reg [1:0] sd_dqm, // two byte masks
output reg [1:0] sd_ba, // two banks
output reg sd_cs, // a single chip select
output reg sd_we, // write enable
output reg sd_ras, // row address select
output reg sd_cas, // columns address select
inout reg [15:0] sd_data, // 16 bit bidirectional data bus
output reg [12:0] sd_addr, // 13 bit multiplexed address bus
output reg [1:0] sd_dqm, // two byte masks
output reg [1:0] sd_ba, // two banks
output reg sd_cs, // a single chip select
output reg sd_we, // write enable
output reg sd_ras, // row address select
output reg sd_cas, // columns address select
// cpu/chipset interface
input init, // init signal after FPGA config to initialize RAM
input clk, // sdram is accessed at up to 128MHz
input clkref, // reference clock to sync to
input init, // init signal after FPGA config to initialize RAM
input clk, // sdram is accessed at up to 128MHz
input clkref, // reference clock to sync to
input [24:0] addr, // 25 bit byte address
input we, // cpu/chipset requests write
input [7:0] din, // data input from chipset/cpu
input oeA, // cpu requests data
output reg [7:0] doutA, // data output to cpu
input oeB, // ppu requests data
output reg [7:0] doutB // data output to ppu
input [24:0] addrA, // 25 bit byte address
input weA, // cpu/chipset requests write
input [7:0] dinA, // data input from chipset/cpu
input oeA, // cpu requests data
output reg [7:0] doutA, // data output to cpu
input [24:0] addrB, // 25 bit byte address
input weB, // cpu/chipset requests write
input [7:0] dinB, // data input from chipset/cpu
input oeB, // ppu requests data
output reg [7:0] doutB // data output to ppu
);
// no burst configured
localparam RASCAS_DELAY = 3'd2; // tRCD=20ns -> 2 cycles@85MHz
localparam BURST_LENGTH = 3'b000; // 000=1, 001=2, 010=4, 011=8
localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved
localparam CAS_LATENCY = 3'd3; // 2/3 allowed
localparam CAS_LATENCY = 3'd2; // 2/3 allowed
localparam OP_MODE = 2'b00; // only 00 (standard operation) allowed
localparam NO_WRITE_BURST = 1'b1; // 0= write burst enabled, 1=only single access write
@@ -62,20 +66,23 @@ localparam MODE = { 3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, B
// ------------------------ cycle state machine ------------------------
// ---------------------------------------------------------------------
localparam STATE_FIRST = 4'd0; // first state in cycle
localparam STATE_CMD_START = 4'd1; // state in which a new command can be started
localparam STATE_CMD_CONT = STATE_CMD_START + RASCAS_DELAY; // 4 command can be continued
localparam STATE_CMD_READ = 4'd7; // read state
localparam STATE_LAST = 4'd15; // last state in cycle
localparam STATE_FIRST = 3'd0; // first state in cycle
localparam STATE_CMD_START = 3'd1; // state in which a new command can be started
localparam STATE_CMD_CONT = STATE_CMD_START + RASCAS_DELAY; // 3 command can be continued
localparam STATE_CMD_READ = STATE_CMD_CONT + CAS_LATENCY + 1'd1; // 6 read state
localparam STATE_LAST = 3'd7; // last state in cycle
reg [3:0] q;
reg [2:0] q;
always @(posedge clk) begin
// SDRAM (state machine) clock is 85MHz. Synchronize this to systems 21.477 Mhz clock
// force counter to pass state LAST->FIRST exactly after the rising edge of clkref
if(((q == STATE_LAST) && ( clkref == 1)) ||
((q == STATE_FIRST) && ( clkref == 0)) ||
((q != STATE_LAST) && (q != STATE_FIRST)))
q <= q + 3'd1;
reg clkref_last;
clkref_last <= clkref;
q <= q + 1'd1;
if (q==STATE_LAST) q<=STATE_FIRST;
if (~clkref_last & clkref) q<=STATE_FIRST + 1'd1;
end
// ---------------------------------------------------------------------
@@ -108,7 +115,12 @@ localparam CMD_LOAD_MODE = 4'b0000;
wire [3:0] sd_cmd; // current command sent to sd ram
wire oe = oeA || oeB;
// clkref high - CPU
// clkref low - PPU
wire oe = clkref ? oeA : oeB;
wire we = clkref ? weA : weB;
wire [24:0] addr = clkref ? addrA : addrB;
wire [7:0] din = clkref ? dinA : dinB;
reg addr0;
always @(posedge clk)
@@ -118,8 +130,8 @@ wire [7:0] dout = addr0?sd_data[7:0]:sd_data[15:8];
always @(posedge clk) begin
if(q == STATE_CMD_READ) begin
if(oeA) doutA <= dout;
if(oeB) doutB <= dout;
if(oeA && clkref) doutA <= dout;
if(oeB && !clkref) doutB <= dout;
end
end
@@ -130,15 +142,15 @@ wire [3:0] reset_cmd =
wire [3:0] run_cmd =
((we || oe) && (q == STATE_CMD_START))?CMD_ACTIVE:
(we && (q == STATE_CMD_CONT ))?CMD_WRITE:
(!we && oe && (q == STATE_CMD_CONT ))?CMD_READ:
( we && (q == STATE_CMD_CONT ))?CMD_WRITE:
(!we && oe && (q == STATE_CMD_CONT ))?CMD_READ:
(!we && !oe && (q == STATE_CMD_START))?CMD_AUTO_REFRESH:
CMD_INHIBIT;
assign sd_cmd = (reset != 0)?reset_cmd:run_cmd;
wire [12:0] reset_addr = (reset == 13)?13'b0010000000000:MODE;
wire [12:0] run_addr =
(q == STATE_CMD_START)?addr[21:9]:{ 4'b0010, addr[24], addr[8:1]};
@@ -149,11 +161,11 @@ always @(posedge clk) begin
// the eight bits are sent on both bytes ports. Which one's actually
// written depends on the state of dqm of which only one is active
// at a time when writing
sd_data <= we?{din, din}:16'bZZZZZZZZZZZZZZZZ;
sd_data <= we?{ din, din }:16'bZZZZZZZZZZZZZZZZ;
sd_addr <= (reset != 0)?reset_addr:run_addr;
sd_ba <= addr[23:22];
sd_ba <= (reset != 0)?2'b00:addr[23:22];
sd_dqm <= we?{ addr[0], ~addr[0] }:2'b00;

View File

@@ -122,8 +122,8 @@ set_location_assignment PIN_43 -to SDRAM_CLK
set_global_assignment -name CYCLONEII_OPTIMIZATION_TECHNIQUE BALANCED
set_global_assignment -name SMART_RECOMPILE ON
set_global_assignment -name ENABLE_SIGNALTAP OFF
set_global_assignment -name USE_SIGNALTAP_FILE mist/stp.stp
set_global_assignment -name ENABLE_SIGNALTAP OFF
set_global_assignment -name USE_SIGNALTAP_FILE out/sdram.stp
set_global_assignment -name PHYSICAL_SYNTHESIS_COMBO_LOGIC ON
set_global_assignment -name PHYSICAL_SYNTHESIS_REGISTER_DUPLICATION ON
set_global_assignment -name PHYSICAL_SYNTHESIS_EFFORT NORMAL
@@ -136,7 +136,7 @@ set_global_assignment -name TPD_REQUIREMENT "2 ns"
set_global_assignment -name TSU_REQUIREMENT "2 ns"
set_global_assignment -name TCO_REQUIREMENT "2 ns"
set_global_assignment -name ALLOW_POWER_UP_DONT_CARE OFF
set_global_assignment -name SYNTH_TIMING_DRIVEN_SYNTHESIS OFF
set_global_assignment -name SYNTH_TIMING_DRIVEN_SYNTHESIS ON
set_global_assignment -name AUTO_RAM_TO_LCELL_CONVERSION OFF
set_global_assignment -name AUTO_RAM_RECOGNITION ON
set_global_assignment -name AUTO_ROM_RECOGNITION ON
@@ -156,10 +156,10 @@ set_global_assignment -name RESERVE_DATA0_AFTER_CONFIGURATION "USE AS REGULAR IO
set_global_assignment -name RESERVE_DATA1_AFTER_CONFIGURATION "USE AS REGULAR IO"
set_global_assignment -name RESERVE_FLASH_NCE_AFTER_CONFIGURATION "USE AS REGULAR IO"
set_global_assignment -name RESERVE_DCLK_AFTER_CONFIGURATION "USE AS REGULAR IO"
set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -rise
set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -fall
set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -rise
set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -fall
set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -rise
set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -fall
set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -rise
set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -fall
@@ -281,30 +281,6 @@ set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_n
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_nCS
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_CKE
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_CLK
set_global_assignment -name VHDL_FILE src/OPLL/dpram.vhd
set_global_assignment -name QIP_FILE src/OPLL/OPLL.qip
set_global_assignment -name QIP_FILE src/t65/t65.qip
set_global_assignment -name VERILOG_FILE src/mapLoopy.v
set_global_assignment -name SYSTEMVERILOG_FILE src/apu.sv
set_global_assignment -name SDC_FILE mist/constraints.sdc
set_global_assignment -name VERILOG_FILE mist/ps2_intf.v
set_global_assignment -name VERILOG_FILE mist/keyboard.v
set_global_assignment -name VERILOG_FILE mist/sigma_delta_dac.v
set_global_assignment -name VERILOG_FILE mist/data_io.v
set_global_assignment -name VERILOG_FILE mist/sdram.v
set_global_assignment -name VERILOG_FILE mist/user_io.v
set_global_assignment -name VERILOG_FILE mist/osd.v
set_global_assignment -name VERILOG_FILE mist/clk.v
set_global_assignment -name VERILOG_FILE src/compat.v
set_global_assignment -name VERILOG_FILE src/ppu.v
set_global_assignment -name VERILOG_FILE src/mmu.v
set_global_assignment -name SYSTEMVERILOG_FILE src/video_mixer.sv
set_global_assignment -name VERILOG_FILE src/video.v
set_global_assignment -name VERILOG_FILE src/nes.v
set_global_assignment -name VERILOG_FILE src/MicroCode.v
set_global_assignment -name VERILOG_FILE src/hq2x.v
set_global_assignment -name VERILOG_FILE src/dsp.v
set_global_assignment -name VERILOG_FILE mist/NES_mist.v
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to VGA_HS
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to VGA_VS
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to VGA_R[*]
@@ -312,4 +288,42 @@ set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to VGA_G[*
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to VGA_B[*]
set_location_assignment PLL_1 -to clock_21mhz|altpll_component|auto_generated|pll1
set_global_assignment -name ENABLE_CONFIGURATION_PINS OFF
set_global_assignment -name ENABLE_NCE_PIN OFF
set_global_assignment -name ENABLE_BOOT_SEL_PIN OFF
set_global_assignment -name SYSTEMVERILOG_FILE mist/NES_mist.sv
set_global_assignment -name VERILOG_FILE mist/ps2_intf.v
set_global_assignment -name VERILOG_FILE mist/keyboard.v
set_global_assignment -name VERILOG_FILE mist/sigma_delta_dac.v
set_global_assignment -name VERILOG_FILE mist/data_io.v
set_global_assignment -name VERILOG_FILE mist/sdram.v
set_global_assignment -name VERILOG_FILE mist/user_io.v
set_global_assignment -name VERILOG_FILE mist/osd.v
set_global_assignment -name VERILOG_FILE mist/clk.v
set_global_assignment -name SDC_FILE mist/constraints.sdc
set_global_assignment -name VERILOG_FILE src/nes.v
set_global_assignment -name SYSTEMVERILOG_FILE src/cart.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/apu.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/ppu.sv
set_global_assignment -name VERILOG_FILE src/compat.v
set_global_assignment -name SYSTEMVERILOG_FILE src/video_mixer.sv
set_global_assignment -name VERILOG_FILE src/video.v
set_global_assignment -name VERILOG_FILE src/hq2x.v
set_global_assignment -name VERILOG_FILE src/dsp.v
set_global_assignment -name VHDL_FILE src/dpram.vhd
set_global_assignment -name SYSTEMVERILOG_FILE src/EEPROM_24C0x.sv
set_global_assignment -name QIP_FILE src/t65/t65.qip
set_global_assignment -name QIP_FILE src/OPLL/OPLL.qip
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/VRC.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/Sunsoft.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/Sachen.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/Namco.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/MMC5.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/MMC3.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/MMC2.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/MMC1.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/misc.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/JYCompany.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/generic.sv
set_global_assignment -name SYSTEMVERILOG_FILE src/mappers/FDS.sv
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

View File

@@ -0,0 +1,120 @@
// 24C01, 24C02 EEPROM support
// by GreyRogue for NES MiSTer
module EEPROM_24C0x
(//Replace type_24C01 with EEPROM size and command size (Test State y/n and address bytes)?
input type_24C01, //24C01 is 128 bytes/no Test State?, 24C02 is 256 bytes/Test State
input clk, input ce, input reset,
input SCL, // Serial Clock
input SDA_in, // Serial Data (same pin as below, split for convenience)
output reg SDA_out, // Serial Data (same pin as above, split for convenience)
input [2:0] E_id, // Chip Enable ID
input WC_n, // ~Write Control
input [7:0] data_from_ram, // Data read from RAM
output [7:0] data_to_ram, // Data written to RAM
output [7:0] ram_addr, // RAM Address
output reg ram_read, // RAM read
output reg ram_write, // RAM write
input ram_done); // RAM access done
typedef enum bit [2:0] { STATE_STANDBY, STATE_TEST, STATE_ADDRESS, STATE_WRITE, STATE_READ } mystate;
mystate state;
reg [9:0] command;
reg [7:0] address;
reg [8:0] data; // 8 bits data, plus ack bit
reg last_SCL;
reg last_SDA;
//Some 24C01 documents show it working with a test state, instead of combined address/read/write.
//If these are used, NoTestState needs to be an input to the module, not just derived from the type.
wire NoTestState = type_24C01;
always @(posedge clk) if (reset) begin
state <= STATE_STANDBY;
command <= 0;
last_SCL <= 0;
last_SDA <= 0;
SDA_out <= 1; //NoAck
ram_read <= 0;
ram_write <= 0;
end else if (ce) begin
last_SCL <= SCL;
last_SDA <= SDA_in;
if (ram_write && ram_done) begin
ram_write <= 0;
address[3:0] <= address[3:0] + 4'b1; //Increment wraps at 16 byte boundary
end
if (ram_read && ram_done) begin
ram_read <= 0;
data <= {data_from_ram, 1'b1}; //NoAck at end
address <= address + 8'b1;
end
if (SCL && last_SCL && !SDA_in && last_SDA) begin
if (NoTestState)
state <= STATE_ADDRESS;
else
state <= STATE_TEST;
command <= 10'd2;
end else if (SCL && last_SCL && SDA_in && !last_SDA) begin
state <= STATE_STANDBY;
command <= 10'd0;
end else if (state == STATE_STANDBY) begin
// Do nothing
end else if (SCL && !last_SCL) begin
command <= {command[8:0], SDA_in };
end else if (!SCL && last_SCL) begin
SDA_out <= 1; //NoAck
if (state == STATE_READ) begin
SDA_out <= data[8];
if (!ram_read) begin
data[8:1] <= data[7:0];
end
end
if (command[9]) begin
if (state == STATE_TEST) begin
if (command[7:1] == {4'b1010, E_id}) begin
if (command[0]) begin
state <= STATE_READ;
ram_read <= 1;
end else begin
state <= STATE_ADDRESS;
end
SDA_out <= 0; //Ack
end
command <= 10'd1;
end else if (state == STATE_ADDRESS) begin
if (NoTestState) begin
address <= {1'b0,command[7:1]};
if (command[0]) begin
state <= STATE_READ;
ram_read <= 1;
end else begin
state <= STATE_WRITE;
end
end else begin
address <= command[7:0];
state <= STATE_WRITE;
end
SDA_out <= 0; //Ack
command <= 10'd1;
end else if (state == STATE_WRITE) begin
data <= {command[7:0], 1'b0};
if (!WC_n) begin
ram_write <= 1;
SDA_out <= 0; //Ack
end
command <= 10'd1;
end else if (state == STATE_READ) begin
ram_read <= 1;
SDA_out <= 1; //NoAck
command <= 10'd1;
end
end
end
end
assign ram_addr = (type_24C01==1) ? {1'b0, address[6:0]} : address;
assign data_to_ram = data[8:1];
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -50,6 +50,7 @@ module SquareChan(input MMC5,
input MW,
input LenCtr_Clock,
input Env_Clock,
input odd_or_even,
input Enabled,
input [7:0] LenCtr_In,
output reg [3:0] Sample,
@@ -72,9 +73,14 @@ reg [2:0] SeqPos;
wire [10:0] ShiftedPeriod = (Period >> SweepShift);
wire [10:0] PeriodRhs = (SweepNegate ? (~ShiftedPeriod + {10'b0, sq2}) : ShiftedPeriod);
wire [11:0] NewSweepPeriod = Period + PeriodRhs;
wire ValidFreq = (MMC5==1) || ((|Period[10:3]) && (SweepNegate || !NewSweepPeriod[11]));
// XXX: This should be enabled for MMC5, but do we really want ultrasonic frequencies?
// Re-enable if we ever get a proper LPF.
wire ValidFreq = /*(MMC5==1) ||*/ ((|Period[10:3]) && (SweepNegate || !NewSweepPeriod[11]));
// |Period[10:3] is equivalent to Period >= 8
//double speed for MMC5=Env_Clock
wire LenCtrClockEnable = (MMC5==0 && LenCtr_Clock) || (MMC5==1 && Env_Clock);
always @(posedge clk) if (reset) begin
LenCtr <= 0;
Duty <= 0;
@@ -132,26 +138,22 @@ always @(posedge clk) if (reset) begin
// Count down the square timer...
if (TimerCtr == 0) begin
// Timer was clocked
TimerCtr <= {Period, 1'b0};
SeqPos <= SeqPos - 1'd1;
end else begin
TimerCtr <= TimerCtr - 1'd1;
// Should be clocked on every even cpu cycle
if (~odd_or_even) begin
if (TimerCtr == 0) begin
// Timer was clocked
TimerCtr <= Period;
SeqPos <= SeqPos - 1'd1;
end else begin
TimerCtr <= TimerCtr - 1'd1;
end
end
if (MMC5==0) begin
// Clock the length counter?
if (LenCtr_Clock && LenCtr != 0 && !LenCtrHalt) begin
if (LenCtrClockEnable && LenCtr != 0 && !LenCtrHalt) begin
LenCtr <= LenCtr - 1'd1;
end
else
// Clock the length counter? //double speed for MMC5=Env_Clock
if (Env_Clock && LenCtr != 0 && !LenCtrHalt) begin
LenCtr <= LenCtr - 1'd1;
end
end
// Clock the sweep unit?
if (LenCtr_Clock) begin
if (SweepDivider == 0) begin
@@ -216,7 +218,7 @@ module TriangleChan(input clk, input ce, input reset,
input LinCtr_Clock,
input Enabled,
input [7:0] LenCtr_In,
output [3:0] Sample,
output reg [3:0] Sample,
output IsNonZero);
//
reg [10:0] Period, TimerCtr;
@@ -239,7 +241,7 @@ module TriangleChan(input clk, input ce, input reset,
SeqPos <= 0;
LinCtrPeriod <= 0;
LinCtr <= 0;
LinCtrl <= 0;
//LinCtrl <= 0; do not reset
LinHalt <= 0;
LenCtr <= 0;
end else if (ce) begin
@@ -292,7 +294,10 @@ module TriangleChan(input clk, input ce, input reset,
SeqPos <= SeqPos + 1'd1;
end
// Generate the output
assign Sample = SeqPos[3:0] ^ {4{~SeqPos[4]}};
// XXX: Ultrisonic frequencies cause issues, so are disabled.
// This can be removed for accuracy if a proper LPF is ever implemented.
always @(posedge clk)
Sample <= (Period > 1) ? SeqPos[3:0] ^ {4{~SeqPos[4]}} : Sample;
//
endmodule
@@ -325,22 +330,22 @@ module NoiseChan(input clk, input ce, input reset,
reg [11:0] NoisePeriod, TimerCtr;
always @* begin
case (Period)
0: NoisePeriod = 12'h004;
1: NoisePeriod = 12'h008;
2: NoisePeriod = 12'h010;
3: NoisePeriod = 12'h020;
4: NoisePeriod = 12'h040;
5: NoisePeriod = 12'h060;
6: NoisePeriod = 12'h080;
7: NoisePeriod = 12'h0A0;
8: NoisePeriod = 12'h0CA;
9: NoisePeriod = 12'h0FE;
10: NoisePeriod = 12'h17C;
11: NoisePeriod = 12'h1FC;
12: NoisePeriod = 12'h2FA;
13: NoisePeriod = 12'h3F8;
14: NoisePeriod = 12'h7F2;
15: NoisePeriod = 12'hFE4;
0: NoisePeriod = 12'd4;
1: NoisePeriod = 12'd8;
2: NoisePeriod = 12'd16;
3: NoisePeriod = 12'd32;
4: NoisePeriod = 12'd64;
5: NoisePeriod = 12'd96;
6: NoisePeriod = 12'd128;
7: NoisePeriod = 12'd160;
8: NoisePeriod = 12'd202;
9: NoisePeriod = 12'd254;
10: NoisePeriod = 12'd380;
11: NoisePeriod = 12'd508;
12: NoisePeriod = 12'd762;
13: NoisePeriod = 12'd1016;
14: NoisePeriod = 12'd2034;
15: NoisePeriod = 12'd4068;
endcase
end
//
@@ -351,11 +356,11 @@ module NoiseChan(input clk, input ce, input reset,
Volume <= 0;
Envelope <= 0;
EnvDivider <= 0;
LenCtr <= 0;
LenCtr <= 1;
ShortMode <= 0;
Shift <= 1;
Period <= 0;
TimerCtr <= 0;
TimerCtr <= NoisePeriod - 1'b1;
end else if (ce) begin
// Check if writing to the regs of this channel
if (MW) begin
@@ -377,7 +382,7 @@ module NoiseChan(input clk, input ce, input reset,
end
// Count down the period timer...
if (TimerCtr == 0) begin
TimerCtr <= NoisePeriod;
TimerCtr <= NoisePeriod - 1'b1;
// Clock the shift register. Use either
// bit 1 or 6 as the tap.
Shift <= {
@@ -427,6 +432,7 @@ module DmcChan(input MMC5,
output [15:0] DmaAddr, // Address DMC wants to read
input [7:0] DmaData, // Input data to DMC from memory.
output Irq,
input PAL,
output IsDmcActive);
reg IrqEnable;
reg IrqActive;
@@ -459,6 +465,13 @@ module DmcChan(input MMC5,
106, 84, 72, 54
};
wire [8:0] NewPeriodPAL[16] = '{
398, 354, 316, 298,
276, 236, 210, 198,
176, 148, 132, 118,
98, 78, 66, 50
};
// Shift register initially loaded with 07
always @(posedge clk) begin
if (reset) begin
@@ -468,16 +481,16 @@ module DmcChan(input MMC5,
Freq <= 0;
Dac <= 0;
SampleAddress <= 0;
SampleLen <= 0;
ShiftReg <= 8'hff;
SampleLen <= 1;
ShiftReg <= 8'h0; // XXX: should be 0 or 07? Visual 2C02 says 0, as does Mesen.
Cycles <= 439;
Address <= 0;
Address <= 15'h4000;
BytesLeft <= 0;
BitsUsed <= 0;
SampleBuffer <= 0;
HasSampleBuffer <= 0;
HasShiftReg <= 0;
DmcEnabled <= 0;
DmcEnabled <= 1;
ActivationDelay <= 0;
end else if (ce) begin
if (ActivationDelay == 3 && !odd_or_even) ActivationDelay <= 1;
@@ -516,7 +529,7 @@ module DmcChan(input MMC5,
Cycles <= Cycles - 1'd1;
if (Cycles == 1) begin
Cycles <= NewPeriod[Freq];
Cycles <= PAL ? NewPeriodPAL[Freq] : NewPeriod[Freq];
if (HasShiftReg) begin
if (ShiftReg[0]) begin
Dac[6:1] <= (Dac[6:1] != 6'b111111) ? Dac[6:1] + 6'b000001 : Dac[6:1];
@@ -551,61 +564,9 @@ module DmcChan(input MMC5,
end
endmodule
module ApuLookupTable
(
input clk,
input [7:0] in_a,
input [7:0] in_b,
output reg [15:0] out
);
wire [15:0] lookup_a[256] = '{
0, 760, 1503, 2228, 2936, 3627, 4303, 4963, 5609, 6240, 6858, 7462, 8053, 8631, 9198, 9752,
10296, 10828, 11349, 11860, 12361, 12852, 13334, 13807, 14270, 14725, 15171, 15609, 16039, 16461, 16876, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
wire [15:0] lookup_b[256] = '{
0, 439, 874, 1306, 1735, 2160, 2581, 2999, 3414, 3826, 4234, 4639, 5041, 5440, 5836, 6229,
6618, 7005, 7389, 7769, 8147, 8522, 8895, 9264, 9631, 9995, 10356, 10714, 11070, 11423, 11774, 12122,
12468, 12811, 13152, 13490, 13825, 14159, 14490, 14818, 15145, 15469, 15791, 16110, 16427, 16742, 17055, 17366,
17675, 17981, 18286, 18588, 18888, 19187, 19483, 19777, 20069, 20360, 20648, 20935, 21219, 21502, 21783, 22062,
22339, 22615, 22889, 23160, 23431, 23699, 23966, 24231, 24494, 24756, 25016, 25274, 25531, 25786, 26040, 26292,
26542, 26791, 27039, 27284, 27529, 27772, 28013, 28253, 28492, 28729, 28964, 29198, 29431, 29663, 29893, 30121,
30349, 30575, 30800, 31023, 31245, 31466, 31685, 31904, 32121, 32336, 32551, 32764, 32976, 33187, 33397, 33605,
33813, 34019, 34224, 34428, 34630, 34832, 35032, 35232, 35430, 35627, 35823, 36018, 36212, 36405, 36597, 36788,
36978, 37166, 37354, 37541, 37727, 37912, 38095, 38278, 38460, 38641, 38821, 39000, 39178, 39355, 39532, 39707,
39881, 40055, 40228, 40399, 40570, 40740, 40909, 41078, 41245, 41412, 41577, 41742, 41906, 42070, 42232, 42394,
42555, 42715, 42874, 43032, 43190, 43347, 43503, 43659, 43813, 43967, 44120, 44273, 44424, 44575, 44726, 44875,
45024, 45172, 45319, 45466, 45612, 45757, 45902, 46046, 46189, 46332, 46474, 46615, 46756, 46895, 47035, 47173,
47312, 47449, 47586, 47722, 47857, 47992, 48127, 48260, 48393, 48526, 48658, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
always @(posedge clk) begin
out <= lookup_a[in_a] + lookup_b[in_b];
end
endmodule
module APU(input MMC5,
input clk, input ce, input reset,
input PAL,
input [4:0] ADDR, // APU Address Line
input [7:0] DIN, // Data to APU
output [7:0] DOUT, // Data from APU
@@ -614,12 +575,12 @@ module APU(input MMC5,
input [4:0] audio_channels, // Enabled audio channels
output [15:0] Sample,
output DmaReq, // 1 when DMC wants DMA
output DmaReq, // 1 when DMC wants DMA
input DmaAck, // 1 when DMC byte is on DmcData. DmcDmaRequested should go low.
output [15:0] DmaAddr, // Address DMC wants to read
input [7:0] DmaData, // Input data to DMC from memory.
output odd_or_even,
input odd_or_even,
output IRQ); // IRQ asserted
// Which channels are enabled?
@@ -654,16 +615,14 @@ reg [15:0] Cycles;
reg ClkE, ClkL;
reg Wrote4017;
reg [1:0] IrqCtr;
reg InternalClock; // APU Differentiates between Even or Odd clocks
assign odd_or_even = InternalClock;
// Generate each channel
SquareChan Sq1(MMC5, clk, ce, reset, 1'b0, ADDR[1:0], DIN, ApuMW0, ClkL, ClkE, Enabled[0], LenCtr_In, Sq1Sample, Sq1NonZero);
SquareChan Sq2(MMC5, clk, ce, reset, 1'b1, ADDR[1:0], DIN, ApuMW1, ClkL, ClkE, Enabled[1], LenCtr_In, Sq2Sample, Sq2NonZero);
SquareChan Sq1(MMC5, clk, ce, reset, 1'b0, ADDR[1:0], DIN, ApuMW0, ClkL, ClkE, odd_or_even, Enabled[0], LenCtr_In, Sq1Sample, Sq1NonZero);
SquareChan Sq2(MMC5, clk, ce, reset, 1'b1, ADDR[1:0], DIN, ApuMW1, ClkL, ClkE, odd_or_even, Enabled[1], LenCtr_In, Sq2Sample, Sq2NonZero);
TriangleChan Tri(clk, ce, reset, ADDR[1:0], DIN, ApuMW2, ClkL, ClkE, Enabled[2], LenCtr_In, TriSample, TriNonZero);
NoiseChan Noi(clk, ce, reset, ADDR[1:0], DIN, ApuMW3, ClkL, ClkE, Enabled[3], LenCtr_In, NoiSample, NoiNonZero);
DmcChan Dmc(MMC5, clk, ce, reset, odd_or_even, ADDR[2:0], DIN, ApuMW4, DmcSample, DmaReq, DmaAck, DmaAddr, DmaData, DmcIrq, IsDmcActive);
DmcChan Dmc(MMC5, clk, ce, reset, odd_or_even, ADDR[2:0], DIN, ApuMW4, DmcSample, DmaReq, DmaAck, DmaAddr, DmaData, DmcIrq, PAL, IsDmcActive);
// Reading this register clears the frame interrupt flag (but not the DMC interrupt flag).
// If an interrupt flag was set at the same moment of the read, it will read back as 1 but it will not be cleared.
@@ -683,49 +642,78 @@ reg FrameInterrupt, DisableFrameInterrupt;
// l - l - - 96 Hz
// e e e e - 192 Hz
reg [7:0] last_4017 = 0;
reg [2:0] delayed_clear;
reg delayed_interrupt;
wire set_irq_ntsc = (Cycles == cyc_ntsc[3]) || (Cycles == cyc_ntsc[4]);
wire set_irq_pal = (Cycles == cyc_pal[3]) || (Cycles == cyc_pal[4]);
wire set_irq = ( (PAL ? set_irq_pal : set_irq_ntsc) || delayed_interrupt) && ~DisableFrameInterrupt && ~FrameSeqMode;
int cyc_pal[7] = '{8312, 16626, 24938, 33251, 33252, 41564, 41565};
int cyc_ntsc[7] = '{7456, 14912, 22370, 29828, 29829, 37280, 37281};
always @(posedge clk) if (reset) begin
FrameSeqMode <= 0;
DisableFrameInterrupt <= 0;
delayed_interrupt <= 0;
FrameInterrupt <= 0;
Enabled <= 0;
InternalClock <= 0;
Wrote4017 <= 0;
ClkE <= 0;
ClkL <= 0;
Cycles <= 4; // This needs to be 5 for proper power up behavior
delayed_clear <= 0;
Cycles <= 0;
IrqCtr <= 0;
{FrameSeqMode, DisableFrameInterrupt} <= last_4017[7:6];
end else if (ce) begin
FrameInterrupt <= IrqCtr[1] ? 1'd1 : (ADDR == 5'h15 && MR || ApuMW5 && ADDR[1:0] == 3 && DIN[6]) ? 1'd0 : FrameInterrupt;
InternalClock <= !InternalClock;
FrameInterrupt <= set_irq ? 1'd1 : (ADDR == 5'h15 && MR || ApuMW5 && ADDR[1:0] == 3 && DIN[6]) ? 1'd0 : FrameInterrupt;
IrqCtr <= {IrqCtr[0], 1'b0};
// NesDev's wiki on this is ambiguous and written from a strange perspective. To the best
// of my understanding, the Frame Counter works like this:
// The APU alternates between Read cycles (even) and Write cycles, (odd). The internal counter
// is incremented on every Write cycle, and if it hits certain special points, the clocks are
// generated *on* the next Read cycle. The counter can only be controlled by one thing at a time,
// so resetting the counter must always be on a Write cycle.
//
// In the case of writes to 4017, the internal registers are written normally as the write occurs, so
// that their values are visible on the next CE, however a reset cannot start writing to the counter
// until the *next* Write cycle. It's probably implemented as a flag to tell the counter to reset, that
// is written as part of the register data, and since the counter only runs on Writes, that's when it
// happens.
if (delayed_clear)
delayed_clear <= delayed_clear - 1'b1;
Cycles <= Cycles + 1'd1;
ClkE <= 0;
ClkL <= 0;
if (Cycles == 7457) begin
delayed_interrupt <= 1'b0;
if (Cycles == (PAL ? cyc_pal[0] : cyc_ntsc[0])) begin
ClkE <= 1;
end else if (Cycles == 14913) begin
end else if (Cycles == (PAL ? cyc_pal[1] : cyc_ntsc[1])) begin
ClkE <= 1;
ClkL <= 1;
end else if (Cycles == 22371) begin
end else if (Cycles == (PAL ? cyc_pal[2] : cyc_ntsc[2])) begin
ClkE <= 1;
end else if (Cycles == 29829) begin
end else if (Cycles == (PAL ? cyc_pal[3] : cyc_ntsc[3])) begin
if (!FrameSeqMode) begin
ClkE <= 1;
ClkL <= 1;
Cycles <= 0;
IrqCtr <= 3;
FrameInterrupt <= 1;
end
end else if (Cycles == 37281) begin
end else if (Cycles == (PAL ? cyc_pal[4] : cyc_ntsc[4])) begin
if (!FrameSeqMode) begin
delayed_interrupt <= 1'b1;
Cycles <= 0;
end
end else if (Cycles == (PAL ? cyc_pal[5] : cyc_ntsc[5])) begin
ClkE <= 1;
ClkL <= 1;
end else if (Cycles == (PAL ? cyc_pal[6] : cyc_ntsc[6])) begin
Cycles <= 0;
end
// Handle one cycle delayed write to 4017.
Wrote4017 <= 0;
if (Wrote4017) begin
if (delayed_clear == 1) begin
if (FrameSeqMode) begin
ClkE <= 1;
ClkL <= 1;
@@ -733,31 +721,23 @@ end else if (ce) begin
Cycles <= 0;
end
// if (ClkE||ClkL) $write("%d: Clocking %s%s\n", Cycles, ClkE?"E":" ", ClkL?"L":" ");
// Handle writes to control registers
if (ApuMW5) begin
case (ADDR[1:0])
1: begin // Register $4015
Enabled <= DIN[3:0];
// $write("$4015 = %X\n", DIN);
end
3: begin // Register $4017
FrameSeqMode <= DIN[7]; // 1 = 5 frames cycle, 0 = 4 frames cycle
DisableFrameInterrupt <= DIN[6];
// If the internal clock is even, things happen
// right away.
if (!InternalClock) begin
if (DIN[7]) begin
ClkE <= 1;
ClkL <= 1;
end
Cycles <= 0;
if (~MMC5) begin
last_4017 <= DIN;
FrameSeqMode <= DIN[7]; // 1 = 5 frames cycle, 0 = 4 frames cycle
DisableFrameInterrupt <= DIN[6];
if (odd_or_even)
delayed_clear <= 3'd2;
else
delayed_clear <= 3'd1;
end
// Otherwise they get delayed one clock
Wrote4017 <= InternalClock;
end
endcase
end
@@ -765,18 +745,19 @@ end else if (ce) begin
end
ApuLookupTable lookup(clk,
(audio_channels[0] ? {4'b0, Sq1Sample} : 8'b0) +
(audio_channels[1] ? {4'b0, Sq2Sample} : 8'b0),
(audio_channels[2] ? {4'b0, TriSample} + {3'b0, TriSample, 1'b0} : 8'b0) +
(audio_channels[3] ? {3'b0, NoiSample, 1'b0} : 8'b0) +
(audio_channels[4] ? {1'b0, DmcSample} : 8'b0),
Sample);
APUMixer mixer (
.square1(Sq1Sample),
.square2(Sq2Sample),
.noise(NoiSample),
.triangle(TriSample),
.dmc(DmcSample),
.sample(Sample)
);
wire frame_irq = FrameInterrupt && !DisableFrameInterrupt;
// Generate bus output
assign DOUT = {DmcIrq, frame_irq, 1'b0,
assign DOUT = {DmcIrq, FrameInterrupt, 1'b0,
IsDmcActive,
NoiNonZero,
TriNonZero,
@@ -786,3 +767,90 @@ assign DOUT = {DmcIrq, frame_irq, 1'b0,
assign IRQ = frame_irq || DmcIrq;
endmodule
// http://wiki.nesdev.com/w/index.php/APU_Mixer
// I generated three LUT's for each mix channel entry and one lut for the squares, then a
// 282 entry lut for the mix channel. It's more accurate than the original LUT system listed on
// the NesDev page.
module APUMixer (
input [3:0] square1,
input [3:0] square2,
input [3:0] triangle,
input [3:0] noise,
input [6:0] dmc,
output [15:0] sample
);
wire [15:0] pulse_lut[32] = '{
16'd0, 16'd763, 16'd1509, 16'd2236, 16'd2947, 16'd3641, 16'd4319, 16'd4982,
16'd5630, 16'd6264, 16'd6883, 16'd7490, 16'd8083, 16'd8664, 16'd9232, 16'd9789,
16'd10334, 16'd10868, 16'd11392, 16'd11905, 16'd12408, 16'd12901, 16'd13384, 16'd13858,
16'd14324, 16'd14780, 16'd15228, 16'd15668, 16'd16099, 16'd16523, 16'd16939, 16'd17348
};
wire [5:0] tri_lut[16] = '{
6'd0, 6'd3, 6'd7, 6'd11, 6'd15, 6'd19, 6'd23, 6'd27,
6'd31, 6'd35, 6'd39, 6'd43, 6'd47, 6'd51, 6'd55, 6'd59
};
wire [5:0] noise_lut[16] = '{
6'd0, 6'd2, 6'd5, 6'd8, 6'd10, 6'd13, 6'd16, 6'd18,
6'd21, 6'd24, 6'd26, 6'd29, 6'd32, 6'd34, 6'd37, 6'd40
};
wire [7:0] dmc_lut[128] = '{
8'd0, 8'd1, 8'd2, 8'd4, 8'd5, 8'd7, 8'd8, 8'd10, 8'd11, 8'd13, 8'd14, 8'd15, 8'd17, 8'd18, 8'd20, 8'd21,
8'd23, 8'd24, 8'd26, 8'd27, 8'd28, 8'd30, 8'd31, 8'd33, 8'd34, 8'd36, 8'd37, 8'd39, 8'd40, 8'd41, 8'd43, 8'd44,
8'd46, 8'd47, 8'd49, 8'd50, 8'd52, 8'd53, 8'd55, 8'd56, 8'd57, 8'd59, 8'd60, 8'd62, 8'd63, 8'd65, 8'd66, 8'd68,
8'd69, 8'd70, 8'd72, 8'd73, 8'd75, 8'd76, 8'd78, 8'd79, 8'd81, 8'd82, 8'd83, 8'd85, 8'd86, 8'd88, 8'd89, 8'd91,
8'd92, 8'd94, 8'd95, 8'd96, 8'd98, 8'd99, 8'd101, 8'd102, 8'd104, 8'd105, 8'd107, 8'd108, 8'd110, 8'd111, 8'd112, 8'd114,
8'd115, 8'd117, 8'd118, 8'd120, 8'd121, 8'd123, 8'd124, 8'd125, 8'd127, 8'd128, 8'd130, 8'd131, 8'd133, 8'd134, 8'd136, 8'd137,
8'd138, 8'd140, 8'd141, 8'd143, 8'd144, 8'd146, 8'd147, 8'd149, 8'd150, 8'd151, 8'd153, 8'd154, 8'd156, 8'd157, 8'd159, 8'd160,
8'd162, 8'd163, 8'd165, 8'd166, 8'd167, 8'd169, 8'd170, 8'd172, 8'd173, 8'd175, 8'd176, 8'd178, 8'd179, 8'd180, 8'd182, 8'd183
};
wire [15:0] mix_lut[512] = '{
16'd0, 16'd318, 16'd635, 16'd950, 16'd1262, 16'd1573, 16'd1882, 16'd2190, 16'd2495, 16'd2799, 16'd3101, 16'd3401, 16'd3699, 16'd3995, 16'd4290, 16'd4583,
16'd4875, 16'd5164, 16'd5452, 16'd5739, 16'd6023, 16'd6306, 16'd6588, 16'd6868, 16'd7146, 16'd7423, 16'd7698, 16'd7971, 16'd8243, 16'd8514, 16'd8783, 16'd9050,
16'd9316, 16'd9581, 16'd9844, 16'd10105, 16'd10365, 16'd10624, 16'd10881, 16'd11137, 16'd11392, 16'd11645, 16'd11897, 16'd12147, 16'd12396, 16'd12644, 16'd12890, 16'd13135,
16'd13379, 16'd13622, 16'd13863, 16'd14103, 16'd14341, 16'd14579, 16'd14815, 16'd15050, 16'd15284, 16'd15516, 16'd15747, 16'd15978, 16'd16206, 16'd16434, 16'd16661, 16'd16886,
16'd17110, 16'd17333, 16'd17555, 16'd17776, 16'd17996, 16'd18215, 16'd18432, 16'd18649, 16'd18864, 16'd19078, 16'd19291, 16'd19504, 16'd19715, 16'd19925, 16'd20134, 16'd20342,
16'd20549, 16'd20755, 16'd20960, 16'd21163, 16'd21366, 16'd21568, 16'd21769, 16'd21969, 16'd22169, 16'd22367, 16'd22564, 16'd22760, 16'd22955, 16'd23150, 16'd23343, 16'd23536,
16'd23727, 16'd23918, 16'd24108, 16'd24297, 16'd24485, 16'd24672, 16'd24858, 16'd25044, 16'd25228, 16'd25412, 16'd25595, 16'd25777, 16'd25958, 16'd26138, 16'd26318, 16'd26497,
16'd26674, 16'd26852, 16'd27028, 16'd27203, 16'd27378, 16'd27552, 16'd27725, 16'd27898, 16'd28069, 16'd28240, 16'd28410, 16'd28579, 16'd28748, 16'd28916, 16'd29083, 16'd29249,
16'd29415, 16'd29580, 16'd29744, 16'd29907, 16'd30070, 16'd30232, 16'd30393, 16'd30554, 16'd30714, 16'd30873, 16'd31032, 16'd31190, 16'd31347, 16'd31503, 16'd31659, 16'd31815,
16'd31969, 16'd32123, 16'd32276, 16'd32429, 16'd32581, 16'd32732, 16'd32883, 16'd33033, 16'd33182, 16'd33331, 16'd33479, 16'd33627, 16'd33774, 16'd33920, 16'd34066, 16'd34211,
16'd34356, 16'd34500, 16'd34643, 16'd34786, 16'd34928, 16'd35070, 16'd35211, 16'd35352, 16'd35492, 16'd35631, 16'd35770, 16'd35908, 16'd36046, 16'd36183, 16'd36319, 16'd36456,
16'd36591, 16'd36726, 16'd36860, 16'd36994, 16'd37128, 16'd37261, 16'd37393, 16'd37525, 16'd37656, 16'd37787, 16'd37917, 16'd38047, 16'd38176, 16'd38305, 16'd38433, 16'd38561,
16'd38689, 16'd38815, 16'd38942, 16'd39068, 16'd39193, 16'd39318, 16'd39442, 16'd39566, 16'd39690, 16'd39813, 16'd39935, 16'd40057, 16'd40179, 16'd40300, 16'd40421, 16'd40541,
16'd40661, 16'd40780, 16'd40899, 16'd41017, 16'd41136, 16'd41253, 16'd41370, 16'd41487, 16'd41603, 16'd41719, 16'd41835, 16'd41950, 16'd42064, 16'd42178, 16'd42292, 16'd42406,
16'd42519, 16'd42631, 16'd42743, 16'd42855, 16'd42966, 16'd43077, 16'd43188, 16'd43298, 16'd43408, 16'd43517, 16'd43626, 16'd43735, 16'd43843, 16'd43951, 16'd44058, 16'd44165,
16'd44272, 16'd44378, 16'd44484, 16'd44589, 16'd44695, 16'd44799, 16'd44904, 16'd45008, 16'd45112, 16'd45215, 16'd45318, 16'd45421, 16'd45523, 16'd45625, 16'd45726, 16'd45828,
16'd45929, 16'd46029, 16'd46129, 16'd46229, 16'd46329, 16'd46428, 16'd46527, 16'd46625, 16'd46723, 16'd46821, 16'd46919, 16'd47016, 16'd47113, 16'd47209, 16'd47306, 16'd47402,
16'd47497, 16'd47592, 16'd47687, 16'd47782, 16'd47876, 16'd47970, 16'd48064, 16'd48157, 16'd48250, 16'd48343, 16'd48436, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0,
16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0, 16'd0
};
wire [4:0] squares = square1 + square2;
wire [8:0] mix = tri_lut[triangle] + noise_lut[noise] + dmc_lut[dmc];
wire [15:0] ch1 = pulse_lut[squares];
wire [15:0] ch2 = mix_lut[mix];
wire [63:0] chan_mix = ch1 + ch2;
assign sample = chan_mix > 16'hFFFF ? 16'hFFFF : chan_mix[15:0];
endmodule

1850
cores/nes/src/cart.sv Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,737 @@
//Famicom Disk System
module MapperFDS(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b, // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
// Special ports
input [7:0] audio_dout,
inout [1:0] diskside_auto_b,
input [1:0] diskside,
input fds_busy,
input fds_eject
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? audio[15:0] : 16'hZ;
assign diskside_auto_b = enable ? diskside_auto : 2'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire [7:0] prg_dout;
wire [15:0] flags_out = {14'd0, prg_bus_write, 1'b0};
wire prg_bus_write;
wire irq;
wire [1:0] diskside_auto;
wire [15:0] audio = audio_in;
wire nesprg_oe;
wire [7:0] neschrdout;
wire neschr_oe;
wire wram_oe;
wire wram_we;
wire prgram_we;
wire chrram_oe;
wire prgram_oe;
wire exp6;
reg [7:0] m2;
wire m2_n = 1;//~ce; //m2_n not used as clk. Invert m2 (ce).
always @(posedge clk) begin
m2[7:1] <= m2[6:0];
m2[0] <= ce;
end
MAPFDS fds(m2[7], m2_n, clk, ~enable, prg_write, nesprg_oe, 0,
1, prg_ain, chr_ain, prg_din, 8'b0, prg_dout, prg_bus_write,
neschrdout, neschr_oe, chr_allow, chrram_oe, wram_oe, wram_we, prgram_we,
prgram_oe, chr_aout[18:10], prg_aout[18:0], irq, vram_ce, exp6,
0, 7'b1111111, 6'b111111, flags[14], flags[16], flags[15],
ce, prg_allow, audio_dout, diskside_auto, diskside, fds_busy, fds_eject);
assign chr_aout[21:19] = 3'b100;
assign chr_aout[9:0] = chr_ain[9:0];
assign vram_a10 = chr_aout[10];
assign prg_aout[21:19] = prg_aout[18] ? 3'b111 : 3'b000; //Switch to Cart Ram for Disk access
//assign prg_aout[12:0] = prg_ain[12:0];
endmodule
// Loopy's FDS mapper for the Power Pak mapFDS.v
//PRG 00000-01FFF = bios
//PRG 08000-0FFFF = wram
//PRG 40000-7FFFF = disk image
module MAPFDS( //signal descriptions in powerpak.v
input m2,
input m2_n,
input clk20,
input reset,
input nesprg_we,
output nesprg_oe,
input neschr_rd,
input neschr_wr,
input [15:0] prgain,
input [13:0] chrain,
input [7:0] nesprgdin,
input [7:0] ramprgdin,
output reg [7:0] nesprgdout,
output prg_bus_write,
output [7:0] neschrdout,
output neschr_oe,
output chrram_we,
output chrram_oe,
output wram_oe,
output wram_we,
output prgram_we,
output prgram_oe,
output [18:10] ramchraout,
output [18:0] ramprgaout,
output irq,
output ciram_ce,
output exp6,
input cfg_boot,
input [18:12] cfg_chrmask,
input [18:13] cfg_prgmask,
input cfg_vertical,
input cfg_fourscreen,
input cfg_chrram,
input ce,// add
output prg_allow,
input [7:0] audio_dout,
//output [11:0] snd_level,
output reg [1:0] diskside_auto,
input [1:0] diskside,
input fds_busy,
input fds_eject
);
localparam WRITE_LO=16'hF4CD, WRITE_HI=16'hF4CE, READ_LO=16'hF4D0, READ_HI=16'hF4D1;
assign neschrdout = 0;
assign neschr_oe = 0;
assign exp6 = 0;
wire disk_eject;
reg timer_irq;
reg [1:0] Wstate;
reg [1:0] Rstate;
assign chrram_we=!chrain[13] & neschr_wr;
assign chrram_oe=!chrain[13] & neschr_rd;
assign wram_we=0; //use main ram for everything
assign wram_oe=0;
assign prgram_we=~cfg_boot & m2_n & nesprg_we & (Wstate==2 | (prgain[15]^(&prgain[14:13]))); //6000-DFFF or disk write
assign prgram_oe=~cfg_boot & m2_n & ~nesprg_we & (prgain[15] | prgain[15:13]==3); //6000-FFFF
wire fds_oe= m2_n & ~nesprg_we & (prgain[15:12]==4) & (|prgain[7:5] | prgain[9]); //$4xxx (except 00-1F) or 42xx
assign nesprg_oe=prgram_oe | fds_oe;
reg saved=0;
reg [15:0] diskpos;
reg [17:0] sideoffset;
wire [17:0] romoffset;
assign prg_bus_write = (fds_prg_bus_write | fds_audio_prg_bus_write);
reg fds_prg_bus_write;
wire fds_audio_prg_bus_write = (prgain >= 16'h4040 && prgain < 16'h4080) | (prgain >= 16'h4090 && prgain <= 16'h4097);
// Loopy's patched bios use a trick to catch requested diskside for games
// using standard bios load process.
// Unlicensed games sometimes doesn't use standard bios load process. This
// break automatic diskside trick.
// diskside_manual to be manage from OSD user input allow to add diskswap capabilities.
// (automatic fds_eject should be preferably stopped before changing diskside_manual)
// reg [1:0] diskside_auto;
// wire[1:0] diskside;
// assign diskside = diskside_auto + diskside_manual;
wire diskend=(diskpos==65499);
always@* case(diskside) //16+65500*diskside
0:sideoffset=18'h00010;
1:sideoffset=18'h0ffec;
2:sideoffset=18'h1ffc8;
3:sideoffset=18'h2ffa4;
endcase
assign romoffset=diskpos + sideoffset;
// Unlicensed fds games use NMI trick to skip protection. Rationale is to load
// a file starting @$2000 with $90 or $80 to enable NMI. This file should be at
// least 256 bytes length to allow NMI to occure before end of load. PC is then
// transfered to a special loader. This is ok with real hardware.
// But with loopy's patched bios the 256 bytes file is loaded too fast and no NMI
// occure early enough.
// Here proposed solution is to create an infinite loop at the end of normal
// load subroutine if @$2000 was written during load subroutine. Infinite loop
// give enough time for NMI to occure. (Generaly observed NMI enabling file is
// the last file of 'normal' loading process to be loaded). @2000.7 is checked to
// ensure the NMI is being turned on ($90 or $80 mentioned above).
// manage infinite loop trap at the end ($E233) of LoadFiles subroutine
// reg previous_is_E1F9;
reg infinite_loop_on_E233 = 0;
reg within_loader = 0;
// reg loader_write_in_2000 = 0;
always@(posedge clk20)
if(reset) begin
// on reset activate infinite loop trap
end
else begin
if ((m2) && (ramprgaout[18]==1'b0))begin
// detect enter / leave LoadFile subroutine
if(prgain==16'hE1FA) within_loader <= 1;
if(prgain==16'hE235) within_loader <= 0;
// deactivate infinite loop at LoadFile subroutine
if(prgain==16'hE1FA) infinite_loop_on_E233 <= 0;
// activate infinite loop if @$2000 is written with NMI (bit 7) high during FileLoad subroutine
if((prgain==16'h2000) && (within_loader == 1) && (nesprgdin[7])) infinite_loop_on_E233 <= 1;
end
end
//NES data out
wire match0=prgain==16'h4030; //IRQ status
wire match1=prgain==16'h4032; //drive status
wire match2=prgain==16'h4033; //power / exp
wire match3=((prgain==READ_LO)|(prgain==WRITE_LO))&!(Wstate==2 | Rstate==2);
wire match4=((prgain==READ_HI)|(prgain==WRITE_HI))&!(Wstate==2 | Rstate==2);
wire match5=prgain==16'h4208; //powerpak save flag
wire match6=prgain[15:8]==8'h40 && |prgain[7:6]; //4040..40FF
wire match7=(prgain==16'hE233) & infinite_loop_on_E233 & (ramprgaout[18]==1'b0);
wire match8=(prgain==16'hE234) & infinite_loop_on_E233 & (ramprgaout[18]==1'b0);
wire match9=(prgain==16'hE235) & infinite_loop_on_E233 & (ramprgaout[18]==1'b0);
wire match10=prgain==16'h4029; //MiSTer Busy
always @* begin
fds_prg_bus_write = 1'b1;
case(1)
match0: nesprgdout={7'd0, timer_irq};
match1: nesprgdout={5'd0, disk_eject, diskend, disk_eject};
match2: nesprgdout=8'b10000000;
match3: nesprgdout=romoffset[7:0];
match4: nesprgdout={3'b111,romoffset[12:8]};
match5: nesprgdout={7'd0,saved};
match6: nesprgdout=audio_dout;
match7: nesprgdout=8'h4C; // when infinite loop is active replace jsr $E778 with jmp $E233
match8: nesprgdout=8'h33;
match9: nesprgdout=8'hE2;
match10:nesprgdout={7'd0,~fds_busy};//MiSTer busy (zero = busy)
default: begin
nesprgdout=ramprgdin;
fds_prg_bus_write = 0;
end
endcase
end
assign prg_allow = (nesprg_we & (Wstate==2 | (prgain[15]^(&prgain[14:13]))))
| (~nesprg_we & ((prgain[15] & !match3 & !match4 & !match7 & !match8 & !match9) | prgain[15:13]==3));
reg write_en;
reg vertical;
reg timer_irq_en;
reg timer_irq_repeat;
reg diskreset;
reg disk_reg_en;
reg [15:0] timerlatch;
reg [15:0] timer;
always@(posedge clk20) begin
if(reset) begin
diskside_auto <= 2'd0;
end
if (ce) begin
if (timer_irq_en) begin
if (timer == 0) begin
timer_irq <= 1;
timer <= timerlatch;
if (~timer_irq_repeat) begin
timer_irq_en <= 0;
end
end else begin
timer <= timer - 1'd1;
end
end
if(nesprg_we)
case(prgain)
16'h4020: timerlatch[7:0]<=nesprgdin;
16'h4021: timerlatch[15:8]<=nesprgdin;
16'h4022: begin
timer_irq_repeat<=nesprgdin[0];
timer_irq_en<=nesprgdin[1] & disk_reg_en;
if (nesprgdin[1] & disk_reg_en) begin
timer <= timerlatch;
end else begin
timer_irq <= 0;
end
end
16'h4023: begin
disk_reg_en <=nesprgdin[0];
if (~nesprgdin[0]) begin
timer_irq_en <= 0;
timer_irq <= 0;
end
end
//16'h4024: //disk data write
16'h4025: begin // disk control
diskreset<=nesprgdin[1];
write_en<=!nesprgdin[2];
vertical<=!nesprgdin[3];
//disk_irq_en<=nesprgdin[7];
end
16'h4027: //powerpak extra: disk side
diskside_auto<=nesprgdin[1:0];
endcase
end
if (m2) begin
if (~nesprg_we & prgain==16'h4030)
timer_irq <= 0;
end
end
//watch for disk read/write
always@(posedge clk20) begin
if (m2) begin
if(write_en & ~nesprg_we & (prgain==WRITE_LO))
Wstate<=1;
else if(~nesprg_we & (prgain==WRITE_HI) & Wstate==1)
Wstate<=2;
else
Wstate<=0;
if(~nesprg_we & (prgain==READ_LO))
Rstate<=1;
else if(~nesprg_we & (prgain==READ_HI) & Rstate==1)
Rstate<=2;
else
Rstate<=0;
if(Wstate==2)
saved<=1;
end
end
//disk pointer
always@(posedge clk20) begin
if (m2) begin
if(diskreset)
diskpos<=0;
else if(Rstate==2 & !diskend)
diskpos<=diskpos+1'd1;
end
end
assign irq=timer_irq; // | disk_irq
//disk eject: toggle flag continuously except when select button is held
//reg [2:0] control_cnt; //use fds_eject instead
//reg [21:0] clkcount;
//assign disk_eject=clkcount[21] | fds_eject;
assign disk_eject=fds_eject;
//always@(posedge clk20) begin
// if (ce) begin
// clkcount<=clkcount+1'd1;
// if(prgain==16'h4016) begin
// if(nesprg_we) control_cnt<=0;
// else if(~nesprg_we & control_cnt!=7) control_cnt<=control_cnt+1'd1;
// //if(~nesprg_we & control_cnt==2) button<=|nesprgdin[1:0];
// end
// end
//end
//bankswitch control: 6000-DFFF = sram, E000-FFFF = bios or disk
reg [18:13] prgbank;
wire [18:13] diskbank={1'b1,romoffset[17:13]};
always@* begin
if(prgain[15:13]==7)
prgbank=diskbank & {6{Rstate==2|Wstate==2}};
else
prgbank={4'b0001,prgain[14:13]};
end
assign ramprgaout={prgbank,prgain[12:0]};
//mirroring
assign ramchraout[18:11]={6'd0,chrain[12:11]};
assign ramchraout[10]=!chrain[13]? chrain[10]: ((vertical & chrain[10]) | (!vertical & chrain[11]));
assign ciram_ce=chrain[13];
endmodule
module fds_mixed (
input clk,
input ce, // Negedge M2 (aka CPU ce)
input enable,
input wren,
input [15:0] addr_in,
input [7:0] data_in,
output [7:0] data_out,
input [15:0] audio_in, // Inverted audio from APU
output [15:0] audio_out
);
//expansion audio
fds_audio fds_audio
(
.clk(clk),
.m2(ce),
.reset(!enable),
.wr(wren),
.addr_in(addr_in),
.data_in(data_in),
.data_out(data_out),
.audio_out(audio_exp)
);
wire [11:0] audio_exp;
// XXX: This needs to be replaced with a proper ~2000hz LPF
lpf_aud fds_lpf
(
.CLK(clk),
.CE(ce),
.IDATA(16'hFFFF - {1'b0, audio_exp[11:0], audio_exp[11:9]}),
.ODATA(audio_exp_f)
);
wire [15:0] audio_exp_f;
wire [16:0] audio = audio_in + audio_exp_f;
assign audio_out = 16'hFFFF - audio[16:1];
endmodule
module lpf_aud
(
input CLK,
input CE,
input [15:0] IDATA,
output reg [15:0] ODATA
);
reg [511:0] acc;
reg [20:0] sum;
always @(*) begin
integer i;
sum = 0;
for (i = 0; i < 32; i = i+1) sum = sum + {{5{acc[(i*16)+15]}}, acc[i*16 +:16]};
end
always @(posedge CLK) begin
if(CE) begin
acc <= {acc[495:0], IDATA};
ODATA <= sum[20:5];
end
end
endmodule
// FDS Audio module by Kitrinx
// Based on the amazing research by Loopy from Jan, 2019
module fds_audio(
input clk,
input m2,
input reset,
input wr,
input [15:0] addr_in,
input [7:0] data_in,
output reg [7:0] data_out,
output [11:0] audio_out
);
// Volume Envelope
reg [5:0] vol_speed;
reg [5:0] vol_gain;
reg [5:0] vol_pwm_lat;
reg vol_dir;
reg vol_disable;
// Sweep Envelope
reg [5:0] sweep_speed;
reg [5:0] sweep_gain;
reg sweep_dir;
reg sweep_disable;
// Modulator
reg [11:0] mod_frequency;
reg [17:0] mod_accum;
reg mod_step;
reg [2:0] mod_table[0:31];
reg signed [6:0] mod_bias;
reg signed [6:0] mod_incr;
reg mod_disable;
// Wave Table
reg wave_wren;
reg [23:0] wave_accum;
reg [5:0] wave_table[0:63];
reg [5:0] wave_latch;
reg [11:0] wave_frequency;
reg wave_disable; // high: Envelopes 4x faster and stops mod table accum.
// Timing
reg env_disable;
reg [7:0] env_speed = 8'hE8;
reg [11:0] vol_env_ticks, sweep_env_ticks;
reg [5:0] vol_ticks, sweep_ticks;
reg [1:0] master_vol;
// Master timer
reg [3:0] cycles;
wire [12:0] mod_acc_next = mod_accum[11:0] + mod_frequency;
// Loopy's magical modulation math
wire signed [11:0] temp = mod_bias * $signed({1'b0, sweep_gain});
wire signed [11:0] temp2 = $signed((|temp[3:0] & ~temp[11]) ? temp + 12'sh20 : temp);
wire signed [11:0] temp3 = temp2 + 12'sh400;
wire [19:0] wave_pitch = $unsigned(temp3[11:4]) * wave_frequency;
// Volume math
wire [11:0] mul_out = wave_latch * (vol_pwm_lat[5] ? 6'd32 : vol_pwm_lat);
wire [15:0] level_out;
assign audio_out = level_out[11:0];
always_comb begin
case (master_vol)
2'b00: level_out = mul_out;
2'b01: level_out = {mul_out, 1'b0} / 16'd3;
2'b10: level_out = mul_out[11:1];
2'b11: level_out = {mul_out, 1'b0} / 16'd5;
default: level_out = mul_out;
endcase
if (addr_in >= 'h4040 && addr_in < 'h4080) begin
if (wave_wren)
data_out = wave_table[addr_in[5:0]];
else
data_out = wave_table[wave_accum[23:18]];
end else begin
case (addr_in)
'h4090: data_out = {2'b01, vol_gain};
'h4091: data_out = wave_accum[19:12];
'h4092: data_out = {2'b01, sweep_gain};
'h4093: data_out = {1'b0, mod_accum[11:5]};
'h4094: data_out = wave_pitch[11:4];
'h4095: data_out = {cycles, mod_incr[3:0]};
'h4096: data_out = {2'b01, wave_table[wave_accum[23:18]]};
'h4097: data_out = {1'b0, mod_bias};
default: data_out = 8'b0100_0000;
endcase
end
case (mod_table[mod_accum[17:13]])
3'h0: mod_incr = 0;
3'h1: mod_incr = 7'sd1;
3'h2: mod_incr = 7'sd2;
3'h3: mod_incr = 7'sd4;
3'h4: mod_incr = -7'sd4;
3'h5: mod_incr = -7'sd4;
3'h6: mod_incr = -7'sd2;
3'h7: mod_incr = -7'sd1;
default: mod_incr = 0;
endcase
end
always_ff @(posedge clk) begin
reg old_m2;
old_m2 <= m2;
if (reset) begin
sweep_disable <= 1'b1;
env_disable <= 1'b1;
wave_disable <= 1'b1;
mod_disable <= 1'b1;
wave_accum <= 0;
mod_accum <= 0;
{cycles, sweep_ticks, sweep_env_ticks, vol_ticks, vol_env_ticks, master_vol} <= 0;
end else if (~old_m2 & m2) begin
//**** Timings ****//
cycles <= wave_disable ? 4'h0 : cycles + 1'b1;
if (&cycles && ~wave_disable) begin
wave_accum <= wave_accum + wave_pitch;
if (~mod_disable)
mod_accum <= mod_accum + mod_frequency;
end
//**** Envelopes ****//
if (~env_disable && env_speed) begin
//**** Volume Envelope ****//
if (~vol_disable) begin
if (vol_env_ticks >= {env_speed, 3'b111}) begin
vol_env_ticks <= 0;
if (vol_ticks == vol_speed) begin
vol_ticks <= 0;
if (vol_dir && ~vol_gain[5])
vol_gain <= vol_gain + 1'b1;
else if (~vol_dir && vol_gain)
vol_gain <= vol_gain - 1'b1;
end else
vol_ticks <= vol_ticks + 1'b1;
end else
vol_env_ticks <= vol_env_ticks + (~wave_disable ? 1'b1 : 4'd4);
end
//**** Sweep Envelope ****//
if (~sweep_disable) begin
if (sweep_env_ticks >= {env_speed, 3'b111}) begin
sweep_env_ticks <= 0;
if (sweep_ticks == sweep_speed) begin
sweep_ticks <= 0;
if (sweep_dir && ~sweep_gain[5])
sweep_gain <= sweep_gain + 1'b1;
else if (~sweep_dir && sweep_gain)
sweep_gain <= sweep_gain - 1'b1;
end else
sweep_ticks <= sweep_ticks + 1'b1;
end else
sweep_env_ticks <= sweep_env_ticks + (~wave_disable ? 1'b1 : 4'd4);
end
end
//**** Modulation ****//
if ((&cycles && mod_acc_next[12]) || mod_step) begin
if (mod_table[mod_accum[17:13]] == 3'h4) begin
mod_bias <= 0;
end else begin
mod_bias <= mod_bias + mod_incr;
end
end
//**** Latches ****//
if (~|wave_accum[23:18])
vol_pwm_lat <= vol_gain;
if (~wave_wren)
wave_latch <= wave_table[wave_accum[23:18]];
//**** Registers ****//
if (wr) begin
if (addr_in >= 'h4040 && addr_in < 'h4080) begin
if (wave_wren)
wave_table[addr_in[5:0]] <= data_in[5:0];
end
case (addr_in)
16'h4080: begin
{vol_disable, vol_dir, vol_speed} <= data_in;
if (data_in[7]) vol_gain <= data_in[5:0];
vol_ticks <= 0;
vol_env_ticks <= 0;
end
16'h4082: wave_frequency[7:0] <= data_in;
16'h4083: begin
wave_frequency[11:8] <= data_in[3:0];
wave_disable <= data_in[7];
env_disable <= data_in[6];
if (data_in[7]) begin
wave_accum <= 0;
cycles <= 0;
end
if (data_in[6]) begin // Reset envelopes
vol_ticks <= 0;
sweep_ticks <= 0;
vol_env_ticks <= 0;
sweep_env_ticks <= 0;
end
end
16'h4084: begin
{sweep_disable, sweep_dir, sweep_speed} <= data_in;
if (data_in[7]) sweep_gain <= data_in[5:0];
sweep_ticks <= 0;
sweep_env_ticks <= 0;
end
16'h4085: mod_bias <= data_in[6:0];
16'h4086: mod_frequency[7:0] <= data_in;
16'h4087: begin
mod_frequency[11:8] <= data_in[3:0];
mod_disable <= data_in[7];
mod_step <= data_in[6];
if (data_in[7])
mod_accum[12:0] <= 0;
end
16'h4088: begin
if (mod_disable) begin
mod_table[mod_accum[17:13]] <= data_in[2:0];
mod_accum[17:13] <= mod_accum[17:13] + 1'b1;
end
end
16'h4089: begin
wave_wren <= data_in[7];
master_vol <= data_in[1:0];
end
16'h408A: begin
env_speed <= data_in;
vol_ticks <= 0;
sweep_ticks <= 0;
vol_env_ticks <= 0; // Undocumented, but I believe this is right.
sweep_env_ticks <= 0;
end
endcase
end
end // if m2
end
endmodule

View File

@@ -0,0 +1,294 @@
// J. Y. Company mappers
module multiplier (
input clk,
input ce,
input start,
input [7:0] a,
input [7:0] b,
output [15:0] p,
output done
);
reg [15:0] shift_a;
reg [15:0] product;
reg [8:0] bindex;
assign p = product;
assign done = bindex[8];
always @(posedge clk) begin
if (start && ce) begin
bindex <= 9'd1 << 1;
product <= {8'h00, b[0] ? a : 8'h00};
shift_a <= a << 1;
end else if (bindex < 9'h100) begin
product <= product + ((bindex[7:0] & b) ? shift_a : 16'd0);
bindex <= bindex << 1;
shift_a <= shift_a << 1;
end
end
endmodule
module JYCompany(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b, // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
// Special ports
input ppu_ce,
input [13:0] chr_ain_o
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout;
reg [21:0] chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
reg [7:0] chr_dout, prg_dout;
wire vram_ce;
wire [15:0] flags_out = {14'h0, prg_bus_write, 1'b0};
wire irq;
reg prg_bus_write;
wire mapper90 = (flags[7:0] == 90);
wire mapper211 = (flags[7:0] == 211); // Should just be 209 with correct behavior below
wire mapper35 = (flags[7:0] == 35);
wire ram_support = mapper35 || (flags[29:26] == 4'd7); //|| NES2.0 check;
reg [1:0] prg_mode, chr_mode;
reg prg_protect_1, prg_protect_2;
reg [3:0] mirroring;
wire xmirr = ~mapper90;
wire fxmirr = mapper211;
reg [2:0] prg_ram_bank;
reg [7:0] prg_bank [3:0];
reg [15:0] chr_bank [7:0];
reg [15:0] name_bank [3:0];
reg [7:0] outer_bank;
reg [7:0] ppu_conf;
reg [7:0] bank_mode;
wire [1:0] dip = 2'b00;
reg multiply_start;
reg [7:0] multiplier_1;
reg [7:0] multiplier_2;
wire [15:0] multiply_result;
reg [7:0] accum;
reg [7:0] accumtest;
reg old_a12;
reg irq_enable;
wire irq_source;
assign irq = irq_pending && irq_enable;
reg irq_pending;
reg irq_en;
reg irq_dis;
reg [7:0] irq_prescalar;
reg [7:0] irq_count;
reg [7:0] irq_xor;
reg [7:0] irq_mode;
// Handle IO register writes
always @(posedge clk) begin
if (!enable)
//Something needs to reset E000-FFFF to last bank
// Could be bank_mode[2] = 0 or prg_bank[3] = FF
// Outer bank might be an issue as well...
// Needed for Tiny Toons 6 and Warioland II.
bank_mode[2] = 1'b0;
if (ce && prg_write) begin // $5000-$FFFF
casez({prg_ain[15:11], prg_ain[2:0]})
8'b0101_1_?00: multiplier_1 <= prg_din; // $5800
8'b0101_1_?01: begin multiplier_2 <= prg_din; multiply_start <= 1; end // $5801
8'b0101_1_?10: accum <= accum + prg_din; // $5802
8'b0101_1_?11: begin accum <= 0; accumtest <= prg_din; end // $5803
8'b1000_0_???: prg_bank[prg_ain[1:0]] <= prg_din; // $8000-3
8'b1001_0_???: chr_bank[prg_ain[2:0]][7:0] <= prg_din; // $9000-7
8'b1010_0_???: chr_bank[prg_ain[2:0]][15:8] <= prg_din; // $A000-7
8'b1011_0_0??: name_bank[prg_ain[1:0]][7:0] <= prg_din; // $B000-3
8'b1011_0_1??: name_bank[prg_ain[1:0]][15:8] <= prg_din; // $B004-7
8'b1100_0_000: begin irq_en <= prg_din[0]; irq_dis <= !prg_din[0]; end // $C000
8'b1100_0_001: irq_mode <= prg_din; // $C001
8'b1100_0_010: irq_dis <= 1; // $C002
8'b1100_0_011: irq_en <= 1; // $C003
8'b1100_0_100: irq_prescalar <= prg_din ^ irq_xor; // $C004
8'b1100_0_101: irq_count <= prg_din ^ irq_xor; // $C005
8'b1100_0_110: irq_xor <= prg_din; // $C006
// 8'b1100_0_111: irq_conf <= prg_din; // $C007
8'b1101_0_?00: bank_mode <= prg_din; // $D000
8'b1101_0_?01: mirroring <= prg_din[3:0]; // $D001
8'b1101_0_?10: ppu_conf <= prg_din; // $D002
8'b1101_0_?11: outer_bank <= prg_din; // $D003
endcase
end
if (ppu_ce) old_a12 <= chr_ain_o[12];
if (irq_source && irq_enable && (irq_mode[7] != irq_mode[6])) begin
irq_prescalar <= irq_mode[6] ? (irq_prescalar + 8'd1) : (irq_prescalar - 8'd1);
if (( irq_mode[6] && ((irq_mode[2] && irq_prescalar[2:0] == 3'h7) || (!irq_mode[2] && irq_prescalar == 8'hFF)))
|| (!irq_mode[6] && ((irq_mode[2] && irq_prescalar[2:0] == 3'h0) || (!irq_mode[2] && irq_prescalar == 8'h00)))) begin
irq_count <= irq_mode[6] ? (irq_count + 8'd1) : (irq_count - 8'd1);
if (( irq_mode[6] && irq_count == 8'hFF)
|| (!irq_mode[6] && irq_count == 8'h00))
irq_pending <= 1;
end
end
if (irq_dis) begin
irq_pending <= 0;
irq_prescalar <= 0;
irq_enable <= 0;
irq_dis <= 0;
end else if (irq_en) begin
irq_en <= 0;
irq_enable <= 1;
end
end
// Determine IRQ handling
always @* begin
case(irq_mode[1:0])
2'b00: irq_source = ce;
2'b01: irq_source = ppu_ce && chr_ain_o[12] && !old_a12;
2'b10: irq_source = ppu_ce && chr_read;
2'b11: irq_source = ce && prg_write;
endcase
end
multiplier mp(
.clk(clk),
.ce(ce),
.start(multiply_start),
.a(multiplier_1),
.b(multiplier_2),
.p(multiply_result),
.done()
);
wire prg_6xxx = prg_ain[15:13] == 2'b011; // $6000-$7FFF
wire prg_ram = prg_6xxx && !bank_mode[7];
// Read from JYCompany
always @* begin
prg_bus_write = 1'b1;
if ((prg_ain == 16'h5000) || (prg_ain == 16'h5400)) begin // || (prg_ain == 16'h5C00)) begin
prg_dout = {dip, 6'h00};
end else if (prg_ain == 16'h5800) begin
prg_dout = multiply_result[7:0];
end else if (prg_ain == 16'h5801) begin
prg_dout = multiply_result[15:8];
end else if (prg_ain == 16'h5802) begin
prg_dout = accum;
end else if (prg_ain == 16'h5803) begin
prg_dout = accumtest;
end else begin
prg_dout = 8'hFF; // By default open bus.
prg_bus_write = 0;
end
end
// Compute PRG address to read from.
wire [1:0] prg_reg;
always @* begin
casez({prg_6xxx, bank_mode[1:0]})
3'b000: prg_reg = {2'b11}; // $8000-$FFFF
3'b001: prg_reg = {prg_ain[14], 1'b1}; // $8000-$BFFF + prg_ain*0x4000
3'b01?: prg_reg = {prg_ain[14:13]}; // $8000-$9FFF + prg_ain*0x2000
3'b1??: prg_reg = {2'b11}; // $6000-$7FFF
endcase
end
wire [7:0] bank_val = (!bank_mode[2] && prg_reg == 2'b11) ? 8'hFF : prg_bank[prg_reg];
wire [6:0] bank_order = bank_mode[1:0] == 2'b11 ? {bank_val[0], bank_val[1], bank_val[2], bank_val[3], bank_val[4], bank_val[5], bank_val[6]} : bank_val[6:0];
wire [5:0] prg_sel;
always @* begin
casez({prg_6xxx, bank_mode[1:0]})
3'b000: prg_sel = {bank_order[3:0], prg_ain[14:13]}; //
3'b001: prg_sel = {bank_order[4:0], prg_ain[13]}; //
3'b?1?: prg_sel = {bank_order[5:0]}; //
3'b100: prg_sel = {bank_order[3:0], 2'b11}; //
3'b101: prg_sel = {bank_order[4:0], 1'b1}; //
endcase
end
assign prg_aout = prg_ram && ram_support ? {9'b11_1100_000, prg_ain[12:0]} : {1'b0, outer_bank[2:1], prg_sel, prg_ain[12:0]};
assign prg_allow = (prg_ain >= 16'h6000) && (prg_ram ? ram_support : !prg_write);
reg [1:0] chr_latch;
// latch is set to 0 when the PPU reads from $0FD8-$0FDF/$1FD8-$1FDF and to 1 when the PPU reads from $0FE8-$0FEF/$1FE8-$1FEF.
always @(posedge clk)
if (~enable)
chr_latch <= 2'b00;
else if (ppu_ce && chr_read) begin
chr_latch[chr_ain_o[12]] <= outer_bank[7] && (((chr_ain_o & 14'h2ff8) == 14'h0fd8) ? 1'd0 : ((chr_ain_o & 14'h2ff8) == 14'h0fe8) ? 1'd1 : chr_latch[chr_ain_o[12]]);
end
wire [2:0] chr_reg;
always @* begin
casez(bank_mode[4:3])
2'b00: chr_reg = {3'b000}; // $0000-$1FFF
2'b01: chr_reg = {chr_ain[12], chr_latch[chr_ain[12]], 1'b0};// $0000-$0FFF + chr_ain*0x1000
2'b10: chr_reg = {chr_ain[12:11], 1'b0}; // $0000-$07FF + chr_ain*0x0800
2'b11: chr_reg = {chr_ain[12:10]}; // $0000-$03FF + chr_ain*0x0400
endcase
end
wire [12:0] chr_val = chr_bank[chr_reg][12:0];
wire [12:0] chr_sel;
always @* begin
casez({romtables, bank_mode[4:3]})
3'b000: chr_sel = {chr_val[9:0], chr_ain[12:10]}; //
3'b001: chr_sel = {chr_val[10:0], chr_ain[11:10]}; //
3'b010: chr_sel = {chr_val[11:0], chr_ain[10]}; //
3'b011: chr_sel = {chr_val[12:0]}; //
3'b1??: chr_sel = {name_bank[chr_ain[11:10]][12:0]}; //
endcase
end
assign chr_aout = {2'b10, outer_bank[3], !outer_bank[5] ? outer_bank[0] : chr_sel[8], chr_sel[7:0], chr_ain[9:0]}; //not enough bits for outer_bank[4]
assign chr_allow = flags[15] && ppu_conf[6];
// The a10 VRAM address line. (Used for mirroring)
wire xtend = (mirroring[3] || bank_mode[5]) && xmirr || fxmirr;
wire romtables = chr_ain[13] && xtend && bank_mode[5] && (bank_mode[6] || (ppu_conf[7] ^ name_bank[chr_ain[11:10]][7]));
always @* begin
casez({xtend, mirroring[1:0]})
3'b1??: vram_a10 = name_bank[chr_ain[11:10]][0];
3'b000: vram_a10 = chr_ain[10];
3'b001: vram_a10 = chr_ain[11];
3'b010: vram_a10 = 1'b0;
3'b011: vram_a10 = 1'b1;
endcase
end
assign vram_ce = chr_ain[13] && (!chr_read || !xtend || !romtables);
endmodule

View File

@@ -0,0 +1,274 @@
// MMC1 mapper chip. Maps prg or chr addresses into a linear address.
// If vram_ce is set, {vram_a10, chr_aout[9:0]} are used to access the NES internal VRAM instead.
module MMC1(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? 8'hFF : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? 1'b0 : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire mapper171 = (flags[7:0] == 171); //Mapper 171 has hardwired mirroring
reg [15:0] flags_out = 0;
reg [4:0] shift;
// CPPMM
// |||||
// |||++- Mirroring (0: one-screen, lower bank; 1: one-screen, upper bank;
// ||| 2: vertical; 3: horizontal)
// |++--- PRG ROM bank mode (0, 1: switch 32 KB at $8000, ignoring low bit of bank number;
// | 2: fix first bank at $8000 and switch 16 KB bank at $C000;
// | 3: fix last bank at $C000 and switch 16 KB bank at $8000)
// +----- CHR ROM bank mode (0: switch 8 KB at a time; 1: switch two separate 4 KB banks)
reg [4:0] control;
// CCCCC
// |||||
// +++++- Select 4 KB or 8 KB CHR bank at PPU $0000 (low bit ignored in 8 KB mode)
reg [4:0] chr_bank_0;
// CCCCC
// |||||
// +++++- Select 4 KB CHR bank at PPU $1000 (ignored in 8 KB mode)
reg [4:0] chr_bank_1;
// RPPPP
// |||||
// |++++- Select 16 KB PRG ROM bank (low bit ignored in 32 KB mode)
// +----- PRG RAM chip enable (0: enabled; 1: disabled; ignored on MMC1A)
reg [4:0] prg_bank;
reg delay_ctrl; // used to prevent fast-write to the control register
wire [2:0] prg_size = flags[10:8];
// Update shift register
always @(posedge clk)
if (~enable) begin
shift <= 5'b10000;
control <= 5'b0_11_00;
chr_bank_0 <= 0;
chr_bank_1 <= 0;
prg_bank <= 5'b00000;
delay_ctrl <= 0;
end else if (ce) begin
if (!prg_write)
delay_ctrl <= 1'b0;
if (prg_write && prg_ain[15] && !delay_ctrl) begin
delay_ctrl <= 1'b1;
if (prg_din[7]) begin
shift <= 5'b10000;
control <= control | 5'b0_11_00;
end else begin
if (shift[0]) begin
casez(prg_ain[14:13])
0: control <= {prg_din[0], shift[4:1]};
1: chr_bank_0 <= {prg_din[0], shift[4:1]};
2: chr_bank_1 <= {prg_din[0], shift[4:1]};
3: prg_bank <= {prg_din[0], shift[4:1]};
endcase
shift <= 5'b10000;
end else begin
shift <= {prg_din[0], shift[4:1]};
end
end
end
end
// The PRG bank to load. Each increment here is 16kb. So valid values are 0..15.
// prg_ain[14] selects bank0 ($8000) or bank1 ($C000)
reg [3:0] prgsel;
always @* begin
casez({control[3:2], prg_ain[14]})
3'b0?_?: prgsel = {prg_bank[3:1], prg_ain[14]}; // Swap 32Kb
3'b10_0: prgsel = 4'b0000; // Swap 16Kb at $C000 with access at $8000, so select page 0 (hardcoded)
3'b10_1: prgsel = prg_bank[3:0]; // Swap 16Kb at $C000 with $C000 access, so select page based on prg_bank (register 3)
3'b11_0: prgsel = prg_bank[3:0]; // Swap 16Kb at $8000 with $8000 access, so select page based on prg_bank (register 3)
3'b11_1: prgsel = 4'b1111; // Swap 16Kb at $8000 with $C000 access, so select last page (hardcoded)
endcase
end
// The CHR bank to load. Each increment here is 4 kb. So valid values are 0..31.
reg [4:0] chrsel;
always @* begin
casez({control[4], chr_ain[12]})
2'b0_?: chrsel = {chr_bank_0[4:1], chr_ain[12]};
2'b1_0: chrsel = chr_bank_0;
2'b1_1: chrsel = chr_bank_1;
endcase
end
assign chr_aout = {5'b100_00, chrsel, chr_ain[11:0]};
wire [21:0] prg_aout_tmp = prg_size == 5 ? {3'b000, chrsel[4], prgsel, prg_ain[13:0]} // for large PRG ROM, CHR A16 selects the 256KB PRG bank
: {4'b00_00, prgsel, prg_ain[13:0]};
// The a10 VRAM address line. (Used for mirroring)
reg vram_a10_t;
always @* begin
casez(mapper171 ? 2'b10 : control[1:0]) //if mapper 171 then set to vertical mirroring, else do normal MMC1 mirroring selection.
2'b00: vram_a10_t = 0; // One screen, lower bank
2'b01: vram_a10_t = 1; // One screen, upper bank
2'b10: vram_a10_t = chr_ain[10]; // One screen, vertical
2'b11: vram_a10_t = chr_ain[11]; // One screen, horizontal
endcase
end
assign vram_a10 = vram_a10_t;
assign vram_ce = chr_ain[13];
wire prg_is_ram = prg_ain >= 'h6000 && prg_ain < 'h8000;
assign prg_allow = prg_ain[15] && !prg_write || prg_is_ram;
wire [21:0] prg_ram = {9'b11_1100_000, prg_ain[12:0]};
assign prg_aout = prg_is_ram ? prg_ram : prg_aout_tmp;
assign chr_allow = flags[15];
endmodule
// #105 - NES-EVENT. Retrofits an MMC1 with lots of extra logic.
module NesEvent(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign irq_b = enable ? irq : 1'hZ;
wire [21:0] chr_aout;
reg [21:0] prg_aout;
wire [21:0] mmc1_chr_addr;
wire [3:0] mmc1_chr = mmc1_chr_addr[16:13]; // Upper 4 CHR output control bits from MMC chip
wire [21:0] mmc1_aout; // PRG output address from MMC chip
wire irq;
MMC1 mmc1_nesevent(
.clk (clk),
.ce (ce),
.enable (enable),
.flags (flags),
.prg_ain (prg_ain),
.prg_aout_b (mmc1_aout),
.prg_read (prg_read),
.prg_write (prg_write),
.prg_din (prg_din),
.prg_dout_b (prg_dout_b),
.prg_allow_b(prg_allow_b),
.chr_ain (chr_ain),
.chr_aout_b (mmc1_chr_addr),
.chr_allow_b(chr_allow_b),
.vram_a10_b (vram_a10_b),
.vram_ce_b (vram_ce_b),
.irq_b (),
.flags_out_b(flags_out_b),
.audio_in (audio_in),
.audio_b (audio_b)
);
// $A000-BFFF: [...I OAA.]
// I = IRQ control / initialization toggle
// O = PRG Mode/Chip select
// A = PRG Reg 'A'
// Mapper gets "initialized" by setting I bit to 0 then to 1.
// On powerup and reset, the first 32k of PRG (from the first PRG chip) is selected at $8000 *no matter what*.
// PRG cannot be swapped until the mapper has been "initialized" by setting the 'I' bit to 0, then to '1'. This
// toggling will "unlock" PRG swapping on the mapper.
reg unlocked, old_val;
reg [29:0] counter;
reg [3:0] oldbits;
always @(posedge clk)
if (~enable) begin
old_val <= 0;
unlocked <= 0;
counter <= 0;
end else if (ce) begin
// Handle unlock.
if (mmc1_chr[3] && !old_val) unlocked <= 1;
old_val <= mmc1_chr[3];
// The 'I' bit in $A000 controls the IRQ counter. When cleared, the IRQ counter counts up every cycle. When
// set, the IRQ counter is reset to 0 and stays there (does not count), and the pending IRQ is acknowledged.
counter <= mmc1_chr[3] ? 1'd0 : counter + 1'd1;
if (mmc1_chr != oldbits) begin
oldbits <= mmc1_chr;
end
end
// In the official tournament, 'C' was closed, and the others were open, so the counter had to reach $2800000.
assign irq = (counter[29:25] == 5'b10100);
always begin
if (!prg_ain[15]) begin
// WRAM is always routed as usual.
prg_aout = mmc1_aout;
end else if (!unlocked) begin
// Not initialized yet, mapper switch disabled.
prg_aout = {7'b00_0000_0, prg_ain[14:0]};
end else if (mmc1_chr[2] == 0) begin
// O=0: Use first PRG chip (first 128k), use 'A' PRG Reg, 32k swap
prg_aout = {5'b00_000, mmc1_chr[1:0], prg_ain[14:0]};
end else begin
// O=1: Use second PRG chip (second 128k), use 'B' PRG Reg, MMC1 style swap
prg_aout = mmc1_aout;
end
end
// 8kB CHR RAM.
assign chr_aout = {9'b10_0000_000, chr_ain[12:0]};
endmodule

View File

@@ -0,0 +1,325 @@
// MMC 2 and 4 mappers. These can probably be consolidated.
// MMC2 mapper chip. PRG ROM: 128kB. Bank Size: 8kB. CHR ROM: 128kB
module MMC2(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b, // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
input [13:0] chr_ain_o
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? 8'hFF : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? 1'b0 : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
reg [15:0] flags_out = 0;
// PRG ROM bank select ($A000-$AFFF)
// 7 bit 0
// ---- ----
// xxxx PPPP
// ||||
// ++++- Select 8 KB PRG ROM bank for CPU $8000-$9FFF
reg [3:0] prg_bank;
// CHR ROM $FD/0000 bank select ($B000-$BFFF)
// 7 bit 0
// ---- ----
// xxxC CCCC
// | ||||
// +-++++- Select 4 KB CHR ROM bank for PPU $0000-$0FFF
// used when latch 0 = $FD
reg [4:0] chr_bank_0a;
// CHR ROM $FE/0000 bank select ($C000-$CFFF)
// 7 bit 0
// ---- ----
// xxxC CCCC
// | ||||
// +-++++- Select 4 KB CHR ROM bank for PPU $0000-$0FFF
// used when latch 0 = $FE
reg [4:0] chr_bank_0b;
// CHR ROM $FD/1000 bank select ($D000-$DFFF)
// 7 bit 0
// ---- ----
// xxxC CCCC
// | ||||
// +-++++- Select 4 KB CHR ROM bank for PPU $1000-$1FFF
// used when latch 1 = $FD
reg [4:0] chr_bank_1a;
// CHR ROM $FE/1000 bank select ($E000-$EFFF)
// 7 bit 0
// ---- ----
// xxxC CCCC
// | ||||
// +-++++- Select 4 KB CHR ROM bank for PPU $1000-$1FFF
// used when latch 1 = $FE
reg [4:0] chr_bank_1b;
// Mirroring ($F000-$FFFF)
// 7 bit 0
// ---- ----
// xxxx xxxM
// |
// +- Select nametable mirroring (0: vertical; 1: horizontal)
reg mirroring;
reg latch_0, latch_1;
// Update registers
always @(posedge clk)
if (~enable)
{prg_bank, chr_bank_0a, chr_bank_0b, chr_bank_1a, chr_bank_1b, mirroring} <= 0;
else if (ce) begin
if (prg_write && prg_ain[15]) begin
case(prg_ain[14:12])
2: prg_bank <= prg_din[3:0]; // $A000
3: chr_bank_0a <= prg_din[4:0]; // $B000
4: chr_bank_0b <= prg_din[4:0]; // $C000
5: chr_bank_1a <= prg_din[4:0]; // $D000
6: chr_bank_1b <= prg_din[4:0]; // $E000
7: mirroring <= prg_din[0]; // $F000
endcase
end
end
// PPU reads $0FD8: latch 0 is set to $FD for subsequent reads
// PPU reads $0FE8: latch 0 is set to $FE for subsequent reads
// PPU reads $1FD8 through $1FDF: latch 1 is set to $FD for subsequent reads
// PPU reads $1FE8 through $1FEF: latch 1 is set to $FE for subsequent reads
always @(posedge clk)
if (~enable)
{latch_0, latch_1} <= 0;
else if (ce && chr_read) begin
latch_0 <= (chr_ain_o & 14'h3fff) == 14'h0fd8 ? 1'd0 : (chr_ain_o & 14'h3fff) == 14'h0fe8 ? 1'd1 : latch_0;
latch_1 <= (chr_ain_o & 14'h3ff8) == 14'h1fd8 ? 1'd0 : (chr_ain_o & 14'h3ff8) == 14'h1fe8 ? 1'd1 : latch_1;
end
// The PRG bank to load. Each increment here is 8kb. So valid values are 0..15.
reg [3:0] prgsel;
always @* begin
casez(prg_ain[14:13])
2'b00: prgsel = prg_bank;
default: prgsel = {2'b11, prg_ain[14:13]};
endcase
end
assign prg_aout = {5'b00_000, prgsel, prg_ain[12:0]};
// The CHR bank to load. Each increment here is 4kb. So valid values are 0..31.
reg [4:0] chrsel;
always @* begin
casez({chr_ain[12], latch_0, latch_1})
3'b00?: chrsel = chr_bank_0a;
3'b01?: chrsel = chr_bank_0b;
3'b1?0: chrsel = chr_bank_1a;
3'b1?1: chrsel = chr_bank_1b;
endcase
end
assign chr_aout = {5'b100_00, chrsel, chr_ain[11:0]};
// The a10 VRAM address line. (Used for mirroring)
assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10];
assign vram_ce = chr_ain[13];
assign prg_allow = prg_ain[15] && !prg_write;
assign chr_allow = flags[15];
endmodule
// MMC4 mapper chip. PRG ROM: 256kB. Bank Size: 16kB. CHR ROM: 128kB
module MMC4(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b, // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
input [13:0] chr_ain_o
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? 1'b0 : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire [7:0] prg_dout = 0;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
reg [15:0] flags_out = 0;
// PRG ROM bank select ($A000-$AFFF)
// 7 bit 0
// ---- ----
// xxxx PPPP
// ||||
// ++++- Select 16 KB PRG ROM bank for CPU $8000-$BFFF
reg [3:0] prg_bank;
// CHR ROM $FD/0000 bank select ($B000-$BFFF)
// 7 bit 0
// ---- ----
// xxxC CCCC
// | ||||
// +-++++- Select 4 KB CHR ROM bank for PPU $0000-$0FFF
// used when latch 0 = $FD
reg [4:0] chr_bank_0a;
// CHR ROM $FE/0000 bank select ($C000-$CFFF)
// 7 bit 0
// ---- ----
// xxxC CCCC
// | ||||
// +-++++- Select 4 KB CHR ROM bank for PPU $0000-$0FFF
// used when latch 0 = $FE
reg [4:0] chr_bank_0b;
// CHR ROM $FD/1000 bank select ($D000-$DFFF)
// 7 bit 0
// ---- ----
// xxxC CCCC
// | ||||
// +-++++- Select 4 KB CHR ROM bank for PPU $1000-$1FFF
// used when latch 1 = $FD
reg [4:0] chr_bank_1a;
// CHR ROM $FE/1000 bank select ($E000-$EFFF)
// 7 bit 0
// ---- ----
// xxxC CCCC
// | ||||
// +-++++- Select 4 KB CHR ROM bank for PPU $1000-$1FFF
// used when latch 1 = $FE
reg [4:0] chr_bank_1b;
// Mirroring ($F000-$FFFF)
// 7 bit 0
// ---- ----
// xxxx xxxM
// |
// +- Select nametable mirroring (0: vertical; 1: horizontal)
reg mirroring;
reg latch_0, latch_1;
// Update registers
always @(posedge clk)
if (ce) begin
if (~enable)
prg_bank <= 4'b1110;
else if (prg_write && prg_ain[15]) begin
case(prg_ain[14:12])
2: prg_bank <= prg_din[3:0]; // $A000
3: chr_bank_0a <= prg_din[4:0]; // $B000
4: chr_bank_0b <= prg_din[4:0]; // $C000
5: chr_bank_1a <= prg_din[4:0]; // $D000
6: chr_bank_1b <= prg_din[4:0]; // $E000
7: mirroring <= prg_din[0]; // $F000
endcase
end
end
// PPU reads $0FD8 through $0FDF: latch 0 is set to $FD for subsequent reads
// PPU reads $0FE8 through $0FEF: latch 0 is set to $FE for subsequent reads
// PPU reads $1FD8 through $1FDF: latch 1 is set to $FD for subsequent reads
// PPU reads $1FE8 through $1FEF: latch 1 is set to $FE for subsequent reads
always @(posedge clk)
if (ce & chr_read) begin
latch_0 <= (chr_ain_o & 14'h3ff8) == 14'h0fd8 ? 1'd0 : (chr_ain_o & 14'h3ff8) == 14'h0fe8 ? 1'd1 : latch_0;
latch_1 <= (chr_ain_o & 14'h3ff8) == 14'h1fd8 ? 1'd0 : (chr_ain_o & 14'h3ff8) == 14'h1fe8 ? 1'd1 : latch_1;
end
// The PRG bank to load. Each increment here is 16kb. So valid values are 0..15.
reg [3:0] prgsel;
always @* begin
casez(prg_ain[14])
1'b0: prgsel = prg_bank;
default: prgsel = 4'b1111;
endcase
end
wire [21:0] prg_aout_tmp = {4'b00_00, prgsel, prg_ain[13:0]};
// The CHR bank to load. Each increment here is 4kb. So valid values are 0..31.
reg [4:0] chrsel;
always @* begin
casez({chr_ain[12], latch_0, latch_1})
3'b00?: chrsel = chr_bank_0a;
3'b01?: chrsel = chr_bank_0b;
3'b1?0: chrsel = chr_bank_1a;
3'b1?1: chrsel = chr_bank_1b;
endcase
end
assign chr_aout = {5'b100_00, chrsel, chr_ain[11:0]};
// The a10 VRAM address line. (Used for mirroring)
assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10];
assign vram_ce = chr_ain[13];
assign chr_allow = flags[15];
wire prg_is_ram = prg_ain >= 'h6000 && prg_ain < 'h8000;
assign prg_allow = prg_ain[15] && !prg_write || prg_is_ram;
wire [21:0] prg_ram = {9'b11_1100_000, prg_ain[12:0]};
assign prg_aout = prg_is_ram ? prg_ram : prg_aout_tmp;
endmodule

View File

@@ -0,0 +1,697 @@
// MMC3 style mappers. Some of these can probably be consolidated.
// iNES mapper 64 and 158 - Tengen's version of MMC3
module Rambo1(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b, // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
input [13:0] chr_ain_o
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire [7:0] prg_dout = 0;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
reg irq;
reg [15:0] flags_out = 0;
reg [3:0] bank_select; // Register to write to next
reg prg_rom_bank_mode; // Mode for PRG banking
reg chr_K; // Mode for CHR banking
reg chr_a12_invert; // Mode for CHR banking
reg mirroring; // 0 = vertical, 1 = horizontal
reg irq_enable, irq_reload; // IRQ enabled, and IRQ reload requested
reg [7:0] irq_latch, counter; // IRQ latch value and current counter
reg [1:0] irq_delay;
wire irq_imm;
reg [7:0] chr_bank_0, chr_bank_1; // Selected CHR banks
reg [7:0] chr_bank_2, chr_bank_3, chr_bank_4, chr_bank_5;
reg [7:0] chr_bank_8, chr_bank_9;
reg [5:0] prg_bank_0, prg_bank_1, prg_bank_2; // Selected PRG banks
reg irq_cycle_mode, next_irq_cycle_mode;
reg [1:0] cycle_counter;
// Mapper has vram_a10 wired to CHR A17
//wire mapper64 = (flags[7:0] == 64);//default
wire mapper158 = (flags[7:0] == 158);
// This code detects rising edges on a12.
reg old_a12_edge;
reg [1:0] a12_ctr;
wire a12_edge = (chr_ain_o[12] && a12_ctr == 0) || old_a12_edge;
always @(posedge clk) begin
old_a12_edge <= a12_edge && !ce;
a12_ctr <= chr_ain_o[12] ? 2'b11 : (a12_ctr != 0 && ce) ? a12_ctr - 2'b01 : a12_ctr;
end
always @(posedge clk)
if (~enable) begin
bank_select <= 0;
prg_rom_bank_mode <= 0;
chr_K <= 0;
chr_a12_invert <= 0;
mirroring <= 0;
{irq_enable, irq_reload} <= 0;
{irq_latch, counter} <= 0;
{chr_bank_0, chr_bank_1} <= 0;
{chr_bank_2, chr_bank_3, chr_bank_4, chr_bank_5} <= 0;
{chr_bank_8, chr_bank_9} <= 0;
{prg_bank_0, prg_bank_1, prg_bank_2} <= 6'b111111;
irq_cycle_mode <= 0;
next_irq_cycle_mode <= 0;
cycle_counter <= 0;
irq <= 0;
end else if (ce) begin
// Process these before writes so irq_reload and cycle_counter register writes take precedence.
cycle_counter <= cycle_counter + 1'd1;
irq_delay <= {1'b0, irq_delay[1]};
if ((cycle_counter == 3) || (!irq_cycle_mode))
irq_cycle_mode <= next_irq_cycle_mode;
irq_imm = 1'b0;
if (irq_cycle_mode ? (cycle_counter == 3) : a12_edge) begin
if (irq_reload || counter == 8'h00) begin
counter <= irq_latch + ((irq_reload && (|irq_latch[7:1])) ? 1'd1 : 1'd0);
irq_imm = irq_latch == 0;
end else begin
counter <= counter - 1'd1;
end
if (((counter == 1) || (irq_imm)) && irq_enable)
irq_delay <= irq_cycle_mode ? 2'b01 : 2'b10;
irq_reload <= 0;
end
if (irq_delay[0]) begin
irq <= 1;
irq_delay <= 2'b00;
end
if (prg_write && prg_ain[15]) begin
case({prg_ain[14:13], prg_ain[0]})
// Bank select ($8000-$9FFE, even)
3'b00_0: {chr_a12_invert, prg_rom_bank_mode, chr_K, bank_select} <= {prg_din[7:5], prg_din[3:0]};
// Bank data ($8001-$9FFF, odd)
3'b00_1:
case (bank_select)
0: chr_bank_0 <= prg_din; // Select 2 (K=0) or 1 (K=1) KB CHR bank at PPU $0000 (or $1000);
1: chr_bank_1 <= prg_din; // Select 2 (K=0) or 1 (K=1) KB CHR bank at PPU $0800 (or $1800);
2: chr_bank_2 <= prg_din; // Select 1 KB CHR bank at PPU $1000-$13FF (or $0000-$03FF);
3: chr_bank_3 <= prg_din; // Select 1 KB CHR bank at PPU $1400-$17FF (or $0400-$07FF);
4: chr_bank_4 <= prg_din; // Select 1 KB CHR bank at PPU $1800-$1BFF (or $0800-$0BFF);
5: chr_bank_5 <= prg_din; // Select 1 KB CHR bank at PPU $1C00-$1FFF (or $0C00-$0FFF);
6: prg_bank_0 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $8000-$9FFF (or $C000-$DFFF);
7: prg_bank_1 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $A000-$BFFF
8: chr_bank_8 <= prg_din; // If K=1, Select 1 KB CHR bank at PPU $0400 (or $1400);
9: chr_bank_9 <= prg_din; // If K=1, Select 1 KB CHR bank at PPU $0C00 (or $1C00)
15: prg_bank_2 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $C000-$DFFF (or $8000-$9FFF);
endcase
3'b01_0: mirroring <= prg_din[0]; // Mirroring ($A000-$BFFE, even)
3'b01_1: begin end
3'b10_0: irq_latch <= prg_din; // IRQ latch ($C000-$DFFE, even)
3'b10_1: begin
{irq_reload, next_irq_cycle_mode} <= {1'b1, prg_din[0]}; // IRQ reload ($C001-$DFFF, odd)
cycle_counter <= 0;
end
3'b11_0: {irq_enable, irq} <= 2'b00; // IRQ disable ($E000-$FFFE, even)
3'b11_1: {irq_enable, irq} <= 2'b10; // IRQ enable ($E001-$FFFF, odd)
endcase
end
end
// The PRG bank to load. Each increment here is 8kb. So valid values are 0..63.
reg [5:0] prgsel;
always @* begin
casez({prg_ain[14:13], prg_rom_bank_mode})
3'b00_0: prgsel = prg_bank_0; // $8000 is R:6
3'b01_0: prgsel = prg_bank_1; // $A000 is R:7
3'b10_0: prgsel = prg_bank_2; // $C000 is R:F
3'b11_0: prgsel = 6'b111111; // $E000 fixed to last bank
3'b00_1: prgsel = prg_bank_2; // $8000 is R:F
3'b01_1: prgsel = prg_bank_0; // $A000 is R:6
3'b10_1: prgsel = prg_bank_1; // $C000 is R:7
3'b11_1: prgsel = 6'b111111; // $E000 fixed to last bank
endcase
end
// The CHR bank to load. Each increment here is 1kb. So valid values are 0..255.
reg [7:0] chrsel;
always @* begin
casez({chr_ain[12] ^ chr_a12_invert, chr_ain[11], chr_ain[10], chr_K})
4'b00?_0: chrsel = {chr_bank_0[7:1], chr_ain[10]};
4'b01?_0: chrsel = {chr_bank_1[7:1], chr_ain[10]};
4'b000_1: chrsel = chr_bank_0;
4'b001_1: chrsel = chr_bank_8;
4'b010_1: chrsel = chr_bank_1;
4'b011_1: chrsel = chr_bank_9;
4'b100_?: chrsel = chr_bank_2;
4'b101_?: chrsel = chr_bank_3;
4'b110_?: chrsel = chr_bank_4;
4'b111_?: chrsel = chr_bank_5;
endcase
end
assign prg_aout = {3'b00_0, prgsel, prg_ain[12:0]};
assign {chr_allow, chr_aout} = {flags[15], 4'b10_00, chrsel, chr_ain[9:0]};
assign prg_allow = prg_ain[15] && !prg_write;
assign vram_a10 = mapper158 ? chrsel[7] : // Mapper 158 controls mirroring by switching the top bits of the CHR address
mirroring ? chr_ain[11] : chr_ain[10];
assign vram_ce = chr_ain[13];
endmodule
// This mapper also handles mapper 33,47,48,74,76,80,82,88,95,118,119,154,191,192,194,195,206 and 207.
module MMC3 (
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b, // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
input [13:0] chr_ain_o
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire [7:0] prg_dout = 0;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire irq;
reg [15:0] flags_out = 0;
reg [2:0] bank_select; // Register to write to next
reg prg_rom_bank_mode; // Mode for PRG banking
reg chr_a12_invert; // Mode for CHR banking
reg mirroring; // 0 = vertical, 1 = horizontal
reg irq_enable, irq_reload; // IRQ enabled, and IRQ reload requested
reg [7:0] irq_latch, counter; // IRQ latch value and current counter
reg [3:0] ram_enable, ram_protect; // RAM protection bits
reg ram6_enabled, ram6_enable, ram6_protect; //extra bits for mmc6
reg [7:0] chr_bank_0, chr_bank_1; // Selected CHR banks
reg [7:0] chr_bank_2, chr_bank_3, chr_bank_4, chr_bank_5;
reg [5:0] prg_bank_0, prg_bank_1, prg_bank_2; // Selected PRG banks
reg last_a12;
wire prg_is_ram;
reg [4:0] irq_reg;
assign irq = mapper48 ? irq_reg[4] : irq_reg[0];
// The alternative behavior has slightly different IRQ counter semantics.
wire mmc3_alt_behavior = acclaim;
wire TQROM = (flags[7:0] == 119); // TQROM maps 8kB CHR RAM
wire TxSROM = (flags[7:0] == 118); // Connects CHR A17 to CIRAM A10
wire mapper47 = (flags[7:0] == 47); // Mapper 47 is a multicart that has 128k for each game. It has no RAM.
wire mapper37 = (flags[7:0] == 37); // European Triple Cart (Super Mario, Tetris, Nintendo World Cup)
wire DxROM = (flags[7:0] == 206);
wire mapper112 = (flags[7:0] == 112); // Ntdec
wire mapper48 = (flags[7:0] == 48); // Taito's TC0690
wire mapper33 = (flags[7:0] == 33); // Taito's TC0190 (TC0690-like. No IRQ. Different Mirroring bit)
wire mapper95 = (flags[7:0] == 95); // NAMCOT-3425
wire mapper88 = (flags[7:0] == 88); // NAMCOT-3433
wire mapper154 = (flags[7:0] == 154); // NAMCOT-3453
wire mapper76 = (flags[7:0] == 76); // NAMCOT-3446
wire mapper80 = (flags[7:0] == 80); // Taito's X1-005
wire mapper82 = (flags[7:0] == 82); // Taito's X1-017
wire mapper207 = (flags[7:0] == 207); // Taito's X1-017
wire mapper74 = (flags[7:0] == 74); // Has 2KB CHR RAM
wire mapper191 = (flags[7:0] == 191); // Has 2KB CHR RAM
wire mapper192 = (flags[7:0] == 192); // Has 4KB CHR RAM
wire mapper194 = (flags[7:0] == 194); // Has 2KB CHR RAM
wire mapper195 = (flags[7:0] == 195); // Has 4KB CHR RAM
wire MMC6 = ((flags[7:0] == 4) && (flags[24:21] == 1)); // mapper 4, submapper 1 = MMC6
wire acclaim = ((flags[7:0] == 4) && (flags[24:21] == 3)); // Acclaim mapper
wire four_screen_mirroring = flags[16];// | DxROM; // not all DxROM are 4-screen
reg mapper47_multicart;
reg [2:0] mapper37_multicart;
wire [7:0] new_counter = (counter == 0 || irq_reload) ? irq_latch : counter - 1'd1;
reg [3:0] a12_ctr;
wire irq_support = !DxROM && !mapper33 && !mapper95 && !mapper88 && !mapper154 && !mapper76
&& !mapper80 && !mapper82 && !mapper207 && !mapper112; //82,207 not needed
wire prg_invert_support = (irq_support && !mapper48);
wire chr_invert_support = (irq_support && !mapper48) || mapper82;
wire regs_7e = mapper80 || mapper82 || mapper207;
wire internal_128 = mapper80 || mapper207;
always @(posedge clk)
if (~enable) begin
irq_reg <= 5'b00000;
bank_select <= 0;
prg_rom_bank_mode <= 0;
chr_a12_invert <= 0;
mirroring <= flags[14];
{irq_enable, irq_reload} <= 0;
{irq_latch, counter} <= 0;
ram_enable <= {4{mapper112}};
ram_protect <= 0;
{chr_bank_0, chr_bank_1} <= 0;
{chr_bank_2, chr_bank_3, chr_bank_4, chr_bank_5} <= 0;
{prg_bank_0, prg_bank_1} <= 0;
prg_bank_2 <= 6'b111110;
a12_ctr <= 0;
last_a12 <= 0;
mapper37_multicart <= 3'b000;
end else if (ce) begin
irq_reg[4:1] <= irq_reg[3:0]; // 4 cycle delay
if (!regs_7e && prg_write && prg_ain[15]) begin
if (!mapper33 && !mapper48 && !mapper112) begin
casez({prg_ain[14:13], prg_ain[1:0]})
4'b00_?0: {chr_a12_invert, prg_rom_bank_mode, ram6_enabled, bank_select} <= {prg_din[7:5], prg_din[2:0]}; // Bank select ($8000-$9FFE, even)
4'b00_?1: begin // Bank data ($8001-$9FFF, odd)
case (bank_select)
0: chr_bank_0 <= {1'b0,prg_din[7:1]}; // Select 2 KB CHR bank at PPU $0000-$07FF (or $1000-$17FF);
1: chr_bank_1 <= {1'b0,prg_din[7:1]}; // Select 2 KB CHR bank at PPU $0800-$0FFF (or $1800-$1FFF);
2: chr_bank_2 <= prg_din; // Select 1 KB CHR bank at PPU $1000-$13FF (or $0000-$03FF);
3: chr_bank_3 <= prg_din; // Select 1 KB CHR bank at PPU $1400-$17FF (or $0400-$07FF);
4: chr_bank_4 <= prg_din; // Select 1 KB CHR bank at PPU $1800-$1BFF (or $0800-$0BFF);
5: chr_bank_5 <= prg_din; // Select 1 KB CHR bank at PPU $1C00-$1FFF (or $0C00-$0FFF);
6: prg_bank_0 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $8000-$9FFF (or $C000-$DFFF);
7: prg_bank_1 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $A000-$BFFF
endcase
end
4'b01_?0: mirroring <= !prg_din[0]; // Mirroring ($A000-$BFFE, even)
4'b01_?1: {ram_enable, ram_protect, ram6_enable, ram6_protect} <= {{4{prg_din[7]}},{4{prg_din[6]}}, prg_din[5:4]}; // PRG RAM protect ($A001-$BFFF, odd)
4'b10_?0: irq_latch <= prg_din; // IRQ latch ($C000-$DFFE, even)
4'b10_?1: irq_reload <= 1; // IRQ reload ($C001-$DFFF, odd)
4'b11_?0: begin irq_enable <= 0; irq_reg[0] <= 0; end// IRQ disable ($E000-$FFFE, even)
4'b11_?1: irq_enable <= 1; // IRQ enable ($E001-$FFFF, odd)
endcase
end else if (!mapper112) begin
casez({prg_ain[14:13], prg_ain[1:0], mapper48})
5'b00_00_0: {mirroring, prg_bank_0} <= prg_din[6:0] ^ 7'h40; // Select 8 KB PRG ROM bank at $8000-$9FFF
5'b00_00_1: prg_bank_0 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $8000-$9FFF
5'b00_01_?: prg_bank_1 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $A000-$BFFF
5'b00_10_?: chr_bank_0 <= prg_din; // Select 2 KB CHR bank at PPU $0000-$07FF
5'b00_11_?: chr_bank_1 <= prg_din; // Select 2 KB CHR bank at PPU $0800-$0FFF
5'b01_00_?: chr_bank_2 <= prg_din; // Select 1 KB CHR bank at PPU $1000-$13FF
5'b01_01_?: chr_bank_3 <= prg_din; // Select 1 KB CHR bank at PPU $1800-$1BFF
5'b01_10_?: chr_bank_4 <= prg_din; // Select 1 KB CHR bank at PPU $1800-$1BFF
5'b01_11_?: chr_bank_5 <= prg_din; // Select 1 KB CHR bank at PPU $1C00-$1FFF
5'b10_00_1: irq_latch <= prg_din ^ 8'hFF; // IRQ latch ($C000-$DFFC)
5'b10_01_1: irq_reload <= 1; // IRQ reload ($C001-$DFFD)
5'b10_10_1: irq_enable <= 1; // IRQ enable ($C002-$DFFE)
5'b10_11_1: {irq_enable, irq_reg[0]} <= 2'b00; // IRQ disable ($C003-$DFFF)
5'b11_00_1: mirroring <= !prg_din[6]; // Mirroring
endcase
end else begin
casez({prg_ain[14:13], prg_ain[0]})
3'b00_0: {bank_select} <= {prg_din[2:0]}; // Bank select ($8000-$9FFE)
3'b01_0: begin // Bank data ($A000-$BFFF)
case (bank_select)
0: prg_bank_0 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $8000-$9FFF (or $C000-$DFFF);
1: prg_bank_1 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $A000-$BFFF
2: chr_bank_0 <= {1'b0,prg_din[7:1]}; // Select 2 KB CHR bank at PPU $0000-$07FF (or $1000-$17FF);
3: chr_bank_1 <= {1'b0,prg_din[7:1]}; // Select 2 KB CHR bank at PPU $0800-$0FFF (or $1800-$1FFF);
4: chr_bank_2 <= prg_din; // Select 1 KB CHR bank at PPU $1000-$13FF (or $0000-$03FF);
5: chr_bank_3 <= prg_din; // Select 1 KB CHR bank at PPU $1400-$17FF (or $0400-$07FF);
6: chr_bank_4 <= prg_din; // Select 1 KB CHR bank at PPU $1800-$1BFF (or $0800-$0BFF);
7: chr_bank_5 <= prg_din; // Select 1 KB CHR bank at PPU $1C00-$1FFF (or $0C00-$0FFF);
endcase
end
3'b11_0: mirroring <= !prg_din[0]; // Mirroring ($E000-$FFFE)
endcase
end
if (mapper154)
mirroring <= !prg_din[6];
if (DxROM || mapper76)
mirroring <= flags[14]; // Hard-wired mirroring
end
else if (regs_7e && prg_write && prg_ain[15:4]==12'h7EF) begin
casez({prg_ain[3:0], mapper82})
5'b0000_?: chr_bank_0 <= {1'b0,prg_din[7:1]}; // Select 2 KB CHR bank at PPU $0000-$07FF
5'b0001_?: chr_bank_1 <= {1'b0,prg_din[7:1]}; // Select 2 KB CHR bank at PPU $0800-$0FFF
5'b0010_?: chr_bank_2 <= prg_din; // Select 1 KB CHR bank at PPU $1000-$13FF
5'b0011_?: chr_bank_3 <= prg_din; // Select 1 KB CHR bank at PPU $1800-$1BFF
5'b0100_?: chr_bank_4 <= prg_din; // Select 1 KB CHR bank at PPU $1800-$1BFF
5'b0101_?: chr_bank_5 <= prg_din; // Select 1 KB CHR bank at PPU $1C00-$1FFF
5'b011?_0: {mirroring} <= prg_din[0]; // Select Mirroing
5'b100?_0: {ram_enable[3], ram_protect[3]} <= {(prg_din==8'hA3),!(prg_din==8'hA3)}; // Enable RAM at $7F00-$7FFF
5'b0110_1: {chr_a12_invert,mirroring} <= prg_din[1:0]; // Select Mirroing
5'b0111_1: {ram_enable[0], ram_protect[0]} <= {(prg_din==8'hCA),!(prg_din==8'hCA)}; // Enable RAM at $6000-$67FF
5'b1000_1: {ram_enable[1], ram_protect[1]} <= {(prg_din==8'h69),!(prg_din==8'h69)}; // Enable RAM at $6F00-$6FFF
5'b1001_1: {ram_enable[2], ram_protect[2]} <= {(prg_din==8'h84),!(prg_din==8'h84)}; // Enable RAM at $7000-$73FF //Using 6K; Require 5K instead?
5'b101?_0: prg_bank_0 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $8000-$9FFF
5'b110?_0: prg_bank_1 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $A000-$BFFF
5'b111?_0: prg_bank_2 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $C000-$DFFF
5'b1010_1: prg_bank_0 <= prg_din[7:2]; // Select 8 KB PRG ROM bank at $8000-$9FFF
5'b1011_1: prg_bank_1 <= prg_din[7:2]; // Select 8 KB PRG ROM bank at $A000-$BFFF
5'b1100_1: prg_bank_2 <= prg_din[7:2]; // Select 8 KB PRG ROM bank at $C000-$DFFF
endcase
end
// For Mapper 47
// $6000-7FFF: [.... ...B] Block select
if (prg_write && prg_is_ram)
mapper47_multicart <= prg_din[0];
// For Mapper 37
// $6000-7FFF: [.... .QBB] Block select
if (prg_write && prg_is_ram)
mapper37_multicart <= prg_din[2:0];
// Trigger IRQ counter on rising edge of chr_ain[12]
// All MMC3A's and non-Sharp MMC3B's will generate only a single IRQ when $C000 is $00.
// This is because this version of the MMC3 generates IRQs when the scanline counter is decremented to 0.
// In addition, writing to $C001 with $C000 still at $00 will result in another single IRQ being generated.
// In the community, this is known as the "alternate" or "old" behavior.
// All MMC3C's and Sharp MMC3B's will generate an IRQ on each scanline while $C000 is $00.
// This is because this version of the MMC3 generates IRQs when the scanline counter is equal to 0.
// In the community, this is known as the "normal" or "new" behavior.
last_a12 <= chr_ain_o[12];
if ((acclaim && (!last_a12 && chr_ain_o[12]) && (a12_ctr == 6)) ||
(~acclaim && chr_ain_o[12] && (a12_ctr == 0))) begin
counter <= new_counter;
// MMC Scanline
if ( (!mmc3_alt_behavior || counter != 0 || irq_reload) && new_counter == 0 && irq_enable && irq_support) begin
irq_reg[0] <= 1;
end
irq_reload <= 0;
end
if (acclaim) begin
if (!last_a12 && chr_ain_o[12]) // acclaim mapper counts down 8 pulses, or 16 edges total
a12_ctr <= (a12_ctr != 0) ? a12_ctr - 4'b0001 : 4'b0111;
if (prg_ain == 16'hC001 && prg_write) a12_ctr <= 4'b0111;
end else begin // nintendo mapper 'cools down' for 16 low cycles
a12_ctr <= chr_ain_o[12] ? 4'b1111 : (a12_ctr != 0) ? a12_ctr - 4'b0001 : a12_ctr;
end
end
// The PRG bank to load. Each increment here is 8kb. So valid values are 0..63.
reg [5:0] prgsel;
always @* begin
casez({prg_ain[14:13], prg_rom_bank_mode && prg_invert_support})
3'b00_0: prgsel = prg_bank_0; // $8000 mode 0
3'b00_1: prgsel = prg_bank_2; // $8000 fixed to second last bank
3'b01_?: prgsel = prg_bank_1; // $A000 mode 0,1
3'b10_0: prgsel = prg_bank_2; // $C000 fixed to second last bank
3'b10_1: prgsel = prg_bank_0; // $C000 mode 1
3'b11_?: prgsel = 6'b111111; // $E000 fixed to last bank
endcase
// mapper47 is limited to 128k PRG, the top bits are controlled by mapper47_multicart instead.
if (mapper47) prgsel[5:4] = {1'b0, mapper47_multicart};
if (mapper37) begin
prgsel[5:4] = {1'b0, mapper37_multicart[2]};
if (mapper37_multicart[1:0] == 3'd3)
prgsel[3] = 1'b1;
else if (mapper37_multicart[2] == 1'b0)
prgsel[3] = 1'b0;
end
end
// The CHR bank to load. Each increment here is 1kb. So valid values are 0..255.
reg [8:0] chrsel;
always @* begin
if (!mapper76) begin
casez({chr_ain[12] ^ (chr_a12_invert && chr_invert_support), chr_ain[11], chr_ain[10]})
3'b00?: chrsel = {chr_bank_0, chr_ain[10]};
3'b01?: chrsel = {chr_bank_1, chr_ain[10]};
3'b100: chrsel = {1'b0, chr_bank_2};
3'b101: chrsel = {1'b0, chr_bank_3};
3'b110: chrsel = {1'b0, chr_bank_4};
3'b111: chrsel = {1'b0, chr_bank_5};
endcase
// mapper47 is limited to 128k CHR, the top bit is controlled by mapper47_multicart instead.
if (mapper47) chrsel[7] = mapper47_multicart;
if (mapper37) chrsel[7] = mapper37_multicart[2];
if ((mapper88) || (mapper154)) chrsel[6] = chr_ain[12];
end else begin
case(chr_ain[12:11])
2'b00: chrsel = {chr_bank_2, chr_ain[10]};
2'b01: chrsel = {chr_bank_3, chr_ain[10]};
2'b10: chrsel = {chr_bank_4, chr_ain[10]};
2'b11: chrsel = {chr_bank_5, chr_ain[10]};
endcase
end
end
wire [21:0] prg_aout_tmp = {3'b00_0, prgsel, prg_ain[12:0]};
wire ram_enable_a = !MMC6 ? (ram_enable[prg_ain[12:11]])
: (ram6_enabled && ram6_enable && prg_ain[12] == 1'b1 && prg_ain[9] == 1'b0)
|| (ram6_enabled && ram_enable[3] && prg_ain[12] == 1'b1 && prg_ain[9] == 1'b1);
wire ram_protect_a = !MMC6 ? (ram_protect[prg_ain[12:11]])
: !(ram6_enabled && ram6_enable && ram6_protect && prg_ain[12] == 1'b1 && prg_ain[9] == 1'b0)
&& !(ram6_enabled && ram_enable[3] && ram_protect[3] && prg_ain[12] == 1'b1 && prg_ain[9] == 1'b1);
assign {chr_allow, chr_aout} =
(TQROM && chrsel[6]) ? {1'b1, 9'b11_1111_111, chrsel[2:0], chr_ain[9:0]} : // TQROM 8kb CHR-RAM
(mapper74 && chrsel[7:1]==7'b0000100) ? {1'b1, 11'b11_1111_1111_1,chrsel[0], chr_ain[9:0]} : // 2kb CHR-RAM
(mapper191 && chrsel[7]) ? {1'b1, 11'b11_1111_1111_1,chrsel[0], chr_ain[9:0]} : // 2kb CHR-RAM
(mapper192 && chrsel[7:2]==6'b000010) ? {1'b1, 10'b11_1111_1111, chrsel[1:0], chr_ain[9:0]} : // 4kb CHR-RAM
(mapper194 && chrsel[7:1]==7'b0000000) ? {1'b1, 11'b11_1111_1111_1,chrsel[0], chr_ain[9:0]} : // 2kb CHR-RAM
(mapper195 && chrsel[7:2]==6'b000000) ? {1'b1, 10'b11_1111_1111, chrsel[1:0], chr_ain[9:0]} : // 4kb CHR-RAM
(four_screen_mirroring && chr_ain[13]) ? {1'b1, 9'b11_1111_111, chr_ain[13], chr_ain[11:0]} : // DxROM 8kb CHR-RAM
{flags[15], 3'b10_0, chrsel, chr_ain[9:0]}; // Standard MMC3
assign prg_is_ram = (prg_ain[15:13] == 3'b011) && ((prg_ain[12:8] == 5'b1_1111) | ~internal_128) //(>= 'h6000 && < 'h8000) && (==7Fxx or external_ram)
&& ram_enable_a && !(ram_protect_a && prg_write);
assign prg_allow = prg_ain[15] && !prg_write || prg_is_ram && !mapper47;
wire [21:0] prg_ram = {9'b11_1100_000, internal_128 ? 6'b000000 : MMC6 ? {3'b000, prg_ain[9:7]} : prg_ain[12:7], prg_ain[6:0]};
assign prg_aout = prg_is_ram && !mapper47 && !DxROM && !mapper95 && !mapper88 ? prg_ram : prg_aout_tmp;
assign vram_a10 = TxSROM ? chrsel[7] : // TxSROM do not support mirroring
mapper95 ? chrsel[5] : // mapper95 does not support mirroring
mapper154 ? mirroring : // mapper154 does not support mirroring
mapper207 ? chrsel[7] : // mapper207 does not support mirroring
(mirroring ? chr_ain[10] : chr_ain[11]);
assign vram_ce = chr_ain[13] && !four_screen_mirroring;
endmodule
// mapper 165
module Mapper165(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b, // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
input [13:0] chr_ain_o
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire [7:0] prg_dout = 0;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
reg irq;
reg [15:0] flags_out = 0;
reg [2:0] bank_select; // Register to write to next
reg prg_rom_bank_mode; // Mode for PRG banking
reg chr_a12_invert; // Mode for CHR banking
reg mirroring; // 0 = vertical, 1 = horizontal
reg irq_enable, irq_reload; // IRQ enabled, and IRQ reload requested
reg [7:0] irq_latch, counter; // IRQ latch value and current counter
reg ram_enable, ram_protect; // RAM protection bits
reg [5:0] prg_bank_0, prg_bank_1; // Selected PRG banks
wire prg_is_ram;
reg [6:0] chr_bank_0, chr_bank_1; // Selected CHR banks
reg [7:0] chr_bank_2, chr_bank_4;
reg latch_0, latch_1;
wire [7:0] new_counter = (counter == 0 || irq_reload) ? irq_latch : counter - 1'd1;
reg [3:0] a12_ctr;
always @(posedge clk)
if (~enable) begin
irq <= 0;
bank_select <= 0;
prg_rom_bank_mode <= 0;
chr_a12_invert <= 0;
mirroring <= flags[14];
{irq_enable, irq_reload} <= 0;
{irq_latch, counter} <= 0;
{ram_enable, ram_protect} <= 0;
{chr_bank_0, chr_bank_1, chr_bank_2, chr_bank_4} <= 0;
{prg_bank_0, prg_bank_1} <= 0;
a12_ctr <= 0;
end else if (ce) begin
if (prg_write && prg_ain[15]) begin
case({prg_ain[14], prg_ain[13], prg_ain[0]})
3'b00_0: {chr_a12_invert, prg_rom_bank_mode, bank_select} <= {prg_din[7], prg_din[6], prg_din[2:0]}; // Bank select ($8000-$9FFE, even)
3'b00_1: begin // Bank data ($8001-$9FFF, odd)
case (bank_select)
0: chr_bank_0 <= prg_din[7:1]; // Select 2 KB CHR bank at PPU $0000-$07FF (or $1000-$17FF);
1: chr_bank_1 <= prg_din[7:1]; // Select 2 KB CHR bank at PPU $0800-$0FFF (or $1800-$1FFF);
2: chr_bank_2 <= prg_din; // Select 1 KB CHR bank at PPU $1000-$13FF (or $0000-$03FF);
3: ; // Select 1 KB CHR bank at PPU $1400-$17FF (or $0400-$07FF);
4: chr_bank_4 <= prg_din; // Select 1 KB CHR bank at PPU $1800-$1BFF (or $0800-$0BFF);
5: ; // Select 1 KB CHR bank at PPU $1C00-$1FFF (or $0C00-$0FFF);
6: prg_bank_0 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $8000-$9FFF (or $C000-$DFFF);
7: prg_bank_1 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $A000-$BFFF
endcase
end
3'b01_0: mirroring <= prg_din[0]; // Mirroring ($A000-$BFFE, even)
3'b01_1: {ram_enable, ram_protect} <= prg_din[7:6]; // PRG RAM protect ($A001-$BFFF, odd)
3'b10_0: irq_latch <= prg_din; // IRQ latch ($C000-$DFFE, even)
3'b10_1: irq_reload <= 1; // IRQ reload ($C001-$DFFF, odd)
3'b11_0: begin irq_enable <= 0; irq <= 0; end // IRQ disable ($E000-$FFFE, even)
3'b11_1: irq_enable <= 1; // IRQ enable ($E001-$FFFF, odd)
endcase
end
// Trigger IRQ counter on rising edge of chr_ain[12]
// All MMC3A's and non-Sharp MMC3B's will generate only a single IRQ when $C000 is $00.
// This is because this version of the MMC3 generates IRQs when the scanline counter is decremented to 0.
// In addition, writing to $C001 with $C000 still at $00 will result in another single IRQ being generated.
// In the community, this is known as the "alternate" or "old" behavior.
// All MMC3C's and Sharp MMC3B's will generate an IRQ on each scanline while $C000 is $00.
// This is because this version of the MMC3 generates IRQs when the scanline counter is equal to 0.
// In the community, this is known as the "normal" or "new" behavior.
if (chr_ain_o[12] && a12_ctr == 0) begin
counter <= new_counter;
if ((counter != 0 || irq_reload) && new_counter == 0 && irq_enable) begin
irq <= 1;
end
irq_reload <= 0;
end
a12_ctr <= chr_ain_o[12] ? 4'b1111 : (a12_ctr != 0) ? a12_ctr - 4'b0001 : a12_ctr;
end
// The PRG bank to load. Each increment here is 8kb. So valid values are 0..63.
reg [5:0] prgsel;
always @* begin
casez({prg_ain[14:13], prg_rom_bank_mode})
3'b00_0: prgsel = prg_bank_0; // $8000 mode 0
3'b00_1: prgsel = 6'b111110; // $8000 fixed to second last bank
3'b01_?: prgsel = prg_bank_1; // $A000 mode 0,1
3'b10_0: prgsel = 6'b111110; // $C000 fixed to second last bank
3'b10_1: prgsel = prg_bank_0; // $C000 mode 1
3'b11_?: prgsel = 6'b111111; // $E000 fixed to last bank
endcase
end
wire [21:0] prg_aout_tmp = {3'b00_0, prgsel, prg_ain[12:0]};
// PPU reads $0FD0: latch 0 is set to $FD for subsequent reads
// PPU reads $0FE0: latch 0 is set to $FE for subsequent reads
// PPU reads $1FD0 through $1FDF: latch 1 is set to $FD for subsequent reads
// PPU reads $1FE0 through $1FEF: latch 1 is set to $FE for subsequent reads
always @(posedge clk)
if (ce && chr_read) begin
latch_0 <= (chr_ain & 14'h3fff) == 14'h0fd0 ? 1'd0 : (chr_ain & 14'h3fff) == 14'h0fe0 ? 1'd1 : latch_0;
latch_1 <= (chr_ain & 14'h3ff0) == 14'h1fd0 ? 1'd0 : (chr_ain & 14'h3ff0) == 14'h1fe0 ? 1'd1 : latch_1;
end
// The CHR bank to load. Each increment here is 1kb. So valid values are 0..255.
reg [7:0] chrsel;
always @* begin
casez({chr_ain[12] ^ chr_a12_invert, latch_0, latch_1})
3'b0_0?: chrsel = {chr_bank_0, chr_ain[10]}; // 2Kb page
3'b0_1?: chrsel = {chr_bank_1, chr_ain[10]}; // 2Kb page
3'b1_?0: chrsel = chr_bank_2;
3'b1_?1: chrsel = chr_bank_4;
endcase
end
assign {chr_allow, chr_aout} = {flags[15] && (chrsel < 4), 4'b10_00, chrsel, chr_ain[9:0]};
assign prg_is_ram = prg_ain >= 'h6000 && prg_ain < 'h8000 && ram_enable && !(ram_protect && prg_write);
assign prg_allow = prg_ain[15] && !prg_write || prg_is_ram;
wire [21:0] prg_ram = {9'b11_1100_000, prg_ain[12:0]};
assign prg_aout = prg_is_ram ? prg_ram : prg_aout_tmp;
assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10];
assign vram_ce = chr_ain[13];
endmodule

View File

@@ -0,0 +1,469 @@
// MMC5 Mapper aka "WTF were they thinking?!"
module MMC5(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b, // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
// Special ports
input [7:0] audio_dout,
input [7:0] chr_din, // CHR Data in
input chr_write, // CHR Write
inout [7:0] chr_dout_b, // chr data (non standard)
input ppu_ce,
input [19:0] ppuflags
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_dout_b = enable ? chr_dout : 8'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? audio : 16'hZ;
wire [21:0] prg_aout;
reg [21:0] chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
reg [7:0] chr_dout, prg_dout;
wire vram_ce;
wire [15:0] flags_out = {14'h0, prg_bus_write, has_chr_dout};
wire irq;
wire prg_bus_write, has_chr_dout;
wire [15:0] audio = audio_in;
reg [1:0] prg_mode, chr_mode;
reg prg_protect_1, prg_protect_2;
reg [1:0] extended_ram_mode;
reg [7:0] mirroring;
reg [7:0] fill_tile;
reg [1:0] fill_attr;
reg [2:0] prg_ram_bank;
reg [7:0] prg_bank_0, prg_bank_1, prg_bank_2;
reg [6:0] prg_bank_3;
reg [9:0] chr_bank_0, chr_bank_1, chr_bank_2, chr_bank_3,
chr_bank_4, chr_bank_5, chr_bank_6, chr_bank_7,
chr_bank_8, chr_bank_9, chr_bank_a, chr_bank_b;
reg [1:0] upper_chr_bank_bits;
reg chr_last; // Which CHR set was written to last?
reg [4:0] vsplit_startstop;
reg vsplit_enable, vsplit_side;
reg [7:0] vsplit_scroll, vsplit_bank;
reg [7:0] irq_scanline;
reg irq_enable;
reg irq_pending;
reg [7:0] multiplier_1;
reg [7:0] multiplier_2;
wire [15:0] multiply_result = multiplier_1 * multiplier_2;
reg [7:0] expansion_ram[0:1023]; // Block RAM, otherwise we need to time multiplex..
reg [7:0] last_read_ram;
reg [7:0] last_read_exattr;
reg [7:0] last_read_vram;
reg last_chr_read;
// unpack ppu flags
//reg display_enable;
//wire ppu_in_frame = ppuflags[0] & display_enable;
wire ppu_in_frame = ppuflags[0];
reg old_ppu_sprite16;
wire ppu_sprite16 = ppuflags[1];
//reg ppu_sprite16;
wire [8:0] ppu_cycle = ppuflags[10:2];
wire [8:0] ppu_scanline = ppuflags[19:11];
// Handle IO register writes
always @(posedge clk) begin
if (ce) begin
if (prg_write && prg_ain[15:10] == 6'b010100) begin // $5000-$53FF
//if (prg_ain <= 16'h5113) $write("%X <= %X (%d)\n", prg_ain, prg_din, ppu_scanline);
casez(prg_ain[9:0])
10'h100: prg_mode <= prg_din[1:0];
10'h101: chr_mode <= prg_din[1:0];
10'h102: prg_protect_1 <= (prg_din[1:0] == 2'b10);
10'h103: prg_protect_2 <= (prg_din[1:0] == 2'b01);
10'h104: extended_ram_mode <= prg_din[1:0];
10'h105: mirroring <= prg_din;
10'h106: fill_tile <= prg_din;
10'h107: fill_attr <= prg_din[1:0];
10'h113: prg_ram_bank <= prg_din[2:0];
10'h114: prg_bank_0 <= prg_din;
10'h115: prg_bank_1 <= prg_din;
10'h116: prg_bank_2 <= prg_din;
10'h117: prg_bank_3 <= prg_din[6:0];
10'h120: chr_bank_0 <= {upper_chr_bank_bits, prg_din};
10'h121: chr_bank_1 <= {upper_chr_bank_bits, prg_din};
10'h122: chr_bank_2 <= {upper_chr_bank_bits, prg_din};
10'h123: chr_bank_3 <= {upper_chr_bank_bits, prg_din};
10'h124: chr_bank_4 <= {upper_chr_bank_bits, prg_din};
10'h125: chr_bank_5 <= {upper_chr_bank_bits, prg_din};
10'h126: chr_bank_6 <= {upper_chr_bank_bits, prg_din};
10'h127: chr_bank_7 <= {upper_chr_bank_bits, prg_din};
10'h128: chr_bank_8 <= {upper_chr_bank_bits, prg_din};
10'h129: chr_bank_9 <= {upper_chr_bank_bits, prg_din};
10'h12a: chr_bank_a <= {upper_chr_bank_bits, prg_din};
10'h12b: chr_bank_b <= {upper_chr_bank_bits, prg_din};
10'h130: upper_chr_bank_bits <= prg_din[1:0];
10'h200: {vsplit_enable, vsplit_side, vsplit_startstop} <= {prg_din[7:6], prg_din[4:0]};
10'h201: vsplit_scroll <= prg_din;
10'h202: vsplit_bank <= prg_din;
10'h203: irq_scanline <= prg_din;
10'h204: irq_enable <= prg_din[7];
10'h205: multiplier_1 <= prg_din;
10'h206: multiplier_2 <= prg_din;
default: begin end
endcase
// Remember which set of CHR was written to last.
// chr_last is set to 0 when changing bank with sprites set to 8x8
if (prg_ain[9:4] == 6'b010010) //(prg_ain[9:0] >= 10'h120 && prg_ain[9:0] < 10'h130)
chr_last <= prg_ain[3] & ppu_sprite16;
end
//Not currently passing prg_write when ppu_cs
//if (prg_write && prg_ain == 16'h2000) begin // $2000
// ppu_sprite16 <= (prg_din[5]);
//end
//if (prg_write && prg_ain == 16'h2001) begin // $2001
// display_enable <= (prg_din[4:3] != 2'b0);
//end
// chr_last is set to 0 when changing sprite size to 8x8
old_ppu_sprite16 <= ppu_sprite16;
if (old_ppu_sprite16 != ppu_sprite16 && ~ppu_sprite16)
chr_last <= 0;
end
// Mode 0/1 - Not readable (returns open bus), can only be written while the PPU is rendering (otherwise, 0 is written)
// Mode 2 - Readable and writable
// Mode 3 - Read-only
if (extended_ram_mode != 3) begin
if (ppu_ce && !ppu_in_frame && !extended_ram_mode[1] && chr_write && (mirrbits == 2) && chr_ain[13])
expansion_ram[chr_ain[9:0]] <= chr_din;
else if (ce && prg_write && prg_ain[15:10] == 6'b010111) // $5C00-$5FFF
expansion_ram[prg_ain[9:0]] <= (extended_ram_mode[1] || ppu_in_frame) ? prg_din : 8'd0;
end
if (~enable) begin
prg_bank_3 <= 7'h7F;
prg_mode <= 3;
end
end
// Read from MMC5
always @* begin
prg_bus_write = 1'b1;
if (prg_ain[15:10] == 6'b010111 && extended_ram_mode[1]) begin
prg_dout = last_read_ram;
end else if (prg_ain == 16'h5204) begin
prg_dout = {irq_pending, ppu_in_frame, 6'b111111};
end else if (prg_ain == 16'h5205) begin
prg_dout = multiply_result[7:0];
end else if (prg_ain == 16'h5206) begin
prg_dout = multiply_result[15:8];
end else if (prg_ain == 16'h5015) begin
prg_dout = {6'h00, audio_dout[1:0]};
// TODO: 5010
end else begin
prg_dout = 8'hFF; // By default open bus.
prg_bus_write = 0;
end
end
// Determine IRQ handling
reg last_scanline;
wire irq_trig = (irq_scanline != 0 && irq_scanline < 240 && ppu_scanline == {1'b0, irq_scanline});
always @(posedge clk) if (ce || ppu_ce) begin
last_scanline <= ppu_scanline[0];
if ((ce && prg_read && prg_ain == 16'h5204) || ~ppu_in_frame)
irq_pending <= 0;
else if (ppu_scanline[0] != last_scanline && irq_trig)
irq_pending <= 1;
end
assign irq = irq_pending && irq_enable;
// Determine if vertical split is active.
reg [5:0] cur_tile; // Current tile the PPU is fetching
reg [5:0] new_cur_tile; // New value for |cur_tile|
reg [7:0] vscroll; // Current y scroll for the split region
reg last_in_split_area, in_split_area;
// Compute if we're in the split area right now by counting PPU tiles.
always @* begin
new_cur_tile = (ppu_cycle[8:3] == 40) ? 6'd0 : (cur_tile + 6'b1);
in_split_area = last_in_split_area;
if (ppu_cycle[2:0] == 0 && ppu_cycle < 336) begin
if (new_cur_tile == 0)
in_split_area = !vsplit_side;
else if (new_cur_tile == {1'b0, vsplit_startstop})
in_split_area = vsplit_side;
else if (new_cur_tile == 34)
in_split_area = 0;
end
end
always @(posedge clk) if (ppu_ce) begin
last_in_split_area <= in_split_area;
if (ppu_cycle[2:0] == 0 && ppu_cycle < 336)
cur_tile <= new_cur_tile;
end
// Keep track of scroll
always @(posedge clk) if (ppu_ce) begin
if (ppu_cycle == 319)
vscroll <= ppu_scanline[8] ? vsplit_scroll :
(vscroll == 239) ? 8'b0 : vscroll + 8'b1;
end
// Mirroring bits
// %00 = NES internal NTA
// %01 = NES internal NTB
// %10 = use ExRAM as NT
// %11 = Fill Mode
wire [1:0] mirrbits = (chr_ain[11:10] == 0) ? mirroring[1:0] :
(chr_ain[11:10] == 1) ? mirroring[3:2] :
(chr_ain[11:10] == 2) ? mirroring[5:4] :
mirroring[7:6];
// Compute the new overriden nametable/attr address the split will read from instead
// when the VSplit is active.
// Cycle 0, 1 = nametable
// Cycle 2, 3 = attribute
// Named it loopy so I can copypaste from PPU code :)
wire [9:0] loopy = {vscroll[7:3], cur_tile[4:0]};
wire [9:0] split_addr = (ppu_cycle[1] == 0) ? loopy : // name table
{4'b1111, loopy[9:7], loopy[4:2]}; // attribute table
// Selects 2 out of the attr bits read from exram.
wire [1:0] split_attr = (!loopy[1] && !loopy[6]) ? last_read_ram[1:0] :
( loopy[1] && !loopy[6]) ? last_read_ram[3:2] :
(!loopy[1] && loopy[6]) ? last_read_ram[5:4] :
last_read_ram[7:6];
// If splitting is active or not
wire insplit = in_split_area && vsplit_enable;
// Currently reading the attribute byte?
wire exattr_read = (extended_ram_mode == 1) && (ppu_cycle[2:1]==1) && ppu_in_frame;
// If the current chr read should be redirected from |chr_dout| instead.
assign has_chr_dout = chr_ain[13] && (mirrbits[1] || insplit || exattr_read);
wire [1:0] override_attr = insplit ? split_attr : (extended_ram_mode == 1) ? last_read_exattr[7:6] : fill_attr;
always @* begin
if (ppu_in_frame) begin
if (ppu_cycle[1] == 0) begin
// Name table fetch
if (insplit || mirrbits[0] == 0)
chr_dout = (extended_ram_mode[1] ? 8'b0 : last_read_ram);
else begin
// Inserting Filltile
chr_dout = fill_tile;
end
end else begin
// Attribute table fetch
if (!insplit && !exattr_read && mirrbits[0] == 0)
chr_dout = (extended_ram_mode[1] ? 8'b0 : last_read_ram);
else
chr_dout = {override_attr, override_attr, override_attr, override_attr};
end
end else begin
chr_dout = last_read_vram;
end
end
// Handle reading from the expansion ram.
// 0 - Use as extra nametable (possibly for split mode)
// 1 - Use as extended attribute data OR an extra nametable
// 2 - Use as ordinary RAM
// 3 - Use as ordinary RAM, write protected
wire [9:0] exram_read_addr = extended_ram_mode[1] ? prg_ain[9:0] : insplit ? split_addr : chr_ain[9:0];
always @(posedge clk) begin
last_read_ram <= expansion_ram[exram_read_addr];
if ((ppu_cycle[2] == 0) && (ppu_cycle[1] == 0) && ppu_in_frame) begin
last_read_exattr <= last_read_ram;
end
last_chr_read <= chr_read;
if (!chr_read && last_chr_read)
last_read_vram <= extended_ram_mode[1] ? 8'b0 : last_read_ram;
end
// Compute PRG address to read from.
reg [7:0] prgsel;
always @* begin
casez({prg_mode, prg_ain[15:13]})
5'b??_0??: prgsel = {5'b0xxxx, prg_ram_bank}; // $6000-$7FFF all modes
5'b00_1??: prgsel = {1'b1, prg_bank_3[6:2], prg_ain[14:13]}; // $8000-$FFFF mode 0, 32kB (prg_bank_3, skip 2 bits)
5'b01_10?: prgsel = { prg_bank_1[7:1], prg_ain[13]}; // $8000-$BFFF mode 1, 16kB (prg_bank_1, skip 1 bit)
5'b01_11?: prgsel = {1'b1, prg_bank_3[6:1], prg_ain[13]}; // $C000-$FFFF mode 1, 16kB (prg_bank_3, skip 1 bit)
5'b10_10?: prgsel = { prg_bank_1[7:1], prg_ain[13]}; // $8000-$BFFF mode 2, 16kB (prg_bank_1, skip 1 bit)
5'b10_110: prgsel = { prg_bank_2}; // $C000-$DFFF mode 2, 8kB (prg_bank_2)
5'b10_111: prgsel = {1'b1, prg_bank_3}; // $E000-$FFFF mode 2, 8kB (prg_bank_3)
5'b11_100: prgsel = { prg_bank_0}; // $8000-$9FFF mode 3, 8kB (prg_bank_0)
5'b11_101: prgsel = { prg_bank_1}; // $A000-$BFFF mode 3, 8kB (prg_bank_1)
5'b11_110: prgsel = { prg_bank_2}; // $C000-$DFFF mode 3, 8kB (prg_bank_2)
5'b11_111: prgsel = {1'b1, prg_bank_3}; // $E000-$FFFF mode 3, 8kB (prg_bank_3)
endcase
//Original
//prgsel[7] = !prgsel[7]; // 0 means RAM, doh.
//Done below
//if (prgsel[7])
// prgsel[7] = 0; //ROM
//else
// // Limit to 64k RAM.
// prgsel[7:3] = 5'b1_1100; //RAM location for saves
end
assign prg_aout = {prgsel[7] ? {2'b00, prgsel[6:0]} : {6'b11_1100, prgsel[2:0]}, prg_ain[12:0]}; // 8kB banks
// Registers $5120-$5127 apply to sprite graphics and $5128-$512B for background graphics, but ONLY when 8x16 sprites are enabled.
// Otherwise, the last set of registers written to (either $5120-$5127 or $5128-$512B) will be used for all graphics.
// 0 if using $5120-$5127, 1 if using $5128-$512F
wire is_bg_fetch = !(ppu_cycle[8] && !ppu_cycle[6]);
wire chrset = (ppu_sprite16 && ppu_in_frame) ? is_bg_fetch : chr_last;
reg [9:0] chrsel;
always @* begin
casez({chr_mode, chr_ain[12:10], chrset})
6'b00_???_0: chrsel = {chr_bank_7[6:0], chr_ain[12:10]}; // $0000-$1FFF mode 0, 8 kB
6'b00_???_1: chrsel = {chr_bank_b[6:0], chr_ain[12:10]}; // $0000-$1FFF mode 0, 8 kB
6'b01_0??_0: chrsel = {chr_bank_3[7:0], chr_ain[11:10]}; // $0000-$0FFF mode 1, 4 kB
6'b01_1??_0: chrsel = {chr_bank_7[7:0], chr_ain[11:10]}; // $1000-$1FFF mode 1, 4 kB
6'b01_???_1: chrsel = {chr_bank_b[7:0], chr_ain[11:10]}; // $0000-$0FFF mode 1, 4 kB
6'b10_00?_0: chrsel = {chr_bank_1[8:0], chr_ain[10]}; // $0000-$07FF mode 2, 2 kB
6'b10_01?_0: chrsel = {chr_bank_3[8:0], chr_ain[10]}; // $0800-$0FFF mode 2, 2 kB
6'b10_10?_0: chrsel = {chr_bank_5[8:0], chr_ain[10]}; // $1000-$17FF mode 2, 2 kB
6'b10_11?_0: chrsel = {chr_bank_7[8:0], chr_ain[10]}; // $1800-$1FFF mode 2, 2 kB
6'b10_?0?_1: chrsel = {chr_bank_9[8:0], chr_ain[10]}; // $0000-$07FF mode 2, 2 kB
6'b10_?1?_1: chrsel = {chr_bank_b[8:0], chr_ain[10]}; // $0800-$0FFF mode 2, 2 kB
6'b11_000_0: chrsel = chr_bank_0; // $0000-$03FF mode 3, 1 kB
6'b11_001_0: chrsel = chr_bank_1; // $0400-$07FF mode 3, 1 kB
6'b11_010_0: chrsel = chr_bank_2; // $0800-$0BFF mode 3, 1 kB
6'b11_011_0: chrsel = chr_bank_3; // $0C00-$0FFF mode 3, 1 kB
6'b11_100_0: chrsel = chr_bank_4; // $1000-$13FF mode 3, 1 kB
6'b11_101_0: chrsel = chr_bank_5; // $1400-$17FF mode 3, 1 kB
6'b11_110_0: chrsel = chr_bank_6; // $1800-$1BFF mode 3, 1 kB
6'b11_111_0: chrsel = chr_bank_7; // $1C00-$1FFF mode 3, 1 kB
6'b11_?00_1: chrsel = chr_bank_8; // $0000-$03FF mode 3, 1 kB
6'b11_?01_1: chrsel = chr_bank_9; // $0400-$07FF mode 3, 1 kB
6'b11_?10_1: chrsel = chr_bank_a; // $0800-$0BFF mode 3, 1 kB
6'b11_?11_1: chrsel = chr_bank_b; // $0C00-$0FFF mode 3, 1 kB
endcase
chr_aout = {2'b10, chrsel, chr_ain[9:0]}; // 1kB banks
// Override |chr_aout| if we're in a vertical split.
if (ppu_in_frame && insplit) begin
//$write("In vertical split!\n");
// chr_aout = {2'b10, vsplit_bank, chr_ain[11:3], vscroll[2:0]}; // SL
chr_aout = {2'b10, vsplit_bank, chr_ain[11:3], chr_ain[2:0]}; // CL
end else if (ppu_in_frame && extended_ram_mode == 1 && is_bg_fetch && (ppu_cycle[2:1]!=0)) begin
//$write("In exram thingy!\n");
// Extended attribute mode. Replace the page with the page from exram.
chr_aout = {2'b10, upper_chr_bank_bits, last_read_exattr[5:0], chr_ain[11:0]};
end
end
// The a10 VRAM address line. (Used for mirroring)
assign vram_a10 = mirrbits[0];
assign vram_ce = chr_ain[13] && !mirrbits[1];
// Writing to RAM is enabled only when the protect bits say so.
wire prg_ram_we = prg_protect_1 && prg_protect_2;
assign prg_allow = (prg_ain >= 16'h6000) && (!prg_write || ((!prgsel[7]) && prg_ram_we));
// MMC5 boards typically have no CHR RAM.
assign chr_allow = flags[15];
endmodule
module mmc5_mixed (
input clk,
input ce, // Negedge M2 (aka CPU ce)
input enable,
input wren,
input rden,
input [15:0] addr_in,
input [7:0] data_in,
output [7:0] data_out,
input [15:0] audio_in, // Inverted audio from APU
output [15:0] audio_out
);
// NOTE: The apu volume is 100% of MMC5 and the polarity is reversed.
wire [16:0] audio_o = audio + audio_in;
wire [15:0] audio;
assign audio_out = audio_o[16:1];
wire apu_cs = (addr_in[15:5]==11'b0101_0000_000) && (addr_in[3]==0);
wire DmaReq; // 1 when DMC wants DMA
wire [15:0] DmaAddr; // Address DMC wants to read
reg odd_or_even;
wire apu_irq; // TODO: IRQ asserted
always @(posedge clk)
if (~enable)
odd_or_even <= 0;
else if (ce)
odd_or_even <= ~odd_or_even;
APU mmc5apu(
.MMC5 (1),
.clk (clk),
.ce (ce),
.reset (~enable),
.ADDR (addr_in[4:0]),
.DIN (data_in),
.DOUT (data_out),
.MW (wren && apu_cs),
.MR (rden && apu_cs),
.audio_channels (5'b10011),
.Sample (audio),
.DmaReq (DmaReq),
.DmaAck (1),
.DmaAddr (DmaAddr),
.DmaData (0),
.odd_or_even (odd_or_even),
.IRQ (apu_irq)
);
endmodule

View File

@@ -0,0 +1,425 @@
// Namco mappers
module N163(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b, // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
// Special ports
input [7:0] audio_dout
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? audio[15:0] : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire irq;
wire [15:0] flags_out = {14'h0, prg_bus_write, 1'b0};
wire prg_bus_write = nesprg_oe;
wire [7:0] prg_dout;
wire [15:0] audio = audio_in;
wire nesprg_oe;
wire [7:0] neschrdout;
wire neschr_oe;
wire wram_oe;
wire wram_we;
wire prgram_we;
wire chrram_oe;
wire prgram_oe;
wire [18:13] ramprgaout;
wire exp6;
reg [7:0] m2;
wire m2_n = 1;//~ce; //m2_n not used as clk. Invert m2 (ce).
wire [18:10] chr_aoutm;
always @(posedge clk) begin
m2[7:1] <= m2[6:0];
m2[0] <= ce;
end
MAPN163 n163(m2[7], m2_n, clk, ~enable, prg_write, nesprg_oe, 0,
1, prg_ain, chr_ain, prg_din, 8'b0, prg_dout,
neschrdout, neschr_oe, chr_allow, chrram_oe, wram_oe, wram_we, prgram_we,
prgram_oe, chr_aoutm, ramprgaout, irq, vram_ce, exp6,
0, 7'b1111111, 6'b111111, flags[14], flags[16], flags[15],
ce, (flags[7:0]==210), flags[24:21], audio_dout);
assign chr_aout[21:18] = {4'b1000};
assign chr_aout[17:10] = chr_aoutm[17:10];
assign chr_aout[9:0] = chr_ain[9:0];
assign vram_a10 = chr_aout[10];
wire [21:13] prg_aout_tmp = {3'b00_0, ramprgaout};
wire [21:13] prg_ram = {9'b11_1100_000};
wire prg_is_ram = prg_ain >= 'h6000 && prg_ain < 'h8000;
assign prg_aout[21:13] = prg_is_ram ? prg_ram : prg_aout_tmp;
assign prg_aout[12:0] = prg_ain[12:0];
assign prg_allow = (prg_ain[15] && !prg_write) || prg_is_ram;
endmodule
//Taken from Loopy's Power Pak mapper source mapN106.v
//fixme- sound ram is supposed to be readable (does this affect any games?)
module MAPN163( //signal descriptions in powerpak.v
input m2,
input m2_n,
input clk20,
input reset,
input nesprg_we,
output nesprg_oe,
input neschr_rd,
input neschr_wr,
input [15:0] prgain,
input [13:0] chrain,
input [7:0] nesprgdin,
input [7:0] ramprgdin,
output reg [7:0] nesprgdout,
output [7:0] neschrdout,
output neschr_oe,
output reg chrram_we,
output reg chrram_oe,
output wram_oe,
output wram_we,
output prgram_we,
output prgram_oe,
output reg [18:10] ramchraout,
output [18:13] ramprgaout,
output irq,
output reg ciram_ce,
output exp6,
input cfg_boot,
input [18:12] cfg_chrmask,
input [18:13] cfg_prgmask,
input cfg_vertical,
input cfg_fourscreen,
input cfg_chrram,
input ce,// add
input mapper210,
input [3:0] submapper,
//output [15:0] snd_level,
input [7:0] audio_dout
);
assign exp6 = 0;
reg [1:0] chr_en;
reg [5:0] prg89,prgAB,prgCD;
reg [7:0] chr0,chr1,chr2,chr3,chr4,chr5,chr6,chr7,chr10,chr11,chr12,chr13;
reg [1:0] mirror;
wire submapper1 = (submapper == 1);
always@(posedge clk20) begin
if (reset) begin
mirror <= cfg_vertical ? 2'b01 : 2'b10;
end else if(ce && nesprg_we)
case(prgain[15:11])
5'b10000: chr0<=nesprgdin; //8000
5'b10001: chr1<=nesprgdin;
5'b10010: chr2<=nesprgdin; //9000
5'b10011: chr3<=nesprgdin;
5'b10100: chr4<=nesprgdin; //A000
5'b10101: chr5<=nesprgdin;
5'b10110: chr6<=nesprgdin; //B000
5'b10111: chr7<=nesprgdin;
5'b11000: chr10<=nesprgdin; //C000
5'b11001: chr11<=nesprgdin;
5'b11010: chr12<=nesprgdin; //D000
5'b11011: chr13<=nesprgdin;
5'b11100: {mirror,prg89}<=nesprgdin; //E000
5'b11101: {chr_en,prgAB}<=nesprgdin; //E800
5'b11110: prgCD<=nesprgdin[5:0]; //F000
//5'b11111: //F800 (sound)
endcase
end
//IRQ
reg [15:0] count;
wire [15:0] count_next=count+1'd1;
wire countup=count[15] & ~&count[14:0];
reg timeout;
assign irq=timeout;
always@(posedge clk20) begin
if (ce) begin
if(prgain[15:12]==4'b0101)
timeout<=0;
else if(count==16'hFFFF)
timeout<=1;
if(nesprg_we & prgain[15:11]==5'b01010)
count[7:0]<=nesprgdin;
else if(countup)
count[7:0]<=count_next[7:0];
if(nesprg_we & prgain[15:11]==5'b01011)
count[15:8]<=nesprgdin;
else if(countup)
count[15:8]<=count_next[15:8];
end
end
//PRG bank
reg [18:13] prgbankin;
always@* begin
case(prgain[14:13])
0:prgbankin=prg89;
1:prgbankin=prgAB;
2:prgbankin=prgCD;
3:prgbankin=6'b111111;
endcase
end
assign ramprgaout[18:13]=prgbankin[18:13] & cfg_prgmask & {4'b1111,{2{prgain[15]}}};
//CHR control
reg chrram;
reg [17:10] chrbank;
always@* begin
case(chrain[13:10])
0:chrbank=chr0;
1:chrbank=chr1;
2:chrbank=chr2;
3:chrbank=chr3;
4:chrbank=chr4;
5:chrbank=chr5;
6:chrbank=chr6;
7:chrbank=chr7;
8,12:chrbank=chr10;
9,13:chrbank=chr11;
10,14:chrbank=chr12;
11,15:chrbank=chr13;
endcase
chrram=(~(chrain[12]?chr_en[1]:chr_en[0]))&(&chrbank[17:15]); //ram/rom select
if(!chrain[13]) begin
ciram_ce=chrram && ~mapper210;
chrram_oe=neschr_rd;
chrram_we=neschr_wr & chrram;
ramchraout[10]=chrbank[10];
end else begin
ciram_ce=(&chrbank[17:15]) | mapper210;
chrram_oe=~ciram_ce & neschr_rd;
chrram_we=~ciram_ce & neschr_wr & chrram;
casez({mapper210,submapper1,mirror,cfg_vertical})
5'b0?_??_?: ramchraout[10] = chrbank[10];
//5'b0?_?1_?: ramchraout[10] = chrain[10];
5'b10_00_?: ramchraout[10] = 1'b0;
5'b10_01_?: ramchraout[10] = chrain[10];
5'b10_10_?: ramchraout[10] = chrain[11];
5'b10_11_?: ramchraout[10] = 1'b1;
5'b11_??_0: ramchraout[10] = chrain[11];
5'b11_??_1: ramchraout[10] = chrain[10];
endcase
end
ramchraout[11]=chrbank[11];
ramchraout[17:12]=chrbank[17:12] & cfg_chrmask[17:12];
ramchraout[18]=ciram_ce;
end
assign wram_oe=m2_n & ~nesprg_we & prgain[15:13]==3'b011;
assign wram_we=m2_n & nesprg_we & prgain[15:13]==3'b011;
assign prgram_we=0;
assign prgram_oe= ~cfg_boot & m2_n & ~nesprg_we & prgain[15];
wire config_rd = 0;
//wire [7:0] gg_out;
//gamegenie gg(m2, reset, nesprg_we, prgain, nesprgdin, ramprgdin, gg_out, config_rd);
//PRG data out
wire mapper_oe = m2_n & ~nesprg_we & ((prgain[15:12]=='b0101) || (prgain[15:11]=='b01001));
always @* begin
case(prgain[15:11])
5'b01001: nesprgdout=audio_dout;
5'b01010: nesprgdout=count[7:0];
5'b01011: nesprgdout=count[15:8];
default: nesprgdout=nesprgdin;
endcase
end
assign nesprg_oe=wram_oe | prgram_oe | mapper_oe | config_rd;
assign neschr_oe=0;
assign neschrdout=0;
endmodule
module namco163_mixed (
input clk,
input ce,
input [3:0] submapper,
input enable,
input wren,
input [15:0] addr_in,
input [7:0] data_in,
output [7:0] data_out,
input [15:0] audio_in,
output [15:0] audio_out
);
reg disabled;
always@(posedge clk) begin
if (!enable)
disabled <= 1'b0;
else if (ce) begin
if(wren & addr_in[15:11]==5'b11100) //E000..E7FF
disabled<=data_in[6];
end
end
//sound
wire [10:0] n163_out;
namco163_sound n163(clk, ce, !enable, wren, addr_in, data_in, data_out, n163_out);
//pdm #(10) pdm_mod(clk20, saturated, exp6);
wire [9:0] saturated=n163_out[9:0] | {10{n163_out[10]}}; //this is still too quiet for the suggested 47k resistor, but more clipping will make some games sound bad
wire [15:0] audio = {1'b0, saturated, 5'b0};
wire [16:0] audio_mix = (!enable | disabled) ? {audio_in, 1'b0} : (submapper==5) ? (audio_in>>>2) + audio : (submapper==4) ? (audio_in>>>1) + audio : audio_in + audio;
assign audio_out = audio_mix[16:1];
endmodule
module namco163_sound(
input clk20,
input m2,
input reset,
input wr,
input [15:0] ain,
input [7:0] din,
output [7:0] dout,
output reg [10:0] out //range is 0..0x708
);
reg carry;
reg autoinc;
reg [6:0] ram_ain;
reg [6:0] ram_aout;
wire [7:0] ram_dout;
reg [2:0] ch;
reg [7:0] cnt_L[7:0];
reg [7:0] cnt_M[7:0];
reg [1:0] cnt_H[7:0];
wire [2:0] sum_H=cnt_H[ch]+ram_dout[1:0]+carry;
reg [4:0] sample_pos[7:0];
reg [2:0] cycle;
reg [3:0] sample;
wire [7:0] chan_out=sample*ram_dout[3:0]; //sample*vol
reg [10:0] out_acc;
wire [10:0] sum=out_acc+chan_out;
reg addr_lsb;
wire [7:0] sample_addr=ram_dout+sample_pos[ch];
reg do_inc;
//ram in
always@(posedge clk20) begin
if (m2) begin
do_inc<= 0;
if (do_inc)
ram_ain<=ram_ain+1'd1;
if(wr & ain[15:11]==5'b11111) //F800..FFFF
{autoinc,ram_ain}<=din;
else if(ain[15:11]==5'b01001 & autoinc) //4800..4FFF
do_inc<=1;
end
end
//mixer FSM
always @* begin
case(cycle)
0: ram_aout={1'b1,ch,3'd0}; //freq[7:0]
1: ram_aout={1'b1,ch,3'd2}; //freq[15:8]
2: ram_aout={1'b1,ch,3'd4}; //length, freq[17:16]
3: ram_aout={1'b1,ch,3'd6}; //address
4: ram_aout=sample_addr[7:1]; //sample address
5: ram_aout={1'b1,ch,3'd7}; //volume
default: ram_aout=7'bXXXXXXX;
endcase
end
reg [3:0] count45,cnt45;
always@(posedge clk20)
if (m2) begin
count45<=(count45==14)?4'd0:count45+1'd1;
end
always@(posedge clk20) begin
cnt45<=count45;
if(cnt45[1:0]==0) cycle<=0; // this gives 45 21.4M clocks per channel
else if(cycle!=7) cycle<=cycle+1'd1;
case(cycle)
1: {carry, cnt_L[ch]}<=cnt_L[ch][7:0]+ram_dout;
2: {carry, cnt_M[ch]}<=cnt_M[ch][7:0]+ram_dout+carry;
3: begin
cnt_H[ch]<=sum_H[1:0];
if(sum_H[2])
sample_pos[ch]<=(sample_pos[ch]=={ram_dout[4:2]^3'b111,2'b11})?5'd0:(sample_pos[ch]+1'd1);
end
4: addr_lsb<=sample_addr[0];
5: sample<=addr_lsb?ram_dout[7:4]:ram_dout[3:0];
6: begin
if(ch==7) begin
ch<=~ram_dout[6:4];
out_acc<=0;
out<=sum;
end else begin
ch<=ch+1'd1;
out_acc<=sum;
end
end
endcase
end
dpram #(.widthad_a(7)) modtable
(
.clock_a (clk20),
.address_a (ram_ain),
.wren_a (wr & ain[15:11]==5'b01001),
.byteena_a (1),
.data_a (din),
.q_a (dout),
.clock_b (clk20),
.address_b (ram_aout),
.wren_b (0),
.byteena_b (1),
.data_b (0),
.q_b (ram_dout)
);
endmodule

View File

@@ -0,0 +1,318 @@
// Sachen Mappers
// 137, 138, 139, 141 - Sachen 8259
// 150, 243 - Sachen 74LS374N
module Sachen8259(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? 1'b0 : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire [15:0] flags_out = {14'd0, prg_bus_write, 1'b0};
// 0x4100
wire [7:0] prg_dout = {5'b00111, (~register)}; // ^ (counter & 0x1)
wire prg_bus_write = ({prg_ain[15:14], prg_ain[8], prg_ain[0]} == 4'b0110);
wire mapper137 = (flags[7:0] == 137);
wire mapper138 = (flags[7:0] == 138);
wire mapper139 = (flags[7:0] == 139);
wire mapper141 = (flags[7:0] == 141);
wire mapper150 = (flags[7:0] == 150);
wire mapper243 = (flags[7:0] == 243);
reg [2:0] register;
reg [2:0] prg_bank;
reg [2:0] chr_bank_0, chr_bank_1, chr_bank_2, chr_bank_3, chr_bank_o, chr_bank_p;
reg [2:0] mirroring;
always @(posedge clk)
if (~enable) begin
//any resets?
end else if (ce && prg_write) begin
if ({prg_ain[15:14], prg_ain[8], prg_ain[0]} == 4'b0110) begin
register <= prg_din[2:0];
end else if ({prg_ain[15:14], prg_ain[8], prg_ain[0]} == 4'b0111) begin
case (register)
0: begin
chr_bank_0 <= prg_din[2:0]; // Select 2 KB CHR bank at PPU $0000-$07FF;
if (mapper243) {prg_bank, chr_bank_2[0], chr_bank_p[1:0], chr_bank_o[0]} = 7'b000_0011;
end
1: chr_bank_1 <= prg_din[2:0]; // Select 2 KB CHR bank at PPU $0800-$0FFF;
2: begin
chr_bank_2 <= prg_din[2:0]; // Select 2 KB CHR bank at PPU $1000-$17FF;
if (mapper150) prg_bank = {2'b00, prg_din[0]};
end
3: chr_bank_3 <= prg_din[2:0]; // Select 2 KB CHR bank at PPU $1800-$1FFF;
4: chr_bank_o <= prg_din[2:0]; // Outer CHR bank
5: prg_bank <= prg_din[2:0]; // Select 32 KB PRG ROM bank at $8000-$FFFF;
6: chr_bank_p <= prg_din[2:0]; // Outer CHR bank
7: mirroring <= prg_din[2:0]; // Select Mirroring
endcase
end
end
// The CHR bank to load. Each increment here is 1kb. So valid values are 0..255.
wire [2:0] chr_bank;
wire [2:0] chr_banko;
reg [8:0] chrsel;
always @* begin
casez({mirroring[0], mapper137 ? chr_ain[11:10] : chr_ain[12:11]})
3'b000: chr_bank = chr_bank_0;
3'b001: chr_bank = chr_bank_1;
3'b010: chr_bank = chr_bank_2;
3'b011: chr_bank = chr_bank_3;
3'b1??: chr_bank = chr_bank_0;
endcase
casez({mapper137, chr_ain[11:10]})
3'b0??: chr_banko = chr_bank_o;
3'b100: chr_banko = 3'b000;
3'b101: chr_banko = {1'b0, chr_bank_o[0], 1'b0};
3'b110: chr_banko = {1'b0, chr_bank_o[1], 1'b0};
3'b111: chr_banko = {1'b0, chr_bank_o[2], chr_bank_p[0]};
endcase
if (mapper137)
chrsel = chr_ain[12] ? {7'h7F, chr_ain[11:10]} : {3'b111, chr_banko, chr_bank};
else if (mapper138)
chrsel = {2'b11, chr_banko, chr_bank, chr_ain[10]};
else if (mapper141)
chrsel = {1'b1, chr_banko, chr_bank, chr_ain[11:10]};
else if (mapper139)
chrsel = {chr_banko, chr_bank, chr_ain[12:10]};
else if (mapper150)
chrsel = {2'b00, chr_bank_2[0], chr_bank_o[0], chr_bank_p[1:0], chr_ain[12:10]};
else if (mapper243)
chrsel = {2'b00, chr_bank_2[0], chr_bank_p[1:0], chr_bank_o[0], chr_ain[12:10]};
else
chrsel = 9'h000;
end
always @* begin
casez(mapper150 ? {mirroring[2:1], 1'b0} : mapper243 ? {1'b0, mirroring[0], 1'b0} : mirroring)
3'b??1: vram_a10 = chr_ain[10];
3'b000: vram_a10 = chr_ain[10];
3'b010: vram_a10 = chr_ain[11];
3'b100: vram_a10 = chr_ain[10] | chr_ain[11];
3'b110: vram_a10 = 1'b0;
endcase
end
assign prg_aout = {4'b00_00, prg_bank, prg_ain[14:0]};
assign chr_allow = flags[15];
assign chr_aout = {3'b10_0, chrsel, chr_ain[9:0]};
assign vram_ce = chr_ain[13];
//assign vram_a10 = ; //done above
assign prg_allow = prg_ain[15] && !prg_write;
endmodule
// #136,#147 - Sachen JV001
// #36, #132 - TXC
// #173 - Idea-Tek
// #172 - Super Mega P-4070
module SachenJV001(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? 1'b0 : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire [15:0] flags_out = {14'd0, prg_bus_write, 1'b0};
reg [5:0] input_reg; //s_reg = input_reg[3]
reg [5:0] output_reg;
reg [5:0] register_reg;
reg inc_reg;
reg invert_reg;
reg [3:0] alt_chr;
reg mirroring;
//wire mapper136 = (flags[7:0] == 136); //default
wire mapper132 = (flags[7:0] == 132);
wire mapper147 = (flags[7:0] == 147);
wire mapper173 = (flags[7:0] == 173);
wire mapper172 = (flags[7:0] == 172);
wire mapper36 = (flags[7:0] == 36);
wire [7:0] prg_din_adj = mapper147 ? {2'b00, prg_din[7:2]} :
mapper36 ? {4'b0000, prg_din[7:4]} :
mapper172 ? {2'b00, prg_din[0], prg_din[1], prg_din[2], prg_din[3], prg_din[4], prg_din[5]} :
prg_din;
wire prg_bus_write = (prg_ain[15:13] == 3'b010 && prg_ain[8]); //0x4100-3
always @(posedge clk)
if (~enable) begin
//
end else if (ce) begin
if (prg_ain[15:13] == 3'b010 && prg_ain[8] && prg_write) //0x4100-3
case (prg_ain[1:0])
0: begin
if (inc_reg)
register_reg[3:0] <= register_reg[3:0] + 4'b1;
else
register_reg <= invert_reg ? {input_reg[5:4], ~input_reg[3:0]} : input_reg;
end
1: invert_reg <= prg_din_adj[0];
2: input_reg <= prg_din_adj[5:0];
3: inc_reg <= prg_din_adj[0];
endcase
if (prg_ain[15:13] == 3'b010 && prg_ain[9] && prg_write) //0x4200
alt_chr <= prg_din[3:0];
if (prg_ain[15] == 1'b1 && prg_write) begin
output_reg <= register_reg;
mirroring <= invert_reg;
end
end
// 0x4100-3
wire use_s = mapper132 | mapper173;
wire [5:0] V001_val = (use_s ? {2'b00, input_reg[3], register_reg[2:0]} : register_reg) ^ (invert_reg ? (use_s ? 6'h08 : 6'h30) : 6'h00);
wire [7:0] prg_dout = mapper172 ? {chr_ain[7:6], V001_val[0], V001_val[1], V001_val[2], V001_val[3], V001_val[4], V001_val[5]} :
mapper147 ? {V001_val, chr_ain[1:0]} :
{chr_ain[7:6], V001_val}; // two bits are open bus
wire [3:0] chr_sel = mapper36 ? alt_chr :
mapper173 ? {2'b00, ~invert_reg, output_reg[0]} :
mapper147 ? {output_reg[4:1]} :
mapper172 ? {2'b00, output_reg[1:0]} :
{2'b00, output_reg[1:0]};
wire [1:0] prg_sel = mapper147 ? {output_reg[5], output_reg[0]} :
mapper132 ? {1'b0, output_reg[2]} :
mapper36 ? {output_reg[1:0]} :
2'b00;
assign prg_aout = {5'b00_000, prg_sel, prg_ain[14:0]};
assign prg_allow = prg_ain[15] && !prg_write;
assign chr_allow = flags[15];
assign chr_aout = {5'b10_000, chr_sel, chr_ain[12:0]};
assign vram_ce = chr_ain[13];
assign vram_a10 = (mapper172 ? mirroring : flags[14]) ? chr_ain[10] : chr_ain[11]; // 0: horiz, 1: vert
endmodule
// #143 - Sachen NROM with protection
module SachenNROM(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? 1'b0 : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire prg_bus_write;
wire [15:0] flags_out = {14'h0, prg_bus_write, 1'b0};
// 0x4100
wire [7:0] prg_dout = {2'b01, ~prg_ain[5:0]}; // top 2 bits are actually open bus
assign prg_bus_write = ((prg_ain[15:13] == 3'b010) && prg_ain[8]);
assign prg_aout = {7'b00_0000_0, prg_ain[14:0]};
assign prg_allow = prg_ain[15] && !prg_write;
assign chr_allow = flags[15];
assign chr_aout = {9'b10_0000_000, chr_ain[12:0]};
assign vram_ce = chr_ain[13];
assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11]; // 0: horiz, 1: vert
endmodule

View File

@@ -0,0 +1,574 @@
// 69 - Sunsoft FME-7
module Mapper69(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? 8'hFF : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? audio : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
reg vram_a10;
wire vram_ce;
reg irq;
reg [15:0] flags_out = 0;
wire [15:0] audio;
reg [7:0] chr_bank[0:7];
reg [4:0] prg_bank[0:3];
reg [1:0] mirroring;
reg irq_countdown, irq_trigger;
reg [15:0] irq_counter;
reg [3:0] addr;
reg ram_enable, ram_select;
wire [16:0] new_irq_counter = irq_counter - {15'b0, irq_countdown};
always @(posedge clk)
if (~enable) begin
chr_bank[0] <= 0;
chr_bank[1] <= 0;
chr_bank[2] <= 0;
chr_bank[3] <= 0;
chr_bank[4] <= 0;
chr_bank[5] <= 0;
chr_bank[6] <= 0;
chr_bank[7] <= 0;
prg_bank[0] <= 0;
prg_bank[1] <= 0;
prg_bank[2] <= 0;
prg_bank[3] <= 0;
mirroring <= 0;
irq_countdown <= 0;
irq_trigger <= 0;
irq_counter <= 0;
addr <= 0;
ram_enable <= 0;
ram_select <= 0;
irq <= 0;
end else if (ce) begin
irq_counter <= new_irq_counter[15:0];
if (irq_trigger && new_irq_counter[16]) irq <= 1;
if (!irq_trigger) irq <= 0;
if (prg_ain[15] & prg_write) begin
case (prg_ain[14:13])
0: addr <= prg_din[3:0];
1: begin
case(addr)
0,1,2,3,4,5,6,7: chr_bank[addr[2:0]] <= prg_din;
8,9,10,11: prg_bank[addr[1:0]] <= prg_din[4:0];
12: mirroring <= prg_din[1:0];
13: {irq_countdown, irq_trigger} <= {prg_din[7], prg_din[0]};
14: irq_counter[7:0] <= prg_din;
15: irq_counter[15:8] <= prg_din;
endcase
if (addr == 8) {ram_enable, ram_select} <= prg_din[7:6];
end
endcase
end
end
always begin
casez(mirroring[1:0])
2'b00: vram_a10 = {chr_ain[10]}; // vertical
2'b01: vram_a10 = {chr_ain[11]}; // horizontal
2'b1?: vram_a10 = {mirroring[0]}; // 1 screen lower
endcase
end
reg [4:0] prgout;
reg [7:0] chrout;
always begin
casez(prg_ain[15:13])
3'b011: prgout = prg_bank[0];
3'b100: prgout = prg_bank[1];
3'b101: prgout = prg_bank[2];
3'b110: prgout = prg_bank[3];
3'b111: prgout = 5'b11111;
default: prgout = 5'bxxxxx;
endcase
chrout = chr_bank[chr_ain[12:10]];
end
wire ram_cs = (prg_ain[15:13] == 3'b011 && ram_select);
assign prg_aout = {ram_cs ? 4'b1111 : 4'b0000, prgout[4:0], prg_ain[12:0]};
assign prg_allow = (prg_ain >= 16'h6000) && (ram_cs ? ram_enable : !prg_write);
assign chr_allow = flags[15];
assign chr_aout = {4'b10_00, chrout, chr_ain[9:0]};
assign vram_ce = chr_ain[13];
assign audio = audio_in;
endmodule
module SS5b_mixed (
input clk,
input ce, // Negedge M2 (aka CPU ce)
input enable,
input wren,
input [15:0] addr_in,
input [7:0] data_in,
input [15:0] audio_in, // Inverted audio from APU
output [15:0] audio_out
);
SS5b_audio snd_5b (
.clk(clk),
.ce(ce),
.enable(enable),
.wren(wren),
.addr_in(addr_in),
.data_in(data_in),
.audio_out(exp_out)
);
// Sunsoft 5B audio amplifies each channel logarithmicly before mixing. It's then mixed
// with APU audio (reverse polarity) and then reverses the polarity of the audio again.
// The expansion audio is much louder than APU audio, so we reduce it to 68% prior to
// mixing.
wire [15:0] exp_out;
wire [15:0] exp_adj = (|exp_out[15:14] ? 16'hFFFF : {exp_out[13:0], exp_out[1:0]});
wire [16:0] audio_mix = audio_in + (exp_adj + exp_adj[15:1]);
assign audio_out = 16'hFFFF - audio_mix[16:1];
endmodule
// Sunsoft 5B audio by Kitrinx
module SS5b_audio (
input clk,
input ce, // Negedge M2 (aka CPU ce)
input enable,
input wren,
input [15:0] addr_in,
input [7:0] data_in,
output [15:0] audio_out
);
reg [3:0] reg_select;
// Register bank
reg [7:0] internal[0:15];
// Register abstraction to readable wires
// Periods
wire [11:0] period_a = {internal[1][3:0], internal[0]};
wire [11:0] period_b = {internal[3][3:0], internal[2]};
wire [11:0] period_c = {internal[5][3:0], internal[4]};
wire [4:0] period_n = internal[6][4:0];
// Enables
wire tone_dis_a = internal[7][0];
wire tone_dis_b = internal[7][1];
wire tone_dis_c = internal[7][2];
wire noise_dis_a = internal[7][3];
wire noise_dis_b = internal[7][4];
wire noise_dis_c = internal[7][5];
// Envelope
// wire env_enable_a = internal[8][4];
wire [3:0] env_vol_a = internal[8][3:0];
// wire env_enable_b = internal[9][4];
wire [3:0] env_vol_b = internal[9][3:0];
// wire env_enable_c = internal[10][4];
wire [3:0] env_vol_c = internal[10][3:0];
// wire [15:0] env_period = {internal[12], internal[11]};
// wire env_continue = internal[13][3];
// wire env_attack = internal[13][2];
// wire env_alt = internal[13][1];
// wire env_hold = internal[13][0];
reg [4:0] cycles;
reg [11:0] tone_a_cnt, tone_b_cnt, tone_c_cnt, noise_cnt;
reg [4:0] tone_a, tone_b, tone_c;
wire [12:0] tone_a_next = tone_a_cnt + 1'b1;
wire [12:0] tone_b_next = tone_b_cnt + 1'b1;
wire [12:0] tone_c_next = tone_c_cnt + 1'b1;
wire [12:0] noise_next = noise_cnt + 1'b1;
reg [16:0] noise_lfsr = 17'h1;
reg [5:0] envelope_a, envelope_b, envelope_c;
always_ff @(posedge clk)
if (~enable) begin
internal <= '{
8'd0, 8'd0, 8'd0, 8'd0, 8'd0, 8'd0, 8'd0, 8'd0,
8'd0, 8'd0, 8'd0, 8'd0, 8'd0, 8'd0, 8'd0, 8'd0};
{tone_a, tone_b, tone_c, envelope_a, envelope_b, envelope_c, cycles, noise_lfsr} <= 0;
{tone_a_cnt, tone_b_cnt, tone_c_cnt, noise_cnt} <= 0;
end else if (ce) begin
cycles <= cycles + 1'b1;
// Write registers
if (wren) begin
if (addr_in[15:13] == 3'b110) // C000
reg_select <= data_in[3:0];
if (addr_in[15:13] == 3'b111) // E000
internal[reg_select] <= data_in;
end
tone_a_cnt <= tone_a_next[11:0];
tone_b_cnt <= tone_b_next[11:0];
tone_c_cnt <= tone_c_next[11:0];
if (tone_a_next >= period_a) begin
tone_a_cnt <= 12'd0;
tone_a <= tone_a + 1'b1;
end
if (tone_b_next >= period_b) begin
tone_b_cnt<= 12'd0;
tone_b <= tone_b + 1'b1;
end
if (tone_c_next >= period_c) begin
tone_c_cnt <= 12'd0;
tone_c <= tone_c + 1'b1;
end
// XXX: Implement modulation envelope if needed (not used in any games)
envelope_a <= {env_vol_a, 1'b1};
envelope_b <= {env_vol_b, 1'b1};
envelope_c <= {env_vol_c, 1'b1};
if (&cycles) begin
// Advance noise LFSR every 32 cycles
noise_cnt <= noise_next[11:0];
if (noise_next >= period_n) begin
noise_lfsr <= {noise_lfsr[15:0], noise_lfsr[16] ^ noise_lfsr[13]};
noise_cnt <= 12'd0;
end
end
end
wire output_a, output_b, output_c;
always_comb begin
case ({tone_dis_a, noise_dis_a})
2'b00: output_a = noise_lfsr[0] & tone_a[4];
2'b01: output_a = tone_a[4];
2'b10: output_a = noise_lfsr[0];
2'b11: output_a = 1'b0;
endcase
case ({tone_dis_b, noise_dis_b})
2'b00: output_b = noise_lfsr[0] & tone_b[4];
2'b01: output_b = tone_b[4];
2'b10: output_b = noise_lfsr[0];
2'b11: output_b = 1'b0;
endcase
case ({tone_dis_c, noise_dis_c})
2'b00: output_c = noise_lfsr[0] & tone_c[4];
2'b01: output_c = tone_c[4];
2'b10: output_c = noise_lfsr[0];
2'b11: output_c = 1'b0;
endcase
end
assign audio_out =
{output_a ? ss5b_amp_lut[envelope_a] : 8'h0, 5'b0} +
{output_b ? ss5b_amp_lut[envelope_b] : 8'h0, 5'b0} +
{output_c ? ss5b_amp_lut[envelope_c] : 8'h0, 5'b0} ;
// Logarithmic amplification table in 1.5db steps
wire [7:0] ss5b_amp_lut[0:31] = '{
8'd0, 8'd0, 8'd1, 8'd1, 8'd1, 8'd1, 8'd2, 8'd2,
8'd3, 8'd3, 8'd4, 8'd5, 8'd6, 8'd7, 8'd9, 8'd11,
8'd13, 8'd15, 8'd18, 8'd22, 8'd26, 8'd31, 8'd37, 8'd44,
8'd53, 8'd63, 8'd74, 8'd89, 8'd105, 8'd125, 8'd149, 8'd177
};
endmodule
// Mapper 190, Magic Kid GooGoo
// Mapper 67, Sunsoft-3
module Mapper67 (
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? 8'hFF : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
reg irq;
reg [15:0] flags_out = 0;
reg [7:0] prg_bank_0;
reg [7:0] chr_bank_0, chr_bank_1, chr_bank_2, chr_bank_3;
reg [1:0] mirroring;
reg irq_ack;
reg irq_enable;
reg irq_low;
reg [15:0] irq_counter;
wire mapper190 = (flags[7:0] == 190);
always @(posedge clk)
if (~enable) begin
prg_bank_0 <= 0;
chr_bank_0 <= 0;
chr_bank_1 <= 0;
chr_bank_2 <= 0;
chr_bank_3 <= 0;
mirroring <= 2'b00; //vertical for mapper190
irq_counter <= 0;
irq_enable <= 0;
irq_low <= 0;
end else if (ce) begin
irq_ack <= 1'b0;
if ((prg_write) && (prg_ain[15])) begin// Cover all from $8000 to $FFFF to maximize compatibility
if (!mapper190)
casez({prg_ain[14:11],irq_low})
5'b000_1_?: chr_bank_0 <= prg_din;
5'b001_1_?: chr_bank_1 <= prg_din;
5'b010_1_?: chr_bank_2 <= prg_din;
5'b011_1_?: chr_bank_3 <= prg_din;
5'b110_1_?: mirroring <= prg_din[1:0];
5'b111_1_?: prg_bank_0 <= prg_din;
5'b100_1_0: {irq_low, irq_counter[15:8]} <= {1'b1,prg_din};
5'b100_1_1: {irq_low, irq_counter[7:0]} <= {1'b0,prg_din};
5'b101_1_?: {irq_low, irq_ack, irq_enable} <= {2'b01, prg_din[4]};
endcase
else
casez({prg_ain[13],prg_ain[1:0]})
3'b0_??: prg_bank_0[3:0] <= {prg_ain[14],prg_din[2:0]};
3'b1_00: chr_bank_0 <= prg_din;
3'b1_01: chr_bank_1 <= prg_din;
3'b1_10: chr_bank_2 <= prg_din;
3'b1_11: chr_bank_3 <= prg_din;
endcase
end
if (irq_enable) begin
irq_counter <= irq_counter - 16'd1;
if (irq_counter == 16'h0) begin
irq <= 1'b1; // IRQ
irq_enable <= 0;
end
end
if (irq_ack)
irq <= 1'b0; // IRQ ACK
end
always begin
casez({mirroring})
2'b00 : vram_a10 = {chr_ain[10]}; // vertical
2'b01 : vram_a10 = {chr_ain[11]}; // horizontal
2'b1? : vram_a10 = {mirroring[0]}; // 1 screen lower:upper
endcase
end
reg [7:0] prgsel;
always begin
case(prg_ain[14])
1'b0: prgsel = prg_bank_0; // $8000 is swapable
1'b1: prgsel = mapper190 ? 8'h00 : 8'hFF; // $C000 is hardwired to first/last bank
endcase
end
reg [7:0] chrsel;
always begin
casez(chr_ain[12:11])
0: chrsel = chr_bank_0;
1: chrsel = chr_bank_1;
2: chrsel = chr_bank_2;
3: chrsel = chr_bank_3;
endcase
end
assign chr_aout = {3'b10_0, chrsel, chr_ain[10:0]}; // 2kB banks
wire [21:0] prg_aout_tmp = {2'b00, prgsel[5:0], prg_ain[13:0]}; // 16kB banks
wire prg_is_ram = (prg_ain >= 'h6000) && (prg_ain < 'h8000);
wire [21:0] prg_ram = {9'b11_1100_000, prg_ain[12:0]};
assign prg_aout = prg_is_ram ? prg_ram : prg_aout_tmp;
assign prg_allow = (prg_ain[15] && !prg_write) || prg_is_ram;
assign chr_allow = flags[15];
assign vram_ce = chr_ain[13];
endmodule
// #68 - Sunsoft-4 - Game After Burner, and some japanese games. MAX: 128kB PRG, 256kB CHR
module Mapper68(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? 8'hFF : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? 1'b0 : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire ram_enable;
wire chr_allow;
wire vram_a10;
wire vram_ce;
reg [15:0] flags_out = 0;
reg [6:0] chr_bank_0, chr_bank_1, chr_bank_2, chr_bank_3;
reg [6:0] nametable_0, nametable_1;
reg [3:0] prg_bank;
reg use_chr_rom;
reg [1:0] mirroring;
always @(posedge clk)
if (~enable) begin
chr_bank_0 <= 0;
chr_bank_1 <= 0;
chr_bank_2 <= 0;
chr_bank_3 <= 0;
nametable_0 <= 0;
nametable_1 <= 0;
prg_bank <= 0;
ram_enable <= 0;
use_chr_rom <= 0;
mirroring <= 0;
end else if (ce) begin
if (prg_ain[15] && prg_write) begin
case(prg_ain[14:12])
0: chr_bank_0 <= prg_din[6:0]; // $8000-$8FFF: 2kB CHR bank at $0000
1: chr_bank_1 <= prg_din[6:0]; // $9000-$9FFF: 2kB CHR bank at $0800
2: chr_bank_2 <= prg_din[6:0]; // $A000-$AFFF: 2kB CHR bank at $1000
3: chr_bank_3 <= prg_din[6:0]; // $B000-$BFFF: 2kB CHR bank at $1800
4: nametable_0 <= prg_din[6:0]; // $C000-$CFFF: 1kB Nametable register 0 at $2000
5: nametable_1 <= prg_din[6:0]; // $D000-$DFFF: 1kB Nametable register 1 at $2400
6: {use_chr_rom, mirroring} <= {prg_din[4], prg_din[1:0]}; // $E000-$EFFF: Nametable control
7: {ram_enable, prg_bank} <= prg_din[4:0]; // $F000-$FFFF: 16kB PRG banks at $8000-$BFFF and WRAM enable
endcase
end
end
// $C000-$FFFF wired to last PRG bank
wire [3:0] prgout = ((prg_ain[15:14] == 2'b11) ? 4'b1111 : prg_bank);
wire prg_is_ram = (prg_ain[15:13] == 3'b011);
wire [21:0] prg_ram = {9'b11_1100_000, prg_ain[12:0]};
assign prg_aout = prg_is_ram ? prg_ram : {4'b00_00, prgout, prg_ain[13:0]};
assign prg_allow = (prg_ain[15] && !prg_write) || (prg_is_ram && ram_enable);
reg [6:0] chrout;
always begin
casez(chr_ain[12:11])
0: chrout = chr_bank_0;
1: chrout = chr_bank_1;
2: chrout = chr_bank_2;
3: chrout = chr_bank_3;
endcase
end
always begin
casez(mirroring[1:0])
2'b00: vram_a10 = {chr_ain[10]}; // vertical
2'b01: vram_a10 = {chr_ain[11]}; // horizontal
2'b1?: vram_a10 = {mirroring[0]}; // 1 screen lower
endcase
end
wire [6:0] nameout = (vram_a10 == 0) ? nametable_0 : nametable_1;
assign chr_allow = flags[15];
assign chr_aout = (chr_ain[13] == 0) ? {4'b10_00, chrout, chr_ain[10:0]} : {5'b10_001, nameout, chr_ain[9:0]};
assign vram_ce = chr_ain[13] && !use_chr_rom;
endmodule

View File

@@ -0,0 +1,974 @@
// Konami VRC Mappers
// VRC1 (75)
module VRC1(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? 8'hFF : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? 1'b0 : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
reg vram_a10;
wire vram_ce;
reg [15:0] flags_out = 0;
reg [3:0] prg_bank0, prg_bank1, prg_bank2;
reg [4:0] chr_bank0, chr_bank1;
reg [1:0] mirroring;
reg [3:0] prg_tmp;
reg [4:0] chr_tmp;
always @(posedge clk)
if (~enable) begin
// Set value for mirroring
mirroring[1:0] <= {flags[16], !flags[14]};
end else if (ce) begin
if (prg_ain[15] & prg_write) begin
case (prg_ain[14:12])
3'b000: prg_bank0 <= prg_din[3:0]; // PRG bank 0x8000-0x9FFF
3'b001: {chr_bank1[4],chr_bank0[4],mirroring[0]} <= prg_din[2:0];
3'b010: prg_bank1 <= prg_din[3:0]; // PRG bank 0xA000-0xBFFF
3'b100: prg_bank2 <= prg_din[3:0]; // PRG bank 0xC000-0xEFFF
3'b110: chr_bank0[3:0] <= prg_din[3:0]; // CHR bank 0x0000-0x0FFF
3'b111: chr_bank1[3:0] <= prg_din[3:0]; // CHR bank 0x1000-0x1FFF
endcase
end
end
always begin
// mirroring mode
casez(mirroring[1:0])
2'b00 : vram_a10 = {chr_ain[10]}; // vertical
2'b01 : vram_a10 = {chr_ain[11]}; // horizontal
2'b1? : vram_a10 = {mirroring[0]}; // 4 screen // Not implemented
endcase
// PRG ROM bank size select
casez(prg_ain[14:13])
2'b00 : prg_tmp = prg_bank0;
2'b01 : prg_tmp = prg_bank1;
2'b10 : prg_tmp = prg_bank2;
2'b11 : prg_tmp = 4'b1111;
endcase
// PRG ROM bank size select
casez(chr_ain[12])
1'b0 : chr_tmp = chr_bank0;
1'b1 : chr_tmp = chr_bank1;
endcase
end
assign vram_ce = chr_ain[13];
assign prg_aout = {5'b00_000, prg_tmp, prg_ain[12:0]};
assign prg_allow = prg_ain[15] && !prg_write;
assign chr_allow = flags[15];
assign chr_aout = {5'b10_000, chr_tmp, chr_ain[11:0]};
endmodule
// VRC3 (73)
module VRC3(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? 8'hFF : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
reg irq;
reg [15:0] flags_out = 0;
reg [2:0] prg_bank;
reg [4:0] irq_enable;
reg [15:0] irq_latch;
reg [15:0] irq_counter;
always @(posedge clk)
if (~enable) begin
// Set value for mirroring
irq <= 0;
prg_bank <= 0;
irq_enable <= 0;
irq_latch <= 0;
end else if (ce) begin
irq_enable[3] <= 1'b0;
if (prg_ain[15] & prg_write) begin
case (prg_ain[14:12])
3'b000: irq_latch[3:0] <= prg_din[3:0];
3'b001: irq_latch[7:4] <= prg_din[3:0];
3'b010: irq_latch[11:8] <= prg_din[3:0];
3'b011: irq_latch[15:12] <= prg_din[3:0];
3'b100: irq_enable[4:0] <= {2'b11, prg_din[2:0]};
3'b101: irq_enable[4:3] <= 2'b01;
3'b111: prg_bank <= prg_din[2:0]; // PRG bank 0x8000-0xBFFF
endcase
end
if (irq_enable[1]) begin
irq_counter[7:0] <= irq_counter[7:0] + 8'd1;
if (irq_counter[7:0] == 8'hFF) begin
if (irq_enable[2]) begin
irq <= 1'b1; // IRQ
end else begin
irq_counter[15:8] <= irq_counter[15:8] + 8'd1;
if (irq_counter[15:8] == 8'hFF) begin
irq <= 1'b1; // IRQ
end
end
end
end
if (irq_enable[3]) begin
irq <= 1'b0; // IRQ ACK
if (irq_enable[4])
irq_counter <= irq_latch;
else
irq_enable[1] <= irq_enable[0];
end
end
assign vram_ce = chr_ain[13];
assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11];
wire prg_is_ram = (prg_ain[15:13] == 3'b011);//prg_ain >= 'h6000 && prg_ain < 'h8000;
assign prg_aout = prg_is_ram ? {9'b11_1100_000, prg_ain[12:0]} : {5'b00_000, prg_ain[14] ? 3'b111 : prg_bank, prg_ain[13:0]};
assign prg_allow = (prg_ain[15] && !prg_write) || prg_is_ram;
assign chr_allow = flags[15];
assign chr_aout = {8'b10_0000_00, chr_ain[13:0]};
endmodule
// VRC2 and VRC4
module VRC24(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? 8'hFF : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {1'b0, audio_in[15:1]} : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
reg vram_a10;
wire vram_ce;
wire irq;
reg [15:0] flags_out = 0;
reg [4:0] prg_bank0, prg_bank1;
reg [8:0] chr_bank0, chr_bank1, chr_bank2, chr_bank3, chr_bank4, chr_bank5, chr_bank6, chr_bank7;
reg [1:0] mirroring;
reg [4:0] prg_tmp;
reg [8:0] chr_tmp;
reg prg_invert;
wire mapper21 = (flags[7:0] == 21);
wire mapper22 = (flags[7:0] == 22);
wire mapper23 = (flags[7:0] == 23 | flags[7:0] == 27);
//wire mapper25 = (flags[7:0] == 25); //default
wire mapperVRC4 = (flags[7:0] != 22) && (flags[24:21] != 3);
wire [1:0] registers = {mapper21 ? {(prg_ain[7]|prg_ain[2]),(prg_ain[6]|prg_ain[1])} :
mapper22 ? {(prg_ain[0]), (prg_ain[1]) } :
mapper23 ? {(prg_ain[3]|prg_ain[1]),(prg_ain[2]|prg_ain[0])} :
/*mapper25*/{(prg_ain[2]|prg_ain[0]),(prg_ain[3]|prg_ain[1])}};
always @(posedge clk)
if (~enable) begin
// Set value for mirroring
mirroring[1:0] <= {1'b0, !flags[14]};
prg_invert <= 0;
prg_bank0 <= 5'd0;
prg_bank1 <= 5'd1;
chr_bank0 <= 9'd0;
chr_bank1 <= 9'd1;
chr_bank2 <= 9'd2;
chr_bank3 <= 9'd3;
chr_bank4 <= 9'd4;
chr_bank5 <= 9'd5;
chr_bank6 <= 9'd6;
chr_bank7 <= 9'd7;
end else if (ce) begin
if (prg_ain[15] & prg_write) begin
casez ({prg_ain[14:12], registers, mapperVRC4})
6'b000_??_?: prg_bank0 <= prg_din[4:0]; // PRG bank 0x8000-0x9FFF or 0xC000-0xDFFF
6'b001_??_0: mirroring[0] <= prg_din[0];
6'b001_0?_1: mirroring <= prg_din[1:0];
6'b001_1?_1: prg_invert <= prg_din[1];
6'b010_??_?: prg_bank1 <= prg_din[4:0]; // PRG bank 0xA000-0xBFFF
6'b011_00_?: chr_bank0[3:0] <= prg_din[3:0]; // CHR bank 0x0000-0x03FF
6'b011_01_?: chr_bank0[8:4] <= prg_din[4:0]; // CHR bank 0x0000-0x03FF
6'b011_10_?: chr_bank1[3:0] <= prg_din[3:0]; // CHR bank 0x0400-0x07FF
6'b011_11_?: chr_bank1[8:4] <= prg_din[4:0]; // CHR bank 0x0400-0x07FF
6'b100_00_?: chr_bank2[3:0] <= prg_din[3:0]; // CHR bank 0x0800-0x0BFF
6'b100_01_?: chr_bank2[8:4] <= prg_din[4:0]; // CHR bank 0x0800-0x0BFF
6'b100_10_?: chr_bank3[3:0] <= prg_din[3:0]; // CHR bank 0x0C00-0x0FFF
6'b100_11_?: chr_bank3[8:4] <= prg_din[4:0]; // CHR bank 0x0C00-0x0FFF
6'b101_00_?: chr_bank4[3:0] <= prg_din[3:0]; // CHR bank 0x1000-0x13FF
6'b101_01_?: chr_bank4[8:4] <= prg_din[4:0]; // CHR bank 0x1000-0x13FF
6'b101_10_?: chr_bank5[3:0] <= prg_din[3:0]; // CHR bank 0x1400-0x17FF
6'b101_11_?: chr_bank5[8:4] <= prg_din[4:0]; // CHR bank 0x1400-0x17FF
6'b110_00_?: chr_bank6[3:0] <= prg_din[3:0]; // CHR bank 0x1800-0x1BFF
6'b110_01_?: chr_bank6[8:4] <= prg_din[4:0]; // CHR bank 0x1800-0x1BFF
6'b110_10_?: chr_bank7[3:0] <= prg_din[3:0]; // CHR bank 0x1C00-0x1FFF
6'b110_11_?: chr_bank7[8:4] <= prg_din[4:0]; // CHR bank 0x1C00-0x1FFF
//6'b111_??_1: IRQ Stuff; // IRQ
endcase
end
end
always begin
// mirroring mode
casez(mirroring[1:0])
2'b00 : vram_a10 = {chr_ain[10]}; // vertical
2'b01 : vram_a10 = {chr_ain[11]}; // horizontal
2'b1? : vram_a10 = {mirroring[0]}; // 1 screen
endcase
// PRG ROM bank size select
casez({prg_ain[14:13],prg_invert})
3'b00_0 : prg_tmp = prg_bank0;
3'b00_1 : prg_tmp = 5'b11110;
3'b01_? : prg_tmp = prg_bank1;
3'b10_0 : prg_tmp = 5'b11110;
3'b10_1 : prg_tmp = prg_bank0;
3'b11_? : prg_tmp = 5'b11111;
endcase
// PRG ROM bank size select
casez(chr_ain[12:10])
3'b000 : chr_tmp = chr_bank0;
3'b001 : chr_tmp = chr_bank1;
3'b010 : chr_tmp = chr_bank2;
3'b011 : chr_tmp = chr_bank3;
3'b100 : chr_tmp = chr_bank4;
3'b101 : chr_tmp = chr_bank5;
3'b110 : chr_tmp = chr_bank6;
3'b111 : chr_tmp = chr_bank7;
endcase
end
assign vram_ce = chr_ain[13];
wire [21:13] prg_aout_tmp = {4'b00_00, prg_tmp};
wire [21:13] prg_ram = {9'b11_1100_000};
wire prg_is_ram = (prg_ain[15:13] == 3'b011);//prg_ain >= 'h6000 && prg_ain < 'h8000;
assign prg_aout[21:13] = prg_is_ram ? prg_ram : prg_aout_tmp;
assign prg_aout[12:0] = prg_ain[12:0];
assign prg_allow = (prg_ain[15] && !prg_write) || prg_is_ram;
assign chr_allow = flags[15];
assign chr_aout = {3'b10_0, vram_ce ? {5'b00000, chr_ain[13:10]} : mapper22 ? {1'b0, chr_tmp[8:1]} : chr_tmp, chr_ain[9:0]};
wire irqll = {prg_ain[15:12],registers[1:0]}==6'b1111_00; // 0xF000
wire irqlh = {prg_ain[15:12],registers[1:0]}==6'b1111_01; // 0xF001
wire irqc = {prg_ain[15:12],registers[1:0]}==6'b1111_10; // 0xF002
wire irqa = {prg_ain[15:12],registers[1:0]}==6'b1111_11; // 0xF003
wire irqout;
assign irq = irqout & mapperVRC4;
vrcIRQ vrc4irq(clk,enable,prg_write,{irqlh,irqll},irqc,irqa,prg_din,irqout,ce);
endmodule
module VRC6(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? audio : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire [7:0] prg_dout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire irq;
wire [15:0] audio = audio_in;
reg [15:0] flags_out = 0;
wire nesprg_oe;
wire [7:0] neschrdout;
wire neschr_oe;
wire wram_oe;
wire wram_we;
wire prgram_we;
wire chrram_oe;
wire prgram_oe;
wire [18:13] ramprgaout;
//wire exp6;
reg [7:0] m2;
wire m2_n = 1;//~ce; //m2_n not used as clk. Invert m2 (ce).
always @(posedge clk) begin
m2[7:1] <= m2[6:0];
m2[0] <= ce;
end
MAPVRC6 vrc6(m2[7], m2_n, clk, enable, prg_write, nesprg_oe, 0,
1, prg_ain, chr_ain, prg_din, 8'b0, prg_dout,
neschrdout, neschr_oe, chr_allow, chrram_oe, wram_oe, wram_we, prgram_we,
prgram_oe, chr_aout[18:10], ramprgaout, irq, vram_ce,// exp6,
0, 7'b1111111, 6'b111111, flags[14], flags[16], flags[15],
ce, flags[1]);
assign chr_aout[21:19] = 3'b100;
assign chr_aout[9:0] = chr_ain[9:0];
assign vram_a10 = chr_aout[10];
wire [21:13] prg_aout_tmp = {3'b00_0, ramprgaout};
wire [21:13] prg_ram = {9'b11_1100_000};
wire prg_is_ram = prg_ain >= 'h6000 && prg_ain < 'h8000;
assign prg_aout[21:13] = prg_is_ram ? prg_ram : prg_aout_tmp;
assign prg_aout[12:0] = prg_ain[12:0];
assign prg_allow = (prg_ain[15] && !prg_write) || prg_is_ram;
endmodule
module VRC7(
input clk, // System clock
input ce, // M2 ~cpu_clk
input enable, // Mapper enabled
input [31:0] flags, // Cart flags
input [15:0] prg_ain, // prg address
inout [21:0] prg_aout_b, // prg address out
input prg_read, // prg read
input prg_write, // prg write
input [7:0] prg_din, // prg data in
inout [7:0] prg_dout_b, // prg data out
inout prg_allow_b, // Enable access to memory for the specified operation.
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
inout irq_b, // IRQ
input [15:0] audio_in, // Inverted audio from APU
inout [15:0] audio_b, // Mixed audio output
inout [15:0] flags_out_b // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout}
);
assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? 8'hFF : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? irq : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? audio : 16'hZ;
wire [21:0] prg_aout, chr_aout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire irq;
reg [15:0] flags_out = 0;
wire [15:0] audio = audio_in;
assign chr_aout[21:18] = 4'b1000;
assign chr_aout[9:0] = chr_ain[9:0];
assign chr_aout[17:11] = chrbank[17:11];
assign chr_aout[10]=!chr_ain[13] ? chrbank[10] : ((mirror==0 & chr_ain[10]) | (mirror==1 & chr_ain[11]) | (mirror==3));
assign vram_ce=chr_ain[13];
assign vram_a10=chr_aout[10];
assign chr_allow=!chr_ain[13] & flags[15];
wire [21:13] prg_aout_tmp = {3'b00_0, prgbankin};
wire [21:13] prg_ram = {9'b11_1100_000};
wire prg_is_ram = prg_ain >= 'h6000 && prg_ain < 'h8000;
assign prg_aout[21:13] = prg_is_ram ? prg_ram : prg_aout_tmp;
assign prg_aout[12:0] = prg_ain[12:0];
assign prg_allow = (prg_ain[15] && !prg_write) || (prg_is_ram && (!prg_write || ramw));
reg [7:0] chrbank0, chrbank1, chrbank2, chrbank3, chrbank4, chrbank5, chrbank6, chrbank7;
reg [1:0] mirror;
reg [5:0] prgbank8;
reg [5:0] prgbankA;
reg [5:0] prgbankC;
wire prg_ain43 = prg_ain[4] ^ prg_ain[3];
reg ramw;
always@(posedge clk) begin
if (~enable) begin
{chrbank0, chrbank1, chrbank2, chrbank3, chrbank4, chrbank5, chrbank6, chrbank7} <= 0;
{prgbank8, prgbankA, prgbankC} <= 0;
ramw <= 0;
end else if(ce && prg_write) begin
casex({prg_ain[15:12],prg_ain43})
5'b10000:prgbank8<=prg_din[5:0]; //8000
5'b10001:prgbankA<=prg_din[5:0]; //8008/10
5'b10010:prgbankC<=prg_din[5:0]; //9000
5'b10100:chrbank0<=prg_din; //A000
5'b10101:chrbank1<=prg_din; //A008/10
5'b10110:chrbank2<=prg_din; //B000
5'b10111:chrbank3<=prg_din; //B008/10
5'b11000:chrbank4<=prg_din; //C000
5'b11001:chrbank5<=prg_din; //C008/10
5'b11010:chrbank6<=prg_din; //D000
5'b11011:chrbank7<=prg_din; //D008/10
5'b11100:{ramw,mirror}<={prg_din[7],prg_din[1:0]}; //E000
//5'b11101:irqlatch<=nesprgdin; //E008/10
//5'b11110:{irqM,irqA}<={nesprgdin[2],nesprgdin[0]}; //F000
endcase
end
end
reg [18:13] prgbankin;
reg [17:10] chrbank;
always@* begin
casex(prg_ain[15:13])
3'b100:prgbankin=prgbank8; //89
3'b101:prgbankin=prgbankA; //AB
3'b110:prgbankin=prgbankC; //CD
default:prgbankin=6'b111111; //EF
endcase
case(chr_ain[12:10])
0:chrbank=chrbank0;
1:chrbank=chrbank1;
2:chrbank=chrbank2;
3:chrbank=chrbank3;
4:chrbank=chrbank4;
5:chrbank=chrbank5;
6:chrbank=chrbank6;
7:chrbank=chrbank7;
endcase
end
wire irql = {prg_ain[15:12],prg_ain43}==5'b11101; // 0xE008 or 0xE010
wire irqc = {prg_ain[15:12],prg_ain43}==5'b11110; // 0xF000
wire irqa = {prg_ain[15:12],prg_ain43}==5'b11111; // 0xF008 or 0xF010
vrcIRQ vrc7irq(clk,enable,prg_write,{irql,irql},irqc,irqa,prg_din,irq,ce);
endmodule
//Taken from Loopy's Power Pak mapper source mapVRC6.v
// change ain below to set VRC6 variant
module MAPVRC6( //signal descriptions in powerpak.v
input m2,
input m2_n,
input clk20,
input enable,
input nesprg_we,
output nesprg_oe,
input neschr_rd,
input neschr_wr,
input [15:0] prgain,
input [13:0] chrain,
input [7:0] nesprgdin,
input [7:0] ramprgdin,
output [7:0] nesprgdout,
output [7:0] neschrdout,
output neschr_oe,
output chrram_we,
output chrram_oe,
output wram_oe,
output wram_we,
output prgram_we,
output prgram_oe,
output [18:10] ramchraout,
output [18:13] ramprgaout,
output irq,
output ciram_ce,
// output exp6,
input cfg_boot,
input [18:12] cfg_chrmask,
input [18:13] cfg_prgmask,
input cfg_vertical,
input cfg_fourscreen,
input cfg_chrram,
input ce,// add
//output [15:0] audio,
input mapper26
);
//wire [15:0] ain=prgain; //MAP18
//wire [15:0] ain={prgain[15:2],prgain[0],prgain[1]}; //MAP1A
wire [15:0] ain=mapper26 ? {prgain[15:2],prgain[0],prgain[1]} : prgain; //MAP1A : MAP18
reg [4:0] prgbank8;
reg [5:0] prgbankC;
reg [7:0] chrbank0, chrbank1, chrbank2, chrbank3, chrbank4, chrbank5, chrbank6, chrbank7;
reg [1:0] mirror;
//reg [7:0] irqlatch;
//reg irqM,irqE,irqA;
wire irql = {ain[15:12],ain[1:0]}==6'b111100;
wire irqc = {ain[15:12],ain[1:0]}==6'b111101;
wire irqa = {ain[15:12],ain[1:0]}==6'b111110;
always@(posedge clk20) begin
if (~enable)
{prgbank8, prgbankC, mirror, chrbank0, chrbank1, chrbank2,
chrbank3, chrbank4, chrbank5, chrbank6, chrbank7} <= 0;
else if(ce && nesprg_we) begin
casex({ain[15:12],ain[1:0]})
6'b1000xx:prgbank8<=nesprgdin[4:0]; //800x
6'b1100xx:prgbankC<=nesprgdin[5:0]; //C00x
6'b101111:mirror<=nesprgdin[3:2]; //B003
6'b110100:chrbank0<=nesprgdin; //D000
6'b110101:chrbank1<=nesprgdin; //D001
6'b110110:chrbank2<=nesprgdin; //D002
6'b110111:chrbank3<=nesprgdin; //D003
6'b111000:chrbank4<=nesprgdin; //E000
6'b111001:chrbank5<=nesprgdin; //E001
6'b111010:chrbank6<=nesprgdin; //E002
6'b111011:chrbank7<=nesprgdin; //E003
//6'b111100:irqlatch<=nesprgdin; //F000
//6'b111101:{irqM,irqA}<={nesprgdin[2],nesprgdin[0]}; //F001
endcase
end
end
//bankswitch
reg [18:13] prgbankin;
reg [17:10] chrbank;
always@* begin
casex(prgain[15:13])
3'b0xx:prgbankin=0; //sram
3'b10x:prgbankin={prgbank8,prgain[13]}; //89AB
3'b110:prgbankin=prgbankC; //CD
default:prgbankin=6'b111111; //EF
endcase
case(chrain[12:10])
0:chrbank=chrbank0;
1:chrbank=chrbank1;
2:chrbank=chrbank2;
3:chrbank=chrbank3;
4:chrbank=chrbank4;
5:chrbank=chrbank5;
6:chrbank=chrbank6;
7:chrbank=chrbank7;
endcase
if (~enable) begin
prgbankin = 0;
chrbank = 0;
end
end
vrcIRQ vrc6irq(clk20,enable,nesprg_we,{irql,irql},irqc,irqa,nesprgdin,irq,ce);
//mirroring
assign ramchraout[10]=!chrain[13] ? chrbank[10] : ((mirror==0 & chrain[10]) | (mirror==1 & chrain[11]) | (mirror==3));
assign ramchraout[11]=chrbank[11];
assign ciram_ce=chrain[13];
//rom size mask
assign ramprgaout[18:13]=prgbankin[18:13] & cfg_prgmask;
assign ramchraout[18:12]={1'b0,chrbank[17:12]} & cfg_chrmask;
//ram control
assign chrram_we=neschr_wr & !chrain[13] & cfg_chrram;
assign chrram_oe=neschr_rd & !chrain[13];
assign neschr_oe=0;
assign neschrdout=0;
assign wram_oe=m2_n & ~nesprg_we & prgain[15:13]=='b011;
assign wram_we=m2_n & nesprg_we & prgain[15:13]=='b011;
assign prgram_we=0;
assign prgram_oe=~cfg_boot & m2_n & ~nesprg_we & prgain[15];
wire config_rd = 0;
assign nesprgdout=8'b0;
assign nesprg_oe=wram_oe | prgram_oe | config_rd;
endmodule
module vrcIRQ(
input clk20,
input enable,
input nesprg_we,
input [1:0] irqlatch_add,
input irqctrl_add,
input irqack_add,
input [7:0] nesprgdin,
output irq,
input ce
);
reg [7:0] irqlatch;
reg irqM,irqE,irqA;
always@(posedge clk20) begin
if (~enable)
{irqM, irqA, irqlatch} <= 0;
else if(ce && nesprg_we) begin
if (irqlatch_add == 2'b11)
irqlatch<=nesprgdin; //F000
else if (irqlatch_add == 2'b10)
irqlatch[7:4]<=nesprgdin[3:0]; //F000h
else if (irqlatch_add == 2'b01)
irqlatch[3:0]<=nesprgdin[3:0]; //F000l
else if (irqctrl_add)
{irqM,irqA}<={nesprgdin[2],nesprgdin[0]}; //F001
end
end
//IRQ
reg [7:0] irqcnt;
reg timeout;
reg [6:0] scalar;
reg [1:0] line;
wire irqclk=irqM|(scalar==0);
wire setE=nesprg_we & irqctrl_add & nesprgdin[1];
always@(posedge clk20) begin
if (~enable)
{irqcnt, scalar, line} <= 0;
else if(setE) begin
scalar<=113;
line<=0;
irqcnt<=irqlatch;
end else if(ce && irqE) begin
if(scalar!=0)
scalar<=scalar-1'd1;
else begin
scalar<=(~line[1])?7'd113:7'd112;
line<=line[1]?2'd0:line+1'd1;
end
if(irqclk) begin
if(irqcnt==255)
irqcnt<=irqlatch;
else
irqcnt<=irqcnt+1'd1;
end
end
end
always@(posedge clk20) begin
if(~enable) begin
irqE<=0;
timeout<=0;
end else if (ce) begin
if(nesprg_we & (irqctrl_add | irqack_add)) //write Fxx1 or Fxx2
timeout<=0;
else if(irqclk & irqcnt==255)
timeout<=1;
if(nesprg_we & irqctrl_add) //write Fxx1
irqE<=nesprgdin[1];
else if(nesprg_we & irqack_add) //write Fxx2
irqE<=irqA;
end
end
assign irq=timeout & irqE;
endmodule
module vrc7_mixed (
input clk,
input ce, // Negedge M2 (aka CPU ce)
input enable,
input wren,
input [15:0] addr_in,
input [7:0] data_in,
input [15:0] audio_in, // Inverted audio from APU
output [15:0] audio_out
);
reg soff;
wire prg_ain43 = addr_in[4] ^ addr_in[3];
always@(posedge clk) begin
if (~enable) begin
soff <= 1'b0;
end else if(ce && wren && {addr_in[15:12],prg_ain43} == 5'b11100) begin
soff<=data_in[6]; //E000
end
end
reg [3:0] ce_count;
always@(posedge clk) begin
if (~enable)
ce_count <= 0;
else if (ce)
ce_count <= 0;
else
ce_count <= ce_count + 4'd1;
end
wire ack;
wire ce_ym2143 = ce | (ce_count==4'd5);
wire signed [13:0] ym2143audio;
wire wr_audio = wren && (addr_in[15:6]==10'b1001_0000_00) && (addr_in[4:0]==5'b1_0000); //0x9010 or 0x9030
eseopll ym2143vrc7 (clk,~enable, ce_ym2143,wr_audio,ce_ym2143,ack,wr_audio,{15'b0,addr_in[5]},data_in,ym2143audio);
// The strategy here:
// VRC7 sound is very low, and the top bit is seldom (if ever) used. It's output as signed with
// an actual used range of 6 * +/-512 = +/-3072. What we do is convert to unsigned (+2048),
// then clip to 4095. This clips the top 50% of the values, which are unlikely to be needed. This volume
// is low compared to NES audio, so we mix accordingly, again clipping if needed. The result
// is audio mixed more or less correctly and at a similar level to the audio from regular games.
wire [13:0] audio_exp = ym2143audio + 14'h800;
wire [13:0] audio_clip = audio_exp > 14'hFFF ? 14'hFFF : audio_exp;
wire [15:0] audio_boost = {audio_clip[11:0], 4'b0000};
wire [16:0] audio_mixed = audio_in[15:1] + audio_boost[15:1] + audio_boost[15:2] + audio_boost[15:4];
assign audio_out = soff ? audio_in[15:1] : (audio_mixed[16] ? 16'hFFFF : audio_mixed[15:0]);
endmodule
module vrc6_mixed (
input clk,
input ce, // Negedge M2 (aka CPU ce)
input enable,
input wren,
input addr_invert,
input [15:0] addr_in,
input [7:0] data_in,
input [15:0] audio_in, // Inverted audio from APU
output [15:0] audio_out
);
vrc6sound snd_vrc6 (
.clk(clk),
.ce(ce),
.enable(enable),
.wr(wren),
.addr_invert(addr_invert),
.addr_in(addr_in),
.din(data_in),
.outSq1(vrc6sq1_out),
.outSq2(vrc6sq2_out),
.outSaw(vrc6saw_out)
);
//sound
// wire [5:0] vrc6_out;
// assign exp6 = 0;
wire [3:0] vrc6sq1_out;
wire [3:0] vrc6sq2_out;
wire [4:0] vrc6saw_out;
// VRC6 sound is mixed before amplification, and them amplified linearly
wire [5:0] exp_audio = vrc6sq1_out + vrc6sq2_out + vrc6saw_out;
wire [15:0] audio = {exp_audio, exp_audio, exp_audio[5:2]};
// VRC6 audio is much louder than APU audio, so match the levels we have to reduce it
// to about 43% to match the audio ratio of the original Famicom with AD3. Note that the
// VRC6 audio is opposite polarity from APU audio.
wire [16:0] mixed_audio = audio_in + (audio[15:1] + audio[15:3]);
assign audio_out = mixed_audio[16:1];
endmodule
module vrc6sound(
input clk,
input ce,
input enable,
input wr,
input addr_invert,
input [15:0] addr_in,
input [7:0] din,
output [3:0] outSq1, //range=0..0x0F
output [3:0] outSq2, //range=0..0x0F
output [4:0] outSaw //range=0..0x1F
);
wire [15:0] ain=addr_invert ? {addr_in[15:2],addr_in[0],addr_in[1]} : addr_in; //MAP1A : MAP18
reg mode0, mode1;
reg [3:0] vol0, vol1;
reg [5:0] vol2;
reg [2:0] duty0, duty1;
reg [11:0] freq0, freq1, freq2;
reg [11:0] div0, div1;
reg [12:0] div2;
reg en0, en1, en2;
reg [3:0] duty0cnt, duty1cnt;
reg [2:0] duty2cnt;
reg [7:0] acc;
always@(posedge clk) begin
if(~enable) begin
en0<=0;
en1<=0;
en2<=0;
end else if(ce) begin
if(wr) begin
case(ain)
16'h9000: {mode0, duty0, vol0}<=din;
16'h9001: freq0[7:0]<=din;
16'h9002: {en0, freq0[11:8]} <= {din[7],din[3:0]};
16'hA000: {mode1, duty1, vol1}<=din;
16'hA001: freq1[7:0]<=din;
16'hA002: {en1, freq1[11:8]} <= {din[7],din[3:0]};
16'hB000: vol2<=din[5:0];
16'hB001: freq2[7:0]<=din;
16'hB002: {en2, freq2[11:8]}<={din[7],din[3:0]};
endcase
end
if(en0) begin
if(div0!=0)
div0<=div0-1'd1;
else begin
div0<=freq0;
duty0cnt<=duty0cnt+1'd1;
end
end
if(en1) begin
if(div1!=0)
div1<=div1-1'd1;
else begin
div1<=freq1;
duty1cnt<=duty1cnt+1'd1;
end
end
if(en2) begin
if(div2!=0)
div2<=div2-1'd1;
else begin
div2<={freq2,1'b1};
if(duty2cnt==6) begin
duty2cnt<=0;
acc<=0;
end else begin
duty2cnt<=duty2cnt+1'd1;
acc<=acc+vol2;
end
end
end
end
end
wire [4:0] duty0pos=duty0cnt+{1'b1,~duty0};
wire [4:0] duty1pos=duty1cnt+{1'b1,~duty1};
wire [3:0] ch0=((~duty0pos[4]|mode0)&en0)?vol0:4'd0;
wire [3:0] ch1=((~duty1pos[4]|mode1)&en1)?vol1:4'd0;
wire [4:0] ch2=en2?acc[7:3]:5'd0;
assign outSq1=ch0;
assign outSq2=ch1;
assign outSaw=ch2;
endmodule

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,6 @@
// Copyright (c) 2012-2013 Ludvig Strigeus
// This program is GPL Licensed. See COPYING for the full license.
//`include "cpu.v"
//`include "apu.v"
//`include "ppu.v"
//`include "mmu.v"
// Sprite DMA Works as follows.
// When the CPU writes to $4014 DMA is initiated ASAP.
// DMA runs for 512 cycles, the first cycle it reads from address
@@ -15,326 +10,553 @@
// 1) Sprite DMA always does reads on even cycles and writes on odd cycles.
// 2) There are 1-2 cycles of cpu_read=1 after cpu_read=0 until Sprite DMA starts (pause_cpu=1, aout_enable=0)
// 3) Sprite DMA reads the address value on the last clock of cpu_read=0
/*
=== DMC State Machine ===
//
if (dmc_state == 0 && dmc_trigger && cpu_read && !odd_cycle) dmc_state <= 1;
if (dmc_state == 1) dmc_state <= (spr_state[1] ? 3 : 2);
pause_cpu = dmc_state[1] && cpu_read;
if (dmc_state == 2 && cpu_read && !odd_cycle) dmc_state <= 3;
aout_enable = (dmc_state == 3 && !odd_cycle)
dmc_ack = (dmc_state == 3 && !odd_cycle)
read = 1
if (dmc_state == 3 && !odd_cycle) dmc_state <= 0;
== Sprite State Machine ==
if (sprite_trigger) { sprite_dma_addr <= data_from_cpu; spr_state <= 1; }
pause_cpu = spr_state[0] && cpu_read;
if (spr_state == 1 && cpu_read && odd_cycle) spr_state <= 3;
if (spr_state == 3 && !odd_cycle) { if (dmc_state == 3) spr_state <= 1; else DO_READ; }
if (spr_state == 3 && odd_cycle) { DO_WRITE; }
// 4) If DMC interrupts Sprite, then it runs on the even cycle, and the odd cycle will be idle (pause_cpu=1, aout_enable=0)
// 5) When DMC triggers && interrupts CPU, there will be 2-3 cycles (pause_cpu=1, aout_enable=0) before DMC DMA starts.
// https://wiki.nesdev.com/w/index.php/PPU_OAM
// https://wiki.nesdev.com/w/index.php/APU_DMC
// https://forums.nesdev.com/viewtopic.php?f=3&t=6100
// https://forums.nesdev.com/viewtopic.php?f=3&t=14120
module DmaController(
input clk,
input ce,
input reset,
input odd_cycle, // Current cycle even or odd?
input sprite_trigger, // Sprite DMA trigger?
input dmc_trigger, // DMC DMA trigger?
input cpu_read, // CPU is in a read cycle?
input [7:0] data_from_cpu, // Data written by CPU?
input [7:0] data_from_ram, // Data read from RAM?
input [15:0] dmc_dma_addr, // DMC DMA Address
output [15:0] aout, // Address to access
output aout_enable, // DMA controller wants bus control
output read, // 1 = read, 0 = write
output [7:0] data_to_ram, // Value to write to RAM
output dmc_ack, // ACK the DMC DMA
output pause_cpu // CPU is pausede
);
reg dmc_state;
reg [1:0] spr_state;
reg [7:0] sprite_dma_lastval;
reg [15:0] sprite_dma_addr; // sprite dma source addr
wire [8:0] new_sprite_dma_addr = sprite_dma_addr[7:0] + 8'h01;
always @(posedge clk) if (reset) begin
dmc_state <= 0;
spr_state <= 0;
sprite_dma_lastval <= 0;
sprite_dma_addr <= 0;
end else if (ce) begin
if (dmc_state == 0 && dmc_trigger && cpu_read && !odd_cycle) dmc_state <= 1;
if (dmc_state == 1 && !odd_cycle) dmc_state <= 0;
if (sprite_trigger) begin sprite_dma_addr <= {data_from_cpu, 8'h00}; spr_state <= 1; end
if (spr_state == 1 && cpu_read && odd_cycle) spr_state <= 3;
if (spr_state[1] && !odd_cycle && dmc_state == 1) spr_state <= 1;
if (spr_state[1] && odd_cycle) sprite_dma_addr[7:0] <= new_sprite_dma_addr[7:0];
if (spr_state[1] && odd_cycle && new_sprite_dma_addr[8]) spr_state <= 0;
if (spr_state[1]) sprite_dma_lastval <= data_from_ram;
end
assign pause_cpu = (spr_state[0] || dmc_trigger);
assign dmc_ack = (dmc_state == 1 && !odd_cycle);
assign aout_enable = dmc_ack || spr_state[1];
assign read = !odd_cycle;
assign data_to_ram = sprite_dma_lastval;
assign aout = dmc_ack ? dmc_dma_addr : !odd_cycle ? sprite_dma_addr : 16'h2004;
endmodule
module NES(
input clk,
input reset_nes,
input [1:0] sys_type,
output [1:0] nes_div,
input [31:0] mapper_flags,
output [15:0] sample, // sample generated from APU
output [5:0] color, // pixel generated from PPU
output joypad_strobe, // Set to 1 to strobe joypads. Then set to zero to keep the value.
output [1:0] joypad_clock, // Set to 1 for each joypad to clock it.
input [3:0] joypad_data, // Data for each joypad + 1 powerpad.
input mic, // Microphone RNG
input fds_busy, // FDS Disk Swap Busy
input fds_eject, // FDS Disk Swap Pause
output [1:0] diskside_req,
input [1:0] diskside,
input [4:0] audio_channels, // Enabled audio channels
input ex_sprites,
input [1:0] mask,
// Access signals for the SDRAM.
output [21:0] cpumem_addr,
output cpumem_read,
output cpumem_write,
output [7:0] cpumem_dout,
input [7:0] cpumem_din,
output [21:0] ppumem_addr,
output ppumem_read,
output ppumem_write,
output [7:0] ppumem_dout,
input [7:0] ppumem_din,
// Override for BRAM
output [17:0] bram_addr, // address to access
input [7:0] bram_din, // Data from BRAM
output [7:0] bram_dout,
output bram_write, // is a write operation
output bram_override,
output [8:0] cycle,
output [8:0] scanline,
input int_audio,
input ext_audio,
output apu_ce,
input gg,
input [128:0] gg_code,
output gg_avail,
input gg_reset,
output [2:0] emphasis,
output save_written
);
/**********************************************************/
/************* Clocks ***************/
/**********************************************************/
// Master clock speed: NTSC/Dendy: 21.477272, PAL: 21.2813696
// Cyc 123456789ABC123456789ABC123456789ABC123456789ABC
// CPU ----M------C----M------C----M------C----M------C
// PPU ---P---P---P---P---P---P---P---P---P---P---P---P
// M: M2 Tick, C: CPU Tick, P: PPU Tick -: Idle Cycle
//
// On Mister, we must pre-fetch data from memory 4 cycles before it is needed.
// Memory requests are aligned to the PPU clock and there are two types: CPU pre-fetch
// and PPU pre-fetch. The CPU pre-fetch needs to be completed before the end of the CPU cycle, and
// the PPU pre-fetch needs to be completed before each PPU CE is ticked.
// The PPU_CE that acknowledges reads and writes always occurs after the M2 rising edge, and cart/mapper
// CE's are always triggered on the rising edge of M2, which means that PPU will always see
// any changes made by the cart mappers. Because the mapper on MiSTer is capable of changing the data that
// is given to the CPU (banking, etc) the best time to run it is the first PPU cycle where the data from the
// CPU is visible on the bus.
//
// The obvious issue is that the CPU and PPU pre-fetches will collide. Fortunately, because Nintendo
// wanted to save pins, the ppu has to take two PPU ticks for every read, meaning there will always be
// a minimum of one free PPU cycle in which to fit the CPU read. This does however create the issue that
// we always need perfect alignment.
//
// Therefore, we can dervive the following order of operations:
// - CPU pre-fetch should happen during first free PPU tick in a CPU cycle.
// - Cart CE should happen on the second PPU tick in a CPU cycle always
// - PPU read/write should happen on the last PPU tick in a CPU cycle (usually third)
assign nes_div = div_sys;
assign apu_ce = cpu_ce;
wire [7:0] from_data_bus;
wire [7:0] cpu_dout;
// odd or even apu cycle, AKA div_apu or apu_/clk2. This is actually not 50% duty cycle. It is high for 18
// master cycles and low for 6 master cycles. It is considered active when low or "even".
reg odd_or_even = 0; // 1 == odd, 0 == even
// Clock Dividers
wire [4:0] div_cpu_n = 5'd12;
wire [2:0] div_ppu_n = 3'd4;
// Counters
reg [4:0] div_cpu = 5'd1;
reg [2:0] div_ppu = 3'd1;
reg [1:0] div_sys = 2'd0;
// CE's
wire cpu_ce = (div_cpu == div_cpu_n);
wire ppu_ce = (div_ppu == div_ppu_n);
wire cart_ce = (cart_pre & ppu_ce); // First PPU cycle where cpu data is visible.
// Signals
wire cart_pre = (ppu_tick == (cpu_tick_count[2] ? 1 : 0));
wire ppu_read = (ppu_tick == (cpu_tick_count[2] ? 2 : 1));
wire ppu_write = (ppu_tick == (cpu_tick_count[2] ? 1 : 0));
// The infamous NES jitter is important for accuracy, but wreks havok on modern devices and scalers,
// so what I do here is pause the whole system for one PPU clock and insert a "fake" ppu clock to
// replace the missing pixel. Thus the system runs accurately (ableit a few nanoseconds per frame slower)
// but all video devices stay happy.
wire skip_pixel;
reg freeze_clocks = 0;
reg [4:0] faux_pixel_cnt;
wire use_fake_h = freeze_clocks && faux_pixel_cnt < 6;
reg [1:0] ppu_tick = 0;
reg [1:0] last_sys_type;
reg [2:0] cpu_tick_count;
wire skip_ppu_cycle = (cpu_tick_count == 4) && (ppu_tick == 0);
reg hold_reset = 0;
wire reset = reset_nes | hold_reset;
always @(posedge clk) begin
if (reset_nes) hold_reset <= 1;
if (cpu_ce) hold_reset <= 0;
if (~freeze_clocks | ~(div_ppu == (div_ppu_n - 1'b1))) begin
if (~skip_ppu_cycle)
div_cpu <= cpu_ce || (ppu_ce && div_cpu > div_cpu_n) ? 5'd1 : div_cpu + 5'd1;
div_ppu <= ppu_ce ? 3'd1 : div_ppu + 3'd1;
// reset the ticker on the first ppu tick at or after a cpu tick.
if (cpu_ce)
ppu_tick <= 0;
else if (ppu_ce)
ppu_tick <= ppu_tick + 1'b1;
end
// Add one extra PPU tick every 5 cpu cycles for PAL.
if (cpu_ce && sys_type[0])
cpu_tick_count <= cpu_tick_count[2] ? 3'd0 : cpu_tick_count + 1'b1;
// SDRAM Clock
div_sys <= div_sys + 1'b1;
// De-Jitter shenanigans
if (faux_pixel_cnt == 3)
freeze_clocks <= 1'b0;
if (|faux_pixel_cnt)
faux_pixel_cnt <= faux_pixel_cnt - 1'b1;
if (skip_pixel && (faux_pixel_cnt == 0)) begin
freeze_clocks <= 1'b1;
faux_pixel_cnt <= {div_ppu_n - 1'b1, 1'b0} + 1'b1;
end
if (reset)
odd_or_even <= 1'b0;
else if (cpu_ce)
odd_or_even <= ~odd_or_even;
// Realign if the system type changes.
last_sys_type <= sys_type;
if (last_sys_type != sys_type) begin
div_cpu <= 5'd1;
div_ppu <= 3'd1;
div_sys <= 0;
cpu_tick_count <= 0;
end
end
/**********************************************************/
/************* CPU ***************/
/**********************************************************/
wire [15:0] cpu_addr;
wire cpu_rnw;
wire pause_cpu;
wire nmi;
wire mapper_irq;
wire apu_irq;
// IRQ only changes once per CPU ce and with our current
// limited CPU model, NMI is only latched on the falling edge
// of M2, which corresponds with CPU ce, so no latches needed.
T65 cpu(
.mode (0),
.BCD_en (0),
.res_n (~reset),
.clk (clk),
.enable (cpu_ce),
.rdy (~pause_cpu),
.IRQ_n (~(apu_irq | mapper_irq)),
.NMI_n (~nmi),
.R_W_n (cpu_rnw),
.A (cpu_addr),
.DI (cpu_rnw ? from_data_bus : cpu_dout),
.DO (cpu_dout)
);
wire [15:0] dma_aout;
wire dma_aout_enable;
wire dma_read;
wire [7:0] dma_data_to_ram;
wire apu_dma_request, apu_dma_ack;
wire [15:0] apu_dma_addr;
// Determine the values on the bus outgoing from the CPU chip (after DMA / APU)
wire [15:0] addr = dma_aout_enable ? dma_aout : cpu_addr;
wire [7:0] dbus = dma_aout_enable ? dma_data_to_ram : cpu_dout;
wire mr_int = dma_aout_enable ? dma_read : cpu_rnw;
wire mw_int = dma_aout_enable ? !dma_read : !cpu_rnw;
DmaController dma(
.clk (clk),
.ce (cpu_ce),
.reset (reset),
.odd_cycle (odd_or_even), // Even or odd cycle
.sprite_trigger ((addr == 'h4014 && mw_int)), // Sprite trigger
.dmc_trigger (apu_dma_request), // DMC Trigger
.cpu_read (cpu_rnw), // CPU in a read cycle?
.data_from_cpu (cpu_dout), // Data from cpu
.data_from_ram (from_data_bus), // Data from RAM etc.
.dmc_dma_addr (apu_dma_addr), // DMC addr
.aout (dma_aout),
.aout_enable (dma_aout_enable),
.read (dma_read),
.data_to_ram (dma_data_to_ram),
.dmc_ack (apu_dma_ack),
.pause_cpu (pause_cpu)
);
/**********************************************************/
/************* APU ***************/
/**********************************************************/
wire apu_cs = addr >= 'h4000 && addr < 'h4018;
wire [7:0] apu_dout;
wire [15:0] sample_apu;
APU apu(
.MMC5 (1'b0),
.clk (clk),
.PAL (sys_type[0]),
.ce (cpu_ce),
.reset (reset),
.ADDR (addr[4:0]),
.DIN (dbus),
.DOUT (apu_dout),
.MW (mw_int && apu_cs),
.MR (mr_int && apu_cs),
.audio_channels (audio_channels),
.Sample (sample_apu),
.DmaReq (apu_dma_request),
.DmaAck (apu_dma_ack),
.DmaAddr (apu_dma_addr),
.DmaData (from_data_bus),
.odd_or_even (odd_or_even),
.IRQ (apu_irq)
);
assign sample = sample_a;
reg [15:0] sample_a;
always @* begin
case (audio_en)
0: sample_a = 16'd0;
1: sample_a = sample_ext;
2: sample_a = sample_inverted;
3: sample_a = sample_ext;
endcase
end
wire [15:0] sample_inverted = 16'hFFFF - sample_apu;
wire [1:0] audio_en = {int_audio, ext_audio};
wire [15:0] audio_mappers = (audio_en == 2'd1) ? 16'd0 : sample_inverted;
// Joypads are mapped into the APU's range.
wire joypad1_cs = (addr == 'h4016);
wire joypad2_cs = (addr == 'h4017);
reg joy_strobe;
always @(posedge clk) begin
if (joypad1_cs && mw_int)
joy_strobe <= cpu_dout[0];
end
assign joypad_strobe = joy_strobe;
assign joypad_clock = {joypad2_cs && mr_int, joypad1_cs && mr_int};
/**********************************************************/
/************* PPU ***************/
/**********************************************************/
// The real PPU has a CS pin which is a combination of the output of the 74319 (ppu address selector)
// and the M2 pin from the CPU. This will only be low for 1 and 7/8th PPU cycles, or
// 7 and 1/2 master cycles on NTSC. Therefore, the PPU should read or write once per cpu cycle, and
// with our alignment, this should occur at PPU cycle 2 (the *third* cycle).
wire mr_ppu = mr_int && ppu_read; // Read *from* the PPU.
wire mw_ppu = mw_int && ppu_write; // Write *to* the PPU.
wire ppu_cs = addr >= 'h2000 && addr < 'h4000;
wire [7:0] ppu_dout; // Data from PPU to CPU
wire chr_read, chr_write, chr_read_ex; // If PPU reads/writes from VRAM
wire [13:0] chr_addr, chr_addr_ex; // Address PPU accesses in VRAM
wire [7:0] chr_from_ppu; // Data from PPU to VRAM
wire [7:0] chr_to_ppu;
wire [19:0] mapper_ppu_flags; // PPU flags for mapper cheating
wire [8:0] ppu_cycle;
assign cycle = use_fake_h ? 9'd340 : ppu_cycle;
PPU ppu(
.clk (clk),
.ce (ppu_ce),
.reset (reset),
.sys_type (sys_type),
.color (color),
.din (dbus),
.dout (ppu_dout),
.ain (addr[2:0]),
.read (ppu_cs && mr_ppu),
.write (ppu_cs && mw_ppu),
.nmi (nmi),
.vram_r (chr_read),
.vram_r_ex (chr_read_ex),
.vram_w (chr_write),
.vram_a (chr_addr),
.vram_a_ex (chr_addr_ex),
.vram_din (chr_to_ppu),
.vram_dout (chr_from_ppu),
.scanline (scanline),
.cycle (ppu_cycle),
.mapper_ppu_flags (mapper_ppu_flags),
.emphasis (emphasis),
.short_frame (skip_pixel),
.extra_sprites (ex_sprites),
.mask (mask)
);
/**********************************************************/
/************* Cart ***************/
/**********************************************************/
wire [15:0] prg_addr = addr;
wire [7:0] prg_din = dbus & (prg_conflict ? cpumem_din : 8'hFF);
wire prg_read = mr_int && cart_pre && !apu_cs && !ppu_cs;
wire prg_write = mw_int && cart_pre && !apu_cs && !ppu_cs;
wire prg_allow, prg_bus_write, prg_conflict, vram_a10, vram_ce, chr_allow;
wire [21:0] prg_linaddr, chr_linaddr;
wire [7:0] prg_dout_mapper, chr_from_ppu_mapper;
wire has_chr_from_ppu_mapper;
wire [15:0] sample_ext;
assign save_written = (mapper_flags[7:0] == 8'h14) ? (prg_linaddr[21:18] == 4'b1111 && prg_write) : (prg_addr[15:13] == 3'b011 && prg_write) | bram_write;
cart_top multi_mapper (
// FPGA specific
.clk (clk),
.reset (reset),
.flags (mapper_flags), // iNES header data (use 0 while loading)
// Cart pins (slightly abstracted)
.ce (cart_ce & ~reset), // M2 (held in high impedance during reset)
.prg_ain (prg_addr), // CPU Address in (a15 abstracted from ROMSEL)
.prg_read (prg_read), // CPU RnW split
.prg_write (prg_write), // CPU RnW split
.prg_din (prg_din), // CPU Data bus in (split from bid)
.prg_dout (prg_dout_mapper), // CPU Data bus out (split from bid)
.chr_ex (chr_read_ex), // Flag indicating to use extra sprite addr
.chr_ain_orig (chr_addr), // PPU address in
.chr_ain_ex (chr_addr_ex), // PPU address for extra sprites
.chr_read (chr_read), // PPU read (inverted, active high)
.chr_write (chr_write), // PPU write (inverted, active high)
.chr_din (chr_from_ppu), // PPU data bus in (split from bid)
.chr_dout (chr_from_ppu_mapper), // PPU data bus in (split from bid)
.vram_a10 (vram_a10), // CIRAM a10 line
.vram_ce (vram_ce), // CIRAM chip enable
.irq (mapper_irq), // IRQ (inverted, active high)
.audio_in (audio_mappers), // Amplified and inverted APU audio
.audio (sample_ext), // Mixed audio output from cart
// SDRAM Communication
.prg_aout (prg_linaddr), // SDRAM adjusted PRG RAM address
.prg_allow (prg_allow), // Simulates internal CE/Locking
.chr_aout (chr_linaddr), // SDRAM adjusted CHR RAM address
.chr_allow (chr_allow), // Simulates internal CE/Locking
// External hardware interface (EEPROM)
.mapper_addr (bram_addr),
.mapper_data_in (bram_din),
.mapper_data_out (bram_dout),
.mapper_prg_write (bram_write),
.mapper_ovr (bram_override),
// Cheats
.prg_from_ram (from_data_bus), // Hacky cpu din <= get rid of this!
.ppuflags (mapper_ppu_flags), // Cheat for MMC5
.ppu_ce (ppu_ce), // PPU Clock (cheat for MMC5/2/4)
// Behavior helper flags
.has_chr_dout (has_chr_from_ppu_mapper), // Output specific data for CHR rather than from SDRAM
.prg_bus_write (prg_bus_write), // PRG data driven to bus
.prg_conflict (prg_conflict), // Simulate bus conflicts
// User input/FDS controls
.fds_eject (fds_eject), // Used to trigger FDS disk changes
.fds_busy (fds_busy), // Used to trigger FDS disk changes
.diskside_auto (diskside_req),
.diskside (diskside)
);
wire genie_ovr;
wire [7:0] genie_data;
/*
CODES codes (
.clk (clk),
.reset (gg_reset),
.enable (~gg),
.addr_in (addr),
.data_in (prg_allow ? cpumem_din : prg_dout_mapper),
.code (gg_code),
.available (gg_avail),
.genie_ovr (genie_ovr),
.genie_data (genie_data)
);
*/
/**********************************************************/
/************* Bus Arbitration ***************/
/**********************************************************/
assign chr_to_ppu = has_chr_from_ppu_mapper ? chr_from_ppu_mapper : ppumem_din;
assign cpumem_addr = prg_linaddr;
assign cpumem_read = (prg_read & prg_allow) | (prg_write && prg_conflict);
assign cpumem_write = prg_write && prg_allow;
assign cpumem_dout = prg_din;
assign ppumem_addr = chr_linaddr;
assign ppumem_read = chr_read;
assign ppumem_write = chr_write && (chr_allow || vram_ce);
assign ppumem_dout = chr_from_ppu;
reg [7:0] open_bus_data;
always @(posedge clk) begin
open_bus_data <= from_data_bus;
end
assign from_data_bus = genie_ovr ? genie_data : raw_data_bus;
reg [7:0] raw_data_bus;
always @* begin
if (reset)
raw_data_bus = 0;
else if (apu_cs) begin
if (joypad1_cs)
raw_data_bus = {open_bus_data[7:5], 2'b0, mic, 1'b0, joypad_data[0]};
else if (joypad2_cs)
raw_data_bus = {open_bus_data[7:5], joypad_data[3:2], 2'b00, joypad_data[1]};
else
raw_data_bus = (addr == 16'h4015) ? apu_dout : open_bus_data;
end else if (ppu_cs) begin
raw_data_bus = ppu_dout;
end else if (prg_allow) begin
raw_data_bus = cpumem_din;
end else if (prg_bus_write) begin
raw_data_bus = prg_dout_mapper;
end else begin
raw_data_bus = open_bus_data;
end
end
module DmaController(input clk, input ce, input reset,
input odd_cycle, // Current cycle even or odd?
input sprite_trigger, // Sprite DMA trigger?
input dmc_trigger, // DMC DMA trigger?
input cpu_read, // CPU is in a read cycle?
input [7:0] data_from_cpu, // Data written by CPU?
input [7:0] data_from_ram, // Data read from RAM?
input [15:0] dmc_dma_addr, // DMC DMA Address
output [15:0] aout, // Address to access
output aout_enable, // DMA controller wants bus control
output read, // 1 = read, 0 = write
output [7:0] data_to_ram, // Value to write to RAM
output dmc_ack, // ACK the DMC DMA
output pause_cpu); // CPU is paused
reg dmc_state;
reg [1:0] spr_state;
reg [7:0] sprite_dma_lastval;
reg [15:0] sprite_dma_addr; // sprite dma source addr
wire [8:0] new_sprite_dma_addr = sprite_dma_addr[7:0] + 8'h01;
always @(posedge clk) if (reset) begin
dmc_state <= 0;
spr_state <= 0;
sprite_dma_lastval <= 0;
sprite_dma_addr <= 0;
end else if (ce) begin
if (dmc_state == 0 && dmc_trigger && cpu_read && !odd_cycle) dmc_state <= 1;
if (dmc_state == 1 && !odd_cycle) dmc_state <= 0;
if (sprite_trigger) begin sprite_dma_addr <= {data_from_cpu, 8'h00}; spr_state <= 1; end
if (spr_state == 1 && cpu_read && odd_cycle) spr_state <= 3;
if (spr_state[1] && !odd_cycle && dmc_state == 1) spr_state <= 1;
if (spr_state[1] && odd_cycle) sprite_dma_addr[7:0] <= new_sprite_dma_addr[7:0];
if (spr_state[1] && odd_cycle && new_sprite_dma_addr[8]) spr_state <= 0;
if (spr_state[1]) sprite_dma_lastval <= data_from_ram;
end
assign pause_cpu = (spr_state[0] || dmc_trigger) && cpu_read;
assign dmc_ack = (dmc_state == 1 && !odd_cycle);
assign aout_enable = dmc_ack || spr_state[1];
assign read = !odd_cycle;
assign data_to_ram = sprite_dma_lastval;
assign aout = dmc_ack ? dmc_dma_addr : !odd_cycle ? sprite_dma_addr : 16'h2004;
endmodule
// Multiplexes accesses by the PPU and the PRG into a single memory, used for both
// ROM and internal memory.
// PPU has priority, its read/write will be honored asap, while the CPU's reads
// will happen only every second cycle when the PPU is idle.
// Data read by PPU will be available on the next clock cycle.
// Data read by CPU will be available within at most 2 clock cycles.
module MemoryMultiplex(input clk, input ce, input reset,
input [21:0] prg_addr, input prg_read, input prg_write, input [7:0] prg_din,
input [21:0] chr_addr, input chr_read, input chr_write, input [7:0] chr_din,
// Access signals for the SRAM.
output [21:0] memory_addr, // address to access
output memory_read_cpu, // read into CPU latch
output memory_read_ppu, // read into PPU latch
output memory_write, // is a write operation
output [7:0] memory_dout);
reg saved_prg_read, saved_prg_write;
assign memory_addr = (chr_read || chr_write) ? chr_addr : prg_addr;
assign memory_write = (chr_read || chr_write) ? chr_write : saved_prg_write;
assign memory_read_ppu = chr_read;
assign memory_read_cpu = !(chr_read || chr_write) && (prg_read || saved_prg_read);
assign memory_dout = chr_write ? chr_din : prg_din;
always @(posedge clk) if (reset) begin
saved_prg_read <= 0;
saved_prg_write <= 0;
end else if (ce) begin
if (chr_read || chr_write) begin
saved_prg_read <= prg_read || saved_prg_read;
saved_prg_write <= prg_write || saved_prg_write;
end else begin
saved_prg_read <= 0;
saved_prg_write <= prg_write;
end
end
endmodule
module NES(input clk, input reset, input ce,
input [31:0] mapper_flags,
output [15:0] sample, // sample generated from APU
output [5:0] color, // pixel generated from PPU
output joypad_strobe,// Set to 1 to strobe joypads. Then set to zero to keep the value.
output [1:0] joypad_clock, // Set to 1 for each joypad to clock it.
input [3:0] joypad_data, // Data for each joypad + 1 powerpad.
input fds_swap,
input [4:0] audio_channels, // Enabled audio channels
// Access signals for the SRAM.
output [21:0] memory_addr, // address to access
output memory_read_cpu, // read into CPU latch
input [7:0] memory_din_cpu, // next cycle, contents of latch A (CPU's data)
output memory_read_ppu, // read into CPU latch
input [7:0] memory_din_ppu, // next cycle, contents of latch B (PPU's data)
output memory_write, // is a write operation
output [7:0] memory_dout,
output [8:0] cycle,
output [8:0] scanline,
input int_audio,
input ext_audio
);
reg [7:0] from_data_bus;
wire [7:0] cpu_dout;
wire odd_or_even; // Is this an odd or even clock cycle?
// The CPU runs at one third the speed of the PPU.
// CPU is clocked at cycle #2. PPU is clocked at cycle #0, #1, #2.
// CPU does its memory I/O on cycle #0. It will be available in time for cycle #2.
reg [1:0] cpu_cycle_counter;
always @(posedge clk) begin
if (reset)
cpu_cycle_counter <= 0;
else if (ce)
cpu_cycle_counter <= (cpu_cycle_counter == 2) ? 2'd0 : cpu_cycle_counter + 1'd1;
end
// Sample the NMI flag on cycle #0, otherwise if NMI happens on cycle #0 or #1,
// the CPU will use it even though it shouldn't be used until the next CPU cycle.
wire nmi;
reg nmi_active;
always @(posedge clk) begin
if (reset)
nmi_active <= 0;
else if (ce && cpu_cycle_counter == 0)
nmi_active <= nmi;
end
wire apu_ce = ce && (cpu_cycle_counter == 2);
// -- CPU
wire [15:0] cpu_addr;
wire cpu_rnw;
wire pause_cpu;
reg apu_irq_delayed;
reg mapper_irq_delayed;
T65 cpu
(
.mode(0),
.BCD_en(0),
.res_n(~reset),
.clk(clk),
.enable(apu_ce && !pause_cpu),
.IRQ_n(~(apu_irq_delayed | mapper_irq_delayed)),
.NMI_n(~nmi_active),
.R_W_n(cpu_rnw),
.A(cpu_addr),
.DI(cpu_rnw ? from_data_bus : cpu_dout),
.DO(cpu_dout)
);
// -- DMA
wire [15:0] dma_aout;
wire dma_aout_enable;
wire dma_read;
wire [7:0] dma_data_to_ram;
wire apu_dma_request, apu_dma_ack;
wire [15:0] apu_dma_addr;
// Determine the values on the bus outgoing from the CPU chip (after DMA / APU)
wire [15:0] addr = dma_aout_enable ? dma_aout : cpu_addr;
wire [7:0] dbus = dma_aout_enable ? dma_data_to_ram : cpu_dout;
wire mr_int = dma_aout_enable ? dma_read : cpu_rnw;
wire mw_int = dma_aout_enable ? !dma_read : !cpu_rnw;
DmaController dma(clk, apu_ce, reset,
odd_or_even, // Even or odd cycle
(addr == 'h4014 && mw_int), // Sprite trigger
apu_dma_request, // DMC Trigger
cpu_rnw, // CPU in a read cycle?
cpu_dout, // Data from cpu
from_data_bus, // Data from RAM etc.
apu_dma_addr, // DMC addr
dma_aout,
dma_aout_enable,
dma_read,
dma_data_to_ram,
apu_dma_ack,
pause_cpu);
// -- Audio Processing Unit
wire apu_cs = addr >= 'h4000 && addr < 'h4018;
wire [7:0] apu_dout;
wire apu_irq;
wire [15:0] sample_apu;
APU apu(0, clk, apu_ce, reset,
addr[4:0], dbus, apu_dout,
mw_int && apu_cs, mr_int && apu_cs,
audio_channels,
sample_apu,
apu_dma_request,
apu_dma_ack,
apu_dma_addr,
from_data_bus,
odd_or_even,
apu_irq);
// Joypads are mapped into the APU's range.
wire joypad1_cs = (addr == 'h4016);
wire joypad2_cs = (addr == 'h4017);
assign joypad_strobe = (joypad1_cs && mw_int && cpu_dout[0]);
assign joypad_clock = {joypad2_cs && mr_int, joypad1_cs && mr_int};
// -- PPU
// PPU _reads_ need to happen on the same cycle the cpu runs on, to guarantee we
// see proper values of register $2002.
wire mr_ppu = mr_int && (cpu_cycle_counter == 2);
wire mw_ppu = mw_int && (cpu_cycle_counter == 0);
wire ppu_cs = addr >= 'h2000 && addr < 'h4000;
wire [7:0] ppu_dout; // Data from PPU to CPU
wire chr_read, chr_write; // If PPU reads/writes from VRAM
wire [13:0] chr_addr; // Address PPU accesses in VRAM
wire [7:0] chr_from_ppu; // Data from PPU to VRAM
wire [7:0] chr_to_ppu;
wire [19:0] mapper_ppu_flags; // PPU flags for mapper cheating
PPU ppu(clk, ce, reset, color, dbus, ppu_dout, addr[2:0],
ppu_cs && mr_ppu, ppu_cs && mw_ppu,
nmi,
chr_read, chr_write, chr_addr, chr_to_ppu, chr_from_ppu,
scanline, cycle, mapper_ppu_flags);
// -- Memory mapping logic
wire [15:0] prg_addr = addr;
wire [7:0] prg_din = dbus;
wire prg_read = mr_int && (cpu_cycle_counter == 0) && !apu_cs && !ppu_cs;
wire prg_write = mw_int && (cpu_cycle_counter == 0) && !apu_cs && !ppu_cs;
wire prg_allow, vram_a10, vram_ce, chr_allow;
wire [21:0] prg_linaddr, chr_linaddr;
wire [7:0] prg_dout_mapper, chr_from_ppu_mapper;
wire cart_ce = (cpu_cycle_counter == 0) && ce;
wire mapper_irq;
wire has_chr_from_ppu_mapper;
wire [15:0] sample_ext;
reg [16:0] sample_sum;
assign sample = sample_sum[16:1]; //loss of 1 bit of resolution. Add control for when no external audio to boost back up?
MultiMapper multi_mapper(clk, cart_ce, ce, reset, mapper_ppu_flags, mapper_flags,
prg_addr, prg_linaddr, prg_read, prg_write, prg_din, prg_dout_mapper, from_data_bus, prg_allow,
chr_read, chr_addr, chr_linaddr, chr_from_ppu_mapper, has_chr_from_ppu_mapper, chr_allow, vram_a10,
vram_ce, mapper_irq, sample_ext, fds_swap);
assign chr_to_ppu = has_chr_from_ppu_mapper ? chr_from_ppu_mapper : memory_din_ppu;
// Mapper IRQ seems to be delayed by one PPU clock.
// APU IRQ seems delayed by one APU clock.
always @(posedge clk) if (reset) begin
mapper_irq_delayed <= 0;
apu_irq_delayed <= 0;
end else begin
if (ce)
mapper_irq_delayed <= mapper_irq;
if (apu_ce)
apu_irq_delayed <= apu_irq;
if (ce | apu_ce) begin
case ({int_audio, ext_audio})
0: sample_sum <= 17'b0;
1: sample_sum <= {1'b0,sample_ext};
2: sample_sum <= {1'b0,sample_apu};
3: sample_sum <= {1'b0,sample_ext} + {1'b0,sample_apu};
endcase
end
end
// -- Multiplexes CPU and PPU accesses into one single RAM
MemoryMultiplex mem(clk, ce, reset, prg_linaddr, prg_read && prg_allow, prg_write && prg_allow, prg_din,
chr_linaddr, chr_read, chr_write && (chr_allow || vram_ce), chr_from_ppu,
memory_addr, memory_read_cpu, memory_read_ppu, memory_write, memory_dout);
always @* begin
if (reset)
from_data_bus = 0;
else if (apu_cs) begin
if (joypad1_cs)
from_data_bus = {7'b0100000, joypad_data[0]};
else if (joypad2_cs)
from_data_bus = {3'b010, joypad_data[3:2] ,2'b00, joypad_data[1]};
else
from_data_bus = apu_dout;
end else if (ppu_cs) begin
from_data_bus = ppu_dout;
end else if (prg_allow) begin
from_data_bus = memory_din_cpu;
end else begin
from_data_bus = prg_dout_mapper;
end
end
endmodule

1341
cores/nes/src/ppu.sv Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,717 +0,0 @@
// Copyright (c) 2012-2013 Ludvig Strigeus
// This program is GPL Licensed. See COPYING for the full license.
// altera message_off 10935
// Module handles updating the loopy scroll register
module LoopyGen (
input clk, input ce,
input is_rendering,
input [2:0] ain, // input address from CPU
input [7:0] din, // data input
input read, // read
input write, // write
input is_pre_render, // Is this the pre-render scanline
input [8:0] cycle,
output [14:0] loopy,
output [2:0] fine_x_scroll); // Current loopy value
// Controls how much to increment on each write
reg ppu_incr; // 0 = 1, 1 = 32
// Current VRAM address
reg [14:0] loopy_v;
// Temporary VRAM address
reg [14:0] loopy_t;
// Fine X scroll (3 bits)
reg [2:0] loopy_x;
// Latch
reg ppu_address_latch;
initial begin
ppu_incr = 0;
loopy_v = 0;
loopy_t = 0;
loopy_x = 0;
ppu_address_latch = 0;
end
// Handle updating loopy_t and loopy_v
always @(posedge clk) if (ce) begin
if (is_rendering) begin
// Increment course X scroll right after attribute table byte was fetched.
if (cycle[2:0] == 3 && (cycle < 256 || cycle >= 320 && cycle < 336)) begin
loopy_v[4:0] <= loopy_v[4:0] + 1'd1;
loopy_v[10] <= loopy_v[10] ^ (loopy_v[4:0] == 31);
end
// Vertical Increment
if (cycle == 251) begin
loopy_v[14:12] <= loopy_v[14:12] + 1'd1;
if (loopy_v[14:12] == 7) begin
if (loopy_v[9:5] == 29) begin
loopy_v[9:5] <= 0;
loopy_v[11] <= !loopy_v[11];
end else begin
loopy_v[9:5] <= loopy_v[9:5] + 1'd1;
end
end
end
// Horizontal Reset at cycle 257
if (cycle == 256)
{loopy_v[10], loopy_v[4:0]} <= {loopy_t[10], loopy_t[4:0]};
// On cycle 256 of each scanline, copy horizontal bits from loopy_t into loopy_v
// On cycle 304 of the pre-render scanline, copy loopy_t into loopy_v
if (cycle == 304 && is_pre_render) begin
loopy_v <= loopy_t;
end
end
if (write && ain == 0) begin
loopy_t[10] <= din[0];
loopy_t[11] <= din[1];
ppu_incr <= din[2];
end else if (write && ain == 5) begin
if (!ppu_address_latch) begin
loopy_t[4:0] <= din[7:3];
loopy_x <= din[2:0];
end else begin
loopy_t[9:5] <= din[7:3];
loopy_t[14:12] <= din[2:0];
end
ppu_address_latch <= !ppu_address_latch;
end else if (write && ain == 6) begin
if (!ppu_address_latch) begin
loopy_t[13:8] <= din[5:0];
loopy_t[14] <= 0;
end else begin
loopy_t[7:0] <= din;
loopy_v <= {loopy_t[14:8], din};
end
ppu_address_latch <= !ppu_address_latch;
end else if (read && ain == 2) begin
ppu_address_latch <= 0; //Reset PPU address latch
end else if ((read || write) && ain == 7 && !is_rendering) begin
// Increment address every time we accessed a reg
loopy_v <= loopy_v + (ppu_incr ? 15'd32 : 15'd1);
end
end
assign loopy = loopy_v;
assign fine_x_scroll = loopy_x;
endmodule
// Generates the current scanline / cycle counters
module ClockGen(input clk, input ce, input reset,
input is_rendering,
output reg [8:0] scanline,
output reg [8:0] cycle,
output reg is_in_vblank,
output end_of_line,
output at_last_cycle_group,
output exiting_vblank,
output entering_vblank,
output reg is_pre_render);
reg second_frame;
// Scanline 0..239 = picture scan lines
// Scanline 240 = dummy scan line
// Scanline 241..260 = VBLANK
// Scanline -1 = Pre render scanline (Fetches objects for next line)
assign at_last_cycle_group = (cycle[8:3] == 42);
// Every second pre-render frame is only 340 cycles instead of 341.
assign end_of_line = at_last_cycle_group && cycle[3:0] == (is_pre_render && second_frame && is_rendering ? 3 : 4);
// Set the clock right before vblank begins
assign entering_vblank = end_of_line && scanline == 240;
// Set the clock right before vblank ends
assign exiting_vblank = end_of_line && scanline == 260;
// New value for is_in_vblank flag
wire new_is_in_vblank = entering_vblank ? 1'b1 : exiting_vblank ? 1'b0 : is_in_vblank;
// Set if the current line is line 0..239
always @(posedge clk) if (reset) begin
cycle <= 0;
is_in_vblank <= 1;
end else if (ce) begin
cycle <= end_of_line ? 1'd0 : cycle + 1'd1;
is_in_vblank <= new_is_in_vblank;
end
// always @(posedge clk) if (ce) begin
// $write("%x %x %x %x %x\n", new_is_in_vblank, entering_vblank, exiting_vblank, is_in_vblank, entering_vblank ? 1'b1 : exiting_vblank ? 1'b0 : is_in_vblank);
// end
always @(posedge clk) if (reset) begin
scanline <= 0;
is_pre_render <= 0;
second_frame <= 0;
end else if (ce && end_of_line) begin
// Once the scanline counter reaches end of 260, it gets reset to -1.
scanline <= exiting_vblank ? 9'b111111111 : scanline + 1'd1;
// The pre render flag is set while we're on scanline -1.
is_pre_render <= exiting_vblank;
//if (exiting_vblank) second_frame <= !second_frame;
end
endmodule // ClockGen
// 8 of these exist, they are used to output sprites.
module Sprite(input clk, input ce,
input enable,
input [3:0] load,
input [26:0] load_in,
output [26:0] load_out,
output [4:0] bits); // Low 4 bits = pixel, high bit = prio
reg [1:0] upper_color; // Upper 2 bits of color
reg [7:0] x_coord; // X coordinate where we want things
reg [7:0] pix1, pix2; // Shift registers, output when x_coord == 0
reg aprio; // Current prio
wire active = (x_coord == 0);
always @(posedge clk) if (ce) begin
if (enable) begin
if (!active) begin
// Decrease until x_coord is zero.
x_coord <= x_coord - 8'h01;
end else begin
pix1 <= pix1 >> 1;
pix2 <= pix2 >> 1;
end
end
if (load[3]) pix1 <= load_in[26:19];
if (load[2]) pix2 <= load_in[18:11];
if (load[1]) x_coord <= load_in[10:3];
if (load[0]) {upper_color, aprio} <= load_in[2:0];
end
assign bits = {aprio, upper_color, active && pix2[0], active && pix1[0]};
assign load_out = {pix1, pix2, x_coord, upper_color, aprio};
endmodule // SpriteGen
// This contains all 8 sprites. Will return the pixel value of the highest prioritized sprite.
// When load is set, and clocked, load_in is loaded into sprite 7 and all others are shifted down.
// Sprite 0 has highest prio.
// 226 LUTs, 68 Slices
module SpriteSet(input clk, input ce, // Input clock
input enable, // Enable pixel generation
input [3:0] load, // Which parts of the state to load/shift.
input [26:0] load_in, // State to load with
output [4:0] bits, // Output bits
output is_sprite0); // Set to true if sprite #0 was output
wire [26:0] load_out7, load_out6, load_out5, load_out4, load_out3, load_out2, load_out1, load_out0;
wire [4:0] bits7, bits6, bits5, bits4, bits3, bits2, bits1, bits0;
Sprite sprite7(clk, ce, enable, load, load_in, load_out7, bits7);
Sprite sprite6(clk, ce, enable, load, load_out7, load_out6, bits6);
Sprite sprite5(clk, ce, enable, load, load_out6, load_out5, bits5);
Sprite sprite4(clk, ce, enable, load, load_out5, load_out4, bits4);
Sprite sprite3(clk, ce, enable, load, load_out4, load_out3, bits3);
Sprite sprite2(clk, ce, enable, load, load_out3, load_out2, bits2);
Sprite sprite1(clk, ce, enable, load, load_out2, load_out1, bits1);
Sprite sprite0(clk, ce, enable, load, load_out1, load_out0, bits0);
// Determine which sprite is visible on this pixel.
assign bits = bits0[1:0] != 0 ? bits0 :
bits1[1:0] != 0 ? bits1 :
bits2[1:0] != 0 ? bits2 :
bits3[1:0] != 0 ? bits3 :
bits4[1:0] != 0 ? bits4 :
bits5[1:0] != 0 ? bits5 :
bits6[1:0] != 0 ? bits6 :
bits7;
assign is_sprite0 = bits0[1:0] != 0;
endmodule // SpriteSet
module SpriteRAM(input clk, input ce,
input reset_line, // OAM evaluator needs to be reset before processing is started.
input sprites_enabled, // Set to 1 if evaluations are enabled
input exiting_vblank, // Set to 1 when exiting vblank so spr_overflow can be reset
input obj_size, // Set to 1 if objects are 16 pixels.
input [8:0] scanline, // Current scan line (compared against Y)
input [8:0] cycle, // Current cycle.
output reg [7:0] oam_bus, // Current value on the OAM bus, returned to NES through $2004.
input oam_ptr_load, // Load oam with specified value, when writing to NES $2003.
input oam_load, // Load oam_ptr with specified value, when writing to NES $2004.
input [7:0] data_in, // New value for oam or oam_ptr
output reg spr_overflow, // Set to true if we had more than 8 objects on a scan line. Reset when exiting vblank.
output reg sprite0); // True if sprite#0 is included on the scan line currently being painted.
reg [7:0] sprtemp[0:31]; // Sprite Temporary Memory. 32 bytes.
reg [7:0] oam_ptr; // Pointer into oam_ptr.
reg [2:0] p; // Upper 3 bits of pointer into temp, the lower bits are oam_ptr[1:0].
reg [1:0] state; // Current state machine state
reg [7:0] oam[256]; // Sprite OAM. 256 bytes.
reg [7:0] oam_data;
// Compute the current address we read/write in sprtemp.
reg [4:0] sprtemp_ptr;
// Check if the current Y coordinate is inside.
wire [8:0] spr_y_coord = scanline - {1'b0, oam_data};
wire spr_is_inside = (spr_y_coord[8:4] == 0) && (obj_size || spr_y_coord[3] == 0);
reg [7:0] new_oam_ptr; // [wire] New value for oam ptr
reg [1:0] oam_inc; // [wire] How much to increment oam ptr
reg sprite0_curr; // If sprite0 is included on the line being processed.
reg oam_wrapped; // [wire] if new_oam or new_p wrapped.
wire [7:0] sprtemp_data = sprtemp[sprtemp_ptr];
always @* begin
// Compute address to read/write in temp sprite ram
casez({cycle[8], cycle[2]})
2'b0_?: sprtemp_ptr = {p, oam_ptr[1:0]};
2'b1_0: sprtemp_ptr = {cycle[5:3], cycle[1:0]}; // 1-4. Read Y, Tile, Attribs
2'b1_1: sprtemp_ptr = {cycle[5:3], 2'b11}; // 5-8. Keep reading X.
endcase
end
always @* begin
/* verilator lint_off CASEOVERLAP */
// Compute value to return to cpu through $2004. And also the value that gets written to temp sprite ram.
casez({sprites_enabled, cycle[8], cycle[6], state, oam_ptr[1:0]})
7'b1_10_??_??: oam_bus = sprtemp_data; // At cycle 256-319 we output what's in sprite temp ram
7'b1_??_00_??: oam_bus = 8'b11111111; // On the first 64 cycles (while inside state 0), we output 0xFF.
7'b1_??_01_00: oam_bus = {4'b0000, spr_y_coord[3:0]}; // Y coord that will get written to temp ram.
7'b?_??_??_10: oam_bus = {oam_data[7:5], 3'b000, oam_data[1:0]}; // Bits 2-4 of attrib are always zero when reading oam.
default: oam_bus = oam_data; // Default to outputting from oam.
endcase
end
always @* begin
// Compute incremented oam counters
casez ({oam_load, state, oam_ptr[1:0]})
5'b1_??_??: oam_inc = {oam_ptr[1:0] == 3, 1'b1}; // Always increment by 1 when writing to oam.
5'b0_00_??: oam_inc = 2'b01; // State 0: On the the first 64 cycles we fill temp ram with 0xFF, increment low bits.
5'b0_01_00: oam_inc = {!spr_is_inside, spr_is_inside}; // State 1: Copy Y coordinate and increment oam by 1 if it's inside, otherwise 4.
5'b0_01_??: oam_inc = {oam_ptr[1:0] == 3, 1'b1}; // State 1: Copy remaining 3 bytes of the oam.
// State 3: We've had more than 8 sprites. Set overflow flag if we found a sprite that overflowed.
// NES BUG: It increments both low and high counters.
5'b0_11_??: oam_inc = 2'b11;
// While in the final state, keep incrementing the low bits only until they're zero.
5'b0_10_??: oam_inc = {1'b0, oam_ptr[1:0] != 0};
endcase
/* verilator lint_on CASEOVERLAP */
new_oam_ptr[1:0] = oam_ptr[1:0] + {1'b0, oam_inc[0]};
{oam_wrapped, new_oam_ptr[7:2]} = {1'b0, oam_ptr[7:2]} + {6'b0, oam_inc[1]};
end
wire [7:0] oam_ptr_tmp = oam_ptr_load ? data_in : new_oam_ptr;
always @(posedge clk) if (ce) begin
// Some bits of the OAM are hardwired to zero.
if (oam_load) begin
oam[oam_ptr] <= (oam_ptr & 3) == 2 ? data_in & 8'hE3: data_in;
oam_data <= (oam_ptr & 3) == 2 ? data_in & 8'hE3: data_in;
end
if((cycle[0] && sprites_enabled) || oam_load || oam_ptr_load) begin
oam_ptr <= oam_ptr_tmp;
oam_data <= oam[oam_ptr_tmp];
end
// Set overflow flag?
if (sprites_enabled && state == 2'b11 && spr_is_inside)
spr_overflow <= 1;
// Remember if sprite0 is included on the scanline, needed for hit test later.
sprite0_curr <= (state == 2'b01 && oam_ptr[7:2] == 0 && spr_is_inside || sprite0_curr);
// if (scanline == 0 && cycle[0] && (state == 2'b01 || state == 2'b00))
// $write("Drawing sprite %d/%d. bus=%d oam_ptr=%X->%X oam_data=%X p=%d (%d %d %d)\n", scanline, cycle, oam_bus, oam_ptr, new_oam_ptr, oam_data, p,
// cycle[0] && sprites_enabled, oam_load, oam_ptr_load);
// Always writing to temp ram while we're in state 0 or 1.
if (!state[1]) sprtemp[sprtemp_ptr] <= oam_bus;
// Update state machine on every second cycle.
if (cycle[0]) begin
// Increment p whenever oam_ptr carries in state 0 or 1.
if (!state[1] && oam_ptr[1:0] == 2'b11) p <= p + 1'd1;
// Set sprite0 if sprite1 was included on the scan line
casez({state, (p == 7) && (oam_ptr[1:0] == 2'b11), oam_wrapped})
4'b00_0_?: state <= 2'b00; // State #0: Keep filling
4'b00_1_?: state <= 2'b01; // State #0: Until we filled 64 items.
4'b01_?_1: state <= 2'b10; // State #1: Goto State 2 if processed all OAM
4'b01_1_0: state <= 2'b11; // State #1: Goto State 3 if we found 8 sprites
4'b01_0_0: state <= 2'b01; // State #1: Keep comparing Y coordinates.
4'b11_?_1: state <= 2'b10; // State #3: Goto State 2 if processed all OAM
4'b11_?_0: state <= 2'b11; // State #3: Keep comparing Y coordinates
4'b10_?_?: state <= 2'b10; // Stuck in state 2.
endcase
end
if (reset_line) begin
state <= 0;
p <= 0;
oam_ptr <= 0;
oam_data <= oam[0];
sprite0_curr <= 0;
sprite0 <= sprite0_curr;
end
if (exiting_vblank)
spr_overflow <= 0;
end
endmodule // SpriteRAM
// Generates addresses in VRAM where we'll fetch sprite graphics from,
// and populates load, load_in so the SpriteGen can be loaded.
// 10 LUT, 4 Slices
module SpriteAddressGen(input clk, input ce,
input enabled, // If unset, |load| will be all zeros.
input obj_size, // 0: Sprite Height 8, 1: Sprite Height 16.
input obj_patt, // Object pattern table selection
input [2:0] cycle, // Current load cycle. At #4, first bitmap byte is loaded. At #6, second bitmap byte is.
input [7:0] temp, // Input temp data from SpriteTemp. #0 = Y Coord, #1 = Tile, #2 = Attribs, #3 = X Coord
output [12:0] vram_addr,// Low bits of address in VRAM that we'd like to read.
input [7:0] vram_data, // Byte of VRAM in the specified address
output [3:0] load, // Which subset of load_in that is now valid, will be loaded into SpritesGen.
output [26:0] load_in); // Bits to load into SpritesGen.
reg [7:0] temp_tile; // Holds the tile that we will get
reg [3:0] temp_y; // Holds the Y coord (will be swapped based on FlipY).
reg flip_x, flip_y; // If incoming bitmap data needs to be flipped in the X or Y direction.
wire load_y = (cycle == 0);
wire load_tile = (cycle == 1);
wire load_attr = (cycle == 2) && enabled;
wire load_x = (cycle == 3) && enabled;
wire load_pix1 = (cycle == 5) && enabled;
wire load_pix2 = (cycle == 7) && enabled;
reg dummy_sprite; // Set if attrib indicates the sprite is invalid.
// Flip incoming vram data based on flipx. Zero out the sprite if it's invalid. The bits are already flipped once.
wire [7:0] vram_f = dummy_sprite ? 8'd0 :
!flip_x ? {vram_data[0], vram_data[1], vram_data[2], vram_data[3], vram_data[4], vram_data[5], vram_data[6], vram_data[7]} :
vram_data;
wire [3:0] y_f = temp_y ^ {flip_y, flip_y, flip_y, flip_y};
assign load = {load_pix1, load_pix2, load_x, load_attr};
assign load_in = {vram_f, vram_f, temp, temp[1:0], temp[5]};
// If $2000.5 = 0, the tile index data is used as usual, and $2000.3
// selects the pattern table to use. If $2000.5 = 1, the MSB of the range
// result value become the LSB of the indexed tile, and the LSB of the tile
// index value determines pattern table selection. The lower 3 bits of the
// range result value are always used as the fine vertical offset into the
// selected pattern.
assign vram_addr = {obj_size ? temp_tile[0] : obj_patt,
temp_tile[7:1], obj_size ? y_f[3] : temp_tile[0], cycle[1], y_f[2:0] };
always @(posedge clk) if (ce) begin
if (load_y) temp_y <= temp[3:0];
if (load_tile) temp_tile <= temp;
if (load_attr) {flip_y, flip_x, dummy_sprite} <= {temp[7:6], temp[4]};
end
// always @(posedge clk) begin
// if (load[3]) $write("Loading pix1: %x\n", load_in[26:19]);
// if (load[2]) $write("Loading pix2: %x\n", load_in[18:11]);
// if (load[1]) $write("Loading x: %x\n", load_in[10:3]);
//
// if (valid_sprite && enabled)
// $write("%d. Found %d. Flip:%d%d, Addr: %x, Vram: %x!\n", cycle, temp, flip_x, flip_y, vram_addr, vram_data);
// end
endmodule // SpriteAddressGen
module BgPainter(input clk, input ce,
input enable, // Shift registers activated
input [2:0] cycle,
input [2:0] fine_x_scroll,
input [14:0] loopy,
output [7:0] name_table, // VRAM name table to read next.
input [7:0] vram_data,
output [3:0] pixel);
reg [15:0] playfield_pipe_1; // Name table pixel pipeline #1
reg [15:0] playfield_pipe_2; // Name table pixel pipeline #2
reg [8:0] playfield_pipe_3; // Attribute table pixel pipe #1
reg [8:0] playfield_pipe_4; // Attribute table pixel pipe #2
reg [7:0] current_name_table; // Holds the current name table byte
reg [1:0] current_attribute_table; // Holds the 2 current attribute table bits
reg [7:0] bg0; // Pixel data for last loaded background
wire [7:0] bg1 = vram_data;
initial begin
playfield_pipe_1 = 0;
playfield_pipe_2 = 0;
playfield_pipe_3 = 0;
playfield_pipe_4 = 0;
current_name_table = 0;
current_attribute_table = 0;
bg0 = 0;
end
always @(posedge clk) if (ce) begin
case (cycle[2:0])
1: current_name_table <= vram_data;
3: current_attribute_table <= (!loopy[1] && !loopy[6]) ? vram_data[1:0] :
( loopy[1] && !loopy[6]) ? vram_data[3:2] :
(!loopy[1] && loopy[6]) ? vram_data[5:4] :
vram_data[7:6];
5: bg0 <= vram_data; // Pattern table bitmap #0
// 7: bg1 <= vram_data; // Pattern table bitmap #1
endcase
if (enable) begin
playfield_pipe_1[14:0] <= playfield_pipe_1[15:1];
playfield_pipe_2[14:0] <= playfield_pipe_2[15:1];
playfield_pipe_3[7:0] <= playfield_pipe_3[8:1];
playfield_pipe_4[7:0] <= playfield_pipe_4[8:1];
// Load the new values into the shift registers at the last pixel.
if (cycle[2:0] == 7) begin
playfield_pipe_1[15:8] <= {bg0[0], bg0[1], bg0[2], bg0[3], bg0[4], bg0[5], bg0[6], bg0[7]};
playfield_pipe_2[15:8] <= {bg1[0], bg1[1], bg1[2], bg1[3], bg1[4], bg1[5], bg1[6], bg1[7]};
playfield_pipe_3[8] <= current_attribute_table[0];
playfield_pipe_4[8] <= current_attribute_table[1];
end
end
end
assign name_table = current_name_table;
wire [3:0] i = {1'b0, fine_x_scroll};
assign pixel = {playfield_pipe_4[i], playfield_pipe_3[i],
playfield_pipe_2[i], playfield_pipe_1[i]};
endmodule // BgPainter
module PixelMuxer(input [3:0] bg, input [3:0] obj, input obj_prio, output [3:0] out, output is_obj);
wire bg_flag = bg[0] | bg[1];
wire obj_flag = obj[0] | obj[1];
assign is_obj = !(obj_prio && bg_flag) && obj_flag;
assign out = is_obj ? obj : bg;
endmodule
module PaletteRam(input clk, input ce, input [4:0] addr, input [5:0] din, output [5:0] dout, input write);
reg [5:0] palette [0:31];
initial begin
$readmemh("oam_palette.txt", palette);
end
// Force read from backdrop channel if reading from any addr 0.
wire [4:0] addr2 = (addr[1:0] == 0) ? 0 : addr;
assign dout = palette[addr2];
always @(posedge clk) if (ce && write) begin
// Allow writing only to x0
if (!(addr[3:2] != 0 && addr[1:0] == 0))
palette[addr2] <= din;
end
endmodule // PaletteRam
module PPU(input clk, input ce, input reset, // input clock 21.48 MHz / 4. 1 clock cycle = 1 pixel
output [5:0] color, // output color value, one pixel outputted every clock
input [7:0] din, // input data from bus
output [7:0] dout, // output data to CPU
input [2:0] ain, // input address from CPU
input read, // read
input write, // write
output nmi, // one while inside vblank
output vram_r, // read from vram active
output vram_w, // write to vram active
output [13:0] vram_a, // vram address
input [7:0] vram_din, // vram input
output [7:0] vram_dout,
output [8:0] scanline,
output [8:0] cycle,
output [19:0] mapper_ppu_flags);
// These are stored in control register 0
reg obj_patt; // Object pattern table
reg bg_patt; // Background pattern table
reg obj_size; // 1 if sprites are 16 pixels high, else 0.
reg vbl_enable; // Enable VBL flag
// These are stored in control register 1
reg grayscale; // Disable color burst
reg playfield_clip; // 0: Left side 8 pixels playfield clipping
reg object_clip; // 0: Left side 8 pixels object clipping
reg enable_playfield; // Enable playfield display
reg enable_objects; // Enable objects display
reg [2:0] color_intensity; // Color intensity
initial begin
obj_patt = 0;
bg_patt = 0;
obj_size = 0;
vbl_enable = 0;
grayscale = 0;
playfield_clip = 0;
object_clip = 0;
enable_playfield = 0;
enable_objects = 0;
color_intensity = 0;
end
reg nmi_occured; // True if NMI has occured but not cleared.
reg [7:0] vram_latch;
// Clock generator
wire is_in_vblank; // True if we're in VBLANK
//wire [8:0] scanline; // Current scanline
//wire [8:0] cycle; // Current cycle inside of the line
wire end_of_line; // At the last pixel of a line
wire at_last_cycle_group; // At the very last cycle group of the scan line.
wire exiting_vblank; // At the very last cycle of the vblank
wire entering_vblank; //
wire is_pre_render_line; // True while we're on the pre render scanline
wire is_rendering = (enable_playfield || enable_objects) && !is_in_vblank && scanline != 240;
ClockGen clock(clk, ce, reset, is_rendering, scanline, cycle, is_in_vblank, end_of_line, at_last_cycle_group,
exiting_vblank, entering_vblank, is_pre_render_line);
// The loopy module handles updating of the loopy address
wire [14:0] loopy;
wire [2:0] fine_x_scroll;
LoopyGen loopy0(clk, ce, is_rendering, ain, din, read, write, is_pre_render_line, cycle, loopy, fine_x_scroll);
// Set to true if the current ppu_addr pointer points into
// palette ram.
wire is_pal_address = (loopy[13:8] == 6'b111111);
// Paints background
wire [7:0] bg_name_table;
wire [3:0] bg_pixel_noblank;
BgPainter bg_painter(clk, ce, !at_last_cycle_group, cycle[2:0], fine_x_scroll, loopy, bg_name_table, vram_din, bg_pixel_noblank);
// Blank out BG in the leftmost 8 pixels?
wire show_bg_on_pixel = (playfield_clip || (cycle[7:3] != 0)) && enable_playfield;
wire [3:0] bg_pixel = {bg_pixel_noblank[3:2], show_bg_on_pixel ? bg_pixel_noblank[1:0] : 2'b00};
// This will set oam_ptr to 0 right before the scanline 240 and keep it there throughout vblank.
wire before_line = (enable_playfield || enable_objects) && (exiting_vblank || end_of_line && !is_in_vblank);
wire [7:0] oam_bus;
wire sprite_overflow;
wire obj0_on_line; // True if sprite#0 is included on the current line
SpriteRAM sprite_ram(clk, ce,
before_line, // Condition for resetting the sprite line state.
is_rendering, // Condition for enabling sprite ram logic. Check so we're not on
exiting_vblank,
obj_size,
scanline, cycle,
oam_bus,
write && (ain == 3), // Write to oam_ptr
write && (ain == 4), // Write to oam[oam_ptr]
din,
sprite_overflow,
obj0_on_line);
wire [4:0] obj_pixel_noblank;
wire [12:0] sprite_vram_addr;
wire is_obj0_pixel; // True if obj_pixel originates from sprite0.
wire [3:0] spriteset_load; // Which subset of the |load_in| to load into SpriteSet
wire [26:0] spriteset_load_in; // Bits to load into SpriteSet
// Between 256..319 (64 cycles), fetches bitmap data for the 8 sprites and fills in the SpriteSet
// so that it can start drawing on the next frame.
SpriteAddressGen address_gen(clk, ce,
cycle[8] && !cycle[6], // Load sprites between 256..319
obj_size, obj_patt, // Object size and pattern table
cycle[2:0], // Cycle counter
oam_bus, // Info from temp buffer.
sprite_vram_addr, // [out] VRAM Address that we want data from
vram_din, // [in] Data at the specified address
spriteset_load,
spriteset_load_in); // Which parts of SpriteGen to load
// Between 0..255 (256 cycles), draws pixels.
// Between 256..319 (64 cycles), will be populated for next line
SpriteSet sprite_gen(clk, ce, !cycle[8], spriteset_load, spriteset_load_in, obj_pixel_noblank, is_obj0_pixel);
// Blank out obj in the leftmost 8 pixels?
wire show_obj_on_pixel = (object_clip || (cycle[7:3] != 0)) && enable_objects;
wire [4:0] obj_pixel = {obj_pixel_noblank[4:2], show_obj_on_pixel ? obj_pixel_noblank[1:0] : 2'b00};
reg sprite0_hit_bg; // True if sprite#0 has collided with the BG in the last frame.
always @(posedge clk) if (ce) begin
if (exiting_vblank)
sprite0_hit_bg <= 0;
else if (is_rendering && // Object rendering is enabled
!cycle[8] && // X Pixel 0..255
cycle[7:0] != 255 && // X pixel != 255
!is_pre_render_line && // Y Pixel 0..239
obj0_on_line && // True if sprite#0 is included on the scan line.
is_obj0_pixel && // True if the pixel came from tempram #0.
show_obj_on_pixel &&
bg_pixel[1:0] != 0) begin // Background pixel nonzero.
sprite0_hit_bg <= 1;
end
// if (!cycle[8] && is_visible_line && obj0_on_line && is_obj0_pixel)
// $write("Sprite0 hit bg scan %d!!\n", scanline);
// if (is_obj0_pixel)
// $write("drawing obj0 pixel %d/%d\n", scanline, cycle);
end
wire [3:0] pixel;
wire pixel_is_obj;
PixelMuxer pixel_muxer(bg_pixel, obj_pixel[3:0], obj_pixel[4], pixel, pixel_is_obj);
// Compute the value to put on the VRAM address bus
assign vram_a = !is_rendering ? loopy[13:0] : // VRAM
(cycle[2:1] == 0) ? {2'b10, loopy[11:0]} : // Name table
(cycle[2:1] == 1) ? {2'b10, loopy[11:10], 4'b1111, loopy[9:7], loopy[4:2]} : // Attribute table
cycle[8] && !cycle[6] ? {1'b0, sprite_vram_addr} :
{1'b0, bg_patt, bg_name_table, cycle[1], loopy[14:12]}; // Pattern table bitmap #0, #1
// Read from VRAM, either when user requested a manual read, or when we're generating pixels.
assign vram_r = read && (ain == 7) ||
is_rendering && cycle[0] == 0 && !end_of_line;
// Write to VRAM?
assign vram_w = write && (ain == 7) && !is_pal_address && !is_rendering;
wire [5:0] color2;
PaletteRam palette_ram(clk, ce,
is_rendering ? {pixel_is_obj, pixel[3:0]} : (is_pal_address ? loopy[4:0] : 5'b0000), // Read addr
din[5:0], // Value to write
color2, // Output color
write && (ain == 7) && is_pal_address); // Condition for writing
assign color = grayscale ? {color2[5:4], 4'b0} : color2;
// always @(posedge clk)
// if (scanline == 194 && cycle < 8 && color == 15) begin
// $write("Pixel black %x %x %x %x %x\n", bg_pixel,obj_pixel,pixel,pixel_is_obj,color);
// end
always @(posedge clk) if (ce) begin
// if (!is_in_vblank && write)
// $write("%d/%d: $200%d <= %x\n", scanline, cycle, ain, din);
if (write) begin
case (ain)
0: begin // PPU Control Register 1
// t:....BA.. ........ = d:......BA
obj_patt <= din[3];
bg_patt <= din[4];
obj_size <= din[5];
vbl_enable <= din[7];
//$write("PPU Control #0 <= %X\n", din);
end
1: begin // PPU Control Register 2
grayscale <= din[0];
playfield_clip <= din[1];
object_clip <= din[2];
enable_playfield <= din[3];
enable_objects <= din[4];
color_intensity <= din[7:5];
if (!din[3] && scanline == 59)
$write("Disabling playfield at cycle %d\n", cycle);
end
endcase
end
// Reset frame specific counters upon exiting vblank
if (exiting_vblank)
nmi_occured <= 0;
// Set the
if (entering_vblank)
nmi_occured <= 1;
// Reset NMI register when reading from Status
if (read && ain == 2)
nmi_occured <= 0;
end
// If we're triggering a VBLANK NMI
assign nmi = nmi_occured && vbl_enable;
// One cycle after vram_r was asserted, the value
// is available on the bus.
reg vram_read_delayed;
always @(posedge clk) if (ce) begin
if (vram_read_delayed)
vram_latch <= vram_din;
vram_read_delayed <= vram_r;
end
// Value currently being written to video ram
assign vram_dout = din;
reg [7:0] latched_dout;
always @* begin
case (ain)
2: latched_dout = {nmi_occured,
sprite0_hit_bg,
sprite_overflow,
5'b00000};
4: latched_dout = oam_bus;
default: if (is_pal_address) begin
latched_dout = {2'b00, color};
end else begin
latched_dout = vram_latch;
end
endcase
end
assign dout = latched_dout;
assign mapper_ppu_flags = {scanline, cycle, obj_size, is_rendering};
endmodule // PPU