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


