|
---------------------------------------------------------------------------
|
|
-- (c) 2013 mark watson
|
|
-- I am happy for anyone to use this for non-commercial use.
|
|
-- If my vhdl files are used commercially or otherwise sold,
|
|
-- please contact me for explicit permission at scrameta (gmail).
|
|
-- This applies for source and binary form and derived works.
|
|
---------------------------------------------------------------------------
|
|
LIBRARY ieee;
|
|
USE ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
ENTITY scandoubler IS
|
|
GENERIC
|
|
(
|
|
video_bits : integer := 4
|
|
);
|
|
PORT
|
|
(
|
|
CLK : IN STD_LOGIC;
|
|
RESET_N : IN STD_LOGIC;
|
|
|
|
VGA : IN STD_LOGIC;
|
|
COMPOSITE_ON_HSYNC : in std_logic;
|
|
|
|
colour_enable : in std_logic;
|
|
doubled_enable : in std_logic;
|
|
|
|
scanlines_on : in std_logic := '0';
|
|
|
|
-- GTIA interface
|
|
pal : in std_logic;
|
|
colour_in : in std_logic_vector(7 downto 0);
|
|
vsync_in : in std_logic;
|
|
hsync_in : in std_logic;
|
|
csync_in : in std_logic;
|
|
|
|
-- TO TV...
|
|
R : OUT STD_LOGIC_vector(video_bits-1 downto 0);
|
|
G : OUT STD_LOGIC_vector(video_bits-1 downto 0);
|
|
B : OUT STD_LOGIC_vector(video_bits-1 downto 0);
|
|
|
|
VSYNC : out std_logic;
|
|
HSYNC : out std_logic
|
|
);
|
|
END scandoubler;
|
|
|
|
ARCHITECTURE vhdl OF scandoubler IS
|
|
|
|
component scandouble_ram_infer IS
|
|
PORT
|
|
(
|
|
clock: IN std_logic;
|
|
data: IN std_logic_vector (7 DOWNTO 0);
|
|
address: IN integer RANGE 0 to 1824;
|
|
we: IN std_logic;
|
|
q: OUT std_logic_vector (7 DOWNTO 0)
|
|
);
|
|
END component;
|
|
|
|
component delay_line IS
|
|
generic(COUNT : natural := 1);
|
|
PORT
|
|
(
|
|
CLK : IN STD_LOGIC;
|
|
SYNC_RESET : IN STD_LOGIC;
|
|
DATA_IN : IN STD_LOGIC;
|
|
ENABLE : IN STD_LOGIC; -- i.e. shift on this clock
|
|
RESET_N : IN STD_LOGIC;
|
|
|
|
DATA_OUT : OUT STD_LOGIC
|
|
);
|
|
END component;
|
|
|
|
signal colour_next : std_logic_vector(7 downto 0);
|
|
signal colour_reg : std_logic_vector(7 downto 0);
|
|
|
|
signal vsync_next : std_logic;
|
|
signal vsync_reg : std_logic;
|
|
|
|
signal hsync_next : std_logic;
|
|
signal hsync_reg : std_logic;
|
|
|
|
signal r_next : std_logic_vector(7 downto 0);
|
|
signal g_next : std_logic_vector(7 downto 0);
|
|
signal b_next : std_logic_vector(7 downto 0);
|
|
signal r_reg : std_logic_vector(7 downto 0);
|
|
signal g_reg : std_logic_vector(7 downto 0);
|
|
signal b_reg : std_logic_vector(7 downto 0);
|
|
|
|
signal linea_address : std_logic_vector(10 downto 0);
|
|
signal linea_address_integer : integer;
|
|
signal linea_write_enable : std_logic;
|
|
signal linea_out : std_logic_vector(7 downto 0);
|
|
|
|
signal lineb_address : std_logic_vector(10 downto 0);
|
|
signal lineb_address_integer : integer;
|
|
signal lineb_write_enable : std_logic;
|
|
signal lineb_out : std_logic_vector(7 downto 0);
|
|
|
|
signal input_address_next : std_logic_vector(10 downto 0);
|
|
signal input_address_reg : std_logic_vector(10 downto 0);
|
|
|
|
signal output_address_next : std_logic_vector(10 downto 0);
|
|
signal output_address_reg : std_logic_vector(10 downto 0);
|
|
|
|
signal buffer_select_next : std_logic;
|
|
signal buffer_select_reg : std_logic;
|
|
|
|
signal hsync_in_reg : std_logic;
|
|
|
|
signal vga_hsync_next : std_logic;
|
|
signal vga_hsync_reg : std_logic;
|
|
signal vga_hsync_start : std_logic;
|
|
signal vga_hsync_end : std_logic;
|
|
|
|
signal vga_odd_reg : std_logic;
|
|
signal vga_odd_next : std_logic;
|
|
|
|
signal reset_output_address : std_logic;
|
|
|
|
begin
|
|
-- register
|
|
process(clk,reset_n)
|
|
begin
|
|
if (reset_n = '0') then
|
|
r_reg <= (others=>'0');
|
|
g_reg <= (others=>'0');
|
|
b_reg <= (others=>'0');
|
|
colour_reg <= (others=>'0');
|
|
hsync_reg <= '0';
|
|
vsync_reg <= '0';
|
|
|
|
input_address_reg <= (others=>'0');
|
|
output_address_reg <= (others=>'0');
|
|
|
|
buffer_select_reg <= '0';
|
|
|
|
vga_hsync_reg <= '0';
|
|
|
|
vga_odd_reg <= '0';
|
|
elsif (clk'event and clk='1') then
|
|
r_reg <= r_next;
|
|
g_reg <= g_next;
|
|
b_reg <= b_next;
|
|
colour_reg <= colour_next;
|
|
hsync_reg <= hsync_next;
|
|
vsync_reg <= vsync_next;
|
|
|
|
input_address_reg <= input_address_next;
|
|
output_address_reg <= output_address_next;
|
|
|
|
buffer_select_reg <= buffer_select_next;
|
|
|
|
hsync_in_reg <= hsync_in;
|
|
|
|
vga_hsync_reg <= vga_hsync_next;
|
|
|
|
vga_odd_reg <= vga_odd_next;
|
|
end if;
|
|
end process;
|
|
|
|
-- TODO - these should use FPGA RAM - at present about 50% of FPGA is taken by these!!!
|
|
-- linea : reg_file
|
|
--generic map (BYTES=>456,WIDTH=>9)
|
|
--port map (clk=>clk,addr=>linea_address,wr_en=>linea_write_enable,data_in=>colour_in,data_out=>linea_out);
|
|
|
|
--lineb : reg_file
|
|
-- generic map (BYTES=>456,WIDTH=>9)
|
|
-- port map (clk=>clk,addr=>lineb_address,wr_en=>lineb_write_enable,data_in=>colour_in,data_out=>lineb_out);
|
|
|
|
linea_address_integer <= to_integer(unsigned(linea_address));
|
|
linea : scandouble_ram_infer
|
|
port map (clock=>clk,address=>linea_address_integer,we=>linea_write_enable,data=>colour_in,q=>linea_out);
|
|
|
|
lineb_address_integer <= to_integer(unsigned(lineb_address));
|
|
lineb : scandouble_ram_infer
|
|
port map (clock=>clk,address=>lineb_address_integer,we=>lineb_write_enable,data=>colour_in,q=>lineb_out);
|
|
|
|
-- capture
|
|
process(input_address_reg,colour_enable,hsync_in,hsync_in_reg,buffer_select_reg)
|
|
begin
|
|
input_address_next <= input_address_reg;
|
|
buffer_select_next <= buffer_select_reg;
|
|
|
|
linea_write_enable <= '0';
|
|
lineb_write_enable <= '0';
|
|
reset_output_address <= '0';
|
|
|
|
if (colour_enable = '1') then
|
|
input_address_next <= std_logic_vector(unsigned(input_address_reg)+1);
|
|
linea_write_enable <= buffer_select_reg;
|
|
lineb_write_enable <= not(buffer_select_reg);
|
|
end if;
|
|
|
|
if (hsync_in = '1' and hsync_in_reg = '0') then
|
|
input_address_next <= (others=>'0');
|
|
buffer_select_next <= not(buffer_select_reg);
|
|
reset_output_address <= '1';
|
|
end if;
|
|
end process;
|
|
|
|
-- output
|
|
process(vga_hsync_reg,vga_hsync_end,output_address_reg,doubled_enable,vga_odd_reg,reset_output_address)
|
|
begin
|
|
output_address_next <= output_address_reg;
|
|
vga_hsync_start<='0';
|
|
vga_hsync_next <= vga_hsync_reg;
|
|
vga_odd_next <= vga_odd_reg;
|
|
|
|
if (doubled_enable = '1') then
|
|
output_address_next <= std_logic_vector(unsigned(output_address_reg)+1);
|
|
|
|
if (output_address_reg = "111"&X"1F") then
|
|
output_address_next <= (others=>'0');
|
|
vga_hsync_start <= '1';
|
|
vga_hsync_next <= '1';
|
|
end if;
|
|
end if;
|
|
|
|
if (vga_hsync_end = '1') then
|
|
vga_hsync_next <= '0';
|
|
vga_odd_next <= not(vga_odd_reg);
|
|
end if;
|
|
|
|
if (reset_output_address = '1') then
|
|
output_address_next <= (others=>'0');
|
|
vga_odd_next <= '1';
|
|
end if;
|
|
end process;
|
|
|
|
linea_address <= input_address_reg when buffer_select_reg='1' else output_address_reg;
|
|
lineb_address <= input_address_reg when buffer_select_reg='0' else output_address_reg;
|
|
|
|
hsync_delay : delay_line
|
|
generic map (COUNT=>128)
|
|
port map(clk=>clk,sync_reset=>'0',data_in=>vga_hsync_start,enable=>doubled_enable,reset_n=>reset_n,data_out=>vga_hsync_end);
|
|
|
|
-- display
|
|
process(colour_reg,vsync_reg,vga_hsync_reg,hsync_reg,colour_in,csync_in,vsync_in,hsync_in,colour_enable,doubled_enable,vga,composite_on_hsync,buffer_select_reg,linea_out,lineb_out, scanlines_on, vga_odd_reg)
|
|
begin
|
|
colour_next <= colour_reg;
|
|
vsync_next <= vsync_reg;
|
|
hsync_next <= hsync_reg;
|
|
|
|
if (vga = '0') then
|
|
-- non-vga mode - pass through
|
|
colour_next <= colour_in;
|
|
--hsync_next <= not(hsync_in or vsync_in);
|
|
if (composite_on_hsync = '1') then
|
|
--hsync_next <= not(hsync_in xor vsync_in);
|
|
hsync_next <= not(csync_in);
|
|
vsync_next <='1';
|
|
else
|
|
hsync_next <= not(hsync_in);
|
|
vsync_next <= not(vsync_in);
|
|
end if;
|
|
else
|
|
-- vga mode, store all inputs - then play back!
|
|
if (buffer_select_reg = '0') then
|
|
if (scanlines_on ='1' and vga_odd_reg='1') then
|
|
colour_next(7 downto 4) <= linea_out(7 downto 4);
|
|
colour_next(3) <= '0';
|
|
colour_next(2 downto 0) <= linea_out(3 downto 1);
|
|
else
|
|
colour_next <= linea_out;
|
|
end if;
|
|
else
|
|
if (scanlines_on ='1' and vga_odd_reg='1') then
|
|
colour_next(7 downto 4) <= lineb_out(7 downto 4);
|
|
colour_next(3) <= '0';
|
|
colour_next(2 downto 0) <= lineb_out(3 downto 1);
|
|
else
|
|
colour_next <= lineb_out;
|
|
end if;
|
|
end if;
|
|
|
|
--hsync_next <= not(vga_hsync_reg);
|
|
if (composite_on_hsync = '1') then
|
|
hsync_next <= not(vga_hsync_reg xor vsync_in);
|
|
vsync_next <='1';
|
|
else
|
|
hsync_next <= not(vga_hsync_reg);
|
|
vsync_next <= not(vsync_in);
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- colour palette
|
|
palette4 : entity work.gtia_palette
|
|
port map (PAL=>pal,ATARI_COLOUR=>colour_reg, R_next=>R_next, G_next=>G_next, B_next=>B_next);
|
|
|
|
-- output
|
|
-- TODO - for DE2, output full 8 bits
|
|
R <= R_reg(7 downto 8-video_bits);
|
|
G <= G_reg(7 downto 8-video_bits);
|
|
B <= B_reg(7 downto 8-video_bits);
|
|
|
|
vsync<=vsync_reg;
|
|
hsync<=hsync_reg;
|
|
|
|
end vhdl;
|
|
|
|
|