diff --git a/cores/nes/mist/NES_mist.sv b/cores/nes/mist/NES_mist.sv index 784608d..9f7a400 100644 --- a/cores/nes/mist/NES_mist.sv +++ b/cores/nes/mist/NES_mist.sv @@ -3,105 +3,6 @@ `timescale 1ns / 1ps - -// 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 [7:0] indata, input indata_clk, input invert_mirroring, - output reg [21:0] mem_addr, output [7:0] mem_data, output mem_write, - output [31:0] mapper_flags, - output reg done, - output reg error); - reg [1:0] state = 0; - reg [7:0] prgsize; - reg [3:0] ctr; - reg [7:0] ines[0:15]; // 16 bytes of iNES header - reg [21: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 has_chr_ram = (chrrom == 0); - assign mem_data = indata; - assign mem_write = (bytes_left != 0) && (state == 1 || state == 2) && indata_clk; - - wire [2:0] prg_size = prgrom <= 1 ? 0 : // 16KB - prgrom <= 2 ? 1 : // 32KB - prgrom <= 4 ? 2 : // 64KB - prgrom <= 8 ? 3 : // 128KB - prgrom <= 16 ? 4 : // 256KB - prgrom <= 32 ? 5 : // 512KB - prgrom <= 64 ? 6 : 7; // 1MB/2MB - - wire [2:0] chr_size = chrrom <= 1 ? 0 : // 8KB - chrrom <= 2 ? 1 : // 16KB - chrrom <= 4 ? 2 : // 32KB - chrrom <= 8 ? 3 : // 64KB - chrrom <= 16 ? 4 : // 128KB - chrrom <= 32 ? 5 : // 256KB - chrrom <= 64 ? 6 : 7; // 512KB/1MB - - // detect iNES2.0 compliant header - wire is_nes20 = (ines[7][3:2] == 2'b10); - // differentiate dirty iNES1.0 headers from proper iNES2.0 ones - wire is_dirty = !is_nes20 && ((ines[8] != 0) - || (ines[9] != 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]}; - - // ines[6][0] is mirroring - // ines[6][3] is 4 screen mode - assign mapper_flags = {15'b0, ines[6][3], has_chr_ram, ines[6][0] ^ invert_mirroring, chr_size, prg_size, mapper}; - - always @(posedge clk) begin - if (reset) begin - state <= 0; - done <= 0; - ctr <= 0; - mem_addr <= 0; // Address for PRG - end else begin - case(state) - // Read 16 bytes of ines header - 0: if (indata_clk) begin - error <= 0; - ctr <= ctr + 1'd1; - ines[ctr] <= indata; - bytes_left <= {prgrom, 14'b0}; - if (ctr == 4'b1111) - // Check the 'NES' header. Also, we don't support trainers. - state <= (ines[0] == 8'h4E) && (ines[1] == 8'h45) && (ines[2] == 8'h53) && (ines[3] == 8'h1A) && !ines[6][2] ? 1 : 3; - end - 1, 2: 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 if (state == 1) begin - state <= 2; - mem_addr <= 22'b10_0000_0000_0000_0000_0000; // Address for CHR - bytes_left <= {1'b0, chrrom, 13'b0}; - end else if (state == 2) begin - done <= 1; - end - end - 3: begin - done <= 1; - error <= 1; - end - endcase - end - end -endmodule - - module NES_mist( // clock input input [1:0] CLOCK_27, // 27 MHz @@ -289,9 +190,28 @@ wire [7:0] nes_joy_B = { joyB[0], joyB[1], joyB[2], joyB[3], joyB[7], joyB[6], j wire [31:0] loader_flags; reg [31:0] mapper_flags; wire loader_done, loader_fail; - GameLoader loader(clk, loader_reset, loader_input, loader_clk, mirroring_osd, - loader_addr, loader_write_data, loader_write, - loader_flags, loader_done, loader_fail); + wire type_bios, type_nes = 1, type_fds, type_nsf; + +GameLoader loader +( + .clk ( clk ), + .reset ( loader_reset ), + .downloading ( downloading ), + .filetype ( {4'b0000, type_nsf, type_fds, type_nes, type_bios} ), + .is_bios ( is_bios ), + .indata ( loader_input ), + .indata_clk ( loader_clk ), + .invert_mirroring ( mirroring_osd ), + .mem_addr ( loader_addr ), + .mem_data ( loader_write_data ), + .mem_write ( loader_write ), + .bios_download ( bios_download ), + .mapper_flags ( loader_flags ), + .busy ( loader_busy ), + .done ( loader_done ), + .error ( loader_fail ), + .rom_loaded ( rom_loaded ) +); always @(posedge clk) if (loader_done) diff --git a/cores/nes/nes.qsf b/cores/nes/nes.qsf index 949c594..aaa261f 100644 --- a/cores/nes/nes.qsf +++ b/cores/nes/nes.qsf @@ -299,6 +299,7 @@ set_global_assignment -name VERILOG_FILE mist/sigma_delta_dac.v set_global_assignment -name VERILOG_FILE mist/sdram.v set_global_assignment -name VERILOG_FILE mist/clk.v set_global_assignment -name SDC_FILE mist/constraints.sdc +set_global_assignment -name SYSTEMVERILOG_FILE src/GameLoader.sv 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 diff --git a/cores/nes/src/GameLoader.sv b/cores/nes/src/GameLoader.sv new file mode 100644 index 0000000..632aec5 --- /dev/null +++ b/cores/nes/src/GameLoader.sv @@ -0,0 +1,276 @@ +///////////////////////////////////////////////////////////////////////// + +// 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 invert_mirroring, + output reg [21:0] mem_addr, + output [7:0] mem_data, + output mem_write, + output reg bios_download, + output [31:0] mapper_flags, + 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 [21: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 has_chr_ram = (chrrom == 0); +assign mem_data = (state == S_CLEARRAM || (~copybios && state == S_COPYBIOS)) ? 8'h00 : indata; +assign mem_write = (((bytes_left != 0) && (state == S_LOADPRG || state == S_LOADCHR) + || (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); + +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 + +// detect iNES2.0 compliant header +wire is_nes20 = (ines[7][3:2] == 2'b10); +// 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 piano = is_nes20 && (ines[15][5:0] == 6'h19); +wire has_saves = ines[6][1]; + +// ines[6][0] is mirroring +// ines[6][3] is 4 screen mode +// ines[8][7:4] is NES 2.0 submapper +// ines[10][3:0] is NES 2.0 PRG RAM shift size (64 << size) +assign mapper_flags = {1'b0, piano, prgram, has_saves, ines2mapper, ines[6][3], has_chr_ram, ines[6][0] ^ invert_mirroring, chr_size, prg_size, mapper}; + +reg [3:0] clearclk; //Wait for SDRAM +reg copybios; + +typedef enum bit [3:0] { S_LOADHEADER, S_LOADPRG, S_LOADCHR, 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 ? 22'b11_1100_0000_0000_0001_0000 : + type_nsf ? 22'b00_0000_0000_0001_0000_0000 // Address for NSF Header (0x80 bytes) + : 22'b00_0000_0000_0000_0000_0000; // Address for FDS : BIOS/PRG + bios_download <= 0; + copybios <= 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 <= {prgrom, 14'b0}; + 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 <= 22'b11_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 <= 22'b00_0000_0000_0000_0001_0000; // Address for BIOS skip Header + bytes_left <= 21'b1; + bios_download <= 1; + end else if(type_fds) begin // FDS + state <= S_LOADFDS; + mem_addr <= 22'b11_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| + if (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 <= 22'b10_0000_0000_0000_0000_0000; // Address for CHR + bytes_left <= {1'b0, chrrom, 13'b0}; + end else if (state == S_LOADCHR) 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 <= 22'b11_1000_0000_0000_0000_0000; +// bytes_left <= 21'h800; + mem_addr <= 22'b11_1000_0000_0001_0000_0010; // FDS - Clear these two RAM addresses to restart BIOS + bytes_left <= 21'h2; + ines[4] <= 8'hFF;//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 begin + mem_addr <= 22'b00_0000_0000_0000_0000_0000; + bytes_left <= 21'h2000; + state <= S_COPYBIOS; + clearclk <= 4'h0; + 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 <= {22'b01_0000_0000_0000_0000_0000; // Address for NSF Data + mem_addr <= {10'b01_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 <= 22'b00_0000_0000_0001_1000_0000; // Address for NSF Player (0x180) + bytes_left <= 21'h0E80; + ines[4] <= 8'hFF;//no masking + ines[5] <= 8'h00;//0x2000 + 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'h00; + ines[12] <= 8'h00; + ines[13] <= 8'h00; + ines[14] <= 8'h00; + ines[15] <= 8'h00; + 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; + bios_download <= 0; + end + endcase + end +end +endmodule