1
0
mirror of https://github.com/UzixLS/zx-sizif-xxs.git synced 2025-07-19 07:11:28 +03:00

replace composite video output implementation

New one is borrowed from Speccy 2010 project. It's have a much, much
better output quality in price of increased FPGA resources usage.
This commit is contained in:
UzixLS
2021-11-15 22:57:36 +03:00
parent f30d6faa6d
commit e33bd97a7e
12 changed files with 645 additions and 566 deletions

View File

@ -1,149 +0,0 @@
// Based on Joerg Wolfram's code //
module chroma_gen #(
parameter CLK_FREQ
) (
input cg_clock, // input clock
input cg_enable, // colour enable
input cg_hsync, // hor. sync
input cg_pnsel, // system (pal/ntsc)
input [2:0] cg_rgb, // rgb input
output reg [2:0] cg_out // chroma out
);
localparam CARRIER_WIDTH =
(CLK_FREQ == 14_000_000)? 17 :
(CLK_FREQ == 14_318_180)? 17 :
(CLK_FREQ == 16_000_000)? 14 :
(CLK_FREQ == 17_734_475)? 3 :
(CLK_FREQ == 20_000_000)? 14 :
(CLK_FREQ == 24_000_000)? 17 :
(CLK_FREQ == 25_000_000)? 16 :
(CLK_FREQ == 28_000_000)? 18 :
(CLK_FREQ == 28_375_000)? 16 :
(CLK_FREQ == 28_636_360)? 18 :
(CLK_FREQ == 32_000_000)? 15 :
(CLK_FREQ == 35_468_950)? 3 :
(CLK_FREQ == 40_000_000)? 15 :
0;
localparam PAL_CARRIER =
(CLK_FREQ == 14_000_000)? 83018 : // 20.776 error
(CLK_FREQ == 14_318_180)? 81173 : // 11.72 error
(CLK_FREQ == 16_000_000)? 9080 : // 25 error
(CLK_FREQ == 17_734_475)? 4 : // 0 error
(CLK_FREQ == 20_000_000)? 7264 : // 25 error
(CLK_FREQ == 24_000_000)? 48427 : // 5.51 error
(CLK_FREQ == 25_000_000)? 23245 : // 13.14 error
(CLK_FREQ == 28_000_000)? 83018 : // 20.78 error
(CLK_FREQ == 28_375_000)? 20480 : // 25 error
(CLK_FREQ == 28_636_360)? 81173 : // 11.76 error
(CLK_FREQ == 32_000_000)? 9080 : // 25 error
(CLK_FREQ == 35_468_950)? 2 : // 0 error
(CLK_FREQ == 40_000_000)? 7264 : // 25 error
0;
localparam NTSC_CARRIER =
(CLK_FREQ == 14_000_000)? 67025 : // 23.82 error
(CLK_FREQ == 14_318_180)? 65536 : // 0 errror
(CLK_FREQ == 16_000_000)? 7331 : // 44.84 error
(CLK_FREQ == 17_734_475)? 4 : // 0 error (NTSC4.43)
(CLK_FREQ == 20_000_000)? 5865 : // 166.91 error
(CLK_FREQ == 24_000_000)? 39098 : // 16.19 error
(CLK_FREQ == 25_000_000)? 18767 : // 23.82 error
(CLK_FREQ == 28_000_000)? 67025 : // 23.82 error
(CLK_FREQ == 28_375_000)? 16535 : // 19.1 error
(CLK_FREQ == 28_636_360)? 65536 : // 0 error
(CLK_FREQ == 32_000_000)? 7331 : // 44.84 error
(CLK_FREQ == 35_468_950)? 2 : // 0 error (NTSC4.43)
(CLK_FREQ == 40_000_000)? 5865 : // 166.91 error
0;
// localparam PAL_CARRIER = 64'd17_734_475 * (1<<(CARRIER_WIDTH-1)) / CLK_FREQ;
// localparam NTSC_CARRIER = 64'd14_318_180 * (1<<(CARRIER_WIDTH-1)) / CLK_FREQ;
reg [CARRIER_WIDTH:0] carrier;
wire [31:0] carrier_next;
reg [3:0] burst_cnt;
wire burst;
reg oddeven;
reg [3:0] phase;
reg [3:0] scarrier;
wire cenable;
// DDS for PAL-carrier
assign carrier_next = (cg_pnsel == 1'b0)?
(carrier + PAL_CARRIER) :
(carrier + NTSC_CARRIER) ;
always @(posedge cg_clock) begin
carrier <= carrier_next[CARRIER_WIDTH:0];
end
// burst generator
always @(posedge carrier[CARRIER_WIDTH] or negedge cg_hsync) begin
if (cg_hsync == 1'b0)
burst_cnt <= 4'b0100;
else if (burst_cnt != 4'b0000)
burst_cnt <= burst_cnt + 1'b1;
end
assign burst = burst_cnt[3];
// odd/even line
always @(posedge cg_hsync) begin
if (cg_pnsel == 1'b0)
oddeven <= ~oddeven;
else
oddeven <= 1'b0;
end
// carrier phase
always @* begin
if (burst == 1'b1) begin
if ((oddeven == 1'b0) && (cg_pnsel == 1'b0))
phase <= 4'b0110; // burst phase 135 deg
else
phase <= 4'b1010; // burst phase -135 deg
end
else if (oddeven == 1'b0) begin
case (cg_rgb)
3'b001: phase <= 4'b0000; // blue phase
3'b010: phase <= 4'b0101; // red phase
3'b011: phase <= 4'b0011; // magenta phase
3'b100: phase <= 4'b1011; // green phase
3'b101: phase <= 4'b1101; // cyan phase
3'b110: phase <= 4'b0111; // yellow phase
default: phase <= 4'b0000; // dummy function
endcase
end
else begin
case (cg_rgb)
3'b001: phase <= 4'b0000; // blue phase
3'b010: phase <= 4'b1011; // red phase
3'b011: phase <= 4'b1101; // magenta phase
3'b100: phase <= 4'b0101; // green phase
3'b101: phase <= 4'b0011; // cyan phase
3'b110: phase <= 4'b1001; // yellow phase
default: phase <= 4'b0000; // dummy function
endcase
end
end
// modulated carrier
always @*
scarrier <= carrier[CARRIER_WIDTH:CARRIER_WIDTH-3] + phase;
// colour enable
assign cenable =
cg_enable == 1'b1 &&
cg_rgb != 3'b000 &&
cg_rgb != 3'b111;
// chroma signal
always @(posedge cg_clock) begin
cg_out[2] <= cenable;
cg_out[1] <= burst;
cg_out[0] <= scarrier[3];
end
endmodule

View File

@ -7,9 +7,9 @@ module screen(
input machine_t machine,
input [2:0] border,
output reg [2:0] r,
output reg [2:0] g,
output reg [1:0] b,
output reg [5:0] r,
output reg [5:0] g,
output reg [5:0] b,
output reg vsync,
output reg hsync,
output reg csync,
@ -287,14 +287,20 @@ always @(posedge clk28) begin
if (blank)
{g, r, b} = 0;
else if (up_en) begin
g = pixel? up_ink0[7:5] : up_paper0[7:5];
r = pixel? up_ink0[4:2] : up_paper0[4:2];
b = pixel? up_ink0[1:0] : up_paper0[1:0];
g[5:3] = pixel? up_ink0[7:5] : up_paper0[7:5];
r[5:3] = pixel? up_ink0[4:2] : up_paper0[4:2];
b[5:4] = pixel? up_ink0[1:0] : up_paper0[1:0];
g[2:0] = g[5:3];
r[2:0] = r[5:3];
b[3:0] = {b[5:4], b[5:4]};
end
else begin
{g[2], r[2], b[1]} = (pixel ^ (attr[7] & blink))? attr[2:0] : attr[5:3];
{g[1], r[1], b[0]} = {g[2] & attr[6], r[2] & attr[6], b[1] & attr[6]};
{g[0], r[0]} = {g[2], r[2]};
{g[5], r[5], b[5]} = (pixel ^ (attr[7] & blink))? attr[2:0] : attr[5:3];
{g[4], r[4], b[4]} = {g[5], r[5], b[5]};
{g[3], r[3], b[3]} = attr[6]? {g[5], r[5], b[5]} : 3'b000;
{g[2], r[2], b[2]} = {g[3], r[3], b[3]};
{g[1], r[1], b[1]} = {g[3], r[3], b[3]};
{g[0], r[0], b[0]} = {g[3], r[3], b[3]};
end
end

View File

@ -22,9 +22,8 @@ module zx_ula(
output reg n_int,
output n_nmi,
output reg [5:0] luma,
output reg [2:0] chroma,
output reg csync,
output [7:0] composite,
input [1:0] reserv,
output snd_l,
output snd_r,
@ -41,10 +40,15 @@ module zx_ula(
/* CLOCK */
wire clk28 = clk_in;
wire clk40;
wire clk20;
wire clk168;
wire rst_n;
pll pll0(.inclk0(clk_in), .c0(clk40), .c1(clk20), .locked(rst_n));
pll pll0(.inclk0(clk_in), .c0(clk168), .locked(rst_n));
reg [1:0] clk168_en42_cnt = 0;
reg clk168_en42;
always @(posedge clk168) begin
clk168_en42 <= clk168_en42_cnt == 2'b00;
clk168_en42_cnt <= clk168_en42_cnt + 1'b1;
end
/* SHARED DEFINITIONS */
@ -104,9 +108,8 @@ end
/* SCREEN CONTROLLER */
wire blink;
wire [2:0] screen_border = {border[2] ^ ~sd_cs, border[1] ^ magic_beeper, border[0]};
wire [2:0] r, g;
wire [1:0] b;
wire hsync;
wire [5:0] r, g, b;
wire hsync, vsync, csync0;
wire screen_contention, port_ff_active;
wire [14:0] screen_addr;
wire [5:0] up_ink_addr, up_paper_addr;
@ -124,8 +127,8 @@ screen screen0(
.r(r),
.g(g),
.b(b),
.csync(csync),
.vsync(),
.csync(csync0),
.vsync(vsync),
.hsync(hsync),
.fetch_allow((!bus.iorq && !bus.mreq && !bus.m1) || bus.rfsh || clkwait),
@ -157,21 +160,17 @@ screen screen0(
/* VIDEO OUTPUT */
always @*
luma <= 2*r + 5*g + b;
wire [2:0] chroma0;
chroma_gen #(.CLK_FREQ(40_000_000)) chroma_gen1(
.cg_clock(clk40),
.cg_rgb({|g,|r,|b}),
.cg_hsync(hsync),
.cg_enable(1'b1),
.cg_pnsel(1'b0),
.cg_out(chroma0)
vencode vencode(
.clk(clk168),
.clk_en(clk168_en42),
.videoR(r),
.videoG(g),
.videoB(b),
.videoHS_n(hsync),
.videoVS_n(vsync),
.videoPS_n(csync0),
.videoV(composite)
);
assign chroma[0] = (chroma0[1])? chroma0[0] : 1'bz;
assign chroma[1] = (chroma0[2])? chroma0[0] : 1'bz;
assign chroma[2] = (chroma0[2])? chroma0[0] : 1'bz;
/* PS/2 KEYBOARD */

194
fpga/rtl/vencode.vhd Normal file
View File

@ -0,0 +1,194 @@
-- Speccy 2010
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity vencode is
generic(
freq : integer := 42
);
port(
clk : in std_logic;
clk_en : in std_logic;
-- Video Input
videoR : in std_logic_vector(5 downto 0);
videoG : in std_logic_vector(5 downto 0);
videoB : in std_logic_vector(5 downto 0);
videoHS_n : in std_logic;
videoVS_n : in std_logic;
videoPS_n : in std_logic;
-- Video Output
videoY : out std_logic_vector(7 downto 0);
videoC : out std_logic_vector(7 downto 0);
videoV : out std_logic_vector(7 downto 0)
);
end vencode;
architecture rtl of vencode is
signal carrier : unsigned(7 downto 0);
signal vcounter : unsigned(8 downto 0);
signal hcounter : unsigned(12 downto 0);
signal burphase : unsigned(1 downto 0) := "00";
signal window_v : std_logic;
signal window_h : std_logic;
signal window_c : std_logic;
signal ivideoR : unsigned(5 downto 0);
signal ivideoG : unsigned(5 downto 0);
signal ivideoB : unsigned(5 downto 0);
signal Y1 : unsigned(13 downto 0);
signal Y2 : unsigned(13 downto 0);
signal Y3 : unsigned(13 downto 0);
signal U1 : unsigned(13 downto 0);
signal U2 : unsigned(13 downto 0);
signal U3 : unsigned(13 downto 0);
signal V1 : unsigned(13 downto 0);
signal V2 : unsigned(13 downto 0);
signal V3 : unsigned(13 downto 0);
signal burstUV : signed(13 downto 0);
signal prevY : signed(13 downto 0);
signal prevU : signed(13 downto 0);
signal prevV : signed(13 downto 0);
signal prevC : signed(23 downto 0);
signal inSin : std_logic_vector(15 downto 0);
signal inCos : std_logic_vector(15 downto 0);
signal sin : signed(15 downto 0);
signal cos : signed(15 downto 0);
signal pcos : signed(15 downto 0);
signal Um : signed(23 downto 0);
signal Vm : signed(23 downto 0);
signal Y : signed(7 downto 0);
signal U : signed(7 downto 0);
signal V : signed(7 downto 0);
signal C : signed(7 downto 0);
constant vref : signed(13 downto 0) := to_signed( 54, 8 ) & "000000";
constant cent : signed(7 downto 0) := X"80";
component vencode_sin_cos
port(
clk : in std_logic;
phase : in std_logic_vector(7 downto 0);
sinus : out std_logic_vector(15 downto 0);
cosinus : out std_logic_vector(15 downto 0)
);
end component;
begin
process(clk, clk_en )
variable carrierCounter : unsigned(15 downto 0);
begin
if ( clk'event and clk = '1' and clk_en = '1' ) then
carrierCounter := carrierCounter + 157;
if carrierCounter >= 6552 then
carrier <= carrier + 28;
carrierCounter := carrierCounter - 6552;
else
carrier <= carrier + 27;
end if;
end if;
end process;
vencode_sin_cos0 : vencode_sin_cos port map( clk, std_logic_vector( carrier ), inSin, inCos );
sin <= signed( inSin( 15 downto 0 ) );
cos <= signed( inCos( 15 downto 0 ) );
pcos <= cos when burphase(0) = '0' else -cos;
process(clk, clk_en )
variable ivideoVS_n : std_logic;
variable ivideoHS_n : std_logic;
begin
if ( clk'event and clk = '1' and clk_en = '1' ) then
Y <= prevY( 13 downto 6 );
Um <= U * sin;
Vm <= V * pcos;
videoY <= std_logic_vector( Y );
videoC <= std_logic_vector( cent + C );
videoV <= std_logic_vector( Y + C );
ivideoR <= unsigned( videoR );
ivideoG <= unsigned( videoG );
ivideoB <= unsigned( videoB );
if ( videoHS_n = '0' and ivideoHS_n = '1' ) then
vcounter <= vcounter + 1;
burphase <= burphase - 1;
hcounter <= ( others => '0' );
else
hcounter <= hcounter + 1;
end if;
if (videoVS_n = '0' and ivideoVS_n = '1') then
vcounter <= ( others => '0' );
end if;
if ( vcounter = ( 10 - 1 ) ) then
window_v <= '1';
elsif ( vcounter = ( 310 - 1 ) ) then
window_v <= '0';
end if;
if (hcounter = ( integer( 10.5 * real( freq ) ) - 1 ) ) then
window_h <= '1';
elsif ( hcounter = ( integer( 62.5 * real( freq ) ) - 1 ) ) then
window_h <= '0';
end if;
if ( hcounter = ( integer( 5.6 * real( freq ) ) - 1 ) ) then
window_c <= '1';
elsif ( hcounter = ( integer( 8.1 * real( freq ) ) - 1 ) ) then
window_c <= '0';
end if;
ivideoVS_n := videoVS_n;
ivideoHS_n := videoHS_n;
end if;
end process;
-- Y = 0.299 R + 0.587 G + 0.114 B
-- U = -0.147 R - 0.289 G + 0.436 B
-- V = 0.615 R - 0.515 G - 0.100 B
Y1 <= ( to_unsigned( 38, 8 ) * ivideoR );
Y2 <= ( to_unsigned( 75, 8 ) * ivideoG );
Y3 <= ( to_unsigned( 15, 8 ) * ivideoB );
U1 <= ( to_unsigned( 19, 8 ) * ivideoR );
U2 <= ( to_unsigned( 37, 8 ) * ivideoG );
U3 <= ( to_unsigned( 56, 8 ) * ivideoB );
V1 <= ( to_unsigned( 79, 8 ) * ivideoR );
V2 <= ( to_unsigned( 66, 8 ) * ivideoG );
V3 <= ( to_unsigned( 13, 8 ) * ivideoB );
prevY <= to_signed( 0, 14 ) when videoPS_n = '0' else
vref + signed( Y1 + Y2 + Y3 ) when ( window_h = '1' and window_v = '1' ) else
vref;
burstUV <= to_signed( 32, 8 ) & "000000" when ( window_c = '1' and window_v = '1' ) else
( others => '0' );
prevU <= signed( U3 - U1 - U2 ) when ( window_h = '1' and window_v = '1' ) else
( -burstUV );
prevV <= signed( V1 - V2 - V3 ) when ( window_h = '1' and window_v = '1' ) else
( burstUV );
U <= prevU( 13 downto 6 );
V <= prevV( 13 downto 6 );
prevC <= Um + Vm;
C <= prevC( 23 downto 16 );
end rtl;

View File

@ -0,0 +1,66 @@
module vencode_sin_cos(
input clk,
input [7:0] phase,
output reg [15:0] sinus,
output reg [15:0] cosinus
);
reg [6:0] addr1, addr2;
wire [15:0] value1, value2;
vencode_sin_cos_rom vencode_sin_cos_rom0(
.address_a(addr1),
.address_b(addr2),
.clock(clk),
.q_a(value1),
.q_b(value2)
);
always @* begin
if(!phase[6])
addr1 <= {1'b0, phase[5:0]};
else
addr1 <= 7'h40 - {1'b0, phase[5:0]};
if (!phase[7])
sinus <= value1;
else
sinus <= -value1;
end
wire [7:0] phase_cosinus = phase + 8'b01000000;
always @* begin
if(!phase_cosinus[6])
addr2 <= {1'b0, phase_cosinus[5:0]};
else
addr2 <= 7'h40 - {1'b0, phase_cosinus[5:0]};
if (!phase_cosinus[7])
cosinus <= value2;
else
cosinus <= -value2;
end
endmodule
module vencode_sin_cos_rom(
input [6:0] address_a,
input [6:0] address_b,
input clock,
output reg [15:0] q_a,
output reg [15:0] q_b
);
reg [15:0] rom [0:64];
initial begin
rom <= '{
16'h0000, 16'h025b, 16'h04b6, 16'h0710, 16'h0969, 16'h0bc0, 16'h0e16, 16'h106a,
16'h12bb, 16'h1509, 16'h1753, 16'h199b, 16'h1bde, 16'h1e1d, 16'h2057, 16'h228d,
16'h24bd, 16'h26e7, 16'h290c, 16'h2b2a, 16'h2d41, 16'h2f51, 16'h315b, 16'h335c,
16'h3556, 16'h3747, 16'h3930, 16'h3b10, 16'h3ce7, 16'h3eb4, 16'h4078, 16'h4232,
16'h43e2, 16'h4587, 16'h4722, 16'h48b1, 16'h4a36, 16'h4bae, 16'h4d1c, 16'h4e7d,
16'h4fd2, 16'h511b, 16'h5258, 16'h5387, 16'h54aa, 16'h55c0, 16'h56c8, 16'h57c4,
16'h58b1, 16'h5991, 16'h5a63, 16'h5b28, 16'h5bde, 16'h5c86, 16'h5d1f, 16'h5dab,
16'h5e28, 16'h5e96, 16'h5ef6, 16'h5f47, 16'h5f8a, 16'h5fbd, 16'h5fe2, 16'h5ff9,
16'h6000 };
end
always @(negedge clock) begin
q_a <= rom[address_a];
q_b <= rom[address_b];
end
endmodule