--
--   Copyright (C) 2003 by J. Kearney, Bolton, Massachusetts
--
--   This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
--   This program is distributed in the hope that it will be useful, but
-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-- for more details.
--
--   You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
--

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;

use work.VT52_cfg.ALL;

entity VideoGen is
	generic
		(CLKFREQ: integer := 29491200;
		 HCENTER: real := 0.50;
		 VCENTER: real := 0.50;
		 curs_blink: boolean := true;
		 curs_block: boolean := false;
       fore_color: RGB := "111";
       back_color: RGB := "000");
    port
	 	(clk : in std_logic;
       X : out COLUMN;
       Y : out ROW;
		 text_rd: out std_logic;
       text : in std_logic_vector(7 downto 0);
       curs_X : in COLUMN;
       curs_Y : in ROW;
		 invert_screen: in boolean;
       vga_RGB : out RGB;
       vga_HSYNC : out std_logic;
       vga_VSYNC : out std_logic);
end VideoGen;

architecture RTL of VideoGen is

	-- n.b. synthesizer doesn't like to use time type for calcs,
	-- so the constants are manually calculated

	-- total line and frame times
--	constant HTIME : time := 31.778 us;
--	constant VTIME : time := 14.286 ms;

	-- H and V sync pulse times
--	constant HSWIDTH : time := 3.813 us;
--	constant VSWIDTH : time := 63 us;

	-- one clock pulse
--	constant CLKTIME : time := 1 sec / CLKFREQ;

	-- number of pixels H and V
	constant HPSIZE : integer := 720;
	constant VPSIZE : integer := 384;	-- 24 lines of 16 rows

	-- total and sync clocks
	constant HTCLKS : integer := 937; -- HTIME / CLKTIME;
	constant HSCLKS : integer := 112; -- HSWIDTH / CLKTIME;
	constant VTLINES : integer := 450; -- VTIME / HTIME;
	constant VSLINES : integer := 2; -- VSWIDTH / HTIME;

	-- back and front porch clocks
	constant HBPCLKS : integer := 56; -- integer((HTCLKS - HSCLKS - HPSIZE) * HCENTER);
	constant HFPCLKS : integer := 881; --HTCLKS - HBPCLKS;
	constant VBPLINES : integer := 32; --integer((VTLINES - VSLINES - VPSIZE) * VCENTER);
	constant VFPLINES : integer := 32; --VTLINES - VBPLINES;

	component CG8x16 is
		port
			(clk : in std_logic;
			 char : in std_logic_vector(6 downto 0);
          row : in integer range 0 to 15;
          outp : out std_logic_vector(7 downto 0));
	end component;

	signal cstate: integer range 0 to 8;
	signal scan_x: COLUMN;
	signal scan_y: ROW;
	signal scan_cell: integer range 0 to CELLV-1;

	signal vidsr: std_logic_vector(8 downto 0);
	signal cursor, invert, visible_invert: boolean;

	signal hpos: integer range 0 to HTCLKS;
	signal vpos: integer range 0 to 450;
	signal eoh, acth,
			 hsync, vsync: boolean;
	signal actv: std_logic;

	signal curs_count: integer range 0 to 15;
	signal cur_blink: boolean;

	signal cg_out: std_logic_vector(7 downto 0);

begin

	X <= scan_x;
	Y <= scan_y;

	cg: CG8x16 port map
		(clk => clk, char => text(6 downto 0),
		 row => scan_cell, outp => cg_out);

	visible_invert <= invert_screen and acth and (actv = '1');

	vga_RGB <= fore_color when (vidsr(8) = '1') /= visible_invert else back_color;
	vga_HSYNC <= '0' when hsync else '1';
	vga_VSYNC <= '1' when vsync else '0';

	-- horizontal counter
	process (clk, hpos)
	begin
--		if reset then
--			hpos <= 0;
--			hsync <= false;
--			eoh <= false;
--			acth <= false;
--		els
		if rising_edge(clk) then
			if hpos = HTCLKS - 1 then
				hpos <= 0;
				eoh <= true;
			else
				hpos <= hpos + 1;
				eoh <= false;
				if hpos = 0 then
					hsync <= true;
				elsif hpos = HSCLKS - 1 then
					hsync <= false;
				elsif hpos = HSCLKS + HBPCLKS - 2 then
					acth <= true;
				elsif hpos = HSCLKS + HBPCLKS + HPSIZE then
					acth <= false;
				end if;
			end if;
		end if;
	end process;

	-- vertical counter
	process (clk, eoh)
	begin
--		if reset then
--			vpos <= 0;
--			vsync <= false;
--			actv <= '0';
--		els
		if rising_edge(clk) then
			if eoh then
				if vpos = VTLINES - 1 then
					vpos <= 0;
				else
					vpos <= vpos + 1;
					if vpos = 0 then
						vsync <= true;
					elsif vpos = VSLINES then
						vsync <= false;
					elsif vpos = VSLINES + VBPLINES then
						actv <= '1';
					elsif vpos = VSLINES + VBPLINES + VPSIZE then
						actv <= '0';
					end if;
				end if;
			end if;
		end if;
	end process;

	-- generate blink for cursor ~ 2Hz
	process (actv)
	begin
		if falling_edge(actv) then
			if curs_count = 15 then
				curs_count <= 0;
				cur_blink <= not cur_blink;
			else
				curs_count <= curs_count + 1;
			end if;
		end if;
	end process;

	-- character generation
	cursor <= (curs_X = scan_X) and (curs_Y = scan_Y) and
				 (curs_block or ((scan_cell = 2) or (scan_cell = 3)));

	invert <= (cursor and cur_blink) or (text(7) = '1');

	text_rd <= '1' when cstate = 0 else '0';

	process (clk, eoh, acth, actv)
	begin
		if rising_edge(clk) then
			if actv = '0' then
				scan_X <= 0;
				scan_y <= 0;
				scan_cell <= 15;
				cstate <= 0;
				vidsr <= (others => '0');
			elsif not acth then
				cstate <= 0;
				vidsr <= (others => '0');
			else

				if cstate = 8 then
					cstate <= 0;
				else
					cstate <= cstate + 1;
				end if;

				vidsr(8 downto 1) <= vidsr(7 downto 0);

				case cstate is

					-- when 0 =>	-- character is read
					-- when 1 =>	-- c.g. is read

					when 2 =>
						if invert then
							vidsr <= '1' & (not cg_out);
						else
							vidsr <= '0' & cg_out;
						end if;

					when 3 =>

						if scan_x = 79 then
							scan_x <= 0;
							if scan_cell = 0 then
								scan_cell <= 15;
								scan_y <= scan_y + 1;
							else
								scan_cell <= scan_cell - 1;
							end if;
						else
							scan_x <= scan_x + 1;
						end if;

					when others =>
						null;

				end case;

			end if;
		end if;
	end process;

end RTL;
