--------------------------------------------------------------------------------
--         .XXXXXXXXXXXXXXXX.  .XXXXXXXXXXXXXXXX.  .XX.                       --
--         XXXXXXXXXXXXXXXXX'  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         XXXX                XXXX          XXXX  XXXX                       --
--         XXXXXXXXXXXXXXXXX.  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         'XXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXX'  XXXX                       --
--                       XXXX  XXXX                XXXX                       --
--         .XXXXXXXXXXXXXXXXX  XXXX                XXXXXXXXXXXXXXXXX.         --
--         'XXXXXXXXXXXXXXXX'  'XX'                'XXXXXXXXXXXXXXXX'         --
--------------------------------------------------------------------------------
--             Copyright 2023 Vittorio Pascucci (SideProjectsLab)             --
--                                                                            --
-- Licensed under the GNU General Public License, Version 3 (the "License");  --
-- you may not use this file except in compliance with the License.           --
-- You may obtain a copy of the License at                                    --
--                                                                            --
--     https://www.gnu.org/licenses/gpl-3.0.en.html#license-text              --
--                                                                            --
-- Unless required by applicable law or agreed to in writing, software        --
-- distributed under the License is distributed on an "AS IS" BASIS,          --
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   --
-- See the License for the specific language governing permissions and        --
-- limitations under the License.                                             --
--------------------------------------------------------------------------------

library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
use     ieee.math_real.all;

library work;
use     work.qol_pkg.all;
use     work.vic_pkg.all;

entity dot_clock is
generic
(
	g_clk_frq : real
);
port
(
	clk   : in  std_wire;
	rst   : in  std_wire;

	i_phi   : in  std_wire;
	o_dot   : out std_wire;
	o_phi   : out std_wire;
	o_clk   : out std_wire
);
end entity;

