Output blank image when LCD is off

The screen stays blank until 1 frame after the LCD is turned on.
Fixes glitches during transitions in some games.
This commit is contained in:
paulb-nl
2020-12-03 01:04:25 +01:00
parent 11ca7142a0
commit 7fff2e2d36
5 changed files with 73 additions and 13 deletions

View File

@@ -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;

View File

@@ -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 ),

View File

@@ -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

View File

@@ -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

View File

@@ -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