Files
Gameboy_MiSTer/rtl/cart.v
paulb-nl 016f45bc7b 3D LUT for color correction. Fix OAM in save state. (#282)
* Move Cart RAM to SDRAM

Used the SDRAM controller from the NES core

* 3D LUT for color correction. Load custom LUT

* Add custom LUTs

* Fix OAM saving to save state
2026-04-13 15:23:58 +08:00

459 lines
13 KiB
Verilog

module cart_top (
input reset,
input clk_sys,
input ce_cpu,
input ce_cpu2x,
input speed,
input megaduck,
input duck_md0_mode,
input [2:0] mapper_sel,
input [14:0] cart_addr,
input cart_a15,
input cart_rd,
input cart_wr,
output [7:0] cart_do,
input [7:0] cart_di, // data from cpu to cart
output cart_oe,
input nCS,
output [22:0] mbc_addr,
output reg dn_write,
output cart_ready,
output is_cram_addr,
output cram_rd,
output cram_wr,
output cram_bram_wr,
output [16:0] cram_addr,
input cart_download,
output [7:0] ram_mask_file,
output [7:0] ram_size,
output has_save,
output bram_save,
output isGBC_game,
output isSGB_game,
input ioctl_download,
input ioctl_wr,
input [24:0] ioctl_addr,
input [15:0] ioctl_dout,
output ioctl_wait,
input bk_wr,
input bk_rtc_wr,
input [16:0] bk_addr,
input [15:0] bk_data,
output [15:0] bk_q,
input [63:0] img_size,
input [7:0] rom_di,
input [15:0] joystick_analog_0,
input ce_32k,
input [32:0] RTC_time,
output [31:0] RTC_timestampOut,
output [47:0] RTC_savedtimeOut,
output RTC_inuse,
input [63:0] SaveStateExt_Din,
input [9:0] SaveStateExt_Adr,
input SaveStateExt_wren,
input SaveStateExt_rst,
output [63:0] SaveStateExt_Dout,
input savestate_load,
input savestate_ovr,
input [19:0] Savestate_CRAMAddr,
input Savestate_CRAMRWrEn,
input [15:0] Savestate_CRAMWriteData,
output [15:0] Savestate_CRAM_Q,
output rumbling
);
///////////////////////////////////////////////////
// http://fms.komkon.org/GameBoy/Tech/Carts.html
// 32MB SDRAM memory map using word addresses
// 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 D
// 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 S
// -------------------------------------------------
// 0 0 0 0 X X X X X X X X X X X X X X X X X X X X X up to 2MB used as ROM (MBC1-3), 8MB for MBC5
// 0 0 0 0 R R B B B B B C C C C C C C C C C C C C C MBC1 ROM (R=RAM bank in mode 0)
wire [15:0] SS_Ext;
wire [15:0] SS_Ext_BACK;
wire [63:0] SS_Ext2;
wire [63:0] SS_Ext2_BACK;
wire [63:0] SaveStateBus_Dout_or[0:1];
eReg_SavestateV #(0, 32, 15, 0, 64'h0000000000000001) iREG_SAVESTATE_Ext (clk_sys, SaveStateExt_Din, SaveStateExt_Adr, SaveStateExt_wren, SaveStateExt_rst, SaveStateBus_Dout_or[0], SS_Ext_BACK, SS_Ext);
eReg_SavestateV #(0, 37, 63, 0, 64'h0000000000000000) iREG_SAVESTATE_Ext2 (clk_sys, SaveStateExt_Din, SaveStateExt_Adr, SaveStateExt_wren, SaveStateExt_rst, SaveStateBus_Dout_or[1], SS_Ext2_BACK, SS_Ext2);
assign SaveStateExt_Dout = SaveStateBus_Dout_or[0] | SaveStateBus_Dout_or[1];
wire [7:0] rom_do;
wire [7:0] mbc_cram_do;
wire [16:0] mbc_cram_addr;
wire mbc_ram_enable, mbc_battery;
wire mbc_cram_wr;
wire [7:0] mbc_cram_wr_do;
mappers mappers (
.reset ( reset ),
.clk_sys ( clk_sys ),
.ce_cpu ( ce_cpu ),
.ce_cpu2x ( ce_cpu2x ),
.speed ( speed ),
.mbc1 ( mbc1 ),
.mbc1m ( mbc1m ),
.mbc2 ( mbc2 ),
.mbc3 ( mbc3 ),
.mbc30( mbc30 ),
.mbc5 ( mbc5 ),
.mbc6 ( mbc6 ),
.mbc7 ( mbc7 ),
.mmm01 ( mmm01 ),
.huc1 ( HuC1 ),
.huc3 ( HuC3 ),
.gb_camera ( gb_camera ),
.tama ( tama ),
.rocket ( rocket ),
.sachen ( sachen),
.wisdom_tree ( wisdom_tree ),
.mani161 ( mani161 ),
.megaduck ( megaduck_en ),
.duck_md0_mode ( duck_md0_mode ),
.isGBC_game ( isGBC_game ),
.joystick_analog_0 ( joystick_analog_0 ),
.ce_32k ( ce_32k ),
.RTC_time ( RTC_time ),
.RTC_timestampOut ( RTC_timestampOut ),
.RTC_savedtimeOut ( RTC_savedtimeOut ),
.RTC_inuse ( RTC_inuse ),
.bk_wr ( bk_wr ),
.bk_rtc_wr ( bk_rtc_wr ),
.bk_addr ( bk_addr ),
.bk_data ( bk_data ),
.img_size ( img_size ),
.savestate_load ( savestate_load ),
.savestate_data ( SS_Ext ),
.savestate_back ( SS_Ext_BACK ),
.savestate_data2 ( SS_Ext2 ),
.savestate_back2 ( SS_Ext2_BACK ),
.has_ram ( |ram_size ),
.ram_mask ( ram_mask ),
.rom_mask ( rom_mask ),
.cart_addr ( cart_addr ),
.cart_a15 ( cart_a15 ),
.cart_mbc_type ( cart_mbc_type ),
.cart_rd ( cart_rd ),
.cart_wr ( cart_wr ),
.cart_di ( cart_di ),
.cart_oe ( cart_oe ),
.rom_di ( rom_di ),
.rom_do ( rom_do ),
.nCS ( nCS ),
.cram_rd ( cram_rd ),
.cram_di ( bram_save ? cram_q : rom_di ),
.cram_do ( mbc_cram_do ),
.cram_addr ( mbc_cram_addr ),
.cram_wr_do ( mbc_cram_wr_do ),
.cram_wr ( mbc_cram_wr ),
.mbc_addr ( mbc_addr ),
.ram_enabled ( mbc_ram_enable ),
.has_battery ( mbc_battery ),
.rumbling ( cart_rumbling )
);
// extract header fields extracted from cartridge
// during download
reg [7:0] cart_mbc_type;
reg [7:0] cart_rom_size;
reg [7:0] cart_ram_size;
reg cart_cgb_flag;
reg [7:0] cart_sgb_flag;
reg [7:0] cart_old_licensee;
reg [15:0] cart_logo_data[0:7];
// RAM size
wire [3:0] ram_mask = // 0 - no ram
(ram_size == 1)?4'b0000: // 1 - 2k, 1 bank
(ram_size == 2)?4'b0000: // 2 - 8k, 1 bank
(ram_size == 3)?4'b0011: // 3 - 32k, 4 banks
(ram_size == 5)?4'b0111: // 5 - 64k, 8 banks
4'b1111; // 4 - 128k 16 banks
// ROM size
/*
wire [8:0] rom_mask =
(cart_rom_size == 0)? 9'b000000001: // 0 - 2 banks, 32k direct mapped
(cart_rom_size == 1)? 9'b000000011: // 1 - 4 banks = 64k
(cart_rom_size == 2)? 9'b000000111: // 2 - 8 banks = 128k
(cart_rom_size == 3)? 9'b000001111: // 3 - 16 banks = 256k
(cart_rom_size == 4)? 9'b000011111: // 4 - 32 banks = 512k
(cart_rom_size == 5)? 9'b000111111: // 5 - 64 banks = 1M
(cart_rom_size == 6)? 9'b001111111: // 6 - 128 banks = 2M
(cart_rom_size == 7)? 9'b011111111: // 7 - 256 banks = 4M
(cart_rom_size == 8)? 9'b111111111: // 8 - 512 banks = 8M
(cart_rom_size == 82)?9'b001111111: //$52 - 72 banks = 1.1M
(cart_rom_size == 83)?9'b001111111: //$53 - 80 banks = 1.2M
(cart_rom_size == 84)?9'b001111111:
9'b001111111; //$54 - 96 banks = 1.5M
*/
reg [8:0] rom_mask; // get mask from file size
wire mbc1 = (mapper_sel_r == 3'd3) || (cart_mbc_type == 1) || (cart_mbc_type == 2) || (cart_mbc_type == 3);
wire mbc2 = (cart_mbc_type == 5) || (cart_mbc_type == 6);
//wire mmm01 = (cart_mbc_type == 11) || (cart_mbc_type == 12) || (cart_mbc_type == 13) || (cart_mbc_type == 14);
wire mbc3 = (mapper_sel_r == 3'd4) || (cart_mbc_type == 15) || (cart_mbc_type == 16) || (cart_mbc_type == 17) || (cart_mbc_type == 18) || (cart_mbc_type == 19);
wire mbc30 = mbc3 && ( (cart_rom_size == 7) || (ram_size == 5) );
//wire mbc4 = (cart_mbc_type == 21) || (cart_mbc_type == 22) || (cart_mbc_type == 23);
wire mbc5 = (cart_mbc_type == 25) || (cart_mbc_type == 26) || (cart_mbc_type == 27) || (cart_mbc_type == 28) || (cart_mbc_type == 29) || (cart_mbc_type == 30);
wire mbc6 = (cart_mbc_type == 32);
wire mbc7 = (cart_mbc_type == 34);
wire rocket = (cart_mbc_type == 151) || (cart_mbc_type == 153);
wire megaduck_en = (cart_mbc_type == 250); // Use a wire to ensure enable is low while loading
wire duck_md0_cart = megaduck_en && duck_md0_mode;
// MD0 system ROM/laptop carts expose the 32KB external memory cart.
assign ram_size = duck_md0_cart ? 8'd3 : cart_ram_size;
wire gb_camera = (cart_mbc_type == 252);
wire tama = (cart_mbc_type == 253);
wire HuC3 = (cart_mbc_type == 254);
wire HuC1 = (cart_mbc_type == 255);
wire wisdom_tree = (mapper_sel_r == 3'd1);
wire mani161 = (mapper_sel_r == 3'd2);
wire has_rumble = (cart_mbc_type[7:2] == 6'b0001_11);
wire cart_rumbling;
assign rumbling = has_rumble & cart_rumbling;
assign isGBC_game = (cart_cgb_flag);
assign isSGB_game = (cart_sgb_flag == 8'h03 && cart_old_licensee == 8'h33);
// MBC1M detect
reg [7:0] cart_logo_check;
reg [2:0] cart_logo_idx;
reg mbc1m, mmm01;
reg mbc1m_check_end, mmm01_check_end;
reg sachen, sachen_t1, sachen_t2;
wire mbc1m_bank = (~|ioctl_addr[24:20] && ioctl_addr[19:12] == 8'h40); // $40000
wire mmm01_bank = (ioctl_addr[18:12] == 7'h78); // $78000+
wire cart_logo_match = &cart_logo_check;
reg old_cart_download;
reg [2:0] mapper_sel_r;
always @(posedge clk_sys) begin
old_cart_download <= cart_download;
if(~old_cart_download & cart_download) begin
cart_logo_idx <= 3'd0;
cart_logo_check <= 8'd0;
cart_mbc_type <= 8'd0;
mbc1m <= 0;
mmm01 <= 0;
{ sachen, sachen_t1, sachen_t2 } <= 0;
mapper_sel_r <= mapper_sel;
rom_mask <= 9'd0;
end
if(cart_download & ioctl_wr) begin
rom_mask <= ioctl_addr[22:14] | rom_mask;
if (megaduck) begin
cart_cgb_flag <= 0;
cart_sgb_flag <= 0;
mapper_sel_r <= 0;
if (ioctl_addr > 'h100) // Make sure there's time to reset the banks in the mapper
cart_mbc_type <= 8'd250;
end else begin
if (~|ioctl_addr[24:12] || (mmm01_bank & mmm01) ) // MMM01 header is at the end of ROM
case(ioctl_addr[11:0])
12'h142: cart_cgb_flag <= ioctl_dout[15];
12'h146: begin
{cart_mbc_type, cart_sgb_flag} <= ioctl_dout;
// "Mani 4 in 1" have incorrectly set MBC3 in the header
if ( mmm01 && ioctl_dout[15:8] == 8'h11) cart_mbc_type <= 8'h0B;
end
12'h148: { cart_ram_size, cart_rom_size } <= ioctl_dout;
12'h14a: { cart_old_licensee } <= ioctl_dout[15:8];
endcase
// Disable other mappers when a specific mapper has been selected
if (|mapper_sel_r) begin
cart_mbc_type <= 8'd0;
mbc1m <= 0;
mmm01 <= 0;
sachen <= 0;
end else begin
// Sachen
if (~|ioctl_addr[24:12]) begin
case(ioctl_addr[11:0])
12'h100: sachen_t1 <= (ioctl_dout[15:8] != 8'hC3);
12'h140: sachen_t2 <= (ioctl_dout[ 7:0] == 8'hC3); // 0xC3 opcode is at $140 instead of $101
12'h150: begin /// CGB flag
if (sachen_t1 & sachen_t2) begin
sachen <= 1'b1;
cart_cgb_flag <= ioctl_dout[15];
cart_mbc_type <= 0;
cart_sgb_flag <= 0;
cart_ram_size <= 0;
cart_old_licensee <= 0;
end
end
endcase
end
//Store cart logo data
if (ioctl_addr >= 'h104 && ioctl_addr <= 'h112) begin
cart_logo_data[cart_logo_idx] <= ioctl_dout;
cart_logo_idx <= cart_logo_idx + 1'b1;
end
// MBC1 Multicart detect: Compare 8 words of logo data at second 256KByte bank ($40000)
// MMM01 detect: Compare the last bank every 512KByte ($78000+)
if ( mbc1m_bank | mmm01_bank ) begin
if (ioctl_addr[11:0] >= 12'h104 && ioctl_addr[11:0] <= 12'h112) begin
cart_logo_check[cart_logo_idx] <= (ioctl_dout == cart_logo_data[cart_logo_idx]);
cart_logo_idx <= cart_logo_idx + 1'b1;
if (&cart_logo_idx) begin
if (mbc1m_bank) mbc1m_check_end <= 1;
if (mmm01_bank) mmm01_check_end <= 1;
end
end
end
if (mbc1m_check_end) begin
mbc1m_check_end <= 0;
mbc1m <= cart_logo_match;
end
if (mmm01_check_end) begin
mmm01_check_end <= 0;
mmm01 <= cart_logo_match;
if (cart_logo_match) mbc1m <= 0;
end
end
end
end
end
reg cart_ready_r = 0;
reg ioctl_wait_r;
always @(posedge clk_sys) begin
if(ioctl_wr) ioctl_wait_r <= 1;
if(speed?ce_cpu2x:ce_cpu) begin
dn_write <= ioctl_wait_r;
if(dn_write) {ioctl_wait_r, dn_write} <= 0;
if(dn_write) cart_ready_r <= 1;
end
end
assign cart_ready = cart_ready_r;
assign ioctl_wait = ioctl_wait_r;
reg [7:0] cart_do_r;
always @* begin
if (~cart_ready)
cart_do_r = 8'h00;
else if (is_cram_addr)
cart_do_r = mbc_cram_do;
else
cart_do_r = rom_do;
end
assign cart_do = cart_do_r;
wire [7:0] cram_q_h, cram_q_l;
wire [7:0] cram_q = cram_addr[0] ? cram_q_h : cram_q_l;
assign is_cram_addr = ~nCS & ~cart_addr[14];
assign cram_rd = cart_rd & is_cram_addr;
assign cram_wr = savestate_ovr ? Savestate_CRAMRWrEn : (cart_wr & is_cram_addr & mbc_ram_enable);
assign cram_bram_wr = savestate_ovr ? (Savestate_CRAMRWrEn & ~|Savestate_CRAMAddr[16:8]) : mbc_cram_wr;
assign cram_addr = savestate_ovr ? Savestate_CRAMAddr[16:0] : mbc_cram_addr;
wire [7:0] cram_di_l = savestate_ovr ? Savestate_CRAMWriteData[ 7:0] : mbc_cram_wr_do;
wire [7:0] cram_di_h = savestate_ovr ? Savestate_CRAMWriteData[15:8] : mbc_cram_wr_do;
assign Savestate_CRAM_Q = { cram_q_h, cram_q_l };
assign bram_save = mbc7 | tama;
// RAM size
assign ram_mask_file = // 0 - no ram
(mbc2 || mbc7 || tama)?8'h01: // mbc2 512x4bits, mbc7 256 bytes EEPROM, TAMA 32 bytes
(ram_size == 1)?8'h03: // 1 - 2k, 1 bank sd_lba[1:0]
(ram_size == 2)?8'h0F: // 2 - 8k, 1 bank sd_lba[3:0]
(ram_size == 3)?8'h3F: // 3 - 32k, 4 banks sd_lba[5:0]
(ram_size == 5)?8'h7F: // 5 - 64k, 8 banks sd_lba[6:0]
8'hFF; // 4 - 128k 16 banks sd_lba[7:0] 1111
assign has_save = mbc_battery && (|ram_size || mbc2 || mbc7 || tama);
dpram #(7) cram_bram_l (
.clock_a (clk_sys),
.address_a (cram_addr[7:1]),
.wren_a (cram_bram_wr & (~cram_addr[0] | savestate_ovr)),
.data_a (cram_di_l),
.q_a (cram_q_l),
.clock_b (clk_sys),
.address_b (bk_addr[6:0]),
.wren_b (bk_wr & ~|bk_addr[16:7]),
.data_b (bk_data[7:0]),
.q_b (bk_q[7:0])
);
dpram #(7) cram_bram_h (
.clock_a (clk_sys),
.address_a (cram_addr[7:1]),
.wren_a (cram_bram_wr & (cram_addr[0] | savestate_ovr)),
.data_a (cram_di_h),
.q_a (cram_q_h),
.clock_b (clk_sys),
.address_b (bk_addr[6:0]),
.wren_b (bk_wr & ~|bk_addr[16:7]),
.data_b (bk_data[15:8]),
.q_b (bk_q[15:8])
);
endmodule