architecture rtl of dot_clock is

	function calc_toggle_times(i_phi_frq : real; sys_frq : real; mult : positive)
	return integer_vector is
		variable ret : integer_vector(0 to mult * 2 - 1);
	begin
		for i in 0 to ret'length - 2 loop
			ret(i) := integer(round(((sys_frq / i_phi_frq) / real(ret'length)) * real(i + 1)));
		end loop;
		ret(ret'length - 1) := 0;
		return ret;
	end function;

	constant c_mult         : positive := 16;
	constant c_i_phi_frq_pal  : real := 0.985e6;
	constant c_i_phi_frq_ntsc : real := 1.023e6;

	constant c_max_prd_pal   : integer := integer(ceil(g_clk_frq / c_i_phi_frq_pal));
	constant c_max_prd_ntsc  : integer := integer(ceil(g_clk_frq / c_i_phi_frq_ntsc));
	constant c_prd_thr       : integer := (c_max_prd_pal + c_max_prd_ntsc) / 2;
	constant c_max_prd       : integer := c_max_prd_pal;
	constant c_toggle_pal    : integer_vector := calc_toggle_times(c_i_phi_frq_pal , g_clk_frq, c_mult);
	constant c_toggle_ntsc   : integer_vector := calc_toggle_times(c_i_phi_frq_ntsc, g_clk_frq, c_mult);

	signal rst_1r     : std_wire := '1';
	signal i_phi_mf   : std_wire;
	signal i_phi_1r   : std_wire;
	signal prd        : natural range 0 to c_max_prd;
	signal toggle     : c_toggle_pal'subtype;
	signal toggle_val : integer range 0 to c_max_prd;
	signal count      : natural range 0 to toggle'length;

begin

	i_i_phi_mf : entity work.multi_flop
	generic map
	(
		g_num_stages => 5
	)
	port map
	(
		i_data(0) => i_phi,
		o_clk     => clk,
		o_rst     => rst,
		o_data(0) => i_phi_mf
	);

	o_phi <= i_phi_mf;

	p_dot_clock : process(clk) is
	begin
		if rising_edge(clk) then

			if prd = toggle_val then
				o_clk      <= not o_clk;
				count      <= count + 1; -- cannot wrap because the last element of the toggle array is zero
				toggle_val <= toggle(count);

				-- the "dot"
				if o_clk = '1' then
					o_dot <= not o_dot;
				end if;
			end if;

			-- calculating the period
			if prd /= c_max_prd then
				prd <= prd + 1;
			end if;

			i_phi_1r <= i_phi_mf;
			if (i_phi_1r = '1') and (i_phi_mf = '0') then
				prd   <= 1;
				o_clk <= '1';
				o_dot <= '0';
				count <= 1;

				if prd > c_prd_thr then
					toggle     <= c_toggle_pal;
					toggle_val <= c_toggle_pal(0);
				else
					toggle     <= c_toggle_ntsc;
					toggle_val <= c_toggle_ntsc(0);
				end if;
			end if;

			rst_1r <= rst;
			if rst_1r then
				prd      <= 0;
				i_phi_1r <= '0';
				o_dot    <= '0';

				o_clk    <= '0';
			end if;
		end if;


--------------------------------------------------------------------------------
--         .XXXXXXXXXXXXXXXX.  .XXXXXXXXXXXXXXXX.  .XX.                       --
--         XXXXXXXXXXXXXXXXX'  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         XXXX                XXXX          XXXX  XXXX                       --
--         XXXXXXXXXXXXXXXXX.  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         'XXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXX'  XXXX                       --
--                       XXXX  XXXX                XXXX                       --
--         .XXXXXXXXXXXXXXXXX  XXXX                XXXXXXXXXXXXXXXXX.         --
--         'XXXXXXXXXXXXXXXX'  'XX'                'XXXXXXXXXXXXXXXX'         --
--------------------------------------------------------------------------------
--             Copyright 2023 Vittorio Pascucci (SideProjectsLab)             --
--                                                                            --
-- Licensed under the GNU General Public License, Version 3 (the "License");  --
-- you may not use this file except in compliance with the License.           --
-- You may obtain a copy of the License at                                    --
--                                                                            --
--     https://www.gnu.org/licenses/gpl-3.0.en.html#license-text              --
--                                                                            --
-- Unless required by applicable law or agreed to in writing, software        --
-- distributed under the License is distributed on an "AS IS" BASIS,          --
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   --
-- See the License for the specific language governing permissions and        --
-- limitations under the License.                                             --
--------------------------------------------------------------------------------

library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
use     ieee.math_real.all;

library work;
use     work.qol_pkg.all;
use     work.vic_pkg.all;

entity graphics_gen is
port
(
	clk     : in  std_wire;
	rst     : in  std_wire;

	reg     : in  t_regs;
	strb    : in  t_strb;

	i_actv  : in  std_wire;
	i_vbrd  : in  std_wire;
	i_grfx  : in  std_word( 7 downto 0);
	i_data  : in  std_word(11 downto 0);
	o_bgnd  : out std_wire;
	o_colr  : out t_colr;

	-- diagnostics & debug
	mark_mode : in  std_wire
);
end entity;

architecture rtl of graphics_gen is

	constant c_shreg_len : natural := 8;

	signal rst_1r    : std_wire := '1';

	signal shreg     : unsigned(c_shreg_len - 1 downto 0);
	signal xscroll   : natural range 0 to 7;

	signal actv_1r   : std_wire;
	signal actv_2r   : std_wire;

	signal vbrd_1r   : std_wire;

	signal grfx_1r   : i_grfx'subtype;
	signal grfx_2r   : i_grfx'subtype;

	signal data_1r   : i_data'subtype;
	signal data_2r   : i_data'subtype;
	signal data_3r   : i_data'subtype;
	signal mc_phy    : std_wire;
	signal bg_colr_1r : t_colr_vector(0 to 3);
	signal bg_colr_2r : t_colr_vector(0 to 3);

	signal ecm_1r     : std_wire;
	signal ecm_2r     : std_wire;
	signal ecm_3r     : std_wire;

	signal mcm_1r     : std_wire;
	signal mcm_2r     : std_wire;
	signal mcm_3r     : std_wire;

	signal bmm_1r     : std_wire;
	signal bmm_2r     : std_wire;
	signal bmm_3r     : std_wire;

	signal ecm        : std_wire;
	signal mcm        : std_wire;
	signal bmm        : std_wire;
	signal mcm_old    : std_wire;

	signal gfx_val    : unsigned(1 downto 0);
	signal bg_sel     : unsigned(1 downto 0);
	signal gfx_bgnd   : std_wire;

	function get_mode(ecm : std_wire; bmm : std_wire; mcm : std_wire) return t_vic_mode is
		variable mode : std_word(2 downto 0) := ecm & bmm & mcm;
	begin
		case mode is
			when "000" =>
				return MODE_STD_TEXT;
			when "001" =>
				return MODE_MCL_TEXT;
			when "010" =>
				return MODE_STD_BMAP;
			when "011" =>
				return MODE_MCL_BMAP;
			when "100" =>
				return MODE_ECM_TEXT;
			when others =>
				return MODE_INVALID;
		end case;
	end function;

begin

	p_serialize : process(clk) is
		variable v_mode      : t_vic_mode;
		variable v_mc_flag   : std_wire;
		variable v_data_colr : i_data'subtype;
		variable v_gfx_colr  : t_colr_vector(0 to 3);
		variable v_gfx_val   : unsigned(1 downto 0);
		variable v_gfx_bgnd  : std_wire;
		variable v_bg_sel    : unsigned(1 downto 0);
	begin
		if rising_edge(clk) then

			if strb(0) = '1' then

				----------------------------------------------------------------
				--                   LATCHING NEW CHARACTER                   --
				----------------------------------------------------------------

				if strb = 15 then
					actv_2r <= actv_1r;
					grfx_2r <= grfx_1r;
					data_2r <= data_1r;

					if i_actv then
						xscroll <= to_integer(unsigned(reg(22)(2 downto 0)));
					end if;

					if i_actv then
						grfx_1r <= i_grfx;
						data_1r <= i_data;
						actv_1r <= i_actv;
					else
						data_1r <= (others => '0');
						actv_1r <= '0';
					end if;
				end if;

				----------------------------------------------------------------
				--                     LATCHING MODE FLAGS                    --
				----------------------------------------------------------------

				mc_phy <= not mc_phy;

				ecm_1r <= reg(17)(6);
				ecm_2r <= ecm_1r;
				ecm_3r <= ecm_2r;

				bmm_1r <= reg(17)(5);
				bmm_2r <= bmm_1r;
				bmm_3r <= bmm_2r;

				mcm_1r <= reg(22)(4);
				mcm_2r <= mcm_1r;
				mcm_3r <= mcm_2r;

				vbrd_1r <= i_vbrd;

				if strb = 1 then
					ecm <= ecm or ecm_3r;
					bmm <= bmm or bmm_3r;
				end if;

				if strb = 3 then
					ecm <= ecm and ecm_3r;
					bmm <= bmm and bmm_3r;
				end if;

				if strb = 9 then
					mcm <= mcm_3r;
				end if;

				if strb = 15 then
					mcm_old <= mcm;
					if mcm_old /= mcm then
						mc_phy <= '1';
					end if;
				end if;

				----------------------------------------------------------------
				--                   LOADING SHIFT REGISTER                   --
				----------------------------------------------------------------

				shreg <= lshift(shreg, 1);

				if rshift(strb, 1) = xscroll then
					if (actv_2r = '1') or (strb < 7) then
						mc_phy <= '0';
					end if;

					if actv_2r then
						data_3r           <= data_2r;
						shreg(7 downto 0) <= unsigned(grfx_2r);
					end if;
				end if;

				----------------------------------------------------------------
				--                       COLOR LATCHING                       --
				----------------------------------------------------------------

				bg_colr_1r(0) <= unsigned(reg(33)(3 downto 0));
				bg_colr_1r(1) <= unsigned(reg(34)(3 downto 0));
				bg_colr_1r(2) <= unsigned(reg(35)(3 downto 0));
				bg_colr_1r(3) <= unsigned(reg(36)(3 downto 0));
				bg_colr_2r <= bg_colr_1r;

				----------------------------------------------------------------
				--                       COLOR SELECTION                      --
				----------------------------------------------------------------

				v_mc_flag := data_3r(11);

				if mcm_old then
					if bmm or v_mc_flag then
						if mc_phy = '0' then
							v_gfx_val  := rresize(shreg, 2);
							v_gfx_bgnd := not shreg(shreg'high);
						else
							v_gfx_val  := gfx_val;
							v_gfx_bgnd := gfx_bgnd;
						end if;
					else
						v_gfx_val  := (others => shreg(shreg'high));
						v_gfx_bgnd := not shreg(shreg'high);
					end if;
				else
					if bmm or v_mc_flag then
						v_gfx_val  := shreg(shreg'high) & '0';
						v_gfx_bgnd := not shreg(shreg'high);
					else
						v_gfx_val  := (others => shreg(shreg'high));
						v_gfx_bgnd := not shreg(shreg'high);
					end if;
				end if;

				-- color selection is based on current mode flags
				v_mode := get_mode(ecm, bmm, mcm);

				v_gfx_colr(0) := (others => '0');
				v_gfx_colr(1) := (others => '0');
				v_gfx_colr(2) := (others => '0');
				v_gfx_colr(3) := (others => '0');

				v_data_colr := data_3r;

				-- only useful for ECM
				v_bg_sel := unsigned(v_data_colr(7 downto 6));

				-- vertical border disables graphics, not clear if the delay is correct here
				if vbrd_1r = '1' then
					v_bg_sel   := bg_sel; -- this is a super dirty hack to fix ECM
					v_gfx_val  := "00";
					v_gfx_bgnd := '1';
				end if;

				case v_mode is
					when MODE_STD_TEXT =>
						v_gfx_colr(0) := bg_colr_2r(0);
						v_gfx_colr(1) := bg_colr_2r(0);
						v_gfx_colr(2) := unsigned(v_data_colr(11 downto 8));
						v_gfx_colr(3) := unsigned(v_data_colr(11 downto 8));

					when MODE_MCL_TEXT =>
						if v_mc_flag then
							v_gfx_colr(0) := bg_colr_2r(0);
							v_gfx_colr(1) := bg_colr_2r(1);
							v_gfx_colr(2) := bg_colr_2r(2);
							v_gfx_colr(3) := '0' & unsigned(v_data_colr(10 downto 8));
						else
							v_gfx_colr(0) := bg_colr_2r(0);
							v_gfx_colr(1) := bg_colr_2r(0);
							v_gfx_colr(2) := '0' & unsigned(v_data_colr(10 downto 8));
							v_gfx_colr(3) := '0' & unsigned(v_data_colr(10 downto 8));
						end if;

					when MODE_STD_BMAP =>
						v_gfx_colr(0) := unsigned(v_data_colr(3 downto 0));
						v_gfx_colr(1) := unsigned(v_data_colr(3 downto 0));
						v_gfx_colr(2) := unsigned(v_data_colr(7 downto 4));
						v_gfx_colr(3) := unsigned(v_data_colr(7 downto 4));

					when MODE_MCL_BMAP =>
						v_gfx_colr(0) := bg_colr_2r(0);
						v_gfx_colr(1) := unsigned(v_data_colr( 7 downto 4));
						v_gfx_colr(2) := unsigned(v_data_colr( 3 downto 0));
						v_gfx_colr(3) := unsigned(v_data_colr(11 downto 8));

					when MODE_ECM_TEXT =>
						v_gfx_colr(0) := bg_colr_2r(to_integer(v_bg_sel));
						v_gfx_colr(1) := bg_colr_2r(to_integer(v_bg_sel));
						v_gfx_colr(2) := unsigned(v_data_colr(11 downto 8));
						v_gfx_colr(3) := unsigned(v_data_colr(11 downto 8));

					when others =>
						-- NOP
				end case;

				-- saving former pixel values
				gfx_val  <= v_gfx_val;
				gfx_bgnd <= v_gfx_bgnd;
				bg_sel   <= v_bg_sel;

				----------------------------------------------------------------
				--                           OUTPUT                           --
				----------------------------------------------------------------

				o_colr <= v_gfx_colr(to_integer(v_gfx_val));
				o_bgnd <= v_gfx_bgnd;

				----------------------------------------------------------------
				--                         DIAGNOSTICS                        --
				----------------------------------------------------------------

				if mark_mode then
					o_bgnd <= '0';

					case v_mode is

							else
								o_colr <= x"2"; -- RED
							end if;
						when MODE_STD_BMAP =>
							o_colr <= x"3";     -- CYAN
						when MODE_MCL_BMAP =>
							o_colr <= x"4";     -- PURPLE
						when MODE_ECM_TEXT =>
							o_colr <= x"5";     -- GREEN
						when others =>
							o_colr <= x"6";     -- BLUE
					end case;
				end if;

			end if;

			rst_1r <= rst;
			if rst_1r then

			end if;
		end if;
	end process;

end architecture;

						when MODE_STD_TEXT =>
							o_colr <= x"0";     -- BLACK
						when MODE_MCL_TEXT =>


--------------------------------------------------------------------------------
--         .XXXXXXXXXXXXXXXX.  .XXXXXXXXXXXXXXXX.  .XX.                       --
--         XXXXXXXXXXXXXXXXX'  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         XXXX                XXXX          XXXX  XXXX                       --
--         XXXXXXXXXXXXXXXXX.  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         'XXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXX'  XXXX                       --
--                       XXXX  XXXX                XXXX                       --
--         .XXXXXXXXXXXXXXXXX  XXXX                XXXXXXXXXXXXXXXXX.         --
--         'XXXXXXXXXXXXXXXX'  'XX'                'XXXXXXXXXXXXXXXX'         --
--------------------------------------------------------------------------------
--             Copyright 2023 Vittorio Pascucci (SideProjectsLab)             --
--                                                                            --
-- Licensed under the GNU General Public License, Version 3 (the "License");  --
-- you may not use this file except in compliance with the License.           --
-- You may obtain a copy of the License at                                    --
--                                                                            --
--     https://www.gnu.org/licenses/gpl-3.0.en.html#license-text              --
--                                                                            --
-- Unless required by applicable law or agreed to in writing, software        --
-- distributed under the License is distributed on an "AS IS" BASIS,          --
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   --
-- See the License for the specific language governing permissions and        --
-- limitations under the License.                                             --
--------------------------------------------------------------------------------

library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
use     ieee.math_real.all;

library work;
use     work.qol_pkg.all;
use     work.vic_pkg.all;

entity graphics_mux is
generic
(
	g_mark_lines : boolean := false
);
port
(
	clk         : in  std_wire;
	rst         : in  std_wire;

	specs       : in  t_vic_specs;
	strb        : in  t_strb;
	i_xpos      : in  t_ppos;
	i_ypos      : in  t_ppos;
	i_bdln      : in  std_wire;

	i_mark_actv : in  std_wire;
	i_mark_colr : in  t_colr;

	i_bord_actv : in  std_wire;
	i_bord_colr : in  t_colr;

	i_grfx_colr : in  t_colr;
	i_grfx_bgnd : in  std_wire;

	i_sprt_actv : in  std_wire;

	o_push      : out std_wire;
	o_lstr      : out std_wire;
	o_lend      : out std_wire;
	o_fstr      : out std_wire;
	o_colr      : out t_colr;

	mark_bdln   : in  std_wire
);
end entity;

architecture rtl of graphics_mux is

	signal rst_1r : std_wire := '1';
	signal xval   : std_wire;
	signal yval   : std_wire;

	signal bord_actv_1r : std_wire;
	signal bord_actv_2r : std_wire;
	signal bord_actv_3r : std_wire;
	signal bord_actv_4r : std_wire;
	signal bord_actv_5r : std_wire;
	signal bord_actv_6r : std_wire;
	signal bord_actv_7r : std_wire;
	signal bord_actv_8r : std_wire;
	signal bord_actv_9r : std_wire;

	signal bord_colr_1r : t_colr;
	signal bord_colr_2r : t_colr;

	signal sprt_actv_1r : std_wire;
	signal sprt_actv_2r : std_wire;

	signal sprt_prio_1r : std_wire;
	signal sprt_prio_2r : std_wire;

	signal sprt_colr_1r : t_colr;
	signal sprt_colr_2r : t_colr;

begin

	o_push <= xval and yval and strb(0);

	p_mux : process(clk) is
	begin
		if rising_edge(clk) then

			if strb(0) = '0' then

				bord_actv_1r <= i_bord_actv;
				bord_actv_2r <= bord_actv_1r;
				bord_actv_3r <= bord_actv_2r;
				bord_actv_4r <= bord_actv_3r;
				bord_actv_5r <= bord_actv_4r;
				bord_actv_6r <= bord_actv_5r;
				bord_actv_7r <= bord_actv_6r;
				bord_actv_8r <= bord_actv_7r;
				bord_actv_9r <= bord_actv_8r;

				bord_colr_1r <= i_bord_colr;
				bord_colr_2r <= bord_colr_1r;

				sprt_actv_1r <= i_sprt_actv;
				sprt_actv_2r <= sprt_actv_1r;

				sprt_prio_1r <= i_sprt_prio;
				sprt_prio_2r <= sprt_prio_1r;

				sprt_colr_1r <= i_sprt_colr;
				sprt_colr_2r <= sprt_colr_1r;

				o_lstr <= '0';
				o_lend <= '0';

				if bord_actv_9r then
					o_colr <= bord_colr_2r;
				else
					if (sprt_actv_2r = '0') or ((sprt_prio_2r = '0') and (i_grfx_bgnd = '0')) then
						o_colr <= i_grfx_colr;
					else
						o_colr <= sprt_colr_2r;
					end if;
				end if;

				if i_mark_actv then
					o_colr <= i_mark_colr;
				end if;

				if g_mark_lines then
					if (i_xpos >= specs.xnul) and (i_xpos <= specs.xnul + 16) and
					   (i_ypos >= specs.ynul) and (i_ypos <= specs.yend     )
					then
						o_colr <= lresize(i_ypos, 4);
					end if;
				end if;

				-- marking badlines cyan
				if mark_bdln and i_bdln then
					o_colr <= x"3";
				end if;

				----------------------------------------------------------------
				--                   FRAME ALIGNMENT SIGNALS                  --
				----------------------------------------------------------------

				if (i_xpos = specs.xnul) then
					o_lstr <= '1';
					xval   <= '1';
					if (i_ypos = specs.ynul) then
						yval   <= '1';
						o_fstr <= '1';
					end if;
				end if;

				if (i_xpos = specs.xend) then
					o_lend <= '1';
				end if;

				if (i_xpos = specs.xend + 1) then
					xval   <= '0';
					o_fstr <= '0';
					if (i_ypos = specs.yend) then
						yval <= '0';
					end if;
				end if;
			end if;

			rst_1r <= rst;
			if rst_1r then
			end if;

		end if;
	end process;

end architecture;

	i_sprt_prio : in  std_wire;
	i_sprt_colr : in  t_colr;


--------------------------------------------------------------------------------
--         .XXXXXXXXXXXXXXXX.  .XXXXXXXXXXXXXXXX.  .XX.                       --
--         XXXXXXXXXXXXXXXXX'  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         XXXX                XXXX          XXXX  XXXX                       --
--         XXXXXXXXXXXXXXXXX.  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         'XXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXX'  XXXX                       --
--                       XXXX  XXXX                XXXX                       --
--         .XXXXXXXXXXXXXXXXX  XXXX                XXXXXXXXXXXXXXXXX.         --
--         'XXXXXXXXXXXXXXXX'  'XX'                'XXXXXXXXXXXXXXXX'         --
--------------------------------------------------------------------------------
--             Copyright 2023 Vittorio Pascucci (SideProjectsLab)             --
--                                                                            --
-- Licensed under the GNU General Public License, Version 3 (the "License");  --
-- you may not use this file except in compliance with the License.           --
-- You may obtain a copy of the License at                                    --
--                                                                            --
--     https://www.gnu.org/licenses/gpl-3.0.en.html#license-text              --
--                                                                            --
-- Unless required by applicable law or agreed to in writing, software        --
-- distributed under the License is distributed on an "AS IS" BASIS,          --
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   --
-- See the License for the specific language governing permissions and        --
-- limitations under the License.                                             --
--------------------------------------------------------------------------------

library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
use     ieee.math_real.all;

library work;
use     work.qol_pkg.all;
use     work.gp3r_pkg.all;
use     work.vic_pkg.all;

entity registers is
generic
(
	g_ext_code_tail : std_word_vector(open)(7 downto 0)
);
port
(
	clk     : in  std_wire;
	rst     : in  std_wire;

	-- gp3r master
	o_req   : out t_gp3r_req;
	o_rsp   : in  t_gp3r_rsp;

	strb    : in  t_strb;
	db      : in  std_word(11 downto 0);
	a       : in  unsigned(5 downto 0);
	rw      : in  std_wire;
	cs      : in  std_wire;

	reg     : out t_regs;
	wrt     : out std_word(t_regs'range);
	gdot_en : in  std_wire
);
end entity;

architecture rtl of registers is

	constant c_code_len : positive := c_ext_code'length + g_ext_code_tail'length;
	constant c_code     : std_word_vector(0 to c_code_len - 1) := c_ext_code & g_ext_code_tail;

	signal rst_1r : std_wire := '1';
	signal enable : boolean;
	signal cs_1r  : std_wire;

	signal wen    : boolean;
	signal a_tmp  : a'subtype;
	signal d_tmp  : std_word(7 downto 0);

	signal reg_addr : std_word(31 downto 0);
	signal reg_data : std_word(31 downto 0);

	signal ext_enable : boolean;
	signal count_code : natural range 0 to c_code'length - 1;

	signal reg_i : t_regs;

begin

	reg_addr <= swap_endianness(merge(reg(c_reg_adr0_idx to c_reg_adr3_idx)));
	reg_data <= swap_endianness(merge(reg(c_reg_dat0_idx to c_reg_dat3_idx)));

	p_regs : process(clk) is
	begin
		if rising_edge(clk) then

			cs_1r <= cs;

			if (cs and cs_1r) then
				enable <= true;
			end if;

			-- handshake for the output master bus
			if (o_rsp.read = '1') then
				o_req.push <= '0';
			end if;

			o_req.read <= o_rsp.push;
			o_req.r_nw <= '0';

			-- latching register value and operating master bus
			if (strb = 10) then
				a_tmp <= a;
				wrt <= (others => '0');

				if (cs = '0') and (rw = '0') and enable then
					wen <= true;
					wrt(to_integer(a)) <= '1';
				end if;
			end if;

			if strb = 14 then
				d_tmp <= db(7 downto 0);
			end if;

			if (strb = 15) then
				if wen then
					wen <= false;
					reg  (to_integer(a_tmp)) <= d_tmp;
					reg_i(to_integer(a_tmp)) <= d_tmp;

					-- the VIC contains 47 valid 8-bit registers [0:46] the rest
					-- are used by the CPU to establish a write-only connection to
					-- the HD-64 register bus. The connection does not support
					-- backpressure

					-- first of all, we run a simple state machine that verifies
					-- the special code necessary to enable the extended registes

					if (a_tmp = c_reg_code_idx) then
						if (d_tmp = c_code(count_code)) then
							if (count_code = c_code_len - 1) then
								ext_enable <= true;
							else
								count_code <= count_code + 1;
							end if;
						else
							count_code <= 0;
						end if;
					end if;

					-- then we run a simple gp3r_master that writes to the bus
					-- whenever a write is performed to the trigger register

					if (a_tmp = c_reg_trig_idx) and ext_enable then
						if (o_req.push = '0') or (o_rsp.read = '1') then
							o_req.push <= '1';
							o_req.addr <= vlresize(to_gp3r_addr(unsigned(reg_addr)), o_req.addr'length);
							o_req.data <= vlresize(to_gp3r_data(reg_data), o_req.data'length);
						end if;
					end if;
				end if;
			end if;

			----------------------------------------------------------------
			--                        GREY DOT BUG                        --
			----------------------------------------------------------------

			if strb(0) then
				-- aligned to strb=14 because it operates on all clock cycles
				if (gdot_en = '1') then
					for i in 32 to 46 loop
						if (wrt(i) = '1') then
							if (strb = 13) then
								-- overriding with grey, will get re-overwritten
								-- on the next odd cycle
								reg(i)(3 downto 0) <= x"f";
							end if;
						end if;
					end loop;
				end if;
			end if;

			rst_1r <= rst;
			if rst_1r then
				enable     <= false;
				wen        <= false;
				wrt        <= (others => '0');
				cs_1r      <= '1';
				ext_enable <= false;
				count_code <= 0;

			end if;
		end if;
	end process;

end architecture;


--------------------------------------------------------------------------------
--         .XXXXXXXXXXXXXXXX.  .XXXXXXXXXXXXXXXX.  .XX.                       --
--         XXXXXXXXXXXXXXXXX'  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         XXXX                XXXX          XXXX  XXXX                       --
--         XXXXXXXXXXXXXXXXX.  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         'XXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXX'  XXXX                       --
--                       XXXX  XXXX                XXXX                       --
--         .XXXXXXXXXXXXXXXXX  XXXX                XXXXXXXXXXXXXXXXX.         --
--         'XXXXXXXXXXXXXXXX'  'XX'                'XXXXXXXXXXXXXXXX'         --
--------------------------------------------------------------------------------
--             Copyright 2023 Vittorio Pascucci (SideProjectsLab)             --
--                                                                            --
-- Licensed under the GNU General Public License, Version 3 (the "License");  --
-- you may not use this file except in compliance with the License.           --
-- You may obtain a copy of the License at                                    --
--                                                                            --
--     https://www.gnu.org/licenses/gpl-3.0.en.html#license-text              --
--                                                                            --
-- Unless required by applicable law or agreed to in writing, software        --
-- distributed under the License is distributed on an "AS IS" BASIS,          --
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   --
-- See the License for the specific language governing permissions and        --
-- limitations under the License.                                             --
--------------------------------------------------------------------------------

library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
use     ieee.math_real.all;

library work;
use     work.qol_pkg.all;
use     work.vic_pkg.all;

entity sprites is
port
(
	clk     : in  std_wire;
	rst     : in  std_wire;

	specs   : in  t_vic_specs;
	wrt     : in  std_word(t_regs'range);

	reg     : in  t_regs;
	strb    : in  t_strb;
	cycl    : in  t_ppos;
	xpos    : in  t_ppos;
	ypos    : in  t_ppos;

	enable  : in  std_word(7 downto 0);
	mark    : in  std_word(7 downto 0);
	enforce : in  std_word(7 downto 0);

architecture rtl of sprites is

	subtype t_xcount is natural range 0 to 24; -- generic counter
	type    t_xcount_vector is array(natural range <>) of t_xcount;

	subtype t_ycount is unsigned(5 downto 0);
	type    t_ycount_vector is array(natural range <>) of t_ycount;

	signal acquire    : std_wire;
	signal spen       : std_word(7 downto 0);
	signal prio       : std_word(7 downto 0);
	signal mxmc       : std_word(7 downto 0);
	signal xexp_1r    : std_word(7 downto 0);
	signal xexp_2r    : std_word(7 downto 0);
	signal xexp_3r    : std_word(7 downto 0);
	signal xexp_4r    : std_word(7 downto 0);
	signal yexp       : std_word(7 downto 0);
	signal yexp_1r    : std_word(7 downto 0);
	signal colr       : t_colr_vector(0 to 7);
	signal mclr       : t_colr_vector(0 to 1);
	signal sp_xpos    : unsigned_vector(0 to 8)(8 downto 0);
	signal sp_ypos    : unsigned_vector(0 to 8)(7 downto 0);

	signal xpos_1r    : t_ppos;
	signal xpos_2r    : t_ppos;
	signal xpos_3r    : t_ppos;
	signal xpos_4r    : t_ppos;
	signal xpos_5r    : t_ppos;
	signal xpos_6r    : t_ppos;

	signal ydisp      : unsigned(7 downto 0);
	signal ypend      : unsigned(7 downto 0);
	signal xdisp      : unsigned(7 downto 0);
	signal yincr      : unsigned(7 downto 0);
	signal xincr      : unsigned(7 downto 0);
	signal spdma      : unsigned(7 downto 0);

	signal count_sprt : integer range 0 to 7;
	signal count_data : integer range 0 to 3;
	signal count_mc   : t_ycount_vector(0 to 7);
	signal count_mb   : t_ycount_vector(0 to 7);
	signal count_xlen : t_xcount_vector(0 to 7);
	signal shreg      : unsigned_vector(0 to 7)(23 downto 0);
	signal sprt_val   : unsigned_vector(0 to 7)( 1 downto 0);
	signal mc_phy     : unsigned(7 downto 0);

	signal rst_1r     : std_wire := '1';

begin

	p_sprites : process(clk) is
		variable v_sprt_val : sprt_val'element'subtype;
	begin
		if rising_edge(clk) then

			--------------------------------------------------------------------
			-- these behave as sequential assignments as they operate on odd
			-- cycles too

			spen <=     reg(21);
			prio <= not reg(27);
			mxmc <=     reg(28);
			yexp <=     reg(23);

			for i in 0 to 7 loop
				colr   (i) <= unsigned(reg(39 + i)(3 downto 0));
				sp_ypos(i) <= unsigned(reg(i*2 + 1));
				sp_xpos(i) <= unsigned(reg(16)(i) & reg(i*2));
			end loop;

			mclr(0) <= unsigned(reg(37)(3 downto 0));
			mclr(1) <= unsigned(reg(38)(3 downto 0));

			--------------------------------------------------------------------
			-- rising edges of DOT clock

			if strb(0) then

				xpos_1r <= xpos;
				xpos_2r <= xpos_1r;
				xpos_3r <= xpos_2r;
				xpos_4r <= xpos_3r;
				xpos_5r <= xpos_4r;
				xpos_6r <= xpos_5r;

				xexp_1r  <= reg(29);
				xexp_2r  <= xexp_1r;
				xexp_3r  <= xexp_2r;
				xexp_4r  <= xexp_3r;

				o_actv <= '0';
				o_prio <= '0';

				-- looping over the sprites
				for i in 7 downto 0 loop

					------------------------------------------------------------
					--                    SPRITE PLAYBACK                     --
					------------------------------------------------------------

					-- horizontal trigger
					if ydisp(i) then
						if (xpos_6r = sp_xpos(i))
						then
							count_xlen(i) <= 0;
							xdisp (i)     <= '1';
							xincr (i)     <= not xexp_4r(i);
							mc_phy(i)     <= '0';
						end if;
					end if;

					-- execution
					if xdisp(i) then
						-- handle multi-color here
						if mxmc(i) then
							if mc_phy(i) = '0' then
								v_sprt_val := shreg(i)(23 downto 22);
							else
								v_sprt_val := sprt_val(i);
							end if;
						else
							v_sprt_val := shreg(i)(23) & '0';
						end if;

						sprt_val(i) <= v_sprt_val;

						-- selecting color and transparency
						if (v_sprt_val /= "00") and (enable(i) = '1') then
							o_actv <= '1';
							o_prio <= prio(i);
						end if;

						-- bringing sprites forward when marking
						if mark(i) then
							o_prio <= '1';
						end if;

						if v_sprt_val = "01" then
							o_colr <= mclr(0);
						elsif v_sprt_val = "10" then
							o_colr <= colr(i);
						elsif v_sprt_val = "11" then
							o_colr <= mclr(1);
						end if;

						if (count_xlen(i) = 23) and (xincr(i) = '1') then
							-- horizontal end of a sprite
							xdisp(i) <= '0';

						else
							if (xincr(i) = '1') then
								count_xlen(i) <= count_xlen(i) + 1;
								shreg(i)      <= lshift(shreg(i), 1);
								shreg(i)(0)   <= '0';
								mc_phy(i)     <= not mc_phy(i);
							end if;

							if xexp_4r(i) then
								xincr(i) <= not xincr(i);
							else
								xincr(i) <= '1';
							end if;
						end if;

						if mark(i) then
							if (count_xlen(i) = 0                            ) or
							   (count_xlen(i) = 23                           ) or
							   (((count_mc(i) = 3  ) or (count_mc(i) = 63  )) and
							    ((count_xlen(i) < 4) or (count_xlen(i) > 19)))
							then
								o_actv <= '1';
								o_prio <= '1';
								o_colr <= to_unsigned(i, 4);
							end if;
						end if;

					end if;

					------------------------------------------------------------
					--                    VERTICAL TRIGGER                    --
					------------------------------------------------------------

					if strb = 3 then

						yexp_1r(i) <= yexp(i);

						-- delaying display by one character cycle
						ydisp(i)   <= ypend(i) or enforce(i);

						if (yexp(i) = '0') and (yincr(i) = '0') then
							yincr(i) <= '1';
						end if;

						if ((cycl = specs.sprt_dma1_cycl)) then
							if (spdma(i) = '0') and (spen(i) = '1') and
							   (sp_ypos(i) = ypos(7 downto 0))
							then
								spdma(i)    <= '1';
								yincr(i)    <= '1';
								count_mb(i) <= (others => '0');
							end if;
						end if;

						if ((cycl = specs.sprt_dma2_cycl)) then
							if (spdma(i) = '0') and (spen(i) = '1') and
							   (sp_ypos(i) = ypos(7 downto 0))
							then
								spdma(i)    <= '1';
								yincr(i)    <= '1';
								count_mb(i) <= (others => '0');
							end if;
						end if;

						if (cycl = specs.sprt_yexp_cycl) then
							if (spdma(i) and yexp(i)) then
								yincr(i) <= not yincr(i);
							end if;
						end if;

						-- turning on the display
						if (cycl = specs.sprt_disp_cycl) then
							count_mc(i) <= count_mb(i);

							if (spdma(i) = '1') then
								if (sp_ypos(i) = ypos(7 downto 0)) and (spen(i) = '1') then
									ypend(i) <= '1';
								end if;
							else
								ypend(i) <= '0';
							end if;
						end if;

						-- updating MC
						if (cycl = 14) then
							-- doesn't really matter when we update the counter as long as it is
							-- after (if) the DMA has been turned on and before it is turned off
							count_mc(i) <= count_mc(i) + 3;
						end if;

						-- upddating MCBASE
						if (cycl = 15) then
							if yincr(i) = '1' then
								count_mb(i) <= count_mc(i);
							end if;

							-- handle sprite crunch
							if yincr(i) = '0' then
								if (spdma(i) = '1') and (yexp(i) = '0') and (yexp_1r(i) = '1') and (wrt(23) = '1') then
									count_mb(i) <= ("101010" and (count_mb(i) and count_mc(i))) or
									               ("010101" and (count_mb(i) or  count_mc(i)));
								end if;
							end if;
						end if;

						-- we could turn off the DMA anywhere between the update of mb and the
						-- check for start
						if (cycl = specs.sprt_dma1_cycl - 1) then
							if count_mb(i) = 63 then
								spdma(i) <= '0';
							end if;
						end if;
					end if;
				end loop;

				----------------------------------------------------------------
				--                     SPRITE ACQUISITION                     --
				----------------------------------------------------------------

				if (strb = 1) and (cycl = specs.sprt_strt_cycl) then
					-- initiate sprite data acquisition on all lines
					count_sprt <= 0;
					count_data <= 3;
					acquire    <= '1';
				end if;

				-- sprite data acquisition
				if acquire then
					if count_data /= 3 then
						-- holding the shift register while it's being filled
						shreg(count_sprt) <= shreg(count_sprt);
					end if;

					if (strb = 7) or (strb = 15) then
						if (count_data /= 3) then
							if count_data = 0 then
								shreg(count_sprt)(23 downto 16) <= unsigned(i_data(7 downto 0));
							end if;
							if count_data = 1 then
								shreg(count_sprt)(15 downto 8) <= unsigned(i_data(7 downto 0));
							end if;
							if count_data = 2 then
								shreg(count_sprt)(7 downto 0) <= unsigned(i_data(7 downto 0));
							end if;

							if (count_data = 2) then
								if (count_sprt = 7) then
									acquire <= '0';
								else
									count_sprt <= count_sprt + 1;
								end if;
							end if;

							count_data <= count_data + 1;
						else
							count_data <= 0;
						end if;
					end if;
				end if;

				----------------------------------------------------------------
				--                          OTHER                             --
				----------------------------------------------------------------

				if false then
					-- just marking a specific column
					if (strb = 1) and (cycl = specs.sprt_disp_cycl) then
						o_actv <= '1';
						o_prio <= '1';
						o_colr <= to_unsigned(1, 4);
					end if;
				end if;

			end if;

			rst_1r <= rst;
			if rst_1r then
				acquire <= '0';
				xdisp   <= (others => '0');
				ydisp   <= (others => '0');
				spdma   <= (others => '0');
			end if;

		end if;
	end process;

end architecture;

	i_data  : in  std_word(7 downto 0);
	o_prio  : out std_wire;
	o_actv  : out std_wire;
	o_colr  : out t_colr


--------------------------------------------------------------------------------
--         .XXXXXXXXXXXXXXXX.  .XXXXXXXXXXXXXXXX.  .XX.                       --
--         XXXXXXXXXXXXXXXXX'  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         XXXX                XXXX          XXXX  XXXX                       --
--         XXXXXXXXXXXXXXXXX.  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         'XXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXX'  XXXX                       --
--                       XXXX  XXXX                XXXX                       --
--         .XXXXXXXXXXXXXXXXX  XXXX                XXXXXXXXXXXXXXXXX.         --
--         'XXXXXXXXXXXXXXXX'  'XX'                'XXXXXXXXXXXXXXXX'         --
--------------------------------------------------------------------------------
--             Copyright 2023 Vittorio Pascucci (SideProjectsLab)             --
--                                                                            --
-- Licensed under the GNU General Public License, Version 3 (the "License");  --
-- you may not use this file except in compliance with the License.           --
-- You may obtain a copy of the License at                                    --
--                                                                            --
--     https://www.gnu.org/licenses/gpl-3.0.en.html#license-text              --
--                                                                            --
-- Unless required by applicable law or agreed to in writing, software        --
-- distributed under the License is distributed on an "AS IS" BASIS,          --
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   --
-- See the License for the specific language governing permissions and        --
-- limitations under the License.                                             --
--------------------------------------------------------------------------------
--                                                                            --
-- Crucially, the ph0 signal LEADS slightly compared to the VIC clock, so     --
-- strobe-0 corresponds to the first half-cycle of the VIC clock where ph0    --
-- is zero for the entire time                                                --
--      ______________                 _______________                 _____  --
-- ph0                |_______________|               |_______________|       --
--          _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _     --
-- vic    _| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_   --
--                                                                            --
-- clk  _|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|  --
-- str                  0 1 2 3 4 5 6 7 8 9 A B C D E F 0                     --
--                                                                            --
--------------------------------------------------------------------------------

library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
use     ieee.math_real.all;

library work;
use     work.qol_pkg.all;
use     work.vic_pkg.all;

entity strobe is

	ph0  : in  std_wire;
	strb : out t_strb
);
end entity;

architecture rtl of strobe is

	signal rst_1r : std_wire := '1';
	signal ph0_1r : std_wire;

begin

	i_strobe_gen : process(clk) is
	begin
		if rising_edge(clk) then

			-- we make sure that additional edges don't cause wraparounds
			if strb /= 0 then
				strb <= strb + 1;
			end if;

			-- we intentionally skip rising edges of the dot-clock to sample
			-- phi0 because phi0 edges are too close to dot-clock rising edges
			if (dot = '1') then
				ph0_1r <= ph0;
				if (ph0_1r = '1') and (ph0 = '0') then
					strb <= to_unsigned(1, strb'length);
				end if;
			end if;

			rst_1r <= rst;
			if rst_1r then
				ph0_1r <= '0';
				strb   <= (others => '0');
			end if;

		end if;
	end process;

end architecture;


--------------------------------------------------------------------------------
--         .XXXXXXXXXXXXXXXX.  .XXXXXXXXXXXXXXXX.  .XX.                       --
--         XXXXXXXXXXXXXXXXX'  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         XXXX                XXXX          XXXX  XXXX                       --
--         XXXXXXXXXXXXXXXXX.  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         'XXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXX'  XXXX                       --
--                       XXXX  XXXX                XXXX                       --
--         .XXXXXXXXXXXXXXXXX  XXXX                XXXXXXXXXXXXXXXXX.         --
--         'XXXXXXXXXXXXXXXX'  'XX'                'XXXXXXXXXXXXXXXX'         --
--------------------------------------------------------------------------------
--             Copyright 2023 Vittorio Pascucci (SideProjectsLab)             --
--                                                                            --
-- Licensed under the GNU General Public License, Version 3 (the "License");  --
-- you may not use this file except in compliance with the License.           --
-- You may obtain a copy of the License at                                    --
--                                                                            --
--     https://www.gnu.org/licenses/gpl-3.0.en.html#license-text              --
--                                                                            --
-- Unless required by applicable law or agreed to in writing, software        --
-- distributed under the License is distributed on an "AS IS" BASIS,          --
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   --
-- See the License for the specific language governing permissions and        --
-- limitations under the License.                                             --
--------------------------------------------------------------------------------

library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
use     ieee.math_real.all;

library work;
use     work.qol_pkg.all;
use     work.vic_pkg.all;

entity sync_flex is
port
(
	clk     : in  std_wire;
	rst     : in  std_wire;
	a       : in  t_addr;
	strb    : in  t_strb;

	enable  : in  std_wire;

	specs   : out t_vic_specs;
	lock    : out std_wire;
	cycl    : out t_ppos;
	xpos    : out t_ppos;
	ypos    : out t_ppos;

	dgn_out : out std_word(3 downto 0) := (others => '0')
);
end entity;

architecture rtl of sync_flex is

	signal h63_lock  : std_wire;
	signal h63_cycl  : t_ppos;
	signal h63_xpos  : t_ppos;
	signal h63_ypos  : t_ppos;

	signal h64_lock  : std_wire;
	signal h64_cycl  : t_ppos;
	signal h64_xpos  : t_ppos;
	signal h64_ypos  : t_ppos;

	signal h65_lock  : std_wire;
	signal h65_cycl  : t_ppos;
	signal h65_xpos  : t_ppos;
	signal h65_ypos  : t_ppos;

begin

	i_h63_sync : entity work.xy_sync
	port map
	(
		clk     => clk,
		rst     => rst,
		a       => a,
		strb    => strb,
		enable  => enable,
		specs   => c_vic_h63_specs,
		lock    => h63_lock,
		cycl    => h63_cycl,
		xpos    => h63_xpos,
		ypos    => h63_ypos,
		dgn_out => open --dgn_out
	);

	i_h64_sync : entity work.xy_sync
	port map
	(
		clk     => clk,
		rst     => rst,
		a       => a,
		strb    => strb,
		enable  => enable,
		specs   => c_vic_h64_specs,
		lock    => h64_lock,
		cycl    => h64_cycl,
		xpos    => h64_xpos,
		ypos    => h64_ypos,
		dgn_out => open --dgn_out
	);

	i_h65_sync : entity work.xy_sync
	port map
	(
		clk     => clk,
		rst     => rst,
		a       => a,
		strb    => strb,
		enable  => enable,
		specs   => c_vic_h65_specs,
		lock    => h65_lock,
		cycl    => h65_cycl,
		xpos    => h65_xpos,
		ypos    => h65_ypos,
		dgn_out => open --dgn_out
	);

	p_mux : process(all) is
	begin
		if (h63_lock = '1') then
			lock  <= h63_lock;
			cycl  <= h63_cycl;
			xpos  <= h63_xpos;
			ypos  <= h63_ypos;
			specs <= c_vic_h63_specs;

		elsif h64_lock then
			lock  <= h64_lock;
			cycl  <= h64_cycl;
			xpos  <= h64_xpos;
			ypos  <= h64_ypos;
			specs <= c_vic_h64_specs;

		elsif h65_lock then
			lock  <= h65_lock;
			cycl  <= h65_cycl;
			xpos  <= h65_xpos;

			cycl  <= (others => '0');
			xpos  <= (others => '0');
			ypos  <= (others => '0');
			specs <= c_vic_h63_specs;
		end if;
	end process;

end architecture;

			ypos  <= h65_ypos;
			specs <= c_vic_h65_specs;


--------------------------------------------------------------------------------
--         .XXXXXXXXXXXXXXXX.  .XXXXXXXXXXXXXXXX.  .XX.                       --
--         XXXXXXXXXXXXXXXXX'  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         XXXX                XXXX          XXXX  XXXX                       --
--         XXXXXXXXXXXXXXXXX.  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         'XXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXX'  XXXX                       --
--                       XXXX  XXXX                XXXX                       --
--         .XXXXXXXXXXXXXXXXX  XXXX                XXXXXXXXXXXXXXXXX.         --
--         'XXXXXXXXXXXXXXXX'  'XX'                'XXXXXXXXXXXXXXXX'         --
--------------------------------------------------------------------------------
--             Copyright 2023 Vittorio Pascucci (SideProjectsLab)             --
--                                                                            --
-- Licensed under the GNU General Public License, Version 3 (the "License");  --
-- you may not use this file except in compliance with the License.           --
-- You may obtain a copy of the License at                                    --
--                                                                            --
--     https://www.gnu.org/licenses/gpl-3.0.en.html#license-text              --
--                                                                            --
-- Unless required by applicable law or agreed to in writing, software        --
-- distributed under the License is distributed on an "AS IS" BASIS,          --
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   --
-- See the License for the specific language governing permissions and        --
-- limitations under the License.                                             --
--------------------------------------------------------------------------------

library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
use     ieee.math_real.all;

library work;
use     work.qol_pkg.all;
use     work.gp3r_pkg.all;
use     work.vic_pkg.all;
use     work.memmap_vicii_pkg.all;

entity vic_passive is
generic
(
	g_ext_code_tail : std_word_vector(open)(7 downto 0);
	g_log_only : boolean := false
);
port
(
	-- VIC signals
	clk          : in  std_wire; -- must be twice the video clock (16 times ph0)
	rst          : in  std_wire;
	dot          : in  std_wire;

	-- gp3r master
	o_req        : out t_gp3r_req;
	o_rsp        : in  t_gp3r_rsp;

	-- gp3r_slave
	i_req        : in  t_gp3r_req;
	i_rsp        : out t_gp3r_rsp;

	-- memory bus
	ph0          : in  std_wire;
	db           : in  std_word(11 downto 0);
	a            : in  t_addr;
	rw           : in  std_wire;
	cs           : in  std_wire;
	aec          : in  std_wire;

	o_push       : out std_wire;
	o_lstr       : out std_wire;
	o_fstr       : out std_wire;
	o_lend       : out std_wire;
	o_colr       : out t_colr;

	-- feature switches
	gdot_en      : in  std_wire;

	-- diagnostics
	sprt_en      : in  std_word(7 downto 0) := (others => '1');
	bord_en      : in  std_word(1 downto 0) := (others => '1');
	idle_en      : in  std_wire := '1';
	mark_mode    : in  std_wire := '0';
	mark_bdln    : in  std_wire := '0';
	mark_sprt    : in  std_word(7 downto 0) := (others => '0');
	force_sprt   : in  std_word(7 downto 0) := (others => '0');
	lock         : out std_wire
);
end entity;

architecture rtl of vic_passive is

	signal req_bus_logger : t_gp3r_req_tpl := c_gp3r_req_tpl;
	signal rsp_bus_logger : t_gp3r_rsp_tpl := c_gp3r_rsp_tpl;

	signal mf_ph0  : ph0'subtype;
	signal mf_db   : db 'subtype;
	signal mf_a    : a  'subtype;
	signal mf_rw   : rw 'subtype;
	signal mf_cs   : cs 'subtype;
	signal mf_aec  : aec'subtype;

	signal reg   : t_regs;
	signal wrt   : std_word(t_regs'range);
	signal strb  : t_strb;
	signal specs : t_vic_specs;
	signal cycl  : t_ppos;
	signal bdln  : std_wire;
	signal ccax  : std_wire;
	signal xpos  : t_ppos;
	signal ypos  : t_ppos;

	signal cc_vm     : db'subtype;
	signal gg_vm     : std_word(7 downto 0);
	signal grfx_actv : std_wire;
	signal vbrd_actv : std_wire;
	signal bord_actv : std_wire;
	signal bord_colr : t_colr;

	signal mark_actv : std_wire := '0';
	signal mark_colr : t_colr   := x"0";

	signal grfx_colr : t_colr;
	signal grfx_bgnd : std_wire;

	signal sprt_actv : std_wire;
	signal sprt_prio : std_wire;
	signal sprt_colr : t_colr;

begin

	vic_type <= specs.tvic;

	i_bus_latch : entity work.bus_latch
	generic map
	(
		g_enable => false
	)
	port map
	(
		clk   => clk,
		rst   => rst,

		i_ph0 => ph0,
		i_db  => db ,
		i_a   => a  ,
		i_rw  => rw ,
		i_cs  => cs ,
		i_aec => aec,

		o_ph0 => mf_ph0,
		o_db  => mf_db ,
		o_a   => mf_a  ,
		o_rw  => mf_rw ,
		o_cs  => mf_cs ,
		o_aec => mf_aec
	);

	i_strobe : entity work.strobe
	port map
	(
		clk  => clk,
		rst  => rst,
		dot  => dot,
		ph0  => mf_ph0,
		strb => strb
	);

	i_registers : entity work.registers
	generic map
	(
		g_ext_code_tail => g_ext_code_tail
	)
	port map
	(
		-- VIC signals
		clk     => clk,
		rst     => rst,

		o_req   => o_req,
		o_rsp   => o_rsp,

		strb    => strb,
		db      => mf_db ,
		a       => mf_a  ,
		rw      => mf_rw ,
		cs      => mf_cs ,
		reg     => reg,
		wrt     => wrt,
		gdot_en => gdot_en
	);

	i_frame_sync : entity work.sync_flex
	port map
	(
		clk     => clk,
		rst     => rst,
		a       => mf_a,
		strb    => strb,
		enable  => '1',
		specs   => specs,
		lock    => lock,
		cycl    => cycl,
		xpos    => xpos,
		ypos    => ypos,
		dgn_out => open --dgn_out
	);

	r_graphics : if not g_log_only generate

		------------------------------------------------------------------------
		--                         BADLINE DETECTION                          --
		------------------------------------------------------------------------

		i_bdln_detect : entity work.bad_line_detect
		port map
		(
			clk    => clk,
			rst    => rst,
			reg    => reg,
			aec    => mf_aec,
			strb   => strb,
			cycl   => cycl,
			ypos   => ypos,
			bdln   => bdln,
			ccax   => ccax
		);

		------------------------------------------------------------------------
		--                            VIDEO MATRIX                            --
		------------------------------------------------------------------------

		i_video_matrix : entity work.video_matrix
		port map
		(
			clk     => clk,
			rst     => rst,
			strb    => strb,
			cycl    => cycl,
			bdln    => bdln,
			ccax    => ccax,
			ypos    => ypos,
			specs   => specs,
			i_db    => mf_db,
			o_cc    => cc_vm,
			o_gg    => gg_vm,
			o_en    => grfx_actv,
			idle_en => idle_en
		);

		------------------------------------------------------------------------
		--                         GRAPHICS GENERATOR                         --
		------------------------------------------------------------------------

		i_graphics_gen : entity work.graphics_gen
		port map
		(
			clk       => clk,
			rst       => rst,
			reg       => reg,
			strb      => strb,
			i_actv    => grfx_actv,
			i_vbrd    => vbrd_actv,
			i_grfx    => gg_vm,
			i_data    => cc_vm,
			o_bgnd    => grfx_bgnd,
			o_colr    => grfx_colr,

			mark_mode => mark_mode
		);

		------------------------------------------------------------------------
		--                          BORDER GENERATOR                          --
		------------------------------------------------------------------------

		i_border : entity work.border
		port map
		(
			clk    => clk,
			rst    => rst,
			strb   => strb,
			specs  => specs,
			cycl   => cycl,
			xpos   => xpos,
			ypos   => ypos,
			reg    => reg,
			o_vbrd => vbrd_actv,
			o_bord => bord_actv,
			o_colr => bord_colr,
			enable => bord_en

--			-- marking border events
--			mark_bord => mark_actv,
--			mark_colr => mark_colr
		);

		------------------------------------------------------------------------
		--                          SPRITE GENERATOR                          --
		------------------------------------------------------------------------

		i_sprites : entity work.sprites
		port map
		(
			clk     => clk,
			rst     => rst,
			specs   => specs,
			reg     => reg,
			wrt     => wrt,
			strb    => strb,
			cycl    => cycl,
			xpos    => xpos,
			ypos    => ypos,
			enable  => sprt_en,
			mark    => mark_sprt,
			enforce => force_sprt,

			i_data  => mf_db(7 downto 0),
			o_prio  => sprt_prio,
			o_actv  => sprt_actv,
			o_colr  => sprt_colr
		);

		------------------------------------------------------------------------
		--                        GRAPHICS MULTIPLEXER                        --
		------------------------------------------------------------------------

		i_graphics_mux : entity work.graphics_mux
		generic map
		(
			g_mark_lines => false
		)
		port map
		(
			clk         => clk,
			rst         => rst,
			specs       => specs,
			strb        => strb,
			i_bdln      => bdln,
			i_xpos      => xpos,
			i_ypos      => ypos,

			i_mark_actv => mark_actv,
			i_mark_colr => mark_colr,

			i_bord_actv => bord_actv,
			i_bord_colr => bord_colr,

			i_sprt_actv => sprt_actv,
			i_sprt_prio => sprt_prio,
			i_sprt_colr => sprt_colr,

			i_grfx_colr => grfx_colr,
			i_grfx_bgnd => grfx_bgnd,

			o_push      => o_push,
			o_lstr      => o_lstr,
			o_lend      => o_lend,
			o_fstr      => o_fstr,
			o_colr      => o_colr,

			mark_bdln   => mark_bdln
		);

		-- dummy to terminate the logger bus
		i_nack : entity work.gp3r_dummy
		generic map
		(
			g_nack => c_gp3r_nack_addr
		)
		port map
		(
			clk  => clk,
			rst  => rst,
			req  => i_req,
			rsp  => i_rsp
		);

	else generate

		i_bus_logger : entity work.bus_logger
		generic map
		(
			g_enable => false
		)
		port map
		(
			clk      => clk,
			rst      => rst,
			req      => req_bus_logger,
			rsp      => rsp_bus_logger,
			xpos     => xpos,
			ypos     => ypos,
			strb     => strb,
			ph0      => mf_ph0,
			db       => mf_db,
			a        => mf_a,
			rw       => mf_rw,
			cs       => mf_cs,
			aec      => mf_aec
		);

		i_memmap_vicii : entity work.memmap_vicii
		port map
		(
			clk            => clk,
			rst            => rst,
			req            => i_req,
			rsp            => i_rsp,
			req_bus_logger => req_bus_logger,
			rsp_bus_logger => rsp_bus_logger,
			reg_0          => reg(0 ),
			reg_1          => reg(1 ),
			reg_2          => reg(2 ),
			reg_3          => reg(3 ),
			reg_4          => reg(4 ),
			reg_5          => reg(5 ),
			reg_6          => reg(6 ),
			reg_7          => reg(7 ),
			reg_8          => reg(8 ),
			reg_9          => reg(9 ),
			reg_10         => reg(10),
			reg_11         => reg(11),
			reg_12         => reg(12),
			reg_13         => reg(13),
			reg_14         => reg(14),
			reg_15         => reg(15),
			reg_16         => reg(16),
			reg_17         => reg(17),
			reg_18         => reg(18),
			reg_19         => reg(19),
			reg_20         => reg(20),
			reg_21         => reg(21),
			reg_22         => reg(22),
			reg_23         => reg(23),
			reg_24         => reg(24),
			reg_25         => reg(25),
			reg_26         => reg(26),
			reg_27         => reg(27),
			reg_28         => reg(28),
			reg_29         => reg(29),
			reg_30         => reg(30),
			reg_31         => reg(31),
			reg_32         => reg(32),
			reg_33         => reg(33),
			reg_34         => reg(34),
			reg_35         => reg(35),
			reg_36         => reg(36),
			reg_37         => reg(37),
			reg_38         => reg(38),
			reg_39         => reg(39),
			reg_40         => reg(40),
			reg_41         => reg(41),
			reg_42         => reg(42),
			reg_43         => reg(43),
			reg_44         => reg(44),
			reg_45         => reg(45),
			reg_46         => reg(46)
		);

	end generate;

end architecture;

	-- vic detection
	vic_type     : out t_vic_type;


--------------------------------------------------------------------------------
--         .XXXXXXXXXXXXXXXX.  .XXXXXXXXXXXXXXXX.  .XX.                       --
--         XXXXXXXXXXXXXXXXX'  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         XXXX                XXXX          XXXX  XXXX                       --
--         XXXXXXXXXXXXXXXXX.  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         'XXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXX'  XXXX                       --
--                       XXXX  XXXX                XXXX                       --
--         .XXXXXXXXXXXXXXXXX  XXXX                XXXXXXXXXXXXXXXXX.         --
--         'XXXXXXXXXXXXXXXX'  'XX'                'XXXXXXXXXXXXXXXX'         --
--------------------------------------------------------------------------------
--             Copyright 2023 Vittorio Pascucci (SideProjectsLab)             --
--                                                                            --
-- Licensed under the GNU General Public License, Version 3 (the "License");  --
-- you may not use this file except in compliance with the License.           --
-- You may obtain a copy of the License at                                    --
--                                                                            --
--     https://www.gnu.org/licenses/gpl-3.0.en.html#license-text              --
--                                                                            --
-- Unless required by applicable law or agreed to in writing, software        --
-- distributed under the License is distributed on an "AS IS" BASIS,          --
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   --
-- See the License for the specific language governing permissions and        --
-- limitations under the License.                                             --
--------------------------------------------------------------------------------

library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
use     ieee.math_real.all;

library work;
use     work.qol_pkg.all;

package vic_pkg is

	constant c_xres_h63  : positive := 403;
	constant c_yres_h63  : positive := 284;

	constant c_num_regs  : positive := 64;
	constant c_max_xlen  : positive := 65 * 8;
	constant c_max_ylen  : positive := 312;

	constant c_ypos_bits : positive := bits_for_range(c_max_ylen);
	constant c_ppos_bits : positive := maximum(c_xpos_bits, c_ypos_bits);

	subtype t_colr is unsigned(3 downto 0);
	subtype t_ppos is unsigned(c_ppos_bits - 1 downto 0);
	subtype t_addr is unsigned(5 downto 0);
	subtype t_strb is unsigned(3 downto 0);
	subtype t_regs is std_word_vector(0 to c_num_regs - 1) (7 downto 0);

	subtype t_colr_vector is unsigned_vector(open)(3 downto 0);

	function to_ppos(x : natural; m : natural := 0) return t_ppos;

	type t_vic_type is
	(
		vic_h63,
		vic_h64,
		vic_h65
	);

	type t_vic_mode is
	(
		mode_std_text,
		mode_mcl_text,
		mode_std_bmap,
		mode_mcl_bmap,
		mode_ecm_text,
		mode_invalid
	);

	-- this implementation

	type t_vic_specs is record
		tvic : t_vic_type;

		cycl : t_ppos; -- Number of characters (visible & invisible) in a line
		xref : t_ppos; -- x position for the synchronization cycle (end of RAM refresh for each line)
		xnul : t_ppos; -- x position of the first visible pixel (line start)
		xend : t_ppos; -- x position of the last visible pixel (line end)
		xlen : t_ppos; -- total pixels in a line (cycl * 8)
		xres : t_ppos; -- total visible pixels in a line
		xfvc : t_ppos; -- first pixel of active area
		xlvc : t_ppos; -- last pixel of active area

		yref : t_ppos; -- y position for the synchronization line (RAM refresh)
		ynul : t_ppos; -- y position of the first visible pixel
		yend : t_ppos; -- y position of the last visible pixel
		ylen : t_ppos; -- total lines in a frame
		yres : t_ppos; -- total visible lines in a frame
		yfvc : t_ppos; -- first line of active area
		ylvc : t_ppos; -- last line of active area

		sprt_dma1_cycl : t_ppos;
		sprt_dma2_cycl : t_ppos;
		sprt_yexp_cycl : t_ppos;
		sprt_disp_cycl : t_ppos;
		sprt_strt_cycl : t_ppos;
	end record;

	-- PAL with 63 lines
	constant c_vic_h63_specs : t_vic_specs :=
	(
		tvic => vic_h63,

		cycl => to_ppos(63),                 -- number of character cycles in a line
		xref => to_ppos(16),                 -- value of xpos on the 1st clock cycle of the 1st character access after the refresh pattern
		xlen => to_ppos(504),                -- number of pixels in a line
		xres => to_ppos(402),                -- visible pixels in a line
		xnul => to_ppos(498),                -- solely needed to center the picture
		xend => to_ppos(498 + 402 - 1, 504), -- ((xnul + xres - 1) % xlen)
		xfvc => to_ppos(24),                 -- first video coordinate (after border)
		xlvc => to_ppos(24 + 319),           -- last video coordinate  (before border)

		yref => to_ppos(0),
		ylen => to_ppos(312),
		yres => to_ppos(284),
		ynul => to_ppos(8),
		yend => to_ppos(8 + 280 - 1),
		yfvc => to_ppos(51),
		ylvc => to_ppos(51 + 199),

		sprt_dma1_cycl => to_ppos(54),
		sprt_dma2_cycl => to_ppos(55),
		sprt_yexp_cycl => to_ppos(55),
		sprt_disp_cycl => to_ppos(57),
		sprt_strt_cycl => to_ppos(57)
	);

	-- NTSC with 64 lines
	constant c_vic_h64_specs : t_vic_specs :=
	(
		tvic => vic_h64,

		cycl => to_ppos(64),
		xref => to_ppos(16),
		xlen => to_ppos(512),
		xres => to_ppos(420),
		xnul => to_ppos(495),
		xend => to_ppos(495 + 420 - 1, 512),
		xfvc => to_ppos(24),
		xlvc => to_ppos(24 + 319),

		yref => to_ppos(0),
		ylen => to_ppos(262),
		yres => to_ppos(252),
		ynul => to_ppos(26),
		yend => to_ppos(26 + 252 - 1, 262),
		yfvc => to_ppos(51),
		ylvc => to_ppos(51 + 199),

		sprt_dma1_cycl => to_ppos(55),
		sprt_dma2_cycl => to_ppos(56),
		sprt_yexp_cycl => to_ppos(56),
		sprt_disp_cycl => to_ppos(57),
		sprt_strt_cycl => to_ppos(58)
	);

	-- NTSC with 65 lines
	constant c_vic_h65_specs : t_vic_specs :=
	(
		tvic => vic_h65,

		cycl => to_ppos(65),
		xref => to_ppos(16),
		xlen => to_ppos(520),
		xres => to_ppos(420),
		xnul => to_ppos(504),
		xend => to_ppos(504 + 420 - 1, 520),
		xfvc => to_ppos(24),
		xlvc => to_ppos(24 + 319),

		yref => to_ppos(0),
		ylen => to_ppos(263),
		yres => to_ppos(252),
		ynul => to_ppos(25),
		yend => to_ppos(25 + 252 - 1, 263),
		yfvc => to_ppos(51),
		ylvc => to_ppos(51 + 199),

		sprt_dma1_cycl => to_ppos(55),
		sprt_dma2_cycl => to_ppos(56),
		sprt_yexp_cycl => to_ppos(56),
		sprt_disp_cycl => to_ppos(58),
		sprt_strt_cycl => to_ppos(58)
	);

	constant c_csmp_strb : t_strb := to_unsigned(6, 4);
	constant c_cycl_ref  : t_ppos := to_ppos(14); -- -1 from the docs because they start from 1... :(
	constant c_cycl_vend : t_ppos := to_ppos(54); -- like above :(
	constant c_cycle_yff : t_ppos := to_ppos(62); -- same same :(

	-- these are constants for accessing the system's register bus from the C64
	-- CPU and shall be replicated manually in the C64 code

	constant c_reg_code_idx : natural := 47;
	constant c_reg_trig_idx : natural := 48;

	constant c_reg_adr0_idx : natural := 49;
	constant c_reg_adr1_idx : natural := 50;
	constant c_reg_adr2_idx : natural := 51;
	constant c_reg_adr3_idx : natural := 52;

	constant c_reg_dat0_idx : natural := 53;
	constant c_reg_dat1_idx : natural := 54;
	constant c_reg_dat2_idx : natural := 55;
	constant c_reg_dat3_idx : natural := 56;

	constant c_ext_code : std_word_vector(0 to 5)(7 downto 0) :=
	(
		x"fe",
		x"ed",
		x"60",
		x"0d",
		x"f0",
		x"0d"
	);

end package;

package body vic_pkg is

	function to_ppos(x : natural; m : natural := 0) return t_ppos is
		variable ret : integer := x;
	begin
		if m /= 0 then
			ret := x mod m;
		end if;
		return ltrim(unsigned(to_signed(ret, t_ppos'length + 1)), 1);
	end function;

end package body;

constant c_xpos_bits : positive := bits_for_range(c_max_xlen);


--------------------------------------------------------------------------------
--         .XXXXXXXXXXXXXXXX.  .XXXXXXXXXXXXXXXX.  .XX.                       --
--         XXXXXXXXXXXXXXXXX'  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         XXXX                XXXX          XXXX  XXXX                       --
--         XXXXXXXXXXXXXXXXX.  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         'XXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXX'  XXXX                       --
--                       XXXX  XXXX                XXXX                       --
--         .XXXXXXXXXXXXXXXXX  XXXX                XXXXXXXXXXXXXXXXX.         --
--         'XXXXXXXXXXXXXXXX'  'XX'                'XXXXXXXXXXXXXXXX'         --
--------------------------------------------------------------------------------
--             Copyright 2023 Vittorio Pascucci (SideProjectsLab)             --
--                                                                            --
-- Licensed under the GNU General Public License, Version 3 (the "License");  --
-- you may not use this file except in compliance with the License.           --
-- You may obtain a copy of the License at                                    --
--                                                                            --
--     https://www.gnu.org/licenses/gpl-3.0.en.html#license-text              --
--                                                                            --
-- Unless required by applicable law or agreed to in writing, software        --
-- distributed under the License is distributed on an "AS IS" BASIS,          --
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   --
-- See the License for the specific language governing permissions and        --
-- limitations under the License.                                             --
--------------------------------------------------------------------------------

library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
use     ieee.math_real.all;

library work;
use     work.qol_pkg.all;
use     work.vic_pkg.all;

entity video_matrix is
generic
(
	g_mark_bdln : boolean := false
);
port
(
	clk     : in  std_wire;
	rst     : in  std_wire;

	strb    : in  t_strb;
	cycl    : in  t_ppos;
	bdln    : in  std_wire;
	ccax    : in  std_wire;
	ypos    : in  t_ppos;
	specs   : in  t_vic_specs;

	i_db    : in  std_word(11 downto 0);
	o_cc    : out std_word(11 downto 0); -- color info
	o_gg    : out std_word( 7 downto 0); -- graphics info
	o_en    : out std_wire;

	idle_en : in  std_wire := '1'
);
end entity;

architecture rtl of video_matrix is

	constant c_ram_len   : positive := 40;
	constant c_ram_width : positive := i_db'length;
	constant c_addr_bits : positive := bits_for_range(c_ram_len);

	signal rst_1r   : std_wire := '1';
	signal ram_wadd : unsigned(c_addr_bits - 1 downto 0);
	signal ram_wdat : std_word(c_ram_width - 1 downto 0);
	signal ram_wen  : std_wire;
	signal ram_radd : unsigned(c_addr_bits - 1 downto 0);
	signal ram_rdat : std_word(c_ram_width - 1 downto 0);

	signal count_line : natural range 0 to 8;
	signal count_cycl : natural range 0 to c_ram_len;

	signal bdln_1r : std_wire;
	signal idle    : std_wire;

begin

	i_ram : entity work.true_dual_port_ram
	generic map
	(
		g_bit_width => c_ram_width,
		g_size      => 40
	)
	port map
	(
		clk1  => clk,
		addr1 => ram_wadd,
		din1  => ram_wdat,
		dout1 => open,
		wen1  => ram_wen,

		clk2  => clk,
		addr2 => ram_radd,
		din2  => (others => '0'),
		dout2 => ram_rdat,
		wen2  => '0'
	);

	p_control : process(clk) is
	begin
		if rising_edge(clk) then

			ram_wen <= '0';

			-- c-accesses are most stable on strobe 13-14
			if (strb = 13) then

				-- checking this here ensures the effects are properly delayed
				-- to the next (output) cycle
				if (bdln = '1') then
					idle    <= '0';
					bdln_1r <= '1';
				end if;

				if (cycl = 13) then
					if (bdln = '1') then
						count_line <= 0;
					else
						bdln_1r <= '0';
					end if;
				end if;

				if (cycl = c_cycl_ref) then
					ram_wadd <= (others => '0');
				elsif (ram_wadd < c_ram_len - 1) then
					ram_wadd <= ram_wadd + 1;
				end if;

				if (bdln or bdln_1r or ccax) then
					if (cycl = c_cycl_ref) or (ram_wadd < c_ram_len - 1) then
						ram_wen  <= '1';
						if (ccax = '1') then
							ram_wdat <= i_db;
						else
							ram_wdat <= i_db or x"0ff";
						end if;
					end if;
				end if;

				if (cycl = 57) then
					if (count_line /= 7) then
						count_line <= count_line + 1;
					elsif bdln = '0' then
						idle <= '1';
					else
						-- line doubling trick
						count_line <= 0;
					end if;
				end if;

			end if;

			-- start video matrix read soon enough for the data to be available
			-- when it needs to be produced on the output
			if (strb = 2) then
				if count_cycl < c_ram_len then
					count_cycl <= count_cycl + 1;
				end if;

				if (cycl = c_cycl_ref + 1) then
					ram_radd <= (others => '0');
					count_cycl <= 0;

				elsif (ram_radd < c_ram_len - 1) then
					ram_radd <= ram_radd + 1;
				end if;
			end if;

			if (strb = 7) then
				if (count_cycl /= c_ram_len) then
					o_en <= '1';
					o_gg <= i_db(7 downto 0);
					o_cc <= ram_rdat;
				else
					o_en <= '0';
				end if;

				if idle and idle_en then
					o_gg <= i_db(7 downto 0);
					o_cc <= (others => '0');
				end if;
			end if;

				idle    <= '1';
			end if;
		end if;
	end process;

end architecture;


--------------------------------------------------------------------------------
--         .XXXXXXXXXXXXXXXX.  .XXXXXXXXXXXXXXXX.  .XX.                       --
--         XXXXXXXXXXXXXXXXX'  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         XXXX                XXXX          XXXX  XXXX                       --
--         XXXXXXXXXXXXXXXXX.  XXXXXXXXXXXXXXXXXX  XXXX                       --
--         'XXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXX'  XXXX                       --
--                       XXXX  XXXX                XXXX                       --
--         .XXXXXXXXXXXXXXXXX  XXXX                XXXXXXXXXXXXXXXXX.         --
--         'XXXXXXXXXXXXXXXX'  'XX'                'XXXXXXXXXXXXXXXX'         --
--------------------------------------------------------------------------------
--             Copyright 2023 Vittorio Pascucci (SideProjectsLab)             --
--                                                                            --
-- Licensed under the GNU General Public License, Version 3 (the "License");  --
-- you may not use this file except in compliance with the License.           --
-- You may obtain a copy of the License at                                    --
--                                                                            --
--     https://www.gnu.org/licenses/gpl-3.0.en.html#license-text              --
--                                                                            --
-- Unless required by applicable law or agreed to in writing, software        --
-- distributed under the License is distributed on an "AS IS" BASIS,          --
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   --
-- See the License for the specific language governing permissions and        --
-- limitations under the License.                                             --
--------------------------------------------------------------------------------

library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
use     ieee.math_real.all;

library work;
use     work.qol_pkg.all;
use     work.vic_pkg.all;

entity xy_sync is
port
(
	clk     : in  std_wire;
	rst     : in  std_wire;
	a       : in  t_addr;
	strb    : in  t_strb;

	enable  : in  std_wire;
	specs   : in  t_vic_specs;

	lock    : out std_wire;
	cycl    : out t_ppos;
	xpos    : out t_ppos;
	ypos    : out t_ppos;

	dgn_out : out std_word(3 downto 0) := (others => '0')
);
end entity;

architecture rtl of xy_sync is

	constant c_unlock_cycles : natural := 3;

	type t_state is
	(
		st_unlocked,
		st_llocking,
		st_llocked,
		st_locked
	);

	signal rst_1r       : std_wire := '1';
	signal state        : t_state;
	signal shreg        : unsigned(9 downto 0);
	signal shreg_old    : shreg'subtype;
	signal refpat       : shreg'subtype;
	signal count_unlock : natural range 0 to c_unlock_cycles;
	signal cycl_i       : t_ppos;

	signal ypos_i       : t_ppos;

	function is_refresh(shreg : unsigned) return boolean is
	begin
		return (shreg = "1110010011") or
			   (shreg = "1001001110") or
			   (shreg = "0100111001") or
			   (shreg = "0011100100");
	end function;

begin

	shreg <= ltrim(shreg_old, 2) & lresize(a, 2);
	lock  <= '1' when state = st_locked else '0';

	p_xy_pos : process(clk) is
	begin
		if rising_edge(clk) then
			if (strb = 1) and (enable = '1') then

				shreg_old <= shreg;

				if (cycl_i < specs.cycl - 1) then
					cycl_i <= cycl_i + 1;
				else
					cycl_i <= (others => '0');

					if (ypos_i < specs.ylen - 1) then
						ypos_i <= ypos_i + 1;
					else
						ypos_i <= (others => '0');
					end if;
				end if;

				if (cycl_i = c_cycl_ref) then

					-- always checking for cycl_i allows us to scan the line one
					-- character-cycle at a time, thus guaranteeing a finite
					-- lock time

					case state is
						when st_unlocked =>
							if is_refresh(shreg) then
								state  <= st_llocking;
								refpat <= ltrim(shreg, 2) & shreg(7 downto 6);
							else
								-- skip a cycle and try again
								cycl_i <= c_cycl_ref + 2;
							end if;

						when st_llocking =>
							if (shreg = refpat) then
							--if is_refresh(shreg) then
								state        <= st_llocked;
								count_unlock <= c_unlock_cycles;
								refpat       <= ltrim(shreg, 2) & shreg(7 downto 6);
							else
								-- skip a cycle and start over
								state  <= st_unlocked;
								cycl_i <= c_cycl_ref + 2;
							end if;

						when st_llocked =>
							if (shreg = "1111111111") then
								state        <= st_locked;
								ypos_i       <= specs.ylen - 1;
								refpat       <= "1110010011";
								count_unlock <= c_unlock_cycles;

							elsif (shreg = refpat) then
							--elsif is_refresh(shreg) then
								refpat       <= ltrim(refpat, 2) & refpat(7 downto 6);
								count_unlock <= c_unlock_cycles;

							elsif count_unlock /= 0 then
								refpat       <= ltrim(refpat, 2) & refpat(7 downto 6);
								count_unlock <= count_unlock - 1;

							else
								-- skip a cycle and start over
								state  <= st_unlocked;
								cycl_i <= c_cycl_ref + 2;

						when st_locked =>
							if (ypos_i = specs.ylen - 1) then
								if (shreg = "1111111111") then
									refpat       <= "1110010011";
									count_unlock <= c_unlock_cycles;

								elsif count_unlock /= 0 then
									refpat       <= "1110010011";
									count_unlock <= count_unlock - 1;

								else
									-- skip a cycle and start over
									state  <= st_unlocked;
									cycl_i <= c_cycl_ref + 2;
								end if;

							else
								if (shreg = refpat) then
								--if is_refresh(shreg) then
									refpat       <= ltrim(refpat, 2) & refpat(7 downto 6);
									count_unlock <= c_unlock_cycles;

								elsif count_unlock /= 0 then
									count_unlock <= count_unlock - 1;
									refpat       <= ltrim(refpat, 2) & refpat(7 downto 6);

								else
									-- skip a cycle and start over
									state  <= st_unlocked;
									cycl_i <= c_cycl_ref + 2;
								end if;
							end if;

						when others =>
					end case;
				end if;
			end if;

			-- advancing the pixel counter on odd strobe cycles
			if (strb(0) = '1') then
				if (xpos < specs.xlen - 1) then
					xpos <= xpos + 1;
				else
					xpos <= to_ppos(0);
				end if;
			end if;

			-- position outputs are latched on the last strobe of a character
			-- cycle, so that they are constant and correct during the whole
			-- cycle
			if (strb = 15) then
				-- cycl_i already contains the next cycle index
				cycl <= cycl_i;

				-- y coordinate cycles automatically
				if (cycl_i = 0) then
					if (ypos < specs.ylen - 1) then
						ypos <= ypos + 1;
					else
						ypos <= to_ppos(0);
					end if;
				end if;

				-- on the reference cycle we re-synchronize all counters with
				-- appropriate offsets
				if (cycl_i = c_cycl_ref + 1) then
					xpos <= specs.xref;

					if (ypos_i = 0) then
						ypos <= specs.yref;
					end if;
				end if;
			end if;

			rst_1r <= rst;
			if (rst_1r = '1') then
				state     <= st_unlocked;
				shreg_old <= (others => '0');
				cycl_i    <= (others => '0');
				ypos_i    <= (others => '0');
			end if;

		end if;

	end process;

end architecture;