Files
NES_MiSTer/NES.sv
littlegamer87 09ec3e8a2b Auto-set system region from NES 2.0 header on rom load (#454)
* Auto-set system region from NES 2.0 header on rom load

* Rework auto region in line with other cores
2026-03-09 03:08:03 +08:00

1758 lines
50 KiB
Systemverilog

// Copyright (c) 2012-2013 Ludvig Strigeus
// This program is GPL Licensed. See COPYING for the full license.
//
// MiSTer port: Copyright (C) 2017,2018 Sorgelig
module emu
(
//Master input clock
input CLK_50M,
//Async reset from top-level module.
//Can be used as initial reset.
input RESET,
//Must be passed to hps_io module
inout [48:0] HPS_BUS,
//Base video clock. Usually equals to CLK_SYS.
output CLK_VIDEO,
//Multiple resolutions are supported using different CE_PIXEL rates.
//Must be based on CLK_VIDEO
output CE_PIXEL,
//Video aspect ratio for HDMI. Most retro systems have ratio 4:3.
//if VIDEO_ARX[12] or VIDEO_ARY[12] is set then [11:0] contains scaled size instead of aspect ratio.
output [12:0] VIDEO_ARX,
output [12:0] VIDEO_ARY,
output [7:0] VGA_R,
output [7:0] VGA_G,
output [7:0] VGA_B,
output VGA_HS,
output VGA_VS,
output VGA_DE, // = ~(VBlank | HBlank)
output VGA_F1,
output [1:0] VGA_SL,
output VGA_SCALER, // Force VGA scaler
output VGA_DISABLE, // analog out is off
input [11:0] HDMI_WIDTH,
input [11:0] HDMI_HEIGHT,
output HDMI_FREEZE,
output HDMI_BLACKOUT,
output HDMI_BOB_DEINT,
`ifdef MISTER_FB
// Use framebuffer in DDRAM
// FB_FORMAT:
// [2:0] : 011=8bpp(palette) 100=16bpp 101=24bpp 110=32bpp
// [3] : 0=16bits 565 1=16bits 1555
// [4] : 0=RGB 1=BGR (for 16/24/32 modes)
//
// FB_STRIDE either 0 (rounded to 256 bytes) or multiple of pixel size (in bytes)
output FB_EN,
output [4:0] FB_FORMAT,
output [11:0] FB_WIDTH,
output [11:0] FB_HEIGHT,
output [31:0] FB_BASE,
output [13:0] FB_STRIDE,
input FB_VBL,
input FB_LL,
output FB_FORCE_BLANK,
`ifdef MISTER_FB_PALETTE
// Palette control for 8bit modes.
// Ignored for other video modes.
output FB_PAL_CLK,
output [7:0] FB_PAL_ADDR,
output [23:0] FB_PAL_DOUT,
input [23:0] FB_PAL_DIN,
output FB_PAL_WR,
`endif
`endif
output LED_USER, // 1 - ON, 0 - OFF.
// b[1]: 0 - LED status is system status OR'd with b[0]
// 1 - LED status is controled solely by b[0]
// hint: supply 2'b00 to let the system control the LED.
output [1:0] LED_POWER,
output [1:0] LED_DISK,
// I/O board button press simulation (active high)
// b[1]: user button
// b[0]: osd button
output [1:0] BUTTONS,
input CLK_AUDIO, // 24.576 MHz
output [15:0] AUDIO_L,
output [15:0] AUDIO_R,
output AUDIO_S, // 1 - signed audio samples, 0 - unsigned
output [1:0] AUDIO_MIX, // 0 - no mix, 1 - 25%, 2 - 50%, 3 - 100% (mono)
//ADC
inout [3:0] ADC_BUS,
//SD-SPI
output SD_SCK,
output SD_MOSI,
input SD_MISO,
output SD_CS,
input SD_CD,
//High latency DDR3 RAM interface
//Use for non-critical time purposes
output DDRAM_CLK,
input DDRAM_BUSY,
output [7:0] DDRAM_BURSTCNT,
output [28:0] DDRAM_ADDR,
input [63:0] DDRAM_DOUT,
input DDRAM_DOUT_READY,
output DDRAM_RD,
output [63:0] DDRAM_DIN,
output [7:0] DDRAM_BE,
output DDRAM_WE,
//SDRAM interface with lower latency
output SDRAM_CLK,
output SDRAM_CKE,
output [12:0] SDRAM_A,
output [1:0] SDRAM_BA,
inout [15:0] SDRAM_DQ,
output SDRAM_DQML,
output SDRAM_DQMH,
output SDRAM_nCS,
output SDRAM_nCAS,
output SDRAM_nRAS,
output SDRAM_nWE,
`ifdef MISTER_DUAL_SDRAM
//Secondary SDRAM
//Set all output SDRAM_* signals to Z ASAP if SDRAM2_EN is 0
input SDRAM2_EN,
output SDRAM2_CLK,
output [12:0] SDRAM2_A,
output [1:0] SDRAM2_BA,
inout [15:0] SDRAM2_DQ,
output SDRAM2_nCS,
output SDRAM2_nCAS,
output SDRAM2_nRAS,
output SDRAM2_nWE,
`endif
input UART_CTS,
output UART_RTS,
input UART_RXD,
output UART_TXD,
output UART_DTR,
input UART_DSR,
// Open-drain User port.
// 0 - D+/RX
// 1 - D-/TX
// 2..6 - USR2..USR6
// Set USER_OUT to 1 to read from USER_IN.
input [6:0] USER_IN,
output [6:0] USER_OUT,
input OSD_STATUS
);
assign ADC_BUS = 'Z;
assign AUDIO_S = 0;
assign AUDIO_L = sample[15:0];
assign AUDIO_R = AUDIO_L;
assign AUDIO_MIX = 0;
assign LED_USER = downloading | (loader_fail & led_blink) | (bk_state != S_IDLE) | (bk_pending & ~status[50]);
assign LED_DISK = 0;
assign LED_POWER = 0;
assign BUTTONS [1] = 0;
assign VGA_SCALER = 0;
assign VGA_DISABLE = 0;
assign HDMI_BLACKOUT = 0;
assign HDMI_BOB_DEINT = 0;
assign VGA_F1 = 0;
//assign {UART_RTS, UART_TXD, UART_DTR} = 0;
assign {SD_SCK, SD_MOSI, SD_CS} = 'Z;
wire [1:0] ar = status[19:18];
wire vcrop_en = status[5];
wire [3:0] vcopt = status[38:35];
reg en216p;
reg [4:0] voff;
always @(posedge CLK_VIDEO) begin
en216p <= ((HDMI_WIDTH == 1920) && (HDMI_HEIGHT == 1080) && !forced_scandoubler && !scale);
voff <= (vcopt < 6) ? {vcopt,1'b0} : ({vcopt,1'b0} - 5'd24);
end
wire vga_de;
video_freak video_freak
(
.*,
.VGA_DE_IN(vga_de),
.ARX((!ar) ? (|hide_overscan ? (hide_overscan[1] ? 12'd4 : 12'd128) : 12'd64) : (ar - 1'd1)),
.ARY((!ar) ? (|hide_overscan ? (hide_overscan[1] ? 12'd3 : 12'd105) : 12'd49) : 12'd0),
.CROP_SIZE((en216p & vcrop_en) ? 10'd216 : 10'd0),
.CROP_OFF(voff),
.SCALE(status[40:39])
);
// If video mode (NTSC/PAL/DANDY) changes, force vmode update
reg [1:0] video_status;
reg new_vmode = 0;
always @(posedge clk) begin
if (video_status != effective_sys_type) begin
video_status <= effective_sys_type;
new_vmode <= ~new_vmode;
end
end
// Status Bit Map:
// 0 1 2 3 4 5 6
// 01234567890123456789012345678901 23456789012345678901234567890123
// 0123456789ABCDEFGHIJKLMNOPQRSTUV 0123456789ABCDEFGHIJKLMNOPQRSTUV
// XXXXXXXX XX XXXXXXXXXXXXX XX XXXXXXXXXXXXXXXXXXXXXX
`include "build_id.v"
parameter CONF_STR = {
"NES;SS3E000000:200000,UART31250,MIDI;",
"FS,NESFDSNSF;",
"H1F2,BIN,Load FDS BIOS;",
"-;",
"ONO,System Type,Auto,NTSC,PAL,Dendy;",
"-;",
"C,Cheats;",
"H2OK,Cheats Enabled,On,Off;",
"-;",
"oI,Autosave,On,Off;",
"H5D0R6,Load Backup RAM;",
"H5D0R7,Save Backup RAM;",
"-;",
"oC,Savestates to SDCard,On,Off;",
"oDE,Savestate Slot,1,2,3,4;",
"d7rA,Save state(Alt+F1-F4);",
"d7rB,Restore state(F1-F4);",
"-;",
"P1,Audio & Video;",
"P1-;",
"P1oFH,Palette,Kitrinx,Smooth,Wavebeam,Sony CXA,PC-10 Better,Custom;",
"H3P1FC3,PAL,Custom Palette;",
"P1-;",
"P1OIJ,Aspect ratio,Original,Full Screen,[ARC1],[ARC2];",
"P1O13,Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%,CRT 75%;",
"d6P1O5,Vertical Crop,Disabled,216p(5x);",
"d6P1o36,Crop Offset,0,2,4,8,10,12,-12,-10,-8,-6,-4,-2;",
"P1o78,Scale,Normal,V-Integer,Narrower HV-Integer,Wider HV-Integer;",
"P1O[68:67],Overscan,Normal,Vertical Overscan,Borders,Everything;",
"P1ORS,Mask Edges,Off,Left,Both,Auto;",
"P1OP,Extra Sprites,Off,On;",
"P1-;",
"P1OUV,Audio Enable,Both,Internal,Cart Expansion,None;",
"P2,Input Options;",
"P2-;",
"P2O9,Swap Joysticks,No,Yes;",
"P2OA,Multitap,Disabled,Enabled;",
"P2oJK,SNAC,Off,Controllers,Zapper,3D Glasses;",
"P2o02,Peripheral,None,Zapper(Mouse),Zapper(Joy1),Zapper(Joy2),Vaus,Vaus(A-Trigger),Powerpad,Family Trainer;",
"P2oL,Famicom Keyboard,No,Yes;",
"P2-;",
"P2OL,Zapper Trigger,Mouse,Joystick;",
"P2OM,Crosshairs,On,Off;",
"P3,Miscellaneous;",
"P3-;",
"P3OG,Disk Swap,Auto,FDS button;",
"P3O[17],Disk Speed,Fast,Original;",
"P3o9,Pause when OSD is open,Off,On;",
"P4,Advanced;",
"P4-;",
"P4O[66:65],RAM Clear,No,$00,$FF,Random;",
"P4O[64],PPU Reset Behavior,Famicom,NES;",
"P4OQ,Video Dijitter,Enabled,Disabled;",
"P4O[69],Debug Dots,Off,On;",
"- ;",
"R0,Reset;",
"J1,A,B,Select,Start,FDS,Mic,Zapper/Vaus Btn,PP/Mat 1,PP/Mat 2,PP/Mat 3,PP/Mat 4,PP/Mat 5,PP/Mat 6,PP/Mat 7,PP/Mat 8,PP/Mat 9,PP/Mat 10,PP/Mat 11,PP/Mat 12,Savestates;",
"jn,A,B,Select,Start,L,,R|P;",
"jp,B,Y,Select,Start,L,,R|P;",
"I,",
"Disk 1A,",
"Disk 1B,",
"Disk 2A,",
"Disk 2B,",
"Slot=DPAD|Save/Load=Start+DPAD,",
"Active Slot 1,",
"Active Slot 2,",
"Active Slot 3,",
"Active Slot 4,",
"Save to state 1,",
"Restore state 1,",
"Save to state 2,",
"Restore state 2,",
"Save to state 3,",
"Restore state 3,",
"Save to state 4,",
"Restore state 4,",
"No NES 2.0 header found\nDefaulting to NTSC;",
"V,v",`BUILD_DATE
};
wire [23:0] joyA,joyB,joyC,joyD;
wire [23:0] joyA_unmod;
wire [10:0] ps2_key;
wire [1:0] buttons;
wire [127:0] status;
wire arm_reset = status[0];
wire [1:0] hide_overscan = status[68:67];
wire [3:0] palette2_osd = status[49:47];
wire joy_swap = status[9] ^ (raw_serial || piano); // Controller on port 2 for Miracle Piano/SNAC
wire fds_auto_eject = ~status[16];
wire fds_fast = ~status[17];
wire ext_audio = ~status[30];
wire int_audio = ~status[31];
// Auto detect console region
wire [1:0] auto_sys_type = (mapper_flags[37:36] == 2'd1) ? 2'd1 : // PAL
(mapper_flags[37:36] == 2'd3) ? 2'd2 : // Dendy
2'd0; // NTSC (0 and multi-region)
wire [1:0] effective_sys_type = (status[24:23] == 2'd0) ? auto_sys_type
: (status[24:23] - 2'd1);
wire pal_video = |effective_sys_type;
// Figure out file types
reg type_bios, type_fds, type_gg, type_nsf, type_nes, type_palette, is_bios, downloading;
reg [24:0] rom_sz;
wire [7:0] random_byte = q[7:0];
wire feedback = q[31]^q[29]^q[28]^ q[27]^ q[23]^q[20]^ q[19]^q[17]^ q[15]^q[14]^q[12]^ q[11]^q[9]^ q[4]^ q[3]^q[2];
reg [31:0] q = 32'haaaaaaaa;
always_ff @(posedge clk) begin
reg old_downld;
q <= {q[30:0], feedback} ;
old_downld <= downloading;
loader_reset <= !download_reset || (~old_downld && downloading);
ioctl_download <= ioctl_downloading;
{type_bios, type_fds, type_gg, type_nsf, type_nes, type_palette, is_bios, downloading} <= 0;
if (~|filetype[5:0])
case(filetype[7:6])
2'b00: begin type_bios <= 1; is_bios <= 1; downloading <= ioctl_downloading; end
2'b01: begin type_nes <= 1; downloading <= ioctl_downloading; end
2'b10: begin type_fds <= 1; downloading <= ioctl_downloading; end
//2'b11: begin type_palette <= 1; end
endcase
else if(&filetype)
type_gg <= 1;
else if (filetype[1:0] == 2'b01)
case (filetype[7:6])
2'b00: begin type_nes <= 1; downloading <= ioctl_downloading; end
2'b01: begin type_fds <= 1; downloading <= ioctl_downloading; end
2'b10: begin type_nsf <= 1; downloading <= ioctl_downloading; end
endcase
else if (filetype[1:0] == 2'b10) begin
type_bios <= 1;
downloading <= ioctl_downloading;
end else if (filetype[1:0] == 2'b11)
type_palette <= 1;
if(old_downld && ~downloading & (type_fds|type_nsf|type_nes)) rom_sz <= ioctl_addr - 1'd1;
end
assign BUTTONS[0] = osd_btn;
// Pop OSD menu if no rom has been loaded automatically
wire rom_loaded;
reg osd_btn = 0;
always @(posedge clk) begin : osd_block
integer timeout = 0;
if(!RESET) begin
osd_btn <= 0;
if(timeout < 61000000) begin
timeout <= timeout + 1;
if (timeout > 50000000)
osd_btn <= ~rom_loaded;
end
end
end
wire apu_ce;
reg [31:0] sd_lba;
reg sd_rd = 0;
reg sd_wr = 0;
wire sd_ack;
wire [8:0] sd_buff_addr;
wire [7:0] sd_buff_dout;
wire [7:0] sd_buff_din;
wire sd_buff_wr;
wire img_mounted;
wire img_readonly;
wire [63:0] img_size;
wire [7:0] filetype;
wire ioctl_downloading;
reg ioctl_download;
wire [24:0] ioctl_addr;
reg ioctl_wait;
wire [24:0] ps2_mouse;
wire [15:0] joy_analog0, joy_analog1;
wire [7:0] pdl[4];
wire forced_scandoubler;
wire [21:0] gamma_bus;
hps_io #(.CONF_STR(CONF_STR)) hps_io
(
.clk_sys(clk),
.HPS_BUS(HPS_BUS),
.buttons(buttons),
.forced_scandoubler(forced_scandoubler),
.new_vmode(new_vmode),
.joystick_0(joyA_unmod),
.joystick_1(joyB),
.joystick_2(joyC),
.joystick_3(joyD),
.joystick_l_analog_0(joy_analog0),
.joystick_l_analog_1(joy_analog1),
.paddle_0(pdl[0]),
.paddle_1(pdl[1]),
.paddle_2(pdl[2]),
.paddle_3(pdl[3]),
.status(status),
.status_menumask({(rom_loaded && mapper_has_savestate), en216p, ~status[50], ~raw_serial, (palette2_osd != 3'd5), ~gg_avail, bios_loaded, ~bk_ena}),
.status_in({status[63:47],ss_slot,status[44:0]}),
.status_set(statusUpdate),
.info_req(info_req),
.info(info),
.gamma_bus(gamma_bus),
.ioctl_download(ioctl_downloading),
.ioctl_addr(ioctl_addr),
.ioctl_wr(loader_clk),
.ioctl_dout(file_input),
.ioctl_wait(ioctl_wait | save_wait),
.ioctl_index(filetype),
.sd_lba('{sd_lba}),
.sd_rd(sd_rd),
.sd_wr(sd_wr),
.sd_ack(sd_ack),
.sd_buff_addr(sd_buff_addr),
.sd_buff_dout(sd_buff_dout),
.sd_buff_din('{sd_buff_din}),
.sd_buff_wr(sd_buff_wr),
.img_mounted(img_mounted),
.img_readonly(img_readonly),
.img_size(img_size),
.ps2_key(ps2_key),
.ps2_kbd_led_use(0),
.ps2_kbd_led_status(0),
.ps2_mouse(ps2_mouse)
//.uart_mode(16'b000_11111_000_11111)
);
assign joyA = joyA_unmod[23] ? 23'b0 : joyA_unmod;
wire info_req = diskside_info || ss_info_req || auto_info_req;
wire [7:0] info = ss_info_req ? ss_info : auto_info_req ? 8'd18 : {1'b0,diskside} + 3'd1;
wire clock_locked;
wire clk85;
wire clk;
pll pll
(
.refclk(CLK_50M),
.rst(0),
.outclk_0(clk85),
.outclk_1(CLK_VIDEO),
.outclk_2(clk),
.reconfig_to_pll(reconfig_to_pll),
.reconfig_from_pll(reconfig_from_pll),
.locked(clock_locked)
);
wire [63:0] reconfig_to_pll;
wire [63:0] reconfig_from_pll;
wire cfg_waitrequest;
reg cfg_write;
reg [5:0] cfg_address;
reg [31:0] cfg_data;
pll_cfg pll_cfg
(
.mgmt_clk(CLK_50M),
.mgmt_reset(0),
.mgmt_waitrequest(cfg_waitrequest),
.mgmt_read(0),
.mgmt_readdata(),
.mgmt_write(cfg_write),
.mgmt_address(cfg_address),
.mgmt_writedata(cfg_data),
.reconfig_to_pll(reconfig_to_pll),
.reconfig_from_pll(reconfig_from_pll)
);
always @(posedge CLK_50M) begin : cfg_block
reg pald = 0, pald2 = 0;
reg [2:0] state = 0;
pald <= (effective_sys_type == 2'd1);
pald2 <= pald;
cfg_write <= 0;
if(pald2 != pald) state <= 1;
if(!cfg_waitrequest) begin
if(state) state<=state+1'd1;
case(state)
1: begin
cfg_address <= 0;
cfg_data <= 0;
cfg_write <= 1;
end
3: begin
cfg_address <= 7;
cfg_data <= pald2 ? 2201376898 : 2537933971;
cfg_write <= 1;
end
5: begin
cfg_address <= 2;
cfg_data <= 0;
cfg_write <= 1;
end
endcase
end
end
// reset after download
reg [7:0] download_reset_cnt;
wire download_reset = download_reset_cnt != 0;
always @(posedge clk) begin
if(downloading) download_reset_cnt <= 8'hFF;
else if(!loader_busy && download_reset_cnt) download_reset_cnt <= download_reset_cnt - 1'd1;
end
// hold machine in reset until first download starts
reg init_reset_n = 0;
always @(posedge clk) if(downloading) init_reset_n <= 1;
wire [8:0] cycle;
wire [8:0] scanline;
wire [15:0] sample;
wire [5:0] color;
wire [2:0] joypad_out;
wire [1:0] joypad_clock;
reg [23:0] joypad_bits, joypad_bits2;
reg [7:0] joypad_d3, joypad_d4;
reg [1:0] last_joypad_clock;
wire [11:0] powerpad = joyA[22:11] | joyB[22:11] | joyC[22:11] | joyD[22:11];
wire [3:0] famtr;
assign famtr[0] = (~joypad_out[2] & powerpad[3]) | (~joypad_out[1] & powerpad[7]) | (~joypad_out[0] & powerpad[11]);
assign famtr[1] = (~joypad_out[2] & powerpad[2]) | (~joypad_out[1] & powerpad[6]) | (~joypad_out[0] & powerpad[10]);
assign famtr[2] = (~joypad_out[2] & powerpad[1]) | (~joypad_out[1] & powerpad[5]) | (~joypad_out[0] & powerpad[9] );
assign famtr[3] = (~joypad_out[2] & powerpad[0]) | (~joypad_out[1] & powerpad[4]) | (~joypad_out[0] & powerpad[8] );
wire [7:0] nes_joy_A = { joyA[0], joyA[1], joyA[2], joyA[3], joyA[7], joyA[6], joyA[5], ~paddle_atr & joyA[4] };
wire [7:0] nes_joy_B = { joyB[0], joyB[1], joyB[2], joyB[3], joyB[7], joyB[6], joyB[5], ~paddle_atr & joyB[4] };
wire [7:0] nes_joy_C = { joyC[0], joyC[1], joyC[2], joyC[3], joyC[7], joyC[6], joyC[5], ~paddle_atr & joyC[4] };
wire [7:0] nes_joy_D = { joyD[0], joyD[1], joyD[2], joyD[3], joyD[7], joyD[6], joyD[5], ~paddle_atr & joyD[4] };
wire mic_button = joyA[9] | joyB[9];
wire fds_btn = joyA[8] | joyB[8];
reg [1:0] nes_ce;
wire raw_serial = |status[52:51];
// Extend SNAC zapper high signal to be closer to original NES
wire extend_serial_d4 = status[52:51] == 2'b10;
wire snac_3d_glasses = &status[52:51];
wire serial_d4 = extend_serial_d4 ? |serial_d4_sr : ~USER_IN[4];
reg [7:0] serial_d4_sr;
reg snac_p2 = 0;
always @(posedge clk) begin
reg [17:0] clk_cnt;
if (raw_serial) begin
if (~USER_IN[6])
snac_p2 <= 1;
end else begin
snac_p2 <= 0;
end
clk_cnt <= clk_cnt + 1'b1;
serial_d4_sr[0] <= ~USER_IN[4];
// Shift every 10ms
if (clk_cnt == 18'd214772) begin
serial_d4_sr <= serial_d4_sr << 1;
clk_cnt <= 0;
end
end
// Indexes:
// IDXDIR Function USBPIN
// 0 OUT Strobe D+
// 1 OUT Clk (P2) D-
// 2 BI Glasses/D3 TX-
// 3 OUT CLK (P2) GND_d
// 4 IN D4 RX+
// 5 IN P1D0 RX-
// 6 IN P2D0 TX+
assign USER_OUT[4] = 1'b1;
assign USER_OUT[5] = 1'b1;
assign USER_OUT[6] = 1'b1;
reg [4:0] joypad1_data, joypad2_data;
wire joy0_d0 = snac_p2 ? ~USER_IN[6] : joypad_bits[0];
wire joy1_d0 = snac_p2 ? ~USER_IN[6] : joypad_bits2[0];
always_comb begin
if (raw_serial) begin
USER_OUT[0] = joypad_out[0];
USER_OUT[1] = ~joy_swap ? ~joypad_clock[1] : ~joypad_clock[0];
USER_OUT[2] = snac_3d_glasses ? joypad_out[1] : 1'b1;
USER_OUT[3] = ~joy_swap ? ~joypad_clock[0] : ~joypad_clock[1];
joypad1_data = {2'b0, mic, 1'b0, ~joy_swap ? joy0_d0 : ~USER_IN[5]};
joypad2_data = {serial_d4, snac_3d_glasses ? 1'b1 : ~USER_IN[2], 2'b00, ~joy_swap ? ~USER_IN[5] : joy1_d0};
end else begin
USER_OUT[0] = 1'b1;
USER_OUT[1] = 1'b1;
USER_OUT[2] = 1'b1;
USER_OUT[3] = 1'b1;
joypad1_data = {2'b0, mic, paddle_en & paddle_btn, joypad_bits[0]};
joypad2_data = joypad_bits2[0];
// periphery on port 2
if (lightgun_en) joypad2_data[4:3] = {trigger,light};
if (paddle_en) joypad2_data[4:1] = {joypad_d4[0], paddle_btn, 1'b0, joypad_d4[0]};
if (status[34:32] == 6) joypad2_data[4:3] = {joypad_d4[0], joypad_d3[0]};
if (status[34:32] == 7) joypad2_data[4:1] = ~famtr;
if (fkeyb) joypad2_data[4:1] = key_out;
end
end
wire mic = (mic_cnt < 8'd215) && mic_button;
reg [7:0] mic_cnt;
always @(posedge clk) mic_cnt <= (mic_cnt == 8'd250) ? 8'd0 : mic_cnt + 1'b1;
wire fkeyb = status[53];
wire [3:0] key_out;
keyboard fkey(
.clk(clk),
.reset(reset_nes || !fkeyb),
.posit(1'd1),
.ps2_key(ps2_key),
.reg_4016(joypad_out),
.reg_4017(key_out)
);
assign {UART_RTS, UART_DTR} = 1;
wire [15:0] uart_data;
miraclepiano miracle(
.clk(clk),
.reset(reset_nes || !piano),
.strobe(joypad_out[0]),
.joypad_o(),
.joypad_clock(joypad_clock[0]),
.data_o(uart_data),
.txd(UART_TXD),
.rxd(UART_RXD)
);
wire lightgun_en = ~status[34] & |status[33:32];
zapper zap (
.clk(clk),
.reset(reset_nes | ~lightgun_en),
.mode(status[33]),
.trigger_mode(status[21]),
.ps2_mouse(ps2_mouse),
.analog(~status[32] ? joy_analog0 : joy_analog1),
.analog_trigger(~status[32] ? joyA[10] : joyB[10]),
.cycle(cycle),
.scanline(scanline),
.color(color),
.reticle(reticle),
.light(light),
.trigger(trigger)
);
reg [7:0] paddle = 0;
always @(posedge clk) begin
reg [7:0] old_pdl[4];
reg [1:0] num = 0;
for(reg [2:0] i=0; i<4; i++) begin
old_pdl[i] <= pdl[i];
if($signed((pdl[i] - old_pdl[i])) > 4) num <= i[1:0];
end
paddle <= pdl[num];
end
localparam [7:0] paddle_off = 32; //middle point for Chase HQ
wire [7:0] paddle_adj = paddle_off + ((paddle < 40) ? 8'd40 : (paddle > 216) ? 8'd216 : paddle);
wire [7:0] paddle_nes = ~{paddle_adj[0],paddle_adj[1],paddle_adj[2],paddle_adj[3],paddle_adj[4],paddle_adj[5],paddle_adj[6],paddle_adj[7]};
wire paddle_en = (status[34:33] == 2);
wire paddle_atr = paddle_en & status[32];
wire paddle_btn = paddle_atr ? (joyA[4] | joyB[4] | joyC[4] | joyD[4]) : (joyA[10] | joyB[10] | joyC[10] | joyD[10]);
always @(posedge clk) begin
if (reset_nes) begin
joypad_bits <= 0;
joypad_bits2 <= 0;
joypad_d3 <= 0;
joypad_d4 <= 0;
last_joypad_clock <= 0;
end else begin
if (joypad_out[0]) begin
joypad_bits <= piano ? {15'h0000, uart_data[8:0]}
: {status[10] ? {8'h08, nes_joy_C} : 16'hFFFF, joy_swap ? nes_joy_B : nes_joy_A};
joypad_bits2 <= {status[10] ? {8'h04, nes_joy_D} : 16'hFFFF, joy_swap ? nes_joy_A : nes_joy_B};
joypad_d4 <= paddle_en ? paddle_nes : {4'b1111, powerpad[7], powerpad[11], powerpad[2], powerpad[3]};
joypad_d3 <= {powerpad[6], powerpad[10], powerpad[9], powerpad[5], powerpad[8], powerpad[4], powerpad[0], powerpad[1]};
end
if (!joypad_clock[0] && last_joypad_clock[0]) begin
joypad_bits <= {1'b0, joypad_bits[23:1]};
end
if (!joypad_clock[1] && last_joypad_clock[1]) begin
joypad_bits2 <= {1'b0, joypad_bits2[23:1]};
joypad_d4 <= {~paddle_en, joypad_d4[7:1]};
joypad_d3 <= {1'b1, joypad_d3[7:1]};
end
last_joypad_clock <= joypad_clock;
end
end
// Loader
wire [7:0] file_input;
wire [7:0] loader_input = (loader_busy && !downloading) ? !nsf ? bios_data : nsf_data : file_input;
wire loader_clk;
wire [24:0] loader_addr;
wire [7:0] loader_write_data;
reg loader_reset;
wire loader_write;
wire [63:0] loader_flags;
reg [63:0] mapper_flags;
wire fds = (mapper_flags[7:0] == 8'h14);
wire nsf = (loader_flags[7:0] == 8'h1F);
wire piano = (mapper_flags[30]);
wire [3:0] prg_nvram = mapper_flags[34:31];
wire loader_busy, loader_done, loader_fail;
wire [9:0] prg_mask, chr_mask;
wire [1:0] clearval = status[66:65];
wire [7:0] cleardata = (clearval == 3) ? random_byte : (clearval == 2 ? 8'hFF : 8'h00);
GameLoader loader
(
.clk ( clk ),
.clearval ( |clearval ),
.cleardata ( cleardata ),
.reset ( loader_reset ),
.downloading ( downloading ),
.filetype ( {4'b0000, type_nsf, type_fds, type_nes, type_bios} ),
.is_bios ( is_bios ), // boot0 bios
.indata ( loader_input ),
.indata_clk ( loader_clk ),
.mem_addr ( loader_addr ),
.mem_data ( loader_write_data ),
.mem_write ( loader_write ),
.mapper_flags ( loader_flags ),
.prg_mask ( prg_mask ),
.chr_mask ( chr_mask ),
.busy ( loader_busy ),
.done ( loader_done ),
.error ( loader_fail ),
.rom_loaded ( rom_loaded )
);
reg auto_info_req;
always @(posedge clk) begin
if (loader_done) mapper_flags <= loader_flags;
auto_info_req <= 0;
if (loader_done && rom_loaded && type_nes && !loader_flags[35] && status[24:23] == 2'd0)
auto_info_req <= 1;
end
reg led_blink;
always @(posedge clk) begin : blink_block
int cnt = 0;
cnt <= cnt + 1;
if(cnt == 10000000) begin
cnt <= 0;
led_blink <= ~led_blink;
end;
end
wire reset_nes =
~init_reset_n ||
buttons[1] ||
arm_reset ||
download_reset ||
loader_fail ||
bk_loading ||
bk_loading_req ||
hold_reset ||
(old_sys_type != effective_sys_type);
reg [1:0] old_sys_type;
always @(posedge clk) old_sys_type <= effective_sys_type;
wire [17:0] bram_addr;
wire [7:0] bram_din;
wire [7:0] bram_dout;
wire bram_write;
wire bram_en;
wire trigger;
wire light;
wire [1:0] diskside;
reg diskside_info;
always @(posedge clk) begin
reg [1:0] old_diskside;
old_diskside <= diskside;
diskside_info <= (old_diskside != diskside);
end
wire gg_reset = (type_fds | type_gg | type_nes | type_nsf) && ioctl_download;
// pause
reg pausecore;
reg [1:0] videopause_ce;
wire corepaused;
wire refresh;
wire sleep_savestate;
assign HDMI_FREEZE = corepaused;
always_ff @(posedge clk) begin
pausecore <= sleep_savestate | (status[41] && OSD_STATUS && !ioctl_download && !reset_nes);
if (!corepaused) begin
videopause_ce <= nes_ce + 1'd1;
end else begin
videopause_ce <= videopause_ce + 1'd1;
end
end
wire nes_hblank, nes_hsync, nes_vsync, nes_vblank;
NES nes (
.clk (clk),
.reset_nes (reset_nes),
.ppu_rst_behavior(status[64]),
.cold_reset (downloading & (type_fds | type_nes)),
.pausecore (pausecore),
.corepaused (corepaused),
.debug_dots (status[69]),
.sys_type (effective_sys_type),
.nes_div (nes_ce),
.mapper_flags (downloading ? 64'd0 : mapper_flags),
.gg (status[20]),
.gg_code (gg_code),
.gg_reset (gg_reset && loader_clk && !ioctl_addr),
.gg_avail (gg_avail),
// Audio
.sample (sample),
.audio_channels (5'b11111),
.int_audio (int_audio),
.ext_audio (ext_audio),
.apu_ce (apu_ce),
// Video
.ex_sprites (status[25]),
.color (color),
.hsync (nes_hsync),
.hblank (nes_hblank),
.vsync (nes_vsync),
.vblank (nes_vblank),
.emphasis (emphasis),
.cycle (cycle),
.scanline (scanline),
.mask (hide_overscan[1] ? 2'b00 : status[28:27]),
.dejitter_timing (status[26]),
// User Input
.joypad_out (joypad_out),
.joypad_clock (joypad_clock),
.joypad1_data (joypad1_data),
.joypad2_data (joypad2_data),
.diskside (diskside),
.fds_busy (fds_busy),
.fds_eject (fds_btn),
.fds_auto_eject (fds_auto_eject),
.max_diskside (max_diskside),
.fds_fast (fds_fast),
// Memory transactions
.cpumem_addr (cpu_addr ),
.cpumem_read (cpu_read ),
.cpumem_write (cpu_write),
.cpumem_dout (cpu_dout ),
.cpumem_din (cpu_din ),
.ppumem_addr (ppu_addr ),
.ppumem_read (ppu_read ),
.ppumem_write (ppu_write),
.ppumem_dout (ppu_dout ),
.ppumem_din (ppu_din ),
.refresh (refresh ),
.prg_mask (prg_mask ),
.chr_mask (chr_mask ),
.bram_addr (bram_addr),
.bram_din (bram_din),
.bram_dout (bram_dout),
.bram_write (bram_write),
.bram_override (bram_en),
.save_written (save_written),
.mapper_has_flashsaves (mapper_has_flashsaves),
// savestates
.mapper_has_savestate (mapper_has_savestate),
.increaseSSHeaderCount (!status[44]),
.save_state (ss_save),
.load_state (ss_load),
.savestate_number (ss_slot),
.sleep_savestate (sleep_savestate),
.Savestate_SDRAMAddr (Savestate_SDRAMAddr ),
.Savestate_SDRAMRdEn (Savestate_SDRAMRdEn ),
.Savestate_SDRAMWrEn (Savestate_SDRAMWrEn ),
.Savestate_SDRAMWriteData(Savestate_SDRAMWriteData),
.Savestate_SDRAMReadData (Savestate_SDRAMReadData ),
.SaveStateExt_Din (SaveStateBus_Din),
.SaveStateExt_Adr (SaveStateBus_Adr),
.SaveStateExt_wren (SaveStateBus_wren),
.SaveStateExt_rst (SaveStateBus_rst),
.SaveStateExt_Dout (SaveStateBus_Dout),
.SaveStateExt_load (savestate_load),
.SAVE_out_Din (ss_din), // data read from savestate
.SAVE_out_Dout (ss_dout), // data written to savestate
.SAVE_out_Adr (ss_addr), // all addresses are DWORD addresses!
.SAVE_out_rnw (ss_rnw), // read = 1, write = 0
.SAVE_out_ena (ss_req), // one cycle high for each action
.SAVE_out_be (ss_be),
.SAVE_out_done (ss_ack) // should be one cycle high when write is done or read value is valid
);
wire [24:0] cpu_addr;
wire [21:0] ppu_addr;
wire cpu_read, cpu_write, ppu_read, ppu_write;
wire [7:0] cpu_dout, cpu_din, ppu_dout, ppu_din;
wire [2:0] emphasis;
wire [7:0] bios_data;
wire bios_download = downloading & type_bios;
wire bios_write = (loader_write && bios_download && ~bios_loaded);
reg bios_loaded = 0; // Only load bios once
reg last_bios_download = 0;
always @(posedge clk) begin
last_bios_download <= bios_download;
if(last_bios_download && ~bios_download) begin
bios_loaded <= 1;
end
end
spram #(.addr_width(13)) fds_bios
(
.clock(clk),
.address(loader_addr[12:0]),
.wren(bios_write),
.data(loader_write_data),
.q(bios_data)
);
wire [7:0] nsf_data;
spram #(12, 8, "rtl/loopy_NSF.mif") nsfplayrom
(
.clock(clk),
.address(loader_addr[11:0]),
.q(nsf_data)
);
// loader_write -> clock when data available
reg loader_write_mem;
reg [7:0] loader_write_data_mem;
reg [24:0] loader_addr_mem;
reg loader_write_triggered;
always @(posedge clk) begin
if(loader_write) begin
loader_write_triggered <= 1'b1;
loader_addr_mem <= loader_addr;
loader_write_data_mem <= loader_write_data;
ioctl_wait <= 1;
end
if(nes_ce == 3) begin
loader_write_mem <= loader_write_triggered;
if(loader_write_triggered) begin
loader_write_triggered <= 1'b0;
end else if(ioctl_wait) begin
ioctl_wait <= 0;
end
end
end
wire [24:0] ch2_addr = sleep_savestate ? Savestate_SDRAMAddr :
mapper_has_flashsaves ? {6'b000000, save_addr} : // PRG-ROM
{7'b0001111, save_addr[17:0]}; // CARTRAM
wire ch2_wr = sleep_savestate ? Savestate_SDRAMWrEn : save_wr;
wire [7:0] ch2_din = sleep_savestate ? Savestate_SDRAMWriteData : sd_buff_dout;
wire ch2_rd = sleep_savestate ? Savestate_SDRAMRdEn : save_rd;
assign Savestate_SDRAMReadData = save_dout;
sdram sdram
(
.*,
// system interface
.clk ( clk85 ),
.init ( !clock_locked ),
// cpu/chipset interface
.ch0_addr ( (downloading | loader_busy) ? loader_addr_mem : {3'b0, ppu_addr} ),
.ch0_wr ( loader_write_mem | ppu_write ),
.ch0_din ( (downloading | loader_busy) ? loader_write_data_mem : ppu_dout ),
.ch0_rd ( ~(downloading | loader_busy) & ppu_read ),
.ch0_dout ( ppu_din ),
.ch0_busy ( ),
.ch1_addr ( cpu_addr ),
.ch1_wr ( cpu_write ),
.ch1_din ( cpu_dout ),
.ch1_rd ( cpu_read ),
.ch1_dout ( cpu_din ),
.ch1_busy ( ),
// reserved for backup ram save/load
.ch2_addr ( ch2_addr ),
.ch2_wr ( ch2_wr ),
.ch2_din ( ch2_din ),
.ch2_rd ( ch2_rd ),
.ch2_dout ( save_dout ),
.ch2_busy ( save_busy ),
.refresh (refresh ),
.ss_in (sdram_ss_in),
.ss_load (savestate_load),
.ss_out (sdram_ss_out)
);
wire [7:0] save_dout;
assign sd_buff_din = bram_en ? eeprom_dout : save_dout;
wire [7:0] eeprom_dout;
dpram #(" ", 11) eeprom
(
.clock_a(clk85),
.address_a(bram_addr),
.data_a(bram_dout),
.wren_a(bram_write),
.q_a(bram_din),
.clock_b(clk),
.address_b({sd_lba[1:0],sd_buff_addr}),
.data_b(sd_buff_dout),
.wren_b(sd_buff_wr & sd_ack),
.q_b(eeprom_dout)
);
wire save_busy;
reg save_rd, save_wr;
reg save_wait;
reg [18:0] save_addr;
always @(posedge clk) begin
if(~save_busy & ~save_rd & ~save_wr) save_wait <= 0;
if(~bk_busy) begin
save_addr <= '1;
save_wait <= 0;
end
else if(sd_ack & ~save_busy ) begin
if(~bk_loading && (save_addr != {sd_lba[9:0], sd_buff_addr})) begin
save_rd <= 1;
save_addr <= {sd_lba[9:0], sd_buff_addr};
save_wait <= 1;
end
if(bk_loading && sd_buff_wr) begin
save_wr <= 1;
save_addr <= {sd_lba[9:0], sd_buff_addr};
save_wait <= 1;
end
end
if(~bk_busy | save_busy | bram_en) {save_rd, save_wr} <= 0;
end
reg bk_pending;
wire save_written, mapper_has_flashsaves;
always @(posedge clk) begin
if ((mapper_flags[25] || fds || mapper_has_flashsaves) && ~OSD_STATUS && save_written)
bk_pending <= 1'b1;
else if (bk_state)
bk_pending <= 1'b0;
end
///////////////////////////////////////////////////
// palette loader
reg [23:0] pal_color;
reg [5:0] pal_index;
reg [1:0] pal_count;
wire pal_write = ioctl_download && type_palette ? ~|pal_count : 1'b0;
always @(posedge clk) begin
if (ioctl_download && loader_clk && type_palette && ioctl_addr < 192) begin
pal_count <= pal_count == 2 ? 2'd0 : pal_count + 2'd1;
case (pal_count)
0: begin
pal_color[23:16] <= file_input;
//pal_write <= 0;
pal_index <= ioctl_addr > 0 ? pal_index + 1'd1 : pal_index;
end
1: begin
pal_color[15:8] <= file_input;
end
2: begin
pal_color[7:0] <= file_input;
//pal_write <= 1;
end
endcase
end
if (!ioctl_download) begin
//pal_write <= 0;
pal_count <= 0;
pal_index <= 0;
end
end
///////////////////////////////////////////////////
wire [2:0] scale = status[3:1];
wire [2:0] sl = scale ? scale - 1'd1 : 3'd0;
assign VGA_SL = sl[1:0];
wire [1:0] reticle;
wire hold_reset;
wire ce_pix;
wire HSync,VSync,HBlank,VBlank;
wire [7:0] R,G,B;
wire [1:0] nes_ce_video = corepaused ? videopause_ce : nes_ce;
video video
(
.*,
.clk(clk),
.reset(reset_nes),
.cnt(nes_ce_video),
.sys_type(effective_sys_type),
.nes_hsync(nes_hsync),
.nes_hblank(nes_hblank),
.nes_vsync(nes_vsync),
.nes_vblank(nes_vblank),
.hold_reset(hold_reset),
.count_v(scanline),
.count_h(cycle),
.hide_overscan(pal_video && ~|hide_overscan ? 2'b01 : hide_overscan),
.palette(palette2_osd),
.load_color(pal_write && ioctl_download),
.load_color_data(pal_color),
.load_color_index(pal_index),
.emphasis(emphasis),
.reticle(~status[22] ? reticle : 2'b00),
.pal_video(pal_video)
);
video_mixer #(260, 0, 1) video_mixer
(
.*,
.freeze_sync(),
.VGA_DE(vga_de),
.hq2x(scale==1),
.scandoubler(scale || forced_scandoubler)
);
//////////////////////////// CODES ///////////////////////////////////
reg [128:0] gg_code;
wire gg_avail;
// Code layout:
// {clock bit, code flags, 32'b address, 32'b compare, 32'b replace}
// 128 127:96 95:64 63:32 31:0
// Integer values are in BIG endian byte order, so it up to the loader
// or generator of the code to re-arrange them correctly.
always_ff @(posedge clk) begin
gg_code[128] <= 1'b0;
if (ioctl_download & type_gg & loader_clk) begin
case (ioctl_addr[3:0])
0: gg_code[111:96] <= file_input; // Flags Bottom Word
1: gg_code[119:112] <= file_input; // Flags Bottom Word
2: gg_code[127:120] <= file_input; // Flags Top Word
3: gg_code[127:112] <= file_input; // Flags Top Word
4: gg_code[71:64] <= file_input; // Address Bottom Word
5: gg_code[79:72] <= file_input; // Address Bottom Word
6: gg_code[87:80] <= file_input; // Address Top Word
7: gg_code[95:88] <= file_input; // Address Top Word
8: gg_code[39:32] <= file_input; // Compare Bottom Word
9: gg_code[47:40] <= file_input; // Compare Bottom Word
10: gg_code[55:48] <= file_input; // Compare top Word
11: gg_code[63:56] <= file_input; // Compare top Word
12: gg_code[7:0] <= file_input; // Replace Bottom Word
13: gg_code[15:8] <= file_input; // Replace Bottom Word
14: gg_code[23:16] <= file_input; // Replace Top Word
15: begin
gg_code[31:24] <= file_input; // Replace Top Word
gg_code[128] <= 1'b1; // Clock it in
end
endcase
end
end
///////////////////////// STATE SAVE/LOAD /////////////////////////////
reg bk_ena = 0;
reg old_downloading = 0;
reg [1:0] max_diskside = 2'd3;
always @(posedge clk) begin
old_downloading <= downloading;
if(~old_downloading & downloading) bk_ena <= 0;
//Save file always mounted in the end of downloading state.
if(downloading && img_mounted && !img_readonly) bk_ena <= 1;
if(~bk_ena && loader_write_triggered) max_diskside <= loader_addr_mem[17:16];
end
wire bk_load = status[6];
wire bk_save = status[7] | (bk_pending & OSD_STATUS && ~status[50]);
reg bk_loading = 0;
reg bk_loading_req = 0;
reg bk_request = 0;
wire bk_busy = (bk_state == S_COPY);
reg fds_busy;
wire [10:0] save_sz = fds ? rom_sz[17:9] :
bram_en ? 11'd3 :
mapper_has_flashsaves ? (11'd32 << mapper_flags[10:8]) : // 512KB max atm
(prg_nvram == 4'd7) ? 11'd15 : 11'd63;
typedef enum bit [1:0] { S_IDLE, S_COPY } mystate;
mystate bk_state = S_IDLE;
always @(posedge clk) begin : save_block
reg old_load = 0, old_save = 0, old_ack;
reg old_downloading = 0;
old_downloading <= downloading;
old_load <= bk_load & bk_ena;
old_save <= bk_save & bk_ena;
old_ack <= sd_ack;
fds_busy <= (bk_state != S_IDLE) || bk_request;
if(~old_ack & sd_ack) {sd_rd, sd_wr} <= 0;
if (downloading) begin
bk_state <= S_IDLE;
bk_request <= 0;
end else if(bk_state == S_IDLE) begin
if((~old_load & bk_load) | (~old_save & bk_save)) begin
bk_loading <= bk_load;
bk_request <= 1;
end
if(old_downloading & ~downloading & |img_size & bk_ena) begin
bk_loading <= 1;
bk_request <= 1;
end
if (bk_request && !loader_busy) begin
bk_request <= 0;
bk_state <= S_COPY;
sd_lba <= 0;
sd_rd <= bk_loading;
sd_wr <= ~bk_loading;
end
end else begin
if(old_ack & ~sd_ack) begin
if(sd_lba == save_sz) begin
bk_loading <= 0;
bk_state <= S_IDLE;
end else begin
sd_lba <= sd_lba + 1'd1;
sd_rd <= bk_loading;
sd_wr <= ~bk_loading;
end
end
end
end
///////////////////////////// savestates /////////////////////////////////
wire [63:0] ss_dout, ss_din;
wire [27:2] ss_addr;
wire [7:0] ss_be;
wire ss_rnw, ss_req, ss_ack;
wire [24:0] Savestate_SDRAMAddr;
wire Savestate_SDRAMRdEn;
wire Savestate_SDRAMWrEn;
wire [7:0] Savestate_SDRAMWriteData;
wire [7:0] Savestate_SDRAMReadData;
wire [63:0] SaveStateBus_Din;
wire [9:0] SaveStateBus_Adr;
wire SaveStateBus_wren;
wire SaveStateBus_rst;
wire [63:0] SaveStateBus_Dout;
wire savestate_load;
wire [15:0] sdram_ss_in = SS_Ext[15:0];
wire [15:0] sdram_ss_out;
wire [63:0] SS_Ext;
wire [63:0] SS_Ext_BACK;
eReg_SavestateV #(SSREG_INDEX_EXT, SSREG_DEFAULT_EXT) iREG_SAVESTATE_Ext (clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_Dout, SS_Ext_BACK, SS_Ext);
assign SS_Ext_BACK[15: 0] = sdram_ss_out;
assign SS_Ext_BACK[63:16] = 48'b0; // free to be used
assign DDRAM_CLK = clk;
ddram ddram
(
.*,
.ch1_addr({ss_addr, 1'b0}),
.ch1_din(ss_din),
.ch1_dout(ss_dout),
.ch1_req(ss_req),
.ch1_rnw(ss_rnw),
.ch1_be(ss_be),
.ch1_ready(ss_ack)
);
// saving with keyboard/OSD/gamepad
wire [1:0] ss_slot;
wire [7:0] ss_info;
wire ss_save, ss_load, ss_info_req;
wire mapper_has_savestate;
wire statusUpdate;
//Ignore F1-F4 when famicom keyboard is enabled
wire skip_ps2 = (ps2_key[7:0] == 'h04) || (ps2_key[7:0] == 'h05) || (ps2_key[7:0] == 'h06) || (ps2_key[7:0] == 'h0C);
wire [10:0] ps2_key_adjust = skip_ps2 && fkeyb ? 11'h0 : ps2_key[10:0];
savestate_ui savestate_ui
(
.clk (clk ),
.ps2_key (ps2_key_adjust),
.allow_ss (rom_loaded & mapper_has_savestate),
.joySS (joyA_unmod[23]),
.joyRight (joyA_unmod[0] ),
.joyLeft (joyA_unmod[1] ),
.joyDown (joyA_unmod[2] ),
.joyUp (joyA_unmod[3] ),
.joyStart (joyA_unmod[7] ),
.status_slot (status[46:45] ),
.OSD_saveload (status[43:42] ),
.ss_save (ss_save ),
.ss_load (ss_load ),
.ss_info_req (ss_info_req ),
.ss_info (ss_info ),
.statusUpdate (statusUpdate ),
.selected_slot (ss_slot )
);
defparam savestate_ui.INFO_TIMEOUT_BITS = 25;
endmodule
/////////////////////////////////////////////////////////////////////////
// Module reads bytes and writes to proper address in ram.
// Done is asserted when the whole game is loaded.
// This parses iNES headers too.
module GameLoader
(
input clk,
input reset,
input downloading,
input [7:0] filetype,
input is_bios,
input [7:0] indata,
input indata_clk,
input clearval,
input [7:0] cleardata,
output reg [24:0] mem_addr,
output [7:0] mem_data,
output mem_write,
output [63:0] mapper_flags,
output reg [9:0] prg_mask,
output reg [9:0] chr_mask,
output reg busy,
output reg done,
output reg error,
output reg rom_loaded
);
initial begin
rom_loaded <= 0;
end
reg [7:0] prgsize;
reg [3:0] ctr;
reg [7:0] ines[0:15]; // 16 bytes of iNES header
reg [24:0] bytes_left;
wire [7:0] prgrom = ines[4]; // Number of 16384 byte program ROM pages
wire [7:0] chrrom = ines[5]; // Number of 8192 byte character ROM pages (0 indicates CHR RAM)
wire [3:0] chrram = ines[11][3:0]; // NES 2.0 CHR-RAM size shift count (64 << count)
wire has_chr_ram = ~is_nes20 ? (chrrom == 0) : |chrram;
assign mem_data = (state == S_CLEARRAM || (~copybios && state == S_COPYBIOS)) ? (clearval && ~(state == S_COPYBIOS) ? (mapper == 8'd232 ? 8'h00 : cleardata) : 8'h00) : indata;
assign mem_write = (((bytes_left != 0) && (state == S_LOADPRG || state == S_LOADCHR || state == S_LOADEXTRA)
|| (downloading && (state == S_LOADHEADER || state == S_LOADFDS || state == S_LOADNSFH || state == S_LOADNSFD))) && indata_clk)
|| ((bytes_left != 0) && ((state == S_CLEARRAM) || (state == S_COPYBIOS) || (state == S_COPYPLAY)) && clearclk == 4'h2);
// detect iNES2.0 compliant header
wire is_nes20 = (ines[7][3:2] == 2'b10);
wire is_nes20_prg = (is_nes20 && (ines[9][3:0] == 4'hF));
wire is_nes20_chr = (is_nes20 && (ines[9][7:4] == 4'hF));
// NES 2.0 PRG & CHR sizes
reg [21:0] prg_size2, chr_size2, chr_ram_size;
function [9:0] mask;
input [10:0] size;
integer i;
begin
for (i=0;i<10;i=i+1) mask[i] = ( size > (11'd1 << i) );
end
endfunction
always @(posedge clk) begin
// PRG
// ines[4][1:0]: Multiplier, actual value is MM*2+1 (1,3,5,7)
// ines[4][7:2]: Exponent (2^E), 0-63
prg_size2 <= is_nes20_prg ? ({19'b0, ines[4][1:0], 1'b1} << ines[4][7:2]) : {prgrom, 14'b0};
prg_mask <= mask(prg_size2[21:11]);
// CHR
chr_size2 <= is_nes20_chr ? ({19'b0, ines[5][1:0], 1'b1} << ines[5][7:2]) : {1'b0, chrrom, 13'b0};
chr_ram_size <= is_nes20 ? (22'd64 << chrram) : 22'h2000;
chr_mask <= mask(|chr_size2 ? chr_size2[21:11] : chr_ram_size[21:11]);
end
wire [2:0] prg_size = prgrom <= 1 ? 3'd0 : // 16KB
prgrom <= 2 ? 3'd1 : // 32KB
prgrom <= 4 ? 3'd2 : // 64KB
prgrom <= 8 ? 3'd3 : // 128KB
prgrom <= 16 ? 3'd4 : // 256KB
prgrom <= 32 ? 3'd5 : // 512KB
prgrom <= 64 ? 3'd6 : 3'd7;// 1MB/2MB
wire [2:0] chr_size = chrrom <= 1 ? 3'd0 : // 8KB
chrrom <= 2 ? 3'd1 : // 16KB
chrrom <= 4 ? 3'd2 : // 32KB
chrrom <= 8 ? 3'd3 : // 64KB
chrrom <= 16 ? 3'd4 : // 128KB
chrrom <= 32 ? 3'd5 : // 256KB
chrrom <= 64 ? 3'd6 : 3'd7;// 512KB/1MB
// differentiate dirty iNES1.0 headers from proper iNES2.0 ones
wire is_dirty = !is_nes20 && ((ines[9][7:1] != 0)
|| (ines[10] != 0)
|| (ines[11] != 0)
|| (ines[12] != 0)
|| (ines[13] != 0)
|| (ines[14] != 0)
|| (ines[15] != 0));
// Read the mapper number
wire [7:0] mapper = {is_dirty ? 4'b0000 : ines[7][7:4], ines[6][7:4]};
wire [7:0] ines2mapper = {is_nes20 ? ines[8] : 8'h00};
wire [3:0] prgram = {is_nes20 ? ines[10][3:0] : 4'h0};
wire [3:0] prg_nvram = (is_nes20 ? ines[10][7:4] : 4'h0);
wire piano = is_nes20 && (ines[15][5:0] == 6'h19);
wire has_saves = ines[6][1];
assign mapper_flags[63:38] = 'd0;
assign mapper_flags[37:36] = is_nes20 ? ines[12][1:0] : 2'b00;
assign mapper_flags[35] = is_nes20;
assign mapper_flags[34:31] = prg_nvram; //NES 2.0 Save RAM shift size (64 << size)
assign mapper_flags[30] = piano;
assign mapper_flags[29:26] = prgram; //NES 2.0 PRG RAM shift size (64 << size)
assign mapper_flags[25] = has_saves;
assign mapper_flags[24:17] = ines2mapper; //NES 2.0 submapper
assign mapper_flags[16] = ines[6][3]; // 4 screen mode
assign mapper_flags[15] = has_chr_ram;
assign mapper_flags[14] = ines[6][0]; // mirroring
assign mapper_flags[13:11] = chr_size;
assign mapper_flags[10:8] = prg_size;
assign mapper_flags[7:0] = mapper;
reg [3:0] clearclk; //Wait for SDRAM
reg copybios;
reg cleardone;
typedef enum bit [3:0] { S_LOADHEADER, S_LOADPRG, S_LOADCHR, S_LOADEXTRA, S_LOADFDS, S_ERROR, S_CLEARRAM, S_COPYBIOS, S_LOADNSFH, S_LOADNSFD, S_COPYPLAY, S_DONE } mystate;
mystate state;
wire type_bios = filetype[0];
wire type_nes = filetype[1];
wire type_fds = filetype[2];
wire type_nsf = filetype[3];
always @(posedge clk) begin
if (downloading && (type_fds || type_nes || type_nsf))
rom_loaded <= 1;
if (reset) begin
state <= S_LOADHEADER;
busy <= 0;
done <= 0;
ctr <= 0;
mem_addr <= type_fds ? 25'b0_0011_1100_0000_0000_0001_0000 :
type_nsf ? 25'b0_0000_0000_0000_0001_0000_0000 // Address for NSF Header (0x80 bytes)
: 25'b0_0000_0000_0000_0000_0000_0000; // Address for FDS : BIOS/PRG
copybios <= 0;
cleardone <= 0;
end else begin
case(state)
// Read 16 bytes of ines header
S_LOADHEADER:
if (indata_clk) begin
error <= 0;
ctr <= ctr + 1'd1;
mem_addr <= mem_addr + 1'd1;
ines[ctr] <= indata;
bytes_left <= prg_size2;
if (ctr == 4'b1111) begin
// Check the 'NES' header. Also, we don't support trainers.
busy <= 1;
if ((ines[0] == 8'h4E) && (ines[1] == 8'h45) && (ines[2] == 8'h53) && (ines[3] == 8'h1A) && !ines[6][2]) begin
mem_addr <= 0; // Address for PRG
state <= S_LOADPRG;
//FDS
end else if ((ines[0] == 8'h46) && (ines[1] == 8'h44) && (ines[2] == 8'h53) && (ines[3] == 8'h1A)) begin
mem_addr <= 25'b0_0011_1100_0000_0000_0001_0000; // Address for FDS skip Header
state <= S_LOADFDS;
bytes_left <= 21'b1;
end else if (type_bios) begin // Bios
state <= S_LOADFDS;
mem_addr <= 25'b0_0000_0000_0000_0000_0001_0000; // Address for BIOS skip Header
bytes_left <= 21'b1;
end else if(type_fds) begin // FDS
state <= S_LOADFDS;
mem_addr <= 25'b0_0011_1100_0000_0000_0010_0000; // Address for FDS no Header
bytes_left <= 21'b1;
end else if(type_nsf) begin // NFS
state <= S_LOADNSFH;
//mem_addr <= 22'b00_0000_0000_0001_0001_0000; // Just keep copying
bytes_left <= 21'h70; // Rest of header
end else begin
state <= S_ERROR;
end
end
end
S_LOADPRG, S_LOADCHR: begin // Read the next |bytes_left| bytes into |mem_addr|
// Abort when downloading stops and there are bytes left (invalid header)
if (downloading && bytes_left != 0) begin
if (indata_clk) begin
bytes_left <= bytes_left - 1'd1;
mem_addr <= mem_addr + 1'd1;
end
end else if (state == S_LOADPRG) begin
state <= S_LOADCHR;
mem_addr <= 25'b0_0010_0000_0000_0000_0000_0000; // Address for CHR
bytes_left <= chr_size2;
end else if (state == S_LOADCHR) begin
state <= S_LOADEXTRA;
mem_addr <= 25'b1_0000_0000_0000_0000_0000_0000; // Address for Extra
//Replace with calculation based on file size requires actual file size?
bytes_left <= ({ines2mapper[1:0],mapper} == 10'd413) ? 25'b0_1000_0000_0000_0000_0000_0000 : 25'd0;
end
end
S_LOADEXTRA: begin
if (downloading && bytes_left != 0) begin
if (indata_clk) begin
bytes_left <= bytes_left - 1'd1;
mem_addr <= mem_addr + 1'd1;
end
end else if (mapper == 8'd232) begin
mem_addr <= 25'b0_0011_1000_0000_0111_1111_1110; // Quattro - Clear these two RAM address to restart game menu
bytes_left <= 21'h2;
state <= S_CLEARRAM;
clearclk <= 4'h0;
cleardone <= 1;
end else if (clearval) begin
mem_addr <= 25'b0_0011_0000_0000_0000_0000_0000;
bytes_left <= 21'hF_FFFF;
state <= S_CLEARRAM;
clearclk <= 4'h0;
cleardone <= 1;
end else begin
done <= 1;
busy <= 0;
end
end
S_ERROR: begin
done <= 1;
error <= 1;
busy <= 0;
end
S_LOADFDS: begin // Read the next |bytes_left| bytes into |mem_addr|
if (downloading) begin
if (indata_clk) begin
mem_addr <= mem_addr + 1'd1;
end
end else begin
// mem_addr <= 25'b0_0011_1000_0000_0000_0000_0000;
// bytes_left <= 21'h800;
mem_addr <= 25'b0_0011_1000_0000_0001_0000_0010; // FDS - Clear these two RAM addresses to restart BIOS
bytes_left <= 21'h2;
ines[4] <= 8'h80;//no masking
ines[5] <= 8'h00;//0x2000
ines[6] <= 8'h40;
ines[7] <= 8'h10;
ines[8] <= 8'h00;
ines[9] <= 8'h00;
ines[10] <= 8'h00;
ines[11] <= 8'h00;
ines[12] <= 8'h00;
ines[13] <= 8'h00;
ines[14] <= 8'h00;
ines[15] <= 8'h00;
state <= S_CLEARRAM;
clearclk <= 4'h0;
copybios <= ~is_bios; // Don't copybios for bootrom0
end
end
S_CLEARRAM: begin // Read the next |bytes_left| bytes into |mem_addr|
clearclk <= clearclk + 4'h1;
if (bytes_left != 21'h0) begin
if (clearclk == 4'hF) begin
bytes_left <= bytes_left - 1'd1;
mem_addr <= mem_addr + 1'd1;
end
end else if (!cleardone) begin
mem_addr <= 25'b0_0000_0000_0000_0000_0000_0000;
bytes_left <= 21'h2000;
state <= S_COPYBIOS;
clearclk <= 4'h0;
end else begin
state <= S_DONE;
end
end
S_COPYBIOS: begin // Read the next |bytes_left| bytes into |mem_addr|
clearclk <= clearclk + 4'h1;
if (bytes_left != 21'h0) begin
if (clearclk == 4'hF) begin
bytes_left <= bytes_left - 1'd1;
mem_addr <= mem_addr + 1'd1;
end
end else begin
state <= S_DONE;
end
end
S_LOADNSFH: begin // Read the next |bytes_left| bytes into |mem_addr|
if (bytes_left != 0) begin
if (indata_clk) begin
bytes_left <= bytes_left - 1'd1;
mem_addr <= mem_addr + 1'd1;
end
end else begin
state <= S_LOADNSFD;
//mem_addr <= {25'b0_0001_0000_0000_0000_0000_0000; // Address for NSF Data
mem_addr <= {13'b0_0001_0000_0000,ines[9][3:0],ines[8]};//_0000_0000_0000; // Address for NSF Data
bytes_left <= 21'b1;
end
end
S_LOADNSFD: begin // Read the next |bytes_left| bytes into |mem_addr|
if (downloading) begin
if (indata_clk) begin
mem_addr <= mem_addr + 1'd1;
end
end else begin
mem_addr <= 25'b0_0000_0000_0000_0001_1000_0000; // Address for NSF Player (0x180)
bytes_left <= 21'h0E80;
ines[4] <= 8'h80;//no masking
ines[5] <= 8'h00;//no CHR ROM
ines[6] <= 8'hF0;//Use Mapper 31
ines[7] <= 8'h18;//Use NES 2.0
ines[8] <= 8'hF0;//Use Submapper 15
ines[9] <= 8'h00;
ines[10] <= 8'h00;
ines[11] <= 8'h07;//NES 2.0 8KB CHR RAM
ines[12] <= 8'h00;
ines[13] <= 8'h00;
ines[14] <= 8'h00;
ines[15] <= 8'h19;//miracle piano; controllers swapped
state <= S_COPYPLAY;
clearclk <= 4'h0;
end
end
S_COPYPLAY: begin // Read the next |bytes_left| bytes into |mem_addr|
clearclk <= clearclk + 4'h1;
if (bytes_left != 21'h0) begin
if (clearclk == 4'hF) begin
bytes_left <= bytes_left - 1'd1;
mem_addr <= mem_addr + 1'd1;
end
end else begin
state <= S_DONE;
end
end
S_DONE: begin // Read the next |bytes_left| bytes into |mem_addr|
done <= 1;
busy <= 0;
end
endcase
end
end
endmodule