mirror of
https://github.com/nippur72/Apple1_MiST.git
synced 2025-02-28 18:29:27 +00:00
243 lines
7.5 KiB
Systemverilog
243 lines
7.5 KiB
Systemverilog
//
|
|
//
|
|
// Copyright (c) 2017 Sorgelig
|
|
//
|
|
// This program is GPL Licensed. See COPYING for the full license.
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
`timescale 1ns / 1ps
|
|
|
|
//
|
|
// LINE_LENGTH: Length of display line in pixels
|
|
// Usually it's length from HSync to HSync.
|
|
// May be less if line_start is used.
|
|
//
|
|
// HALF_DEPTH: If =1 then color dept is 3 bits per component
|
|
// For half depth 6 bits monochrome is available with
|
|
// mono signal enabled and color = {G, R}
|
|
|
|
module video_mixer
|
|
#(
|
|
parameter LINE_LENGTH = 768,
|
|
parameter HALF_DEPTH = 0,
|
|
|
|
parameter OSD_COLOR = 3'd4,
|
|
parameter OSD_X_OFFSET = 10'd0,
|
|
parameter OSD_Y_OFFSET = 10'd0
|
|
)
|
|
(
|
|
// master clock
|
|
// it should be multiple by (ce_pix*4).
|
|
input clk_sys,
|
|
|
|
// Pixel clock or clock_enable (both are accepted).
|
|
input ce_pix,
|
|
|
|
// Some systems have multiple resolutions.
|
|
// ce_pix_actual should match ce_pix where every second or fourth pulse is enabled,
|
|
// thus half or qurter resolutions can be used without brake video sync while switching resolutions.
|
|
// For fixed single resolution (or when video sync stability isn't required) ce_pix_actual = ce_pix.
|
|
input ce_pix_actual,
|
|
|
|
// OSD SPI interface
|
|
input SPI_SCK,
|
|
input SPI_SS3,
|
|
input SPI_DI,
|
|
|
|
// scanlines (00-none 01-25% 10-50% 11-75%)
|
|
input [1:0] scanlines,
|
|
|
|
// 0 = HVSync 31KHz, 1 = CSync 15KHz
|
|
input scandoubler_disable,
|
|
|
|
// High quality 2x scaling
|
|
input hq2x,
|
|
|
|
// YPbPr always uses composite sync
|
|
input ypbpr,
|
|
|
|
// 0 = 16-240 range. 1 = 0-255 range. (only for YPbPr color space)
|
|
input ypbpr_full,
|
|
|
|
// color
|
|
input [DWIDTH:0] R,
|
|
input [DWIDTH:0] G,
|
|
input [DWIDTH:0] B,
|
|
|
|
// Monochrome mode (for HALF_DEPTH only)
|
|
input mono,
|
|
|
|
// interlace sync. Positive pulses.
|
|
input HSync,
|
|
input VSync,
|
|
|
|
// Falling of this signal means start of informative part of line.
|
|
// It can be horizontal blank signal.
|
|
// This signal can be used to reduce amount of required FPGA RAM for HQ2x scan doubler
|
|
// If FPGA RAM is not an issue, then simply set it to 0 for whole line processing.
|
|
// Keep in mind: due to algo first and last pixels of line should be black to avoid side artefacts.
|
|
// Thus, if blank signal is used to reduce the line, make sure to feed at least one black (or paper) pixel
|
|
// before first informative pixel.
|
|
input line_start,
|
|
|
|
// MiST video output signals
|
|
output [5:0] VGA_R,
|
|
output [5:0] VGA_G,
|
|
output [5:0] VGA_B,
|
|
output VGA_VS,
|
|
output VGA_HS
|
|
);
|
|
|
|
localparam DWIDTH = HALF_DEPTH ? 2 : 5;
|
|
|
|
wire [DWIDTH:0] R_sd;
|
|
wire [DWIDTH:0] G_sd;
|
|
wire [DWIDTH:0] B_sd;
|
|
wire hs_sd, vs_sd;
|
|
|
|
scandoubler #(.LENGTH(LINE_LENGTH), .HALF_DEPTH(HALF_DEPTH)) scandoubler
|
|
(
|
|
.*,
|
|
.hs_in(HSync),
|
|
.vs_in(VSync),
|
|
.r_in(R),
|
|
.g_in(G),
|
|
.b_in(B),
|
|
|
|
.hs_out(hs_sd),
|
|
.vs_out(vs_sd),
|
|
.r_out(R_sd),
|
|
.g_out(G_sd),
|
|
.b_out(B_sd)
|
|
);
|
|
|
|
wire [DWIDTH:0] rt = (scandoubler_disable ? R : R_sd);
|
|
wire [DWIDTH:0] gt = (scandoubler_disable ? G : G_sd);
|
|
wire [DWIDTH:0] bt = (scandoubler_disable ? B : B_sd);
|
|
|
|
generate
|
|
if(HALF_DEPTH) begin
|
|
wire [5:0] r = mono ? {gt,rt} : {rt,rt};
|
|
wire [5:0] g = mono ? {gt,rt} : {gt,gt};
|
|
wire [5:0] b = mono ? {gt,rt} : {bt,bt};
|
|
end else begin
|
|
wire [5:0] r = rt;
|
|
wire [5:0] g = gt;
|
|
wire [5:0] b = bt;
|
|
end
|
|
endgenerate
|
|
|
|
wire hs = (scandoubler_disable ? HSync : hs_sd);
|
|
wire vs = (scandoubler_disable ? VSync : vs_sd);
|
|
|
|
reg scanline = 0;
|
|
always @(posedge clk_sys) begin
|
|
reg old_hs, old_vs;
|
|
|
|
old_hs <= hs;
|
|
old_vs <= vs;
|
|
|
|
if(old_hs && ~hs) scanline <= ~scanline;
|
|
if(old_vs && ~vs) scanline <= 0;
|
|
end
|
|
|
|
wire [5:0] r_out, g_out, b_out;
|
|
always @(*) begin
|
|
case(scanlines & {scanline, scanline})
|
|
1: begin // reduce 25% = 1/2 + 1/4
|
|
r_out = {1'b0, r[5:1]} + {2'b00, r[5:2]};
|
|
g_out = {1'b0, g[5:1]} + {2'b00, g[5:2]};
|
|
b_out = {1'b0, b[5:1]} + {2'b00, b[5:2]};
|
|
end
|
|
|
|
2: begin // reduce 50% = 1/2
|
|
r_out = {1'b0, r[5:1]};
|
|
g_out = {1'b0, g[5:1]};
|
|
b_out = {1'b0, b[5:1]};
|
|
end
|
|
|
|
3: begin // reduce 75% = 1/4
|
|
r_out = {2'b00, r[5:2]};
|
|
g_out = {2'b00, g[5:2]};
|
|
b_out = {2'b00, b[5:2]};
|
|
end
|
|
|
|
default: begin
|
|
r_out = r;
|
|
g_out = g;
|
|
b_out = b;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
wire [5:0] red, green, blue;
|
|
osd #(OSD_X_OFFSET, OSD_Y_OFFSET, OSD_COLOR) osd
|
|
(
|
|
.*,
|
|
|
|
.R_in(r_out),
|
|
.G_in(g_out),
|
|
.B_in(b_out),
|
|
.HSync(hs),
|
|
.VSync(vs),
|
|
|
|
.R_out(red),
|
|
.G_out(green),
|
|
.B_out(blue)
|
|
);
|
|
|
|
wire [5:0] yuv_full[225] = '{
|
|
6'd0, 6'd0, 6'd0, 6'd0, 6'd1, 6'd1, 6'd1, 6'd1,
|
|
6'd2, 6'd2, 6'd2, 6'd3, 6'd3, 6'd3, 6'd3, 6'd4,
|
|
6'd4, 6'd4, 6'd5, 6'd5, 6'd5, 6'd5, 6'd6, 6'd6,
|
|
6'd6, 6'd7, 6'd7, 6'd7, 6'd7, 6'd8, 6'd8, 6'd8,
|
|
6'd9, 6'd9, 6'd9, 6'd9, 6'd10, 6'd10, 6'd10, 6'd11,
|
|
6'd11, 6'd11, 6'd11, 6'd12, 6'd12, 6'd12, 6'd13, 6'd13,
|
|
6'd13, 6'd13, 6'd14, 6'd14, 6'd14, 6'd15, 6'd15, 6'd15,
|
|
6'd15, 6'd16, 6'd16, 6'd16, 6'd17, 6'd17, 6'd17, 6'd17,
|
|
6'd18, 6'd18, 6'd18, 6'd19, 6'd19, 6'd19, 6'd19, 6'd20,
|
|
6'd20, 6'd20, 6'd21, 6'd21, 6'd21, 6'd21, 6'd22, 6'd22,
|
|
6'd22, 6'd23, 6'd23, 6'd23, 6'd23, 6'd24, 6'd24, 6'd24,
|
|
6'd25, 6'd25, 6'd25, 6'd25, 6'd26, 6'd26, 6'd26, 6'd27,
|
|
6'd27, 6'd27, 6'd27, 6'd28, 6'd28, 6'd28, 6'd29, 6'd29,
|
|
6'd29, 6'd29, 6'd30, 6'd30, 6'd30, 6'd31, 6'd31, 6'd31,
|
|
6'd31, 6'd32, 6'd32, 6'd32, 6'd33, 6'd33, 6'd33, 6'd33,
|
|
6'd34, 6'd34, 6'd34, 6'd35, 6'd35, 6'd35, 6'd35, 6'd36,
|
|
6'd36, 6'd36, 6'd36, 6'd37, 6'd37, 6'd37, 6'd38, 6'd38,
|
|
6'd38, 6'd38, 6'd39, 6'd39, 6'd39, 6'd40, 6'd40, 6'd40,
|
|
6'd40, 6'd41, 6'd41, 6'd41, 6'd42, 6'd42, 6'd42, 6'd42,
|
|
6'd43, 6'd43, 6'd43, 6'd44, 6'd44, 6'd44, 6'd44, 6'd45,
|
|
6'd45, 6'd45, 6'd46, 6'd46, 6'd46, 6'd46, 6'd47, 6'd47,
|
|
6'd47, 6'd48, 6'd48, 6'd48, 6'd48, 6'd49, 6'd49, 6'd49,
|
|
6'd50, 6'd50, 6'd50, 6'd50, 6'd51, 6'd51, 6'd51, 6'd52,
|
|
6'd52, 6'd52, 6'd52, 6'd53, 6'd53, 6'd53, 6'd54, 6'd54,
|
|
6'd54, 6'd54, 6'd55, 6'd55, 6'd55, 6'd56, 6'd56, 6'd56,
|
|
6'd56, 6'd57, 6'd57, 6'd57, 6'd58, 6'd58, 6'd58, 6'd58,
|
|
6'd59, 6'd59, 6'd59, 6'd60, 6'd60, 6'd60, 6'd60, 6'd61,
|
|
6'd61, 6'd61, 6'd62, 6'd62, 6'd62, 6'd62, 6'd63, 6'd63,
|
|
6'd63
|
|
};
|
|
|
|
// http://marsee101.blog19.fc2.com/blog-entry-2311.html
|
|
// Y = 16 + 0.257*R + 0.504*G + 0.098*B (Y = 0.299*R + 0.587*G + 0.114*B)
|
|
// Pb = 128 - 0.148*R - 0.291*G + 0.439*B (Pb = -0.169*R - 0.331*G + 0.500*B)
|
|
// Pr = 128 + 0.439*R - 0.368*G - 0.071*B (Pr = 0.500*R - 0.419*G - 0.081*B)
|
|
|
|
wire [18:0] y_8 = 19'd04096 + ({red, 8'd0} + {red, 3'd0}) + ({green, 9'd0} + {green, 2'd0}) + ({blue, 6'd0} + {blue, 5'd0} + {blue, 2'd0});
|
|
wire [18:0] pb_8 = 19'd32768 - ({red, 7'd0} + {red, 4'd0} + {red, 3'd0}) - ({green, 8'd0} + {green, 5'd0} + {green, 3'd0}) + ({blue, 8'd0} + {blue, 7'd0} + {blue, 6'd0});
|
|
wire [18:0] pr_8 = 19'd32768 + ({red, 8'd0} + {red, 7'd0} + {red, 6'd0}) - ({green, 8'd0} + {green, 6'd0} + {green, 5'd0} + {green, 4'd0} + {green, 3'd0}) - ({blue, 6'd0} + {blue , 3'd0});
|
|
|
|
wire [7:0] y = ( y_8[17:8] < 16) ? 8'd16 : ( y_8[17:8] > 235) ? 8'd235 : y_8[15:8];
|
|
wire [7:0] pb = (pb_8[17:8] < 16) ? 8'd16 : (pb_8[17:8] > 240) ? 8'd240 : pb_8[15:8];
|
|
wire [7:0] pr = (pr_8[17:8] < 16) ? 8'd16 : (pr_8[17:8] > 240) ? 8'd240 : pr_8[15:8];
|
|
|
|
assign VGA_R = ypbpr ? (ypbpr_full ? yuv_full[pr-8'd16] : pr[7:2]) : red;
|
|
assign VGA_G = ypbpr ? (ypbpr_full ? yuv_full[y -8'd16] : y[7:2]) : green;
|
|
assign VGA_B = ypbpr ? (ypbpr_full ? yuv_full[pb-8'd16] : pb[7:2]) : blue;
|
|
assign VGA_VS = (scandoubler_disable | ypbpr) ? 1'b1 : ~vs_sd;
|
|
assign VGA_HS = scandoubler_disable ? ~(HSync ^ VSync) : ypbpr ? ~(hs_sd ^ vs_sd) : ~hs_sd;
|
|
|
|
endmodule
|