Files
Gameboy_MiSTer/rtl/gb.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

1156 lines
37 KiB
Verilog

//
// gb.v
//
// Gameboy for the MIST board https://github.com/mist-devel
//
// Copyright (c) 2015 Till Harbaum <till@harbaum.org>
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
module gb (
input reset,
input clk_sys,
input ce,
input ce_n,
input ce_2x,
input [7:0] joystick,
input isGBC,
input real_cgb_boot,
input isSGB,
input extra_spr_en,
// cartridge interface
// can adress up to 1MB ROM
output [14:0] ext_bus_addr,
output ext_bus_a15,
output cart_rd,
output cart_wr,
input [7:0] cart_do,
output [7:0] cart_di,
input cart_oe,
// WRAM or Cart RAM CS
output nCS,
output sdram_rd,
input cgb_boot_download,
input dmg_boot_download,
input sgb_boot_download,
input ioctl_wr,
input [24:0] ioctl_addr,
input [15:0] ioctl_dout,
// Bootrom features
input boot_gba_en,
input fast_boot_en,
// audio
output [15:0] audio_l,
output [15:0] audio_r,
input audio_no_pops,
// Megaduck?
input megaduck,
// lcd interface
output lcd_clkena,
output [14:0] lcd_data,
output [1:0] lcd_data_gb,
output [1:0] lcd_mode,
output lcd_on,
output lcd_vsync,
output [1:0] joy_p54,
input [3:0] joy_din,
output speed, //GBC
output DMA_on,
input gg_reset,
input gg_en,
input [128:0] gg_code,
output gg_available,
//serial port
output sc_int_clock2,
input serial_clk_in,
output serial_clk_out,
input serial_data_in,
output serial_data_out,
// savestates
input increaseSSHeaderCount,
input [7:0] cart_ram_size,
input save_state,
input load_state,
input [1:0] savestate_number,
output sleep_savestate,
output savestate_ovr,
output [63:0] SaveStateExt_Din,
output [9:0] SaveStateExt_Adr,
output SaveStateExt_wren,
output SaveStateExt_rst,
input [63:0] SaveStateExt_Dout,
output SaveStateExt_load,
output [19:0] Savestate_CRAMAddr,
output Savestate_CRAMRdEn,
output Savestate_CRAMRWrEn,
output [15:0] Savestate_CRAMWriteData,
input [15:0] Savestate_CRAMReadData,
output [63:0] SAVE_out_Din, // data read from savestate
input [63:0] SAVE_out_Dout, // data written to savestate
output [25:0] SAVE_out_Adr, // all addresses are DWORD addresses!
output SAVE_out_rnw, // read = 1, write = 0
output SAVE_out_ena, // one cycle high for each action
output [7:0] SAVE_out_be,
input SAVE_out_done, // should be one cycle high when write is done or read value is valid
input savestate_sdram_busy,
input rewind_on,
input rewind_active
);
// savestates
wire [63:0] SaveStateBus_Din;
wire [9:0] SaveStateBus_Adr;
wire SaveStateBus_wren, SaveStateBus_rst;
wire [15:0] Savestate_RAMWriteData;
wire [7:0] Savestate_RAMReadData_WRAM, Savestate_RAMReadData_VRAM, Savestate_RAMReadData_ORAM, Savestate_RAMReadData_ZRAM;
wire [19:0] Savestate_RAMAddr;
wire [4:0] Savestate_RAMRdEn;
wire [4:0] Savestate_RAMRWrEn;
localparam SAVESTATE_MODULES = 8;
wire [63:0] SaveStateBus_wired_or[0:SAVESTATE_MODULES-1];
wire [56:0] SS_Top;
wire [56:0] SS_Top_BACK;
eReg_SavestateV #(0, 31, 56, 0, 64'h0000000000800000) iREG_SAVESTATE_Top (clk_sys, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[6], SS_Top_BACK, SS_Top);
wire [10:0] SS_Top2;
wire [10:0] SS_Top2_BACK;
eReg_SavestateV #(0, 38, 10, 0, 64'h0000000000000000) iREG_SAVESTATE_Top2 (clk_sys, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[7], SS_Top2_BACK, SS_Top2);
// include cpu
reg boot_rom_enabled;
reg [1:0] ff4c_key0; // GBC DMG mode register
wire isGBC_mode = !ff4c_key0 | boot_rom_enabled;
wire [15:0] cpu_addr;
wire [7:0] cpu_do;
wire apu_framecount_en;
wire sel_timer = (cpu_addr[15:4] == 12'hff0) && (cpu_addr[3:2] == 2'b01);
wire sel_video_reg = (cpu_addr[15:4] == 12'hff4) || (isGBC && (cpu_addr[15:4] == 12'hff6) && (cpu_addr[3:0] >= 4'h8 && cpu_addr[3:0] <= 4'hC)); //video and oam dma (+ ff68-ff6C when gbc)
wire sel_video_oam = cpu_addr[15:8] == 8'hfe;
wire sel_joy = cpu_addr == 16'hff00; // joystick controller
wire sel_sb = cpu_addr == 16'hff01; // serial SB - Serial transfer data
wire sel_sc = cpu_addr == 16'hff02; // SC - Serial Transfer Control (R/W)
wire sel_rom = !cpu_addr[15]; // lower 32k are rom
wire sel_cram = cpu_addr[15:13] == 3'b101; // 8k cart ram at $a000
wire sel_vram = cpu_addr[15:13] == 3'b100; // 8k video ram at $8000
wire sel_ie = cpu_addr == 16'hffff; // interupt enable
wire sel_if = cpu_addr == 16'hff0f; // interupt flag
wire sel_wram = (cpu_addr[15:14] == 2'b11) && (~&cpu_addr[13:9]); // 8k internal ram at $C000-DFFF. C000-DDFF mirrored to E000-FDFF
wire sel_zpram = (cpu_addr[15:7] == 9'b111111111) && // 127 bytes zero pageram at $ff80
(cpu_addr != 16'hffff);
wire sel_audio = (cpu_addr[15:8] == 8'hff) && // audio reg ff10 - ff3f and ff76/ff77 PCM12/PCM34 (undocumented registers)
((cpu_addr[7:5] == 3'b001) || (cpu_addr[7:4] == 4'b0001) || (cpu_addr[7:0] == 8'h76) || (cpu_addr[7:0] == 8'h77));
wire sel_ext_bus = sel_rom | sel_cram | sel_wram;
wire sel_boot_rom, sel_boot_rom_cgb;
// unused cgb registers
wire sel_FF72 = isGBC && cpu_addr == 16'hff72; // unused register, all bits read/write
wire sel_FF73 = isGBC && cpu_addr == 16'hff73; // unused register, all bits read/write
wire sel_FF74 = isGBC && isGBC_mode && (cpu_addr == 16'hff74); // unused register, all bits read/write, only in CGB mode
wire sel_FF75 = isGBC && cpu_addr == 16'hff75; // unused register, bits 4-6 read/write
// Special MiSTer register for signalling to cpu bootrom
wire sel_FF50 = boot_rom_enabled && cpu_addr == 16'hff50;
wire ext_bus_wram_sel, ext_bus_cram_sel, ext_bus_rom_sel;
wire ext_bus_rd, ext_bus_wr;
wire [7:0] ext_bus_di;
wire cart_sel;
// From Game Boy: Complete Technical Reference:
// DMA register value, Selected bus, Asserted CS:
// 0x00-0x7F, external bus, external ROM (A15)
// 0x80-0x9F, video RAM bus, video RAM (MCS)
// 0xA0-0xFF, external bus, external RAM (CS)
wire [15:0] dma_addr;
wire dma_sel_rom = ~dma_addr[15]; // lower 32k are rom
wire dma_sel_vram = dma_addr[15:13] == 3'b100; // 8k video ram at $8000-$9FFF
wire dma_sel_ext_bus_dmg = ~dma_sel_vram; // WRAM or Cart ROM/RAM
wire dma_sel_ext_ram = ~dma_sel_rom; // External RAM CS
//CGB
wire dma_sel_cram = dma_addr[15:13] == 3'b101; // 8k cart ram at $a000
wire dma_sel_wram = dma_addr[15:13] == 3'b110; // 8k WRAM at $c000-$dfff
wire dma_sel_ext_bus_cgb = dma_sel_rom | dma_sel_cram; // Cart ROM/RAM
wire dma_sel_ext_bus = isGBC ? dma_sel_ext_bus_cgb : dma_sel_ext_bus_dmg;
//CGB
wire sel_vram_bank = (cpu_addr==16'hff4f);
wire sel_wram_bank = isGBC && isGBC_mode && (cpu_addr==16'hff70);
wire sel_hdma = isGBC && isGBC_mode && (cpu_addr[15:4]==12'hff5) &&
((cpu_addr[3:0]!=4'd0)&&(cpu_addr[3:0]< 4'd6)); //HDMA FF51-FF55
wire sel_key0 = isGBC && boot_rom_enabled && (cpu_addr == 16'hff4c); // KEY0 - CGB / DMG mode register
wire sel_key1 = isGBC && isGBC_mode && (cpu_addr == 16'hff4d); // KEY1 - CGB Mode Only - Prepare Speed Switch
wire sel_rp = isGBC && isGBC_mode && (cpu_addr == 16'hff56); //FF56 - RP - CGB Mode Only - Infrared Communications Port
//HDMA can select from $0000 to $7ff0 or A000-DFF0
wire [15:0] hdma_source_addr;
wire hdma_sel_rom = !hdma_source_addr[15]; // lower 32k are rom
wire hdma_sel_cram = hdma_source_addr[15:13] == 3'b101; // 8k cart ram at $a000
wire hdma_sel_wram = hdma_source_addr[15:13] == 3'b110; // 8k WRAM at $c000-$dff0
wire hdma_sel_ext_bus = hdma_sel_rom | hdma_sel_cram;
wire sc_start;
wire sc_shiftclock;
wire [7:0] sc_r = {sc_start,6'h3F,sc_shiftclock};
wire irq_ack;
wire [7:0] irq_vec;
reg [4:0] if_r;
reg [7:0] ie_r; // writing $ffff sets the irq enable mask
wire irq_n;
reg [2:0] wram_bank; //1-7 FF70 - SVBK
reg vram_bank; //0-1 FF4F - VBK
wire [7:0] hdma_do;
wire hdma_active;
wire hdma_rd_clk;
reg cpu_speed; // - 0 Normal mode (4MHz) - 1 Double Speed Mode (8MHz)
reg prepare_switch; // set to 1 to toggle speed
wire oam_cpu_allow;
wire vram_cpu_allow;
wire [7:0] joy_do;
wire [7:0] sb_o;
wire [7:0] timer_do;
wire [7:0] video_do;
wire [7:0] audio_do;
wire [7:0] boot_do;
wire [7:0] vram_do;
wire [7:0] vram1_do;
wire [7:0] zpram_do;
wire [7:0] wram_do;
reg[7:0] FF72;
reg[7:0] FF73;
reg[7:0] FF74;
reg[2:0] FF75;
// http://gameboy.mongenel.com/dmg/asmmemmap.html
wire [7:0] cpu_di =
irq_ack?irq_vec:
sel_if?{3'b111, if_r}: // interrupt flag register
sel_rp?8'h02:
sel_wram_bank?{5'h1f,wram_bank}:
isGBC&&sel_vram_bank?{7'h7f,vram_bank}:
sel_hdma?{hdma_do}: //hdma GBC
sel_key0 ? { 4'hF, ff4c_key0, 2'b10 } :
sel_key1?{cpu_speed,6'h3f,prepare_switch}: //key1 cpu speed register(GBC)
sel_joy?joy_do: // joystick register
sel_sb?sb_o: // serial transfer data register
sel_sc?sc_r: // serial transfer control register
sel_timer?timer_do: // timer registers
sel_video_reg?video_do: // video registers
(sel_video_oam&&oam_cpu_allow)?video_do: // video object attribute memory
sel_audio?audio_do: // audio registers
sel_boot_rom?boot_do: // boot rom
isGBC&sel_wram ? wram_do: // wram on GBC
sel_ext_bus?ext_bus_di: // wram (DMG) + cartridge rom/ram
(sel_vram&&vram_cpu_allow)?(isGBC&&vram_bank)?vram1_do:vram_do: // vram (GBC bank 0+1)
sel_zpram?zpram_do: // zero page ram
sel_ie?ie_r: // interrupt enable register
sel_FF72?FF72: // unused register, all bits read/write
sel_FF73?FF73: // unused register, all bits read/write
sel_FF74?FF74: // unused register, all bits read/write, only in CGB mode
sel_FF75?{1'b1,FF75, 4'b1111}: // unused register, bits 4-6 read/write
sel_FF50?{6'b0, fast_boot_en, boot_gba_en}: // MiSTer special instruction register
8'hff;
wire cpu_wr_n;
wire cpu_rd_n;
wire cpu_iorq_n;
wire cpu_m1_n;
wire cpu_mreq_n;
wire cpu_phi_early;
wire clk = clk_sys & ce;
wire ce_cpu = cpu_speed ? ce_2x:ce;
wire clk_cpu = clk_sys & ce_cpu;
// when hdma is enabled stop CPU (GBC). Finish read/write before stopping CPU
wire hdma_cpu_stop = (isGBC & hdma_active & cpu_rd_n & cpu_wr_n);
wire cpu_clken = ~hdma_cpu_stop & ce_cpu;
assign sdram_rd = ~cpu_phi_early | hdma_rd_clk; // For SDRAM read & refresh
reg reset_r = 1;
wire reset_ss;
//sync reset with clock
always @ (posedge clk) begin
reset_r <= reset;
end
reg old_cpu_wr_n;
assign SS_Top_BACK[54] = old_cpu_wr_n;
always @(posedge clk_sys) begin
if(reset_ss) old_cpu_wr_n <= SS_Top[54]; // 1'b0
else if (ce_cpu) old_cpu_wr_n <= cpu_wr_n;
end
wire cpu_wr_n_edge = ~(old_cpu_wr_n & ~cpu_wr_n);
wire cpu_stop;
wire genie_ovr;
wire [7:0] genie_data;
wire [15:0] cpu_addr_raw;
wire [7:0] snd_d_in;
wire [7:0] snd_d_out;
megaduck_swizzle md_swizz
(
.megaduck (megaduck),
.a_in (cpu_addr_raw),
.a_out (cpu_addr),
.snd_in_di (cpu_do),
.snd_in_do (snd_d_in),
.snd_out_di (snd_d_out),
.snd_out_do (audio_do)
);
GBse cpu (
.RESET_n ( !reset_ss ),
.CLK_n ( clk_sys ),
.CLKEN ( cpu_clken ),
.WAIT_n ( 1'b1 ),
.INT_n ( irq_n ),
.NMI_n ( 1'b1 ),
.BUSRQ_n ( 1'b1 ),
.M1_n ( cpu_m1_n ),
.MREQ_n ( cpu_mreq_n ),
.IORQ_n ( cpu_iorq_n ),
.RD_n ( cpu_rd_n ),
.WR_n ( cpu_wr_n ),
.RFSH_n ( ),
.HALT_n ( ),
.BUSAK_n ( ),
.A ( cpu_addr_raw ),
.DI ( genie_ovr ? genie_data : cpu_di),
.DO ( cpu_do ),
.STOP ( cpu_stop ),
.phi_early ( cpu_phi_early ),
.isGBC ( isGBC ),
// savestates
.SaveStateBus_Din (SaveStateBus_Din ),
.SaveStateBus_Adr (SaveStateBus_Adr ),
.SaveStateBus_wren (SaveStateBus_wren),
.SaveStateBus_rst (SaveStateBus_rst ),
.SaveStateBus_Dout (SaveStateBus_wired_or[0])
);
// --------------------------------------------------------------------
// --------------------------- Cheat Engine ---------------------------
// --------------------------------------------------------------------
CODES codes (
.clk (clk_sys),
.reset (gg_reset),
.enable (gg_en),
.addr_in (cpu_addr),
.data_in (cpu_di),
.available (gg_available),
.code (gg_code),
.genie_ovr (genie_ovr),
.genie_data (genie_data)
);
// --------------------------------------------------------------------
// --------------------- GBC/DMG mode KEY0 (GBC) ----------------------
// --------------------------------------------------------------------
assign SS_Top_BACK[56:55] = ff4c_key0;
// KEY0 FF4C CPU mode register
// Bits 2 and 3 CPU mode select
// 00: CGB mode (Mode used by carts supporting CGB)
// 01: DMG/MGB mode (Mode used by DMG/MGB-only carts)
//10: PGB1 mode (STOP the CPU, with the LCD operated by an external signal)
// 11: PGB2 mode (CPU still running, with the LCD operated by an external signal)
// R/W only when boot rom is enabled
// https://forums.nesdev.org/viewtopic.php?t=19888
always @(posedge clk_sys) begin
if(reset_ss) begin
ff4c_key0 <= SS_Top[56:55]; // 2'd0;
end else if(sel_key0 & ce_cpu & ~cpu_wr_n_edge) begin
ff4c_key0 <= cpu_do[3:2];
end
end
// --------------------------------------------------------------------
// --------------------- Speed Toggle KEY1 (GBC)-----------------------
// --------------------------------------------------------------------
assign speed = cpu_speed;
assign SS_Top_BACK[3] = cpu_speed;
assign SS_Top_BACK[4] = prepare_switch;
always @(posedge clk_sys) begin
if(reset_ss) begin
cpu_speed <= SS_Top[3]; // 1'b0;
prepare_switch <= SS_Top[4]; // 1'b0;
end
else if (ce_2x && sel_key1 && !cpu_wr_n_edge)begin
prepare_switch <= cpu_do[0];
end
if (ce_2x && isGBC && prepare_switch && cpu_stop) begin
cpu_speed <= !cpu_speed;
prepare_switch <= 1'b0;
end
end
// --------------------------------------------------------------------
// ------------------------------ audio -------------------------------
// --------------------------------------------------------------------
wire audio_rd = !cpu_rd_n && sel_audio;
wire audio_wr = !cpu_wr_n && sel_audio;
gbc_snd audio (
.clk ( clk_sys ),
.ce ( ce ),
.reset ( reset_ss ),
.apu_framecount_en ( apu_framecount_en ),
.is_gbc ( isGBC ),
.remove_pops ( audio_no_pops ),
.s1_read ( audio_rd ),
.s1_write ( audio_wr ),
.s1_addr ( cpu_addr[6:0] ),
.s1_readdata ( snd_d_out ),
.s1_writedata ( snd_d_in ),
.snd_left ( audio_l ),
.snd_right ( audio_r ),
.SaveStateBus_Din (SaveStateBus_Din ),
.SaveStateBus_Adr (SaveStateBus_Adr ),
.SaveStateBus_wren (SaveStateBus_wren),
.SaveStateBus_rst (SaveStateBus_rst ),
.SaveStateBus_Dout (SaveStateBus_wired_or[5])
);
// --------------------------------------------------------------------
// -----------------------serial port()--------------------------------
// --------------------------------------------------------------------
// SNAC
wire serial_irq;
assign sc_int_clock2 = sc_shiftclock;
link link (
.clk_sys(clk_sys),
.ce(ce_cpu),
.rst(reset_ss),
.sel_sc(sel_sc),
.sel_sb(sel_sb),
.cpu_wr_n(cpu_wr_n_edge),
.sc_start_in(cpu_do[7]),
.sc_int_clock_in(cpu_do[0]),
.sb_in(cpu_do),
.serial_clk_in(serial_clk_in),
.serial_data_in(serial_data_in),
.serial_clk_out(serial_clk_out),
.serial_data_out(serial_data_out),
.sb(sb_o),
.serial_irq(serial_irq),
.sc_start(sc_start),
.sc_int_clock(sc_shiftclock),
.SaveStateBus_Din (SaveStateBus_Din ),
.SaveStateBus_Adr (SaveStateBus_Adr ),
.SaveStateBus_wren (SaveStateBus_wren),
.SaveStateBus_rst (SaveStateBus_rst ),
.SaveStateBus_Dout (SaveStateBus_wired_or[3])
);
// --------------------------------------------------------------------
// ------------------------------ inputs ------------------------------
// --------------------------------------------------------------------
reg [1:0] p54;
assign SS_Top_BACK[6:5] = p54;
always @(posedge clk_sys) begin
if(reset_ss) p54 <= SS_Top[6:5]; //2'b00 for DMG, 2'b11 for CGB/SGB will be written by BIOS
else if(ce_cpu && sel_joy && !cpu_wr_n_edge) p54 <= cpu_do[5:4];
end
assign joy_do = { 2'b11, p54, joy_din };
assign joy_p54 = p54;
// --------------------------------------------------------------------
// ------------------------------ unused regs -------------------------
// --------------------------------------------------------------------
assign SS_Top_BACK[34:27] = FF72;
assign SS_Top_BACK[42:35] = FF73;
assign SS_Top_BACK[50:43] = FF74;
assign SS_Top_BACK[53:51] = FF75;
always @(posedge clk_sys) begin
if(reset_ss) begin
FF72 <= SS_Top[34:27]; // 0;
FF73 <= SS_Top[42:35]; // 0;
FF74 <= SS_Top[50:43]; // 0;
FF75 <= SS_Top[53:51]; // 0;
end else if(ce_cpu && !cpu_wr_n_edge) begin
if (sel_FF72) FF72 <= cpu_do;
if (sel_FF73) FF73 <= cpu_do;
if (sel_FF74) FF74 <= cpu_do;
if (sel_FF75) FF75 <= cpu_do[6:4];
end
end
// --------------------------------------------------------------------
// ---------------------------- interrupts ----------------------------
// --------------------------------------------------------------------
// interrupt flags are set when the event happens or when the cpu writes
// the register to 1. The "highest" one active is cleared when the cpu
// runs an interrupt ack cycle or when it writes a 0 to the register
assign irq_ack = !cpu_iorq_n && !cpu_m1_n;
// irq vector
assign irq_vec =
if_r[0]&&ie_r[0]?8'h40: // vsync
if_r[1]&&ie_r[1]?8'h48: // lcdc
if_r[2]&&ie_r[2]?8'h50: // timer
if_r[3]&&ie_r[3]?8'h58: // serial
if_r[4]&&ie_r[4]?8'h60: // input
8'h00;
//wire vs = (lcd_mode == 2'b01);
//reg vsD, vsD2;
reg [3:0] inputD, inputD2;
// irq is low when an enable irq is active
assign irq_n = !(ie_r & if_r);
wire video_irq,vblank_irq;
wire timer_irq;
reg old_vblank_irq, old_video_irq, old_timer_irq, old_serial_irq;
reg old_ack = 0;
assign SS_Top_BACK[11: 7] = ie_r[4:0];
assign SS_Top_BACK[16:12] = if_r;
assign SS_Top_BACK[ 17] = old_vblank_irq;
assign SS_Top_BACK[ 18] = old_video_irq;
assign SS_Top_BACK[ 19] = old_timer_irq;
assign SS_Top_BACK[ 20] = old_serial_irq;
assign SS_Top_BACK[ 21] = old_ack;
assign SS_Top_BACK[26:24] = ie_r[7:5];
always @(negedge clk_sys) begin //negedge to trigger interrupt earlier
if(reset_ss) begin
ie_r[4:0] <= SS_Top[11: 7]; // 5'h00;
if_r <= SS_Top[16:12]; // 5'h00;
old_vblank_irq <= SS_Top[ 17]; // 1'b0;
old_video_irq <= SS_Top[ 18]; // 1'b0;
old_timer_irq <= SS_Top[ 19]; // 1'b0;
old_serial_irq <= SS_Top[ 20]; // 1'b0;
old_ack <= SS_Top[ 21]; // 1'b0;
ie_r[7:5] <= SS_Top[26:24]; // 3'h00;
end else if (ce_cpu) begin
// "When an interrupt signal changes from low to high,
// then the corresponding bit in the IF register becomes set."
old_vblank_irq <= vblank_irq;
if(~old_vblank_irq & vblank_irq) if_r[0] <= 1'b1;
// video irq already is a 1 clock event
old_video_irq <= video_irq;
if(~old_video_irq & video_irq) if_r[1] <= 1'b1;
// timer_irq already is a 1 clock event
old_timer_irq <= timer_irq;
if(~old_timer_irq & timer_irq) if_r[2] <= 1'b1;
// serial irq already is a 1 clock event
old_serial_irq <= serial_irq;
if(~old_serial_irq & serial_irq) if_r[3] <= 1'b1;
// falling edge on any input line P10..P13
inputD <= joy_din;
inputD2 <= inputD;
if(~inputD & inputD2) if_r[4] <= 1'b1;
// cpu acknowledges irq. this clears the active irq with hte
// highest priority
old_ack <= irq_ack;
if(old_ack & ~irq_ack) begin
if(if_r[0] && ie_r[0]) if_r[0] <= 1'b0;
else if(if_r[1] && ie_r[1]) if_r[1] <= 1'b0;
else if(if_r[2] && ie_r[2]) if_r[2] <= 1'b0;
else if(if_r[3] && ie_r[3]) if_r[3] <= 1'b0;
else if(if_r[4] && ie_r[4]) if_r[4] <= 1'b0;
end
// cpu writes interrupt enable register
if(sel_ie && !cpu_wr_n_edge)
ie_r <= cpu_do;
// cpu writes interrupt flag register
if(sel_if && !cpu_wr_n_edge)
if_r <= cpu_do[4:0];
end
end
// --------------------------------------------------------------------
// ------------------------------ timer -------------------------------
// --------------------------------------------------------------------
timer timer (
.reset ( reset_ss ),
.clk_sys ( clk_sys ),
.ce ( ce_cpu ), // 2x in fast mode
.ce_4MHz (ce), // Always 4 MiHz
.cpu_speed ( cpu_speed ),
.irq ( timer_irq ),
.cpu_sel ( sel_timer ),
.cpu_addr ( cpu_addr[1:0] ),
.cpu_wr ( !cpu_wr_n_edge ),
.cpu_di ( cpu_do ),
.cpu_do ( timer_do ),
.apu_framecount_en ( apu_framecount_en),
.SaveStateBus_Din (SaveStateBus_Din ),
.SaveStateBus_Adr (SaveStateBus_Adr ),
.SaveStateBus_wren (SaveStateBus_wren),
.SaveStateBus_rst (SaveStateBus_rst ),
.SaveStateBus_Dout (SaveStateBus_wired_or[1])
);
// --------------------------------------------------------------------
// ------------------------------ video -------------------------------
// --------------------------------------------------------------------
// cpu tries to read or write the lcd controller registers
wire [12:0] video_addr;
wire video_rd, dma_rd;
wire [7:0] dma_data = (isGBC & dma_sel_wram) ? wram_do :
dma_sel_ext_bus ? ext_bus_di :
dma_sel_vram ? (isGBC&vram_bank ? vram1_do : vram_do) :
8'hFF;
video video (
.reset ( reset_ss ),
.clk ( clk_sys ),
.ce ( ce ), // 4Mhz
.ce_n ( ce_n ),
.ce_cpu ( ce_cpu ), //can be 2x in cgb double speed mode
.isGBC ( isGBC ),
.isGBC_mode ( isGBC_mode ), //enable GBC mode during bootstrap rom
.megaduck ( megaduck ),
.boot_rom_en ( boot_rom_enabled ),
.irq ( video_irq ),
.vblank_irq ( vblank_irq ),
.cpu_sel_reg ( sel_video_reg ),
.cpu_sel_oam ( sel_video_oam ),
.cpu_addr ( cpu_addr[7:0] ),
.cpu_wr ( !cpu_wr_n_edge ),
.cpu_di ( cpu_do ),
.cpu_do ( video_do ),
.lcd_on ( lcd_on ),
.lcd_clkena ( lcd_clkena ),
.lcd_data ( lcd_data ),
.lcd_data_gb ( lcd_data_gb ),
.mode ( lcd_mode ),
.lcd_vsync ( lcd_vsync ),
.oam_cpu_allow ( oam_cpu_allow ),
.vram_cpu_allow ( vram_cpu_allow ),
.vram_rd ( video_rd ),
.vram_addr ( video_addr ),
.vram_data ( vram_do ),
// vram connection bank1 (GBC)
.vram1_data ( vram1_do ),
.dma_rd ( dma_rd ),
.dma_addr ( dma_addr ),
.dma_data ( dma_data ),
.extra_spr_en( extra_spr_en ),
.extra_wait ( (isGBC & hdma_rd) | dma_rd | sel_vram ),
.Savestate_OAMRAMAddr (Savestate_RAMAddr[7:0]),
.Savestate_OAMRAMRWrEn (Savestate_RAMRWrEn[2]),
.Savestate_OAMRAMWriteData (Savestate_RAMWriteData[7:0]),
.Savestate_OAMRAMReadData (Savestate_RAMReadData_ORAM),
.SaveStateBus_Din (SaveStateBus_Din ),
.SaveStateBus_Adr (SaveStateBus_Adr ),
.SaveStateBus_wren (SaveStateBus_wren),
.SaveStateBus_rst (SaveStateBus_rst ),
.SaveStateBus_Dout (SaveStateBus_wired_or[4])
);
// total 8k/16k (CGB) vram from $8000 to $9fff
wire cpu_wr_vram = sel_vram && !cpu_wr_n && vram_cpu_allow;
wire hdma_rd;
wire [7:0] vram_di = (hdma_read_wram_bus) ? wram_do :
(hdma_read_ext_bus) ? ext_bus_di :
cpu_do;
wire vram_wren = video_rd?1'b0:!vram_bank&&((hdma_rd&&isGBC)||cpu_wr_vram);
wire vram1_wren = video_rd?1'b0:vram_bank&&((hdma_rd&&isGBC)||cpu_wr_vram);
wire [15:0] hdma_target_addr;
wire [12:0] vram_addr = video_rd?video_addr:(hdma_rd&&isGBC)?hdma_target_addr[12:0]:(dma_rd&&dma_sel_vram)?dma_addr[12:0]:cpu_addr[12:0];
wire [7:0] Savestate_RAMReadData_VRAM0, Savestate_RAMReadData_VRAM1;
dpram #(13) vram0 (
.clock_a (clk_cpu ),
.address_a (vram_addr),
.wren_a (vram_wren),
.data_a (vram_di ),
.q_a (vram_do ),
.clock_b (clk_sys),
.address_b (Savestate_RAMAddr[12:0]),
.wren_b (Savestate_RAMRWrEn[1] & !Savestate_RAMAddr[13]),
.data_b (Savestate_RAMWriteData[7:0]),
.q_b (Savestate_RAMReadData_VRAM0)
);
//separate 8k for vbank1 for gbc because of BG reads
dpram #(13) vram1 (
.clock_a (clk_cpu ),
.address_a (vram_addr ),
.wren_a (vram1_wren),
.data_a (vram_di ),
.q_a (vram1_do ),
.clock_b (clk_sys),
.address_b (Savestate_RAMAddr[12:0]),
.wren_b (Savestate_RAMRWrEn[1] & Savestate_RAMAddr[13]),
.data_b (Savestate_RAMWriteData[7:0]),
.q_b (Savestate_RAMReadData_VRAM1)
);
assign Savestate_RAMReadData_VRAM = Savestate_RAMAddr[13] ? Savestate_RAMReadData_VRAM1 : Savestate_RAMReadData_VRAM0;
//GBC VRAM banking
assign SS_Top_BACK[22] = vram_bank;
always @(posedge clk_sys) begin
if(reset_ss)
vram_bank <= SS_Top[22]; // 1'd0;
else if (ce_cpu) begin
if((cpu_addr == 16'hff4f) && !cpu_wr_n_edge && isGBC)
vram_bank <= cpu_do[0];
end
end
// --------------------------------------------------------------------
// -------------------------- HDMA engine(GBC) ------------------------
// --------------------------------------------------------------------
hdma hdma(
.reset ( reset_ss ),
.clk ( clk_sys ),
.ce ( ce_cpu ),
.speed ( cpu_speed ),
// cpu register interface
.sel_reg ( sel_hdma ),
.addr ( cpu_addr[3:0] ),
.wr ( !cpu_wr_n_edge ),
.dout ( hdma_do ),
.din ( cpu_do ),
.lcd_mode ( lcd_mode ),
// dma connection
.hdma_rd ( hdma_rd ),
.hdma_active ( hdma_active ),
.hdma_rd_clk ( hdma_rd_clk ),
.hdma_source_addr ( hdma_source_addr ),
.hdma_target_addr ( hdma_target_addr ),
.SaveStateBus_Din (SaveStateBus_Din ),
.SaveStateBus_Adr (SaveStateBus_Adr ),
.SaveStateBus_wren (SaveStateBus_wren),
.SaveStateBus_rst (SaveStateBus_rst ),
.SaveStateBus_Dout (SaveStateBus_wired_or[2])
);
// --------------------------------------------------------------------
// -------------------------- zero page ram ---------------------------
// --------------------------------------------------------------------
// 127 bytes internal zero page ram from $ff80 to $fffe
wire cpu_wr_zpram = sel_zpram && !cpu_wr_n;
dpram #(7) zpram (
.clock_a (clk_cpu ),
.address_a (cpu_addr[6:0]),
.wren_a (cpu_wr_zpram ),
.data_a (cpu_do ),
.q_a (zpram_do ),
.clock_b (clk_sys),
.address_b (Savestate_RAMAddr[6:0]),
.wren_b (Savestate_RAMRWrEn[3]),
.data_b (Savestate_RAMWriteData[7:0]),
.q_b (Savestate_RAMReadData_ZRAM)
);
// --------------------------------------------------------------------
// -------------------------- 8k/32k(GBC) work ram -------------------
// --------------------------------------------------------------------
// Separate WRAM bus on GBC.
wire hdma_read_wram_bus = (isGBC & hdma_rd & hdma_sel_wram);
wire dma_read_wram_bus = (dma_rd & dma_sel_wram);
wire wram_wren = ~isGBC ? (ext_bus_wram_sel & ext_bus_wr) :
(sel_wram & ~cpu_wr_n_edge & ~hdma_read_wram_bus & ~dma_read_wram_bus);
wire [12:0] wram_addr_i = ~isGBC ? ext_bus_addr[12:0] :
hdma_read_wram_bus ? hdma_source_addr[12:0] :
dma_read_wram_bus ? dma_addr[12:0] :
cpu_addr[12:0];
// 0 -> 1
wire [2:0] wram_bank_o = (!wram_bank ? 3'd1 : wram_bank);
wire [14:0] wram_addr = (wram_addr_i[12]) ? { wram_bank_o, wram_addr_i[11:0] } // bank 1-7 $D000-DFFF
: { 3'd0, wram_addr_i[11:0] }; // bank 0 $C000-CFFF
dpram #(15) wram (
.clock_a (clk_cpu),
.address_a (wram_addr),
.wren_a (wram_wren),
.data_a (cpu_do),
.q_a (wram_do),
.clock_b (clk_sys),
.address_b (Savestate_RAMAddr[14:0]),
.wren_b (Savestate_RAMRWrEn[0]),
.data_b (Savestate_RAMWriteData[7:0]),
.q_b (Savestate_RAMReadData_WRAM)
);
//GBC WRAM banking
assign SS_Top_BACK[2:0] = wram_bank;
always @(posedge clk_sys) begin
if(reset_ss)
wram_bank <= SS_Top[2:0]; // 3'd0;
else if(ce_cpu && sel_wram_bank && !cpu_wr_n_edge) begin
wram_bank <= cpu_do[2:0];
end
end
// --------------------------------------------------------------------
// ------------------------ internal boot rom -------------------------
// --------------------------------------------------------------------
// writing 01(GB) or 11(GBC) to $ff50 disables the internal rom
assign SS_Top_BACK[23] = boot_rom_enabled;
always @(posedge clk_sys) begin
if(reset_ss)
boot_rom_enabled <= SS_Top[23]; // 1'b1;
else if (ce) begin
if((cpu_addr == 16'hff50) && !cpu_wr_n_edge && cpu_do[0]) begin
boot_rom_enabled <= 1'b0;
end
end
end
// combine boot rom data with cartridge data
wire [15:0] boot_rom_addr = (isGBC && hdma_rd) ? hdma_source_addr : cpu_addr;
// 0- FF bootrom 1st part
//100-1FF Cart Header
//200-8FF bootrom 2nd part
assign sel_boot_rom_cgb = isGBC && (boot_rom_addr[15:8] >= 8'h02 && boot_rom_addr[15:8] <= 8'h08);
assign sel_boot_rom = boot_rom_enabled && (!boot_rom_addr[15:8] || sel_boot_rom_cgb) && ~megaduck;
// $000-8FF: GBC
// $900-9FF: DMG
// $A00-AFF: SGB
wire [11:0] boot_addr =
isGBC ? boot_rom_addr[11:0] :
isSGB ? { 4'hA, boot_rom_addr[7:0] } :
{ 4'h9, boot_rom_addr[7:0] };
wire boot_download = cgb_boot_download | dmg_boot_download | sgb_boot_download;
wire [10:0] boot_wr_addr =
dmg_boot_download ? {4'h9, ioctl_addr[7:1] } :
sgb_boot_download ? {4'hA, ioctl_addr[7:1] } :
ioctl_addr[11:1];
wire [7:0] boot_q;
dpram_dif #(12,8,11,16,"BootROMs/cgb_boot.mif") boot_rom (
.clock (clk_sys),
.address_a (boot_addr),
.q_a (boot_q),
.address_b (boot_wr_addr),
.wren_b (ioctl_wr && boot_download),
.data_b (ioctl_dout)
);
reg [7:0] boot_do_gba;
always begin
case (boot_addr)
12'h0F2: boot_do_gba = 8'h00;
12'h0F3: boot_do_gba = 8'h00;
12'h0F5: boot_do_gba = 8'hCD;
12'h0F6: boot_do_gba = 8'hD0;
12'h0F7: boot_do_gba = 8'h05;
12'h0F8: boot_do_gba = 8'hAF;
12'h0F9: boot_do_gba = 8'hE0;
12'h0FA: boot_do_gba = 8'h70;
12'h0FB: boot_do_gba = 8'h04;
12'h409: boot_do_gba = 8'h80;
12'h40A: boot_do_gba = 8'hFF;
default: boot_do_gba = boot_q;
endcase
end
assign boot_do = (isGBC && boot_gba_en && real_cgb_boot) ? boot_do_gba : boot_q;
// --------------------------------------------------------------------
// ------------------ External bus (WRAM, Cartridge) ------------------
// --------------------------------------------------------------------
// Emulate cartridge open bus behavior.
// Some games read from Cart RAM while it is disabled or not present and
// expect to read the previous ROM byte instead of $FF otherwise they freeze.
// Ex. Tokyo Disneyland - Fantasy Tour (in Minnie's house) and
// Daiku no Gen-san - Kachikachi no Tonkachi ga Kachi (Stage 1-4)
reg [7:0] open_bus_data;
reg [2:0] open_bus_cnt;
assign SS_Top2_BACK[ 7: 0] = open_bus_data;
assign SS_Top2_BACK[10: 8] = open_bus_cnt;
always @(posedge clk_sys) begin
if(reset_ss) begin
open_bus_data <= SS_Top2[ 7: 0]; // 8'd0;
open_bus_cnt <= SS_Top2[10: 8]; // 3'd0;
end else if (ce) begin
open_bus_data <= ext_bus_di;
if ( (~isGBC & ext_bus_wram_sel) | (cart_sel & cart_oe) ) begin
open_bus_cnt <= 0;
end else if (~&open_bus_cnt) begin
open_bus_cnt <= open_bus_cnt + 1'b1;
if (open_bus_cnt == 3'd4) open_bus_data <= 8'hFF; // Slow pull-up
end
end
end
assign cart_di = cpu_do;
wire hdma_read_ext_bus = (isGBC & hdma_rd & hdma_sel_ext_bus);
wire dma_read_ext_bus = (dma_rd & dma_sel_ext_bus);
assign nCS = ~( hdma_read_ext_bus ? hdma_sel_cram : dma_read_ext_bus ? dma_sel_ext_ram : (sel_cram | sel_wram) );
wire [15:0] ext_bus_i = hdma_read_ext_bus ? hdma_source_addr : dma_read_ext_bus ? dma_addr : cpu_addr;
// External A15 is not directly connected to internal A15. It does not go low when accessing the boot rom.
assign ext_bus_a15 = ext_bus_i[15] | sel_boot_rom;
assign ext_bus_addr = ext_bus_i[14:0];
assign ext_bus_rd = hdma_read_ext_bus | dma_read_ext_bus | (sel_ext_bus & ~cpu_rd_n);
assign ext_bus_wr = (sel_ext_bus & ~cpu_wr_n) & ~hdma_read_ext_bus & ~dma_read_ext_bus;
assign ext_bus_wram_sel = ~nCS & ext_bus_addr[14];
assign ext_bus_cram_sel = ~nCS & ~ext_bus_addr[14];
assign ext_bus_rom_sel = ~ext_bus_a15;
assign ext_bus_di = (~isGBC & ext_bus_wram_sel) ? wram_do :
(cart_sel & cart_oe) ? cart_do : open_bus_data;
assign cart_sel = ext_bus_rom_sel | ext_bus_cram_sel;
assign cart_rd = cart_sel & ext_bus_rd;
assign cart_wr = cart_sel & ext_bus_wr;
assign DMA_on = cart_sel & (hdma_active | dma_rd);
// --------------------------------------------------------------------
// ------------------------ savestates -------------------------
// --------------------------------------------------------------------
wire savestate_savestate;
wire savestate_loadstate;
wire [31:0] savestate_address;
wire savestate_busy;
wire savestate_loaded;
assign SaveStateExt_Din = SaveStateBus_Din;
assign SaveStateExt_Adr = SaveStateBus_Adr;
assign SaveStateExt_wren = SaveStateBus_wren;
assign SaveStateExt_rst = SaveStateBus_rst;
assign SaveStateExt_load = savestate_loaded;
assign Savestate_CRAMAddr = Savestate_RAMAddr;
assign Savestate_CRAMRdEn = Savestate_RAMRdEn[4];
assign Savestate_CRAMRWrEn = Savestate_RAMRWrEn[4];
assign Savestate_CRAMWriteData = Savestate_RAMWriteData;
wire [63:0] SaveStateBus_Dout = SaveStateBus_wired_or[0] | SaveStateBus_wired_or[1] | SaveStateBus_wired_or[2] | SaveStateBus_wired_or[3] |
SaveStateBus_wired_or[4] | SaveStateBus_wired_or[5] | SaveStateBus_wired_or[6] | SaveStateBus_wired_or[7] |
SaveStateExt_Dout;
wire sleep_rewind, sleep_savestates;
wire loading_savestate, saving_savestate;
gb_savestates gb_savestates (
.clk (clk_sys),
.reset_in (reset_r),
.reset_out (reset_ss),
.load_done (savestate_loaded),
.increaseSSHeaderCount (increaseSSHeaderCount),
.save (savestate_savestate),
.load (savestate_loadstate),
.savestate_address (savestate_address),
.savestate_busy (savestate_busy),
.cart_ram_size (cart_ram_size),
.lcd_vsync (lcd_vsync),
.BUS_Din (SaveStateBus_Din),
.BUS_Adr (SaveStateBus_Adr),
.BUS_wren (SaveStateBus_wren),
.BUS_rst (SaveStateBus_rst),
.BUS_Dout (SaveStateBus_Dout),
.loading_savestate (loading_savestate),
.saving_savestate (saving_savestate),
.sleep_savestate (sleep_savestates),
.clock_ena_in (ce_2x),
.memory_busy (savestate_sdram_busy),
.Save_RAMAddr (Savestate_RAMAddr),
.Save_RAMRdEn (Savestate_RAMRdEn),
.Save_RAMWrEn (Savestate_RAMRWrEn),
.Save_RAMWriteData (Savestate_RAMWriteData),
.Save_RAMReadData_WRAM (Savestate_RAMReadData_WRAM),
.Save_RAMReadData_VRAM (Savestate_RAMReadData_VRAM),
.Save_RAMReadData_ORAM (Savestate_RAMReadData_ORAM),
.Save_RAMReadData_ZRAM (Savestate_RAMReadData_ZRAM),
.Save_RAMReadData_CRAM (Savestate_CRAMReadData),
.bus_out_Din (SAVE_out_Din),
.bus_out_Dout (SAVE_out_Dout),
.bus_out_Adr (SAVE_out_Adr),
.bus_out_rnw (SAVE_out_rnw),
.bus_out_ena (SAVE_out_ena),
.bus_out_be (SAVE_out_be),
.bus_out_done (SAVE_out_done)
);
gb_statemanager #(58720256, 33554432) gb_statemanager (
.clk (clk_sys),
.reset (reset_r),
.rewind_on (rewind_on),
.rewind_active (rewind_active),
.savestate_number (savestate_number),
.save (save_state),
.load (load_state),
.sleep_rewind (sleep_rewind),
.vsync (lcd_vsync),
.request_savestate (savestate_savestate),
.request_loadstate (savestate_loadstate),
.request_address (savestate_address),
.request_busy (savestate_busy)
);
assign sleep_savestate = sleep_rewind | sleep_savestates;
assign savestate_ovr = loading_savestate | saving_savestate;
endmodule