diff --git a/Gameboy.sv b/Gameboy.sv index 37ba089..b2883f5 100644 --- a/Gameboy.sv +++ b/Gameboy.sv @@ -542,6 +542,7 @@ wire lcd_clkena; wire [14:0] lcd_data; wire [1:0] lcd_mode; wire lcd_on; +wire lcd_vsync; assign AUDIO_S = 0; @@ -590,6 +591,7 @@ gb gb ( .lcd_data ( lcd_data ), .lcd_mode ( lcd_mode ), .lcd_on ( lcd_on ), + .lcd_vsync ( lcd_vsync ), .speed ( speed ), // serial port @@ -620,10 +622,13 @@ lcd lcd ( // serial interface .clk_sys( clk_sys ), - .pix_wr ( sgb_lcd_clkena & ce_cpu ), + .ce ( ce_cpu ), + + .lcd_clkena ( sgb_lcd_clkena ), .data ( sgb_lcd_data ), .mode ( sgb_lcd_mode ), // used to detect begin of new lines and frames .on ( sgb_lcd_on ), + .lcd_vs ( sgb_lcd_vsync ), .isGBC ( isGBC ), @@ -658,7 +663,7 @@ wire [1:0] joy_p54; wire [3:0] joy_do_sgb; wire [14:0] sgb_lcd_data; wire [15:0] sgb_border_pix; -wire sgb_lcd_clkena, sgb_lcd_on; +wire sgb_lcd_clkena, sgb_lcd_on, sgb_lcd_vsync; wire [1:0] sgb_lcd_mode; wire sgb_pal_en; wire [1:0] sgb_en = {~status[24] ^ status[23], status[23]}; @@ -685,6 +690,7 @@ sgb sgb ( .lcd_clkena ( lcd_clkena ), .lcd_data ( lcd_data ), .lcd_mode ( lcd_mode ), + .lcd_vsync ( lcd_vsync ), .h_cnt ( h_cnt ), .v_cnt ( v_cnt ), @@ -694,7 +700,8 @@ sgb sgb ( .sgb_lcd_data ( sgb_lcd_data ), .sgb_lcd_on ( sgb_lcd_on ), .sgb_lcd_clkena ( sgb_lcd_clkena ), - .sgb_lcd_mode ( sgb_lcd_mode ) + .sgb_lcd_mode ( sgb_lcd_mode ), + .sgb_lcd_vsync ( sgb_lcd_vsync ) ); reg hs_o, vs_o; diff --git a/rtl/gb.v b/rtl/gb.v index c428a72..1a8bf43 100644 --- a/rtl/gb.v +++ b/rtl/gb.v @@ -52,6 +52,7 @@ module gb ( output [14:0] lcd_data, output [1:0] lcd_mode, output lcd_on, + output lcd_vsync, output [1:0] joy_p54, input [3:0] joy_din, @@ -484,7 +485,8 @@ video video ( .lcd_clkena ( lcd_clkena ), .lcd_data ( lcd_data ), .mode ( lcd_mode ), - + .lcd_vsync ( lcd_vsync ), + .vram_rd ( video_rd ), .vram_addr ( video_addr ), .vram_data ( vram_do ), diff --git a/rtl/lcd.v b/rtl/lcd.v index 9a1b882..a43c799 100644 --- a/rtl/lcd.v +++ b/rtl/lcd.v @@ -6,7 +6,10 @@ module lcd ( input clk_sys, - input pix_wr, + input ce, + input lcd_clkena, + input lcd_vs, + input [14:0] data, input [1:0] mode, @@ -45,22 +48,54 @@ module lcd reg [14:0] vbuffer_inptr; reg vbuffer_in_bank; reg lcd_off; +reg blank_de, blank_output; +reg [14:0] blank_data; + +wire pix_wr = ce & (lcd_clkena | blank_de); always @(posedge clk_sys) begin - reg old_lcd_off; + reg old_lcd_off, old_on, old_lcd_vs; + reg [8:0] blank_hcnt,blank_vcnt; lcd_off <= !on || (mode == 2'd01); + blank_de <= (!on && blank_output && blank_hcnt < 160 && blank_vcnt < 144); - if (pix_wr & ~lcd_off) vbuffer_inptr <= vbuffer_inptr + 1'd1; + if (pix_wr) vbuffer_inptr <= vbuffer_inptr + 1'd1; old_lcd_off <= lcd_off; - if(~old_lcd_off & lcd_off) begin //lcd disabled or vsync restart pointer + if(old_lcd_off ^ lcd_off) begin vbuffer_inptr <= 0; - vbuffer_in_bank <= ~vbuffer_in_bank; + if (lcd_off) vbuffer_in_bank <= ~vbuffer_in_bank; //LCD disabled or VBlank end + + old_on <= on; + if (old_on & ~on & ~blank_output) begin // LCD disabled, start blank output + blank_output <= 1'b1; + {blank_hcnt,blank_vcnt} <= 0; + end + + // Regenerate LCD timings for filling with blank color when LCD is off + if (ce & ~on & blank_output) begin + blank_data <= data; + blank_hcnt <= blank_hcnt + 1'b1; + if (blank_hcnt == 9'd455) begin + blank_hcnt <= 0; + blank_vcnt <= blank_vcnt + 1'b1; + if (blank_vcnt == 9'd153) begin + blank_vcnt <= 0; + vbuffer_inptr <= 0; + vbuffer_in_bank <= ~vbuffer_in_bank; + end + end + end + + // Output 1 blank frame until VSync after LCD is enabled + old_lcd_vs <= lcd_vs; + if (~old_lcd_vs & lcd_vs & blank_output) + blank_output <= 0; end reg [14:0] vbuffer[65536]; -always @(posedge clk_sys) if(pix_wr) vbuffer[{vbuffer_in_bank, vbuffer_inptr}] <= data; +always @(posedge clk_sys) if(pix_wr) vbuffer[{vbuffer_in_bank, vbuffer_inptr}] <= (on & blank_output) ? blank_data : data; // Mode 00: h-blank diff --git a/rtl/sgb.v b/rtl/sgb.v index 5224947..75544f0 100644 --- a/rtl/sgb.v +++ b/rtl/sgb.v @@ -13,6 +13,7 @@ module sgb ( input [14:0] lcd_data, input [1:0] lcd_mode, input lcd_on, + input lcd_vsync, input [8:0] h_cnt, input [8:0] v_cnt, @@ -31,7 +32,8 @@ module sgb ( output reg [14:0] sgb_lcd_data, output reg sgb_lcd_clkena, output reg [1:0] sgb_lcd_mode, - output reg sgb_lcd_on + output reg sgb_lcd_on, + output reg sgb_lcd_vsync ); localparam CMD_PAL01 = 5'h00; @@ -833,7 +835,7 @@ end reg [14:0] lcd_data_r; reg [1:0] pal_no; -reg lcd_clkena_r, lcd_on_r; +reg lcd_clkena_r, lcd_on_r, lcd_vsync_r; reg [1:0] lcd_mode_r; reg [1:0] mask_en_r; wire [1:0] lcd_data_2 = lcd_data_r[1:0]; @@ -848,6 +850,7 @@ always @(posedge clk_sys) begin lcd_clkena_r <= lcd_clkena; lcd_mode_r <= lcd_mode; lcd_on_r <= lcd_on; + lcd_vsync_r <= lcd_vsync; if (~sgb_en | ((~output_sgb_pal | tint) & !mask_en_r) ) begin sgb_lcd_data <= lcd_data_r; @@ -863,6 +866,7 @@ always @(posedge clk_sys) begin sgb_lcd_mode <= lcd_mode_r; sgb_lcd_on <= lcd_on_r; sgb_pal_en <= sgb_en & ( (output_sgb_pal & ~tint) || |mask_en_r); + sgb_lcd_vsync <= lcd_vsync_r; end end diff --git a/rtl/video.v b/rtl/video.v index 69bde82..3ceb281 100644 --- a/rtl/video.v +++ b/rtl/video.v @@ -39,6 +39,7 @@ module video ( output lcd_on, output lcd_clkena, output [14:0] lcd_data, + output lcd_vsync, output irq, output vblank_irq, @@ -404,12 +405,14 @@ reg [7:0] v_cnt; // max 153 // This results in v_cnt 0 lasting for almost 2 lines. wire line153 = (v_cnt == 8'd153); reg vcnt_reset; +reg vsync; always @(posedge clk) begin if (!lcdc_on) begin v_cnt <= 8'd0; vcnt_reset <= 1'b0; + vsync <= 1'b0; end else if (ce) begin - if (~vcnt_reset && h_cnt == 9'd455) begin + if (~vcnt_reset && h455) begin v_cnt <= v_cnt + 1'b1; end @@ -419,9 +422,15 @@ always @(posedge clk) begin v_cnt <= 8'd0; end end + + // VSync goes high on line 0 but it takes a full frame after the LCD is enabled + // because the first line where end_of_line is high after LCD is enabled is line 1. + if (end_of_line) vsync <= !v_cnt; end end +assign lcd_vsync = vsync; + // line inside the background currently being drawn wire [7:0] bg_line = v_cnt + scy; wire [7:0] bg_col = pcnt + scx; @@ -750,6 +759,9 @@ always @(posedge clk) begin if (lcd_clk) begin lcd_data_out <= (sprite_pixel_visible) ? sprite_pix : pix_rgb_data; end + + // Output blank pixels if lcd is off. + if (~lcd_on) lcd_data_out <= isGBC ? 15'h7FFF : 15'd0; end end