diff --git a/cores/nes/mist/NES_mist.v b/cores/nes/mist/NES_mist.sv similarity index 79% rename from cores/nes/mist/NES_mist.v rename to cores/nes/mist/NES_mist.sv index b7453f1..8b2a23d 100644 --- a/cores/nes/mist/NES_mist.v +++ b/cores/nes/mist/NES_mist.sv @@ -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 ( diff --git a/cores/nes/mist/sdram.v b/cores/nes/mist/sdram.v index 8d05ee4..ed5ed87 100644 --- a/cores/nes/mist/sdram.v +++ b/cores/nes/mist/sdram.v @@ -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; diff --git a/cores/nes/nes.qsf b/cores/nes/nes.qsf index 6ba0bf9..32e5218 100644 --- a/cores/nes/nes.qsf +++ b/cores/nes/nes.qsf @@ -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 \ No newline at end of file diff --git a/cores/nes/src/EEPROM_24C0x.sv b/cores/nes/src/EEPROM_24C0x.sv new file mode 100644 index 0000000..98a9d43 --- /dev/null +++ b/cores/nes/src/EEPROM_24C0x.sv @@ -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 + diff --git a/cores/nes/src/MicroCode.v b/cores/nes/src/MicroCode.v deleted file mode 100644 index bec5d10..0000000 --- a/cores/nes/src/MicroCode.v +++ /dev/null @@ -1,2374 +0,0 @@ -module MicroCodeTableInner(input clk, input ce, input reset, input [7:0] IR, input [2:0] State, output reg [8:0] M); - reg [8:0] L[0:2047]; - initial begin -L[0] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2] = 9'b00_10_10100; // ['PCH->[SP--]', 'PCL->[SP--],', 'PCH->[SP--](KEEPAC)', 'PCL->[SP--](KEEPAC)'] -L[3] = 9'b00_10_10100; // ['PCH->[SP--]', 'PCL->[SP--],', 'PCH->[SP--](KEEPAC)', 'PCL->[SP--](KEEPAC)'] -L[4] = 9'b00_10_10101; // P->[SP--] -L[5] = 9'b00_11_00011; // [VECT]->T -L[6] = 9'b10_11_10011; // [VECT]:T->PC -L[7] = 9'bxx_xx_xxxxx; // [] -L[8] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[9] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[10] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[11] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[12] = 9'b00_01_01011; // [AX]->AH,T->AL -L[13] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[14] = 9'bxx_xx_xxxxx; // [] -L[15] = 9'bxx_xx_xxxxx; // [] -L[16] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[17] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[18] = 9'bxx_xx_xxxxx; // [] -L[19] = 9'bxx_xx_xxxxx; // [] -L[20] = 9'bxx_xx_xxxxx; // [] -L[21] = 9'bxx_xx_xxxxx; // [] -L[22] = 9'bxx_xx_xxxxx; // [] -L[23] = 9'bxx_xx_xxxxx; // [] -L[24] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[25] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[26] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[27] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[28] = 9'b00_01_01011; // [AX]->AH,T->AL -L[29] = 9'b00_01_00011; // [AX]->T -L[30] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[31] = 9'b10_01_00101; // T->[AX] -L[32] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[33] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[34] = 9'bxx_xx_xxxxx; // [] -L[35] = 9'bxx_xx_xxxxx; // [] -L[36] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[37] = 9'bxx_xx_xxxxx; // [] -L[38] = 9'bxx_xx_xxxxx; // [] -L[39] = 9'bxx_xx_xxxxx; // [] -L[40] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[41] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[42] = 9'bxx_xx_xxxxx; // [] -L[43] = 9'bxx_xx_xxxxx; // [] -L[44] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[45] = 9'bxx_xx_xxxxx; // [] -L[46] = 9'bxx_xx_xxxxx; // [] -L[47] = 9'bxx_xx_xxxxx; // [] -L[48] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[49] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[50] = 9'bxx_xx_xxxxx; // [] -L[51] = 9'bxx_xx_xxxxx; // [] -L[52] = 9'b00_01_00011; // [AX]->T -L[53] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[54] = 9'b10_01_00101; // T->[AX] -L[55] = 9'bxx_xx_xxxxx; // [] -L[56] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[57] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[58] = 9'bxx_xx_xxxxx; // [] -L[59] = 9'bxx_xx_xxxxx; // [] -L[60] = 9'b00_01_00011; // [AX]->T -L[61] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[62] = 9'b10_01_00101; // T->[AX] -L[63] = 9'bxx_xx_xxxxx; // [] -L[64] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[65] = 9'b00_00_00011; // [PC]-> -L[66] = 9'b10_10_01111; // P->[SP--] -L[67] = 9'bxx_xx_xxxxx; // [] -L[68] = 9'bxx_xx_xxxxx; // [] -L[69] = 9'bxx_xx_xxxxx; // [] -L[70] = 9'bxx_xx_xxxxx; // [] -L[71] = 9'bxx_xx_xxxxx; // [] -L[72] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[73] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[74] = 9'bxx_xx_xxxxx; // [] -L[75] = 9'bxx_xx_xxxxx; // [] -L[76] = 9'bxx_xx_xxxxx; // [] -L[77] = 9'bxx_xx_xxxxx; // [] -L[78] = 9'bxx_xx_xxxxx; // [] -L[79] = 9'bxx_xx_xxxxx; // [] -L[80] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[81] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[82] = 9'bxx_xx_xxxxx; // [] -L[83] = 9'bxx_xx_xxxxx; // [] -L[84] = 9'bxx_xx_xxxxx; // [] -L[85] = 9'bxx_xx_xxxxx; // [] -L[86] = 9'bxx_xx_xxxxx; // [] -L[87] = 9'bxx_xx_xxxxx; // [] -L[88] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[89] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[90] = 9'bxx_xx_xxxxx; // [] -L[91] = 9'bxx_xx_xxxxx; // [] -L[92] = 9'bxx_xx_xxxxx; // [] -L[93] = 9'bxx_xx_xxxxx; // [] -L[94] = 9'bxx_xx_xxxxx; // [] -L[95] = 9'bxx_xx_xxxxx; // [] -L[96] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[97] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[98] = 9'b11_00_00001; // [PC++]->AH -L[99] = 9'bxx_xx_xxxxx; // [] -L[100] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[101] = 9'bxx_xx_xxxxx; // [] -L[102] = 9'bxx_xx_xxxxx; // [] -L[103] = 9'bxx_xx_xxxxx; // [] -L[104] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[105] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[106] = 9'b11_00_00001; // [PC++]->AH -L[107] = 9'bxx_xx_xxxxx; // [] -L[108] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[109] = 9'bxx_xx_xxxxx; // [] -L[110] = 9'bxx_xx_xxxxx; // [] -L[111] = 9'bxx_xx_xxxxx; // [] -L[112] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[113] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[114] = 9'b11_00_00001; // [PC++]->AH -L[115] = 9'bxx_xx_xxxxx; // [] -L[116] = 9'b00_01_00011; // [AX]->T -L[117] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[118] = 9'b10_01_00101; // T->[AX] -L[119] = 9'bxx_xx_xxxxx; // [] -L[120] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[121] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[122] = 9'b11_00_00001; // [PC++]->AH -L[123] = 9'bxx_xx_xxxxx; // [] -L[124] = 9'b00_01_00011; // [AX]->T -L[125] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[126] = 9'b10_01_00101; // T->[AX] -L[127] = 9'bxx_xx_xxxxx; // [] -L[128] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[129] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[130] = 9'b11_00_10001; // PC+T->PC -L[131] = 9'bxx_xx_xxxxx; // [] -L[132] = 9'b10_00_00011; // ['NO-OP', ''] -L[133] = 9'bxx_xx_xxxxx; // [] -L[134] = 9'bxx_xx_xxxxx; // [] -L[135] = 9'bxx_xx_xxxxx; // [] -L[136] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[137] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[138] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[139] = 9'b01_01_01100; // [AX]->AH,T+Y->AL -L[140] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[141] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[142] = 9'bxx_xx_xxxxx; // [] -L[143] = 9'bxx_xx_xxxxx; // [] -L[144] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[145] = 9'b10_00_00011; // ['NO-OP', ''] -L[146] = 9'bxx_xx_xxxxx; // [] -L[147] = 9'bxx_xx_xxxxx; // [] -L[148] = 9'bxx_xx_xxxxx; // [] -L[149] = 9'bxx_xx_xxxxx; // [] -L[150] = 9'bxx_xx_xxxxx; // [] -L[151] = 9'bxx_xx_xxxxx; // [] -L[152] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[153] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[154] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[155] = 9'b00_01_01100; // [AX]->AH,T+Y->AL -L[156] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[157] = 9'b00_01_00011; // [AX]->T -L[158] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[159] = 9'b10_01_00101; // T->[AX] -L[160] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[161] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[162] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[163] = 9'bxx_xx_xxxxx; // [] -L[164] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[165] = 9'bxx_xx_xxxxx; // [] -L[166] = 9'bxx_xx_xxxxx; // [] -L[167] = 9'bxx_xx_xxxxx; // [] -L[168] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[169] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[170] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[171] = 9'bxx_xx_xxxxx; // [] -L[172] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[173] = 9'bxx_xx_xxxxx; // [] -L[174] = 9'bxx_xx_xxxxx; // [] -L[175] = 9'bxx_xx_xxxxx; // [] -L[176] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[177] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[178] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[179] = 9'bxx_xx_xxxxx; // [] -L[180] = 9'b00_01_00011; // [AX]->T -L[181] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[182] = 9'b10_01_00101; // T->[AX] -L[183] = 9'bxx_xx_xxxxx; // [] -L[184] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[185] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[186] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[187] = 9'bxx_xx_xxxxx; // [] -L[188] = 9'b00_01_00011; // [AX]->T -L[189] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[190] = 9'b10_01_00101; // T->[AX] -L[191] = 9'bxx_xx_xxxxx; // [] -L[192] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[193] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[194] = 9'bxx_xx_xxxxx; // [] -L[195] = 9'bxx_xx_xxxxx; // [] -L[196] = 9'bxx_xx_xxxxx; // [] -L[197] = 9'bxx_xx_xxxxx; // [] -L[198] = 9'bxx_xx_xxxxx; // [] -L[199] = 9'bxx_xx_xxxxx; // [] -L[200] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[201] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[202] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[203] = 9'bxx_xx_xxxxx; // [] -L[204] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[205] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[206] = 9'bxx_xx_xxxxx; // [] -L[207] = 9'bxx_xx_xxxxx; // [] -L[208] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[209] = 9'b10_00_00011; // ['NO-OP', ''] -L[210] = 9'bxx_xx_xxxxx; // [] -L[211] = 9'bxx_xx_xxxxx; // [] -L[212] = 9'bxx_xx_xxxxx; // [] -L[213] = 9'bxx_xx_xxxxx; // [] -L[214] = 9'bxx_xx_xxxxx; // [] -L[215] = 9'bxx_xx_xxxxx; // [] -L[216] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[217] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[218] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[219] = 9'bxx_xx_xxxxx; // [] -L[220] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[221] = 9'b00_01_00011; // [AX]->T -L[222] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[223] = 9'b10_01_00101; // T->[AX] -L[224] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[225] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[226] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[227] = 9'bxx_xx_xxxxx; // [] -L[228] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[229] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[230] = 9'bxx_xx_xxxxx; // [] -L[231] = 9'bxx_xx_xxxxx; // [] -L[232] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[233] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[234] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[235] = 9'bxx_xx_xxxxx; // [] -L[236] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[237] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[238] = 9'bxx_xx_xxxxx; // [] -L[239] = 9'bxx_xx_xxxxx; // [] -L[240] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[241] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[242] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[243] = 9'bxx_xx_xxxxx; // [] -L[244] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[245] = 9'b00_01_00011; // [AX]->T -L[246] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[247] = 9'b10_01_00101; // T->[AX] -L[248] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[249] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[250] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[251] = 9'bxx_xx_xxxxx; // [] -L[252] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[253] = 9'b00_01_00011; // [AX]->T -L[254] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[255] = 9'b10_01_00101; // T->[AX] -L[256] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[257] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[258] = 9'b00_10_10100; // ['PCH->[SP--]', 'PCL->[SP--],', 'PCH->[SP--](KEEPAC)', 'PCL->[SP--](KEEPAC)'] -L[259] = 9'b00_10_10100; // ['PCH->[SP--]', 'PCL->[SP--],', 'PCH->[SP--](KEEPAC)', 'PCL->[SP--](KEEPAC)'] -L[260] = 9'b00_10_00111; // KEEP_AC -L[261] = 9'b10_00_10011; // [PC]:T->PC -L[262] = 9'bxx_xx_xxxxx; // [] -L[263] = 9'bxx_xx_xxxxx; // [] -L[264] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[265] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[266] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[267] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[268] = 9'b00_01_01011; // [AX]->AH,T->AL -L[269] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[270] = 9'bxx_xx_xxxxx; // [] -L[271] = 9'bxx_xx_xxxxx; // [] -L[272] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[273] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[274] = 9'bxx_xx_xxxxx; // [] -L[275] = 9'bxx_xx_xxxxx; // [] -L[276] = 9'bxx_xx_xxxxx; // [] -L[277] = 9'bxx_xx_xxxxx; // [] -L[278] = 9'bxx_xx_xxxxx; // [] -L[279] = 9'bxx_xx_xxxxx; // [] -L[280] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[281] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[282] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[283] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[284] = 9'b00_01_01011; // [AX]->AH,T->AL -L[285] = 9'b00_01_00011; // [AX]->T -L[286] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[287] = 9'b10_01_00101; // T->[AX] -L[288] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[289] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[290] = 9'bxx_xx_xxxxx; // [] -L[291] = 9'bxx_xx_xxxxx; // [] -L[292] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[293] = 9'bxx_xx_xxxxx; // [] -L[294] = 9'bxx_xx_xxxxx; // [] -L[295] = 9'bxx_xx_xxxxx; // [] -L[296] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[297] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[298] = 9'bxx_xx_xxxxx; // [] -L[299] = 9'bxx_xx_xxxxx; // [] -L[300] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[301] = 9'bxx_xx_xxxxx; // [] -L[302] = 9'bxx_xx_xxxxx; // [] -L[303] = 9'bxx_xx_xxxxx; // [] -L[304] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[305] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[306] = 9'bxx_xx_xxxxx; // [] -L[307] = 9'bxx_xx_xxxxx; // [] -L[308] = 9'b00_01_00011; // [AX]->T -L[309] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[310] = 9'b10_01_00101; // T->[AX] -L[311] = 9'bxx_xx_xxxxx; // [] -L[312] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[313] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[314] = 9'bxx_xx_xxxxx; // [] -L[315] = 9'bxx_xx_xxxxx; // [] -L[316] = 9'b00_01_00011; // [AX]->T -L[317] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[318] = 9'b10_01_00101; // T->[AX] -L[319] = 9'bxx_xx_xxxxx; // [] -L[320] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[321] = 9'b00_00_00011; // [PC]-> -L[322] = 9'b00_10_10000; // ['SP++', 'SP+1->SP', '[SP]->T,SP+1->SP'] -L[323] = 9'b10_10_00010; // ['ALU([SP])->A', '[SP]->P'] -L[324] = 9'bxx_xx_xxxxx; // [] -L[325] = 9'bxx_xx_xxxxx; // [] -L[326] = 9'bxx_xx_xxxxx; // [] -L[327] = 9'bxx_xx_xxxxx; // [] -L[328] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[329] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[330] = 9'bxx_xx_xxxxx; // [] -L[331] = 9'bxx_xx_xxxxx; // [] -L[332] = 9'bxx_xx_xxxxx; // [] -L[333] = 9'bxx_xx_xxxxx; // [] -L[334] = 9'bxx_xx_xxxxx; // [] -L[335] = 9'bxx_xx_xxxxx; // [] -L[336] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[337] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[338] = 9'bxx_xx_xxxxx; // [] -L[339] = 9'bxx_xx_xxxxx; // [] -L[340] = 9'bxx_xx_xxxxx; // [] -L[341] = 9'bxx_xx_xxxxx; // [] -L[342] = 9'bxx_xx_xxxxx; // [] -L[343] = 9'bxx_xx_xxxxx; // [] -L[344] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[345] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[346] = 9'bxx_xx_xxxxx; // [] -L[347] = 9'bxx_xx_xxxxx; // [] -L[348] = 9'bxx_xx_xxxxx; // [] -L[349] = 9'bxx_xx_xxxxx; // [] -L[350] = 9'bxx_xx_xxxxx; // [] -L[351] = 9'bxx_xx_xxxxx; // [] -L[352] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[353] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[354] = 9'b11_00_00001; // [PC++]->AH -L[355] = 9'bxx_xx_xxxxx; // [] -L[356] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[357] = 9'bxx_xx_xxxxx; // [] -L[358] = 9'bxx_xx_xxxxx; // [] -L[359] = 9'bxx_xx_xxxxx; // [] -L[360] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[361] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[362] = 9'b11_00_00001; // [PC++]->AH -L[363] = 9'bxx_xx_xxxxx; // [] -L[364] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[365] = 9'bxx_xx_xxxxx; // [] -L[366] = 9'bxx_xx_xxxxx; // [] -L[367] = 9'bxx_xx_xxxxx; // [] -L[368] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[369] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[370] = 9'b11_00_00001; // [PC++]->AH -L[371] = 9'bxx_xx_xxxxx; // [] -L[372] = 9'b00_01_00011; // [AX]->T -L[373] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[374] = 9'b10_01_00101; // T->[AX] -L[375] = 9'bxx_xx_xxxxx; // [] -L[376] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[377] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[378] = 9'b11_00_00001; // [PC++]->AH -L[379] = 9'bxx_xx_xxxxx; // [] -L[380] = 9'b00_01_00011; // [AX]->T -L[381] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[382] = 9'b10_01_00101; // T->[AX] -L[383] = 9'bxx_xx_xxxxx; // [] -L[384] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[385] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[386] = 9'b11_00_10001; // PC+T->PC -L[387] = 9'bxx_xx_xxxxx; // [] -L[388] = 9'b10_00_00011; // ['NO-OP', ''] -L[389] = 9'bxx_xx_xxxxx; // [] -L[390] = 9'bxx_xx_xxxxx; // [] -L[391] = 9'bxx_xx_xxxxx; // [] -L[392] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[393] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[394] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[395] = 9'b01_01_01100; // [AX]->AH,T+Y->AL -L[396] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[397] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[398] = 9'bxx_xx_xxxxx; // [] -L[399] = 9'bxx_xx_xxxxx; // [] -L[400] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[401] = 9'b10_00_00011; // ['NO-OP', ''] -L[402] = 9'bxx_xx_xxxxx; // [] -L[403] = 9'bxx_xx_xxxxx; // [] -L[404] = 9'bxx_xx_xxxxx; // [] -L[405] = 9'bxx_xx_xxxxx; // [] -L[406] = 9'bxx_xx_xxxxx; // [] -L[407] = 9'bxx_xx_xxxxx; // [] -L[408] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[409] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[410] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[411] = 9'b00_01_01100; // [AX]->AH,T+Y->AL -L[412] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[413] = 9'b00_01_00011; // [AX]->T -L[414] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[415] = 9'b10_01_00101; // T->[AX] -L[416] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[417] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[418] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[419] = 9'bxx_xx_xxxxx; // [] -L[420] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[421] = 9'bxx_xx_xxxxx; // [] -L[422] = 9'bxx_xx_xxxxx; // [] -L[423] = 9'bxx_xx_xxxxx; // [] -L[424] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[425] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[426] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[427] = 9'bxx_xx_xxxxx; // [] -L[428] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[429] = 9'bxx_xx_xxxxx; // [] -L[430] = 9'bxx_xx_xxxxx; // [] -L[431] = 9'bxx_xx_xxxxx; // [] -L[432] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[433] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[434] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[435] = 9'bxx_xx_xxxxx; // [] -L[436] = 9'b00_01_00011; // [AX]->T -L[437] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[438] = 9'b10_01_00101; // T->[AX] -L[439] = 9'bxx_xx_xxxxx; // [] -L[440] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[441] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[442] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[443] = 9'bxx_xx_xxxxx; // [] -L[444] = 9'b00_01_00011; // [AX]->T -L[445] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[446] = 9'b10_01_00101; // T->[AX] -L[447] = 9'bxx_xx_xxxxx; // [] -L[448] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[449] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[450] = 9'bxx_xx_xxxxx; // [] -L[451] = 9'bxx_xx_xxxxx; // [] -L[452] = 9'bxx_xx_xxxxx; // [] -L[453] = 9'bxx_xx_xxxxx; // [] -L[454] = 9'bxx_xx_xxxxx; // [] -L[455] = 9'bxx_xx_xxxxx; // [] -L[456] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[457] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[458] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[459] = 9'bxx_xx_xxxxx; // [] -L[460] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[461] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[462] = 9'bxx_xx_xxxxx; // [] -L[463] = 9'bxx_xx_xxxxx; // [] -L[464] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[465] = 9'b10_00_00011; // ['NO-OP', ''] -L[466] = 9'bxx_xx_xxxxx; // [] -L[467] = 9'bxx_xx_xxxxx; // [] -L[468] = 9'bxx_xx_xxxxx; // [] -L[469] = 9'bxx_xx_xxxxx; // [] -L[470] = 9'bxx_xx_xxxxx; // [] -L[471] = 9'bxx_xx_xxxxx; // [] -L[472] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[473] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[474] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[475] = 9'bxx_xx_xxxxx; // [] -L[476] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[477] = 9'b00_01_00011; // [AX]->T -L[478] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[479] = 9'b10_01_00101; // T->[AX] -L[480] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[481] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[482] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[483] = 9'bxx_xx_xxxxx; // [] -L[484] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[485] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[486] = 9'bxx_xx_xxxxx; // [] -L[487] = 9'bxx_xx_xxxxx; // [] -L[488] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[489] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[490] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[491] = 9'bxx_xx_xxxxx; // [] -L[492] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[493] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[494] = 9'bxx_xx_xxxxx; // [] -L[495] = 9'bxx_xx_xxxxx; // [] -L[496] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[497] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[498] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[499] = 9'bxx_xx_xxxxx; // [] -L[500] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[501] = 9'b00_01_00011; // [AX]->T -L[502] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[503] = 9'b10_01_00101; // T->[AX] -L[504] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[505] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[506] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[507] = 9'bxx_xx_xxxxx; // [] -L[508] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[509] = 9'b00_01_00011; // [AX]->T -L[510] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[511] = 9'b10_01_00101; // T->[AX] -L[512] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[513] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[514] = 9'b00_10_10000; // ['SP++', 'SP+1->SP', '[SP]->T,SP+1->SP'] -L[515] = 9'b00_10_10110; // [SP]->P,SP+1->SP -L[516] = 9'b00_10_10000; // ['SP++', 'SP+1->SP', '[SP]->T,SP+1->SP'] -L[517] = 9'b10_10_10011; // [SP]:T->PC -L[518] = 9'bxx_xx_xxxxx; // [] -L[519] = 9'bxx_xx_xxxxx; // [] -L[520] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[521] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[522] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[523] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[524] = 9'b00_01_01011; // [AX]->AH,T->AL -L[525] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[526] = 9'bxx_xx_xxxxx; // [] -L[527] = 9'bxx_xx_xxxxx; // [] -L[528] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[529] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[530] = 9'bxx_xx_xxxxx; // [] -L[531] = 9'bxx_xx_xxxxx; // [] -L[532] = 9'bxx_xx_xxxxx; // [] -L[533] = 9'bxx_xx_xxxxx; // [] -L[534] = 9'bxx_xx_xxxxx; // [] -L[535] = 9'bxx_xx_xxxxx; // [] -L[536] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[537] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[538] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[539] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[540] = 9'b00_01_01011; // [AX]->AH,T->AL -L[541] = 9'b00_01_00011; // [AX]->T -L[542] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[543] = 9'b10_01_00101; // T->[AX] -L[544] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[545] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[546] = 9'bxx_xx_xxxxx; // [] -L[547] = 9'bxx_xx_xxxxx; // [] -L[548] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[549] = 9'bxx_xx_xxxxx; // [] -L[550] = 9'bxx_xx_xxxxx; // [] -L[551] = 9'bxx_xx_xxxxx; // [] -L[552] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[553] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[554] = 9'bxx_xx_xxxxx; // [] -L[555] = 9'bxx_xx_xxxxx; // [] -L[556] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[557] = 9'bxx_xx_xxxxx; // [] -L[558] = 9'bxx_xx_xxxxx; // [] -L[559] = 9'bxx_xx_xxxxx; // [] -L[560] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[561] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[562] = 9'bxx_xx_xxxxx; // [] -L[563] = 9'bxx_xx_xxxxx; // [] -L[564] = 9'b00_01_00011; // [AX]->T -L[565] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[566] = 9'b10_01_00101; // T->[AX] -L[567] = 9'bxx_xx_xxxxx; // [] -L[568] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[569] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[570] = 9'bxx_xx_xxxxx; // [] -L[571] = 9'bxx_xx_xxxxx; // [] -L[572] = 9'b00_01_00011; // [AX]->T -L[573] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[574] = 9'b10_01_00101; // T->[AX] -L[575] = 9'bxx_xx_xxxxx; // [] -L[576] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[577] = 9'b00_00_00011; // [PC]-> -L[578] = 9'b10_10_01110; // ALU(A)->[SP--] -L[579] = 9'bxx_xx_xxxxx; // [] -L[580] = 9'bxx_xx_xxxxx; // [] -L[581] = 9'bxx_xx_xxxxx; // [] -L[582] = 9'bxx_xx_xxxxx; // [] -L[583] = 9'bxx_xx_xxxxx; // [] -L[584] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[585] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[586] = 9'bxx_xx_xxxxx; // [] -L[587] = 9'bxx_xx_xxxxx; // [] -L[588] = 9'bxx_xx_xxxxx; // [] -L[589] = 9'bxx_xx_xxxxx; // [] -L[590] = 9'bxx_xx_xxxxx; // [] -L[591] = 9'bxx_xx_xxxxx; // [] -L[592] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[593] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[594] = 9'bxx_xx_xxxxx; // [] -L[595] = 9'bxx_xx_xxxxx; // [] -L[596] = 9'bxx_xx_xxxxx; // [] -L[597] = 9'bxx_xx_xxxxx; // [] -L[598] = 9'bxx_xx_xxxxx; // [] -L[599] = 9'bxx_xx_xxxxx; // [] -L[600] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[601] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[602] = 9'bxx_xx_xxxxx; // [] -L[603] = 9'bxx_xx_xxxxx; // [] -L[604] = 9'bxx_xx_xxxxx; // [] -L[605] = 9'bxx_xx_xxxxx; // [] -L[606] = 9'bxx_xx_xxxxx; // [] -L[607] = 9'bxx_xx_xxxxx; // [] -L[608] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[609] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[610] = 9'bxx_xx_xxxxx; // [] -L[611] = 9'bxx_xx_xxxxx; // [] -L[612] = 9'b10_00_10011; // [PC]:T->PC -L[613] = 9'bxx_xx_xxxxx; // [] -L[614] = 9'bxx_xx_xxxxx; // [] -L[615] = 9'bxx_xx_xxxxx; // [] -L[616] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[617] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[618] = 9'b11_00_00001; // [PC++]->AH -L[619] = 9'bxx_xx_xxxxx; // [] -L[620] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[621] = 9'bxx_xx_xxxxx; // [] -L[622] = 9'bxx_xx_xxxxx; // [] -L[623] = 9'bxx_xx_xxxxx; // [] -L[624] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[625] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[626] = 9'b11_00_00001; // [PC++]->AH -L[627] = 9'bxx_xx_xxxxx; // [] -L[628] = 9'b00_01_00011; // [AX]->T -L[629] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[630] = 9'b10_01_00101; // T->[AX] -L[631] = 9'bxx_xx_xxxxx; // [] -L[632] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[633] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[634] = 9'b11_00_00001; // [PC++]->AH -L[635] = 9'bxx_xx_xxxxx; // [] -L[636] = 9'b00_01_00011; // [AX]->T -L[637] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[638] = 9'b10_01_00101; // T->[AX] -L[639] = 9'bxx_xx_xxxxx; // [] -L[640] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[641] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[642] = 9'b11_00_10001; // PC+T->PC -L[643] = 9'bxx_xx_xxxxx; // [] -L[644] = 9'b10_00_00011; // ['NO-OP', ''] -L[645] = 9'bxx_xx_xxxxx; // [] -L[646] = 9'bxx_xx_xxxxx; // [] -L[647] = 9'bxx_xx_xxxxx; // [] -L[648] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[649] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[650] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[651] = 9'b01_01_01100; // [AX]->AH,T+Y->AL -L[652] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[653] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[654] = 9'bxx_xx_xxxxx; // [] -L[655] = 9'bxx_xx_xxxxx; // [] -L[656] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[657] = 9'b10_00_00011; // ['NO-OP', ''] -L[658] = 9'bxx_xx_xxxxx; // [] -L[659] = 9'bxx_xx_xxxxx; // [] -L[660] = 9'bxx_xx_xxxxx; // [] -L[661] = 9'bxx_xx_xxxxx; // [] -L[662] = 9'bxx_xx_xxxxx; // [] -L[663] = 9'bxx_xx_xxxxx; // [] -L[664] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[665] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[666] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[667] = 9'b00_01_01100; // [AX]->AH,T+Y->AL -L[668] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[669] = 9'b00_01_00011; // [AX]->T -L[670] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[671] = 9'b10_01_00101; // T->[AX] -L[672] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[673] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[674] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[675] = 9'bxx_xx_xxxxx; // [] -L[676] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[677] = 9'bxx_xx_xxxxx; // [] -L[678] = 9'bxx_xx_xxxxx; // [] -L[679] = 9'bxx_xx_xxxxx; // [] -L[680] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[681] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[682] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[683] = 9'bxx_xx_xxxxx; // [] -L[684] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[685] = 9'bxx_xx_xxxxx; // [] -L[686] = 9'bxx_xx_xxxxx; // [] -L[687] = 9'bxx_xx_xxxxx; // [] -L[688] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[689] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[690] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[691] = 9'bxx_xx_xxxxx; // [] -L[692] = 9'b00_01_00011; // [AX]->T -L[693] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[694] = 9'b10_01_00101; // T->[AX] -L[695] = 9'bxx_xx_xxxxx; // [] -L[696] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[697] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[698] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[699] = 9'bxx_xx_xxxxx; // [] -L[700] = 9'b00_01_00011; // [AX]->T -L[701] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[702] = 9'b10_01_00101; // T->[AX] -L[703] = 9'bxx_xx_xxxxx; // [] -L[704] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[705] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[706] = 9'bxx_xx_xxxxx; // [] -L[707] = 9'bxx_xx_xxxxx; // [] -L[708] = 9'bxx_xx_xxxxx; // [] -L[709] = 9'bxx_xx_xxxxx; // [] -L[710] = 9'bxx_xx_xxxxx; // [] -L[711] = 9'bxx_xx_xxxxx; // [] -L[712] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[713] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[714] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[715] = 9'bxx_xx_xxxxx; // [] -L[716] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[717] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[718] = 9'bxx_xx_xxxxx; // [] -L[719] = 9'bxx_xx_xxxxx; // [] -L[720] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[721] = 9'b10_00_00011; // ['NO-OP', ''] -L[722] = 9'bxx_xx_xxxxx; // [] -L[723] = 9'bxx_xx_xxxxx; // [] -L[724] = 9'bxx_xx_xxxxx; // [] -L[725] = 9'bxx_xx_xxxxx; // [] -L[726] = 9'bxx_xx_xxxxx; // [] -L[727] = 9'bxx_xx_xxxxx; // [] -L[728] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[729] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[730] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[731] = 9'bxx_xx_xxxxx; // [] -L[732] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[733] = 9'b00_01_00011; // [AX]->T -L[734] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[735] = 9'b10_01_00101; // T->[AX] -L[736] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[737] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[738] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[739] = 9'bxx_xx_xxxxx; // [] -L[740] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[741] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[742] = 9'bxx_xx_xxxxx; // [] -L[743] = 9'bxx_xx_xxxxx; // [] -L[744] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[745] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[746] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[747] = 9'bxx_xx_xxxxx; // [] -L[748] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[749] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[750] = 9'bxx_xx_xxxxx; // [] -L[751] = 9'bxx_xx_xxxxx; // [] -L[752] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[753] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[754] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[755] = 9'bxx_xx_xxxxx; // [] -L[756] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[757] = 9'b00_01_00011; // [AX]->T -L[758] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[759] = 9'b10_01_00101; // T->[AX] -L[760] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[761] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[762] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[763] = 9'bxx_xx_xxxxx; // [] -L[764] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[765] = 9'b00_01_00011; // [AX]->T -L[766] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[767] = 9'b10_01_00101; // T->[AX] -L[768] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[769] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[770] = 9'b11_10_10000; // SP+1->SP -L[771] = 9'bxx_xx_xxxxx; // [] -L[772] = 9'b00_10_10000; // ['SP++', 'SP+1->SP', '[SP]->T,SP+1->SP'] -L[773] = 9'b00_10_10011; // [SP]:T->PC -L[774] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[775] = 9'bxx_xx_xxxxx; // [] -L[776] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[777] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[778] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[779] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[780] = 9'b00_01_01011; // [AX]->AH,T->AL -L[781] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[782] = 9'bxx_xx_xxxxx; // [] -L[783] = 9'bxx_xx_xxxxx; // [] -L[784] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[785] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[786] = 9'bxx_xx_xxxxx; // [] -L[787] = 9'bxx_xx_xxxxx; // [] -L[788] = 9'bxx_xx_xxxxx; // [] -L[789] = 9'bxx_xx_xxxxx; // [] -L[790] = 9'bxx_xx_xxxxx; // [] -L[791] = 9'bxx_xx_xxxxx; // [] -L[792] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[793] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[794] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[795] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[796] = 9'b00_01_01011; // [AX]->AH,T->AL -L[797] = 9'b00_01_00011; // [AX]->T -L[798] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[799] = 9'b10_01_00101; // T->[AX] -L[800] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[801] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[802] = 9'bxx_xx_xxxxx; // [] -L[803] = 9'bxx_xx_xxxxx; // [] -L[804] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[805] = 9'bxx_xx_xxxxx; // [] -L[806] = 9'bxx_xx_xxxxx; // [] -L[807] = 9'bxx_xx_xxxxx; // [] -L[808] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[809] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[810] = 9'bxx_xx_xxxxx; // [] -L[811] = 9'bxx_xx_xxxxx; // [] -L[812] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[813] = 9'bxx_xx_xxxxx; // [] -L[814] = 9'bxx_xx_xxxxx; // [] -L[815] = 9'bxx_xx_xxxxx; // [] -L[816] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[817] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[818] = 9'bxx_xx_xxxxx; // [] -L[819] = 9'bxx_xx_xxxxx; // [] -L[820] = 9'b00_01_00011; // [AX]->T -L[821] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[822] = 9'b10_01_00101; // T->[AX] -L[823] = 9'bxx_xx_xxxxx; // [] -L[824] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[825] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[826] = 9'bxx_xx_xxxxx; // [] -L[827] = 9'bxx_xx_xxxxx; // [] -L[828] = 9'b00_01_00011; // [AX]->T -L[829] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[830] = 9'b10_01_00101; // T->[AX] -L[831] = 9'bxx_xx_xxxxx; // [] -L[832] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[833] = 9'b00_00_00011; // [PC]-> -L[834] = 9'b00_10_10000; // ['SP++', 'SP+1->SP', '[SP]->T,SP+1->SP'] -L[835] = 9'b10_10_00010; // ['ALU([SP])->A', '[SP]->P'] -L[836] = 9'bxx_xx_xxxxx; // [] -L[837] = 9'bxx_xx_xxxxx; // [] -L[838] = 9'bxx_xx_xxxxx; // [] -L[839] = 9'bxx_xx_xxxxx; // [] -L[840] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[841] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[842] = 9'bxx_xx_xxxxx; // [] -L[843] = 9'bxx_xx_xxxxx; // [] -L[844] = 9'bxx_xx_xxxxx; // [] -L[845] = 9'bxx_xx_xxxxx; // [] -L[846] = 9'bxx_xx_xxxxx; // [] -L[847] = 9'bxx_xx_xxxxx; // [] -L[848] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[849] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[850] = 9'bxx_xx_xxxxx; // [] -L[851] = 9'bxx_xx_xxxxx; // [] -L[852] = 9'bxx_xx_xxxxx; // [] -L[853] = 9'bxx_xx_xxxxx; // [] -L[854] = 9'bxx_xx_xxxxx; // [] -L[855] = 9'bxx_xx_xxxxx; // [] -L[856] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[857] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[858] = 9'bxx_xx_xxxxx; // [] -L[859] = 9'bxx_xx_xxxxx; // [] -L[860] = 9'bxx_xx_xxxxx; // [] -L[861] = 9'bxx_xx_xxxxx; // [] -L[862] = 9'bxx_xx_xxxxx; // [] -L[863] = 9'bxx_xx_xxxxx; // [] -L[864] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[865] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[866] = 9'b00_00_00001; // [PC++]->AH -L[867] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[868] = 9'b10_01_10011; // [AX]:T->PC -L[869] = 9'bxx_xx_xxxxx; // [] -L[870] = 9'bxx_xx_xxxxx; // [] -L[871] = 9'bxx_xx_xxxxx; // [] -L[872] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[873] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[874] = 9'b11_00_00001; // [PC++]->AH -L[875] = 9'bxx_xx_xxxxx; // [] -L[876] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[877] = 9'bxx_xx_xxxxx; // [] -L[878] = 9'bxx_xx_xxxxx; // [] -L[879] = 9'bxx_xx_xxxxx; // [] -L[880] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[881] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[882] = 9'b11_00_00001; // [PC++]->AH -L[883] = 9'bxx_xx_xxxxx; // [] -L[884] = 9'b00_01_00011; // [AX]->T -L[885] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[886] = 9'b10_01_00101; // T->[AX] -L[887] = 9'bxx_xx_xxxxx; // [] -L[888] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[889] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[890] = 9'b11_00_00001; // [PC++]->AH -L[891] = 9'bxx_xx_xxxxx; // [] -L[892] = 9'b00_01_00011; // [AX]->T -L[893] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[894] = 9'b10_01_00101; // T->[AX] -L[895] = 9'bxx_xx_xxxxx; // [] -L[896] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[897] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[898] = 9'b11_00_10001; // PC+T->PC -L[899] = 9'bxx_xx_xxxxx; // [] -L[900] = 9'b10_00_00011; // ['NO-OP', ''] -L[901] = 9'bxx_xx_xxxxx; // [] -L[902] = 9'bxx_xx_xxxxx; // [] -L[903] = 9'bxx_xx_xxxxx; // [] -L[904] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[905] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[906] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[907] = 9'b01_01_01100; // [AX]->AH,T+Y->AL -L[908] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[909] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[910] = 9'bxx_xx_xxxxx; // [] -L[911] = 9'bxx_xx_xxxxx; // [] -L[912] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[913] = 9'b10_00_00011; // ['NO-OP', ''] -L[914] = 9'bxx_xx_xxxxx; // [] -L[915] = 9'bxx_xx_xxxxx; // [] -L[916] = 9'bxx_xx_xxxxx; // [] -L[917] = 9'bxx_xx_xxxxx; // [] -L[918] = 9'bxx_xx_xxxxx; // [] -L[919] = 9'bxx_xx_xxxxx; // [] -L[920] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[921] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[922] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[923] = 9'b00_01_01100; // [AX]->AH,T+Y->AL -L[924] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[925] = 9'b00_01_00011; // [AX]->T -L[926] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[927] = 9'b10_01_00101; // T->[AX] -L[928] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[929] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[930] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[931] = 9'bxx_xx_xxxxx; // [] -L[932] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[933] = 9'bxx_xx_xxxxx; // [] -L[934] = 9'bxx_xx_xxxxx; // [] -L[935] = 9'bxx_xx_xxxxx; // [] -L[936] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[937] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[938] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[939] = 9'bxx_xx_xxxxx; // [] -L[940] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[941] = 9'bxx_xx_xxxxx; // [] -L[942] = 9'bxx_xx_xxxxx; // [] -L[943] = 9'bxx_xx_xxxxx; // [] -L[944] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[945] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[946] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[947] = 9'bxx_xx_xxxxx; // [] -L[948] = 9'b00_01_00011; // [AX]->T -L[949] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[950] = 9'b10_01_00101; // T->[AX] -L[951] = 9'bxx_xx_xxxxx; // [] -L[952] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[953] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[954] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[955] = 9'bxx_xx_xxxxx; // [] -L[956] = 9'b00_01_00011; // [AX]->T -L[957] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[958] = 9'b10_01_00101; // T->[AX] -L[959] = 9'bxx_xx_xxxxx; // [] -L[960] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[961] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[962] = 9'bxx_xx_xxxxx; // [] -L[963] = 9'bxx_xx_xxxxx; // [] -L[964] = 9'bxx_xx_xxxxx; // [] -L[965] = 9'bxx_xx_xxxxx; // [] -L[966] = 9'bxx_xx_xxxxx; // [] -L[967] = 9'bxx_xx_xxxxx; // [] -L[968] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[969] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[970] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[971] = 9'bxx_xx_xxxxx; // [] -L[972] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[973] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[974] = 9'bxx_xx_xxxxx; // [] -L[975] = 9'bxx_xx_xxxxx; // [] -L[976] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[977] = 9'b10_00_00011; // ['NO-OP', ''] -L[978] = 9'bxx_xx_xxxxx; // [] -L[979] = 9'bxx_xx_xxxxx; // [] -L[980] = 9'bxx_xx_xxxxx; // [] -L[981] = 9'bxx_xx_xxxxx; // [] -L[982] = 9'bxx_xx_xxxxx; // [] -L[983] = 9'bxx_xx_xxxxx; // [] -L[984] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[985] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[986] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[987] = 9'bxx_xx_xxxxx; // [] -L[988] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[989] = 9'b00_01_00011; // [AX]->T -L[990] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[991] = 9'b10_01_00101; // T->[AX] -L[992] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[993] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[994] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[995] = 9'bxx_xx_xxxxx; // [] -L[996] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[997] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[998] = 9'bxx_xx_xxxxx; // [] -L[999] = 9'bxx_xx_xxxxx; // [] -L[1000] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1001] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1002] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1003] = 9'bxx_xx_xxxxx; // [] -L[1004] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1005] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1006] = 9'bxx_xx_xxxxx; // [] -L[1007] = 9'bxx_xx_xxxxx; // [] -L[1008] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1009] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1010] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1011] = 9'bxx_xx_xxxxx; // [] -L[1012] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1013] = 9'b00_01_00011; // [AX]->T -L[1014] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1015] = 9'b10_01_00101; // T->[AX] -L[1016] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1017] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1018] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1019] = 9'bxx_xx_xxxxx; // [] -L[1020] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1021] = 9'b00_01_00011; // [AX]->T -L[1022] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1023] = 9'b10_01_00101; // T->[AX] -L[1024] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1025] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1026] = 9'bxx_xx_xxxxx; // [] -L[1027] = 9'bxx_xx_xxxxx; // [] -L[1028] = 9'bxx_xx_xxxxx; // [] -L[1029] = 9'bxx_xx_xxxxx; // [] -L[1030] = 9'bxx_xx_xxxxx; // [] -L[1031] = 9'bxx_xx_xxxxx; // [] -L[1032] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1033] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1034] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[1035] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1036] = 9'b00_01_01011; // [AX]->AH,T->AL -L[1037] = 9'b10_01_00110; // ALU()->[AX] -L[1038] = 9'bxx_xx_xxxxx; // [] -L[1039] = 9'bxx_xx_xxxxx; // [] -L[1040] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1041] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1042] = 9'bxx_xx_xxxxx; // [] -L[1043] = 9'bxx_xx_xxxxx; // [] -L[1044] = 9'bxx_xx_xxxxx; // [] -L[1045] = 9'bxx_xx_xxxxx; // [] -L[1046] = 9'bxx_xx_xxxxx; // [] -L[1047] = 9'bxx_xx_xxxxx; // [] -L[1048] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1049] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1050] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[1051] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1052] = 9'b00_01_01011; // [AX]->AH,T->AL -L[1053] = 9'b10_01_00110; // ALU()->[AX] -L[1054] = 9'bxx_xx_xxxxx; // [] -L[1055] = 9'bxx_xx_xxxxx; // [] -L[1056] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1057] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1058] = 9'bxx_xx_xxxxx; // [] -L[1059] = 9'bxx_xx_xxxxx; // [] -L[1060] = 9'b10_01_00110; // ALU()->[AX] -L[1061] = 9'bxx_xx_xxxxx; // [] -L[1062] = 9'bxx_xx_xxxxx; // [] -L[1063] = 9'bxx_xx_xxxxx; // [] -L[1064] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1065] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1066] = 9'bxx_xx_xxxxx; // [] -L[1067] = 9'bxx_xx_xxxxx; // [] -L[1068] = 9'b10_01_00110; // ALU()->[AX] -L[1069] = 9'bxx_xx_xxxxx; // [] -L[1070] = 9'bxx_xx_xxxxx; // [] -L[1071] = 9'bxx_xx_xxxxx; // [] -L[1072] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1073] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1074] = 9'bxx_xx_xxxxx; // [] -L[1075] = 9'bxx_xx_xxxxx; // [] -L[1076] = 9'b10_01_00110; // ALU()->[AX] -L[1077] = 9'bxx_xx_xxxxx; // [] -L[1078] = 9'bxx_xx_xxxxx; // [] -L[1079] = 9'bxx_xx_xxxxx; // [] -L[1080] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1081] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1082] = 9'bxx_xx_xxxxx; // [] -L[1083] = 9'bxx_xx_xxxxx; // [] -L[1084] = 9'b10_01_00110; // ALU()->[AX] -L[1085] = 9'bxx_xx_xxxxx; // [] -L[1086] = 9'bxx_xx_xxxxx; // [] -L[1087] = 9'bxx_xx_xxxxx; // [] -L[1088] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1089] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1090] = 9'bxx_xx_xxxxx; // [] -L[1091] = 9'bxx_xx_xxxxx; // [] -L[1092] = 9'bxx_xx_xxxxx; // [] -L[1093] = 9'bxx_xx_xxxxx; // [] -L[1094] = 9'bxx_xx_xxxxx; // [] -L[1095] = 9'bxx_xx_xxxxx; // [] -L[1096] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1097] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1098] = 9'bxx_xx_xxxxx; // [] -L[1099] = 9'bxx_xx_xxxxx; // [] -L[1100] = 9'bxx_xx_xxxxx; // [] -L[1101] = 9'bxx_xx_xxxxx; // [] -L[1102] = 9'bxx_xx_xxxxx; // [] -L[1103] = 9'bxx_xx_xxxxx; // [] -L[1104] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1105] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1106] = 9'bxx_xx_xxxxx; // [] -L[1107] = 9'bxx_xx_xxxxx; // [] -L[1108] = 9'bxx_xx_xxxxx; // [] -L[1109] = 9'bxx_xx_xxxxx; // [] -L[1110] = 9'bxx_xx_xxxxx; // [] -L[1111] = 9'bxx_xx_xxxxx; // [] -L[1112] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1113] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1114] = 9'bxx_xx_xxxxx; // [] -L[1115] = 9'bxx_xx_xxxxx; // [] -L[1116] = 9'bxx_xx_xxxxx; // [] -L[1117] = 9'bxx_xx_xxxxx; // [] -L[1118] = 9'bxx_xx_xxxxx; // [] -L[1119] = 9'bxx_xx_xxxxx; // [] -L[1120] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1121] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1122] = 9'b11_00_00001; // [PC++]->AH -L[1123] = 9'bxx_xx_xxxxx; // [] -L[1124] = 9'b10_01_00110; // ALU()->[AX] -L[1125] = 9'bxx_xx_xxxxx; // [] -L[1126] = 9'bxx_xx_xxxxx; // [] -L[1127] = 9'bxx_xx_xxxxx; // [] -L[1128] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1129] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1130] = 9'b11_00_00001; // [PC++]->AH -L[1131] = 9'bxx_xx_xxxxx; // [] -L[1132] = 9'b10_01_00110; // ALU()->[AX] -L[1133] = 9'bxx_xx_xxxxx; // [] -L[1134] = 9'bxx_xx_xxxxx; // [] -L[1135] = 9'bxx_xx_xxxxx; // [] -L[1136] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1137] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1138] = 9'b11_00_00001; // [PC++]->AH -L[1139] = 9'bxx_xx_xxxxx; // [] -L[1140] = 9'b10_01_00110; // ALU()->[AX] -L[1141] = 9'bxx_xx_xxxxx; // [] -L[1142] = 9'bxx_xx_xxxxx; // [] -L[1143] = 9'bxx_xx_xxxxx; // [] -L[1144] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1145] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1146] = 9'b11_00_00001; // [PC++]->AH -L[1147] = 9'bxx_xx_xxxxx; // [] -L[1148] = 9'b10_01_00110; // ALU()->[AX] -L[1149] = 9'bxx_xx_xxxxx; // [] -L[1150] = 9'bxx_xx_xxxxx; // [] -L[1151] = 9'bxx_xx_xxxxx; // [] -L[1152] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1153] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1154] = 9'b11_00_10001; // PC+T->PC -L[1155] = 9'bxx_xx_xxxxx; // [] -L[1156] = 9'b10_00_00011; // ['NO-OP', ''] -L[1157] = 9'bxx_xx_xxxxx; // [] -L[1158] = 9'bxx_xx_xxxxx; // [] -L[1159] = 9'bxx_xx_xxxxx; // [] -L[1160] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1161] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1162] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1163] = 9'b00_01_01100; // [AX]->AH,T+Y->AL -L[1164] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1165] = 9'b10_01_00110; // ALU()->[AX] -L[1166] = 9'bxx_xx_xxxxx; // [] -L[1167] = 9'bxx_xx_xxxxx; // [] -L[1168] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1169] = 9'b10_00_00011; // ['NO-OP', ''] -L[1170] = 9'bxx_xx_xxxxx; // [] -L[1171] = 9'bxx_xx_xxxxx; // [] -L[1172] = 9'bxx_xx_xxxxx; // [] -L[1173] = 9'bxx_xx_xxxxx; // [] -L[1174] = 9'bxx_xx_xxxxx; // [] -L[1175] = 9'bxx_xx_xxxxx; // [] -L[1176] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1177] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1178] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1179] = 9'b00_01_01100; // [AX]->AH,T+Y->AL -L[1180] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1181] = 9'b10_01_00110; // ALU()->[AX] -L[1182] = 9'bxx_xx_xxxxx; // [] -L[1183] = 9'bxx_xx_xxxxx; // [] -L[1184] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1185] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1186] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1187] = 9'bxx_xx_xxxxx; // [] -L[1188] = 9'b10_01_00110; // ALU()->[AX] -L[1189] = 9'bxx_xx_xxxxx; // [] -L[1190] = 9'bxx_xx_xxxxx; // [] -L[1191] = 9'bxx_xx_xxxxx; // [] -L[1192] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1193] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1194] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1195] = 9'bxx_xx_xxxxx; // [] -L[1196] = 9'b10_01_00110; // ALU()->[AX] -L[1197] = 9'bxx_xx_xxxxx; // [] -L[1198] = 9'bxx_xx_xxxxx; // [] -L[1199] = 9'bxx_xx_xxxxx; // [] -L[1200] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1201] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1202] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1203] = 9'bxx_xx_xxxxx; // [] -L[1204] = 9'b10_01_00110; // ALU()->[AX] -L[1205] = 9'bxx_xx_xxxxx; // [] -L[1206] = 9'bxx_xx_xxxxx; // [] -L[1207] = 9'bxx_xx_xxxxx; // [] -L[1208] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1209] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1210] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1211] = 9'bxx_xx_xxxxx; // [] -L[1212] = 9'b10_01_00110; // ALU()->[AX] -L[1213] = 9'bxx_xx_xxxxx; // [] -L[1214] = 9'bxx_xx_xxxxx; // [] -L[1215] = 9'bxx_xx_xxxxx; // [] -L[1216] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1217] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1218] = 9'bxx_xx_xxxxx; // [] -L[1219] = 9'bxx_xx_xxxxx; // [] -L[1220] = 9'bxx_xx_xxxxx; // [] -L[1221] = 9'bxx_xx_xxxxx; // [] -L[1222] = 9'bxx_xx_xxxxx; // [] -L[1223] = 9'bxx_xx_xxxxx; // [] -L[1224] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1225] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1226] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1227] = 9'bxx_xx_xxxxx; // [] -L[1228] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1229] = 9'b10_01_00110; // ALU()->[AX] -L[1230] = 9'bxx_xx_xxxxx; // [] -L[1231] = 9'bxx_xx_xxxxx; // [] -L[1232] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1233] = 9'b10_00_10010; // X->S -L[1234] = 9'bxx_xx_xxxxx; // [] -L[1235] = 9'bxx_xx_xxxxx; // [] -L[1236] = 9'bxx_xx_xxxxx; // [] -L[1237] = 9'bxx_xx_xxxxx; // [] -L[1238] = 9'bxx_xx_xxxxx; // [] -L[1239] = 9'bxx_xx_xxxxx; // [] -L[1240] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1241] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1242] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1243] = 9'bxx_xx_xxxxx; // [] -L[1244] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1245] = 9'b10_01_00110; // ALU()->[AX] -L[1246] = 9'bxx_xx_xxxxx; // [] -L[1247] = 9'bxx_xx_xxxxx; // [] -L[1248] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1249] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1250] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1251] = 9'bxx_xx_xxxxx; // [] -L[1252] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1253] = 9'b10_01_00110; // ALU()->[AX] -L[1254] = 9'bxx_xx_xxxxx; // [] -L[1255] = 9'bxx_xx_xxxxx; // [] -L[1256] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1257] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1258] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1259] = 9'bxx_xx_xxxxx; // [] -L[1260] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1261] = 9'b10_01_00110; // ALU()->[AX] -L[1262] = 9'bxx_xx_xxxxx; // [] -L[1263] = 9'bxx_xx_xxxxx; // [] -L[1264] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1265] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1266] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1267] = 9'bxx_xx_xxxxx; // [] -L[1268] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1269] = 9'b10_01_00110; // ALU()->[AX] -L[1270] = 9'bxx_xx_xxxxx; // [] -L[1271] = 9'bxx_xx_xxxxx; // [] -L[1272] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1273] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1274] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1275] = 9'bxx_xx_xxxxx; // [] -L[1276] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1277] = 9'b10_01_00110; // ALU()->[AX] -L[1278] = 9'bxx_xx_xxxxx; // [] -L[1279] = 9'bxx_xx_xxxxx; // [] -L[1280] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1281] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[1282] = 9'bxx_xx_xxxxx; // [] -L[1283] = 9'bxx_xx_xxxxx; // [] -L[1284] = 9'bxx_xx_xxxxx; // [] -L[1285] = 9'bxx_xx_xxxxx; // [] -L[1286] = 9'bxx_xx_xxxxx; // [] -L[1287] = 9'bxx_xx_xxxxx; // [] -L[1288] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1289] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1290] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[1291] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1292] = 9'b00_01_01011; // [AX]->AH,T->AL -L[1293] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1294] = 9'bxx_xx_xxxxx; // [] -L[1295] = 9'bxx_xx_xxxxx; // [] -L[1296] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1297] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[1298] = 9'bxx_xx_xxxxx; // [] -L[1299] = 9'bxx_xx_xxxxx; // [] -L[1300] = 9'bxx_xx_xxxxx; // [] -L[1301] = 9'bxx_xx_xxxxx; // [] -L[1302] = 9'bxx_xx_xxxxx; // [] -L[1303] = 9'bxx_xx_xxxxx; // [] -L[1304] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1305] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1306] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[1307] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1308] = 9'b00_01_01011; // [AX]->AH,T->AL -L[1309] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1310] = 9'bxx_xx_xxxxx; // [] -L[1311] = 9'bxx_xx_xxxxx; // [] -L[1312] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1313] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1314] = 9'bxx_xx_xxxxx; // [] -L[1315] = 9'bxx_xx_xxxxx; // [] -L[1316] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1317] = 9'bxx_xx_xxxxx; // [] -L[1318] = 9'bxx_xx_xxxxx; // [] -L[1319] = 9'bxx_xx_xxxxx; // [] -L[1320] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1321] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1322] = 9'bxx_xx_xxxxx; // [] -L[1323] = 9'bxx_xx_xxxxx; // [] -L[1324] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1325] = 9'bxx_xx_xxxxx; // [] -L[1326] = 9'bxx_xx_xxxxx; // [] -L[1327] = 9'bxx_xx_xxxxx; // [] -L[1328] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1329] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1330] = 9'bxx_xx_xxxxx; // [] -L[1331] = 9'bxx_xx_xxxxx; // [] -L[1332] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1333] = 9'bxx_xx_xxxxx; // [] -L[1334] = 9'bxx_xx_xxxxx; // [] -L[1335] = 9'bxx_xx_xxxxx; // [] -L[1336] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1337] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1338] = 9'bxx_xx_xxxxx; // [] -L[1339] = 9'bxx_xx_xxxxx; // [] -L[1340] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1341] = 9'bxx_xx_xxxxx; // [] -L[1342] = 9'bxx_xx_xxxxx; // [] -L[1343] = 9'bxx_xx_xxxxx; // [] -L[1344] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1345] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1346] = 9'bxx_xx_xxxxx; // [] -L[1347] = 9'bxx_xx_xxxxx; // [] -L[1348] = 9'bxx_xx_xxxxx; // [] -L[1349] = 9'bxx_xx_xxxxx; // [] -L[1350] = 9'bxx_xx_xxxxx; // [] -L[1351] = 9'bxx_xx_xxxxx; // [] -L[1352] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1353] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[1354] = 9'bxx_xx_xxxxx; // [] -L[1355] = 9'bxx_xx_xxxxx; // [] -L[1356] = 9'bxx_xx_xxxxx; // [] -L[1357] = 9'bxx_xx_xxxxx; // [] -L[1358] = 9'bxx_xx_xxxxx; // [] -L[1359] = 9'bxx_xx_xxxxx; // [] -L[1360] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1361] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1362] = 9'bxx_xx_xxxxx; // [] -L[1363] = 9'bxx_xx_xxxxx; // [] -L[1364] = 9'bxx_xx_xxxxx; // [] -L[1365] = 9'bxx_xx_xxxxx; // [] -L[1366] = 9'bxx_xx_xxxxx; // [] -L[1367] = 9'bxx_xx_xxxxx; // [] -L[1368] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1369] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1370] = 9'bxx_xx_xxxxx; // [] -L[1371] = 9'bxx_xx_xxxxx; // [] -L[1372] = 9'bxx_xx_xxxxx; // [] -L[1373] = 9'bxx_xx_xxxxx; // [] -L[1374] = 9'bxx_xx_xxxxx; // [] -L[1375] = 9'bxx_xx_xxxxx; // [] -L[1376] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1377] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1378] = 9'b11_00_00001; // [PC++]->AH -L[1379] = 9'bxx_xx_xxxxx; // [] -L[1380] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1381] = 9'bxx_xx_xxxxx; // [] -L[1382] = 9'bxx_xx_xxxxx; // [] -L[1383] = 9'bxx_xx_xxxxx; // [] -L[1384] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1385] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1386] = 9'b11_00_00001; // [PC++]->AH -L[1387] = 9'bxx_xx_xxxxx; // [] -L[1388] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1389] = 9'bxx_xx_xxxxx; // [] -L[1390] = 9'bxx_xx_xxxxx; // [] -L[1391] = 9'bxx_xx_xxxxx; // [] -L[1392] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1393] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1394] = 9'b11_00_00001; // [PC++]->AH -L[1395] = 9'bxx_xx_xxxxx; // [] -L[1396] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1397] = 9'bxx_xx_xxxxx; // [] -L[1398] = 9'bxx_xx_xxxxx; // [] -L[1399] = 9'bxx_xx_xxxxx; // [] -L[1400] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1401] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1402] = 9'b11_00_00001; // [PC++]->AH -L[1403] = 9'bxx_xx_xxxxx; // [] -L[1404] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1405] = 9'bxx_xx_xxxxx; // [] -L[1406] = 9'bxx_xx_xxxxx; // [] -L[1407] = 9'bxx_xx_xxxxx; // [] -L[1408] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1409] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1410] = 9'b11_00_10001; // PC+T->PC -L[1411] = 9'bxx_xx_xxxxx; // [] -L[1412] = 9'b10_00_00011; // ['NO-OP', ''] -L[1413] = 9'bxx_xx_xxxxx; // [] -L[1414] = 9'bxx_xx_xxxxx; // [] -L[1415] = 9'bxx_xx_xxxxx; // [] -L[1416] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1417] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1418] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1419] = 9'b01_01_01100; // [AX]->AH,T+Y->AL -L[1420] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1421] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1422] = 9'bxx_xx_xxxxx; // [] -L[1423] = 9'bxx_xx_xxxxx; // [] -L[1424] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1425] = 9'b10_00_00011; // ['NO-OP', ''] -L[1426] = 9'bxx_xx_xxxxx; // [] -L[1427] = 9'bxx_xx_xxxxx; // [] -L[1428] = 9'bxx_xx_xxxxx; // [] -L[1429] = 9'bxx_xx_xxxxx; // [] -L[1430] = 9'bxx_xx_xxxxx; // [] -L[1431] = 9'bxx_xx_xxxxx; // [] -L[1432] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1433] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1434] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1435] = 9'b01_01_01100; // [AX]->AH,T+Y->AL -L[1436] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1437] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1438] = 9'bxx_xx_xxxxx; // [] -L[1439] = 9'bxx_xx_xxxxx; // [] -L[1440] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1441] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1442] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1443] = 9'bxx_xx_xxxxx; // [] -L[1444] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1445] = 9'bxx_xx_xxxxx; // [] -L[1446] = 9'bxx_xx_xxxxx; // [] -L[1447] = 9'bxx_xx_xxxxx; // [] -L[1448] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1449] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1450] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1451] = 9'bxx_xx_xxxxx; // [] -L[1452] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1453] = 9'bxx_xx_xxxxx; // [] -L[1454] = 9'bxx_xx_xxxxx; // [] -L[1455] = 9'bxx_xx_xxxxx; // [] -L[1456] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1457] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1458] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1459] = 9'bxx_xx_xxxxx; // [] -L[1460] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1461] = 9'bxx_xx_xxxxx; // [] -L[1462] = 9'bxx_xx_xxxxx; // [] -L[1463] = 9'bxx_xx_xxxxx; // [] -L[1464] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1465] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1466] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1467] = 9'bxx_xx_xxxxx; // [] -L[1468] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1469] = 9'bxx_xx_xxxxx; // [] -L[1470] = 9'bxx_xx_xxxxx; // [] -L[1471] = 9'bxx_xx_xxxxx; // [] -L[1472] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1473] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1474] = 9'bxx_xx_xxxxx; // [] -L[1475] = 9'bxx_xx_xxxxx; // [] -L[1476] = 9'bxx_xx_xxxxx; // [] -L[1477] = 9'bxx_xx_xxxxx; // [] -L[1478] = 9'bxx_xx_xxxxx; // [] -L[1479] = 9'bxx_xx_xxxxx; // [] -L[1480] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1481] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1482] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1483] = 9'bxx_xx_xxxxx; // [] -L[1484] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1485] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1486] = 9'bxx_xx_xxxxx; // [] -L[1487] = 9'bxx_xx_xxxxx; // [] -L[1488] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1489] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1490] = 9'bxx_xx_xxxxx; // [] -L[1491] = 9'bxx_xx_xxxxx; // [] -L[1492] = 9'bxx_xx_xxxxx; // [] -L[1493] = 9'bxx_xx_xxxxx; // [] -L[1494] = 9'bxx_xx_xxxxx; // [] -L[1495] = 9'bxx_xx_xxxxx; // [] -L[1496] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1497] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1498] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1499] = 9'bxx_xx_xxxxx; // [] -L[1500] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1501] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1502] = 9'bxx_xx_xxxxx; // [] -L[1503] = 9'bxx_xx_xxxxx; // [] -L[1504] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1505] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1506] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1507] = 9'bxx_xx_xxxxx; // [] -L[1508] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1509] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1510] = 9'bxx_xx_xxxxx; // [] -L[1511] = 9'bxx_xx_xxxxx; // [] -L[1512] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1513] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1514] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1515] = 9'bxx_xx_xxxxx; // [] -L[1516] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1517] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1518] = 9'bxx_xx_xxxxx; // [] -L[1519] = 9'bxx_xx_xxxxx; // [] -L[1520] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1521] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1522] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1523] = 9'bxx_xx_xxxxx; // [] -L[1524] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1525] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1526] = 9'bxx_xx_xxxxx; // [] -L[1527] = 9'bxx_xx_xxxxx; // [] -L[1528] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1529] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1530] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1531] = 9'bxx_xx_xxxxx; // [] -L[1532] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1533] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1534] = 9'bxx_xx_xxxxx; // [] -L[1535] = 9'bxx_xx_xxxxx; // [] -L[1536] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1537] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[1538] = 9'bxx_xx_xxxxx; // [] -L[1539] = 9'bxx_xx_xxxxx; // [] -L[1540] = 9'bxx_xx_xxxxx; // [] -L[1541] = 9'bxx_xx_xxxxx; // [] -L[1542] = 9'bxx_xx_xxxxx; // [] -L[1543] = 9'bxx_xx_xxxxx; // [] -L[1544] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1545] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1546] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[1547] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1548] = 9'b00_01_01011; // [AX]->AH,T->AL -L[1549] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1550] = 9'bxx_xx_xxxxx; // [] -L[1551] = 9'bxx_xx_xxxxx; // [] -L[1552] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1553] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1554] = 9'bxx_xx_xxxxx; // [] -L[1555] = 9'bxx_xx_xxxxx; // [] -L[1556] = 9'bxx_xx_xxxxx; // [] -L[1557] = 9'bxx_xx_xxxxx; // [] -L[1558] = 9'bxx_xx_xxxxx; // [] -L[1559] = 9'bxx_xx_xxxxx; // [] -L[1560] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1561] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1562] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[1563] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1564] = 9'b00_01_01011; // [AX]->AH,T->AL -L[1565] = 9'b00_01_00011; // [AX]->T -L[1566] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1567] = 9'b10_01_00101; // T->[AX] -L[1568] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1569] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1570] = 9'bxx_xx_xxxxx; // [] -L[1571] = 9'bxx_xx_xxxxx; // [] -L[1572] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1573] = 9'bxx_xx_xxxxx; // [] -L[1574] = 9'bxx_xx_xxxxx; // [] -L[1575] = 9'bxx_xx_xxxxx; // [] -L[1576] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1577] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1578] = 9'bxx_xx_xxxxx; // [] -L[1579] = 9'bxx_xx_xxxxx; // [] -L[1580] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1581] = 9'bxx_xx_xxxxx; // [] -L[1582] = 9'bxx_xx_xxxxx; // [] -L[1583] = 9'bxx_xx_xxxxx; // [] -L[1584] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1585] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1586] = 9'bxx_xx_xxxxx; // [] -L[1587] = 9'bxx_xx_xxxxx; // [] -L[1588] = 9'b00_01_00011; // [AX]->T -L[1589] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1590] = 9'b10_01_00101; // T->[AX] -L[1591] = 9'bxx_xx_xxxxx; // [] -L[1592] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1593] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1594] = 9'bxx_xx_xxxxx; // [] -L[1595] = 9'bxx_xx_xxxxx; // [] -L[1596] = 9'b00_01_00011; // [AX]->T -L[1597] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1598] = 9'b10_01_00101; // T->[AX] -L[1599] = 9'bxx_xx_xxxxx; // [] -L[1600] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1601] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1602] = 9'bxx_xx_xxxxx; // [] -L[1603] = 9'bxx_xx_xxxxx; // [] -L[1604] = 9'bxx_xx_xxxxx; // [] -L[1605] = 9'bxx_xx_xxxxx; // [] -L[1606] = 9'bxx_xx_xxxxx; // [] -L[1607] = 9'bxx_xx_xxxxx; // [] -L[1608] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1609] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[1610] = 9'bxx_xx_xxxxx; // [] -L[1611] = 9'bxx_xx_xxxxx; // [] -L[1612] = 9'bxx_xx_xxxxx; // [] -L[1613] = 9'bxx_xx_xxxxx; // [] -L[1614] = 9'bxx_xx_xxxxx; // [] -L[1615] = 9'bxx_xx_xxxxx; // [] -L[1616] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1617] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1618] = 9'bxx_xx_xxxxx; // [] -L[1619] = 9'bxx_xx_xxxxx; // [] -L[1620] = 9'bxx_xx_xxxxx; // [] -L[1621] = 9'bxx_xx_xxxxx; // [] -L[1622] = 9'bxx_xx_xxxxx; // [] -L[1623] = 9'bxx_xx_xxxxx; // [] -L[1624] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1625] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1626] = 9'bxx_xx_xxxxx; // [] -L[1627] = 9'bxx_xx_xxxxx; // [] -L[1628] = 9'bxx_xx_xxxxx; // [] -L[1629] = 9'bxx_xx_xxxxx; // [] -L[1630] = 9'bxx_xx_xxxxx; // [] -L[1631] = 9'bxx_xx_xxxxx; // [] -L[1632] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1633] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1634] = 9'b11_00_00001; // [PC++]->AH -L[1635] = 9'bxx_xx_xxxxx; // [] -L[1636] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1637] = 9'bxx_xx_xxxxx; // [] -L[1638] = 9'bxx_xx_xxxxx; // [] -L[1639] = 9'bxx_xx_xxxxx; // [] -L[1640] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1641] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1642] = 9'b11_00_00001; // [PC++]->AH -L[1643] = 9'bxx_xx_xxxxx; // [] -L[1644] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1645] = 9'bxx_xx_xxxxx; // [] -L[1646] = 9'bxx_xx_xxxxx; // [] -L[1647] = 9'bxx_xx_xxxxx; // [] -L[1648] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1649] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1650] = 9'b11_00_00001; // [PC++]->AH -L[1651] = 9'bxx_xx_xxxxx; // [] -L[1652] = 9'b00_01_00011; // [AX]->T -L[1653] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1654] = 9'b10_01_00101; // T->[AX] -L[1655] = 9'bxx_xx_xxxxx; // [] -L[1656] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1657] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1658] = 9'b11_00_00001; // [PC++]->AH -L[1659] = 9'bxx_xx_xxxxx; // [] -L[1660] = 9'b00_01_00011; // [AX]->T -L[1661] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1662] = 9'b10_01_00101; // T->[AX] -L[1663] = 9'bxx_xx_xxxxx; // [] -L[1664] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1665] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1666] = 9'b11_00_10001; // PC+T->PC -L[1667] = 9'bxx_xx_xxxxx; // [] -L[1668] = 9'b10_00_00011; // ['NO-OP', ''] -L[1669] = 9'bxx_xx_xxxxx; // [] -L[1670] = 9'bxx_xx_xxxxx; // [] -L[1671] = 9'bxx_xx_xxxxx; // [] -L[1672] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1673] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1674] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1675] = 9'b01_01_01100; // [AX]->AH,T+Y->AL -L[1676] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1677] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1678] = 9'bxx_xx_xxxxx; // [] -L[1679] = 9'bxx_xx_xxxxx; // [] -L[1680] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1681] = 9'b10_00_00011; // ['NO-OP', ''] -L[1682] = 9'bxx_xx_xxxxx; // [] -L[1683] = 9'bxx_xx_xxxxx; // [] -L[1684] = 9'bxx_xx_xxxxx; // [] -L[1685] = 9'bxx_xx_xxxxx; // [] -L[1686] = 9'bxx_xx_xxxxx; // [] -L[1687] = 9'bxx_xx_xxxxx; // [] -L[1688] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1689] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1690] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1691] = 9'b00_01_01100; // [AX]->AH,T+Y->AL -L[1692] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1693] = 9'b00_01_00011; // [AX]->T -L[1694] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1695] = 9'b10_01_00101; // T->[AX] -L[1696] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1697] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1698] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1699] = 9'bxx_xx_xxxxx; // [] -L[1700] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[1701] = 9'bxx_xx_xxxxx; // [] -L[1702] = 9'bxx_xx_xxxxx; // [] -L[1703] = 9'bxx_xx_xxxxx; // [] -L[1704] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1705] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1706] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1707] = 9'bxx_xx_xxxxx; // [] -L[1708] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1709] = 9'bxx_xx_xxxxx; // [] -L[1710] = 9'bxx_xx_xxxxx; // [] -L[1711] = 9'bxx_xx_xxxxx; // [] -L[1712] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1713] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1714] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1715] = 9'bxx_xx_xxxxx; // [] -L[1716] = 9'b00_01_00011; // [AX]->T -L[1717] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1718] = 9'b10_01_00101; // T->[AX] -L[1719] = 9'bxx_xx_xxxxx; // [] -L[1720] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1721] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1722] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1723] = 9'bxx_xx_xxxxx; // [] -L[1724] = 9'b00_01_00011; // [AX]->T -L[1725] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1726] = 9'b10_01_00101; // T->[AX] -L[1727] = 9'bxx_xx_xxxxx; // [] -L[1728] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1729] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1730] = 9'bxx_xx_xxxxx; // [] -L[1731] = 9'bxx_xx_xxxxx; // [] -L[1732] = 9'bxx_xx_xxxxx; // [] -L[1733] = 9'bxx_xx_xxxxx; // [] -L[1734] = 9'bxx_xx_xxxxx; // [] -L[1735] = 9'bxx_xx_xxxxx; // [] -L[1736] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1737] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1738] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1739] = 9'bxx_xx_xxxxx; // [] -L[1740] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1741] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1742] = 9'bxx_xx_xxxxx; // [] -L[1743] = 9'bxx_xx_xxxxx; // [] -L[1744] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1745] = 9'b10_00_00011; // ['NO-OP', ''] -L[1746] = 9'bxx_xx_xxxxx; // [] -L[1747] = 9'bxx_xx_xxxxx; // [] -L[1748] = 9'bxx_xx_xxxxx; // [] -L[1749] = 9'bxx_xx_xxxxx; // [] -L[1750] = 9'bxx_xx_xxxxx; // [] -L[1751] = 9'bxx_xx_xxxxx; // [] -L[1752] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1753] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1754] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1755] = 9'bxx_xx_xxxxx; // [] -L[1756] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1757] = 9'b00_01_00011; // [AX]->T -L[1758] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1759] = 9'b10_01_00101; // T->[AX] -L[1760] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1761] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1762] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1763] = 9'bxx_xx_xxxxx; // [] -L[1764] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1765] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[1766] = 9'bxx_xx_xxxxx; // [] -L[1767] = 9'bxx_xx_xxxxx; // [] -L[1768] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1769] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1770] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1771] = 9'bxx_xx_xxxxx; // [] -L[1772] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1773] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1774] = 9'bxx_xx_xxxxx; // [] -L[1775] = 9'bxx_xx_xxxxx; // [] -L[1776] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1777] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1778] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1779] = 9'bxx_xx_xxxxx; // [] -L[1780] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1781] = 9'b00_01_00011; // [AX]->T -L[1782] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1783] = 9'b10_01_00101; // T->[AX] -L[1784] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1785] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1786] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1787] = 9'bxx_xx_xxxxx; // [] -L[1788] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1789] = 9'b00_01_00011; // [AX]->T -L[1790] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1791] = 9'b10_01_00101; // T->[AX] -L[1792] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1793] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[1794] = 9'bxx_xx_xxxxx; // [] -L[1795] = 9'bxx_xx_xxxxx; // [] -L[1796] = 9'bxx_xx_xxxxx; // [] -L[1797] = 9'bxx_xx_xxxxx; // [] -L[1798] = 9'bxx_xx_xxxxx; // [] -L[1799] = 9'bxx_xx_xxxxx; // [] -L[1800] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1801] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1802] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[1803] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1804] = 9'b00_01_01011; // [AX]->AH,T->AL -L[1805] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1806] = 9'bxx_xx_xxxxx; // [] -L[1807] = 9'bxx_xx_xxxxx; // [] -L[1808] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1809] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1810] = 9'bxx_xx_xxxxx; // [] -L[1811] = 9'bxx_xx_xxxxx; // [] -L[1812] = 9'bxx_xx_xxxxx; // [] -L[1813] = 9'bxx_xx_xxxxx; // [] -L[1814] = 9'bxx_xx_xxxxx; // [] -L[1815] = 9'bxx_xx_xxxxx; // [] -L[1816] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1817] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1818] = 9'b00_01_00111; // [AX]->?,AL+X->AL -L[1819] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1820] = 9'b00_01_01011; // [AX]->AH,T->AL -L[1821] = 9'b00_01_00011; // [AX]->T -L[1822] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1823] = 9'b10_01_00101; // T->[AX] -L[1824] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1825] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1826] = 9'bxx_xx_xxxxx; // [] -L[1827] = 9'bxx_xx_xxxxx; // [] -L[1828] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1829] = 9'bxx_xx_xxxxx; // [] -L[1830] = 9'bxx_xx_xxxxx; // [] -L[1831] = 9'bxx_xx_xxxxx; // [] -L[1832] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1833] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1834] = 9'bxx_xx_xxxxx; // [] -L[1835] = 9'bxx_xx_xxxxx; // [] -L[1836] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1837] = 9'bxx_xx_xxxxx; // [] -L[1838] = 9'bxx_xx_xxxxx; // [] -L[1839] = 9'bxx_xx_xxxxx; // [] -L[1840] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1841] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1842] = 9'bxx_xx_xxxxx; // [] -L[1843] = 9'bxx_xx_xxxxx; // [] -L[1844] = 9'b00_01_00011; // [AX]->T -L[1845] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1846] = 9'b10_01_00101; // T->[AX] -L[1847] = 9'bxx_xx_xxxxx; // [] -L[1848] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1849] = 9'b11_00_00000; // ['[PC++]->AL', '[PC++]->T'] -L[1850] = 9'bxx_xx_xxxxx; // [] -L[1851] = 9'bxx_xx_xxxxx; // [] -L[1852] = 9'b00_01_00011; // [AX]->T -L[1853] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1854] = 9'b10_01_00101; // T->[AX] -L[1855] = 9'bxx_xx_xxxxx; // [] -L[1856] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1857] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1858] = 9'bxx_xx_xxxxx; // [] -L[1859] = 9'bxx_xx_xxxxx; // [] -L[1860] = 9'bxx_xx_xxxxx; // [] -L[1861] = 9'bxx_xx_xxxxx; // [] -L[1862] = 9'bxx_xx_xxxxx; // [] -L[1863] = 9'bxx_xx_xxxxx; // [] -L[1864] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1865] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[1866] = 9'bxx_xx_xxxxx; // [] -L[1867] = 9'bxx_xx_xxxxx; // [] -L[1868] = 9'bxx_xx_xxxxx; // [] -L[1869] = 9'bxx_xx_xxxxx; // [] -L[1870] = 9'bxx_xx_xxxxx; // [] -L[1871] = 9'bxx_xx_xxxxx; // [] -L[1872] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1873] = 9'b10_00_00011; // ['NO-OP', ''] -L[1874] = 9'bxx_xx_xxxxx; // [] -L[1875] = 9'bxx_xx_xxxxx; // [] -L[1876] = 9'bxx_xx_xxxxx; // [] -L[1877] = 9'bxx_xx_xxxxx; // [] -L[1878] = 9'bxx_xx_xxxxx; // [] -L[1879] = 9'bxx_xx_xxxxx; // [] -L[1880] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1881] = 9'b10_00_01101; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -L[1882] = 9'bxx_xx_xxxxx; // [] -L[1883] = 9'bxx_xx_xxxxx; // [] -L[1884] = 9'bxx_xx_xxxxx; // [] -L[1885] = 9'bxx_xx_xxxxx; // [] -L[1886] = 9'bxx_xx_xxxxx; // [] -L[1887] = 9'bxx_xx_xxxxx; // [] -L[1888] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1889] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1890] = 9'b11_00_00001; // [PC++]->AH -L[1891] = 9'bxx_xx_xxxxx; // [] -L[1892] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1893] = 9'bxx_xx_xxxxx; // [] -L[1894] = 9'bxx_xx_xxxxx; // [] -L[1895] = 9'bxx_xx_xxxxx; // [] -L[1896] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1897] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1898] = 9'b11_00_00001; // [PC++]->AH -L[1899] = 9'bxx_xx_xxxxx; // [] -L[1900] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1901] = 9'bxx_xx_xxxxx; // [] -L[1902] = 9'bxx_xx_xxxxx; // [] -L[1903] = 9'bxx_xx_xxxxx; // [] -L[1904] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1905] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1906] = 9'b11_00_00001; // [PC++]->AH -L[1907] = 9'bxx_xx_xxxxx; // [] -L[1908] = 9'b00_01_00011; // [AX]->T -L[1909] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1910] = 9'b10_01_00101; // T->[AX] -L[1911] = 9'bxx_xx_xxxxx; // [] -L[1912] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1913] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1914] = 9'b11_00_00001; // [PC++]->AH -L[1915] = 9'bxx_xx_xxxxx; // [] -L[1916] = 9'b00_01_00011; // [AX]->T -L[1917] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1918] = 9'b10_01_00101; // T->[AX] -L[1919] = 9'bxx_xx_xxxxx; // [] -L[1920] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1921] = 9'b10_00_00000; // ['[PC++]->?', 'PC+1->PC', ''] -L[1922] = 9'b11_00_10001; // PC+T->PC -L[1923] = 9'bxx_xx_xxxxx; // [] -L[1924] = 9'b10_00_00011; // ['NO-OP', ''] -L[1925] = 9'bxx_xx_xxxxx; // [] -L[1926] = 9'bxx_xx_xxxxx; // [] -L[1927] = 9'bxx_xx_xxxxx; // [] -L[1928] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1929] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1930] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1931] = 9'b01_01_01100; // [AX]->AH,T+Y->AL -L[1932] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1933] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1934] = 9'bxx_xx_xxxxx; // [] -L[1935] = 9'bxx_xx_xxxxx; // [] -L[1936] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1937] = 9'b10_00_00011; // ['NO-OP', ''] -L[1938] = 9'bxx_xx_xxxxx; // [] -L[1939] = 9'bxx_xx_xxxxx; // [] -L[1940] = 9'bxx_xx_xxxxx; // [] -L[1941] = 9'bxx_xx_xxxxx; // [] -L[1942] = 9'bxx_xx_xxxxx; // [] -L[1943] = 9'bxx_xx_xxxxx; // [] -L[1944] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1945] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1946] = 9'b00_01_01010; // [AX]->T,AL+1->AL -L[1947] = 9'b00_01_01100; // [AX]->AH,T+Y->AL -L[1948] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1949] = 9'b00_01_00011; // [AX]->T -L[1950] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1951] = 9'b10_01_00101; // T->[AX] -L[1952] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1953] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1954] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1955] = 9'bxx_xx_xxxxx; // [] -L[1956] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[1957] = 9'bxx_xx_xxxxx; // [] -L[1958] = 9'bxx_xx_xxxxx; // [] -L[1959] = 9'bxx_xx_xxxxx; // [] -L[1960] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1961] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1962] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1963] = 9'bxx_xx_xxxxx; // [] -L[1964] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1965] = 9'bxx_xx_xxxxx; // [] -L[1966] = 9'bxx_xx_xxxxx; // [] -L[1967] = 9'bxx_xx_xxxxx; // [] -L[1968] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1969] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1970] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1971] = 9'bxx_xx_xxxxx; // [] -L[1972] = 9'b00_01_00011; // [AX]->T -L[1973] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1974] = 9'b10_01_00101; // T->[AX] -L[1975] = 9'bxx_xx_xxxxx; // [] -L[1976] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1977] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1978] = 9'b11_01_00111; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL'] -L[1979] = 9'bxx_xx_xxxxx; // [] -L[1980] = 9'b00_01_00011; // [AX]->T -L[1981] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[1982] = 9'b10_01_00101; // T->[AX] -L[1983] = 9'bxx_xx_xxxxx; // [] -L[1984] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1985] = 9'b10_00_00010; // ['[PC]->,ALU()->A', 'Setappropriateflags', 'ALU()->X,Y', 'ALU()->A'] -L[1986] = 9'bxx_xx_xxxxx; // [] -L[1987] = 9'bxx_xx_xxxxx; // [] -L[1988] = 9'bxx_xx_xxxxx; // [] -L[1989] = 9'bxx_xx_xxxxx; // [] -L[1990] = 9'bxx_xx_xxxxx; // [] -L[1991] = 9'bxx_xx_xxxxx; // [] -L[1992] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1993] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[1994] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[1995] = 9'bxx_xx_xxxxx; // [] -L[1996] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[1997] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[1998] = 9'bxx_xx_xxxxx; // [] -L[1999] = 9'bxx_xx_xxxxx; // [] -L[2000] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2001] = 9'b10_00_00011; // ['NO-OP', ''] -L[2002] = 9'bxx_xx_xxxxx; // [] -L[2003] = 9'bxx_xx_xxxxx; // [] -L[2004] = 9'bxx_xx_xxxxx; // [] -L[2005] = 9'bxx_xx_xxxxx; // [] -L[2006] = 9'bxx_xx_xxxxx; // [] -L[2007] = 9'bxx_xx_xxxxx; // [] -L[2008] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2009] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2010] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[2011] = 9'bxx_xx_xxxxx; // [] -L[2012] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[2013] = 9'b00_01_00011; // [AX]->T -L[2014] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[2015] = 9'b10_01_00101; // T->[AX] -L[2016] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2017] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2018] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[2019] = 9'bxx_xx_xxxxx; // [] -L[2020] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[2021] = 9'b10_01_00011; // ['ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->'] -L[2022] = 9'bxx_xx_xxxxx; // [] -L[2023] = 9'bxx_xx_xxxxx; // [] -L[2024] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2025] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2026] = 9'b01_00_01000; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[2027] = 9'bxx_xx_xxxxx; // [] -L[2028] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[2029] = 9'b10_01_00010; // ['ALU([AX])->A', 'ALU([AX])->?'] -L[2030] = 9'bxx_xx_xxxxx; // [] -L[2031] = 9'bxx_xx_xxxxx; // [] -L[2032] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2033] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2034] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[2035] = 9'bxx_xx_xxxxx; // [] -L[2036] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[2037] = 9'b00_01_00011; // [AX]->T -L[2038] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[2039] = 9'b10_01_00101; // T->[AX] -L[2040] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2041] = 9'b00_00_00000; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T'] -L[2042] = 9'b11_00_01000; // ['[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+Y->AL'] -L[2043] = 9'bxx_xx_xxxxx; // [] -L[2044] = 9'b00_01_01001; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -L[2045] = 9'b00_01_00011; // [AX]->T -L[2046] = 9'b00_01_00100; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -L[2047] = 9'b10_01_00101; // T->[AX] - end - always @(posedge clk) if (reset) begin - M <= 0; // Stupid XILINX inferral only allows 0 as reset value. - end else if (ce) begin - M <= L[{IR, State}]; - end -endmodule - -module MicroCodeTable(input clk, input ce, input reset, input [7:0] IR, input [2:0] State, output [37:0] Mout); - wire [8:0] M; - MicroCodeTableInner inner(clk, ce, reset, IR, State, M); - reg [14:0] A[0:31]; - reg [18:0] B[0:255]; - initial begin -A[0] = 15'b_10__0_10101_0xx_01_00; // ['[PC++]', '[PC++]->AL', '[PC++]->?', '[PC++]->T', 'PC+1->PC', ''] -A[1] = 15'b_xx__0_0xx11_0xx_01_00; // [PC++]->AH -A[2] = 15'b_xx__1_00000_0xx_00_00; // ['ALU([AX])->A', 'ALU([AX])->?', '[PC]->,ALU()->A', 'Setappropriateflags', 'ALU([SP])->A', '[SP]->P', 'ALU()->X,Y', 'ALU()->A'] -A[3] = 15'b_10__0_00000_0xx_00_00; // ['[AX]->T', 'ALU([AX])->?', 'ALU([AX])->A', 'ALU([AX])->', '[PC]->', 'NO-OP', '[VECT]->T', ''] -A[4] = 15'b_11__1_00000_100_00_00; // ['T->[AX],ALU(T)->T', 'T->[AX],ALU(T)->T:A'] -A[5] = 15'b_xx__0_xxxxx_100_00_00; // T->[AX] -A[6] = 15'b_xx__0_00000_101_00_00; // ALU()->[AX] -A[7] = 15'b_0x__0_10000_0xx_00_00; // ['[AX]->?,AL+X->AL', '[AX]->?,AL+X/Y->AL', 'KEEP_AC'] -A[8] = 15'b_xx__0_10011_0xx_01_00; // ['[PC++]->AH,AL+X/Y->AL', '[PC++]->AH,AL+X->AL', '[PC++]->AH,AL+Y->AL'] -A[9] = 15'b_10__0_0xx10_0xx_00_00; // ['[AX]->?,AH+FIX->AH', '[AX]->T,AH+FIX->AH'] -A[10] = 15'b_10__0_11000_0xx_00_00; // [AX]->T,AL+1->AL -A[11] = 15'b_xx__0_11111_0xx_00_00; // [AX]->AH,T->AL -A[12] = 15'b_xx__0_10011_0xx_00_00; // [AX]->AH,T+Y->AL -A[13] = 15'b_xx__1_xxxxx_0xx_01_00; // ['ALU([PC++])->A', 'ALU([PC++])->?', 'ALU([PC++])->Reg'] -A[14] = 15'b_xx__0_xxxxx_101_00_11; // ALU(A)->[SP--] -A[15] = 15'b_xx__0_xxxxx_110_00_11; // P->[SP--] -A[16] = 15'b_10__0_xxxxx_0xx_00_10; // ['SP++', 'SP+1->SP', '[SP]->T,SP+1->SP'] -A[17] = 15'b_xx__0_xxxxx_0xx_11_00; // PC+T->PC -A[18] = 15'b_xx__0_xxxxx_0xx_00_01; // X->S -A[19] = 15'b_xx__0_xxxxx_0xx_10_00; // ['[PC]:T->PC', '[AX]:T->PC', '[VECT]:T->PC', '[SP]:T->PC'] -A[20] = 15'b_0x__0_xxxxx_111_00_11; // ['PCH->[SP--]', 'PCL->[SP--],', 'PCH->[SP--](KEEPAC)', 'PCL->[SP--](KEEPAC)'] -A[21] = 15'b_xx__1_xxxxx_110_00_11; // P->[SP--] -A[22] = 15'b_xx__1_xxxxx_0xx_00_10; // [SP]->P,SP+1->SP -A[23] = 15'b_xx__x_xxxxx_xxx_xx_xx; // [] -A[24] = 15'b_xx__x_xxxxx_xxx_xx_xx; // [] -A[25] = 15'b_xx__x_xxxxx_xxx_xx_xx; // [] -A[26] = 15'b_xx__x_xxxxx_xxx_xx_xx; // [] -A[27] = 15'b_xx__x_xxxxx_xxx_xx_xx; // [] -A[28] = 15'b_xx__x_xxxxx_xxx_xx_xx; // [] -A[29] = 15'b_xx__x_xxxxx_xxx_xx_xx; // [] -A[30] = 15'b_xx__x_xxxxx_xxx_xx_xx; // [] -A[31] = 15'b_xx__x_xxxxx_xxx_xx_xx; // [] -B[0] = 19'bxxxxxxxxxx0_000_00_010; -B[32] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[64] = 19'bxxxxxxxxxx0_000_00_100; -B[96] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[128] = 19'b011010x10x0_xxx_00_xxx; -B[160] = 19'bxx0010x10x0_100_00_001; -B[192] = 19'b010010x1100_000_00_001; -B[224] = 19'b100010x1100_000_00_001; -B[1] = 19'b000010x0001_010_00_001; -B[33] = 19'b000010x0011_010_00_001; -B[65] = 19'b000010x0101_010_00_001; -B[97] = 19'b000010x0111_010_00_001; -B[129] = 19'b001010x10x1_xxx_00_xxx; -B[161] = 19'bxx0010x10x1_010_00_001; -B[193] = 19'b000010x1101_000_00_001; -B[225] = 19'b000010x1111_010_00_001; -B[2] = 19'bxx0100010x0_000_00_001; -B[34] = 19'bxx0100110x0_000_00_001; -B[66] = 19'bxx0101010x0_000_00_001; -B[98] = 19'bxx0101110x0_000_00_001; -B[130] = 19'b101010x10x0_xxx_00_xxx; -B[162] = 19'bxx0010x10x0_001_00_001; -B[194] = 19'bxx0111010x0_000_00_001; -B[226] = 19'bxx0111110x0_000_00_001; -B[3] = 19'b00010000001_010_00_001; -B[35] = 19'b00010010011_010_00_001; -B[67] = 19'b00010100101_010_00_001; -B[99] = 19'b00010110111_010_00_001; -B[131] = 19'b111010x10x1_xxx_00_xxx; -B[163] = 19'bxx0010x10x1_011_00_001; -B[195] = 19'b00011101101_000_00_001; -B[227] = 19'b00011111111_010_00_001; -B[4] = 19'b000010x0000_xxx_00_xxx; -B[36] = 19'b000010x0010_000_00_001; -B[68] = 19'b000010x0100_xxx_00_xxx; -B[100] = 19'b000010x0110_xxx_00_xxx; -B[132] = 19'b011010x10x0_xxx_00_xxx; -B[164] = 19'bxx0010x10x0_100_00_001; -B[196] = 19'b010010x1100_000_00_001; -B[228] = 19'b100010x1100_000_00_001; -B[5] = 19'b000010x0001_010_00_001; -B[37] = 19'b000010x0011_010_00_001; -B[69] = 19'b000010x0101_010_00_001; -B[101] = 19'b000010x0111_010_00_001; -B[133] = 19'b001010x10x1_xxx_00_xxx; -B[165] = 19'bxx0010x10x1_010_00_001; -B[197] = 19'b000010x1101_000_00_001; -B[229] = 19'b000010x1111_010_00_001; -B[6] = 19'bxx0100010x0_000_00_001; -B[38] = 19'bxx0100110x0_000_00_001; -B[70] = 19'bxx0101010x0_000_00_001; -B[102] = 19'bxx0101110x0_000_00_001; -B[134] = 19'b101010x10x0_xxx_00_xxx; -B[166] = 19'bxx0010x10x0_001_00_001; -B[198] = 19'bxx0111010x0_000_00_001; -B[230] = 19'bxx0111110x0_000_00_001; -B[7] = 19'b00010000001_010_00_001; -B[39] = 19'b00010010011_010_00_001; -B[71] = 19'b00010100101_010_00_001; -B[103] = 19'b00010110111_010_00_001; -B[135] = 19'b111010x10x1_xxx_00_xxx; -B[167] = 19'bxx0010x10x1_011_00_001; -B[199] = 19'b00011101101_000_00_001; -B[231] = 19'b00011111111_010_00_001; -B[8] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[40] = 19'bxxxxxxxxxx0_000_00_100; -B[72] = 19'b001010x10x0_xxx_00_xxx; -B[104] = 19'bxx0010x10x0_010_00_001; -B[136] = 19'b011011010x0_100_00_001; -B[168] = 19'b001010x10x0_100_00_001; -B[200] = 19'b011011110x0_100_00_001; -B[232] = 19'b101011110x0_001_00_001; -B[9] = 19'b000010x0001_010_00_001; -B[41] = 19'b000010x0011_010_00_001; -B[73] = 19'b000010x0101_010_00_001; -B[105] = 19'b000010x0111_010_00_001; -B[137] = 19'b001010x10x1_xxx_00_xxx; -B[169] = 19'bxx0010x10x1_010_00_001; -B[201] = 19'b000010x1101_000_00_001; -B[233] = 19'b000010x1111_010_00_001; -B[10] = 19'b001000010x0_010_00_001; -B[42] = 19'b001000110x0_010_00_001; -B[74] = 19'b001001010x0_010_00_001; -B[106] = 19'b001001110x0_010_00_001; -B[138] = 19'b101010x10x0_010_00_001; -B[170] = 19'b001010x10x0_001_00_001; -B[202] = 19'b101011010x0_001_00_001; -B[234] = 19'bxx0111110x0_000_00_001; -B[11] = 19'b000010x0001_010_00_001; -B[43] = 19'b000010x0011_010_00_001; -B[75] = 19'b000010x0101_010_00_001; -B[107] = 19'b000010x0111_010_00_001; -B[139] = 19'b111010x10x1_xxx_00_xxx; -B[171] = 19'bxx0010x10x1_011_00_001; -B[203] = 19'b000010x1101_000_00_001; -B[235] = 19'b000010x1111_010_00_001; -B[12] = 19'b000010x0000_xxx_00_xxx; -B[44] = 19'b000010x0010_000_00_001; -B[76] = 19'bxxxxxxxxxx0_000_00_001; -B[108] = 19'bxxxxxxxxxx0_000_00_001; -B[140] = 19'b011010x10x0_xxx_00_xxx; -B[172] = 19'bxx0010x10x0_100_00_001; -B[204] = 19'b010010x1100_000_00_001; -B[236] = 19'b100010x1100_000_00_001; -B[13] = 19'b000010x0001_010_00_001; -B[45] = 19'b000010x0011_010_00_001; -B[77] = 19'b000010x0101_010_00_001; -B[109] = 19'b000010x0111_010_00_001; -B[141] = 19'b001010x10x1_xxx_00_xxx; -B[173] = 19'bxx0010x10x1_010_00_001; -B[205] = 19'b000010x1101_000_00_001; -B[237] = 19'b000010x1111_010_00_001; -B[14] = 19'bxx0100010x0_000_00_001; -B[46] = 19'bxx0100110x0_000_00_001; -B[78] = 19'bxx0101010x0_000_00_001; -B[110] = 19'bxx0101110x0_000_00_001; -B[142] = 19'b101010x10x0_xxx_00_xxx; -B[174] = 19'bxx0010x10x0_001_00_001; -B[206] = 19'bxx0111010x0_000_00_001; -B[238] = 19'bxx0111110x0_000_00_001; -B[15] = 19'b00010000001_010_00_001; -B[47] = 19'b00010010011_010_00_001; -B[79] = 19'b00010100101_010_00_001; -B[111] = 19'b00010110111_010_00_001; -B[143] = 19'b111010x10x1_xxx_00_xxx; -B[175] = 19'bxx0010x10x1_011_00_001; -B[207] = 19'b00011101101_000_00_001; -B[239] = 19'b00011111111_010_00_001; -B[16] = 19'bxxxxxxxxxx0_xxx_11_xxx; -B[48] = 19'bxxxxxxxxxx0_xxx_11_xxx; -B[80] = 19'bxxxxxxxxxx0_xxx_11_xxx; -B[112] = 19'bxxxxxxxxxx0_xxx_11_xxx; -B[144] = 19'b011010x10x0_xxx_11_xxx; -B[176] = 19'bxx0010x10x0_xxx_11_xxx; -B[208] = 19'bxxxxxxxxxx0_xxx_11_xxx; -B[240] = 19'bxxxxxxxxxx0_xxx_11_xxx; -B[17] = 19'b000010x0001_010_11_001; -B[49] = 19'b000010x0011_010_11_001; -B[81] = 19'b000010x0101_010_11_001; -B[113] = 19'b000010x0111_010_11_001; -B[145] = 19'b001010x10x1_xxx_11_xxx; -B[177] = 19'bxx0010x10x1_010_11_001; -B[209] = 19'b000010x1101_000_11_001; -B[241] = 19'b000010x1111_010_11_001; -B[18] = 19'bxx0100010x0_000_11_001; -B[50] = 19'bxx0100110x0_000_11_001; -B[82] = 19'bxx0101010x0_000_11_001; -B[114] = 19'bxx0101110x0_000_11_001; -B[146] = 19'b101010x10x0_xxx_11_xxx; -B[178] = 19'bxx0010x10x0_xxx_11_xxx; -B[210] = 19'bxx0111010x0_000_11_001; -B[242] = 19'bxx0111110x0_000_11_001; -B[19] = 19'b00010000001_010_11_001; -B[51] = 19'b00010010011_010_11_001; -B[83] = 19'b00010100101_010_11_001; -B[115] = 19'b00010110111_010_11_001; -B[147] = 19'b111010x10x1_xxx_11_xxx; -B[179] = 19'bxx0010x10x1_011_11_001; -B[211] = 19'b00011101101_000_11_001; -B[243] = 19'b00011111111_010_11_001; -B[20] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[52] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[84] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[116] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[148] = 19'b011010x10x0_xxx_00_xxx; -B[180] = 19'bxx0010x10x0_100_00_001; -B[212] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[244] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[21] = 19'b000010x0001_010_00_001; -B[53] = 19'b000010x0011_010_00_001; -B[85] = 19'b000010x0101_010_00_001; -B[117] = 19'b000010x0111_010_00_001; -B[149] = 19'b001010x10x1_xxx_00_xxx; -B[181] = 19'bxx0010x10x1_010_00_001; -B[213] = 19'b000010x1101_000_00_001; -B[245] = 19'b000010x1111_010_00_001; -B[22] = 19'bxx0100010x0_000_00_001; -B[54] = 19'bxx0100110x0_000_00_001; -B[86] = 19'bxx0101010x0_000_00_001; -B[118] = 19'bxx0101110x0_000_00_001; -B[150] = 19'b101010x10x0_xxx_10_xxx; -B[182] = 19'bxx0010x10x0_001_10_001; -B[214] = 19'bxx0111010x0_000_00_001; -B[246] = 19'bxx0111110x0_000_00_001; -B[23] = 19'b00010000001_010_00_001; -B[55] = 19'b00010010011_010_00_001; -B[87] = 19'b00010100101_010_00_001; -B[119] = 19'b00010110111_010_00_001; -B[151] = 19'b111010x10x1_xxx_10_xxx; -B[183] = 19'bxx0010x10x1_011_10_001; -B[215] = 19'b00011101101_000_00_001; -B[247] = 19'b00011111111_010_00_001; -B[24] = 19'bxxxxxxxxxx0_000_10_101; -B[56] = 19'bxxxxxxxxxx0_000_10_101; -B[88] = 19'bxxxxxxxxxx0_000_10_110; -B[120] = 19'bxxxxxxxxxx0_000_10_110; -B[152] = 19'b011010x10x0_010_10_001; -B[184] = 19'bxx1110x10x0_000_10_011; -B[216] = 19'bxxxxxxxxxx0_000_10_111; -B[248] = 19'bxxxxxxxxxx0_000_10_111; -B[25] = 19'b000010x0001_010_10_001; -B[57] = 19'b000010x0011_010_10_001; -B[89] = 19'b000010x0101_010_10_001; -B[121] = 19'b000010x0111_010_10_001; -B[153] = 19'b001010x10x1_xxx_10_xxx; -B[185] = 19'bxx0010x10x1_010_10_001; -B[217] = 19'b000010x1101_000_10_001; -B[249] = 19'b000010x1111_010_10_001; -B[26] = 19'bxx0100010x0_000_10_001; -B[58] = 19'bxx0100110x0_000_10_001; -B[90] = 19'bxx0101010x0_000_10_001; -B[122] = 19'bxx0101110x0_000_10_001; -B[154] = 19'b101010x10x0_xxx_10_xxx; -B[186] = 19'bxx1110x10x0_001_10_001; -B[218] = 19'bxx0111010x0_000_10_001; -B[250] = 19'bxx0111110x0_000_10_001; -B[27] = 19'b00010000001_010_10_001; -B[59] = 19'b00010010011_010_10_001; -B[91] = 19'b00010100101_010_10_001; -B[123] = 19'b00010110111_010_10_001; -B[155] = 19'b111010x10x1_xxx_10_xxx; -B[187] = 19'bxx0010x10x1_xxx_10_xxx; -B[219] = 19'b00011101101_000_10_001; -B[251] = 19'b00011111111_010_10_001; -B[28] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[60] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[92] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[124] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[156] = 19'b011010x10x0_xxx_00_xxx; -B[188] = 19'bxx0010x10x0_100_00_001; -B[220] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[252] = 19'bxxxxxxxxxx0_xxx_00_xxx; -B[29] = 19'b000010x0001_010_00_001; -B[61] = 19'b000010x0011_010_00_001; -B[93] = 19'b000010x0101_010_00_001; -B[125] = 19'b000010x0111_010_00_001; -B[157] = 19'b001010x10x1_xxx_00_xxx; -B[189] = 19'bxx0010x10x1_010_00_001; -B[221] = 19'b000010x1101_000_00_001; -B[253] = 19'b000010x1111_010_00_001; -B[30] = 19'bxx0100010x0_000_00_001; -B[62] = 19'bxx0100110x0_000_00_001; -B[94] = 19'bxx0101010x0_000_00_001; -B[126] = 19'bxx0101110x0_000_00_001; -B[158] = 19'b101010x10x0_xxx_10_xxx; -B[190] = 19'bxx0010x10x0_001_10_001; -B[222] = 19'bxx0111010x0_000_00_001; -B[254] = 19'bxx0111110x0_000_00_001; -B[31] = 19'b00010000001_010_00_001; -B[63] = 19'b00010010011_010_00_001; -B[95] = 19'b00010100101_010_00_001; -B[127] = 19'b00010110111_010_00_001; -B[159] = 19'b111010x10x1_xxx_10_xxx; -B[191] = 19'bxx0010x10x1_011_10_001; -B[223] = 19'b00011101101_000_00_001; -B[255] = 19'b00011111111_010_00_001; - end - wire [14:0] R = A[M[4:0]]; - reg [18:0] AluFlags; - always @(posedge clk) if (reset) begin - AluFlags <= 0; - end else if (ce) begin - AluFlags <= B[IR]; - end - - assign Mout = {AluFlags,// 19 - M[8:7], // NextState // 2 - R[14:13],// LoadT // 2 - R[12], // FlagCtrl // 1 - R[11:7], // AddrCtrl // 5 - R[6:4], // MemWrite // 3 - M[6:5], // AddrBus // 2 - R[3:2], // LoadPC // 2 - R[1:0] // LoadSP // 2 - }; -endmodule - diff --git a/cores/nes/src/apu.sv b/cores/nes/src/apu.sv index d9d73c8..92bf0d0 100644 --- a/cores/nes/src/apu.sv +++ b/cores/nes/src/apu.sv @@ -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 diff --git a/cores/nes/src/cart.sv b/cores/nes/src/cart.sv new file mode 100644 index 0000000..10f2796 --- /dev/null +++ b/cores/nes/src/cart.sv @@ -0,0 +1,1850 @@ +// Mapper top level selection + +// Notes by Kitrinx: +// This module uses bidirectional ports to handle the data out of each mapper. +// Although FPGA's do not use bidirectional wiring internally, the alternative is +// creating a tenticle-monster of wires for each mapper, and muxing them all together. +// As it stands, the compiler will efficiently do it for us with much more readable and +// manageable code if we do it this way, and since not more than one mapper can be active +// at a time, there will be no conflicts. + +// SDRAM Locations for various RAM types: +// PRG = 0.... +// CHR = 10... +// CHR-VRAM = 1100 +// CPU-RAM = 1110 +// CARTRAM = 1111 + +module cart_top ( + input clk, + input ce, + input ppu_ce, + input reset, + input [19:0] ppuflags, // Misc flags from PPU for MMC5 cheating + input [31:0] flags, // Misc flags from ines header {prg_size(3), chr_size(3), mapper(8)} + input [15:0] prg_ain, // Better known as "CPU Address in" + output reg [21:0] prg_aout, // PRG Input / Output Address Lines + input prg_read, // PRG Read / write signals + input prg_write, + input [7:0] prg_din, // CPU Data In + output reg [7:0] prg_dout, // CPU Data Out + input [7:0] prg_from_ram, // PRG Data from RAM + output reg prg_allow, // PRG Allow write access + output reg prg_bus_write, // PRG Data Driven + output reg prg_conflict, // PRG Data is ROM & prg_din + input chr_ex, // chr_addr is from an extra sprite read if high + input chr_read, // Read from CHR + input chr_write, // Write to CHR + input [7:0] chr_din, // PPU Data In + input [13:0] chr_ain_orig, // Better known as "PPU Address in" + input [13:0] chr_ain_ex, // Address for extra sprite fetches + output reg [21:0] chr_aout, // CHR Input / Output Address Lines + output reg [7:0] chr_dout, // Value to override CHR data with + output reg has_chr_dout, // True if CHR data should be overridden + output reg chr_allow, // CHR Allow write + output reg vram_a10, // CHR Value for A10 address line + output reg vram_ce, // CHR True if the address should be routed to the internal 2kB VRAM. + output reg [17:0] mapper_addr, + input [7:0] mapper_data_in, + output reg [7:0] mapper_data_out, + output reg mapper_prg_write, + output reg mapper_ovr, + output reg irq, + input [15:0] audio_in, + output reg [15:0] audio, // External Audio + output reg [1:0] diskside_auto, + input [1:0] diskside, + input fds_busy, // FDS Disk Swap Busy + input fds_eject // FDS Disk Swap Pause +); + +tri0 prg_allow_b, vram_a10_b, vram_ce_b, chr_allow_b, irq_b; +tri0 [21:0] prg_addr_b, chr_addr_b; +tri0 [15:0] flags_out_b, audio_out_b; +tri1 [7:0] prg_dout_b, chr_dout_b; + +wire [13:0] chr_ain = chr_ex ? chr_ain_ex : chr_ain_orig; + +// This mapper used to be default if no other mapper was found +// It seems MMC0 is handled by map28. Does it have any purpose? +// flags_out_b will be high if no other mappers are selected, so we use that. +wire [15:0] mmc0_flags; +MMC0 mmc0( + .clk (clk), + .ce (ce), + .enable (1'b0), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(mmc0_flags), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : MMC1 // +// Mappers: 1, 155, 171 (hard wired vertical mirroring) // +// Status : Working // +// Notes : // +// Games : Simon's Quest // +//*****************************************************************************// +MMC1 mmc1( + .clk (clk), + .ce (ce), + .enable (me[171] | me[155] | me[1]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Tepples // +// Mappers: 0, 2, 3, 7, 28, 94, 180 // +// Status : Working // +// Notes : This mapper relies on open bus and bus conflict behavior. // +// Games : Donkey Kong // +//*****************************************************************************// +wire mapper28_en = me[0] | me[2] | me[3] | me[7] | me[94] | me[180] | me[185] | me[28]; +Mapper28 map28( + .clk (clk), + .ce (ce), + .enable (mapper28_en & ~reset), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_dout_b (chr_dout_b), // Special port + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : UNROM 512 // +// Mappers: 30 // +// Status : No Self Flashing/Needs testing // +// Notes : Homebrew mapper // +// Games : ? // +//*****************************************************************************// +Mapper30 map30( + .clk (clk), + .ce (ce), + .enable (me[30]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Mapper 32 // +// Mappers: 32 // +// Status : Needs evaluation // +// Notes : // +// Games : Image Fight // +//*****************************************************************************// +Mapper32 map32( + .clk (clk), + .ce (ce), + .enable (me[32]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : MMC2 // +// Mappers: 9 // +// Status : // +// Notes : Working // +// Games : Mike Tyson's Punch-Out // +//*****************************************************************************// +MMC2 mmc2( + .clk (clk), + .ce (ppu_ce), // PPU_CE + .enable (me[9]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b), + // Special ports + .chr_ain_o (chr_ain_orig) +); + +//*****************************************************************************// +// Name : MMC3 // +// Mappers: 4, 33, 37, 47, 48, 74, 76, 80, 82, 88, 95, 112, 118, 119, 154, 191,// +// 192, 194, 195, 206, 207 // +// Status : Working -- Blaarg IRQ timing test fails, but may be submapper // +// Notes : While currently working well, this mapper could use a full review. // +// Games : Crystalis, Battletoads // +//*****************************************************************************// +wire mmc3_en = me[118] | me[119] | me[47] | me[206] | me[112] | me[88] | me[154] | me[95] + | me[76] | me[80] | me[82] | me[207] | me[48] | me[33] | me[37] | me[74] | me[191] + | me[192] | me[194] | me[195] | me[4]; + +MMC3 mmc3 ( + .clk (clk), + .ce (ppu_ce), // PPU CE + .enable (mmc3_en), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b), + // Special ports + .chr_ain_o (chr_ain_orig) +); + +//*****************************************************************************// +// Name : MMC4 // +// Mappers: 10 // +// Status : Working // +// Notes : // +// Games : Fire Emblem // +//*****************************************************************************// +MMC4 mmc4( + .clk (clk), + .ce (ppu_ce), // PPU_CE + .enable (me[10]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b), + // Special ports + .chr_ain_o (chr_ain_orig) +); + +//*****************************************************************************// +// Name : MMC5 // +// Mappers: 5 // +// Status : Fairly complete, but has some bugs. Check Rockman Minus Infinity. // +// Notes : Uses expansion audio and PPU hacks. Could use a thorough review. // +// Games : Castlevania III, Just Breed // +//*****************************************************************************// +MMC5 mmc5( + .clk (clk), + .ce (ce), + .enable (me[5]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (mmc5_audio), + .audio_b (audio_out_b), + // Special ports + .audio_dout (mmc5_data), + .chr_din (chr_din), + .chr_write (chr_write), + .chr_dout_b (chr_dout_b), + .ppu_ce (ppu_ce), + .ppuflags (ppuflags) +); + +//*****************************************************************************// +// Name : CPROM // +// Mappers: 13 // +// Status : Working // +// Notes : // +// Games : Videomation // +//*****************************************************************************// +Mapper13 map13( + .clk (clk), + .ce (ce), + .enable (me[13]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Mapper 15 // +// Mappers: 15 // +// Status : Working // +// Notes : // +// Games : Bao Xiao San Guo // +//*****************************************************************************// +Mapper15 map15( + .clk (clk), + .ce (ce), + .enable (me[15]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Bandai 16 // +// Mappers: 159, 153, 16 // +// Status : Working/EEPROM needs testing // +// Notes : // +// Games : SD Gundam Gaiden, Dragon Ball 3, Famicom Jump II // +//*****************************************************************************// +wire map16_prg_write, map16_ovr; +wire [7:0] map16_data_out; +wire [17:0] map16_mapper_addr; +Mapper16 map16( + .clk (clk), + .ce (ce), + .enable (me[159] | me[153] | me[16]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b), + // Special Ports + .mapper_addr(map16_mapper_addr), + .mapper_data_in(mapper_data_in), + .mapper_data_out(map16_data_out), + .mapper_prg_write(map16_prg_write), + .mapper_ovr(map16_ovr) +); + +//*****************************************************************************// +// Name : Jaleco 18 // +// Mappers: 18 // +// Status : Needs Evaluation // +// Notes : // +// Games : Pizza Pop!, Plasma Ball, USA Ice Hockey in FC // +//*****************************************************************************// +Mapper18 map18( + .clk (clk), + .ce (ce), + .enable (me[18]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : BNROM // +// Mappers: 34 // +// Status : Working // +// Notes : // +// Games : Mashou, Deadly Towers // +//*****************************************************************************// +Mapper34 map34( + .clk (clk), + .ce (ce), + .enable (me[34]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Mapper 41 // +// Mappers: 41 // +// Status : Working // +// Notes : // +// Games : Caltron 6-in-1 // +//*****************************************************************************// +Mapper41 map41( + .clk (clk), + .ce (ce), + .enable (me[41]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Mapper 42 // +// Mappers: 42 // +// Status : Not working // +// Notes : Used for converted FDS carts. // +// Games : Love Warrior Nicol, Green Beret (unl) // +//*****************************************************************************// +Mapper42 map42( + .clk (clk), + .ce (ce), + .enable (me[42]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Irem H3001 // +// Mappers: 65 // +// Status : Needs evaluation // +// Notes : // +// Games : Spartan X 2, Daiku no Gen-san 2 // +//*****************************************************************************// +Mapper65 map65( + .clk (clk), + .ce (ce), + .enable (me[65]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : GxROM // +// Mappers: 11, 38, 46, 66, 86, 87, 101, 140 // +// Status : 38/66 - Working, 38/87/101/140 - Needs eval, 86 - No Audio Samples // +// Notes : // +// Games : Doraemon, Dragon Power, Sidewinder (145), Taiwan Mahjong 16 (149) // +//*****************************************************************************// +wire mapper66_en = me[11] | me[38] | me[46] | me[86] | me[87] | me[101] | me[140] | me[66] | me[145] | me[149]; +Mapper66 map66( + .clk (clk), + .ce (ce), + .enable (mapper66_en), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Sunsoft-3 // +// Mappers: 67, 190 // +// Status : Needs Evaluation // +// Notes : // +// Games : Fantasy Zone II, Mito Koumon // +//*****************************************************************************// +Mapper67 map67( + .clk (clk), + .ce (ce), + .enable (me[67] | me[190]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Sunsoft-4 // +// Mappers: 68 // +// Status : Working // +// Notes : // +// Games : After Burner (J), Majaraja // +//*****************************************************************************// +Mapper68 map68( + .clk (clk), + .ce (ce), + .enable (me[68]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Sunsoft FME-7 // +// Mappers: 69 // +// Status : Working* // +// Notes : Audio needs better mixing/processing // +// Games : Gimmick!, Barcode World, Hebereke // +//*****************************************************************************// +Mapper69 map69( + .clk (clk), + .ce (ce), + .enable (me[69]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (ss5b_audio), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Codemasters/Camerica // +// Mappers: 71, 232 // +// Status : Working // +// Notes : // +// Games : Micro Machines, Big Nose the Caveman // +//*****************************************************************************// +Mapper71 map71( + .clk (clk), + .ce (ce), + .enable (me[71] | me[232]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Jaleco JF-17 // +// Mappers: 72, 92 // +// Status : 72/92 - Needs evaluation/No audio samples. // +// Notes : // +// Games : Pro Tennis (J), Pinball Quest (J), Pro Soccer (J) // +//*****************************************************************************// +Mapper72 map72( + .clk (clk), + .ce (ce), + .enable (me[92] | me[72]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Mapper 77 // +// Mappers: 77 // +// Status : Needs Evaluation // +// Notes : // +// Games : Napoleon Senki // +//*****************************************************************************// +Mapper77 map77( + .clk (clk), + .ce (ce), + .enable (me[77]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Holy Diver // +// Mappers: 78, 70, 152 // +// Status : Needs testing overall // +// Notes : Submapper 1 Requires NES 2.0 // +// Games : Holy Diver, Uchuusent // +//*****************************************************************************// +Mapper78 map78( + .clk (clk), + .ce (ce), + .enable (me[152] | me[70] | me[78]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : NINA // +// Mappers: 79, 113, 133, 146, 148 // +// Status : Working // +// Notes : 133 uses simplified (72 pin) version, 146 Duplicate of 79? // +// Games : Tiles of Fate, Dudes with Attitude, Krazy Kreatures, // +// Twin Eagle (146), Mahjong World (148), Jovial Race (133) // +//*****************************************************************************// +Mapper79 map79( + .clk (clk), + .ce (ce), + .enable (me[79] | me[113] | me[133] | me[146] | me[148]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Sunsoft // +// Mappers: 89, 93, 184 // +// Status : Needs Evaluation // +// Notes : // +// Games : Tenka no Goikenban // +//*****************************************************************************// +Mapper89 map89( + .clk (clk), + .ce (ce), + .enable (me[89] | me[93] | me[184]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Magic Dragon // +// Mappers: 107 // +// Status : Needs Evaluation // +// Notes : // +// Games : Magic Dragon // +//*****************************************************************************// +Mapper107 map107( + .clk (clk), + .ce (ce), + .enable (me[107]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : GTROM // +// Mappers: 111 // +// Status : Passes all tests except reflash test // +// Notes : No LED or self-reflash support // +// Games : Super Homebrew War, Candelabra: Estoscerro, more homebrew // +//*****************************************************************************// +Mapper111 map111( + .clk (clk), + .ce (ce), + .enable (me[111]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Mapper 165 // +// Mappers: 165 // +// Status : Corrupt Graphics // +// Notes : Possibly merge-able with MMC3, only used for one bootleg game // +// Games : Fire Emblem Gaiden (unl) // +//*****************************************************************************// +Mapper165 map165( + .clk (clk), + .ce (ppu_ce), // PPU_CE + .enable (me[165]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b), + // Special ports + .chr_ain_o (chr_ain_orig) +); + +//*****************************************************************************// +// Name : Magic Floor // +// Mappers: 218 // +// Status : Working // +// Notes : Appears unused in modern packs? // +// Games : Magic Floor // +//*****************************************************************************// +Mapper218 map218( + .clk (clk), + .ce (ce), + .enable (me[218]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Active Enterprises // +// Mappers: 228 // +// Status : Working // +// Notes : // +// Games : Cheetamen // +//*****************************************************************************// +Mapper228 map228( + .clk (clk), + .ce (ce), + .enable (me[228]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Maxi 15 // +// Mappers: 234 // +// Status : Needs Evaluation // +// Notes : The fact that this mapper needs a different cpu data in concerns me// +// either this indicates the mapper is not correctly written or that // +// the system itself is not behaving correctly. // +// Games : Maxi-15 Pack (unl) // +//*****************************************************************************// +Mapper234 map234( + .clk (clk), + .ce (ce), + .enable (me[234]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .prg_read (prg_read), + .prg_write (prg_write), + .prg_din (prg_from_ram), + .prg_dout_b (prg_dout_b), + .prg_allow_b(prg_allow_b), + .chr_ain (chr_ain), + .chr_aout_b (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : RAMBO1 (Tengen MMC3) // +// Mappers: 64, 158 // +// Status : Needs testing. Irq might be slightly off. // +// Notes : Consider merging with MMC3 // +// Games : Rolling Thunder, Klax, Skull and Crossbones, Alien Syndrome (158) // +//*****************************************************************************// +Rambo1 rambo1( + .clk (clk), + .ce (ce), + .enable (me[64] | me[158]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b), + // Special ports + .chr_ain_o (chr_ain_orig) +); + +//*****************************************************************************// +// Name : NesEvent // +// Mappers: 105 // +// Status : Working // +// Notes : This wraps the MMC1 mapper, consider merging more elegantly // +// Games : Nintendo World Championships 1990 (start hack) // +//*****************************************************************************// +NesEvent nesev( + .clk (clk), + .ce (ce), + .enable (me[105]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + + +//*****************************************************************************// +// Name : Konami VRC-1 // +// Mappers: 75 // +// Status : Needs Evaluation // +// Notes : // +// Games : King Kong 2, Exciting Boxing, Tetsuwan Atom // +//*****************************************************************************// +VRC1 vrc1( + .clk (clk), + .ce (ce), + .enable (me[75]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Konami VRC-3 // +// Mappers: 73 // +// Status : Needs Evaluation // +// Notes : // +// Games : Salamander (j) // +//*****************************************************************************// +VRC3 vrc3( + .clk (clk), + .ce (ce), + .enable (me[73]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Konami VRC2/4 // +// Mappers: 21, 22, 23, 25, 27 (pirate of 23) // +// Status : Needs Evaluation // +// Notes : // +// Games : Wai Wai World 2, Twinbee 3, Contra (j), Gradius II (j) // +//*****************************************************************************// +VRC24 vrc24( + .clk (clk), + .ce (ce), + .enable (me[21] | me[22] | me[23] | me[25] | me[27]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Konami VRC-6 // +// Mappers: 24, 26 // +// Status : Working. Audio needs evaluation. Startup instability. // +// Notes : External audio needs to be mixed correctly. // +// Games : Akamajou Densetsu, Esper Dream 2, Mouryou Senki Madara // +//*****************************************************************************// +VRC6 vrc6( + .clk (clk), + .ce (ce), + .enable (me[24] | me[26]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (vrc6_audio), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Konami VRC-7 // +// Mappers: 85 // +// Status : Working. // +// Notes : Audio mixing needs evaluation // +// Games : Lagrange Point, Tiny Toon Aventures 2 (j) // +//*****************************************************************************// +VRC7 vrc7( + .clk (clk), + .ce (ce), + .enable (me[85]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (vrc7_audio), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Namco 163 // +// Mappers: 19, 210 // +// Status : Needs Evaluation // +// Notes : This mapper requires submappers for correct operation // +// Games : Digital Devil Story, Battle Fleet, Famista // +//*****************************************************************************// +N163 n163( + .clk (clk), + .ce (ce), + .enable (me[210] | me[19]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (n163_audio), + .audio_b (audio_out_b), + // Special ports + .audio_dout (n163_data) +); + +//*****************************************************************************// +// Name : Waixing 162 // +// Mappers: 162 // +// Status : Working // +// Notes : // +// Games : Zelda - San Shen Zhi Li // +//*****************************************************************************// +Mapper162 map162( + .clk (clk), + .ce (ce), + .enable (me[162]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Nanjing 163 // +// Mappers: 163 // +// Status : Working // +// Notes : // +// Games : Final Fantasy VII (163), Pokemon Yellow (163) // +//*****************************************************************************// +Nanjing map163( + .clk (clk), + .ce (ce), + .enable (me[163]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b), + // Special Ports + .ppu_ce (ppu_ce), + .ppuflags (ppuflags) +); + + +//*****************************************************************************// +// Name : Waixing 164 // +// Mappers: 164 // +// Status : Working // +// Notes : // +// Games : Final Fantasy V // +//*****************************************************************************// +Mapper164 map164( + .clk (clk), + .ce (ce), + .enable (me[164]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Sachen 8259 // +// Mappers: 137, 138, 139, 141, 150, 243 // +// Status : Working // +// Notes : // +// Games : The Great Wall (137), Silver Eagle (138), Hell Fighter (139), // +// Super Cart 6 - 6 in 1(141), Strategist (150), Poker III (243) // +//*****************************************************************************// +Sachen8259 sachen( + .clk (clk), + .ce (ce), + .enable (me[137] | me[138] | me[139] | me[141] | me[150] | me[243]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Sachen JV001 // +// Mappers: 136, 147, 132, 173, 172, 36 // +// Status : Working // +// Notes : 147 only tested with 60 pin version // +// Games : Wei Lai Xiao Zi (136), Chinese Kungfu (147), Creatom (132), // +// F-15 City War (173), Mahjong Block (172), Strike Wolf (36) // +//*****************************************************************************// +SachenJV001 sachenj( + .clk (clk), + .ce (ce), + .enable (me[136] | me[147] | me[132] | me[173] | me[172] | me[36]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : Sachen NROM // +// Mappers: 143 // +// Status : Working // +// Notes : // +// Games : Dancing Blocks, Magical Mathematics // +//*****************************************************************************// +SachenNROM sachenn( + .clk (clk), + .ce (ce), + .enable (me[143]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : JY Company // +// Mappers: 90, 209, 211, 35 // +// Status : Working (needs testing) // +// Notes : 211 and 35 can be considered duplicates. // +// Games : Aladdin (90), Power Rangers 3 (209), Warioland II (35), // +// Tiny Toon Adventures 6 (211) // +//*****************************************************************************// +JYCompany jycompany( + .clk (clk), + .ce (ce), + .enable (me[90] | me[209] | me[211] | me[35]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b), + // Special ports + .ppu_ce (ppu_ce), + .chr_ain_o (chr_ain_orig) +); + +//*****************************************************************************// +// Name : Mapper 225 // +// Mappers: 225, 255 // +// Status : Working // +// Notes : Defining 225 as with 74'670 (4-nybble RAM) and 255 as without // +// Games : 64-in-1 (225), 110-in-1 (255 - with glitched menu selection) // +//*****************************************************************************// +Mapper225 map225( + .clk (clk), + .ce (ce), + .enable (me[225] | me[255]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (audio_in), + .audio_b (audio_out_b) +); + +//*****************************************************************************// +// Name : FDS // +// Mappers: 20 // +// Status : Audio good. Drive mechanics okay, but dated. Needs rewrite. // +// Notes : Uses a special wire to signal disk changes. Req. modified BIOS. // +// Games : Bio Miracle for audio, Various unlicensed games for compatibility. // +//*****************************************************************************// +tri0 [1:0] fds_diskside_auto; +MapperFDS mapfds( + .clk (clk), + .ce (ce), + .enable (me[20]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (fds_audio), + .audio_b (audio_out_b), + // Special ports + .audio_dout (fds_data), + .diskside_auto_b (fds_diskside_auto), + .diskside (diskside), + .fds_busy (fds_busy), + .fds_eject (fds_eject) +); + +//*****************************************************************************// +// Name : Mapper 31 // +// Mappers: 31 and NSF Player // +// Status : Testing // +// Notes : Uses Mapper 31.15 (submapper) for NSF Player; NSF 1.0 only // +// Games : Famicompo Pico 2014, NSF 1.0 // +//*****************************************************************************// +wire [5:0] exp_audioe; +NSF nsfplayer( + .clk (clk), + .ce (ce), + .enable (me[31]), + .flags (flags), + .prg_ain (prg_ain), + .prg_aout_b (prg_addr_b), + .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 (chr_addr_b), + .chr_read (chr_read), + .chr_allow_b(chr_allow_b), + .vram_a10_b (vram_a10_b), + .vram_ce_b (vram_ce_b), + .irq_b (irq_b), + .flags_out_b(flags_out_b), + .audio_in (exp_audioe[5] ? ss5b_audio : + exp_audioe[4] ? n163_audio : + exp_audioe[3] ? mmc5_audio : + exp_audioe[2] ? fds_audio : + exp_audioe[1] ? vrc7_audio : + exp_audioe[0] ? vrc6_audio : + audio_in), + .exp_audioe (exp_audioe), // Expansion Enabled (0x0=None, 0x1=VRC6, 0x2=VRC7, 0x4=FDS, 0x8=MMC5, 0x10=N163, 0x20=SS5B + .audio_b (audio_out_b), + .fds_din (fds_data) +); + +wire [15:0] ss5b_audio; +SS5b_mixed snd_5bm ( + .clk(clk), + .ce(ce), + .enable(me[69] | (me[31] && exp_audioe[5])), + .wren(prg_write), + .addr_in(prg_ain), + .data_in(prg_din), + .audio_in(audio_in), + .audio_out(ss5b_audio) +); + +wire [15:0] n163_audio; +wire [7:0] n163_data; +namco163_mixed snd_n163 ( + .clk(clk), + .ce(ce), + .submapper(flags[24:21]), + .enable(me[19] | (me[31] && exp_audioe[4])), + .wren(prg_write), + .addr_in(prg_ain), + .data_in(prg_din), + .data_out(n163_data), + .audio_in(audio_in), + .audio_out(n163_audio) +); + +wire [15:0] mmc5_audio; +wire [7:0] mmc5_data; +mmc5_mixed snd_mmc5 ( + .clk(clk), + .ce(ce), + .enable(me[5] | (me[31] && exp_audioe[3])), + .wren(prg_write), + .rden(prg_read), + .addr_in(prg_ain), + .data_in(prg_din), + .data_out(mmc5_data), + .audio_in(audio_in), + .audio_out(mmc5_audio) +); + +wire [15:0] fds_audio; +wire [7:0] fds_data; +fds_mixed snd_fds ( + .clk(clk), + .ce(ce), + .enable(me[20] | (me[31] && exp_audioe[2])), + .wren(prg_write), + .addr_in(prg_ain), + .data_in(prg_din), + .data_out(fds_data), + .audio_in(audio_in), + .audio_out(fds_audio) +); + +wire [15:0] vrc7_audio; +vrc7_mixed snd_vrc7 ( + .clk(clk), + .ce(ce), + .enable(me[85] | (me[31] && exp_audioe[1])), + .wren(prg_write), + .addr_in(prg_ain), + .data_in(prg_din), + .audio_in(audio_in), + .audio_out(vrc7_audio) +); + +wire [15:0] vrc6_audio; +vrc6_mixed snd_vrc6 ( + .clk(clk), + .ce(ce), + .enable(me[24] | me[26] | (me[31] && exp_audioe[0])), + .wren(prg_write), + .addr_invert(me[26]), + .addr_in(prg_ain), + .data_in(prg_din), + .audio_in(audio_in), + .audio_out(vrc6_audio) +); + + +wire [6:0] prg_mask; +wire [6:0] chr_mask; +wire [255:0] me; + +always @* begin + me = 256'd0; + me[flags[7:0]] = 1'b1; + + case(flags[10:8]) + 0: prg_mask = 7'b0000000; + 1: prg_mask = 7'b0000001; + 2: prg_mask = 7'b0000011; + 3: prg_mask = 7'b0000111; + 4: prg_mask = 7'b0001111; + 5: prg_mask = 7'b0011111; + 6: prg_mask = 7'b0111111; + 7: prg_mask = 7'b1111111; + endcase + + case(flags[13:11]) + 0: chr_mask = 7'b0000000; + 1: chr_mask = 7'b0000001; + 2: chr_mask = 7'b0000011; + 3: chr_mask = 7'b0000111; + 4: chr_mask = 7'b0001111; + 5: chr_mask = 7'b0011111; + 6: chr_mask = 7'b0111111; + 7: chr_mask = 7'b1111111; + endcase + + // Mapper output to cart pins + {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, prg_dout, chr_dout, irq, audio} = + {prg_addr_b, prg_allow_b, chr_addr_b, vram_a10_b, vram_ce_b, chr_allow_b, prg_dout_b, chr_dout_b, irq_b, audio_out_b}; + + // Currently only used for Mapper 16 EEPROM. Expand if needed. + {mapper_addr, mapper_data_out, mapper_prg_write, mapper_ovr} = (me[159] | me[16]) ? + {map16_mapper_addr, map16_data_out, map16_prg_write, map16_ovr} : 28'd0; + + {diskside_auto} = {fds_diskside_auto}; + + // Behavior helper flags + {prg_conflict, prg_bus_write, has_chr_dout} = {flags_out_b[2], flags_out_b[1], flags_out_b[0]}; + + // Address translation for SDRAM + if (prg_aout[21] == 1'b0) + prg_aout[20:0] = {prg_aout[20:14] & prg_mask, prg_aout[13:0]}; + + if (chr_aout[21:20] == 2'b10) + chr_aout[19:0] = {chr_aout[19:13] & chr_mask, chr_aout[12:0]}; + + // Remap the CHR address into VRAM, if needed. + chr_aout = vram_ce ? {11'b11_0000_0000_0, vram_a10, chr_ain[9:0]} : chr_aout; + prg_aout = (prg_ain < 'h2000) ? {11'b11_1000_0000_0, prg_ain[10:0]} : prg_aout; + prg_allow = prg_allow || (prg_ain < 'h2000); +end + +endmodule diff --git a/cores/nes/src/OPLL/dpram.vhd b/cores/nes/src/dpram.vhd similarity index 100% rename from cores/nes/src/OPLL/dpram.vhd rename to cores/nes/src/dpram.vhd diff --git a/cores/nes/src/mappers/FDS.sv b/cores/nes/src/mappers/FDS.sv new file mode 100644 index 0000000..2ac1fd9 --- /dev/null +++ b/cores/nes/src/mappers/FDS.sv @@ -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 diff --git a/cores/nes/src/mappers/JYCompany.sv b/cores/nes/src/mappers/JYCompany.sv new file mode 100644 index 0000000..3b0769f --- /dev/null +++ b/cores/nes/src/mappers/JYCompany.sv @@ -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 diff --git a/cores/nes/src/mappers/MMC1.sv b/cores/nes/src/mappers/MMC1.sv new file mode 100644 index 0000000..c102e8a --- /dev/null +++ b/cores/nes/src/mappers/MMC1.sv @@ -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 diff --git a/cores/nes/src/mappers/MMC2.sv b/cores/nes/src/mappers/MMC2.sv new file mode 100644 index 0000000..02e9637 --- /dev/null +++ b/cores/nes/src/mappers/MMC2.sv @@ -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 diff --git a/cores/nes/src/mappers/MMC3.sv b/cores/nes/src/mappers/MMC3.sv new file mode 100644 index 0000000..2cf472b --- /dev/null +++ b/cores/nes/src/mappers/MMC3.sv @@ -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 diff --git a/cores/nes/src/mappers/MMC5.sv b/cores/nes/src/mappers/MMC5.sv new file mode 100644 index 0000000..87171f2 --- /dev/null +++ b/cores/nes/src/mappers/MMC5.sv @@ -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 diff --git a/cores/nes/src/mappers/Namco.sv b/cores/nes/src/mappers/Namco.sv new file mode 100644 index 0000000..6dd4860 --- /dev/null +++ b/cores/nes/src/mappers/Namco.sv @@ -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 diff --git a/cores/nes/src/mappers/Sachen.sv b/cores/nes/src/mappers/Sachen.sv new file mode 100644 index 0000000..8645fa5 --- /dev/null +++ b/cores/nes/src/mappers/Sachen.sv @@ -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 diff --git a/cores/nes/src/mappers/Sunsoft.sv b/cores/nes/src/mappers/Sunsoft.sv new file mode 100644 index 0000000..2a185b9 --- /dev/null +++ b/cores/nes/src/mappers/Sunsoft.sv @@ -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 \ No newline at end of file diff --git a/cores/nes/src/mappers/VRC.sv b/cores/nes/src/mappers/VRC.sv new file mode 100644 index 0000000..64d987d --- /dev/null +++ b/cores/nes/src/mappers/VRC.sv @@ -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 + diff --git a/cores/nes/src/mappers/generic.sv b/cores/nes/src/mappers/generic.sv new file mode 100644 index 0000000..1c17ac8 --- /dev/null +++ b/cores/nes/src/mappers/generic.sv @@ -0,0 +1,1057 @@ +// These mappers are simple generic mappers which can eventually be combined into a single module with parameters + +// No mapper chip +module MMC0( + 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; +reg [15:0] flags_out = 0; + + +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]; + +endmodule + +// #13 - CPROM - Used by Videomation +module Mapper13( + 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; +reg [15:0] flags_out = 0; + +reg [1:0] chr_bank; +always @(posedge clk) begin + if (~enable) begin + chr_bank <= 0; + end else if (ce) begin + if (prg_ain[15] && prg_write) + chr_bank <= prg_din[1:0]; + end +end + +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 = {8'b01_0000_00, chr_ain[12] ? chr_bank : 2'b00, chr_ain[11:0]}; +assign vram_ce = chr_ain[13]; +assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11]; + +endmodule + +// 30-UNROM512 +module Mapper30( + 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 [4:0] prgbank; +reg [1:0] chrbank; +reg [2:0] mirror; +wire four_screen = (mirror[2:1] == 2'b11); + +always @(posedge clk) begin + if (~enable) begin + // Set value for mirroring + mirror[2:1] <= {flags[16], flags[14]}; + end else if (ce) begin + if (prg_ain[15] & prg_write) begin + {mirror[0], chrbank, prgbank} <= prg_din[7:0]; + end + end +end + +always begin + // mirroring mode + casez({mirror[2:1],chr_ain[13]}) + 3'b001 : vram_a10 = {chr_ain[11]}; // horizontal + 3'b011 : vram_a10 = {chr_ain[10]}; // vertical + 3'b101 : vram_a10 = {mirror[0]}; // 1 screen + 3'b111 : vram_a10 = {chr_ain[10]}; // 4 screen + default : vram_a10 = {chr_ain[10]}; // pattern table + endcase +end + +assign prg_aout = {3'b000, prg_ain[14] ? 5'b11111 : prgbank, prg_ain[13:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; +assign chr_aout = {flags[15] ? 7'b11_1111_1 : 7'b10_0000_0, (four_screen && (chr_ain[13])) ? 2'b11 : chrbank, chr_ain[12:11], vram_a10, chr_ain[9:0]}; +assign vram_ce = chr_ain[13] && !four_screen; + +endmodule + + +// 11 - Color Dreams +// 38 - Bit Corps +// 46 - RumbleStation 15-in-1 +// 86 - Jaleco JF-13 -- no audio samples +// 87 - Jaleco JF-11,JF-14 +// 101 - Jaleco JF-11,JF-14 +// 140 - Jaleco JF-11,JF-14 +// 66 - GxROM +// 145 - Sachen +// 149 - Sachen +module Mapper66( + 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 [15:0] flags_out = {13'h0, prg_conflict, 2'b00}; + + +reg [4:0] prg_bank; +reg [6:0] chr_bank; +wire [7:0] mapper = flags[7:0]; +wire GXROM = (mapper == 66); +wire BitCorps = (mapper == 38); +wire Mapper140 = (mapper == 140); +wire Mapper101 = (mapper == 101); +wire Mapper46 = (mapper == 46); +wire Mapper86 = (mapper == 86); +wire Mapper87 = (mapper == 87); +wire Mapper145 = (mapper == 145); +wire Mapper149 = (mapper == 149); + +always @(posedge clk) +if (~enable) begin + prg_bank <= 0; + chr_bank <= 0; +end else if (ce) begin + if (prg_ain[15] & prg_write) begin + if (GXROM) + {prg_bank, chr_bank} <= {3'b0, prg_din[5:4], 5'b0, prg_din[1:0]}; + else if (Mapper149) + {chr_bank} <= {6'b0, prg_din[7]}; + else if (Mapper46) + {chr_bank[2:0], prg_bank[0]} <= {prg_din[6:4], prg_din[0]}; + else // Color Dreams + {chr_bank, prg_bank} <= {3'b0, prg_din[7:4], 3'b0, prg_din[1:0]}; + end else if ((prg_ain[15:12]==4'h7) & prg_write & BitCorps) begin + {chr_bank, prg_bank} <= {3'b0, prg_din[3:0]}; + end else if ((prg_ain[15:12]==4'h6) & prg_write) begin + if (Mapper140) begin + {prg_bank, chr_bank} <= {3'b0, prg_din[5:4], 3'b0, prg_din[3:0]}; + end else if (Mapper46) begin + {chr_bank[6:3], prg_bank[4:1]} <= {prg_din[7:4], prg_din[3:0]}; + end else if (Mapper101) begin + {chr_bank} <= {3'b0, prg_din[3:0]}; // All 8 bits instead? + end else if (Mapper87) begin + {chr_bank} <= {5'b00, prg_din[0], prg_din[1]}; + end else if (Mapper86) begin + {prg_bank, chr_bank} <= {3'b0, prg_din[5:4], 4'b0, prg_din[6], prg_din[1:0]}; + end + end else if ((prg_ain[15:8]==8'h41) & prg_write) begin + if (Mapper145) + {chr_bank} <= {6'b0, prg_din[7]}; + end +end + +assign prg_aout = {2'b00, prg_bank, prg_ain[14:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; +assign chr_aout = {2'b10, chr_bank, chr_ain[12:0]}; +assign vram_ce = chr_ain[13]; +assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11]; +wire prg_conflict = prg_ain[15] && (Mapper149); + +endmodule + + +// 34 - BxROM or NINA-001 +module Mapper34( + 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; +reg [15:0] flags_out = 0; + + +reg [5:0] prg_bank; +reg [3:0] chr_bank_0, chr_bank_1; + +wire NINA = (flags[13:11] != 0); // NINA is used when there is more than 8kb of CHR +always @(posedge clk) +if (~enable) begin + prg_bank <= 0; + chr_bank_0 <= 0; + chr_bank_1 <= 1; // To be compatible with BxROM +end else if (ce && prg_write) begin + if (!NINA) begin // BxROM + if (prg_ain[15]) + prg_bank <= prg_din[5:0]; //[1:0] offical, [5:0] oversize + end else begin // NINA + if (prg_ain == 16'h7ffd) + prg_bank <= prg_din[5:0]; //[1:0] offical, [5:0] oversize + else if (prg_ain == 16'h7ffe) + chr_bank_0 <= prg_din[3:0]; + else if (prg_ain == 16'h7fff) + chr_bank_1 <= prg_din[3:0]; + end +end + +wire [21:0] prg_aout_tmp = {1'b0, prg_bank, prg_ain[14:0]}; +assign chr_allow = flags[15]; +assign chr_aout = {6'b10_0000, chr_ain[12] == 0 ? chr_bank_0 : chr_bank_1, chr_ain[11:0]}; +assign vram_ce = chr_ain[13]; +assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11]; + +wire prg_is_ram = (prg_ain >= 'h6000 && prg_ain < 'h8000) && NINA; +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 + + +// #71,#232 - Camerica +module Mapper71( + 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; +reg [15:0] flags_out = 0; + +reg [3:0] prg_bank; +reg ciram_select; +wire mapper232 = (flags[7:0] == 232); +always @(posedge clk) +if (~enable) begin + prg_bank <= 0; + ciram_select <= 0; +end else if (ce) begin + if (prg_ain[15] && prg_write) begin + if (!prg_ain[14] && mapper232) // $8000-$BFFF Outer bank select (only on iNES 232) + prg_bank[3:2] <= prg_din[4:3]; + if (prg_ain[14:13] == 0) // $8000-$9FFF Fire Hawk Mirroring + ciram_select <= prg_din[4]; + if (prg_ain[14]) // $C000-$FFFF Bank select + prg_bank <= {mapper232 ? prg_bank[3:2] : prg_din[3:2], prg_din[1:0]}; + end +end + +reg [3:0] prgout; +always begin + casez({prg_ain[14], mapper232}) + 2'b0?: prgout = prg_bank; + 2'b10: prgout = 4'b1111; + 2'b11: prgout = {prg_bank[3:2], 2'b11}; + endcase +end + +assign prg_aout = {4'b00_00, prgout, prg_ain[13: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]; +// XXX(ludde): Fire hawk uses flags[14] == 0 while no other game seems to do that. +// So when flags[14] == 0 we use ciram_select instead. +assign vram_a10 = flags[14] ? chr_ain[10] : ciram_select; + +endmodule + + +// 77-IREM +module Mapper77( + 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] prgbank; +reg [3:0] chrbank; + +always @(posedge clk) begin + if (~enable) begin + prgbank <= 0; + chrbank <= 0; + end else if (ce) begin + if (prg_ain[15] & prg_write) begin + {chrbank, prgbank} <= prg_din[7:0]; + end + end +end + +always begin + vram_a10 = {chr_ain[10]}; // four screen (consecutive) +end + +assign prg_aout = {3'b000, prgbank, prg_ain[14:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = chrram; +wire chrram = (chr_ain[13:11]!=3'b000); +assign chr_aout[10:0] = {chr_ain[10:0]}; +assign chr_aout[21:11] = chrram ? {8'b11_1111_11, chr_ain[13:11]} : {7'b10_0000_0, chrbank}; +assign vram_ce = 0; + +endmodule + +// #78-IREM-HOLYDIVER/JALECO-JF-16 +// #70,#152-Bandai +module Mapper78( + 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; +reg [15:0] flags_out = 0; + +reg [3:0] prg_bank; +reg [3:0] chr_bank; +reg mirroring; // See vram_a10_t +wire mapper70 = (flags[7:0] == 70); +wire mapper152 = (flags[7:0] == 152); +wire onescreen = (flags[22:21] == 1) | mapper152; // default (0 or 3) Holy Diver submapper; (1) JALECO-JF-16 +always @(posedge clk) begin + if (~enable) begin + prg_bank <= 0; + chr_bank <= 0; + mirroring <= flags[14]; + end else if (ce) begin + if (prg_ain[15] == 1'b1 && prg_write) begin + if (mapper70) + {prg_bank, chr_bank} <= prg_din; + else if (mapper152) + {mirroring, prg_bank[2:0], chr_bank} <= prg_din; + else + {chr_bank, mirroring, prg_bank[2:0]} <= prg_din; + end + end +end + +assign prg_aout = {4'b00_00, (prg_ain[14] ? 4'b1111 : prg_bank), prg_ain[13:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; +assign chr_aout = {5'b10_000, chr_bank, chr_ain[12:0]}; +assign vram_ce = chr_ain[13]; + +// The a10 VRAM address line. (Used for mirroring) +reg vram_a10_t; +always begin + case({onescreen, mirroring}) + 2'b00: vram_a10_t = chr_ain[11]; // One screen, horizontal + 2'b01: vram_a10_t = chr_ain[10]; // One screen, vertical + 2'b10: vram_a10_t = 0; // One screen, lower bank + 2'b11: vram_a10_t = 1; // One screen, upper bank + endcase +end + +assign vram_a10 = vram_a10_t; + +endmodule + +// #79,#113 - NINA-03 / NINA-06 +// #133,#146,#148 - Sachen +module Mapper79( + 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 [15:0] flags_out = {13'h0, prg_conflict, 2'b00}; + +reg [2:0] prg_bank; +reg [3:0] chr_bank; +reg mirroring; // 0: Horizontal, 1: Vertical +wire mapper113 = (flags[7:0] == 113); // NINA-06 +wire mapper133 = (flags[7:0] == 133); +wire mapper148 = (flags[7:0] == 148); + +always @(posedge clk) +if (~enable) begin + prg_bank <= 0; + chr_bank <= 0; + mirroring <= 0; +end else if (ce) begin + if (prg_ain[15:13] == 3'b010 && prg_ain[8] && prg_write) + if (mapper133) + {mirroring, chr_bank[3], prg_bank, chr_bank[2:0]} <= {4'h0,prg_din[2],1'b0,prg_din[1:0]}; + else + {mirroring, chr_bank[3], prg_bank, chr_bank[2:0]} <= prg_din; + if (prg_ain[15] == 1'b1 && prg_write) + if (mapper148) + {mirroring, chr_bank[3], prg_bank, chr_bank[2:0]} <= prg_din; +end + +assign prg_aout = {4'b00_00, prg_bank, prg_ain[14:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; +assign chr_aout = {5'b10_000, chr_bank, chr_ain[12:0]}; +assign vram_ce = chr_ain[13]; +wire mirrconfig = mapper113 ? mirroring : flags[14]; // Mapper #13 has mapper controlled mirroring +assign vram_a10 = mirrconfig ? chr_ain[10] : chr_ain[11]; // 0: horiz, 1: vert +wire prg_conflict = prg_ain[15] && mapper148; + +endmodule + + +// #89,#93,#184 - Sunsoft mappers +module Mapper89( + 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 [2:0] prgsel; +reg [3:0] chrsel0; +reg [3:0] chrsel1; +reg [2:0] prg_temp; +reg [4:0] chr_temp; + +reg mirror; + +wire [7:0] mapper = flags[7:0]; +wire mapper89 = (mapper == 8'd89); +wire mapper93 = (mapper == 8'd93); +wire mapper184 = (mapper == 8'd184); + +always @(posedge clk) +if (~enable) begin + prgsel <= 3'b110; + chrsel0 <= 4'b1111; + chrsel1 <= 4'b1111; +end else if (ce) begin + if (prg_ain[15] & prg_write & mapper89) begin + {chrsel0[3], prgsel, mirror, chrsel0[2:0]} <= prg_din; + end else if (prg_ain[15] & prg_write & mapper93) begin + prgsel <= prg_din[6:4]; + // chrrameanble <= prg_din[0]; + end else if ((prg_ain[15:13]==3'b011) & prg_write & mapper184) begin + {chrsel1[3:0], chrsel0[3:0]} <= {2'b01,prg_din[5:4],1'b0,prg_din[2:0]}; + end +end + +always begin + // mirroring mode + casez({mapper89,flags[14]}) + 2'b00 : vram_a10 = {chr_ain[11]}; // horizontal + 2'b01 : vram_a10 = {chr_ain[10]}; // vertical + 2'b1? : vram_a10 = {mirror}; // 1 screen + endcase + + // PRG ROM bank size select + casez({mapper184, prg_ain[14]}) + 2'b00 : prg_temp = {prgsel}; // 16K banks + 2'b01 : prg_temp = {3'b111}; // 16K banks last + 2'b1? : prg_temp = {2'b0,prg_ain[14]}; // 32K banks pass thru + endcase + + // CHR ROM bank size select + casez({mapper184, chr_ain[12]}) + 2'b0? : chr_temp = {chrsel0, chr_ain[12]};// 8K Bank + 2'b10 : chr_temp = {1'b0,chrsel0}; // 4K Bank + 2'b11 : chr_temp = {1'b0,chrsel1}; // 4K Bank + endcase +end + +assign vram_ce = chr_ain[13]; +assign prg_aout = {5'b0, prg_temp, prg_ain[13:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; +assign chr_aout = {5'b10_000, chr_temp, chr_ain[11:0]}; + +endmodule + + + +// 107 Magicseries Magic Dragon +module Mapper107( + 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; +reg [15:0] flags_out = 0; + + +reg [6:0] prg_bank; +reg [7:0] chr_bank; +always @(posedge clk) begin + if (~enable) begin + prg_bank <= 0; + chr_bank <= 0; + end else if (ce) begin + if (prg_ain[15] & prg_write) begin + prg_bank <= prg_din[7:1]; + chr_bank <= prg_din[7:0]; + end + end +end + +assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11]; +assign prg_aout = {1'b0, prg_bank[5:0], prg_ain[14:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; +assign chr_aout = {2'b10, chr_bank[6:0], chr_ain[12:0]}; +assign vram_ce = chr_ain[13]; + +endmodule + +// Tepples/Multi-discrete mapper +// This mapper can emulate other mappers, such as mapper #0, mapper #2 +module Mapper28( + 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 + inout [7:0] chr_dout_b, // chr data (non standard) + 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_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 ? 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; +reg [7:0] chr_dout; +wire vram_ce; +wire [15:0] flags_out = {13'h0, prg_conflict, 1'b0, has_chr_dout}; + +wire prg_conflict, has_chr_dout; + +reg [6:0] a53prg; // output PRG ROM (A14-A20 on ROM) +reg [1:0] a53chr; // output CHR RAM (A13-A14 on RAM) + +reg [3:0] inner; // "inner" bank at 01h +reg [5:0] mode; // mode register at 80h +reg [5:0] outer; // "outer" bank at 81h +reg [2:0] selreg; // selector register +reg [3:0] security; // selector register + +// Allow writes to 0x5000 only when launching through the proper mapper ID. +wire [7:0] mapper = flags[7:0]; +wire allow_select = (mapper == 8'd28); +wire extend_bit = (mapper == 97); + +always @(posedge clk) +if (~enable) begin + mode[5:2] <= 0; // NROM mode, 32K mode + outer[5:0] <= 6'h3f; // last bank + inner <= 0; + selreg <= 1; + + // Set value for mirroring + if (mapper == 2 || mapper == 0 || mapper == 3 || mapper == 94 || mapper == 180 || mapper == 185) + mode[1:0] <= flags[14] ? 2'b10 : 2'b11; + + // UNROM #2 - Current bank in $8000-$BFFF and fixed top half of outer bank in $C000-$FFFF + if (mapper == 2) begin + mode[5:2] <= 4'b1111; // 256K banks, UNROM mode + end + + // CNROM #3 - Fixed PRG bank, switchable CHR bank. + if (mapper == 3) + selreg <= 0; + + // CNROM #185 - Fixed PRG bank, Fixed CHR Bank, Security. + if (mapper == 185) + selreg <= 4; + + // UNROM #180 - Fixed first PRG bank, Fixed CHR Bank. + if (mapper == 180) begin + selreg <= 1; + mode[5:2] <= 4'b1110; + outer[5:0] <= 6'h00; + end + + // IREM TAM-S1 IC #97 - Fixed first PRG bank, Fixed CHR Bank, Mirroring. + if (mapper == 97) begin + selreg <= 6; + mode[5:2] <= 4'b1110; + outer[5:0] <= 6'h3F; + end + + // IREM TAM-S1 IC #94 - Fixed last PRG bank, Fixed CHR Bank + if (mapper == 94) begin + selreg <= 7; + mode[5:2] <= 4'b1111; + end + + // AxROM #7 - Switch 32kb rom bank + switchable nametables + if (mapper == 7) begin + mode[1:0] <= 2'b00; // Switchable VRAM page. + mode[5:2] <= 4'b1100; // 256K banks, (B)NROM mode + outer[5:0] <= 6'h00; + end +end else if (ce) begin + if ((prg_ain[15:12] == 4'h5) & prg_write && allow_select) + selreg <= {1'b0,prg_din[7], prg_din[0]}; // select register + if (prg_ain[15] & prg_write) begin + casez (selreg) + 3'b000: {mode[0], a53chr} <= {(mode[1] ? mode[0] : prg_din[4]), prg_din[1:0]}; // CHR RAM bank + 3'b001: {mode[0], inner} <= {(mode[1] ? mode[0] : prg_din[4]), prg_din[3:0]}; // "inner" bank + 3'b010: {mode} <= {prg_din[5:0]}; // mode register + 3'b011: {outer} <= {prg_din[5:0]}; // "outer" bank + 3'b10?: {security} <= {prg_din[5:4],prg_din[1:0]}; // security + 3'b110: {mode[1:0],inner} <= {prg_din[7] ^ prg_din[6], prg_din[6], prg_din[3:0]}; // "inner" bank + 3'b111: {inner} <= {prg_din[5:2]}; // "inner" bank + endcase + end +end + +always begin + // mirroring mode + casez(mode[1:0]) + 2'b0? : vram_a10 = {mode[0]}; // 1 screen lower + 2'b10 : vram_a10 = {chr_ain[10]}; // vertical + 2'b11 : vram_a10 = {chr_ain[11]}; // horizontal + endcase + + // PRG ROM bank size select + casez({mode[5:2], prg_ain[14]}) + 5'b00_0?_?: a53prg = {outer[5:0], prg_ain[14]}; // 32K banks, (B)NROM mode + 5'b01_0?_?: a53prg = {outer[5:1], inner[0], prg_ain[14]}; // 64K banks, (B)NROM mode + 5'b10_0?_?: a53prg = {outer[5:2], inner[1:0], prg_ain[14]}; // 128K banks, (B)NROM mode + 5'b11_0?_?: a53prg = {outer[5:3], inner[2:0], prg_ain[14]}; // 256K banks, (B)NROM mode + + 5'b00_10_1, + 5'b00_11_0: a53prg = {outer[5:0], inner[0]}; // 32K banks, UNROM mode + 5'b01_10_1, + 5'b01_11_0: a53prg = {outer[5:1], inner[1:0]}; // 64K banks, UNROM mode + 5'b10_10_1, + 5'b10_11_0: a53prg = {outer[5:2], inner[2:0]}; // 128K banks, UNROM mode + 5'b11_10_1, + 5'b11_11_0: a53prg = {outer[5:3], inner[3:0]}; // 256K banks, UNROM mode + + default: a53prg = {outer[5:0], extend_bit ? outer[0] : prg_ain[14]}; // 16K fixed bank + endcase + + chr_dout = 8'hFF;//chr_ain[7:0]; // return open bus = LSB of address? below when CHR disabled by security +end + +assign vram_ce = chr_ain[13]; +assign prg_aout = {1'b0, (a53prg & 7'b0011111), prg_ain[13:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign prg_conflict = prg_ain[15] && (mapper == 3) && (submapper != 1); +assign chr_allow = flags[15]; +assign chr_aout = {7'b10_0000_0, a53chr, chr_ain[12:0]}; +wire [4:0] submapper = flags[24:21]; +assign has_chr_dout = (mapper == 185) && (((submapper == 0) && (security[1:0] == 2'b00)) + || ((submapper == 4) && (security[1:0] != 2'b00)) + || ((submapper == 5) && (security[1:0] != 2'b01)) + || ((submapper == 6) && (security[1:0] != 2'b10)) + || ((submapper == 7) && (security[1:0] != 2'b11))); + +endmodule diff --git a/cores/nes/src/mappers/misc.sv b/cores/nes/src/mappers/misc.sv new file mode 100644 index 0000000..88ebc91 --- /dev/null +++ b/cores/nes/src/mappers/misc.sv @@ -0,0 +1,1826 @@ +// These are misc small one-off mappers. Some may end up being merged with Generic mappers. +// altera message_off 10027 +// #15 - 100-in-1 Contra Function 16 +module Mapper15( + 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; +reg [15:0] flags_out = 0; + + +// 15 bit 8 7 bit 0 Address bus +// ---- ---- ---- ---- +// 1xxx xxxx xxxx xxSS +// | || +// | ++- Select PRG ROM bank mode +// | 0: 32K; 1: 128K (UNROM style); 2: 8K; 3: 16K +// +------------------- Always 1 +// 7 bit 0 Data bus +// ---- ---- +// bMBB BBBB +// |||| |||| +// ||++-++++- Select 16 KB PRG ROM bank +// |+-------- Select nametable mirroring mode (0=vertical; 1=horizontal) +// +--------- Select 8 KB half of 16 KB PRG ROM bank +// (should be 0 except in bank mode 0) +reg [1:0] prg_rom_bank_mode; +reg prg_rom_bank_lowbit; +reg mirroring; +reg [5:0] prg_rom_bank; + +always @(posedge clk) begin + if (~enable) begin + prg_rom_bank_mode <= 0; + prg_rom_bank_lowbit <= 0; + mirroring <= 0; + prg_rom_bank <= 0; + end else if (ce) begin + if (prg_ain[15] && prg_write) + {prg_rom_bank_mode, prg_rom_bank_lowbit, mirroring, prg_rom_bank} <= {prg_ain[1:0], prg_din[7:0]}; + end +end + +reg [6:0] prg_bank; +always begin + casez({prg_rom_bank_mode, prg_ain[14]}) + // Bank mode 0 ( 32K ) / CPU $8000-$BFFF: Bank B / CPU $C000-$FFFF: Bank (B OR 1) + 3'b00_0: prg_bank = {prg_rom_bank, prg_ain[13]}; + 3'b00_1: prg_bank = {prg_rom_bank | 6'b1, prg_ain[13]}; + // Bank mode 1 ( 128K ) / CPU $8000-$BFFF: Switchable 16 KB bank B / CPU $C000-$FFFF: Fixed to last bank in the cart + 3'b01_0: prg_bank = {prg_rom_bank, prg_ain[13]}; + 3'b01_1: prg_bank = {6'b111111, prg_ain[13]}; + // Bank mode 2 ( 8K ) / CPU $8000-$9FFF: Sub-bank b of 16 KB PRG ROM bank B / CPU $A000-$FFFF: Mirrors of $8000-$9FFF + 3'b10_?: prg_bank = {prg_rom_bank, prg_rom_bank_lowbit}; + // Bank mode 3 ( 16K ) / CPU $8000-$BFFF: 16 KB bank B / CPU $C000-$FFFF: Mirror of $8000-$BFFF + 3'b11_?: prg_bank = {prg_rom_bank, prg_ain[13]}; + endcase +end + +assign prg_aout = {2'b00, prg_bank, prg_ain[12:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; // CHR RAM? +assign chr_aout = {9'b10_0000_000, chr_ain[12:0]}; +assign vram_ce = chr_ain[13]; +assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10]; + +endmodule + + +// Mapper 16, 153, 159 Bandai +module Mapper16( + 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 + output [17:0] mapper_addr, + input [7:0] mapper_data_in, + output [7:0] mapper_data_out, + output mapper_prg_write, + output mapper_ovr +); + +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 prg_allow; +wire chr_allow; +reg vram_a10; +wire vram_ce; +reg irq; +wire [15:0] flags_out = {14'd0, prg_bus_write, 1'b0}; +wire [7:0] prg_dout; + +reg outer_prg_bank; +reg [3:0] inner_prg_bank; +reg [7: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; +reg [3:0] prg_sel; +reg [1:0] mirroring; +reg irq_enable; +reg irq_up; +reg [15:0] irq_counter; +reg [15:0] irq_latch; +reg eeprom_scl, eeprom_sda; +wire submapper5 = (flags[24:21] == 5); +wire mapper153 = (flags[7:0] == 153); +wire mapper159 = (flags[7:0] == 159); +wire mapperalt = submapper5 | mapper159 | mapper153; + +always @(posedge clk) begin + if (~enable) begin + outer_prg_bank <= 0; + inner_prg_bank <= 0; + 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; + mirroring <= 0; + irq_counter <= 0; + irq_latch <= 0; + irq_up <= 0; + eeprom_scl <= 0; + eeprom_sda <= 0; + end else if (ce) begin + irq_up <= 1'b0; + if (prg_write) + if(((prg_ain[14:13] == 2'b11) && (!mapperalt)) || (prg_ain[15])) // Cover all from $6000 to $FFFF to maximize compatibility + case(prg_ain & 'hf) // Registers are mapped every 16 bytes + 'h0: if (!mapper153) chr_bank_0 <= prg_din[7:0]; else outer_prg_bank <= prg_din[0]; + 'h1: if (!mapper153) chr_bank_1 <= prg_din[7:0]; else outer_prg_bank <= prg_din[0]; + 'h2: if (!mapper153) chr_bank_2 <= prg_din[7:0]; else outer_prg_bank <= prg_din[0]; + 'h3: if (!mapper153) chr_bank_3 <= prg_din[7:0]; else outer_prg_bank <= prg_din[0]; + 'h4: if (!mapper153) chr_bank_4 <= prg_din[7:0]; else outer_prg_bank <= prg_din[0]; + 'h5: if (!mapper153) chr_bank_5 <= prg_din[7:0]; else outer_prg_bank <= prg_din[0]; + 'h6: if (!mapper153) chr_bank_6 <= prg_din[7:0]; else outer_prg_bank <= prg_din[0]; + 'h7: if (!mapper153) chr_bank_7 <= prg_din[7:0]; else outer_prg_bank <= prg_din[0]; + 'h8: inner_prg_bank <= prg_din[3:0]; + 'h9: mirroring <= prg_din[1:0]; + 'ha: {irq_up, irq_enable} <= {1'b1, prg_din[0]}; + 'hb: begin + if (mapperalt) + irq_latch[7:0] <= prg_din[7:0]; + else + irq_counter[7:0] <= prg_din[7:0]; + end + + 'hc: begin + if (mapperalt) + irq_latch[15:8] <= prg_din[7:0]; + else + irq_counter[15:8] <= prg_din[7:0]; + end + + 'hd: {eeprom_sda, eeprom_scl} <= prg_din[6:5]; //RAM enable or EEPROM control + endcase + + if (irq_enable) + irq_counter <= irq_counter - 16'd1; + + if (irq_up) begin + irq <= 1'b0; // IRQ ACK + if (mapperalt) + irq_counter <= irq_latch; + end + + if ((irq_counter == 16'h0000) && (irq_enable)) + irq <= 1'b1; // IRQ + end +end + +always begin + // mirroring + 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]}; // single screen lower + endcase +end + +reg [4:0] prgsel; +always begin + case(prg_ain[15:14]) + 2'b10: prgsel = {outer_prg_bank, inner_prg_bank}; // $8000 is swapable + 2'b11: prgsel = {outer_prg_bank, 4'hF}; // $C000 is hardwired to last inner bank + default: prgsel = 0; + endcase +end + +reg [7:0] chrsel; +always begin + casez(chr_ain[12:10]) + 0: chrsel = chr_bank_0; + 1: chrsel = chr_bank_1; + 2: chrsel = chr_bank_2; + 3: chrsel = chr_bank_3; + 4: chrsel = chr_bank_4; + 5: chrsel = chr_bank_5; + 6: chrsel = chr_bank_6; + 7: chrsel = chr_bank_7; + endcase +end + +assign chr_aout = mapper153 ? {9'b10_0000_000, chr_ain[12:0]} : {4'b10_00, chrsel, chr_ain[9:0]}; // 1kB banks or 8kb unbanked +wire [21:0] prg_aout_tmp = {3'b00_0, prgsel, 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; +// EEPROM - not used - Could use write to EEPROM cycle for both reads and write accesses, but this is easier +assign prg_dout = (!mapper153 && prg_is_ram) ? prg_write ? mapper_data_out : {3'b111, sda_out, 4'b1111} : 8'hFF; +wire prg_bus_write = (!mapper153 && prg_is_ram); + +assign prg_allow = (prg_ain[15] && !prg_write) || (prg_is_ram && eeprom_scl && mapper153); +assign chr_allow = flags[15]; +assign vram_ce = chr_ain[13]; + +wire sda_out; +wire [7:0] ram_addr; +wire ram_read; +assign mapper_addr[17:8] = 0; +assign mapper_addr[7:0] = ram_addr; +assign mapper_ovr = 1'b1; + +EEPROM_24C0x eeprom( + .type_24C01(mapper159), //24C01 is 128 bytes, 24C02 is 256 bytes + .clk(clk), + .ce(ce), + .reset(~enable), + .SCL(eeprom_scl), // Serial Clock + .SDA_in(eeprom_sda), // Serial Data (same pin as below, split for convenience) + .SDA_out(sda_out), // Serial Data (same pin as above, split for convenience) + .E_id(3'b000), // Chip Enable + .WC_n(1'b0), // ~Write Control + .data_from_ram(mapper_data_in), // Data read from RAM + .data_to_ram(mapper_data_out), // Data written to RAM + .ram_addr(ram_addr), // RAM Address + .ram_read(ram_read), // RAM read + .ram_write(mapper_prg_write), // RAM write + .ram_done(1'b1) // RAM access done +); + +endmodule + +// Mapper 18, Jaleco SS88006 +module Mapper18( + 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 ? {1'b0, audio_in[15:1]} : 16'hZ; + +wire [21:0] prg_aout, chr_aout; +wire prg_allow; +wire chr_allow; +reg vram_a10; +reg irq; +wire [7:0] prg_dout; +wire vram_ce; +reg [15:0] flags_out = 0; + +reg [7:0] prg_bank_0, prg_bank_1, prg_bank_2; +reg [7: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; +reg [3:0] prg_sel; +reg [1:0] mirroring; +reg irq_ack; +reg [3:0] irq_enable; +reg [15:0] irq_reload; +reg [15:0] irq_counter; +reg [1:0] ram_enable; + +always @(posedge clk) +if (~enable) begin + prg_bank_0 <= 8'hFF; + prg_bank_1 <= 8'hFF; + prg_bank_2 <= 8'hFF; + 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; + mirroring <= 0; + irq_reload <= 0; + irq_counter <= 0; + irq_enable <= 4'h0; +end else if (ce) begin + irq_ack <= 1'b0; + if (prg_write) + if(prg_ain[15]) // Cover all from $8000 to $FFFF to maximize compatibility + case({prg_ain[14:12],prg_ain[1:0]}) + 5'b000_00: prg_bank_0[3:0] <= prg_din[3:0]; + 5'b000_01: prg_bank_0[7:4] <= prg_din[3:0]; + 5'b000_10: prg_bank_1[3:0] <= prg_din[3:0]; + 5'b000_11: prg_bank_1[7:4] <= prg_din[3:0]; + 5'b001_00: prg_bank_2[3:0] <= prg_din[3:0]; + 5'b001_01: prg_bank_2[7:4] <= prg_din[3:0]; + 5'b010_00: chr_bank_0[3:0] <= prg_din[3:0]; + 5'b010_01: chr_bank_0[7:4] <= prg_din[3:0]; + 5'b010_10: chr_bank_1[3:0] <= prg_din[3:0]; + 5'b010_11: chr_bank_1[7:4] <= prg_din[3:0]; + 5'b011_00: chr_bank_2[3:0] <= prg_din[3:0]; + 5'b011_01: chr_bank_2[7:4] <= prg_din[3:0]; + 5'b011_10: chr_bank_3[3:0] <= prg_din[3:0]; + 5'b011_11: chr_bank_3[7:4] <= prg_din[3:0]; + 5'b100_00: chr_bank_4[3:0] <= prg_din[3:0]; + 5'b100_01: chr_bank_4[7:4] <= prg_din[3:0]; + 5'b100_10: chr_bank_5[3:0] <= prg_din[3:0]; + 5'b100_11: chr_bank_5[7:4] <= prg_din[3:0]; + 5'b101_00: chr_bank_6[3:0] <= prg_din[3:0]; + 5'b101_01: chr_bank_6[7:4] <= prg_din[3:0]; + 5'b101_10: chr_bank_7[3:0] <= prg_din[3:0]; + 5'b101_11: chr_bank_7[7:4] <= prg_din[3:0]; + 5'b110_00: irq_reload[3:0] <= prg_din[3:0]; + 5'b110_01: irq_reload[7:4] <= prg_din[3:0]; + 5'b110_10: irq_reload[11:8] <= prg_din[3:0]; + 5'b110_11: irq_reload[15:12] <= prg_din[3:0]; + 5'b111_00: {irq_ack, irq_counter} <= {1'b1, irq_reload}; + 5'b111_01: {irq_ack, irq_enable} <= {1'b1, prg_din[3:0]}; + 5'b111_10: mirroring <= prg_din[1:0]; + 5'b111_11: ram_enable <= prg_din[1:0]; + endcase + + //Is this necessary? or even correct? Just load number of needed bits into separate counter instead? + if (irq_enable[0]) begin + irq_counter[3:0] <= irq_counter[3:0] - 4'd1; + if (irq_counter[3:0] == 4'h0) begin + if (irq_enable[3]) begin + irq <= 1'b1; // IRQ + end else begin + irq_counter[7:4] <= irq_counter[7:4] - 4'd1; + if (irq_counter[7:4] == 4'h0) begin + if (irq_enable[2]) begin + irq <= 1'b1; // IRQ + end else begin + irq_counter[11:8] <= irq_counter[11:8] - 4'd1; + if (irq_counter[11:8] == 4'h0) begin + if (irq_enable[1]) begin + irq <= 1'b1; // IRQ + end else begin + irq_counter[15:12] <= irq_counter[15:12] - 4'd1; + if (irq_counter[15:12] == 4'h0) begin + irq <= 1'b1; // IRQ + end + end + end + end + end + end + end + end + if (irq_ack) + irq <= 1'b0; // IRQ ACK +end + +always begin + // mirroring + casez(mirroring[1:0]) + 2'b00: vram_a10 = {chr_ain[11]}; // horizontal + 2'b01: vram_a10 = {chr_ain[10]}; // vertical + 2'b1?: vram_a10 = {mirroring[0]}; // single screen lower + endcase +end + +reg [7:0] prgsel; +always begin + case(prg_ain[14:13]) + 2'b00: prgsel = prg_bank_0; // $8000 is swapable + 2'b01: prgsel = prg_bank_1; // $A000 is swapable + 2'b10: prgsel = prg_bank_2; // $C000 is swapable + 2'b11: prgsel = 8'hFF; // $E000 is hardwired to last bank + endcase +end + +reg [7:0] chrsel; +always begin + casez(chr_ain[12:10]) + 0: chrsel = chr_bank_0; + 1: chrsel = chr_bank_1; + 2: chrsel = chr_bank_2; + 3: chrsel = chr_bank_3; + 4: chrsel = chr_bank_4; + 5: chrsel = chr_bank_5; + 6: chrsel = chr_bank_6; + 7: chrsel = chr_bank_7; + endcase +end + +assign chr_aout = {4'b10_00, chrsel, chr_ain[9:0]}; // 1kB banks +wire [21:0] prg_aout_tmp = {2'b00, prgsel[6:0], prg_ain[12:0]}; // 8kB 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_dout = 8'hFF; + +assign prg_allow = (prg_ain[15] && !prg_write) || (prg_is_ram && ram_enable[0] && (ram_enable[1] || !prg_write)); +assign chr_allow = flags[15]; +assign vram_ce = chr_ain[13]; + +endmodule + +// 32 - IREM +module Mapper32( + 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 [4:0] prgreg0; +reg [4:0] prgreg1; +reg [7:0] chrreg0; +reg [7:0] chrreg1; +reg [7:0] chrreg2; +reg [7:0] chrreg3; +reg [7:0] chrreg4; +reg [7:0] chrreg5; +reg [7:0] chrreg6; +reg [7:0] chrreg7; +reg prgmode; +reg mirror; +wire submapper1 = (flags[21] == 1); // default (0) default submapper; (1) Major League +reg [4:0] prgsel; +reg [7:0] chrsel; + +always @(posedge clk) +if (~enable) begin + prgmode <= 1'b0; +end else if (ce) begin + if ((prg_ain[15:14] == 2'b10) & prg_write) begin + casez ({prg_ain[13:12], submapper1, prg_ain[2:0]}) + 6'b00_?_???: prgreg0 <= prg_din[4:0]; + 6'b01_0_???: {prgmode, mirror} <= prg_din[1:0]; + 6'b10_?_???: prgreg1 <= prg_din[4:0]; + 6'b11_?_000: chrreg0 <= prg_din; + 6'b11_?_001: chrreg1 <= prg_din; + 6'b11_?_010: chrreg2 <= prg_din; + 6'b11_?_011: chrreg3 <= prg_din; + 6'b11_?_100: chrreg4 <= prg_din; + 6'b11_?_101: chrreg5 <= prg_din; + 6'b11_?_110: chrreg6 <= prg_din; + 6'b11_?_111: chrreg7 <= prg_din; + endcase + end +end + +always begin + // mirroring mode + casez({submapper1, mirror}) + 2'b00 : vram_a10 = {chr_ain[10]}; // vertical + 2'b01 : vram_a10 = {chr_ain[11]}; // horizontal + 2'b1? : vram_a10 = {1'b1}; // 1 screen lower + endcase + + // PRG ROM bank size select + casez({prg_ain[14:13], prgmode}) + 3'b000 : prgsel = prgreg0; + 3'b001 : prgsel = {5'b11110}; + 3'b01? : prgsel = prgreg1; + 3'b100 : prgsel = {5'b11110}; + 3'b101 : prgsel = prgreg0; + 3'b11? : prgsel = {5'b11111}; + endcase + + // CHR ROM bank size select + casez({chr_ain[12:10]}) + 3'b000 : chrsel = chrreg0; + 3'b001 : chrsel = chrreg1; + 3'b010 : chrsel = chrreg2; + 3'b011 : chrsel = chrreg3; + 3'b100 : chrsel = chrreg4; + 3'b101 : chrsel = chrreg5; + 3'b110 : chrsel = chrreg6; + 3'b111 : chrsel = chrreg7; + endcase +end + +assign vram_ce = chr_ain[13]; +assign prg_aout = {4'b00_00, prgsel, prg_ain[12:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; +assign chr_aout = {4'b10_00, chrsel, chr_ain[9:0]}; + +endmodule + +// Mapper 42, used for hacked FDS games converted to cartridge form +module Mapper42( + 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 [3:0] prg_bank; +reg [3:0] chr_bank; +reg [3:0] prg_sel; +reg mirroring; +reg irq_enable; +reg [14:0] irq_counter; + +always @(posedge clk) +if (~enable) begin + prg_bank <= 0; + chr_bank <= 0; + mirroring <= flags[14]; + irq_counter <= 0; +end else if (ce) begin + if (prg_write) + case(prg_ain & 16'he003) + 16'h8000: chr_bank <= prg_din[3:0]; + 16'he000: prg_bank <= prg_din[3:0]; + 16'he001: mirroring <= prg_din[3]; + 16'he002: irq_enable <= prg_din[1]; + endcase + + if (irq_enable) + irq_counter <= irq_counter + 15'd1; + else begin + irq <= 1'b0; // ACK + irq_counter <= 0; + end + + if (irq_counter == 15'h6000) + irq <= 1'b1; +end + +always @* begin + // PRG bank selection + // 6000-7FFF: Selectable + // 8000-9FFF: bank #0Ch + // A000-BFFF: bank #0Dh + // C000-DFFF: bank #0Eh + // E000-FFFF: bank #0Fh + case(prg_ain[15:13]) + 3'b011: prg_sel = prg_bank; // $6000-$7FFF + 3'b100: prg_sel = 4'hC; + 3'b101: prg_sel = 4'hD; + 3'b110: prg_sel = 4'hE; + 3'b111: prg_sel = 4'hF; + default: prg_sel = 0; + endcase +end + +assign prg_aout = {5'b0, prg_sel, prg_ain[12:0]}; // 8kB banks +assign chr_aout = {5'b10_000, chr_bank, chr_ain[12:0]}; // 8kB banks + +assign prg_allow = (prg_ain >= 16'h6000) && !prg_write; +assign chr_allow = flags[15]; +assign vram_ce = chr_ain[13]; +assign vram_a10 = mirroring ? chr_ain[10] : chr_ain[11]; + +endmodule + + +// Mapper 65, IREM H3001 +module Mapper65( + 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; +reg irq; +reg [15:0] flags_out = 0; + +reg [7:0] prg_bank_0, prg_bank_1, prg_bank_2; +reg [7: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; +reg mirroring; +reg irq_ack; +reg irq_enable; +reg [15:0] irq_reload; +reg [15:0] irq_counter; + +always @(posedge clk) +if (~enable) begin + prg_bank_0 <= 8'h00; + prg_bank_1 <= 8'h01; + prg_bank_2 <= 8'hFE; + 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; + mirroring <= 0; + irq_reload <= 0; + irq_counter <= 0; + irq_enable <= 0; +end else if (ce) begin + irq_ack <= 1'b0; + if ((prg_write) && (prg_ain[15])) // Cover all from $8000 to $FFFF to maximize compatibility + case({prg_ain[14:12],prg_ain[2:0]}) + 6'b000_000: prg_bank_0 <= prg_din; + 6'b010_000: prg_bank_1 <= prg_din; + 6'b100_000: prg_bank_2 <= prg_din; + 6'b011_000: chr_bank_0 <= prg_din; + 6'b011_001: chr_bank_1 <= prg_din; + 6'b011_010: chr_bank_2 <= prg_din; + 6'b011_011: chr_bank_3 <= prg_din; + 6'b011_100: chr_bank_4 <= prg_din; + 6'b011_101: chr_bank_5 <= prg_din; + 6'b011_110: chr_bank_6 <= prg_din; + 6'b011_111: chr_bank_7 <= prg_din; + 6'b001_001: mirroring <= prg_din[7]; + 6'b001_011: {irq_ack, irq_enable} <= {1'b1, prg_din[7]}; + 6'b001_100: {irq_ack, irq_counter} <= {1'b1, irq_reload}; + 6'b001_101: irq_reload[15:8] <= prg_din; + 6'b001_110: irq_reload[7:0] <= prg_din; + endcase + + if (irq_enable) begin + irq_counter <= irq_counter - 16'd1; + if (irq_counter == 16'h0) begin + irq <= 1'b1; // IRQ + irq_enable <= 0; + irq_counter <= 0; + end + end + if (irq_ack) + irq <= 1'b0; // IRQ ACK +end + +always begin + vram_a10 = mirroring ? chr_ain[11] : chr_ain[10]; // horizontal:vertical +end + +reg [7:0] prgsel; +always begin + case(prg_ain[14:13]) + 2'b00: prgsel = prg_bank_0; // $8000 is swapable + 2'b01: prgsel = prg_bank_1; // $A000 is swapable + 2'b10: prgsel = prg_bank_2; // $C000 is swapable + 2'b11: prgsel = 8'hFF; // $E000 is hardwired to last bank + endcase +end + +reg [7:0] chrsel; +always begin + casez(chr_ain[12:10]) + 0: chrsel = chr_bank_0; + 1: chrsel = chr_bank_1; + 2: chrsel = chr_bank_2; + 3: chrsel = chr_bank_3; + 4: chrsel = chr_bank_4; + 5: chrsel = chr_bank_5; + 6: chrsel = chr_bank_6; + 7: chrsel = chr_bank_7; + endcase +end + assign chr_aout = {4'b10_00, chrsel, chr_ain[9:0]}; // 1kB banks + assign prg_aout = {2'b00, prgsel[6:0], prg_ain[12:0]}; // 8kB banks + +assign prg_allow = (prg_ain[15] && !prg_write); + assign chr_allow = flags[15]; + assign vram_ce = chr_ain[13]; +endmodule + + +// 41 - Caltron 6-in-1 +module Mapper41( + 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; +reg [15:0] flags_out = 0; + + +reg [2:0] prg_bank; +reg [1:0] chr_outer_bank, chr_inner_bank; +reg mirroring; + +always @(posedge clk) +if (~enable) begin + prg_bank <= 0; + chr_outer_bank <= 0; + chr_inner_bank <= 0; + mirroring <= 0; +end else if (ce && prg_write) begin + if (prg_ain[15:11] == 5'b01100) begin + {mirroring, chr_outer_bank, prg_bank} <= prg_ain[5:0]; + end else if (prg_ain[15] && prg_bank[2]) begin + // The Inner CHR Bank Select only can be written while the PRG ROM bank is 4, 5, 6, or 7 + chr_inner_bank <= prg_din[1:0]; + end +end + +assign prg_aout = {4'b00_00, prg_bank, prg_ain[14:0]}; +assign chr_allow = flags[15]; +assign chr_aout = {5'b10_000, chr_outer_bank, chr_inner_bank, chr_ain[12:0]}; +assign vram_ce = chr_ain[13]; +assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10]; +assign prg_allow = prg_ain[15] && !prg_write; + +endmodule + +// 218 - Magic Floor +module Mapper218( + 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; +reg [15:0] flags_out = 0; + +assign prg_aout = {7'b00_0000_0, prg_ain[14:0]}; +assign chr_allow =1'b1; +assign chr_aout = {9'b10_0000_000, chr_ain[12:11], vram_a10, chr_ain[9:0]}; +assign vram_ce = 1'b1; //Always internal CHR RAM (no CHR ROM or RAM on cart) +assign vram_a10 = flags[16] ? (flags[14] ? chr_ain[13] : chr_ain[12]) : flags[14] ? chr_ain[10] : chr_ain[11]; // 11=1ScrB, 10=1ScrA, 01=vertical,00=horizontal +assign prg_allow = prg_ain[15] && !prg_write; + +endmodule + +// iNES Mapper 228 represents the board used by Active Enterprises for Action 52 and Cheetahmen II. +module Mapper228( + 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; +reg [15:0] flags_out = 0; + + +reg mirroring; +reg [1:0] prg_chip; +reg [4:0] prg_bank; +reg prg_bank_mode; +reg [5:0] chr_bank; +always @(posedge clk) +if (~enable) begin + {mirroring, prg_chip, prg_bank, prg_bank_mode} <= 0; + chr_bank <= 0; +end else if (ce) begin + if (prg_ain[15] & prg_write) begin + {mirroring, prg_chip, prg_bank, prg_bank_mode} <= prg_ain[13:5]; + chr_bank <= {prg_ain[3:0], prg_din[1:0]}; + end +end + +assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10]; +wire prglow = prg_bank_mode ? prg_bank[0] : prg_ain[14]; +wire [1:0] addrsel = {prg_chip[1], prg_chip[1] ^ prg_chip[0]}; +assign prg_aout = {1'b0, addrsel, prg_bank[4:1], prglow, prg_ain[13:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; +assign chr_aout = {3'b10_0, chr_bank, chr_ain[12:0]}; +assign vram_ce = chr_ain[13]; + +endmodule + + +module Mapper234( + 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; +reg [15:0] flags_out = 0; + + +reg [2:0] block, inner_chr; +reg mode, mirroring, inner_prg; +always @(posedge clk) +if (~enable) begin + block <= 0; + {mode, mirroring} <= 0; + inner_chr <= 0; + inner_prg <= 0; +end else if (ce) begin + if (prg_read && prg_ain[15:7] == 9'b1111_1111_1) begin + // Outer bank control $FF80 - $FF9F + if (prg_ain[6:0] < 7'h20 && (block == 0)) begin + {mirroring, mode} <= prg_din[7:6]; + block <= prg_din[3:1]; + {inner_chr[2], inner_prg} <= {prg_din[0], prg_din[0]}; + end + // Inner bank control ($FFE8-$FFF7) + if (prg_ain[6:0] >= 7'h68 && prg_ain[6:0] < 7'h78) begin + {inner_chr[2], inner_prg} <= mode ? {prg_din[6], prg_din[0]} : {inner_chr[2], inner_prg}; + inner_chr[1:0] <= prg_din[5:4]; + end + end +end + +assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10]; +assign prg_aout = {3'b00_0, block, inner_prg, prg_ain[14:0]}; +assign chr_aout = {3'b10_0, block, inner_chr, chr_ain[12:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; +assign vram_ce = chr_ain[13]; + +endmodule + + +// 92 - Jaleco JF-19 -- no audio samples +// 72 - Jaleco JF-17 -- no audio samples +module Mapper72( + 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; +reg [15:0] flags_out = 0; + +reg [3:0] prg_bank; +reg [3:0] chr_bank; +wire [7:0] mapper = flags[7:0]; +reg last_prg; +reg last_chr; +wire mapper72 = (mapper == 72); + +always @(posedge clk) +if (~enable) begin + prg_bank <= 0; + chr_bank <= 0; + last_prg <= 0; + last_chr <= 0; +end else if (ce) begin + if (prg_ain[15] & prg_write) begin + if ((!last_prg) && (prg_din[7])) + {prg_bank} <= {prg_din[3:0]}; + + if ((!last_chr) && (prg_din[6])) + {chr_bank} <= {prg_din[3:0]}; + + {last_prg, last_chr} <= prg_din[7:6]; + end +end + +assign prg_aout = {4'b00_00, prg_ain[14] ^ mapper72 ? prg_bank : mapper72 ? 4'b1111 : 4'b0000, prg_ain[13:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; +assign chr_aout = {5'b10_000, chr_bank, chr_ain[12:0]}; +assign vram_ce = chr_ain[13]; +assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11]; + +endmodule + + +// 162 Waixing - Zelda San Shen Zhi Li +module Mapper162( + 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; +reg [15:0] flags_out = 0; + +wire [1:0] reg_a = flags[7:0] == 162 ? 2'd1 : 2'd2; +wire [1:0] reg_b = flags[7:0] == 162 ? 2'd2 : 2'd1; + +reg [7:0] state[4]; + +// register 0x5000 to 0x5FFF +wire [7:0] prg_bank; + +always_comb begin + case ({state[3][2], 1'b0, state[3][0]}) + 0: prg_bank = {state[reg_b][3:0], state[0][3:2], state[reg_a][1], 1'b0}; + 1: prg_bank = {state[reg_b][3:0], state[0][3:2], 2'b0}; + 4: prg_bank = {state[reg_b][3:0], state[0][3:1], state[reg_a][1]}; + 5: prg_bank = {state[reg_b][3:0], state[0][3:0]}; + endcase +end + +always @(posedge clk) begin + if (~enable) begin + state <= '{8'd3, 8'd0, 8'd0, 8'd7}; + end else if (ce) begin + if (prg_ain[14:12] == 3'b101 && prg_write) + state[prg_ain[9:8]] <= prg_din; + end +end + +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_bank[5:0], prg_ain[14:0]}; +assign prg_allow = prg_ain[15] && !prg_write || prg_is_ram; +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]; + +endmodule + + +// 164 Waixing - Final Fantasy V +module Mapper164( + 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; +reg [15:0] flags_out = 0; + +reg [7:0] prg_bank; + +always @(posedge clk) begin + if (~enable) begin + prg_bank <= 8'h0F; + end else if (ce) begin + if (prg_write) begin + case (prg_ain & 16'h7300) + 'h5000: prg_bank[3:0] <= prg_din[3:0]; + 'h5100: prg_bank[7:4] <= prg_din[3:0]; + endcase + end + end +end + +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_bank[5:0], prg_ain[14:0]}; +assign prg_allow = prg_ain[15] && !prg_write || prg_is_ram; +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]; + +endmodule + +// 163 Nanjing +module Nanjing( + 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 [19:0] ppuflags, + input ppu_ce +); + +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; +reg [7:0] prg_dout; +wire [15:0] flags_out = {14'd0, prg_bus_write, 1'b0}; +reg prg_bus_write; + +reg [7:0] prg_bank; +reg chr_bank; +reg chr_switch; +reg trigger; +reg trig_comp; + +reg [7:0] security[4]; + +wire [8:0] scanline = ppuflags[19:11]; +wire [8:0] cycle = ppuflags[10:2]; + +always @(posedge clk) begin + if (~enable) begin + prg_bank <= 8'h0F; + trigger <= 0; // Initial value 0 + security <= '{8'h00, 8'h00, 8'h00, 8'h00}; + chr_switch <= 0; + trig_comp <= 1; // Initial value 1 + end else if (ce) begin + prg_dout <= prg_din; + prg_bus_write <= 0; + if (prg_write) begin + if (prg_ain == 16'h5101) begin + if (trig_comp && ~|prg_din) + trigger <= ~trigger; + trig_comp <= |prg_din; + end else begin + case (prg_ain & 16'h7300) + // If the most significant bit of this register is set, it does automatic CHR RAM switching + 'h5000: begin + prg_bank[3:0] <= prg_din[3:0]; + chr_switch <= prg_din[7]; + security[0] <= prg_din; + end + + 'h5100: begin + security[1] <= prg_din; + if (prg_din == 6) + prg_bank <= 8'h3; + end + + 'h5200: begin + prg_bank[7:4] <= prg_din[3:0]; + security[2] <= prg_din; + end + + 'h5300: security[3] <= prg_din; + endcase + end + end else if (prg_read) begin // Security stuff as Mesen does it + prg_bus_write <= 1'b1; + case (prg_ain & 16'h7700) + 'h5100: prg_dout <= security[0] | security[1] | security[3] | (security[2] ^ 8'hFF); + 'h5500: prg_dout <= trigger ? (security[3] | security[0]) : 8'h0; + default: begin + prg_dout <= 8'hFF; + prg_bus_write <= 0; + end + endcase + end + end + + // The exact way this works is unknown but is conjectured + // to resemble iNES Mapper 096, latching PA9 at start of nametable reads. + // When turned on, both 4K CHR RAM banks 0000-0FFF and 1000-1FFF map to 0000-0FFF + // for scanline 240 until scanline 128. Then at scanline 128, both 4K CHR banks + // point to 1000-1FFF. + if(~enable) begin + chr_bank <= 0; + end else if (ppu_ce) begin + if (cycle > 254) begin + if (scanline == 239) + chr_bank <= 0; + else if(scanline == 127) + chr_bank <= 1; + end + end +end + +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_bank[5:0], prg_ain[14:0]}; +assign prg_allow = prg_ain[15] && !prg_write || prg_is_ram; +assign chr_allow = flags[15]; +assign chr_aout = {9'b10_0000_000, chr_switch ? chr_bank : chr_ain[12], chr_ain[11:0]}; +assign vram_ce = chr_ain[13]; +assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11]; + +endmodule +// Combine with other mapper (15?) +// #225 - 64-in-1 +// #255 - 110-in-1 - This runs with buggy menu selection. It runs correctly as mapper 225. +// Unsure if any games require simulating missing 74-670 RAM (4 nybbles). +module Mapper225( + 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 [7:0] prg_dout; +wire prg_bus_write = (~mapper255 & prg_ram); +wire prg_ram = (prg_ain[15:11] == 5'b01011); +wire [15:0] flags_out = {14'h0, prg_bus_write, 1'b0}; + +wire [7:0] mapper = flags[7:0]; +wire mapper255 = (mapper == 8'd255); + +// A~[1BMZ PPPP PpCC CCCC] +// ||| |||| |||| |||| +// +-----------++-++++ - Select 8 KiB CHR at PPU $0000 +// ||| |||| || +// +---++++--++------- - Select 16 KiB PRG at CPU $8000 and $C000 if Z=1 +// +---++++--+-------- - Select 32 KiB PRG at CPU $8000 if Z=0 +// |+---------------- - Select PRG bank size: 0-32K 1-16K +// +----------------- - Nametable mirroring: 0-PPUA10 ("vertical") 1-PPUA11 ("horizontal") +//74'670: (otherwise open bus) +// $5800-5803: [.... RRRR] RAM (readable/writable) +// (16 bits of RAM -- 4 bits in each of the 4 regs) +// $5804-5FFF: mirrors $5800-5803 +reg [14:0] bank_mode; +wire mirroring = bank_mode[13]; +wire prg_mode = bank_mode[12]; +reg [3:0] ram [3:0]; + +always @(posedge clk) begin + if (~enable) begin + // resets? + end else if (ce) begin + if (prg_ain[15] && prg_write) + bank_mode <= prg_ain[14:0]; + if (prg_ram && prg_write) // 5800-5FFF + ram[prg_ain[1:0]] <= prg_din[3:0]; + end +end + +assign prg_dout = {4'h0, ram[prg_ain[1:0]]}; +assign prg_aout = {1'b0, bank_mode[14], bank_mode[11:7], prg_mode ? bank_mode[6] : prg_ain[14], prg_ain[13:0]}; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = flags[15]; // CHR RAM? +assign chr_aout = {2'b10, bank_mode[14], bank_mode[5:0], chr_ain[12:0]}; +assign vram_ce = chr_ain[13]; +assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10]; + +endmodule + + +// #31 - NSF Player +module NSF( + 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 + output [5:0] exp_audioe, // Expansion Enabled (0x0=None, 0x1=VRC6, 0x2=VRC7, 0x4=FDS, 0x8=MMC5, 0x10=N163, 0x20=SS5B + inout [15:0] flags_out_b, // flags {0, 0, 0, 0, 0, prg_conflict, prg_bus_write, has_chr_dout} + input [7:0] fds_din // fds data in +); + +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 ? {audio_in[15:0]} : 16'hZ; +assign exp_audioe = enable ? nsf_reg[3][5:0] : 6'h00; + +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 [15:0] flags_out = {14'd0, prg_bus_write, 1'b0}; +reg prg_bus_write; + +wire [3:0] submapper = flags[24:21]; +reg [7:0] nsf_reg [15:0]; +reg [15:0] counter; +reg [5:0] clk1MHz; + +// Resuse MMC5 multiplier instead? +reg [7:0] multiplier_1; +reg [7:0] multiplier_2; +wire [15:0] multiply_result = multiplier_1 * multiplier_2; + +/* +;---------------------------------- +; NSF player for PowerPak +; +; Player rom is at $4100-4FFF (NSF header at $4100) +; +; PowerPak registers: +; +; 5FF0: timer latch LSB +; 5FF1: timer latch MSB +; 5FF2: timer status (Read: bit7=timer wrapped, Write: clear status) +; 5FF3: Expansion audio control (copy header[0x7B] here) +; 5FF6-5FFF: banking registers (as described in NSF spec) +; +; Timer details: +; PowerPak NSF mapper has a 16bit 1MHz counter that counts down from [5FF1:5FF0] to 0. +; After the counter reaches 0, it's automatically reloaded and timer status bit is set. +; Clear the status bit by writing to $5FF2. +; +;----------------------------------- +*/ +always @(posedge clk) begin + // 21.477272MHz/1MHz + // Using 21.5; Replace with actual pll? + clk1MHz <= clk1MHz + 1'b1; + if (clk1MHz == 6'd42) + clk1MHz <= 6'd0; + if (clk1MHz == 6'd21 || clk1MHz == 6'd42) begin + counter <= counter - 1'b1; + if (counter == 16'h0000) + begin + counter <= {nsf_reg[1], nsf_reg[0]}; + nsf_reg[2] <= 8'h80; + end + end + + + if (~enable) begin + nsf_reg[4'h3] <= 8'h00; + nsf_reg[4'h6] <= 8'h06; + nsf_reg[4'h7] <= 8'h07; + nsf_reg[4'h8] <= 8'h00; + nsf_reg[4'h9] <= 8'h01; + nsf_reg[4'hA] <= 8'h02; + nsf_reg[4'hB] <= 8'h03; + nsf_reg[4'hC] <= 8'h04; + nsf_reg[4'hD] <= 8'h05; + nsf_reg[4'hE] <= 8'h06; + nsf_reg[4'hF] <= 8'hFF; + end else if (ce) begin + if ((prg_ain[15:4]==12'h5FF) && prg_write) + nsf_reg[prg_ain[3:0]] <= prg_din; + if ((prg_ain==16'h5FF2) && prg_write) + nsf_reg[2] <= 8'h00; + if ((prg_ain==16'h5205) && prg_write) + multiplier_1 <= prg_din; + if ((prg_ain==16'h5206) && prg_write) + multiplier_2 <= prg_din; + end +end + +wire [9:0] prg_bank; +always begin + casez({prg_ain[15:12], exp_audioe[2]}) + 5'b00???: prg_bank = 10'h0;//{10'b11_1110_0000}; + 5'b0100?: prg_bank = 10'h0;//{10'b11_1110_0000}; + 5'b0101?: prg_bank = {10'b11_1110_0000}; + 5'b011?0: prg_bank = {9'b11_1100_000, prg_ain[12]}; + 5'b011?1: prg_bank = {2'b01, nsf_reg[{3'b011, prg_ain[12]}]}; + 5'b1????: prg_bank = {2'b01, nsf_reg[{1'b1, prg_ain[14:12]}]}; + endcase +end + +always begin + prg_bus_write = 1'b1; + 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[15:8] == 8'h40) begin + prg_dout = fds_din; + end else if (prg_ain == 16'h5FF2) begin + prg_dout = nsf_reg[4'h2]; + end else begin + prg_dout = prg_din; + prg_bus_write = 0; + end +end + +assign prg_aout = ((submapper == 4'hF) && ({prg_ain[15:1],1'b0} == 16'hFFFC)) ? {10'h0, prg_ain[11:0]} : {prg_bank, prg_ain[11:0]}; +assign prg_allow = (((prg_ain[15] || ((prg_ain>=16'h4080) && (prg_ain<16'h4FFF))) && !prg_write) || (prg_ain[15:13]==3'b011) + || (prg_ain[15:10]==6'b010111 && prg_ain[9:4]!=6'b111111) || ((prg_ain>=16'h8000) && (prg_ain<16'hDFFF) && exp_audioe[2])); +assign chr_allow = flags[15]; // CHR RAM always... +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]; + +endmodule + +// 111 - Cheapocabra/GTROM +// Supports all features except LED and self-reflashing support +module Mapper111( + 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, chr_allow; +wire vram_ce, vram_a10; +wire [15:0] audio = audio_in; +wire [18:15] ramprgaout; +wire irq; + +reg [15:0] flags_out = 0; +reg [3:0] prgbank_reg; +reg chrbank_reg; +reg namebank_reg; + +always@(posedge clk) begin // register mask: 01x1 xxxx xxxx xxxx + if (~enable) begin + {prgbank_reg, chrbank_reg, namebank_reg} <= 0; + end else if(ce & prg_write & prg_ain[12] & prg_ain[14] & !prg_ain[15]) begin + prgbank_reg <= prg_din[3:0]; + chrbank_reg <= prg_din[4]; + namebank_reg <= prg_din[5]; + end +end + +assign chr_aout[21:15] = 7'b11_1100_0; +assign chr_aout[14:13] = {chr_ain[13], chr_ain[13]?namebank_reg:chrbank_reg}; +assign chr_aout[12:0] = chr_ain[12:0]; +assign vram_a10 = chr_aout[10]; +assign prg_aout[21:19] = 3'b000; +assign prg_aout[18:15] = prgbank_reg; +assign prg_aout[14:0] = prg_ain[14:0]; +assign prg_allow = prg_ain[15] && !prg_write; +assign chr_allow = 1'b1; +assign prg_dout = 8'hFF; +assign vram_ce = 1'b0; +assign irq = 1'b0; + +endmodule diff --git a/cores/nes/src/mmu.v b/cores/nes/src/mmu.v deleted file mode 100644 index ceafa39..0000000 --- a/cores/nes/src/mmu.v +++ /dev/null @@ -1,3282 +0,0 @@ -// Copyright (c) 2012-2013 Ludvig Strigeus -// This program is GPL Licensed. See COPYING for the full license. - -// No mapper chip -module MMC0(input clk, input ce, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - 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]; -endmodule - -// 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, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - 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 (reset) 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; -// $write("MMC1 RESET!\n"); - end else begin - if (shift[0]) begin -// $write("MMC1 WRITE %X to %X!\n", {prg_din[0], shift[4:1]}, prg_ain); - 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(control[1:0]) - 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 - -// MMC2 mapper chip. PRG ROM: 128kB. Bank Size: 8kB. CHR ROM: 128kB -module MMC2(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input chr_read, input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - -// 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 (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 (ce && chr_read) begin - latch_0 <= (chr_ain & 14'h3fff) == 14'h0fd8 ? 1'd0 : (chr_ain & 14'h3fff) == 14'h0fe8 ? 1'd1 : latch_0; - latch_1 <= (chr_ain & 14'h3ff8) == 14'h1fd8 ? 1'd0 : (chr_ain & 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 - -// 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, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output irq); - 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 [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 - 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 = 0; - - 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 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 four_screen_mirroring = flags[16] | DxROM; - 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; //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 (reset) 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, 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; - 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 - casez({prg_ain[14:13], prg_ain[1:0], mapper33 | mapper48, mapper48}) - 6'b00_?0_00: {chr_a12_invert, prg_rom_bank_mode, bank_select} <= {prg_din[7], prg_din[6], prg_din[2:0]}; // Bank select ($8000-$9FFE, even) - 6'b00_?1_00: 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 - 6'b01_?0_00: mirroring <= !prg_din[0]; // Mirroring ($A000-$BFFE, even) - 6'b01_?1_00: {ram_enable, ram_protect} <= {{4{prg_din[7]}},{4{prg_din[6]}}}; // PRG RAM protect ($A001-$BFFF, odd) - 6'b10_?0_00: irq_latch <= prg_din; // IRQ latch ($C000-$DFFE, even) - 6'b10_?1_00: irq_reload <= 1; // IRQ reload ($C001-$DFFF, odd) - 6'b11_?0_00: begin irq_enable <= 0; irq_reg[0] <= 0; end// IRQ disable ($E000-$FFFE, even) - 6'b11_?1_00: irq_enable <= 1; // IRQ enable ($E001-$FFFF, odd) - - 6'b00_00_10: begin prg_bank_0 <= prg_din[5:0]; mirroring <= prg_din[6]; end // Select 8 KB PRG ROM bank at $8000-$9FFF - 6'b00_00_11: prg_bank_0 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $8000-$9FFF - 6'b00_01_1?: prg_bank_1 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $A000-$BFFF - 6'b00_10_1?: chr_bank_0 <= prg_din; // Select 2 KB CHR bank at PPU $0000-$07FF - 6'b00_11_1?: chr_bank_1 <= prg_din; // Select 2 KB CHR bank at PPU $0800-$0FFF - 6'b01_00_1?: chr_bank_2 <= prg_din; // Select 1 KB CHR bank at PPU $1000-$13FF - 6'b01_01_1?: chr_bank_3 <= prg_din; // Select 1 KB CHR bank at PPU $1800-$1BFF - 6'b01_10_1?: chr_bank_4 <= prg_din; // Select 1 KB CHR bank at PPU $1800-$1BFF - 6'b01_11_1?: chr_bank_5 <= prg_din; // Select 1 KB CHR bank at PPU $1C00-$1FFF - - 6'b10_00_11: irq_latch <= prg_din ^ 8'hFF; // IRQ latch ($C000-$DFFC) - 6'b10_01_11: irq_reload <= 1; // IRQ reload ($C001-$DFFD) - 6'b10_10_11: begin irq_enable <= 0; irq_reg[0] <= 0; end// IRQ disable ($C002-$DFFE) - 6'b10_11_11: irq_enable <= 1; // IRQ enable ($C003-$DFFF) - - 6'b11_00_11: mirroring <= !prg_din[6]; // Mirroring - endcase - if (mapper154) begin - mirroring <= !prg_din[6]; - end - 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. - if (chr_ain[12] && a12_ctr == 0) begin - counter <= new_counter; - if ( (!mmc3_alt_behavior || counter != 0 || irq_reload) && new_counter == 0 && irq_enable && irq_support) begin -// $write("MMC3 SCANLINE IRQ!\n"); - irq_reg[0] <= 1; - end - irq_reload <= 0; - end - a12_ctr <= chr_ain[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 && 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 = (ram_enable[0] && prg_ain[12:11] == 2'b00) - || (ram_enable[1] && prg_ain[12:11] == 2'b01) - || (ram_enable[2] && prg_ain[12:11] == 2'b10) - || (ram_enable[3] && prg_ain[12:11] == 2'b11); - wire ram_protect_a = (ram_protect[0] && prg_ain[12:11] == 2'b00) - || (ram_protect[1] && prg_ain[12:11] == 2'b01) - || (ram_protect[2] && prg_ain[12:11] == 2'b10) - || (ram_protect[3] && prg_ain[12:11] == 2'b11); - - 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 : 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 - -// MMC4 mapper chip. PRG ROM: 256kB. Bank Size: 16kB. CHR ROM: 128kB -module MMC4(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input chr_read, input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - -// 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 (reset) - 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 & 14'h3ff8) == 14'h0fd8 ? 1'd0 : (chr_ain & 14'h3ff8) == 14'h0fe8 ? 1'd1 : latch_0; - latch_1 <= (chr_ain & 14'h3ff8) == 14'h1fd8 ? 1'd0 : (chr_ain & 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 - -module MMC5(input clk, input ce, input reset, - input [31:0] flags, - input [19:0] ppuflags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, output reg [7:0] prg_dout, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output reg [21:0] chr_aout, - output reg [7:0] chr_dout, output has_chr_dout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output irq, - output [15:0] audio); - 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; - - // unpack ppu flags - wire ppu_in_frame = ppuflags[0]; - wire ppu_sprite16 = ppuflags[1]; - 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. - if (prg_ain[9:0] >= 10'h120 && prg_ain[9:0] < 10'h130) - chr_last <= prg_ain[3]; - 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 (prg_write && prg_ain[15:10] == 6'b010111) begin // $5C00-$5FFF - if (extended_ram_mode != 3) - expansion_ram[prg_ain[9:0]] <= (extended_ram_mode[1] || ppu_in_frame) ? prg_din : 8'd0; - end - end - if (reset) begin - prg_bank_3 <= 7'h7F; - prg_mode <= 3; - end - end - - // Read from MMC5 - always @* begin - prg_dout = 8'hFF; // By default open bus. - 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 - end - - // Determine IRQ handling - reg last_scanline, irq_trig; - always @(posedge clk) if (ce) begin - if (prg_read && prg_ain == 16'h5204) - irq_pending <= 0; - irq_trig <= (irq_scanline != 0 && irq_scanline < 240 && ppu_scanline == {1'b0, irq_scanline}); - last_scanline <= ppu_scanline[0]; - 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 (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 (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); - - // 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_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 - //$write("Inserting filltile!\n"); - 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 - - // 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)) begin - last_read_exattr <= last_read_ram; - end - 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 - //Done below - //prgsel[7] = !prgsel[7]; // 0 means RAM, doh. - - 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], prgsel, 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 ? 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 (insplit) begin - //$write("In vertical split!\n"); - chr_aout = {2'b10, vsplit_bank, chr_ain[11:3], vscroll[2:0]}; - end else if (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]; - - wire [7:0] apu_dout; - wire apu_cs = (prg_ain[15:5]==11'b0101_0000_000) && (prg_ain[3]==0); - wire DmaReq; // 1 when DMC wants DMA - wire [15:0] DmaAddr; // Address DMC wants to read - wire odd_or_even; - wire apu_irq; // IRQ asserted - APU mmc5apu(1, clk, ce, reset, - prg_ain[4:0], prg_din, apu_dout, - prg_write && apu_cs, prg_read && apu_cs, - 5'b10011, - audio, - DmaReq, - 1, - DmaAddr, - 0, - odd_or_even, - apu_irq); - -endmodule - -// iNES mapper 64 and 158 - Tengen's version of MMC3 -module Rambo1(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output reg irq); - 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 want_irq; - 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; - reg [1:0] cycle_counter; - - // Mapper has vram_a10 wired to CHR A17 - wire mapper64 = (flags[7:0] == 64); - - // This code detects rising edges on a12. - reg old_a12_edge; - reg [1:0] a12_ctr; - wire a12_edge = (chr_ain[12] && a12_ctr == 0) || old_a12_edge; - always @(posedge clk) begin - old_a12_edge <= a12_edge && !ce; - a12_ctr <= chr_ain[12] ? 2'b11 : (a12_ctr != 0 && ce) ? a12_ctr - 2'b01 : a12_ctr; - end - - always @(posedge clk) if (reset) 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; - want_irq <= 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; - cycle_counter <= 0; - irq <= 0; - end else if (ce) begin - cycle_counter <= cycle_counter + 1'd1; - - 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, irq_cycle_mode} <= {1'b1, prg_din[0]}; // IRQ reload ($C001-$DFFF, odd) - cycle_counter <= 0; - end - 3'b11_0: {irq_enable, irq} <= 2'b0; // IRQ disable ($E000-$FFFE, even) - 3'b11_1: irq_enable <= 1; // IRQ enable ($E001-$FFFF, odd) - endcase - end - - if (irq_cycle_mode ? (cycle_counter == 3) : a12_edge) begin - if (irq_reload || counter == 0) begin - counter <= irq_latch; - want_irq <= irq_reload; - end else begin - counter <= counter - 1'd1; - want_irq <= 1; - end - if (counter == 0 && want_irq && !irq_reload && irq_enable) - irq <= 1; - irq_reload <= 0; - 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 = mapper64 ? chrsel[7] : // Mapper 64 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 - - -// #13 - CPROM - Used by Videomation -module Mapper13(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [1:0] chr_bank; - always @(posedge clk) if (reset) begin - chr_bank <= 0; - end else if (ce) begin - if (prg_ain[15] && prg_write) - chr_bank <= prg_din[1:0]; - end - 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 = {8'b01_0000_00, chr_ain[12] ? chr_bank : 2'b00, chr_ain[11:0]}; - assign vram_ce = chr_ain[13]; - assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11]; -endmodule - -// #15 - 100-in-1 Contra Function 16 -module Mapper15(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. -// 15 bit 8 7 bit 0 Address bus -// ---- ---- ---- ---- -// 1xxx xxxx xxxx xxSS -// | || -// | ++- Select PRG ROM bank mode -// | 0: 32K; 1: 128K (UNROM style); 2: 8K; 3: 16K -// +------------------- Always 1 -// 7 bit 0 Data bus -// ---- ---- -// bMBB BBBB -// |||| |||| -// ||++-++++- Select 16 KB PRG ROM bank -// |+-------- Select nametable mirroring mode (0=vertical; 1=horizontal) -// +--------- Select 8 KB half of 16 KB PRG ROM bank -// (should be 0 except in bank mode 0) - reg [1:0] prg_rom_bank_mode; - reg prg_rom_bank_lowbit; - reg mirroring; - reg [5:0] prg_rom_bank; - always @(posedge clk) if (reset) begin - prg_rom_bank_mode <= 0; - prg_rom_bank_lowbit <= 0; - mirroring <= 0; - prg_rom_bank <= 0; - end else if (ce) begin - if (prg_ain[15] && prg_write) - {prg_rom_bank_mode, prg_rom_bank_lowbit, mirroring, prg_rom_bank} <= {prg_ain[1:0], prg_din[7:0]}; - end - reg [6:0] prg_bank; - always begin - casez({prg_rom_bank_mode, prg_ain[14]}) - // Bank mode 0 ( 32K ) / CPU $8000-$BFFF: Bank B / CPU $C000-$FFFF: Bank (B OR 1) - 3'b00_0: prg_bank = {prg_rom_bank, prg_ain[13]}; - 3'b00_1: prg_bank = {prg_rom_bank | 6'b1, prg_ain[13]}; - // Bank mode 1 ( 128K ) / CPU $8000-$BFFF: Switchable 16 KB bank B / CPU $C000-$FFFF: Fixed to last bank in the cart - 3'b01_0: prg_bank = {prg_rom_bank, prg_ain[13]}; - 3'b01_1: prg_bank = {6'b111111, prg_ain[13]}; - // Bank mode 2 ( 8K ) / CPU $8000-$9FFF: Sub-bank b of 16 KB PRG ROM bank B / CPU $A000-$FFFF: Mirrors of $8000-$9FFF - 3'b10_?: prg_bank = {prg_rom_bank, prg_rom_bank_lowbit}; - // Bank mode 3 ( 16K ) / CPU $8000-$BFFF: 16 KB bank B / CPU $C000-$FFFF: Mirror of $8000-$BFFF - 3'b11_?: prg_bank = {prg_rom_bank, prg_ain[13]}; - endcase - end - assign prg_aout = {2'b00, prg_bank, prg_ain[12:0]}; - assign prg_allow = prg_ain[15] && !prg_write; - assign chr_allow = flags[15]; // CHR RAM? - assign chr_aout = {9'b10_0000_000, chr_ain[12:0]}; - assign vram_ce = chr_ain[13]; - assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10]; -endmodule - -// Mapper 16, Bandai -module Mapper16(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, output [7:0] prg_dout, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output reg vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output reg irq); - - reg [3:0] prg_bank; - reg [7: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; - reg [3:0] prg_sel; - reg [1:0] mirroring; - reg irq_enable; - reg [15:0] irq_counter; - - always @(posedge clk) if (reset) begin - prg_bank <= 4'hF; - 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; - mirroring <= 0; - irq_counter <= 0; - end else if (ce) begin - if (prg_write) - if(prg_ain >= 'h6000) // Cover all from $6000 to $FFFF to maximize compatibility - case(prg_ain & 'hf) // Registers are mapped every 16 bytes - 'h0: chr_bank_0 <= prg_din[7:0]; - 'h1: chr_bank_1 <= prg_din[7:0]; - 'h2: chr_bank_2 <= prg_din[7:0]; - 'h3: chr_bank_3 <= prg_din[7:0]; - 'h4: chr_bank_4 <= prg_din[7:0]; - 'h5: chr_bank_5 <= prg_din[7:0]; - 'h6: chr_bank_6 <= prg_din[7:0]; - 'h7: chr_bank_7 <= prg_din[7:0]; - 'h8: prg_bank <= prg_din[3:0]; - 'h9: mirroring <= prg_din[1:0]; - 'ha: irq_enable <= prg_din[0]; - 'hb: irq_counter[7:0] <= prg_din[7:0]; - 'hc: irq_counter[15:8] <= prg_din[7:0]; -// 'hd: RAM enable or EEPROM control - endcase - - if (irq_enable) - irq_counter <= irq_counter - 16'd1; - else begin - irq <= 1'b0; // IRQ ACK - end - - if (irq_counter == 16'h0000) - irq <= 1'b1; // IRQ - - end - - always begin - // mirroring - 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]}; // single screen lower - endcase - end - - reg [3:0] prgsel; - always begin - case(prg_ain[15:14]) - 2'b10: prgsel = prg_bank; // $8000 is swapable - 2'b11: prgsel = 4'hF; // $C000 is hardwired to last bank - default: prgsel = 0; - endcase - end - - reg [7:0] chrsel; - always begin - casez(chr_ain[12:10]) - 0: chrsel = chr_bank_0; - 1: chrsel = chr_bank_1; - 2: chrsel = chr_bank_2; - 3: chrsel = chr_bank_3; - 4: chrsel = chr_bank_4; - 5: chrsel = chr_bank_5; - 6: chrsel = chr_bank_6; - 7: chrsel = chr_bank_7; - endcase - end - assign chr_aout = {4'b10_00, chrsel, chr_ain[9:0]}; // 1kB banks - wire [21:0] prg_aout_tmp = {4'b00_00, prgsel, 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_dout = prg_is_ram ? 8'h00 : 8'hFF; // EEPROM stub - - assign prg_allow = (prg_ain[15] && !prg_write) || prg_is_ram; - assign chr_allow = flags[15]; - assign vram_ce = chr_ain[13]; -endmodule - -// Mapper 18, Jaleco SS88006 -module Mapper18(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, output [7:0] prg_dout, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output reg vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output reg irq); - - reg [7:0] prg_bank_0, prg_bank_1, prg_bank_2; - reg [7: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; - reg [3:0] prg_sel; - reg [1:0] mirroring; - reg irq_ack; - reg [3:0] irq_enable; - reg [15:0] irq_reload; - reg [15:0] irq_counter; - reg [1:0] ram_enable; - - always @(posedge clk) if (reset) begin - prg_bank_0 <= 8'hFF; - prg_bank_1 <= 8'hFF; - prg_bank_2 <= 8'hFF; - 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; - mirroring <= 0; - irq_reload <= 0; - irq_counter <= 0; - irq_enable <= 4'h0; - end else if (ce) begin - irq_ack <= 1'b0; - if (prg_write) - if(prg_ain[15]) // Cover all from $8000 to $FFFF to maximize compatibility - case({prg_ain[14:12],prg_ain[1:0]}) // - 5'b000_00: prg_bank_0[3:0] <= prg_din[3:0]; - 5'b000_01: prg_bank_0[7:4] <= prg_din[3:0]; - 5'b000_10: prg_bank_1[3:0] <= prg_din[3:0]; - 5'b000_11: prg_bank_1[7:4] <= prg_din[3:0]; - 5'b001_00: prg_bank_2[3:0] <= prg_din[3:0]; - 5'b001_01: prg_bank_2[7:4] <= prg_din[3:0]; - 5'b010_00: chr_bank_0[3:0] <= prg_din[3:0]; - 5'b010_01: chr_bank_0[7:4] <= prg_din[3:0]; - 5'b010_10: chr_bank_1[3:0] <= prg_din[3:0]; - 5'b010_11: chr_bank_1[7:4] <= prg_din[3:0]; - 5'b011_00: chr_bank_2[3:0] <= prg_din[3:0]; - 5'b011_01: chr_bank_2[7:4] <= prg_din[3:0]; - 5'b011_10: chr_bank_3[3:0] <= prg_din[3:0]; - 5'b011_11: chr_bank_3[7:4] <= prg_din[3:0]; - 5'b100_00: chr_bank_4[3:0] <= prg_din[3:0]; - 5'b100_01: chr_bank_4[7:4] <= prg_din[3:0]; - 5'b100_10: chr_bank_5[3:0] <= prg_din[3:0]; - 5'b100_11: chr_bank_5[7:4] <= prg_din[3:0]; - 5'b101_00: chr_bank_6[3:0] <= prg_din[3:0]; - 5'b101_01: chr_bank_6[7:4] <= prg_din[3:0]; - 5'b101_10: chr_bank_7[3:0] <= prg_din[3:0]; - 5'b101_11: chr_bank_7[7:4] <= prg_din[3:0]; - 5'b110_00: irq_reload[3:0] <= prg_din[3:0]; - 5'b110_01: irq_reload[7:4] <= prg_din[3:0]; - 5'b110_10: irq_reload[11:8] <= prg_din[3:0]; - 5'b110_11: irq_reload[15:12] <= prg_din[3:0]; - 5'b111_00: {irq_ack, irq_counter} <= {1'b1, irq_reload}; - 5'b111_01: {irq_ack, irq_enable} <= {1'b1, prg_din[3:0]}; - 5'b111_10: mirroring <= prg_din[1:0]; - 5'b111_11: ram_enable <= prg_din[1:0]; - endcase - - if (irq_enable[0]) begin - irq_counter[3:0] <= irq_counter[3:0] - 4'd1; - if (irq_counter[3:0] == 4'h0) begin - if (irq_enable[3]) begin - irq <= 1'b1; // IRQ - end else begin - irq_counter[7:4] <= irq_counter[7:4] - 4'd1; - if (irq_counter[7:4] == 4'h0) begin - if (irq_enable[2]) begin - irq <= 1'b1; // IRQ - end else begin - irq_counter[11:8] <= irq_counter[11:8] - 4'd1; - if (irq_counter[11:8] == 4'h0) begin - if (irq_enable[1]) begin - irq <= 1'b1; // IRQ - end else begin - irq_counter[15:12] <= irq_counter[15:12] - 4'd1; - if (irq_counter[15:12] == 4'h0) begin - irq <= 1'b1; // IRQ - end - end - end - end - end - end - end - end - if (irq_ack) - irq <= 1'b0; // IRQ ACK - - end - - always begin - // mirroring - casez(mirroring[1:0]) - 2'b00: vram_a10 = {chr_ain[11]}; // horizontal - 2'b01: vram_a10 = {chr_ain[10]}; // vertical - 2'b1?: vram_a10 = {mirroring[0]}; // single screen lower - endcase - end - - reg [7:0] prgsel; - always begin - case(prg_ain[14:13]) - 2'b00: prgsel = prg_bank_0; // $8000 is swapable - 2'b01: prgsel = prg_bank_1; // $A000 is swapable - 2'b10: prgsel = prg_bank_2; // $C000 is swapable - 2'b11: prgsel = 8'hFF; // $E000 is hardwired to last bank - endcase - end - - reg [7:0] chrsel; - always begin - casez(chr_ain[12:10]) - 0: chrsel = chr_bank_0; - 1: chrsel = chr_bank_1; - 2: chrsel = chr_bank_2; - 3: chrsel = chr_bank_3; - 4: chrsel = chr_bank_4; - 5: chrsel = chr_bank_5; - 6: chrsel = chr_bank_6; - 7: chrsel = chr_bank_7; - endcase - end - assign chr_aout = {4'b10_00, chrsel, chr_ain[9:0]}; // 1kB banks - wire [21:0] prg_aout_tmp = {2'b00, prgsel[6:0], prg_ain[12:0]}; // 8kB 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_dout = prg_is_ram ? 8'h00 : 8'hFF; // EEPROM stub - - assign prg_allow = (prg_ain[15] && !prg_write) || (prg_is_ram && ram_enable[0] && (ram_enable[1] || !prg_write)); - assign chr_allow = flags[15]; - assign vram_ce = chr_ain[13]; -endmodule - -// Tepples/Multi-discrete mapper -// This mapper can emulate other mappers, such as mapper #0, mapper #2 -module Mapper28(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output reg vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [6:0] a53prg; // output PRG ROM (A14-A20 on ROM) - reg [1:0] a53chr; // output CHR RAM (A13-A14 on RAM) - - reg [3:0] inner; // "inner" bank at 01h - reg [5:0] mode; // mode register at 80h - reg [5:0] outer; // "outer" bank at 81h - reg [1:0] selreg; // selector register - - // Allow writes to 0x5000 only when launching through the proper mapper ID. - wire [7:0] mapper = flags[7:0]; - wire allow_select = (mapper == 8'd28); - - always @(posedge clk) if (reset) begin - mode[5:2] <= 0; // NROM mode, 32K mode - outer[5:0] <= 6'h3f; // last bank - inner <= 0; - selreg <= 1; - - // Set value for mirroring - if (mapper == 2 || mapper == 0 || mapper == 3) - mode[1:0] <= flags[14] ? 2'b10 : 2'b11; - - // UNROM #2 - Current bank in $8000-$BFFF and fixed top half of outer bank in $C000-$FFFF - if (mapper == 2) begin - mode[5:2] <= 4'b1111; // 256K banks, UNROM mode - end - - // CNROM #3 - Fixed PRG bank, switchable CHR bank. - if (mapper == 3) - selreg <= 0; - - // AxROM #7 - Switch 32kb rom bank + switchable nametables - if (mapper == 7) begin - mode[1:0] <= 2'b00; // Switchable VRAM page. - mode[5:2] <= 4'b1100; // 256K banks, (B)NROM mode - outer[5:0] <= 6'h00; - end - end else if (ce) begin - if ((prg_ain[15:12] == 4'h5) & prg_write && allow_select) - selreg <= {prg_din[7], prg_din[0]}; // select register - if (prg_ain[15] & prg_write) begin - case (selreg) - 2'h0: {mode[0], a53chr} <= {(mode[1] ? mode[0] : prg_din[4]), prg_din[1:0]}; // CHR RAM bank - 2'h1: {mode[0], inner} <= {(mode[1] ? mode[0] : prg_din[4]), prg_din[3:0]}; // "inner" bank - 2'h2: {mode} <= {prg_din[5:0]}; // mode register - 2'h3: {outer} <= {prg_din[5:0]}; // "outer" bank - endcase - end - end - - always begin - // mirroring mode - casez(mode[1:0]) - 2'b0? : vram_a10 = {mode[0]}; // 1 screen lower - 2'b10 : vram_a10 = {chr_ain[10]}; // vertical - 2'b11 : vram_a10 = {chr_ain[11]}; // horizontal - endcase - - // PRG ROM bank size select - casez({mode[5:2], prg_ain[14]}) - 5'b00_0?_? : a53prg = {outer[5:0], prg_ain[14]}; // 32K banks, (B)NROM mode - 5'b01_0?_? : a53prg = {outer[5:1], inner[0], prg_ain[14]}; // 64K banks, (B)NROM mode - 5'b10_0?_? : a53prg = {outer[5:2], inner[1:0], prg_ain[14]}; // 128K banks, (B)NROM mode - 5'b11_0?_? : a53prg = {outer[5:3], inner[2:0], prg_ain[14]}; // 256K banks, (B)NROM mode - - 5'b00_10_1, - 5'b00_11_0 : a53prg = {outer[5:0], inner[0]}; // 32K banks, UNROM mode - 5'b01_10_1, - 5'b01_11_0 : a53prg = {outer[5:1], inner[1:0]}; // 64K banks, UNROM mode - 5'b10_10_1, - 5'b10_11_0 : a53prg = {outer[5:2], inner[2:0]}; // 128K banks, UNROM mode - 5'b11_10_1, - 5'b11_11_0 : a53prg = {outer[5:3], inner[3:0]}; // 256K banks, UNROM mode - - default : a53prg = {outer[5:0], prg_ain[14]}; // 16K fixed bank - endcase - end - - assign vram_ce = chr_ain[13]; - assign prg_aout = {1'b0, (a53prg & 7'b0011111), prg_ain[13:0]}; - assign prg_allow = prg_ain[15] && !prg_write; - assign chr_allow = flags[15]; - assign chr_aout = {7'b10_0000_0, a53chr, chr_ain[12:0]}; -endmodule - -// 30-UNROM512 -module Mapper30(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output reg vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [4:0] prgbank; - reg [1:0] chrbank; - reg [2:0] mirror; - wire four_screen = (mirror[2:1] == 2'b11); - - always @(posedge clk) if (reset) begin - // Set value for mirroring - mirror[2:1] <= {flags[16], flags[14]}; - end else if (ce) begin - if (prg_ain[15] & prg_write) begin - {mirror[0], chrbank, prgbank} <= prg_din[7:0]; - end - end - - always begin - // mirroring mode - casez({mirror[2:1],chr_ain[13]}) - 3'b001 : vram_a10 = {chr_ain[11]}; // horizontal - 3'b011 : vram_a10 = {chr_ain[10]}; // vertical - 3'b101 : vram_a10 = {mirror[0]}; // 1 screen - 3'b111 : vram_a10 = {chr_ain[10]}; // 4 screen - default : vram_a10 = {chr_ain[10]}; // pattern table - endcase - end - - assign prg_aout = {3'b000, prg_ain[14] ? 5'b11111 : prgbank, prg_ain[13:0]}; - assign prg_allow = prg_ain[15] && !prg_write; - assign chr_allow = flags[15]; - assign chr_aout = {flags[15] ? 7'b11_1111_1 : 7'b10_0000_0, (four_screen && (chr_ain[13])) ? 2'b11 : chrbank, chr_ain[12:11], vram_a10, chr_ain[9:0]}; - assign vram_ce = chr_ain[13] && !four_screen; -endmodule - -// 32 - IREM -module Mapper32(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output reg vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [4:0] prgreg0; - reg [4:0] prgreg1; - reg [7:0] chrreg0; - reg [7:0] chrreg1; - reg [7:0] chrreg2; - reg [7:0] chrreg3; - reg [7:0] chrreg4; - reg [7:0] chrreg5; - reg [7:0] chrreg6; - reg [7:0] chrreg7; - reg prgmode; - reg mirror; - wire submapper1 = (flags[21] == 1); // default (0) default submapper; (1) Major League - reg [4:0] prgsel; - reg [7:0] chrsel; - - always @(posedge clk) if (reset) begin - prgmode <= 1'b0; - end else if (ce) begin - if ((prg_ain[15:14] == 2'b10) & prg_write) begin - casez ({prg_ain[13:12], submapper1, prg_ain[2:0]}) - 6'b00_?_???: prgreg0 <= prg_din[4:0]; - 6'b01_0_???: {prgmode, mirror} <= prg_din[1:0]; - 6'b10_0_???: prgreg1 <= prg_din[4:0]; - 6'b11_?_000: chrreg0 <= prg_din; - 6'b11_?_001: chrreg1 <= prg_din; - 6'b11_?_010: chrreg2 <= prg_din; - 6'b11_?_011: chrreg3 <= prg_din; - 6'b11_?_100: chrreg4 <= prg_din; - 6'b11_?_101: chrreg5 <= prg_din; - 6'b11_?_110: chrreg6 <= prg_din; - 6'b11_?_111: chrreg7 <= prg_din; - endcase - end - end - - always begin - // mirroring mode - casez({submapper1, mirror}) - 2'b00 : vram_a10 = {chr_ain[10]}; // vertical - 2'b01 : vram_a10 = {chr_ain[11]}; // horizontal - 2'b1? : vram_a10 = {1'b1}; // 1 screen lower - endcase - - // PRG ROM bank size select - casez({prg_ain[14:13], prgmode}) - 3'b000 : prgsel = prgreg0; - 3'b001 : prgsel = {5'b11110}; - 3'b01? : prgsel = prgreg1; - 3'b100 : prgsel = {5'b11110}; - 3'b101 : prgsel = prgreg0; - 3'b11? : prgsel = {5'b11111}; - endcase - - // CHR ROM bank size select - casez({chr_ain[12:10]}) - 3'b000 : chrsel = chrreg0; - 3'b001 : chrsel = chrreg1; - 3'b010 : chrsel = chrreg2; - 3'b011 : chrsel = chrreg3; - 3'b100 : chrsel = chrreg4; - 3'b101 : chrsel = chrreg5; - 3'b110 : chrsel = chrreg6; - 3'b111 : chrsel = chrreg7; - endcase - end - - assign vram_ce = chr_ain[13]; - assign prg_aout = {4'b00_00, prgsel, prg_ain[12:0]}; - assign prg_allow = prg_ain[15] && !prg_write; - assign chr_allow = flags[15]; - assign chr_aout = {4'b10_00, chrsel, chr_ain[9:0]}; -endmodule - -// Mapper 42, used for hacked FDS games converted to cartridge form -module Mapper42(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output reg irq); - - reg [3:0] prg_bank; - reg [3:0] chr_bank; - reg [3:0] prg_sel; - reg mirroring; - reg irq_enable; - reg [14:0] irq_counter; - - always @(posedge clk) if (reset) begin - prg_bank <= 0; - chr_bank <= 0; - mirroring <= flags[14]; - irq_counter <= 0; - end else if (ce) begin - if (prg_write) - case(prg_ain & 16'he003) - 16'h8000: chr_bank <= prg_din[3:0]; - 16'he000: prg_bank <= prg_din[3:0]; - 16'he001: mirroring <= prg_din[3]; - 16'he002: irq_enable <= prg_din[1]; - endcase - - if (irq_enable) - irq_counter <= irq_counter + 15'd1; - else begin - irq <= 1'b0; // ACK - irq_counter <= 0; - end - - if (irq_counter == 15'h6000) - irq <= 1'b1; - - end - - always @* begin -/* PRG bank selection - 6000-7FFF: Selectable - 8000-9FFF: bank #0Ch - A000-BFFF: bank #0Dh - C000-DFFF: bank #0Eh - E000-FFFF: bank #0Fh -*/ - case(prg_ain[15:13]) - 3'b011: prg_sel = prg_bank; // $6000-$7FFF - 3'b100: prg_sel = 4'hC; - 3'b101: prg_sel = 4'hD; - 3'b110: prg_sel = 4'hE; - 3'b111: prg_sel = 4'hF; - default: prg_sel = 0; - endcase - end - assign prg_aout = {5'b0, prg_sel, prg_ain[12:0]}; // 8kB banks - assign chr_aout = {5'b10_000, chr_bank, chr_ain[12:0]}; // 8kB banks - - assign prg_allow = (prg_ain >= 16'h6000) && !prg_write; - assign chr_allow = flags[15]; - assign vram_ce = chr_ain[13]; - assign vram_a10 = mirroring ? chr_ain[10] : chr_ain[11]; -endmodule - -// 11 - Color Dreams -// 38 - Bit Corps -// 87 - Jaleco JF-11,JF-14 -// 101 - Jaleco JF-11,JF-14 -// 140 - Jaleco JF-11,JF-14 -// 66 - GxROM -module Mapper66(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [1:0] prg_bank; - reg [3:0] chr_bank; - wire [7:0] mapper = flags[7:0]; - wire GXROM = (mapper == 66); - wire BitCorps = (mapper == 38); - wire Mapper140 = (mapper == 140); - wire Mapper101 = (mapper == 101); - wire Mapper87 = (mapper == 87); - always @(posedge clk) if (reset) begin - prg_bank <= 0; - chr_bank <= 0; - end else if (ce) begin - if (prg_ain[15] & prg_write) begin - if (GXROM) - {prg_bank, chr_bank} <= {prg_din[5:4], 2'b0, prg_din[1:0]}; - else // Color Dreams - {chr_bank, prg_bank} <= {prg_din[7:4], prg_din[1:0]}; - end - else if ((prg_ain[15:12]==4'h7) & prg_write & BitCorps) begin - {chr_bank, prg_bank} <= {prg_din[3:0]}; - end - else if ((prg_ain[15:12]==4'h6) & prg_write) begin - if (Mapper140) begin - {prg_bank, chr_bank} <= {prg_din[5:4], prg_din[3:0]}; - end else if (Mapper101) begin - {chr_bank} <= {prg_din[3:0]}; // All 8 bits instead? - end else if (Mapper87) begin - {chr_bank} <= {2'b00, prg_din[0], prg_din[1]}; - end - end - end - assign prg_aout = {5'b00_000, prg_bank, prg_ain[14:0]}; - assign prg_allow = prg_ain[15] && !prg_write; - assign chr_allow = flags[15]; - assign chr_aout = {5'b10_000, chr_bank, chr_ain[12:0]}; - assign vram_ce = chr_ain[13]; - assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11]; -endmodule - -// 34 - BxROM or NINA-001 -module Mapper34(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [1:0] prg_bank; - reg [3:0] chr_bank_0, chr_bank_1; - - wire NINA = (flags[13:11] != 0); // NINA is used when there is more than 8kb of CHR - always @(posedge clk) if (reset) begin - prg_bank <= 0; - chr_bank_0 <= 0; - chr_bank_1 <= 1; // To be compatible with BxROM - end else if (ce && prg_write) begin - if (!NINA) begin // BxROM - if (prg_ain[15]) - prg_bank <= prg_din[1:0]; - end else begin // NINA - if (prg_ain == 16'h7ffd) - prg_bank <= prg_din[1:0]; - else if (prg_ain == 16'h7ffe) - chr_bank_0 <= prg_din[3:0]; - else if (prg_ain == 16'h7fff) - chr_bank_1 <= prg_din[3:0]; - end - end - - wire [21:0] prg_aout_tmp = {5'b00_000, prg_bank, prg_ain[14:0]}; - assign chr_allow = flags[15]; - assign chr_aout = {6'b10_0000, chr_ain[12] == 0 ? chr_bank_0 : chr_bank_1, chr_ain[11:0]}; - assign vram_ce = chr_ain[13]; - assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11]; - - wire prg_is_ram = (prg_ain >= 'h6000 && prg_ain < 'h8000) && NINA; - 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 - -// 41 - Caltron 6-in-1 -module Mapper41(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [2:0] prg_bank; - reg [1:0] chr_outer_bank, chr_inner_bank; - reg mirroring; - - always @(posedge clk) if (reset) begin - prg_bank <= 0; - chr_outer_bank <= 0; - chr_inner_bank <= 0; - mirroring <= 0; - end else if (ce && prg_write) begin - if (prg_ain[15:11] == 5'b01100) begin - {mirroring, chr_outer_bank, prg_bank} <= prg_ain[5:0]; - end else if (prg_ain[15] && prg_bank[2]) begin - // The Inner CHR Bank Select only can be written while the PRG ROM bank is 4, 5, 6, or 7 - chr_inner_bank <= prg_din[1:0]; - end - end - - assign prg_aout = {4'b00_00, prg_bank, prg_ain[14:0]}; - assign chr_allow = flags[15]; - assign chr_aout = {5'b10_000, chr_outer_bank, chr_inner_bank, chr_ain[12:0]}; - assign vram_ce = chr_ain[13]; - assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10]; - assign prg_allow = prg_ain[15] && !prg_write; -endmodule - -// #68 - Sunsoft-4 - Game After Burner, and some japanese games. MAX: 128kB PRG, 256kB CHR -module Mapper68(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [6:0] chr_bank_0, chr_bank_1, chr_bank_2, chr_bank_3; - reg [6:0] nametable_0, nametable_1; - reg [2:0] prg_bank; - reg use_chr_rom; - reg mirroring; - always @(posedge clk) if (reset) 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; - use_chr_rom <= 0; - mirroring <= 0; - end else if (ce) begin - if (prg_ain[15] && prg_write) begin -// $write("REG[%d] <= %X\n", prg_ain[14:12], prg_din); - 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[0]}; // $E000-$EFFF: Nametable control - 7: prg_bank <= prg_din[2:0]; - endcase - end - end - wire [2:0] prgout = (prg_ain[14] ? 3'b111 : prg_bank); - assign prg_aout = {5'b00_000, prgout, prg_ain[13:0]}; - assign prg_allow = prg_ain[15] && !prg_write; - - 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 - assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10]; - 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 - -// 69 - Sunsoft FME-7 -module Mapper69(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output reg vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output reg irq, - output [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 (reset) 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] == 0 && ram_select); - assign prg_aout = {1'b0, ram_cs, 2'b00, prgout[4:0], prg_ain[12:0]}; - assign prg_allow = 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]; - -//Taken from Loopy's Power Pak mapper source -//audio - wire [6:0] fme7_out; - wire [15:0] fme7_sample; - FME7_sound snd0(clk, ce, reset, prg_write, prg_ain, prg_din, fme7_out); - //FME7_sound snd0(m2, reset, nesprg_we, prgain, nesprgdin, fme7_out); - //pdm #(7) pdm_mod(clk20, fme7_out, exp6); - - //Need a better lookup table for this - //This is just the NES APU lookup table, which is designed for 2 4-bit square waves, not 3 - ApuLookupTable lookup(clk, - {4'b0, fme7_out[5:1]}, //fme7_out range: 0-2D - {8'b0}, //No triange, noise or DMC - fme7_sample); - assign audio = {fme7_sample[14:0], 1'b0}; // Double. Volume will be slightly higher, rather than slightly lower than expected - -endmodule - -// #71,#232 - Camerica -module Mapper71(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [3:0] prg_bank; - reg ciram_select; - wire mapper232 = (flags[7:0] == 232); - always @(posedge clk) if (reset) begin - prg_bank <= 0; - ciram_select <= 0; - end else if (ce) begin - if (prg_ain[15] && prg_write) begin - //$write("%X <= %X (bank = %x)\n", prg_ain, prg_din, prg_bank); - if (!prg_ain[14] && mapper232) // $8000-$BFFF Outer bank select (only on iNES 232) - prg_bank[3:2] <= prg_din[4:3]; - if (prg_ain[14:13] == 0) // $8000-$9FFF Fire Hawk Mirroring - ciram_select <= prg_din[4]; - if (prg_ain[14]) // $C000-$FFFF Bank select - prg_bank <= {mapper232 ? prg_bank[3:2] : prg_din[3:2], prg_din[1:0]}; - end - end - reg [3:0] prgout; - always begin - casez({prg_ain[14], mapper232}) - 2'b0?: prgout = prg_bank; - 2'b10: prgout = 4'b1111; - 2'b11: prgout = {prg_bank[3:2], 2'b11}; - endcase - end - assign prg_aout = {4'b00_00, prgout, prg_ain[13: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]; - // XXX(ludde): Fire hawk uses flags[14] == 0 while no other game seems to do that. - // So when flags[14] == 0 we use ciram_select instead. - assign vram_a10 = flags[14] ? chr_ain[10] : ciram_select; -endmodule - -// #78-IREM-HOLYDIVER/JALECO-JF-16 -// #70,#152-Bandai -module Mapper78(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [3:0] prg_bank; - reg [3:0] chr_bank; - reg mirroring; // See vram_a10_t - wire mapper70 = (flags[7:0] == 70); - wire mapper152 = (flags[7:0] == 152); - wire onescreen = (flags[22:21] == 1) | mapper152; // default (0 or 3) Holy Diver submapper; (1) JALECO-JF-16 - always @(posedge clk) if (reset) begin - prg_bank <= 0; - chr_bank <= 0; - mirroring <= flags[14]; - end else if (ce) begin - if (prg_ain[15] == 1'b1 && prg_write) begin - if (mapper70) - {prg_bank, chr_bank} <= prg_din; - else if (mapper152) - {mirroring, prg_bank[2:0], chr_bank} <= prg_din; - else - {chr_bank, mirroring, prg_bank[2:0]} <= prg_din; - end - end - assign prg_aout = {4'b00_00, (prg_ain[14] ? 4'b1111 : prg_bank), prg_ain[13:0]}; - assign prg_allow = prg_ain[15] && !prg_write; - assign chr_allow = flags[15]; - assign chr_aout = {5'b10_000, chr_bank, chr_ain[12:0]}; - assign vram_ce = chr_ain[13]; - - // The a10 VRAM address line. (Used for mirroring) - reg vram_a10_t; - always begin - case({onescreen, mirroring}) - 2'b00: vram_a10_t = chr_ain[11]; // One screen, horizontal - 2'b01: vram_a10_t = chr_ain[10]; // One screen, vertical - 2'b10: vram_a10_t = 0; // One screen, lower bank - 2'b11: vram_a10_t = 1; // One screen, upper bank - endcase - end - assign vram_a10 = vram_a10_t; -endmodule - -// #79,#113 - NINA-03 / NINA-06 -module Mapper79(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [2:0] prg_bank; - reg [3:0] chr_bank; - reg mirroring; // 0: Horizontal, 1: Vertical - wire mapper113 = (flags[7:0] == 113); // NINA-06 - always @(posedge clk) if (reset) begin - prg_bank <= 0; - chr_bank <= 0; - mirroring <= 0; - end else if (ce) begin - if (prg_ain[15:13] == 3'b010 && prg_ain[8] && prg_write) - {mirroring, chr_bank[3], prg_bank, chr_bank[2:0]} <= prg_din; - end - assign prg_aout = {4'b00_00, prg_bank, prg_ain[14:0]}; - assign prg_allow = prg_ain[15] && !prg_write; - assign chr_allow = flags[15]; - assign chr_aout = {5'b10_000, chr_bank, chr_ain[12:0]}; - assign vram_ce = chr_ain[13]; - wire mirrconfig = mapper113 ? mirroring : flags[14]; // Mapper #13 has mapper controlled mirroring - assign vram_a10 = mirrconfig ? chr_ain[10] : chr_ain[11]; // 0: horiz, 1: vert -endmodule - - -// #89,#93,#184 - Sunsoft mappers -module Mapper89(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output reg vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [2:0] prgsel; - reg [3:0] chrsel0; - reg [3:0] chrsel1; - reg [2:0] prg_temp; - reg [4:0] chr_temp; - - reg mirror; - - wire [7:0] mapper = flags[7:0]; - wire mapper89 = (mapper == 8'd89); - wire mapper93 = (mapper == 8'd93); - wire mapper184 = (mapper == 8'd184); - - always @(posedge clk) if (reset) begin - prgsel <= 3'b110; - chrsel0 <= 4'b1111; - chrsel1 <= 4'b1111; - end else if (ce) begin - if (prg_ain[15] & prg_write & mapper89) begin - {chrsel0[3], prgsel, mirror, chrsel0[2:0]} <= prg_din; - end else if (prg_ain[15] & prg_write & mapper93) begin - prgsel <= prg_din[6:4]; - // chrrameanble <= prg_din[0]; - end else if ((prg_ain[15:13]==3'b011) & prg_write & mapper184) begin - {chrsel1[3:0], chrsel0[3:0]} <= {2'b01,prg_din[5:4],1'b0,prg_din[2:0]}; - end - end - - always begin - // mirroring mode - casez({mapper89,flags[14]}) - 2'b00 : vram_a10 = {chr_ain[11]}; // horizontal - 2'b01 : vram_a10 = {chr_ain[10]}; // vertical - 2'b1? : vram_a10 = {mirror}; // 1 screen - endcase - - // PRG ROM bank size select - casez({mapper184, prg_ain[14]}) - 2'b00 : prg_temp = {prgsel}; // 16K banks - 2'b01 : prg_temp = {3'b111}; // 16K banks last - 2'b1? : prg_temp = {2'b0,prg_ain[14]}; // 32K banks pass thru - endcase - - // CHR ROM bank size select - casez({mapper184, chr_ain[12]}) - 2'b0? : chr_temp = {chrsel0, chr_ain[12]};// 8K Bank - 2'b10 : chr_temp = {1'b0,chrsel0}; // 4K Bank - 2'b11 : chr_temp = {1'b0,chrsel1}; // 4K Bank - endcase - end - - assign vram_ce = chr_ain[13]; - assign prg_aout = {5'b0, prg_temp, prg_ain[13:0]}; - assign prg_allow = prg_ain[15] && !prg_write; - assign chr_allow = flags[15]; - assign chr_aout = {5'b10_000, chr_temp, chr_ain[11:0]}; -endmodule - -// #105 - NES-EVENT. Retrofits an MMC3 with lots of extra logic. -module NesEvent(input clk, input ce, input reset, - input [15:0] prg_ain, output reg [21:0] prg_aout, - input [13:0] chr_ain, output [21:0] chr_aout, - input [3:0] mmc1_chr, // Upper 4 CHR output control bits from MMC chip - input [21:0] mmc1_aout, // PRG output address from MMC chip - output irq); - // $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 (reset) 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 - //$write("NESEV Control Bits: %X => %X (%d)\n", oldbits, mmc1_chr, unlocked); - 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 - -// mapper 165 -module Mapper165(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input chr_read, input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output reg irq); - 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 (reset) 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[12] && a12_ctr == 0) begin - counter <= new_counter; - if ( (counter != 0 || irq_reload) && new_counter == 0 && irq_enable) begin -// $write("MMC3 SCANLINE IRQ!\n"); - irq <= 1; - end - irq_reload <= 0; - end - a12_ctr <= chr_ain[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 - -// iNES Mapper 228 represents the board used by Active Enterprises for Action 52 and Cheetahmen II. -module Mapper228(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg mirroring; - reg [1:0] prg_chip; - reg [4:0] prg_bank; - reg prg_bank_mode; - reg [5:0] chr_bank; - always @(posedge clk) if (reset) begin - {mirroring, prg_chip, prg_bank, prg_bank_mode} <= 0; - chr_bank <= 0; - end else if (ce) begin - if (prg_ain[15] & prg_write) begin - {mirroring, prg_chip, prg_bank, prg_bank_mode} <= prg_ain[13:5]; - chr_bank <= {prg_ain[3:0], prg_din[1:0]}; - end - end - assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10]; - wire prglow = prg_bank_mode ? prg_bank[0] : prg_ain[14]; - wire [1:0] addrsel = {prg_chip[1], prg_chip[1] ^ prg_chip[0]}; - assign prg_aout = {1'b0, addrsel, prg_bank[4:1], prglow, prg_ain[13:0]}; - assign prg_allow = prg_ain[15] && !prg_write; - assign chr_allow = flags[15]; - assign chr_aout = {3'b10_0, chr_bank, chr_ain[12:0]}; - assign vram_ce = chr_ain[13]; -endmodule - -module Mapper234(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - reg [2:0] block, inner_chr; - reg mode, mirroring, inner_prg; - always @(posedge clk) if (reset) begin - block <= 0; - {mode, mirroring} <= 0; - inner_chr <= 0; - inner_prg <= 0; - end else if (ce) begin - if (prg_read && prg_ain[15:7] == 9'b1111_1111_1) begin - // Outer bank control $FF80 - $FF9F - if (prg_ain[6:0] < 7'h20 && (block == 0)) begin - {mirroring, mode} <= prg_din[7:6]; - block <= prg_din[3:1]; - {inner_chr[2], inner_prg} <= {prg_din[0], prg_din[0]}; - end - // Inner bank control ($FFE8-$FFF7) - if (prg_ain[6:0] >= 7'h68 && prg_ain[6:0] < 7'h78) begin - {inner_chr[2], inner_prg} <= mode ? {prg_din[6], prg_din[0]} : {inner_chr[2], inner_prg}; - inner_chr[1:0] <= prg_din[5:4]; - end - end - end - assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10]; - assign prg_aout = {3'b00_0, block, inner_prg, prg_ain[14:0]}; - assign chr_aout = {3'b10_0, block, inner_chr, chr_ain[12:0]}; - assign prg_allow = prg_ain[15] && !prg_write; - assign chr_allow = flags[15]; - assign vram_ce = chr_ain[13]; -endmodule - -// VRC1 (75) -module VRC1(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output reg vram_a10, // Value for A10 address line - output vram_ce); // True if the address should be routed to the internal 2kB VRAM. - 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 (reset) 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 - -module VRC6(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, output [7:0] prg_dout, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output irq, - output [15:0] audio); - 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 -//module MAPVRC6( //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 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, output prg_allow, output [11:0] snd_level, input mapper26); - MAPVRC6 vrc6(m2[7], m2_n, clk, reset, 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, audio, 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, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output irq, - output [15:0] audio); - - 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, soff; - - always@(posedge clk) begin - if (reset) - soff <= 1'b0; - 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,soff,mirror}<={prg_din[7:6],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,reset,prg_write,irql,irqc,irqa,prg_din,irq,ce); - - reg [3:0] ce_count; - always@(posedge clk) begin - if (ce) - ce_count <= 0; - else - ce_count <= ce_count + 4'd1; - end - wire ack; - wire ce_ym2143 = ce | (ce_count==4'd5); - wire [13:0] ym2143audio; - wire wr_audio = prg_write && (prg_ain[15:6]==10'b1001_0000_00) && (prg_ain[4:0]==5'b1_0000); //0x9010 or 0x9030 - eseopll ym2143vrc7 (clk,reset, ce_ym2143,wr_audio,ce_ym2143,ack,wr_audio,{15'b0,prg_ain[5]},prg_din,ym2143audio); - //No clipping. Lower volume. - //wire [12:0] ym2143audiounsigned = ym2143audio[12:0] ^ 13'b1_0000_0000_0000; // 10 bits * 6 channels = Max 13 bits. - //assign audio[15:0] = soff ? 16'h8000 : {ym2143audiounsigned, 3'b0}; - //Clipping possible. Higher volume. - wire [11:0] ym2143audiounsigned = ym2143audio[13:12]==3'b10 ? 12'h000 : ym2143audio[13:11]==3'b01 ? 12'hFFF : ym2143audio[11:0] ^ 12'b1000_0000_0000; // Cheat one bit (some clipping) - assign audio[15:0] = soff ? 16'h8000 : {ym2143audiounsigned, 4'b0}; - -endmodule - -module N106(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, output [7:0] prg_dout, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output irq, - output [15:0] audio); - 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 -//module MAPN106( //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 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, output prg_allow, output [11:0] snd_level, input mapper26); - MAPN106 n106(m2[7], m2_n, clk, reset, 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, audio[15:5]); - - 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; - assign audio[4:0] = 4'b0; - -endmodule - -// - Famicom Disk System -module MapperFDS(input clk, input ce, input reset, - input [31:0] flags, - input [15:0] prg_ain, output [21:0] prg_aout, - input prg_read, prg_write, // Read / write signals - input [7:0] prg_din, output [7:0] prg_dout, - output prg_allow, // Enable access to memory for the specified operation. - input [13:0] chr_ain, output [21:0] chr_aout, - output chr_allow, // Allow write - output vram_a10, // Value for A10 address line - output vram_ce, // True if the address should be routed to the internal 2kB VRAM. - output irq, - output [15:0] audio, - input fds_swap); - 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 - -//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 [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, output prg_allow, output [11:0] snd_level); - MAPFDS fds(m2[7], m2_n, clk, reset, 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], prg_aout[18:0], irq, vram_ce, exp6, - 0, 7'b1111111, 6'b111111, flags[14], flags[16], flags[15], - ce, fds_swap, prg_allow, audio[15:4]); - 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] = 3'b000; - //assign prg_aout[12:0] = prg_ain[12:0]; - assign audio[3:0] = 4'b0; - -endmodule - -module MultiMapper(input clk, input ce, input ppu_ce, input reset, - input [19:0] ppuflags, // Misc flags from PPU for MMC5 cheating - input [31:0] flags, // Misc flags from ines header {prg_size(3), chr_size(3), mapper(8)} - input [15:0] prg_ain, output reg [21:0] prg_aout,// PRG Input / Output Address Lines - input prg_read, prg_write, // PRG Read / write signals - input [7:0] prg_din, output reg [7:0] prg_dout, // PRG Data - input [7:0] prg_from_ram, // PRG Data from RAM - output reg prg_allow, // PRG Allow write access - input chr_read, // Read from CHR - input [13:0] chr_ain, output reg [21:0] chr_aout,// CHR Input / Output Address Lines - output reg [7:0] chr_dout, // Value to override CHR data with - output reg has_chr_dout, // True if CHR data should be overridden - output reg chr_allow, // CHR Allow write - output reg vram_a10, // CHR Value for A10 address line - output reg vram_ce, // CHR True if the address should be routed to the internal 2kB VRAM. - output reg irq, - output reg [15:0] audio, // External Audio - input fds_swap); // FDS Disk Swap Pause - wire mmc0_prg_allow, mmc0_vram_a10, mmc0_vram_ce, mmc0_chr_allow; - wire [21:0] mmc0_prg_addr, mmc0_chr_addr; - MMC0 mmc0(clk, ce, flags, prg_ain, mmc0_prg_addr, prg_read, prg_write, prg_din, mmc0_prg_allow, - chr_ain, mmc0_chr_addr, mmc0_chr_allow, mmc0_vram_a10, mmc0_vram_ce); - - wire mmc1_prg_allow, mmc1_vram_a10, mmc1_vram_ce, mmc1_chr_allow; - wire [21:0] mmc1_prg_addr, mmc1_chr_addr; - MMC1 mmc1(clk, ce, reset, flags, prg_ain, mmc1_prg_addr, prg_read, prg_write, prg_din, mmc1_prg_allow, - chr_ain, mmc1_chr_addr, mmc1_chr_allow, mmc1_vram_a10, mmc1_vram_ce); - - wire map28_prg_allow, map28_vram_a10, map28_vram_ce, map28_chr_allow; - wire [21:0] map28_prg_addr, map28_chr_addr; - Mapper28 map28(clk, ce, reset, flags, prg_ain, map28_prg_addr, prg_read, prg_write, prg_din, map28_prg_allow, - chr_ain, map28_chr_addr, map28_chr_allow, map28_vram_a10, map28_vram_ce); - - wire map30_prg_allow, map30_vram_a10, map30_vram_ce, map30_chr_allow; - wire [21:0] map30_prg_addr, map30_chr_addr; - Mapper30 map30(clk, ce, reset, flags, prg_ain, map30_prg_addr, prg_read, prg_write, prg_din, map30_prg_allow, - chr_ain, map30_chr_addr, map30_chr_allow, map30_vram_a10, map30_vram_ce); - - wire map32_prg_allow, map32_vram_a10, map32_vram_ce, map32_chr_allow; - wire [21:0] map32_prg_addr, map32_chr_addr; - Mapper32 map32(clk, ce, reset, flags, prg_ain, map32_prg_addr, prg_read, prg_write, prg_din, map32_prg_allow, - chr_ain, map32_chr_addr, map32_chr_allow, map32_vram_a10, map32_vram_ce); - - wire mmc2_prg_allow, mmc2_vram_a10, mmc2_vram_ce, mmc2_chr_allow; - wire [21:0] mmc2_prg_addr, mmc2_chr_addr; - MMC2 mmc2(clk, ppu_ce, reset, flags, prg_ain, mmc2_prg_addr, prg_read, prg_write, prg_din, mmc2_prg_allow, - chr_read, chr_ain, mmc2_chr_addr, mmc2_chr_allow, mmc2_vram_a10, mmc2_vram_ce); - - wire mmc3_prg_allow, mmc3_vram_a10, mmc3_vram_ce, mmc3_chr_allow, mmc3_irq; - wire [21:0] mmc3_prg_addr, mmc3_chr_addr; - MMC3 mmc3(clk, ppu_ce, reset, flags, prg_ain, mmc3_prg_addr, prg_read, prg_write, prg_din, mmc3_prg_allow, - chr_ain, mmc3_chr_addr, mmc3_chr_allow, mmc3_vram_a10, mmc3_vram_ce, mmc3_irq); - - wire mmc4_prg_allow, mmc4_vram_a10, mmc4_vram_ce, mmc4_chr_allow; - wire [21:0] mmc4_prg_addr, mmc4_chr_addr; - MMC4 mmc4(clk, ppu_ce, reset, flags, prg_ain, mmc4_prg_addr, prg_read, prg_write, prg_din, mmc4_prg_allow, - chr_read, chr_ain, mmc4_chr_addr, mmc4_chr_allow, mmc4_vram_a10, mmc4_vram_ce); - - wire mmc5_prg_allow, mmc5_vram_a10, mmc5_vram_ce, mmc5_chr_allow, mmc5_irq; - wire [21:0] mmc5_prg_addr, mmc5_chr_addr; - wire [7:0] mmc5_chr_dout, mmc5_prg_dout; - wire mmc5_has_chr_dout; - wire [15:0] mmc5_audio; - MMC5 mmc5(clk, ppu_ce, reset, flags, ppuflags, prg_ain, mmc5_prg_addr, prg_read, prg_write, prg_din, mmc5_prg_dout, mmc5_prg_allow, - chr_ain, mmc5_chr_addr, mmc5_chr_dout, mmc5_has_chr_dout, - mmc5_chr_allow, mmc5_vram_a10, mmc5_vram_ce, mmc5_irq, mmc5_audio); - - wire map13_prg_allow, map13_vram_a10, map13_vram_ce, map13_chr_allow; - wire [21:0] map13_prg_addr, map13_chr_addr; - Mapper13 map13(clk, ce, reset, flags, prg_ain, map13_prg_addr, prg_read, prg_write, prg_din, map13_prg_allow, - chr_ain, map13_chr_addr, map13_chr_allow, map13_vram_a10, map13_vram_ce); - - wire map15_prg_allow, map15_vram_a10, map15_vram_ce, map15_chr_allow; - wire [21:0] map15_prg_addr, map15_chr_addr; - Mapper15 map15(clk, ce, reset, flags, prg_ain, map15_prg_addr, prg_read, prg_write, prg_din, map15_prg_allow, - chr_ain, map15_chr_addr, map15_chr_allow, map15_vram_a10, map15_vram_ce); - - wire map16_prg_allow, map16_vram_a10, map16_vram_ce, map16_chr_allow, map16_irq; - wire [21:0] map16_prg_addr, map16_chr_addr; - wire [7:0] map16_prg_dout; - Mapper16 map16(clk, ce, reset, flags, prg_ain, map16_prg_addr, prg_read, prg_write, prg_din, map16_prg_dout, map16_prg_allow, - chr_ain, map16_chr_addr, map16_chr_allow, map16_vram_a10, map16_vram_ce, map16_irq); - - wire map18_prg_allow, map18_vram_a10, map18_vram_ce, map18_chr_allow, map18_irq; - wire [21:0] map18_prg_addr, map18_chr_addr; - wire [7:0] map18_prg_dout; - Mapper18 map18(clk, ce, reset, flags, prg_ain, map18_prg_addr, prg_read, prg_write, prg_din, map18_prg_dout, map18_prg_allow, - chr_ain, map18_chr_addr, map18_chr_allow, map18_vram_a10, map18_vram_ce, map18_irq); - - wire map34_prg_allow, map34_vram_a10, map34_vram_ce, map34_chr_allow; - wire [21:0] map34_prg_addr, map34_chr_addr; - Mapper34 map34(clk, ce, reset, flags, prg_ain, map34_prg_addr, prg_read, prg_write, prg_din, map34_prg_allow, - chr_ain, map34_chr_addr, map34_chr_allow, map34_vram_a10, map34_vram_ce); - - wire map41_prg_allow, map41_vram_a10, map41_vram_ce, map41_chr_allow; - wire [21:0] map41_prg_addr, map41_chr_addr; - Mapper41 map41(clk, ce, reset, flags, prg_ain, map41_prg_addr, prg_read, prg_write, prg_din, map41_prg_allow, - chr_ain, map41_chr_addr, map41_chr_allow, map41_vram_a10, map41_vram_ce); - - wire map42_prg_allow, map42_vram_a10, map42_vram_ce, map42_chr_allow, map42_irq; - wire [21:0] map42_prg_addr, map42_chr_addr; - Mapper42 map42(clk, ce, reset, flags, prg_ain, map42_prg_addr, prg_read, prg_write, prg_din, map42_prg_allow, - chr_ain, map42_chr_addr, map42_chr_allow, map42_vram_a10, map42_vram_ce, map42_irq); - - wire map66_prg_allow, map66_vram_a10, map66_vram_ce, map66_chr_allow; - wire [21:0] map66_prg_addr, map66_chr_addr; - Mapper66 map66(clk, ce, reset, flags, prg_ain, map66_prg_addr, prg_read, prg_write, prg_din, map66_prg_allow, - chr_ain, map66_chr_addr, map66_chr_allow, map66_vram_a10, map66_vram_ce); - - wire map68_prg_allow, map68_vram_a10, map68_vram_ce, map68_chr_allow; - wire [21:0] map68_prg_addr, map68_chr_addr; - Mapper68 map68(clk, ce, reset, flags, prg_ain, map68_prg_addr, prg_read, prg_write, prg_din, map68_prg_allow, - chr_ain, map68_chr_addr, map68_chr_allow, map68_vram_a10, map68_vram_ce); - - wire map69_prg_allow, map69_vram_a10, map69_vram_ce, map69_chr_allow, map69_irq; - wire [21:0] map69_prg_addr, map69_chr_addr; - wire [15:0] map69_audio; - Mapper69 map69(clk, ce, reset, flags, prg_ain, map69_prg_addr, prg_read, prg_write, prg_din, map69_prg_allow, - chr_ain, map69_chr_addr, map69_chr_allow, map69_vram_a10, map69_vram_ce, map69_irq, map69_audio); - - wire map71_prg_allow, map71_vram_a10, map71_vram_ce, map71_chr_allow; - wire [21:0] map71_prg_addr, map71_chr_addr; - Mapper71 map71(clk, ce, reset, flags, prg_ain, map71_prg_addr, prg_read, prg_write, prg_din, map71_prg_allow, - chr_ain, map71_chr_addr, map71_chr_allow, map71_vram_a10, map71_vram_ce); - - wire map78_prg_allow, map78_vram_a10, map78_vram_ce, map78_chr_allow; - wire [21:0] map78_prg_addr, map78_chr_addr; - Mapper78 map78(clk, ce, reset, flags, prg_ain, map78_prg_addr, prg_read, prg_write, prg_din, map78_prg_allow, - chr_ain, map78_chr_addr, map78_chr_allow, map78_vram_a10, map78_vram_ce); - - wire map79_prg_allow, map79_vram_a10, map79_vram_ce, map79_chr_allow; - wire [21:0] map79_prg_addr, map79_chr_addr; - Mapper79 map79(clk, ce, reset, flags, prg_ain, map79_prg_addr, prg_read, prg_write, prg_din, map79_prg_allow, - chr_ain, map79_chr_addr, map79_chr_allow, map79_vram_a10, map79_vram_ce); - - wire map89_prg_allow, map89_vram_a10, map89_vram_ce, map89_chr_allow; - wire [21:0] map89_prg_addr, map89_chr_addr; - Mapper89 map89(clk, ce, reset, flags, prg_ain, map89_prg_addr, prg_read, prg_write, prg_din, map89_prg_allow, - chr_ain, map89_chr_addr, map89_chr_allow, map89_vram_a10, map89_vram_ce); - - wire map165_prg_allow, map165_vram_a10, map165_vram_ce, map165_chr_allow, map165_irq; - wire [21:0] map165_prg_addr, map165_chr_addr; - Mapper165 map165(clk, ppu_ce, reset, flags, prg_ain, map165_prg_addr, prg_read, prg_write, prg_din, map165_prg_allow, - chr_read, chr_ain, map165_chr_addr, map165_chr_allow, map165_vram_a10, map165_vram_ce, map165_irq); - - wire map228_prg_allow, map228_vram_a10, map228_vram_ce, map228_chr_allow; - wire [21:0] map228_prg_addr, map228_chr_addr; - Mapper228 map228(clk, ce, reset, flags, prg_ain, map228_prg_addr, prg_read, prg_write, prg_din, map228_prg_allow, - chr_ain, map228_chr_addr, map228_chr_allow, map228_vram_a10, map228_vram_ce); - - - wire map234_prg_allow, map234_vram_a10, map234_vram_ce, map234_chr_allow; - wire [21:0] map234_prg_addr, map234_chr_addr; - Mapper234 map234(clk, ce, reset, flags, prg_ain, map234_prg_addr, prg_read, prg_write, prg_from_ram, map234_prg_allow, - chr_ain, map234_chr_addr, map234_chr_allow, map234_vram_a10, map234_vram_ce); - - wire rambo1_prg_allow, rambo1_vram_a10, rambo1_vram_ce, rambo1_chr_allow, rambo1_irq; - wire [21:0] rambo1_prg_addr, rambo1_chr_addr; - Rambo1 rambo1(clk, ce, reset, flags, prg_ain, rambo1_prg_addr, prg_read, prg_write, prg_din, rambo1_prg_allow, - chr_ain, rambo1_chr_addr, rambo1_chr_allow, rambo1_vram_a10, rambo1_vram_ce, rambo1_irq); - - wire [21:0] nesev_prg_addr, nesev_chr_addr; - wire nesev_irq; - NesEvent nesev(clk, ce, reset, prg_ain, nesev_prg_addr, chr_ain, nesev_chr_addr, mmc1_chr_addr[16:13], mmc1_prg_addr, nesev_irq); - - wire vrc1_prg_allow, vrc1_vram_a10, vrc1_vram_ce, vrc1_chr_allow; - wire [21:0] vrc1_prg_addr, vrc1_chr_addr; - VRC1 vrc1(clk, ce, reset, flags, prg_ain, vrc1_prg_addr, prg_read, prg_write, prg_din, vrc1_prg_allow, - chr_ain, vrc1_chr_addr, vrc1_chr_allow, vrc1_vram_a10, vrc1_vram_ce); - - wire vrc6_prg_allow, vrc6_vram_a10, vrc6_vram_ce, vrc6_chr_allow, vrc6_irq; - wire [21:0] vrc6_prg_addr, vrc6_chr_addr; - wire [15:0] vrc6_audio; - wire [7:0] vrc6_prg_dout; - VRC6 vrc6(clk, ce, reset, flags, prg_ain, vrc6_prg_addr, prg_read, prg_write, prg_din, vrc6_prg_dout, vrc6_prg_allow, - chr_ain, vrc6_chr_addr, vrc6_chr_allow, vrc6_vram_a10, vrc6_vram_ce, vrc6_irq, vrc6_audio); - - wire vrc7_prg_allow, vrc7_vram_a10, vrc7_vram_ce, vrc7_chr_allow, vrc7_irq; - wire [21:0] vrc7_prg_addr, vrc7_chr_addr; - wire [15:0] vrc7_audio; - VRC7 vrc7(clk, ce, reset, flags, prg_ain, vrc7_prg_addr, prg_read, prg_write, prg_din, vrc7_prg_allow, - chr_ain, vrc7_chr_addr, vrc7_chr_allow, vrc7_vram_a10, vrc7_vram_ce, vrc7_irq, vrc7_audio); - - wire map19_prg_allow, map19_vram_a10, map19_vram_ce, map19_chr_allow, map19_irq; - wire [21:0] map19_prg_addr, map19_chr_addr; - wire [15:0] map19_audio; - wire [7:0] map19_prg_dout; - N106 n106(clk, ce, reset, flags, prg_ain, map19_prg_addr, prg_read, prg_write, prg_din, map19_prg_dout, map19_prg_allow, - chr_ain, map19_chr_addr, map19_chr_allow, map19_vram_a10, map19_vram_ce, map19_irq, map19_audio); - - wire mapfds_prg_allow, mapfds_vram_a10, mapfds_vram_ce, mapfds_chr_allow, mapfds_irq; - wire [21:0] mapfds_prg_addr, mapfds_chr_addr; - wire [15:0] mapfds_audio; - wire [7:0] mapfds_chr_dout, mapfds_prg_dout; - MapperFDS mapfds(clk, ce, reset, flags, prg_ain, mapfds_prg_addr, prg_read, prg_write, prg_din, mapfds_prg_dout, mapfds_prg_allow, - chr_ain, mapfds_chr_addr, mapfds_chr_allow, mapfds_vram_a10, mapfds_vram_ce, mapfds_irq, - mapfds_audio, fds_swap); - - // Mask - reg [5:0] prg_mask; - reg [6:0] chr_mask; - - always @* begin - case(flags[10:8]) - 0: prg_mask = 6'b000000; - 1: prg_mask = 6'b000001; - 2: prg_mask = 6'b000011; - 3: prg_mask = 6'b000111; - 4: prg_mask = 6'b001111; - 5: prg_mask = 6'b011111; - default: prg_mask = 6'b111111; - endcase - - case(flags[13:11]) - 0: chr_mask = 7'b0000000; - 1: chr_mask = 7'b0000001; - 2: chr_mask = 7'b0000011; - 3: chr_mask = 7'b0000111; - 4: chr_mask = 7'b0001111; - 5: chr_mask = 7'b0011111; - 6: chr_mask = 7'b0111111; - 7: chr_mask = 7'b1111111; - endcase - - irq = 0; - prg_dout = 8'hff; - has_chr_dout = 0; - chr_dout = mmc5_chr_dout; - audio = 16'h0000; -// 0 = Working -// 1 = Working -// 2 = Working -// 3 = Working -// 4 = Working -// 5 = Working/Audio needs testing/Some games graphics corruption (Just Breed) -// 7 = Working -// 9 = Working -// 10 = Working -// 11 = Working -// 13 = Working -// 15 = Working -// 16 = Working minus EEPROM support -// 18 = Needs testing -// 19 = Needs testing -// 20 = Needs testing -// 24 = Needs testing -// 26 = Needs testing -// 28 = Working -// 30 = No Self Flashing/Needs testing -// 32 = Needs testing -// 33 = Needs testing -// 34 = Working -// 37 = Needs testing -// 38 = Needs testing -// 41 = Working -// 42 = Working -// 47 = Working -// 48 = Needs testing -// 64 = Tons of GFX bugs -// 66 = Working -// 68 = Working -// 69 = Working -// 70 = Needs testing -// 71 = Working -// 74 = Needs testing -// 75 = Needs testing -// 76 = Needs testing -// 78 = Submapper 1 Requires NES 2.0/Needs testing overall -// 79 = Working -// 80 = Needs testing -// 82 = Needs testing -// 85 = Needs testing/Audio needs testing -// 87 = Needs testing -// 88 = Needs testing -// 89 = Needs testing -// 93 = Needs testing -// 95 = Needs testing -// 101 = Needs testing -// 105 = Working -// 113 = Working -// 118 = Working -// 119 = Working -// 140 = Needs testing -// 152 = Needs testing -// 154 = Needs testing -// 158 = Tons of GFX bugs -// 165 = GFX corrupted -// 184 = Needs testing -// 191 = Not Tested -// 192 = Not Tested -// 194 = Not Tested -// 195 = Not Tested -// 206 = Not Tested -// 207 = Needs testing -// 228 = Working -// 234 = Not Tested - case(flags[7:0]) - 1: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {mmc1_prg_addr, mmc1_prg_allow, mmc1_chr_addr, mmc1_vram_a10, mmc1_vram_ce, mmc1_chr_allow}; - 9: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {mmc2_prg_addr, mmc2_prg_allow, mmc2_chr_addr, mmc2_vram_a10, mmc2_vram_ce, mmc2_chr_allow}; - 118, // TxSROM connects A17 to CIRAM A10. - 119, // TQROM uses the Nintendo MMC3 like other TxROM boards but uses the CHR bank number specially. - 47, // Mapper 047 is a MMC3 multicart - 206, // MMC3 w/o IRQ or WRAM support - 88, // NAMCOT-3433 is mapper 206-like, but connects PPU-A12 to CHROM A16. - 154, // NAMCOT-3453 is mapper 88-like, but with one screen mirroring. - 95, // NAMCOT-3425 is mapper 206-like, but connects A16 to CIRAM A10. - 76, // NAMCOT-3446 is mapper 206-like, but coarser chr banking. - 80, // Taito X01-005 is MMC3-like with Internal RAM and no IRQ - 82, // Tatio X01-017 is mapper 80-like with more Internal RAM - 207, // Tatio X01-005 is mapper 80-like with one screen mirroring - 48, // MMC3-like with delayed IRQ - 33, // Mapper 48 without IRQ and different mirroring location - 37, // European Triple Cart (Super Mario, Tetris, Nintendo World Cup) - 74, // MMC3 like but uses the CHR RAM. - 191, // MMC3 like but uses the CHR RAM. - 192, // MMC3 like but uses the CHR RAM. - 194, // MMC3 like but uses the CHR RAM. - 195, // MMC3 like but uses the CHR RAM. - 4: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, irq} = {mmc3_prg_addr, mmc3_prg_allow, mmc3_chr_addr, mmc3_vram_a10, mmc3_vram_ce, mmc3_chr_allow, mmc3_irq}; - - 10: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {mmc4_prg_addr, mmc4_prg_allow, mmc4_chr_addr, mmc4_vram_a10, mmc4_vram_ce, mmc4_chr_allow}; - - 5: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, has_chr_dout, prg_dout, irq, audio} = {mmc5_prg_addr, mmc5_prg_allow, mmc5_chr_addr, mmc5_vram_a10, mmc5_vram_ce, mmc5_chr_allow, mmc5_has_chr_dout, mmc5_prg_dout, mmc5_irq, mmc5_audio}; - - 0, - 2, - 3, - 7, - 28: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map28_prg_addr, map28_prg_allow, map28_chr_addr, map28_vram_a10, map28_vram_ce, map28_chr_allow}; - - 89, - 93, - 184: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map89_prg_addr, map89_prg_allow, map89_chr_addr, map89_vram_a10, map89_vram_ce, map89_chr_allow}; - - 30: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map30_prg_addr, map30_prg_allow, map30_chr_addr, map30_vram_a10, map30_vram_ce, map30_chr_allow}; - - 32: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map32_prg_addr, map32_prg_allow, map32_chr_addr, map32_vram_a10, map32_vram_ce, map32_chr_allow}; - - 13: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map13_prg_addr, map13_prg_allow, map13_chr_addr, map13_vram_a10, map13_vram_ce, map13_chr_allow}; - 15: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map15_prg_addr, map15_prg_allow, map15_chr_addr, map15_vram_a10, map15_vram_ce, map15_chr_allow}; - - 16: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, prg_dout, irq} = {map16_prg_addr, map16_prg_allow, map16_chr_addr, map16_vram_a10, map16_vram_ce, map16_chr_allow, map16_prg_dout, map16_irq}; - - 18: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, prg_dout, irq} = {map18_prg_addr, map18_prg_allow, map18_chr_addr, map18_vram_a10, map18_vram_ce, map18_chr_allow, map18_prg_dout, map18_irq}; - - 34: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map34_prg_addr, map34_prg_allow, map34_chr_addr, map34_vram_a10, map34_vram_ce, map34_chr_allow}; - 41: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map41_prg_addr, map41_prg_allow, map41_chr_addr, map41_vram_a10, map41_vram_ce, map41_chr_allow}; - - 64, - 158: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, irq} = {rambo1_prg_addr, rambo1_prg_allow, rambo1_chr_addr, rambo1_vram_a10, rambo1_vram_ce, rambo1_chr_allow, rambo1_irq}; - - 42: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, irq} = {map42_prg_addr, map42_prg_allow, map42_chr_addr, map42_vram_a10, map42_vram_ce, map42_chr_allow, map42_irq}; - - 11, - 38, - 87, - 101, - 140, - 66: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map66_prg_addr, map66_prg_allow, map66_chr_addr, map66_vram_a10, map66_vram_ce, map66_chr_allow}; - 68: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map68_prg_addr, map68_prg_allow, map68_chr_addr, map68_vram_a10, map68_vram_ce, map68_chr_allow}; - 69: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, irq, audio} = {map69_prg_addr, map69_prg_allow, map69_chr_addr, map69_vram_a10, map69_vram_ce, map69_chr_allow, map69_irq, map69_audio}; - - 71, - 232: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map71_prg_addr, map71_prg_allow, map71_chr_addr, map71_vram_a10, map71_vram_ce, map71_chr_allow}; - - 152, - 70, - 78: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map78_prg_addr, map78_prg_allow, map78_chr_addr, map78_vram_a10, map78_vram_ce, map78_chr_allow}; - - 79, - 113: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map79_prg_addr, map79_prg_allow, map79_chr_addr, map79_vram_a10, map79_vram_ce, map79_chr_allow}; - - 105: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, irq}= {nesev_prg_addr, mmc1_prg_allow, nesev_chr_addr, mmc1_vram_a10, mmc1_vram_ce, mmc1_chr_allow, nesev_irq}; - - 165: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, irq} = {map165_prg_addr, map165_prg_allow, map165_chr_addr, map165_vram_a10, map165_vram_ce, map165_chr_allow, map165_irq}; - - 228: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map228_prg_addr, map228_prg_allow, map228_chr_addr, map228_vram_a10, map228_vram_ce, map228_chr_allow}; - 234: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {map234_prg_addr, map234_prg_allow, map234_chr_addr, map234_vram_a10, map234_vram_ce, map234_chr_allow}; - 75: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {vrc1_prg_addr, vrc1_prg_allow, vrc1_chr_addr, vrc1_vram_a10, vrc1_vram_ce, vrc1_chr_allow}; - 20: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, prg_dout, irq, audio} = {mapfds_prg_addr, mapfds_prg_allow, mapfds_chr_addr, mapfds_vram_a10, mapfds_vram_ce, mapfds_chr_allow, mapfds_prg_dout, mapfds_irq, mapfds_audio}; - 24, - 26: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, prg_dout, irq, audio} = {vrc6_prg_addr, vrc6_prg_allow, vrc6_chr_addr, vrc6_vram_a10, vrc6_vram_ce, vrc6_chr_allow, vrc6_prg_dout, vrc6_irq, vrc6_audio}; - 85: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, irq, audio} = {vrc7_prg_addr, vrc7_prg_allow, vrc7_chr_addr, vrc7_vram_a10, vrc7_vram_ce, vrc7_chr_allow, vrc7_irq, vrc7_audio}; - 19: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow, prg_dout, irq, audio} = {map19_prg_addr, map19_prg_allow, map19_chr_addr, map19_vram_a10, map19_vram_ce, map19_chr_allow, map19_prg_dout, map19_irq, map19_audio}; - default: {prg_aout, prg_allow, chr_aout, vram_a10, vram_ce, chr_allow} = {mmc0_prg_addr, mmc0_prg_allow, mmc0_chr_addr, mmc0_vram_a10, mmc0_vram_ce, mmc0_chr_allow}; - endcase - if (prg_aout[21:20] == 2'b00) - prg_aout[19:0] = {prg_aout[19:14] & prg_mask, prg_aout[13:0]}; - if (chr_aout[21:20] == 2'b10) - chr_aout[19:0] = {chr_aout[19:13] & chr_mask, chr_aout[12:0]}; - // Remap the CHR address into VRAM, if needed. - chr_aout = vram_ce ? {11'b11_0000_0000_0, vram_a10, chr_ain[9:0]} : chr_aout; - prg_aout = (prg_ain < 'h2000) ? {11'b11_1000_0000_0, prg_ain[10:0]} : prg_aout; - prg_allow = prg_allow || (prg_ain < 'h2000); - end -endmodule - -// PRG = 0.... -// CHR = 10... -// CHR-VRAM = 1100 -// CPU-RAM = 1110 -// CARTRAM = 1111 - diff --git a/cores/nes/src/nes.v b/cores/nes/src/nes.v index 227a540..e3a6cef 100644 --- a/cores/nes/src/nes.v +++ b/cores/nes/src/nes.v @@ -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 diff --git a/cores/nes/src/ppu.sv b/cores/nes/src/ppu.sv new file mode 100644 index 0000000..db6edfc --- /dev/null +++ b/cores/nes/src/ppu.sv @@ -0,0 +1,1341 @@ +// Copyright (c) 2012-2013 Ludvig Strigeus +// This program is GPL Licensed. See COPYING for the full license. + +// altera message_off 10935 +// altera message_off 10027 + +// 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; +reg [7:0] din_shift[2]; +reg [1:0] write_shift; +reg [1:0] latch_shift; + +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 + 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) begin + // Increment address every time we accessed a reg + if (~is_rendering) begin + loopy_v <= loopy_v + (ppu_incr ? 15'd32 : 15'd1); + end else begin + // During rendering (on the pre-render line and the visible lines 0-239, provided either background or sprite rendering is + // enabled), it will update v in an odd way, triggering a coarse X increment and a Y increment simultaneously (with normal + // wrapping behavior). Internally, this is caused by the carry inputs to various sections of v being set up for rendering, + // and the $2007 access triggering a "load next value" signal for all of v (when not rendering, the carry inputs are set up + // to linearly increment v by either 1 or 32). This behavior is not affected by the status of the increment bit. The Young + // Indiana Jones Chronicles uses this for some effects to adjust the Y scroll during rendering, and also Burai Fighter (U) + // to draw the scorebar. + loopy_v[4:0] <= loopy_v[4:0] + 1'd1; + loopy_v[10] <= loopy_v[10] ^ (loopy_v[4:0] == 31); + 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 + end + + // Writes to vram address appear to be delayed by 2 cycles + latch_shift <= {latch_shift[0], ppu_address_latch}; + write_shift <= {write_shift[0], (write && ain == 6)}; + din_shift <= '{din, din_shift[0]}; + + if (write_shift[1]) begin + if (!latch_shift[1]) begin + loopy_t[13:8] <= din_shift[1][5:0]; + loopy_t[14] <= 0; + end else begin + loopy_t[7:0] <= din_shift[1]; + loopy_v <= {loopy_t[14:8], din_shift[1]}; + end + 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 [1:0] sys_type, + 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, + output short_frame, + output is_vbe_sl +); + +reg even_frame_toggle = 0; // 1 indicates even frame. + +// Dendy is 291 to 310 +wire [8:0] vblank_start_sl; +wire [8:0] vblank_end_sl; +wire [8:0] last_sl; +wire skip_en; +reg [3:0] rendering_sr; + +always_comb begin + case (sys_type) + 2'b00,2'b11: begin // NTSC/Vs. + vblank_start_sl = 9'd241; + vblank_end_sl = 9'd260; + skip_en = 1'b1; + end + + 2'b01: begin // PAL + vblank_start_sl = 9'd241; + vblank_end_sl = 9'd310; + skip_en = 1'b0; + end + + 2'b10: begin // Dendy + vblank_start_sl = 9'd291; + vblank_end_sl = 9'd310; + skip_en = 1'b0; + end + endcase +end + +assign at_last_cycle_group = (cycle[8:3] == 42); + +// For NTSC only, the *last* cycle of odd frames is skipped. +// In Visual 2C02, the counter starts at zero and flips at scanline 256. +assign short_frame = end_of_line & skip_pixel; + +wire skip_pixel = is_pre_render && ~even_frame_toggle && rendering_sr[3] && skip_en; +assign end_of_line = at_last_cycle_group && (cycle[3:0] == (skip_pixel ? 3 : 4)); + +// Confimed with Visual 2C02 +// All vblank clocked registers should have changed and be readable by cycle 1 of 241/261 +assign entering_vblank = (cycle == 0) && scanline == vblank_start_sl; +assign exiting_vblank = (cycle == 0) && is_pre_render; + +assign is_vbe_sl = (scanline == vblank_end_sl); + +// 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 <= 338; + is_in_vblank <= 0; +end else if (ce) begin + // On a real AV famicom, the NMI even_odd_timing test fails with 09, this SR is to make that happen + rendering_sr <= {rendering_sr[2:0], is_rendering}; + cycle <= end_of_line ? 9'd0 : cycle + 9'd1; + is_in_vblank <= new_is_in_vblank; +end + +always @(posedge clk) if (reset) begin + scanline <= 0; + is_pre_render <= 0; + even_frame_toggle <= 0; // Resets to 0, the first frame will always end with 341 pixels. +end else if (ce && end_of_line) begin + // Once the scanline counter reaches end of 260, it gets reset to -1. + scanline <= (scanline == vblank_end_sl) ? 9'b111111111 : scanline + 1'd1; + // The pre render flag is set while we're on scanline -1. + is_pre_render <= (scanline == vblank_end_sl); + + if (scanline == 255) + even_frame_toggle <= ~even_frame_toggle; +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 sprites. Will return the pixel value of the highest prioritized sprite. +// When load is set, and clocked, load_in is loaded into sprite 15 and all others are shifted down. +// Sprite 0 has highest prio. +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 [3:0] load_ex, // Which parts of the state to load/shift for extra sprites. + input [26:0] load_in, // State to load with + input [26:0] load_in_ex,// Extra spirtes + output [4:0] bits, // Output bits + output is_sprite0, // Set to true if sprite #0 was output + input extra_sprites +); + +wire [26:0] load_out7, load_out6, load_out5, load_out4, load_out3, load_out2, load_out1, load_out0, + load_out15, load_out14, load_out13, load_out12, load_out11, load_out10, load_out9, load_out8; +wire [4:0] bits7, bits6, bits5, bits4, bits3, bits2, bits1, bits0, + bits15, bits14, bits13, bits12, bits11, bits10, bits9, bits8; + +// Extra sprites +Sprite sprite15(clk, ce, enable, load_ex, load_in_ex, load_out15, bits15); +Sprite sprite14(clk, ce, enable, load_ex, load_out15, load_out14, bits14); +Sprite sprite13(clk, ce, enable, load_ex, load_out14, load_out13, bits13); +Sprite sprite12(clk, ce, enable, load_ex, load_out13, load_out12, bits12); +Sprite sprite11(clk, ce, enable, load_ex, load_out12, load_out11, bits11); +Sprite sprite10(clk, ce, enable, load_ex, load_out11, load_out10, bits10); +Sprite sprite9( clk, ce, enable, load_ex, load_out10, load_out9, bits9); +Sprite sprite8( clk, ce, enable, load_ex, load_out9, load_out8, bits8); + +// Basic Sprites +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 = bits_orig; +wire [4:0] bits_orig = + 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[1:0] != 0 || ~extra_sprites ? bits7 : + bits_ex; + +wire [4:0] bits_ex = + bits8[1:0] != 0 ? bits8 : + bits9[1:0] != 0 ? bits9 : + bits10[1:0] != 0 ? bits10 : + bits11[1:0] != 0 ? bits11 : + bits12[1:0] != 0 ? bits12 : + bits13[1:0] != 0 ? bits13 : + bits14[1:0] != 0 ? bits14 : + bits15; + +assign is_sprite0 = bits0[1:0] != 0; + +endmodule // SpriteSet + +module OAMEval( + input clk, + input ce, + input reset, + input rendering_enabled, // Set to 1 if evaluations are enabled + 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 [7:0] oam_bus, // Current value on the OAM bus, returned to NES through $2004. + output reg [31:0] oam_bus_ex, + input oam_addr_write, // Load oam with specified value, when writing to NES $2003. + input oam_data_write, // Load oam_ptr with specified value, when writing to NES $2004. + input [7:0] oam_din, // 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. + input is_vbe, // Last line before pre-render + input PAL, + output masked_sprites // If the game is trying to mask extra sprites +); + +// https://wiki.nesdev.com/w/index.php/PPU_sprite_evaluation +// NOTE: At the time of this writing, much information on the wiki is off by one, as mentioned here: +// https://forums.nesdev.com/viewtopic.php?f=3&t=19005 + +assign oam_bus = oam_data; + +enum { + STATE_IDLE, + STATE_CLEAR, + STATE_EVAL, + STATE_FETCH, + STATE_REFRESH +} oam_state = STATE_IDLE; + +reg [7:0] oam_temp[64]; // OAM Temporary buffer, normally 32 bytes, 64 for extra sprites +reg [7:0] oam[256]; // OAM RAM, 256 bytes +reg [7:0] oam_addr; // OAM Address Register 2003 +reg [2:0] oam_temp_slot; // Pointer to oam_temp; +reg [7:0] oam_data; // OAM Data Register 2004 +reg oam_temp_wren; // Write enable for OAM temp, disabled if full + +// Extra Registers +reg [5:0] oam_addr_ex; // OAM pointer for use with extra sprites +reg [3:0] oam_temp_slot_ex; +reg [1:0] m_ex; +reg [7:0] oam_data_ex; +reg [2:0] spr_counter; // Count sprites + +wire visible = (scanline < 240); +wire rendering = (scanline == 9'd511 || visible) && rendering_enabled; +wire evaluating = visible && rendering_enabled; + +reg [5:0] oam_temp_addr; +reg [6:0] feed_cnt; + +reg sprite0_curr; +reg [2:0] repeat_count; + +assign masked_sprites = &repeat_count; + +always @(posedge clk) begin :oam_eval +reg n_ovr, ex_ovr; +reg [1:0] eval_counter; +reg old_rendering; +reg [8:0] last_y, last_tile, last_attr; + +reg overflow; + +if (cycle == 340 && ce) begin + sprite0 <= sprite0_curr; + sprite0_curr <= 0; +end + +if (reset) begin + oam_temp <= '{64{8'hFF}}; + oam_data <= oam_temp[0]; + oam_temp_addr <= 0; + oam_temp_slot <= 0; + oam_temp_wren <= 1; + oam_temp_slot_ex <= 0; + n_ovr <= 0; + spr_counter <= 0; + repeat_count <= 0; + sprite0 <= 0; + sprite0_curr <= 0; + feed_cnt <= 0; + overflow <= 0; + eval_counter <= 0; + ex_ovr <= 0; + oam_state <= STATE_IDLE; +end else if (ce) begin + + // State machine. Remember these will be one ppu cycle early. + case (cycle) + 337: oam_state <= STATE_IDLE; // 1 cycle + 338,0: oam_state <= STATE_CLEAR; // 64 cycles + 62: oam_state <= STATE_EVAL; // 192 cycles + 254: oam_state <= STATE_FETCH; // 64 cycles + 318: oam_state <= STATE_REFRESH; // 19 cycles + endcase + + // It is also the case that if OAMADDR is not less than eight when rendering starts, + // the eight bytes starting at OAMADDR & 0xF8 are copied to the first eight bytes + // of OAM + if (rendering && cycle == 0) begin + if (|oam_addr[7:3] && ~PAL) begin + oam[0] <= oam[{oam_addr[7:3], 3'b000}]; + oam[1] <= oam[{oam_addr[7:3], 3'b001}]; + oam[2] <= oam[{oam_addr[7:3], 3'b010}]; + oam[3] <= oam[{oam_addr[7:3], 3'b011}]; + oam[4] <= oam[{oam_addr[7:3], 3'b100}]; + oam[5] <= oam[{oam_addr[7:3], 3'b101}]; + oam[6] <= oam[{oam_addr[7:3], 3'b110}]; + oam[7] <= oam[{oam_addr[7:3], 3'b111}]; + end + end + + // XXX this is outside the "evaluating" block because of timing issues + if (rendering) begin + if (oam_state == STATE_IDLE) begin + oam_data <= oam_temp[0]; + oam_temp_addr <= 0; + oam_temp_slot <= 0; + oam_temp_wren <= 1; + oam_temp_slot_ex <= 0; + oam_addr_ex <= 0; + n_ovr <= 0; + ex_ovr <= 0; + spr_counter <= 0; + repeat_count <= 0; + feed_cnt <= 0; + eval_counter <= 0; + oam_bus_ex <= 32'hFFFFFFFF; + end else if (oam_state == STATE_CLEAR) begin // Initialization state + oam_data <= 8'hFF; + + if (~cycle[0]) begin + oam_temp[oam_temp_addr] <= 8'hFF; + // Clear extra sprite space too + oam_temp[oam_temp_addr + 6'd32] <= 8'hFF; + oam_temp_addr <= oam_temp_addr + 1'b1; + end + + // During init, we hunt for the 8th sprite in OAM, so we know where to start for extra sprites + if (~&spr_counter) begin + oam_addr_ex <= oam_addr_ex + 1'd1; + if (scanline[7:0] >= oam[{oam_addr_ex, 2'b00}] && scanline[7:0] < oam[{oam_addr_ex, 2'b00}] + (obj_size ? 16 : 8)) + spr_counter <= spr_counter + 1'b1; + end + end else if (oam_state == STATE_EVAL) begin // Evaluation State + if (evaluating || (visible && PAL)) begin + // This phase has exactly enough cycles to evaluate all 64 sprites if 8 are on the current line, + // so extra sprite evaluation has to be done seperately. + if (&spr_counter && ~ex_ovr) begin + {ex_ovr, oam_addr_ex} <= oam_addr_ex + 7'd1; + if (scanline[7:0] >= oam[{oam_addr_ex, 2'b00}] && + scanline[7:0] < oam[{oam_addr_ex, 2'b00}] + (obj_size ? 16 : 8)) begin + if (oam_temp_slot_ex < 8) begin // Turbo style. + oam_temp_slot_ex <= oam_temp_slot_ex + 1'b1; + oam_temp[{oam_temp_slot_ex, 2'b00} + 6'd32] <= oam[{oam_addr_ex, 2'b00}]; + oam_temp[{oam_temp_slot_ex, 2'b01} + 6'd32] <= oam[{oam_addr_ex, 2'b01}]; + oam_temp[{oam_temp_slot_ex, 2'b10} + 6'd32] <= oam[{oam_addr_ex, 2'b10}]; + oam_temp[{oam_temp_slot_ex, 2'b11} + 6'd32] <= oam[{oam_addr_ex, 2'b11}]; + end + end + end + + //On odd cycles, data is read from (primary) OAM + if (cycle[0]) begin + oam_data <= oam[oam_addr]; + end else begin + if (~n_ovr) begin + if (oam_temp_wren) + oam_temp[{1'b0, oam_temp_slot, oam_addr[1:0]}] <= oam_data; + else + oam_data <= oam_temp[{1'b0, oam_temp_slot, 2'b00}]; + if (~|eval_counter) begin // m is 0 + if (scanline[7:0] >= oam_data && scanline[7:0] < oam_data + (obj_size ? 16 : 8)) begin + if (~oam_temp_wren) + overflow <= 1; + if (~|oam_addr[7:2]) + sprite0_curr <= 1'b1; + eval_counter <= eval_counter + 2'd1; + {n_ovr, oam_addr} <= {1'b0, oam_addr} + 9'd1; // is good, copy + end else begin + if (~oam_temp_wren) begin // Sprite overflow bug emulation + {n_ovr, oam_addr[7:2]} <= oam_addr[7:2] + 7'd1; + oam_addr[1:0] <= oam_addr[1:0] + 2'd1; + end else begin // skip to next sprite + {n_ovr, oam_addr} <= oam_addr + 9'd4; + end + end + end else begin + eval_counter <= eval_counter + 2'd1; + {n_ovr, oam_addr} <= {1'b0, oam_addr} + 9'd1; + + if (&eval_counter) begin // end of copy + if (oam_temp_wren) begin + last_y <= oam[{oam_addr[7:2], 2'b00}]; + last_tile <= oam[{oam_addr[7:2], 2'b01}]; + last_attr <= oam[{oam_addr[7:2], 2'b10}]; + // Check for repeats to see if the game is trying to mask sprites + if (|oam_temp_slot && + last_y == oam[{oam_addr[7:2], 2'b00}] && + last_tile == oam[{oam_addr[7:2], 2'b01}] && + last_attr == oam[{oam_addr[7:2], 2'b10}]) begin + repeat_count <= repeat_count + 3'd1; + end + oam_temp_slot <= oam_temp_slot+ 1'b1; + end else begin + n_ovr <= 1; + end + + if (oam_temp_slot == 7) + oam_temp_wren <= 0; + end + end + end else begin + oam_addr <= {oam_addr[7:2] + 1'd1, 2'b00}; + oam_data <= oam_temp[{1'b0, oam_temp_slot, 2'b00}]; + end + end + // Check if the 9th sprite is a repeat + if (last_y == oam_temp[6'd32] && + last_tile == oam_temp[6'd33] && + last_attr == oam_temp[6'd34] && + cycle == 9'h0FD && repeat_count < 7) + repeat_count <= repeat_count + 3'd1; + end + end else if (oam_state == STATE_FETCH) begin + feed_cnt <= feed_cnt + 1'd1; + + case (feed_cnt[2:0]) + 0: begin // Y Coord + oam_data <= oam_temp[{feed_cnt[6:3], 2'b00}]; + oam_bus_ex <= { + oam_temp[{(feed_cnt[6:3] + 4'd8), 2'b11}], + oam_temp[{(feed_cnt[6:3] + 4'd8), 2'b10}], + oam_temp[{(feed_cnt[6:3] + 4'd8), 2'b01}], + oam_temp[{(feed_cnt[6:3] + 4'd8), 2'b00}] + }; + end + + 1: begin // Tile Num + oam_data <= oam_temp[{feed_cnt[6:3], 2'b01}]; + end + + 2: begin // Attr + oam_data <= oam_temp[{feed_cnt[6:3], 2'b10}]; + end + + 3,4,5,6,7: begin // X Coord + oam_data <= oam_temp[{feed_cnt[6:3], 2'b11}]; + end + endcase + end else begin // STATE_REFRESH + oam_data <= oam_temp[0]; + end + end else begin + oam_data <= oam[oam_addr]; // Keep it available in case it's read + end + + // OAMADDR is set to 0 during each of ticks 257-320 (the sprite tile loading interval) of the pre-render + // and visible scanlines. + if (oam_state == STATE_FETCH && rendering) + oam_addr <= 0; + + // XXX: This delay is nessisary probably because the OAM handling is a cycle early + spr_overflow <= overflow; + + if (is_vbe && cycle == 340) begin + overflow <= 0; + spr_overflow <= 0; + end + + // Writes to OAMDATA during rendering (on the pre-render line and the visible lines 0-239, + // provided either sprite or background rendering is enabled) do not modify values in OAM, + // but do perform a glitchy increment of OAMADDR, bumping only the high 6 bits (i.e., it bumps + // the [n] value in PPU sprite evaluation - it's plausible that it could bump the low bits instead + // depending on the current status of sprite evaluation). This extends to DMA transfers via OAMDMA, + // since that uses writes to $2004. For emulation purposes, it is probably best to completely ignore + // writes during rendering. + if (oam_data_write) begin + if (~rendering) begin + oam[oam_addr] <= (oam_addr[1:0] == 2'b10) ? (oam_din & 8'hE3) : oam_din; // byte 3 has no bits 2-4 + oam_data <= (oam_addr[1:0] == 2'b10) ? (oam_din & 8'hE3) : oam_din; + oam_addr <= oam_addr + 1'b1; + end else begin + oam_addr <= oam_addr + 8'd4; + end + end + + if (oam_addr_write) begin + oam_addr <= oam_din; + end + +end +end // End Always + +endmodule + + +// 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 [8:0] scanline, + 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] }; +wire [7:0] scanline_y = scanline[7:0] - temp; +always @(posedge clk) if (ce) begin + if (load_y) temp_y <= scanline_y[3:0]; + if (load_tile) temp_tile <= temp; + if (load_attr) {flip_y, flip_x, dummy_sprite} <= {temp[7:6], temp[4]}; +end + +endmodule // SpriteAddressGen + + +// Condensed sprite address generator for extra sprites +module SpriteAddressGenEx( + 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 [7:0] scanline, + input [2:0] cycle, // Current load cycle. At #4, first bitmap byte is loaded. At #6, second bitmap byte is. + input [31:0] temp, // Input temp data from SpriteTemp. #0 = Y Coord, #1 = Tile, #2 = Attribs, #3 = X Coord + input [7:0] vram_data, // Byte of VRAM in the specified address + output [12:0] vram_addr,// Low bits of address in VRAM that we'd like to read. + 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. + output use_ex, // If extra sprite address should be used + input masked_sprites +); + +// We keep an odd structure here to maintain compatibility with the existing sprite modules +// which are constrained by the behavior of the original system. +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 [7:0] pix1_latch, pix2_latch; + +wire [7:0] temp_y = scanline[7:0] - temp[7:0]; +wire [7:0] tile = temp[15:8]; +wire [7:0] attr = temp[23:16]; +wire [7:0] temp_x = temp[31:24]; + +wire flip_x = attr[6]; +wire flip_y = attr[7]; +wire dummy_sprite = attr[4]; + +assign use_ex = ~dummy_sprite && ~cycle[2] && ~masked_sprites; + +// 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 || masked_sprites ? 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[3:0] ^ {flip_y, flip_y, flip_y, flip_y}; +assign load = {load_pix1, load_pix2, load_x, load_attr}; +assign load_in = {pix1_latch, pix2_latch, load_temp, load_temp[1:0], load_temp[5]}; + +wire [7:0] load_temp; +always_comb begin + case (cycle) + 0: load_temp = temp_y; + 1: load_temp = tile; + 2: load_temp = attr; + 3,4,5,6,7: load_temp = temp_x; + endcase +end + +// 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 ? tile[0] : obj_patt, + tile[7:1], obj_size ? y_f[3] : tile[0], cycle[1], y_f[2:0] }; +always @(posedge clk) if (ce) begin + if (load_tile) pix1_latch <= vram_f; + if (load_x) pix2_latch <= vram_f; +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, + input reset +); + +reg [5:0] palette [32] = '{ + 6'h00, 6'h01, 6'h00, 6'h01, + 6'h00, 6'h02, 6'h02, 6'h0D, + 6'h08, 6'h10, 6'h08, 6'h24, + 6'h00, 6'h00, 6'h04, 6'h2C, + 6'h09, 6'h01, 6'h34, 6'h03, + 6'h00, 6'h04, 6'h00, 6'h14, + 6'h08, 6'h3A, 6'h00, 6'h02, + 6'h00, 6'h20, 6'h2C, 6'h08 +}; + +// Force read from backdrop channel if reading from any addr 0. +// Do this to the input, not here +//wire [4:0] addr2 = (addr[1:0] == 0) ? 5'd0 : addr; +// If 0x0,4,8,C: mirror every 0x10 +wire [4:0] addr2 = (addr[1:0] == 0) ? {1'b0, addr[3:0]} : addr; +assign dout = palette[addr2]; + +always @(posedge clk) if (reset) + palette <= '{ + 6'h00, 6'h01, 6'h00, 6'h01, + 6'h00, 6'h02, 6'h02, 6'h0D, + 6'h08, 6'h10, 6'h08, 6'h24, + 6'h00, 6'h00, 6'h04, 6'h2C, + 6'h09, 6'h01, 6'h34, 6'h03, + 6'h00, 6'h04, 6'h00, 6'h14, + 6'h08, 6'h3A, 6'h00, 6'h02, + 6'h00, 6'h20, 6'h2C, 6'h08 + }; +else if (ce && write) begin + 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 + inout [1:0] sys_type, // System type. 0 = NTSC 1 = PAL 2 = Dendy 3 = Vs. + 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 reg nmi, // one while inside vblank + output vram_r, // read from vram active + output vram_r_ex, // use extra sprite address + output vram_w, // write to vram active + output [13:0] vram_a, // vram address + output [13:0] vram_a_ex, // vram address for extra sprites + 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, + output reg [2:0] emphasis, + output short_frame, + input extra_sprites, + input [1:0] mask +); + +// 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 + +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; + emphasis = 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 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 + +reg enable_playfield, enable_objects; + +wire rendering_enabled = enable_objects | enable_playfield; + +// 2C02 has an "is_vblank" flag that is true from pixel 0 of line 241 to pixel 0 of line 0; +wire is_rendering = rendering_enabled && (scanline < 240 || is_pre_render_line); +wire is_vbe_sl; + +ClockGen clock( + .clk (clk), + .ce (ce), + .reset (reset), + .sys_type (sys_type), + .is_rendering (rendering_enabled), + .scanline (scanline), + .cycle (cycle), + .is_in_vblank (is_in_vblank), + .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 (short_frame), + .is_vbe_sl (is_vbe_sl) +); + +// The loopy module handles updating of the loopy address +wire [14:0] loopy; +wire [2:0] fine_x_scroll; + +LoopyGen loopy0( + .clk (clk), + .ce (ce), + .is_rendering (is_rendering), + .ain (ain), + .din (din), + .read (read), + .write (write), + .is_pre_render (is_pre_render_line), + .cycle (cycle), + .loopy (loopy), + .fine_x_scroll (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 (clk), + .ce (ce), + .enable (!at_last_cycle_group), + .cycle (cycle[2:0]), + .fine_x_scroll (fine_x_scroll), + .loopy (loopy), + .name_table (bg_name_table), + .vram_data (vram_din), + .pixel (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}; + +wire [31:0] oam_bus_ex; +wire masked_sprites; + +OAMEval spriteeval ( + .clk (clk), + .ce (ce), + .reset (reset), + .rendering_enabled (rendering_enabled), + .obj_size (obj_size), + .scanline (scanline), + .cycle (cycle), + .oam_bus (oam_bus), + .oam_bus_ex (oam_bus_ex), + .oam_addr_write (write && (ain == 3)), + .oam_data_write (write && (ain == 4)), + .oam_din (din), + .spr_overflow (sprite_overflow), + .sprite0 (obj0_on_line), + .is_vbe (is_vbe_sl), + .PAL (sys_type[0]), + .masked_sprites (masked_sprites) +); + + +wire [7:0] oam_bus; +wire sprite_overflow; +wire obj0_on_line; // True if sprite#0 is included on the current 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 (clk), + .ce (ce), + .enabled (cycle[8] && !cycle[6]), // Load sprites between 256..319 + .obj_size (obj_size), + .scanline (scanline), + .obj_patt (obj_patt), // Object size and pattern table + .cycle (cycle[2:0]), // Cycle counter + .temp (is_pre_render_line ? 8'hFF : oam_bus), // Info from temp buffer. + .vram_addr (sprite_vram_addr), // [out] VRAM Address that we want data from + .vram_data (vram_din), // [in] Data at the specified address + .load (spriteset_load), + .load_in (spriteset_load_in) // Which parts of SpriteGen to load +); + +wire [12:0] sprite_vram_addr_ex; +wire [3:0] spriteset_load_ex; +wire [26:0] spriteset_load_in_ex; +wire use_ex; + +SpriteAddressGenEx address_gen_ex( + .clk (clk), + .ce (ce), + .enabled (cycle[8] && !cycle[6]), // Load sprites between 256..319 + .obj_size (obj_size), + .scanline (scanline[7:0]), + .obj_patt (obj_patt), // Object size and pattern table + .cycle (cycle[2:0]), // Cycle counter + .temp (is_pre_render_line ? 32'hFFFFFFFF : oam_bus_ex), // Info from temp buffer. + .vram_addr (sprite_vram_addr_ex), // [out] VRAM Address that we want data from + .vram_data (vram_din), // [in] Data at the specified address + .load (spriteset_load_ex), + .load_in (spriteset_load_in_ex), // Which parts of SpriteGen to load + .use_ex (use_ex), + .masked_sprites (masked_sprites) +); + +// Between 0..255 (256 cycles), draws pixels. +// Between 256..319 (64 cycles), will be populated for next line +SpriteSet sprite_gen( + .clk (clk), + .ce (ce), + .enable (!cycle[8]), + .load (spriteset_load), + .load_in (spriteset_load_in), + .load_ex (spriteset_load_ex), + .load_in_ex (spriteset_load_in_ex), + .bits (obj_pixel_noblank), + .is_sprite0 (is_obj0_pixel), + .extra_sprites (extra_sprites) +); + +// 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 (cycle == 340 && is_vbe_sl) begin + sprite0_hit_bg <= 0; + end 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 +end + +wire [3:0] pixel; +wire pixel_is_obj; + +PixelMuxer pixel_muxer( + .bg (bg_pixel), + .obj (obj_pixel[3:0]), + .obj_prio (obj_pixel[4]), + .out (pixel), + .is_obj (pixel_is_obj) +); + +// VRAM Address Assignment + +assign vram_a_ex = {1'b0, sprite_vram_addr_ex}; + +always_comb begin + if (~is_rendering) begin + vram_a = loopy[13:0]; + vram_r_ex = 0; + end else begin + // Extra sprite fetch override flag + if (cycle[8] && !cycle[6]) + vram_r_ex = use_ex && extra_sprites; + else + vram_r_ex = 0; + + if (cycle[2:1] == 0) + vram_a = {2'b10, loopy[11:0]}; // Name Table + else if (cycle[2:1] == 1) + vram_a = {2'b10, loopy[11:10], 4'b1111, loopy[9:7], loopy[4:2]}; // Attribute table + else if (cycle[8] && !cycle[6]) + vram_a = {1'b0, sprite_vram_addr}; // Sprite data + else + vram_a = {1'b0, bg_patt, bg_name_table, cycle[1], loopy[14:12]}; // Pattern table + end +end + +// Read from VRAM, either when user requested a manual read, or when we're generating pixels. +wire vram_r_ppudata = read && (ain == 7); + +assign vram_r = vram_r_ppudata || 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; +wire [4:0] pram_addr = is_rendering ? + ((|pixel[1:0]) ? {pixel_is_obj, pixel[3:0]} : 5'b00000) : + (is_pal_address ? loopy[4:0] : 5'b0000); + +PaletteRam palette_ram( + .clk (clk), + .reset (reset), + .ce (ce), + .addr (pram_addr), // Read addr + .din (din[5:0]), // Value to write + .dout (color2), // Output color + .write (write && (ain == 7) && is_pal_address) // Condition for writing +); + +wire pal_mask = ~|scanline || cycle < 2 || cycle > 253; +wire auto_mask = (mask == 2'b11) && ~object_clip && ~playfield_clip; +wire mask_left = (cycle < 8) && ((|mask && ~&mask) || auto_mask); +wire mask_right = cycle > 247 && mask == 2'b10; +// PAL/Dendy masks scanline 0 and 2 pixels on each side with black. +wire mask_pal = (|sys_type && pal_mask); +wire [5:0] color1 = (grayscale ? {color2[5:4], 4'b0} : color2); +assign color = (mask_right | mask_left | mask_pal) ? 6'h0E : color1; + +wire clear_nmi = (exiting_vblank | (read && ain == 2)); +wire set_nmi = entering_vblank & ~clear_nmi; + +always @(posedge clk) +if (ce) begin + if (reset) begin + {obj_patt, bg_patt, obj_size, vbl_enable} <= 0; // 2000 resets to 0 + {grayscale, playfield_clip, object_clip, enable_playfield, enable_objects, emphasis} <= 0; // 2001 resets to 0 + nmi_occured <= 0; + end else 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]; + 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]; + emphasis <= |sys_type ? {din[7], din[5], din[6]} : din[7:5]; + end + endcase + end + // https://wiki.nesdev.com/w/index.php/NMI + if (set_nmi) + nmi_occured <= 1; + if (clear_nmi) + 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_ppudata; +end + +// Value currently being written to video ram +assign vram_dout = din; + +// Last data on bus is persistent +reg [7:0] latched_dout; + +reg [23:0] decay_high; +reg [23:0] decay_low; + +reg refresh_high, refresh_low; + +always @(posedge clk) begin + if (refresh_high) begin + decay_high <= 3221590; // aprox 600ms decay rate + refresh_high <= 0; + end + + if (refresh_low) begin + decay_low <= 3221590; + refresh_low <= 0; + end + + if (ce) begin + if (decay_high > 0) + decay_high <= decay_high - 1'b1; + else + latched_dout[7:5] <= 3'b000; + + if (decay_low > 0) + decay_low <= decay_low - 1'b1; + else + latched_dout[4:0] <= 5'b00000; + end + + if (read) begin + case (ain) + 2: begin + latched_dout <= {nmi_occured, + sprite0_hit_bg, + sprite_overflow, + latched_dout[4:0]}; + refresh_high <= 1'b1; + end + + 4: begin + latched_dout <= oam_bus; + refresh_high <= 1'b1; + refresh_low <= 1'b1; + end + + 7: if (is_pal_address) begin + latched_dout <= {latched_dout[7:6], color1}; + refresh_low <= 1'b1; + end else begin + latched_dout <= vram_latch; + refresh_high <= 1'b1; + refresh_low <= 1'b1; + end + default: latched_dout <= latched_dout; + endcase + + if (reset) + latched_dout <= 8'd0; + + end else if (write) begin + refresh_high <= 1'b1; + refresh_low <= 1'b1; + latched_dout <= din; + end +end + +assign dout = latched_dout; + + +assign mapper_ppu_flags = {scanline, cycle, obj_size, is_rendering}; + +endmodule // PPU \ No newline at end of file diff --git a/cores/nes/src/ppu.v b/cores/nes/src/ppu.v deleted file mode 100644 index 40ee2d2..0000000 --- a/cores/nes/src/ppu.v +++ /dev/null @@ -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