library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

--  Uncomment the following lines to use the declarations that are
--  provided for instantiating Xilinx primitive components.
--library UNISIM;
--use UNISIM.VComponents.all;

use work.cpu_pack.ALL;

entity data_core is
	PORT(	CLK_I : in  std_logic;
			T2    : in  std_logic;
			CLR   : in  std_logic;
			CE    : in  std_logic;

			-- select signals
			SX    : in  std_logic_vector( 1 downto 0);
			SY    : in  std_logic_vector( 3 downto 0);
			OP    : in  std_logic_vector( 4 downto 0);		-- alu op
			PC    : in  std_logic_vector(15 downto 0);		-- PC
			QU    : in  std_logic_vector( 3 downto 0);			-- quick operand
			SA    : in  std_logic_vector(4 downto 0);			-- select address
			SMQ   : in  std_logic;							-- select MQ (H/L)

			-- write enable/select signal
			WE_RR : in  std_logic;
			WE_LL : in  std_logic;
			WE_SP : in  SP_OP;

			-- data in signals
			IMM  : in  std_logic_vector(15 downto 0);		-- immediate data
			RDAT : in  std_logic_vector( 7 downto 0);		-- memory/IO data

			-- memory control signals
			ADR     : out std_logic_vector(15 downto 0);
			MQ     : out std_logic_vector( 7 downto 0);

			Q_RR   : out std_logic_vector(15 downto 0);
			Q_LL   : out std_logic_vector(15 downto 0);
			Q_SP   : out std_logic_vector(15 downto 0)
		);
end data_core;

architecture Behavioral of data_core is

	function b8(A : std_logic) return std_logic_vector is
	begin
		return A & A & A & A & A & A & A & A;
	end;

	COMPONENT alu8
	PORT(	CLK_I : in  std_logic;
			T2    : in  std_logic;
			CE    : in  std_logic;
			CLR   : in  std_logic;

			ALU_OP : IN  std_logic_vector( 4 downto 0);
			XX     : IN  std_logic_vector(15 downto 0);
			YY     : IN  std_logic_vector(15 downto 0);
			ZZ     : OUT std_logic_vector(15 downto 0)
		);
	END COMPONENT;

	COMPONENT select_yy
	PORT(	SY      : IN  std_logic_vector( 3 downto 0);
			IMM     : IN  std_logic_vector(15 downto 0);
			QUICK   : IN  std_logic_vector( 3 downto 0);
			RDAT    : IN  std_logic_vector( 7 downto 0);
			RR      : IN  std_logic_vector(15 downto 0);
			YY      : OUT std_logic_vector(15 downto 0)
		);
	END COMPONENT;

	-- cpu registers
	--
	signal RR     : std_logic_vector(15 downto 0);
	signal LL     : std_logic_vector(15 downto 0);
	signal SP     : std_logic_vector(15 downto 0);

	-- internal buses
	--
	signal XX      : std_logic_vector(15 downto 0);
	signal YY      : std_logic_vector(15 downto 0);
	signal ZZ      : std_logic_vector(15 downto 0);
	signal ADR_X   : std_logic_vector(15 downto 0);
	signal ADR_Z   : std_logic_vector(15 downto 0);
	signal ADR_YZ  : std_logic_vector(15 downto 0);
	signal ADR_XYZ : std_logic_vector(15 downto 0);

begin

	alu_8: alu8
	PORT MAP(	CLK_I  => CLK_I,
				T2     => T2,
				CE     => CE,
				CLR    => CLR,
				ALU_OP => OP,
				XX     => XX,
				YY     => YY,
				ZZ     => ZZ
	);

	selyy: select_yy
	PORT MAP(	SY      => SY,
				IMM     => IMM,
				QUICK   => QU,
				RDAT    => RDAT,
				RR      => RR,
				YY      => YY
	);

	ADR		<= ADR_XYZ;
	MQ      <= ZZ(15 downto 8) when SMQ = '1' else ZZ(7 downto 0);

	Q_RR <= RR;
	Q_LL <= LL;
	Q_SP <= SP;

	-- memory address
	--
	sel_ax: process(SA(4 downto 3), IMM)

		variable SAX : std_logic_vector(4 downto 3);

	begin
		SAX := SA(4 downto 3);

		case SAX is

			when SA_43_I16    =>	ADR_X <= IMM;
			when SA_43_I8S =>	ADR_X <= b8(IMM(7)) & IMM(7 downto 0);
			when others	   =>	ADR_X <= b8(SA(3)) & b8(SA(3));
		end case;
	end process;

	sel_az: process(SA(2 downto 1), LL, RR, SP)

		variable SAZ : std_logic_vector(2 downto 1);

	begin
		SAZ := SA(2 downto 1);

		case SAZ is
			when SA_21_0  =>	ADR_Z <= X"0000";
			when SA_21_LL =>	ADR_Z <= LL;
			when SA_21_RR =>	ADR_Z <= RR;
			when others	  =>	ADR_Z <= SP;
		end case;
	end process;

	sel_ayz: process(SA(0), ADR_Z)
	begin
		ADR_YZ <= ADR_Z + (X"000" & "000" & SA(0));
	end process;

	sel_axyz: process(ADR_X, ADR_YZ)
	begin
		ADR_XYZ <= ADR_X + ADR_YZ;
	end process;

	sel_xx: process(SX, LL, RR, SP, PC)
	begin
		case SX is
			when SX_LL	=>	XX <= LL;
			when SX_RR	=>	XX <= RR;
			when SX_SP	=>	XX <= SP;
			when others	=>	XX <= PC;
		end case;
	end process;

	regs: process(CLK_I)
	begin
		if (rising_edge(CLK_I)) then
			if    (CLR = '1') then
				RR  <= X"0000";
				LL  <= X"0000";
				SP  <= X"0000";
			elsif (CE  = '1' and T2 = '1') then
				if (WE_RR = '1') then		RR  <= ZZ;		end if;
				if (WE_LL = '1') then		LL  <= ZZ;		end if;

				case WE_SP is
					when SP_INC  	=>		SP <= ADR_YZ;
					when SP_LOAD 	=>		SP <= ADR_XYZ;
					when SP_NOP		=>		null;
				end case;
			end if;
		end if;
	end process;

end Behavioral;
