Files
NES_MiSTer/rtl/nes.v
2025-11-09 23:52:47 +08:00

930 lines
34 KiB
Verilog

// Copyright (c) 2012-2013 Ludvig Strigeus
// This program is GPL Licensed. See COPYING for the full license.
// 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
// xx00 - xxFF, into a latch, and the second cycle it writes to $2004.
// Facts:
// 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
// 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 put_cycle, // Current cycle even or odd?
input put_ce, // CE on a PUT cycle from APU
input get_ce, // CE on a GET cycle from APU
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] dma_data_to_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 && put_ce) dmc_state <= 1;
if (dmc_state == 1 && put_ce) 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 && get_ce) spr_state <= 3;
if (spr_state[1] && put_ce && dmc_state == 1) spr_state <= 1;
if (spr_state[1] && get_ce) sprite_dma_addr[7:0] <= new_sprite_dma_addr[7:0];
if (spr_state[1] && get_ce && new_sprite_dma_addr[8]) spr_state <= 0;
if (spr_state[1]) sprite_dma_lastval <= dma_data_to_ram;
end
assign pause_cpu = (spr_state[0] || dmc_trigger || dmc_state == 1);
assign dmc_ack = (dmc_state == 1 && !put_cycle && dmc_trigger); // a DMC hardware bug can make trigger fall before it's done
assign aout_enable = dmc_ack || spr_state[1];
assign read = !put_cycle;
assign data_to_ram = sprite_dma_lastval;
assign aout = dmc_ack ? dmc_dma_addr : !put_cycle ? sprite_dma_addr : 16'h2004;
endmodule
module NES(
input clk,
input reset_nes,
input ppu_rst_behavior,
input cold_reset,
input pausecore,
output corepaused,
input [1:0] sys_type,
output [1:0] nes_div,
input [63:0] mapper_flags,
output [15:0] sample, // sample generated from APU
output [5:0] color, // pixel generated from PPU
output [1:0] joypad_clock, // Set to 1 for each joypad to clock it.
output [2:0] joypad_out, // Set to 1 to strobe joypads. Then set to zero to keep the value.
input [4:0] joypad1_data, // Port1
input [4:0] joypad2_data, // Port2
input fds_busy, // FDS Disk Swap Busy
input fds_eject, // FDS Disk Swap Pause
input fds_auto_eject,
input [1:0] max_diskside,
input fds_fast,
output [1:0] diskside,
input [4:0] audio_channels, // Enabled audio channels
input ex_sprites,
input [1:0] mask,
input dejitter_timing,
// Access signals for the SDRAM.
output [24: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,
output refresh,
input [20:0] prg_mask,
input [19:0] chr_mask,
// 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,
output mapper_has_flashsaves,
input debug_dots,
// savestates
output mapper_has_savestate,
input increaseSSHeaderCount,
input save_state,
input load_state,
input [1:0] savestate_number,
output sleep_savestate,
output state_loaded,
output hsync,
output hblank,
output vsync,
output vblank,
output [24:0] Savestate_SDRAMAddr,
output Savestate_SDRAMRdEn,
output Savestate_SDRAMWrEn,
output [7:0] Savestate_SDRAMWriteData,
input [7:0] Savestate_SDRAMReadData,
output [63:0] SaveStateExt_Din,
output [9:0] SaveStateExt_Adr,
output SaveStateExt_wren,
output SaveStateExt_rst,
input [63:0] SaveStateExt_Dout,
output SaveStateExt_load,
output [63:0] SAVE_out_Din, // data read from savestate
input [63:0] SAVE_out_Dout, // data written to savestate
output [25:0] SAVE_out_Adr, // all addresses are DWORD addresses!
output SAVE_out_rnw, // read = 1, write = 0
output SAVE_out_ena, // one cycle high for each action
output [7:0] SAVE_out_be,
input SAVE_out_done // should be one cycle high when write is done or read value is valid
);
/**********************************************************/
/************* 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
// 2000011112222
// 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 = 1; // 1 == odd, 0 == even
// Clock Dividers
localparam div_cpu_n = 5'd12;
localparam 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 = (div_cpu == div_cpu_n - 5'd2); // First PPU cycle where cpu data is visible.
// Signals
wire cart_pre = (div_cpu >= div_cpu_n - 5'd6) && (div_cpu <= div_cpu_n - 5'd2);
wire ppu_read = (ppu_tick == 1);
wire ppu_write = (ppu_tick == 1);
wire phi2 = (div_cpu > 4 && div_cpu < div_cpu_n);
// 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;
reg bootvector_flag;
wire cpu_reset = reset_ss | hold_reset;
wire reset = cpu_reset | bootvector_flag;
wire reset_noSS = reset_nes | hold_reset | bootvector_flag;
// pause
reg corepause_active = 0;
reg corepause_active_delay = 0;
reg skip_pause_ce = 0;
reg [7:0] corepause_delay = 8'd0;
reg [2:0] div_ppu_pause = 0;
wire skip_pixel_pause;
wire ppu_ce_pause = corepause_active ? (div_ppu_pause == div_ppu_n) : ppu_ce;
wire render_ena;
wire [8:0] cycle_paused;
wire [8:0] scanline_paused;
wire is_in_vblank_paused;
wire evenframe;
wire evenframe_paused;
assign corepaused = corepause_active;
assign refresh = corepause_active_delay && ppu_ce_pause;
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 == 2'b01))
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 && ~corepause_active) || (skip_pixel_pause && corepause_active)) && (faux_pixel_cnt == 0)) && !dejitter_timing) begin
freeze_clocks <= 1'b1;
faux_pixel_cnt <= {div_ppu_n - 1'b1, 1'b0} + 1'b1;
end
if (reset_nes | hold_reset) begin
bootvector_flag <= 1;
odd_or_even <= 1;
end else if (loading_savestate) begin
odd_or_even <= SS_TOP[0];
end else if (cpu_ce) begin
odd_or_even <= ~odd_or_even;
bootvector_flag <= 0;
end
// 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
// pause
if (ppu_ce_pause) skip_pause_ce <= 0; // must skip the first CE after pause to sync back to correct ppu
if (reset_nes) begin
corepause_active <= 0;
corepause_active_delay <= 0;
end else begin
if (corepause_active || (pausecore && div_cpu == 5'd1 && div_ppu == 3'd1 && div_sys == 0 && cpu_tick_count == 0 && ~freeze_clocks && is_in_vblank_paused && ~pause_cpu && cpu_Instrnew)) begin
div_cpu <= 5'd1;
div_ppu <= 3'd1;
div_sys <= 0;
cpu_tick_count <= 0;
corepause_active <= 1;
div_ppu_pause <= div_ppu + 3'd1;
end
if (corepause_active) begin
if (corepause_delay < 8'hFF) begin
corepause_delay <= corepause_delay + 1'd1;
end else begin
corepause_active_delay <= 1;
end
if (~freeze_clocks | ~(div_ppu_pause == (div_ppu_n - 1'b1))) begin
div_ppu_pause <= ppu_ce_pause ? 3'd1 : div_ppu_pause + 3'd1;
if (~pausecore && ppu_ce_pause && (cycle_paused == ppu_cycle) && (scanline_paused == scanline_ppu) && (evenframe == evenframe_paused)) begin
corepause_active <= 0;
corepause_active_delay <= 0;
skip_pause_ce <= 1;
end
end
end else begin
corepause_delay <= 8'd0;
end
end
end
assign SS_TOP_BACK[0] = odd_or_even;
ClockGen clockgen_pause(
.clk (clk),
.ce (ppu_ce_pause && ~skip_pause_ce),
.reset (reset_noSS),
.sys_type (sys_type),
.is_rendering (render_ena),
.scanline (scanline_paused),
.cycle (cycle_paused),
.is_in_vblank (is_in_vblank_paused),
//.end_of_line (end_of_line),
//.at_last_cycle_group (at_last_cycle_group),
//.exiting_vblank (exiting_vblank),
//.entering_vblank (entering_vblank),
//.is_pre_render (is_pre_render_line),
.short_frame (skip_pixel_pause),
//.is_vbe_sl (is_vbe_sl)
.evenframe (evenframe_paused)
);
/**********************************************************/
/************* CPU ***************/
/**********************************************************/
// TODO: At some point the CPU, APU, and DMA need to be isolated into their own unit, as in the
// actual system they were part of the same package and the internal bus was isolated in a variety
// of ways from the external bus. This is represented here in the code, but in a way that is pretty
// unintuitive to anyone who looks at it.
wire [15:0] cpu_addr;
wire cpu_rnw;
wire pause_cpu;
wire nmi;
wire mapper_irq;
wire apu_irq;
wire cpu_Instrnew;
// If the external bus is driven, electrically it will overwhelm the internal bus of register
// 4015's output.
wire apu_reg_cs = (apu_cs && addr[4:0] == 5'h15);
wire [7:0] apu_reg_value = {apu_dout[7:6], from_data_bus[5], apu_dout[4:0]}; // Fill in the undriven bit with bus.
wire [7:0] internal_data_bus = (apu_reg_cs ? apu_reg_value : from_data_bus);
T65 cpu(
.mode (0),
.BCD_en (0),
.res_n (~cpu_reset && ~cold_reset),
.pwr_n (~cold_reset), // Cold boot, power reset, must be paired with 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 ? internal_data_bus : cpu_dout),
.DO (cpu_dout),
.Instrnew (cpu_Instrnew),
// savestates
.SaveStateBus_Din (SaveStateBus_Din ),
.SaveStateBus_Adr (SaveStateBus_Adr ),
.SaveStateBus_wren (SaveStateBus_wren),
.SaveStateBus_rst (SaveStateBus_rst ),
.SaveStateBus_load (loading_savestate ),
.SaveStateBus_Dout (SaveStateBus_wired_or[0])
);
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] dma_data_bus = (joypad1_cs && dma_aout_enable) ? {from_data_bus[7:5], joypad1_data[4:0]} :
(joypad2_cs && dma_aout_enable) ? {from_data_bus[7:5], joypad2_data[4:0]} :
from_data_bus;
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;
wire get_ce, put_ce;
DmaController dma(
.clk (clk),
.ce (cpu_ce),
.reset (reset_noSS),
.put_cycle (odd_or_even), // Even or odd cycle
.sprite_trigger (apu_cs && addr[4:0] == 5'h14 && ~cpu_rnw), // 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
.dma_data_to_ram (internal_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),
.get_ce (get_ce),
.put_ce (put_ce)
);
/**********************************************************/
/************* APU ***************/
/**********************************************************/
// The APU is part of the 2A03 and parts of it are not exposed to external busses.
wire apu_cs = cpu_addr[15:5] == 11'b0100_0000_000;
wire [7:0] apu_dout;
wire [15:0] sample_apu;
APU apu(
.MMC5 (1'b0),
.clk (clk),
.PHI2 (phi2),
.CS (apu_cs),
.PAL (sys_type == 2'b01),
.ce (apu_ce),
.reset (reset),
.cold_reset (cold_reset),
.ADDR (addr[4:0]),
.RW (cpu_rnw),
.DIN (dbus),
.DOUT (apu_dout),
.audio_channels (audio_channels),
.Sample (sample_apu),
.DmaReq (apu_dma_request),
.DmaAck (apu_dma_ack),
.DmaAddr (apu_dma_addr),
.DmaData (dma_data_bus),
.get_or_put (odd_or_even),
.IRQ (apu_irq),
.put_ce (put_ce),
.get_ce (get_ce),
// savestates
.SaveStateBus_Din (SaveStateBus_Din ),
.SaveStateBus_Adr (SaveStateBus_Adr ),
.SaveStateBus_wren (SaveStateBus_wren),
.SaveStateBus_rst (SaveStateBus_rst ),
.SaveStateBus_load (loading_savestate ),
.SaveStateBus_Dout (SaveStateBus_wired_or[1])
);
defparam apu.SSREG_INDEX_TOP = SSREG_INDEX_APU_TOP;
defparam apu.SSREG_INDEX_DMC1 = SSREG_INDEX_APU_DMC1;
defparam apu.SSREG_INDEX_DMC2 = SSREG_INDEX_APU_DMC2;
defparam apu.SSREG_INDEX_FCT = SSREG_INDEX_APU_FCT;
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 = apu_cs && addr[4:0] == 5'h16;
wire joypad2_cs = apu_cs && addr[4:0] == 5'h17;
reg [2:0] joy_out;
reg [2:0] joy_latch;
always @(posedge clk) begin
if (put_ce) joy_out <= joy_latch;
if (joypad1_cs && ~cpu_rnw) begin
joy_latch <= cpu_dout[2:0];
if (put_ce) joy_out <= cpu_dout[2:0];
end
end
assign joypad_out = joy_out;
assign joypad_clock = {joypad2_cs && cpu_rnw, joypad1_cs && cpu_rnw};
/**********************************************************/
/************* 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 [8:0] ppu_cycle;
wire [8:0] scanline_ppu;
assign cycle = use_fake_h ? 9'd340 : (corepause_active) ? cycle_paused : ppu_cycle;
assign scanline = (corepause_active) ? scanline_paused : scanline_ppu;
PPU ppu(
.clk (clk),
.cs (addr[15:13] == 3'b001 && phi2),
.RWn (mr_int && !mw_int),
.rst_behavior (ppu_rst_behavior),
.ce (ppu_ce),
.reset (reset),
.sys_type (sys_type),
.debug_dots (debug_dots),
.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_addr (chr_addr),
.vram_a_ex (chr_addr_ex),
.vram_dbus_in (chr_to_ppu),
.vram_dout (chr_from_ppu),
.scanline (scanline_ppu),
.cycle (ppu_cycle),
.emphasis (emphasis),
.short_frame (skip_pixel),
.extra_sprites (ex_sprites),
.mask (mask),
.render_ena_out (render_ena),
.evenframe (evenframe),
.hblank (hblank),
.vblank (vblank),
.hsync (hsync),
.vsync (vsync),
// savestates
.SaveStateBus_Din (SaveStateBus_Din ),
.SaveStateBus_Adr (SaveStateBus_Adr ),
.SaveStateBus_wren (SaveStateBus_wren ),
.SaveStateBus_rst (SaveStateBus_rst ),
.SaveStateBus_load (loading_savestate ),
.SaveStateBus_Dout (SaveStateBus_wired_or[2]),
.Savestate_OAMAddr (Savestate_OAMAddr ),
.Savestate_OAMRdEn (Savestate_OAMRdEn ),
.Savestate_OAMWrEn (Savestate_OAMWrEn ),
.Savestate_OAMWriteData (Savestate_OAMWriteData ),
.Savestate_OAMReadData (Savestate_OAMReadData )
);
/**********************************************************/
/************* Cart ***************/
/**********************************************************/
wire [15:0] prg_addr = addr;
wire [7:0] prg_din = (dbus & (prg_conflict ? cpumem_din : 8'hFF)) | (prg_conflict_d0 ? cpumem_din & 8'h01 : 8'h00);
wire prg_read = mr_int && cart_pre && (addr[15:5] != 11'b0100_0000_000) && !ppu_cs;
wire prg_write = mw_int && cart_pre;
wire prg_allow, vram_a10, vram_ce, chr_allow;
wire [24:0] prg_linaddr;
wire [21:0] chr_linaddr;
wire [7:0] prg_dout_mapper, chr_from_ppu_mapper;
wire [15:0] sample_ext;
wire has_chr_from_ppu_mapper, prg_bus_write, prg_conflict, prg_conflict_d0, has_flashsaves;
assign save_written = has_flashsaves ? (!prg_linaddr[24] && prg_write && prg_allow) : // Flash save: writes to PRG-ROM
(mapper_flags[7:0] == 8'h14) ? (prg_linaddr[21:18] == 4'b1111 && prg_write && prg_allow) : // Mapper 20/FDS
(prg_addr[15:13] == 3'b011 && prg_write) | bram_write; // Default - $6000-$7FFF or BRAM
assign mapper_has_flashsaves = has_flashsaves;
cart_top multi_mapper (
// FPGA specific
.clk (clk),
.reset (reset_noSS),
.flags (mapper_flags), // iNES header data (use 0 while loading)
.paused (freeze_clocks),
// Cart pins (slightly abstracted)
.ce (cart_ce & ~reset_noSS), // M2 (held in high impedance during reset)
.cpu_ce (cpu_ce), // Serves as M2 Inverted
.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
.prg_mask (prg_mask), // PRG Mask for SDRAM translation
.chr_mask (chr_mask), // CHR Mask for SDRAM translation
// 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!
// 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
.has_savestate (mapper_has_savestate), // Mapper supports savestates
.prg_conflict_d0 (prg_conflict_d0), // Simulate bus conflicts for Mapper 144
.has_flashsaves (has_flashsaves), // Homebrew mapper that saves to PRG-ROM in flash memory
// User input/FDS controls
.fds_eject (fds_eject), // Used to trigger FDS disk changes
.fds_busy (fds_busy), // Used to trigger FDS disk changes
.fds_fast (fds_fast),
.diskside (diskside),
.max_diskside (max_diskside),
.fds_auto_eject (fds_auto_eject),
// savestates
.SaveStateBus_Din (SaveStateBus_Din ),
.SaveStateBus_Adr (SaveStateBus_Adr ),
.SaveStateBus_wren (SaveStateBus_wren),
.SaveStateBus_rst (SaveStateBus_rst ),
.SaveStateBus_load (loading_savestate ),
.SaveStateBus_Dout (SaveStateBus_wired_or[3]),
.Savestate_MAPRAMactive (Savestate_MAPRAMactive),
.Savestate_MAPRAMAddr (Savestate_MAPRAMAddr),
.Savestate_MAPRAMRdEn (Savestate_MAPRAMRdEn),
.Savestate_MAPRAMWrEn (Savestate_MAPRAMWrEn),
.Savestate_MAPRAMWriteData(Savestate_MAPRAMWriteData),
.Savestate_MAPRAMReadData (Savestate_MAPRAMReadData)
);
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
if (loading_savestate) begin
open_bus_data <= SS_TOP[8:1];
end else begin
if (!cpu_ce)
open_bus_data <= mw_int ? dbus : dma_data_bus;
end
end
assign from_data_bus = genie_ovr ? genie_data : external_data_bus;
reg [7:0] external_data_bus;
always @* begin
if (reset) begin
external_data_bus = SS_TOP[16:9]; // 0;
end else if (joypad1_cs && ~dma_aout_enable) begin // Joypad1 Read
external_data_bus = {open_bus_data[7:5], joypad1_data};
end else if (joypad2_cs && ~dma_aout_enable) begin // Joypad2 Read
external_data_bus = {open_bus_data[7:5], joypad2_data};
end else if (ppu_cs) begin // PPU Read
external_data_bus = ppu_dout;
end else if (prg_allow) begin // PRG Read
external_data_bus = cpumem_din;
end else if (prg_bus_write) begin // PRG/CPU Write
external_data_bus = prg_dout_mapper;
end else begin // Open Bus
external_data_bus = open_bus_data;
end
end
assign SS_TOP_BACK[ 8: 1] = open_bus_data;
assign SS_TOP_BACK[16: 9] = external_data_bus;
assign SS_TOP_BACK[63:17] = 47'b0; // free to be used
/**********************************************************/
/************* Savestates ***************/
/**********************************************************/
wire [63:0] SaveStateBus_Din;
wire [9:0] SaveStateBus_Adr;
wire SaveStateBus_wren, SaveStateBus_rst;
wire [7:0] Savestate_RAMWriteData;
wire [7:0] Savestate_RAMReadData;
wire [24:0] Savestate_RAMAddr;
wire Savestate_RAMRdEn;
wire Savestate_RAMWrEn;
wire [2:0] Savestate_RAMType;
localparam SAVESTATE_MODULES = 5;
wire [63:0] SaveStateBus_wired_or[0:SAVESTATE_MODULES-1];
wire reset_ss;
wire reset_delay;
wire savestate_savestate;
wire savestate_loadstate;
wire [31:0] savestate_address;
wire savestate_busy;
wire [63:0] SS_TOP;
wire [63:0] SS_TOP_BACK;
eReg_SavestateV #(SSREG_INDEX_TOP, SSREG_DEFAULT_TOP) iREG_SAVESTATE_TOP (clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[4], SS_TOP_BACK, SS_TOP);
wire [63:0] SaveStateBus_Dout = SaveStateBus_wired_or[0] | SaveStateBus_wired_or[1] | SaveStateBus_wired_or[2] | SaveStateBus_wired_or[3] | SaveStateBus_wired_or[4] | SaveStateExt_Dout;
wire loading_savestate;
wire saving_savestate;
wire sleep_savestates;
wire sleep_rewind;
assign Savestate_SDRAMAddr = Savestate_RAMAddr;
assign Savestate_SDRAMRdEn = Savestate_RAMRdEn && (Savestate_RAMType > 1);
assign Savestate_SDRAMWrEn = Savestate_RAMWrEn && (Savestate_RAMType > 1);
assign Savestate_SDRAMWriteData = Savestate_RAMWriteData;
wire [7:0] Savestate_OAMAddr = Savestate_RAMAddr[7:0];
wire Savestate_OAMRdEn = Savestate_RAMRdEn && (Savestate_RAMType == 0);
wire Savestate_OAMWrEn = Savestate_RAMWrEn && (Savestate_RAMType == 0);
wire [7:0] Savestate_OAMWriteData = Savestate_RAMWriteData;
wire [7:0] Savestate_OAMReadData;
wire Savestate_MAPRAMactive = loading_savestate | saving_savestate;
wire [12:0] Savestate_MAPRAMAddr = Savestate_RAMAddr[12:0];
wire Savestate_MAPRAMRdEn = Savestate_RAMRdEn && (Savestate_RAMType == 1);
wire Savestate_MAPRAMWrEn = Savestate_RAMWrEn && (Savestate_RAMType == 1);
wire [7:0] Savestate_MAPRAMWriteData = Savestate_RAMWriteData;
wire [7:0] Savestate_MAPRAMReadData;
assign Savestate_RAMReadData = (Savestate_RAMType == 0) ? Savestate_OAMReadData :
(Savestate_RAMType == 1) ? Savestate_MAPRAMReadData :
Savestate_SDRAMReadData;
assign SaveStateExt_Din = SaveStateBus_Din;
assign SaveStateExt_Adr = SaveStateBus_Adr;
assign SaveStateExt_wren = SaveStateBus_wren;
assign SaveStateExt_rst = SaveStateBus_rst;
assign SaveStateExt_load = loading_savestate;
savestates savestates (
.clk (clk),
.reset_in (reset_nes),
.reset_ss (reset_ss),
.reset_delay (reset_delay),
.load_done (state_loaded),
.increaseSSHeaderCount (increaseSSHeaderCount),
.save (savestate_savestate),
.load (savestate_loadstate),
.savestate_address (savestate_address),
.savestate_busy (savestate_busy),
.paused (corepause_active_delay),
.BUS_Din (SaveStateBus_Din),
.BUS_Adr (SaveStateBus_Adr),
.BUS_wren (SaveStateBus_wren),
.BUS_rst (SaveStateBus_rst),
.BUS_Dout (SaveStateBus_Dout),
.loading_savestate (loading_savestate),
.saving_savestate (saving_savestate),
.sleep_savestate (sleep_savestates),
.Save_RAMAddr (Savestate_RAMAddr),
.Save_RAMRdEn (Savestate_RAMRdEn),
.Save_RAMWrEn (Savestate_RAMWrEn),
.Save_RAMWriteData (Savestate_RAMWriteData),
.Save_RAMReadData (Savestate_RAMReadData),
.Save_RAMType (Savestate_RAMType),
.bus_out_Din (SAVE_out_Din),
.bus_out_Dout (SAVE_out_Dout),
.bus_out_Adr (SAVE_out_Adr),
.bus_out_rnw (SAVE_out_rnw),
.bus_out_ena (SAVE_out_ena),
.bus_out_be (SAVE_out_be),
.bus_out_done (SAVE_out_done)
);
statemanager #(58720256, 33554432) statemanager (
.clk (clk),
.reset (reset_nes),
.rewind_on (1'b0),
.rewind_active (1'b0),
.savestate_number (savestate_number),
.save (save_state),
.load (load_state),
.sleep_rewind (sleep_rewind),
.vsync (1'b0),
.request_savestate (savestate_savestate),
.request_loadstate (savestate_loadstate),
.request_address (savestate_address),
.request_busy (savestate_busy)
);
assign sleep_savestate = sleep_rewind | sleep_savestates;
endmodule