diff --git a/Gameboy.sv b/Gameboy.sv index 0537baf..fd8cb48 100644 --- a/Gameboy.sv +++ b/Gameboy.sv @@ -151,7 +151,7 @@ assign AUDIO_MIX = status[8:7]; // 0 1 2 3 // 01234567890123456789012345678901 // 0123456789ABCDEFGHIJKLMNOPQRSTUV -// XXXXXXXXXXXXXXXX XXXX XX +// XXXXXXXXXXXXXXXXXXXXX XX `include "build_id.v" localparam CONF_STR = { @@ -174,6 +174,7 @@ localparam CONF_STR = { "O34,Aspect ratio,Original,Full Screen,[ARC1],[ARC2];", "OIK,Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%,CRT 75%;", "O5,Stabilize video(buffer),Off,On;", + "OG,Frame blend,Off,On;", "O78,Stereo mix,none,25%,50%,100%;", "-;", "OB,Boot,Normal,Fast;", @@ -599,7 +600,6 @@ gb gb ( .serial_data_in(ser_data_in), .serial_clk_out(ser_clk_out), .serial_data_out(ser_data_out), - .serial_ena(status[6]), // Palette download will disable cheats option (HPS doesn't distinguish downloads), // so clear the cheats and disable second option (chheats enable/disable) @@ -634,6 +634,7 @@ lcd lcd .tint ( |tint ), .inv ( status[12] ), .double_buffer( status[5]), + .frame_blend( status[16] ), // Palettes .pal1 (palette[127:104]), @@ -808,12 +809,13 @@ wire ser_data_in; wire ser_data_out; wire ser_clk_in; wire ser_clk_out; +wire serial_ena = status[6]; -assign ser_data_in = USER_IN[2]; -assign USER_OUT[1] = ser_data_out; +assign ser_data_in = serial_ena ? USER_IN[2] : 1'b1; +assign USER_OUT[1] = serial_ena ? ser_data_out : 1'b1; -assign ser_clk_in = USER_IN[0]; -assign USER_OUT[0] = sc_int_clock_out?ser_clk_out:1'b1; +assign ser_clk_in = serial_ena ? USER_IN[0] : 1'b1; +assign USER_OUT[0] = (serial_ena & sc_int_clock_out) ? ser_clk_out : 1'b1; diff --git a/rtl/gb.v b/rtl/gb.v index 1a8bf43..9055e6f 100644 --- a/rtl/gb.v +++ b/rtl/gb.v @@ -65,7 +65,6 @@ module gb ( output gg_available, //serial port - input serial_ena, output sc_int_clock2, input serial_clk_in, output serial_clk_out, @@ -130,7 +129,7 @@ wire [7:0] cpu_di = isGBC&&sel_hdma?{hdma_do}: //hdma GBC isGBC&&sel_key1?{cpu_speed,6'h3f,prepare_switch}: //key1 cpu speed register(GBC) sel_joy?joy_do: // joystick register - sel_sb?sb: // serial transfer data 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 @@ -260,16 +259,11 @@ gbc_snd audio ( // -----------------------serial port()-------------------------------- // -------------------------------------------------------------------- -wire serial_irq = serial_ena ? serial_irq_s : serial_irq_f; -wire [7:0] sb = serial_ena ? sb_s : 8'hFF; -wire sc_start = serial_ena ? sc_start_s : sc_start_f; -wire sc_shiftclock = serial_ena ? sc_shiftclock_s : sc_shiftclock_f; - // SNAC -wire serial_irq_s; -wire [7:0] sb_s; -wire sc_start_s; -wire sc_shiftclock_s; +wire serial_irq; +wire [7:0] sb_o; +wire sc_start; +wire sc_shiftclock; assign sc_int_clock2 = sc_shiftclock; @@ -290,50 +284,13 @@ link link ( .serial_clk_out(serial_clk_out), .serial_data_out(serial_data_out), - .sb(sb_s), - .serial_irq(serial_irq_s), - .sc_start(sc_start_s), - .sc_int_clock(sc_shiftclock_s) + .sb(sb_o), + .serial_irq(serial_irq), + .sc_start(sc_start), + .sc_int_clock(sc_shiftclock) ); -// Fake -reg sc_start_f,sc_shiftclock_f; -reg serial_irq_f; - -always @(posedge clk_cpu) begin - reg [3:0] serial_counter; - reg [8:0] serial_clk_div; //8192Hz - - serial_irq_f <= 1'b0; - if(reset_r) begin - sc_start_f <= 1'b0; - sc_shiftclock_f <= 1'b0; - end else if (sel_sc && !cpu_wr_n) begin //cpu write - sc_start_f <= cpu_do[7]; - sc_shiftclock_f <= cpu_do[0]; - if (cpu_do[7]) begin //enable transfer - serial_clk_div <= 9'h1FF; - serial_counter <= 4'd8; - end - end else if (sc_start_f && sc_shiftclock_f) begin // serial transfer and serial clock enabled - - serial_clk_div <= serial_clk_div - 9'd1; - - if (serial_clk_div == 9'd0 && serial_counter) - serial_counter <= serial_counter - 4'd1; - - if (!serial_counter) begin - serial_irq_f <= 1'b1; //trigger interrupt - sc_start_f <= 1'b0; //reset transfer state - serial_clk_div <= 9'h1FF; - serial_counter <= 4'd8; - end - - end - -end - // -------------------------------------------------------------------- // ------------------------------ inputs ------------------------------ // -------------------------------------------------------------------- diff --git a/rtl/lcd.v b/rtl/lcd.v index a43c799..94db676 100644 --- a/rtl/lcd.v +++ b/rtl/lcd.v @@ -28,6 +28,7 @@ module lcd input tint, input inv, + input frame_blend, input on, @@ -123,12 +124,14 @@ parameter VTOTAL = 264; // We need 4256 cycles per line so 1 pixel clock cycle needs to be 6 cycles longer. // 424x10 + 1x16 cycles reg [3:0] pix_div_cnt; +reg ce_pix_n; always @(posedge clk_vid) begin pix_div_cnt <= pix_div_cnt + 1'd1; if (h_cnt != HTOTAL-1 && pix_div_cnt == 4'd9) // Longer cycle at the last pixel pix_div_cnt <= 0; ce_pix <= !pix_div_cnt; + ce_pix_n <= (pix_div_cnt == 4'd5); end reg [14:0] vbuffer_outptr; @@ -144,7 +147,7 @@ always @(posedge clk_vid) begin inptr1 <= inptr2; if(inptr1 == inptr2) inptr <= inptr1; - if (!pix_div_cnt) begin + if (ce_pix_n) begin // generate positive hsync signal if(h_cnt == H_START+H+HFP+HS) hs <= 0; if(h_cnt == H_START+H+HFP) begin @@ -213,11 +216,26 @@ end reg [14:0] pixel_reg; always @(posedge clk_vid) pixel_reg <= vbuffer[{vbuffer_out_bank, vbuffer_outptr}]; -wire [1:0] pixel = (pixel_reg[1:0] ^ {inv,inv}); //invert gb only +// Previous frame data for frame blend +reg [14:0] prev_vbuffer[160*144]; +reg [14:0] prev_pixel_reg; +always @(posedge clk_vid) begin + if(ce_pix & ~gb_hb & ~gb_vb) prev_vbuffer[vbuffer_outptr] <= pixel_reg; + prev_pixel_reg <= prev_vbuffer[vbuffer_outptr]; +end -wire [4:0] r5 = pixel_reg[4:0]; -wire [4:0] g5 = pixel_reg[9:5]; -wire [4:0] b5 = pixel_reg[14:10]; +// Current pixel_reg latched at ce_pix_n so it is ready at ce_pix +reg [14:0] pixel_out; +always@(posedge clk_vid) begin + if (ce_pix_n) pixel_out <= pixel_reg; + else if (ce_pix) pixel_out <= prev_pixel_reg; +end + +wire [1:0] pixel = (pixel_out[1:0] ^ {inv,inv}); //invert gb only + +wire [4:0] r5 = pixel_out[4:0]; +wire [4:0] g5 = pixel_out[9:5]; +wire [4:0] b5 = pixel_out[14:10]; wire [31:0] r10 = (r5 * 13) + (g5 * 2) +b5; wire [31:0] g10 = (g5 * 3) + b5; @@ -229,35 +247,69 @@ wire [7:0] grey = (pixel==0) ? 8'd252 : (pixel==1) ? 8'd168 : (pixel==2) ? 8'd96 // sgb_border_pix contains backdrop color when sgb_border_pix[15] is low. wire sgb_border = sgb_border_pix[15] & sgb_en; -always@(posedge clk_vid) begin - if(ce_pix) begin - // visible area? - hbl <= sgb_en ? hb : gb_hb; - vbl <= sgb_en ? vb : gb_vb; - - // Allow backdrop color in border area and the border to overlap game area. - if (((gb_hb|gb_vb) & sgb_en) | sgb_border) begin - r <= {sgb_border_pix[4:0],sgb_border_pix[4:2]}; - g <= {sgb_border_pix[9:5],sgb_border_pix[9:7]}; - b <= {sgb_border_pix[14:10],sgb_border_pix[14:12]}; - end else if (isGBC) begin - r <= r10[8:1]; - g <= {g10[6:0],1'b0}; - b <= b10[8:1]; - end else if (sgb_pal_en) begin - r <= {r5,r5[4:2]}; - g <= {g5,g5[4:2]}; - b <= {b5,b5[4:2]}; - end else if (tint) begin - {r,g,b} <= (pixel==0) ? pal1 : (pixel==1) ? pal2 : (pixel==2) ? pal3 : pal4; - end else begin - {r,g,b} <= {3{grey}}; - end +function [7:0] blend; + input [7:0] a,b; + reg [8:0] sum; + begin + sum = a + b; + blend = sum[8:1]; + end +endfunction +reg [7:0] r_tmp, g_tmp, b_tmp; +always@(*) begin + if (isGBC) begin + r_tmp = r10[8:1]; + g_tmp = {g10[6:0],1'b0}; + b_tmp = b10[8:1]; + end else if (sgb_pal_en) begin + r_tmp = {r5,r5[4:2]}; + g_tmp = {g5,g5[4:2]}; + b_tmp = {b5,b5[4:2]}; + end else if (tint) begin + {r_tmp,g_tmp,b_tmp} = (pixel==0) ? pal1 : (pixel==1) ? pal2 : (pixel==2) ? pal3 : pal4; + end else begin + {r_tmp,g_tmp,b_tmp} = {3{grey}}; end end +reg [7:0] r_prev, g_prev, b_prev; +reg [7:0] r_cur, g_cur, b_cur; +reg [14:0] sgb_border_d; +reg hbl_l, vbl_l; +reg border_en; +always@(posedge clk_vid) begin + if (ce_pix) + {r_cur, g_cur, b_cur} <= {r_tmp, g_tmp, b_tmp}; + if (ce_pix_n) + {r_prev, g_prev, b_prev} <= {r_tmp, g_tmp, b_tmp}; + + if (ce_pix) begin + // visible area? + hbl_l <= sgb_en ? hb : gb_hb; + vbl_l <= sgb_en ? vb : gb_vb; + hbl <= hbl_l; + vbl <= vbl_l; + + // Allow backdrop color in border area and the border to overlap game area. + border_en <= ((gb_hb|gb_vb) & sgb_en) | sgb_border; + sgb_border_d <= sgb_border_pix[14:0]; + + if (border_en) begin + r <= {sgb_border_d[4:0],sgb_border_d[4:2]}; + g <= {sgb_border_d[9:5],sgb_border_d[9:7]}; + b <= {sgb_border_d[14:10],sgb_border_d[14:12]}; + end else if (frame_blend) begin + r <= blend(r_cur, r_prev); + g <= blend(g_cur, g_prev); + b <= blend(b_cur, b_prev); + end else begin + {r,g,b} <= {r_cur, g_cur, b_cur}; + end + end + +end endmodule diff --git a/rtl/link.v b/rtl/link.v index 4a771f2..7bf7ca0 100644 --- a/rtl/link.v +++ b/rtl/link.v @@ -69,7 +69,7 @@ always @(posedge clk) begin serial_clk_div <= serial_clk_div - 9'd1; if (serial_counter != 0) begin - if (serial_clk_div == {1'b0,CLK_DIV[8:1]+1'd1}) begin + if (serial_clk_div == CLK_DIV/2+1) begin serial_clk_out_r <= ~serial_clk_out_r; serial_out_r <= sb[7]; end else if (!serial_clk_div) begin diff --git a/rtl/video.v b/rtl/video.v index 3ceb281..40ec801 100644 --- a/rtl/video.v +++ b/rtl/video.v @@ -197,11 +197,12 @@ end wire h455 = (h_cnt == 9'd455); wire vblank = (v_cnt >= 144); -reg vblank_l, end_of_line, lyc_match_l; +reg vblank_l, end_of_line, end_of_line_l, lyc_match_l; always @(posedge clk) begin if (!lcd_on) begin vblank_l <= 1'b0; end_of_line <= 1'b0; + end_of_line_l <= 1'b0; end else if (ce) begin if (h455) end_of_line <= 1'b1; else if (end_of_line) begin @@ -216,6 +217,8 @@ always @(posedge clk) begin end_of_line <= 1'b0; end end + + end_of_line_l <= end_of_line; end end @@ -223,7 +226,7 @@ always @(posedge clk) begin if (reset) begin lyc_match_l <= 1'b0; // lyc_match does not reset when lcd is off end else if (ce) begin - if (h_cnt[1:0] == 2'b10) begin + if (h_cnt[1:0] == 2'b11) begin lyc_match_l <= lyc_match; end end @@ -244,7 +247,7 @@ always @(posedge clk) begin end wire int_lyc = (stat[6] & lyc_match_l); -wire int_oam = (stat[5] & end_of_line & ~vblank_l); +wire int_oam = (stat[5] & end_of_line_l & ~vblank_l); wire int_vbl = (stat[4] & vblank_l); wire int_hbl = (stat[3] & mode3_end & ~vblank_l);