From 3e39e1a6f46fcbd21039d8ef4a71625db0ab90e3 Mon Sep 17 00:00:00 2001 From: Fabricio Breve Date: Sat, 25 Apr 2026 03:49:05 -0300 Subject: [PATCH] VDP: Fix legacy modes (Super Boy I/II) while maintaining Mode 4 compatibility (#178) * Added support for loading Master System BIOS files. Master System BIOS files can now be used to boot games and play built-in titles. * Fix lightgun position calculation for vertical aim The Master System core exhibited a vertical aim deviation with the lightgun, even when properly calibrated. The offset was zero at the top of the screen but increased progressively toward the bottom. This commit corrects the lightgun position calculation logic, ensuring accurate aiming across the full vertical range of the screen. * Implement Soft Reset (Console Reset Button) via OSD and Controller - Added a "Soft Reset" option to the OSD menu. - Implemented falling-edge triggering for the OSD reset to ensure the pulse fires after the menu closes. - Added a ~37ms pulse stretcher to guarantee the Z80 CPU polling loop detects the reset press. - Mapped the controller 'Select' button to trigger the Soft Reset signal. - Routed the signal to I/O port $DD bit 4 (active-low), matching the physical reset button behavior on original SMS hardware. - Improves compatibility and usability for games that utilize the reset button to return to the title screen or menu. * VDP: Fix legacy modes (Super Boy I/II) while maintaining Mode 4 compatibility vdp_background.vhd: Refactor legacy mode (Graphics I, II, Text) rendering logic to fix Super Boy I and II. Correct VRAM addressing for Graphics II (Mode 2) to properly handle banked pattern and color tables. vdp_main.vhd: Ensure vertical scroll wrapping remains correct for Mode 4. This prevents graphical regressions in titles like After Burner (SMS) and Fray (GG). vdp.vhd: Add smode_M2 signal to propagate mode bits for proper legacy mode decoding. video.vhd: Refine high-resolution (224/240 line) timing detection logic. --------- Co-authored-by: Fabricio Breve <7475908+fbreve@users.noreply.github.com> --- rtl/vdp.vhd | 3 +++ rtl/vdp_background.vhd | 44 ++++++++++++++++++++++++++++-------------- rtl/vdp_main.vhd | 5 +++-- rtl/video.vhd | 18 +++++++++-------- 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/rtl/vdp.vhd b/rtl/vdp.vhd index cc129ee..c09c762 100644 --- a/rtl/vdp.vhd +++ b/rtl/vdp.vhd @@ -112,6 +112,7 @@ architecture Behavioral of vdp is signal mode_M3: std_logic; signal mode_M4: std_logic; signal xmode_M1: std_logic; + signal xmode_M2: std_logic; signal xmode_M3: std_logic; signal xmode_M4: std_logic; @@ -119,6 +120,7 @@ begin mask_column <= mask_column0; xmode_M1<= mode_M1 and mode_M2 ; + xmode_M2<= mode_M2; xmode_M3<= mode_M3 and mode_M2 ; xmode_M4<= mode_M4; @@ -145,6 +147,7 @@ begin palettemode => palettemode, y1 => y1, smode_M1 => xmode_M1, + smode_M2 => xmode_M2, smode_M3 => xmode_M3, smode_M4 => xmode_M4, ysj_quirk => ysj_quirk, diff --git a/rtl/vdp_background.vhd b/rtl/vdp_background.vhd index 97474db..eb9dc50 100644 --- a/rtl/vdp_background.vhd +++ b/rtl/vdp_background.vhd @@ -14,6 +14,7 @@ port ( scroll_x: in std_logic_vector(7 downto 0); disable_hscroll: in std_logic; smode_M1: in std_logic; + smode_M2: in std_logic; smode_M3: in std_logic; smode_M4: in std_logic; ysj_quirk: in std_logic; @@ -37,6 +38,7 @@ architecture rtl of vdp_background is signal priority_latch: std_logic; signal flip_x : std_logic; + signal datap : std_logic_vector(7 downto 0); signal datac : std_logic_vector(7 downto 0); signal data0 : std_logic_vector(7 downto 0); signal data1 : std_logic_vector(7 downto 0); @@ -109,11 +111,21 @@ begin else case x(2 downto 0) is when "000" => vram_A <= table_address & y(7 downto 3) & x(7 downto 3); - when "010" => vram_A <= pt_address(13) & ( y(7 downto 6) and pt_address(12 downto 11) )& - tile_index(7 downto 0) & y(2 downto 0); - when "011" => vram_A <= ct_address(13) & ( y(7 downto 6) and ct_address(12 downto 11) )& - tile_index(7 downto 0) & - y(2 downto 0); + when "010" => + if smode_M2 = '1' then -- Graphics II (Mode 2) + vram_A <= pt_address(13) & ( y(7 downto 6) and pt_address(12 downto 11) )& + tile_index(7 downto 0) & y(2 downto 0); + else -- Default / Graphics I / Text + vram_A <= pt_address(13 downto 11) & tile_index(7 downto 0) & y(2 downto 0); + end if; + when "011" => + if smode_M2 = '1' then -- Graphics II (Mode 2) + vram_A <= ct_address(13) & + ( (y(7 downto 6) & tile_index(7 downto 3)) and ct_address(12 downto 6) ) & + tile_index(2 downto 0) & y(2 downto 0); + else -- Default / Graphics I + vram_A <= ct_address(13 downto 6) & '0' & tile_index(7 downto 3); + end if; when others => end case; end if ; @@ -151,16 +163,18 @@ begin when "001" => tile_index(7 downto 0) <= vram_D; when "011" => - datac <= vram_D; - when "100" => - flip_x <= '0' ; - palette <='0' ; - priority_latch <= '0' ; - for i in 0 to 7 loop - data0(i) <= (not datac(i) and vram_D(0)) or (datac(i) and vram_D(4)) ; - data1(i) <= (not datac(i) and vram_D(1)) or (datac(i) and vram_D(5)) ; - data2(i) <= (not datac(i) and vram_D(2)) or (datac(i) and vram_D(6)) ; - data3(i) <= (not datac(i) and vram_D(3)) or (datac(i) and vram_D(7)) ; + datap <= vram_D; + when "101" => + datac <= vram_D; + when "110" => + flip_x <= '0' ; + palette <='0' ; + priority_latch <= '0' ; + for i in 0 to 7 loop + data0(i) <= (datap(i) and datac(4)) or (not datap(i) and datac(0)); + data1(i) <= (datap(i) and datac(5)) or (not datap(i) and datac(1)); + data2(i) <= (datap(i) and datac(6)) or (not datap(i) and datac(2)); + data3(i) <= (datap(i) and datac(7)) or (not datap(i) and datac(3)); end loop; when others => end case; diff --git a/rtl/vdp_main.vhd b/rtl/vdp_main.vhd index e679fa7..e9692ff 100644 --- a/rtl/vdp_main.vhd +++ b/rtl/vdp_main.vhd @@ -30,6 +30,7 @@ entity vdp_main is mask_column0: in std_logic; black_column: in std_logic; smode_M1: in std_logic; + smode_M2: in std_logic; smode_M3: in std_logic; smode_M4: in std_logic; ysj_quirk: in std_logic; @@ -104,10 +105,10 @@ begin vram_D => vram_D, color => bg_color, smode_M1 => smode_M1, + smode_M2 => smode_M2, smode_M3 => smode_M3, smode_M4 => smode_M4, ysj_quirk => ysj_quirk, - priority => bg_priority); vdp_spr_inst: entity work.vdp_sprites @@ -141,7 +142,7 @@ begin variable bg_active : boolean; begin y1 <= '1'; - if ((x>48 and x<=208) or (ggres='0' and x<=256 and x>0)) and -- thank you slingshot + if ((x>48 and x<=208) or (ggres='0' and x<=256 and x>0)) and -- thank you slingshot (mask_column0='0' or x>=9) and display_on='1' then if (((y>=24 and y<168) and smode_M1='0') or ((y>=40 and y<184) and smode_M1='1') diff --git a/rtl/video.vhd b/rtl/video.vhd index 49e3cbc..915d534 100644 --- a/rtl/video.vhd +++ b/rtl/video.vhd @@ -13,7 +13,9 @@ entity video is mask_column: in std_logic := '0'; cut_mask: in std_logic; smode_M1: in std_logic; + smode_M2: in std_logic; smode_M3: in std_logic; + smode_M4: in std_logic; x: out std_logic_vector(8 downto 0); y: out std_logic_vector(8 downto 0); @@ -40,7 +42,7 @@ begin vcount <= vcount + 1; if pal = '1' then -- VCounter: 0-258, 458-511 = 313 steps - if smode_M1='1' then + if smode_M1='1' and smode_M2='1' then if vcount = 258 then vcount <= conv_std_logic_vector(458,9); elsif vcount = 461 then @@ -48,7 +50,7 @@ begin elsif vcount = 464 then vsync <= '0'; end if; - elsif smode_M3='1' then + elsif smode_M3='1' and smode_M2='1' then if vcount = 266 then vcount <= conv_std_logic_vector(482,9); elsif vcount = 482 then @@ -68,7 +70,7 @@ begin end if; else -- NTSC mode 224 lines ... - if smode_M1='1' then + if smode_M1='1' and smode_M2='1' then if vcount = 234 then vcount <= conv_std_logic_vector(485,9); elsif vcount = 487 then @@ -77,7 +79,7 @@ begin vsync <= '0'; end if; -- NTSC mode 240 lines -- this mode is not suposed to work anyway - elsif smode_M3='1' then + elsif smode_M3='1' and smode_M2='1' then if vcount = 261 then -- needs to be > 240 to generate an IRQ vcount <= conv_std_logic_vector(0,9); elsif vcount = 257 then @@ -115,16 +117,16 @@ begin x <= hcount; y <= vcount; - vbl_st <= conv_std_logic_vector(184,9) when (smode_M1='1' and ggres='1') - else conv_std_logic_vector(224,9) when smode_M1 = '1' - else conv_std_logic_vector(240,9) when smode_M3 = '1' + vbl_st <= conv_std_logic_vector(184,9) when (smode_M1='1' and smode_M2='1' and ggres='1') + else conv_std_logic_vector(224,9) when (smode_M1 = '1' and smode_M2 = '1') + else conv_std_logic_vector(240,9) when (smode_M3 = '1' and smode_M2 = '1') else conv_std_logic_vector(216,9) when border = '1' and pal = '0' else conv_std_logic_vector(240,9) when border = '1' else conv_std_logic_vector(192,9) when ggres = '0' else conv_std_logic_vector(168,9); vbl_end <= conv_std_logic_vector(40,9) when (smode_M1='1' and ggres='1') - else conv_std_logic_vector(000,9) when smode_M1 = '1' or smode_M3 = '1' or (border = '0' and ggres = '0') + else conv_std_logic_vector(000,9) when (smode_M1 = '1' and smode_M2 = '1') or (smode_M3 = '1' and smode_M2 = '1') or (border = '0' and ggres = '0') else conv_std_logic_vector(488,9) when border = '1' and pal = '0' else conv_std_logic_vector(458,9) when border = '1' else conv_std_logic_vector(024,9);