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:
@@ -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 (
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
120
cores/nes/src/EEPROM_24C0x.sv
Normal file
120
cores/nes/src/EEPROM_24C0x.sv
Normal 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
@@ -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
1850
cores/nes/src/cart.sv
Normal file
File diff suppressed because it is too large
Load Diff
737
cores/nes/src/mappers/FDS.sv
Normal file
737
cores/nes/src/mappers/FDS.sv
Normal 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
|
||||
294
cores/nes/src/mappers/JYCompany.sv
Normal file
294
cores/nes/src/mappers/JYCompany.sv
Normal 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
|
||||
274
cores/nes/src/mappers/MMC1.sv
Normal file
274
cores/nes/src/mappers/MMC1.sv
Normal 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
|
||||
325
cores/nes/src/mappers/MMC2.sv
Normal file
325
cores/nes/src/mappers/MMC2.sv
Normal 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
|
||||
697
cores/nes/src/mappers/MMC3.sv
Normal file
697
cores/nes/src/mappers/MMC3.sv
Normal 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
|
||||
469
cores/nes/src/mappers/MMC5.sv
Normal file
469
cores/nes/src/mappers/MMC5.sv
Normal 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
|
||||
425
cores/nes/src/mappers/Namco.sv
Normal file
425
cores/nes/src/mappers/Namco.sv
Normal 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
|
||||
318
cores/nes/src/mappers/Sachen.sv
Normal file
318
cores/nes/src/mappers/Sachen.sv
Normal 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
|
||||
574
cores/nes/src/mappers/Sunsoft.sv
Normal file
574
cores/nes/src/mappers/Sunsoft.sv
Normal 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
|
||||
974
cores/nes/src/mappers/VRC.sv
Normal file
974
cores/nes/src/mappers/VRC.sv
Normal 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
|
||||
|
||||
1057
cores/nes/src/mappers/generic.sv
Normal file
1057
cores/nes/src/mappers/generic.sv
Normal file
File diff suppressed because it is too large
Load Diff
1826
cores/nes/src/mappers/misc.sv
Normal file
1826
cores/nes/src/mappers/misc.sv
Normal file
File diff suppressed because it is too large
Load Diff
3282
cores/nes/src/mmu.v
3282
cores/nes/src/mmu.v
File diff suppressed because it is too large
Load Diff
@@ -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
1341
cores/nes/src/ppu.sv
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
Reference in New Issue
Block a user