From 310be30d767738f6ea0db42eec82f89643e16aae Mon Sep 17 00:00:00 2001 From: paulb-nl Date: Sat, 26 Nov 2022 16:13:39 +0100 Subject: [PATCH] Implement KEY0 and OPRI registers --- Gameboy.sv | 1 - rtl/gb.v | 49 ++++++++++++++++++++++++-------- rtl/reg_savestates.vhd | 4 +-- rtl/video.v | 64 +++++++++++++++++++++++++++++------------- 4 files changed, 84 insertions(+), 34 deletions(-) diff --git a/Gameboy.sv b/Gameboy.sv index 30a4913..8ce0226 100644 --- a/Gameboy.sv +++ b/Gameboy.sv @@ -592,7 +592,6 @@ gb gb ( .ce_2x ( ce_cpu2x ), // ~8MHz in dualspeed mode (GBC) .isGBC ( isGBC ), - .isGBC_game ( isGBC_game ), .isSGB ( |sgb_en & ~isGBC ), .megaduck ( megaduck ), diff --git a/rtl/gb.v b/rtl/gb.v index 24fff2d..a370ff3 100644 --- a/rtl/gb.v +++ b/rtl/gb.v @@ -28,7 +28,6 @@ module gb ( input [7:0] joystick, input isGBC, - input isGBC_game, input isSGB, // cartridge interface @@ -131,9 +130,9 @@ wire [4:0] Savestate_RAMRWrEn; localparam SAVESTATE_MODULES = 8; wire [63:0] SaveStateBus_wired_or[0:SAVESTATE_MODULES-1]; -wire [54:0] SS_Top; -wire [54:0] SS_Top_BACK; -eReg_SavestateV #(0, 31, 54, 0, 64'h0000000000800001) iREG_SAVESTATE_Top (clk_sys, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[6], SS_Top_BACK, SS_Top); +wire [56:0] SS_Top; +wire [56:0] SS_Top_BACK; +eReg_SavestateV #(0, 31, 56, 0, 64'h0000000000800001) 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; @@ -142,11 +141,14 @@ eReg_SavestateV #(0, 38, 10, 0, 64'h0000000000000000) iREG_SAVESTATE_Top2 (clk_s // 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 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:2] == 14'h3fda)); //video and oam dma (+ ff68-ff6B when gbc) +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 @@ -169,7 +171,7 @@ 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_game && (cpu_addr == 16'hff74); // unused register, all bits read/write, only in CGB mode +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 wire ext_bus_wram_sel, ext_bus_cram_sel, ext_bus_rom_sel; @@ -198,11 +200,12 @@ 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_game || boot_rom_enabled) && (cpu_addr==16'hff70); -wire sel_hdma = isGBC && (isGBC_game || boot_rom_enabled) && (cpu_addr[15:4]==12'hff5) && +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_key1 = isGBC && isGBC_game && (cpu_addr == 16'hff4d); // KEY1 - CGB Mode Only - Prepare Speed Switch -wire sel_rp = isGBC && isGBC_game && (cpu_addr == 16'hff56); //FF56 - RP - CGB Mode Only - Infrared Communications Port +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; @@ -257,6 +260,7 @@ wire [7:0] cpu_di = 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 @@ -365,6 +369,27 @@ CODES codes ( .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)----------------------- @@ -659,9 +684,11 @@ video video ( .ce ( ce ), // 4Mhz .ce_cpu ( ce_cpu ), //can be 2x in cgb double speed mode .isGBC ( isGBC ), - .isGBC_game ( isGBC_game|boot_rom_enabled ), //enable GBC mode during bootstrap rom + .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 ), diff --git a/rtl/reg_savestates.vhd b/rtl/reg_savestates.vhd index c379824..7f85dba 100644 --- a/rtl/reg_savestates.vhd +++ b/rtl/reg_savestates.vhd @@ -24,7 +24,7 @@ package pReg_savestates is constant REG_SAVESTATE_Link : regmap_type := ( 8, 16, 0, 1, x"0000000000000000"); constant REG_SAVESTATE_Video1 : regmap_type := ( 9, 60, 0, 1, x"0000000000000000"); - constant REG_SAVESTATE_Video2 : regmap_type := ( 10, 61, 0, 1, x"00000000FFFFFC00"); + constant REG_SAVESTATE_Video2 : regmap_type := ( 10, 63, 0, 1, x"00000000FFFFFC00"); constant REG_SAVESTATE_BPalette : regmap_type := ( 11, 63, 0, 8, x"0000000000000000"); constant REG_SAVESTATE_OPalette : regmap_type := ( 19, 63, 0, 8, x"0000000000000000"); constant REG_SAVESTATE_Video3 : regmap_type := ( 27, 63, 0, 1, x"0000000000000000"); @@ -38,7 +38,7 @@ package pReg_savestates is constant REG_SAVESTATE_Wave1_GBC : regmap_type := ( 35, 63, 0, 1, x"FF00FF00FF00FF00"); constant REG_SAVESTATE_Wave2_GBC : regmap_type := ( 36, 63, 0, 1, x"FF00FF00FF00FF00"); - constant REG_SAVESTATE_Top : regmap_type := ( 31, 54, 0, 1, x"0000000000800001"); + constant REG_SAVESTATE_Top : regmap_type := ( 31, 56, 0, 1, x"0000000000800001"); constant REG_SAVESTATE_Top2 : regmap_type := ( 38, 10, 0, 1, x"0000000000000000"); constant REG_SAVESTATE_Ext : regmap_type := ( 32, 15, 0, 1, x"0000000000000001"); constant REG_SAVESTATE_Ext2 : regmap_type := ( 37, 63, 0, 1, x"0000000000000000"); diff --git a/rtl/video.v b/rtl/video.v index 4c9d3c9..0d79aad 100644 --- a/rtl/video.v +++ b/rtl/video.v @@ -25,9 +25,11 @@ module video ( input ce, // 4 Mhz cpu clock input ce_cpu, // 4 or 8Mhz input isGBC, - input isGBC_game, + input isGBC_mode, input megaduck, + input boot_rom_en, + // cpu register adn oam interface input cpu_sel_oam, input cpu_sel_reg, @@ -81,13 +83,13 @@ wire [63:0] SaveStateBus_wired_or[0:SAVESTATE_MODULES-1]; wire [60:0] SS_Video1; wire [60:0] SS_Video1_BACK; -wire [61:0] SS_Video2; -wire [61:0] SS_Video2_BACK; +wire [63:0] SS_Video2; +wire [63:0] SS_Video2_BACK; wire [63:0] SS_Video3; wire [63:0] SS_Video3_BACK; eReg_SavestateV #(0, 9, 60, 0, 64'h0000000000000000) iREG_SAVESTATE_Video1 (clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[ 0], SS_Video1_BACK, SS_Video1); -eReg_SavestateV #(0, 10, 61, 0, 64'h00000000FFFFFC00) iREG_SAVESTATE_Video2 (clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[ 1], SS_Video2_BACK, SS_Video2); +eReg_SavestateV #(0, 10, 63, 0, 64'h00000000FFFFFC00) iREG_SAVESTATE_Video2 (clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[ 1], SS_Video2_BACK, SS_Video2); eReg_SavestateV #(0, 27, 63, 0, 64'h0000000000000000) iREG_SAVESTATE_Video3 (clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[18], SS_Video3_BACK, SS_Video3); wire [63:0] SS_BPAL [7:0]; @@ -150,7 +152,7 @@ wire lcdc_spr_ena = megaduck ? lcdc[0] : lcdc[1]; // When Bit 0 is cleared, the background and window lose their priority // - the sprites will be always displayed on top of background and window, // independently of the priority flags in OAM and BG Map attributes." -wire lcdc_bg_ena = (megaduck ? lcdc[6] : lcdc[0]) | (isGBC&&isGBC_game); +wire lcdc_bg_ena = (megaduck ? lcdc[6] : lcdc[0]) | (isGBC&&isGBC_mode); wire lcdc_bg_prio = megaduck ? lcdc[6] : lcdc[0]; assign lcd_on = lcdc_on; @@ -197,6 +199,13 @@ reg obpi_ai; //Bit 7 Auto Increment (0=Disabled, 1=Increment after Writi //FF6B - OCPD/OBPD - Sprite Palette Data reg[7:0] obpd [63:0]; //64 bytes +//FF6C Bit 0 OBJ priority mode select +// 0: smaller OBJ-NO has higher priority (GBC) +// 1: smaller X coordinate has higher priority (DMG) +// https://forums.nesdev.org/viewtopic.php?t=19888 +reg ff6c_opri; +reg obj_prio_dmg_mode; + // -------------------------------------------------------------------- // ----------------------------- DMA engine --------------------------- // -------------------------------------------------------------------- @@ -385,6 +394,8 @@ assign SS_Video2_BACK[ 44] = bgpi_ai; assign SS_Video2_BACK[ 45] = obpi_ai; assign SS_Video2_BACK[53:46] = lyc_r_gbc; assign SS_Video2_BACK[61:54] = lyc_r_dmg; +assign SS_Video2_BACK[ 62] = ff6c_opri; +assign SS_Video2_BACK[ 63] = obj_prio_dmg_mode; genvar palI; generate @@ -443,6 +454,8 @@ always @(posedge clk) begin bgpi_ai <= SS_Video2[ 44]; // 1'b0; obpi_ai <= SS_Video2[ 45]; // 1'b0; lyc_r_gbc <= SS_Video2[53:46]; // 8'h00; + ff6c_opri <= SS_Video2[ 62]; // 1'b0; + obj_prio_dmg_mode <= SS_Video2[ 63]; // 1'b0; bgpd[ 0] <= SS_BPAL[0][ 7: 0]; bgpd[16] <= SS_BPAL[2][ 7: 0]; bgpd[32] <= SS_BPAL[4][ 7: 0]; bgpd[48] <= SS_BPAL[6][ 7: 0]; //8'h00; bgpd[ 1] <= SS_BPAL[0][15: 8]; bgpd[17] <= SS_BPAL[2][15: 8]; bgpd[33] <= SS_BPAL[4][15: 8]; bgpd[49] <= SS_BPAL[6][15: 8]; //8'h00; @@ -491,13 +504,15 @@ always @(posedge clk) begin 8'h49: obp1 <= cpu_di; 8'h4a: wy <= cpu_di; 8'h4b: wx <= cpu_di; + endcase - //gbc + //gbc + if (isGBC) case(cpu_addr) 8'h68: begin bgpi <= cpu_di[5:0]; bgpi_ai <= cpu_di[7]; end - 8'h69: begin + 8'h69: if (isGBC_mode) begin if (vram_cpu_allow) begin bgpd[bgpi] <= cpu_di; end @@ -508,12 +523,20 @@ always @(posedge clk) begin obpi <= cpu_di[5:0]; obpi_ai <= cpu_di[7]; end - 8'h6B: begin + 8'h6B: if (isGBC_mode) begin if (vram_cpu_allow) begin obpd[obpi] <= cpu_di; end if (obpi_ai) obpi <= obpi + 6'h1; end + 8'h6C: begin + // Reportedly can be written to when boot rom is enabled or FF4C Bit2=0 (GBC mode) + // but only affects OBJ prio if written when boot rom is enabled. + if (boot_rom_en | isGBC_mode) begin + ff6c_opri <= cpu_di[0]; + end + if (boot_rom_en) obj_prio_dmg_mode <= cpu_di[0]; + end endcase end end @@ -536,9 +559,10 @@ assign cpu_do = (cpu_addr == 8'h4b)?wx: isGBC? (cpu_addr == 8'h68)?{bgpi_ai,1'd1,bgpi}: - (cpu_addr == 8'h69 && isGBC_game && vram_cpu_allow)?bgpd[bgpi]: + (cpu_addr == 8'h69 && isGBC_mode && vram_cpu_allow)?bgpd[bgpi]: (cpu_addr == 8'h6a)?{obpi_ai,1'd1,obpi}: - (cpu_addr == 8'h6b && isGBC_game && vram_cpu_allow)?obpd[obpi]: + (cpu_addr == 8'h6b && isGBC_mode && vram_cpu_allow)?obpd[obpi]: + (cpu_addr == 8'h6c) ? { 7'h7f, ff6c_opri } : 8'hff: 8'hff; @@ -773,9 +797,9 @@ reg [7:0] spr_tile_data0; reg [7:0] bg_tile_attr,bg_tile_attr_new; //GBC //gbc check tile bank -wire [7:0] bg_vram_data_a = (isGBC & isGBC_game & bg_tile_attr_new[3]) ? vram1_data : vram_data; +wire [7:0] bg_vram_data_a = (isGBC & isGBC_mode & bg_tile_attr_new[3]) ? vram1_data : vram_data; //gbc x-flip -wire [7:0] bg_vram_data_in = (isGBC & isGBC_game & bg_tile_attr_new[5]) ? bit_reverse(bg_vram_data_a) : bg_vram_data_a; +wire [7:0] bg_vram_data_in = (isGBC & isGBC_mode & bg_tile_attr_new[5]) ? bit_reverse(bg_vram_data_a) : bg_vram_data_a; // A bg or sprite fetch takes 6 cycles reg [2:0] bg_fetch_cycle; @@ -797,7 +821,7 @@ wire spr_pal = sprite_attr[4]; wire spr_attr_cgb_bank = sprite_attr[3]; wire [2:0] spr_cgb_pal = sprite_attr[2:0]; -wire [7:0] spr_vram_data = (isGBC & isGBC_game & spr_attr_cgb_bank) ? vram1_data : vram_data; +wire [7:0] spr_vram_data = (isGBC & isGBC_mode & spr_attr_cgb_bank) ? vram1_data : vram_data; wire [7:0] spr_tile_data_in = spr_attr_h_flip ? bit_reverse(spr_vram_data) : spr_vram_data; // CGB sprite priority. Non-transparent pixels with lower sprite_index have priority. @@ -814,7 +838,7 @@ wire [7:0] spr_cgb_index_prio = spr_cgb_prio(spr_cgb_index_shift[3], spr_cgb_ind // DMG sprite pixels are only loaded into the shift register if the old pixel is transparent. // CGB will mask the old pixel to 0 if the new pixel has higher priority. -wire [7:0] spr_tile_mask = (spr_tile_shift_0 | spr_tile_shift_1) & ((isGBC & isGBC_game) ? ~spr_cgb_index_prio : 8'hFF); +wire [7:0] spr_tile_mask = (spr_tile_shift_0 | spr_tile_shift_1) & ((isGBC & ~obj_prio_dmg_mode) ? ~spr_cgb_index_prio : 8'hFF); // cycle through the B01s states wire bg_tile_map_rd = (mode3 && bg_fetch_cycle[2:1] == 2'b00); @@ -839,7 +863,7 @@ always @(posedge clk) begin if (bg_fetch_cycle[0]) begin if(bg_tile_map_rd) begin bg_tile <= vram_data; - if (isGBC & isGBC_game) bg_tile_attr_new <= vram1_data; //get tile attr from vram bank1 + if (isGBC & isGBC_mode) bg_tile_attr_new <= vram1_data; //get tile attr from vram bank1 end if(bg_tile_data0_rd) bg_tile_data0 <= bg_vram_data_in; @@ -929,7 +953,7 @@ wire bg_tile_a12 = !lcdc_tile_data_sel?(~bg_tile[7]):1'b0; wire tile_map_sel = window_ena?lcdc_win_tile_map_sel:lcdc_bg_tile_map_sel; //GBC: check if flipped y -wire [2:0] tile_line_flip = (isGBC && isGBC_game && bg_tile_attr_new[6]) ? ~tile_line : tile_line; +wire [2:0] tile_line_flip = (isGBC && isGBC_mode && bg_tile_attr_new[6]) ? ~tile_line : tile_line; assign vram_addr = bg_tile_map_rd?{2'b11, tile_map_sel, bg_tile_map_addr}: @@ -979,7 +1003,7 @@ sprites sprites ( // https://forums.nesdev.com/viewtopic.php?f=20&t=10771&sid=8fdb6e110fd9b5434d4a567b1199585e#p122222 // priority list: BG0 < OBJL < BGL < OBJH < BGH -wire bg_piority = isGBC && isGBC_game && bg_tile_attr[7]; +wire bg_piority = isGBC && isGBC_mode && bg_tile_attr[7]; reg sprite_pixel_visible; @@ -992,7 +1016,7 @@ always @(*) begin sprite_pixel_visible = 1'b0; if (|sprite_pixel_data && lcdc_spr_ena) begin // pixel active and sprites enabled - if (isGBC&&isGBC_game) begin + if (isGBC&&isGBC_mode) begin if (sprite_pixel_data == 2'b00) sprite_pixel_visible = 1'b0; else if (bg_pix_data == 2'b00) @@ -1030,7 +1054,7 @@ wire [1:0] obp_data = (sprite_pixel_data == 2'b00) ? obp[1:0] : (sprite_pixel_data == 2'b10) ? obp[5:4] : obp[7:6]; -wire [5:0] palette_index = isGBC_game ? {bg_tile_attr[2:0], bg_pix_data, 1'b0} : //GBC game +wire [5:0] palette_index = isGBC_mode ? {bg_tile_attr[2:0], bg_pix_data, 1'b0} : //GBC game {3'd0, bgp_data , 1'b0}; //GB game in GBC mode // apply bg palette @@ -1039,7 +1063,7 @@ wire [14:0] pix_rgb_data = isGBC ? {bgpd[palette_index+1][6:0],bgpd[palette_inde // apply sprite palette wire [2:0] spr_cgb_pal_out = {spr_cgb_pal_shift[2][7], spr_cgb_pal_shift[1][7],spr_cgb_pal_shift[0][7]}; -wire [5:0] sprite_palette_index = isGBC_game? {spr_cgb_pal_out, sprite_pixel_data, 1'b0 } : //gbc game +wire [5:0] sprite_palette_index = isGBC_mode? {spr_cgb_pal_out, sprite_pixel_data, 1'b0 } : //gbc game {sprite_pixel_cmap, obp_data, 1'b0}; //GB game in GBC mode wire [14:0] sprite_pix = isGBC ? {obpd[sprite_palette_index+1][6:0],obpd[sprite_palette_index]} : //gbc