LIBRARY ieee;
use IEEE.STD_LOGIC_1164.ALL;
USE ieee.numeric_std.ALL;

entity j1 is
	Generic
	(
		R_STACK_SIZE			: natural := 32;
		D_STACK_SIZE			: natural := 32

	);
	Port
	(
		sys_clk_i	: in  std_logic;
		sys_rst_i	: in  std_logic;
		io_din		: in  unsigned(15 downto 0);
		io_rd		: out std_logic;
		io_wr		: out std_logic;
		io_addr		: out unsigned(15 downto 0);
		io_dout		: out unsigned(15 downto 0);
		inst_din	: in  unsigned(15 downto 0);
		inst_addr	: out unsigned(12 downto 0);
		data_addr	: out unsigned(12 downto 0);
		data_we		: out std_logic;
		data_en		: out std_logic;
		data_din	: in  unsigned(15 downto 0);
		data_dout	: out unsigned(15 downto 0)
		
	);
end j1;

architecture behave of j1 is

signal insn				: unsigned(15 downto 0);
signal immediate		: unsigned(15 downto 0);
signal ramrd			: unsigned(15 downto 0);
	
signal dsp				: unsigned(4 downto 0);		-- Data stack pointer (register)
signal dsp2				: unsigned(4 downto 0);		-- register
signal st0				: unsigned(15 downto 0);	-- Return stack pointer (register)
signal st02				: unsigned(15 downto 0);	-- register

signal dstkW2			: std_logic;				-- D stack write

signal pc				: unsigned(12 downto 0);	-- register
signal pc2				: unsigned(12 downto 0);	-- register
signal rsp				: unsigned(4 downto 0);		-- register
signal rsp2				: unsigned(4 downto 0);		-- register

signal rstkW2			: std_logic;				-- register
signal rstkD2			: unsigned(15 downto 0);	-- register
signal ramWE2			: std_logic;				-- RAM write enable

signal pc_plus_1		: unsigned(15 downto 0);

type stack_t is array (integer range <>) of unsigned(15 downto 0);

signal dstack			: stack_t(0 to D_STACK_SIZE-1);	-- register
signal rstack			: stack_t(0 to R_STACK_SIZE-1);	-- register

signal st1				: unsigned(15 downto 0);
signal rst0				: unsigned(15 downto 0);

signal st0sel			: unsigned(3 downto 0);

signal is_alu			: BOOLEAN;
signal is_lit			: BOOLEAN;

signal dd				: unsigned(1 downto 0);		-- D stack delta
signal rd				: unsigned(1 downto 0);		-- R stack delta

signal stack_ram_en		: std_logic;				-- helper for RAM R/W
signal stack_ram_we		: std_logic;				-- helper for RAM R/W

begin

-- LINE 25:
pc_plus_1	<= ("000" & pc)  + 1;

-- LINE 37:
st1			<= dstack(to_integer(dsp));
rst0		<= rstack(to_integer(rsp));
immediate	<= '1' & insn(14 downto 0);

-- LINE 112:
is_alu		<= (insn(15 downto 13) = "011");
is_lit		<= (insn(15) = '1');


-- LINE 115:
io_rd		<= '1' when (is_alu and (insn(11 downto 8) = X"C")) else '0';
io_wr		<= ramWE2;
io_addr		<= st0;
io_dout		<= st1;
ramWE2		<= '1' when (is_alu and (insn(5) = '1')) else '0';
dstkW2		<= '1' when (is_lit or (is_alu and insn(7) = '1')) else '0';
dd			<= insn(1 downto 0);	
rd			<= insn(3 downto 2);


-- LINE 27:
-- The D and R stacks
proc_d_and_r_stack:
process(sys_clk_i)
begin
	if rising_edge(sys_clk_i) then
		if dstkW2 = '1' then
			dstack(to_integer(dsp2)) <= st0;
		end if;
		if rstkW2 = '1' then
			rstack(to_integer(rsp2)) <= rstkD2;
		end if;
		
	end if;
end process;


-- LINE 40:
-- st0sel is the ALU operation.  For branch and call the operation
-- is T, for 0branch it is N.  For ALU ops it is loaded from the instruction
-- field.

proc_aluop_sel:
process(insn)
begin

	
	case insn(14 downto 13) is
	
	when "00" => st0sel <= "0000";
	when "10" => st0sel <= "0000";
	when "01" => st0sel <= "0001";
	when "11" => st0sel <= insn(11 downto 8);
	when others => st0sel <= "XXXX";
		
	end case;
end process;


-- LINE 55:
-- define RAMS outside this module
insn		<= inst_din;
inst_addr	<= pc2(12 downto 0);
data_en		<= not (st02(15) or st02(14));
data_we		<= ramWE2 and not (st02(15) or st02(14));
ramrd		<= data_din;
data_dout	<= st1;
data_addr	<= st02(13 downto 1);


-- LINE 85:
-- Compute the new value of T.
proc_alu:
process(insn, immediate, st0sel, st0, st1, rst0, rsp, dsp)
begin

	if insn(15) = '1' then
		st02 <= immediate;
	else
		case st0sel is
		
		when "0000" => st02 <= st0;
		when "0001" => st02 <= st1;
		when "0010" => st02 <= st0 + st1;
		when "0011" => st02 <= st0 and st1;
		when "0100" => st02 <= st0 or st1;
		when "0101" => st02 <= st0 xor st1;
		when "0110" => st02 <= not st0;
		when "0111" => 
			if st1 = st0 then
				st02 <= X"FFFF";
			else
				st02 <= X"0000";
			end if;
		
		when "1000" =>
			if signed(st1) < signed(st0) then
				st02 <= X"FFFF";
			else
				st02 <= X"0000";
			end if;
			
		when "1001" => st02 <= st1 srl to_integer(st0(3 downto 0));
		when "1010" => st02 <= st0 - 1;
		when "1011" => st02 <= rst0;
		when "1100" =>
			if st0(15) = '1' or st0(14) = '1' then	-- LINE 104: ????
				st02 <= io_din;
			else
				st02 <= ramrd;
			end if;
	
		when "1101" => st02 <= st1 sll to_integer(st0(3 downto 0));
		when "1110" => st02 <= "000" & rsp & "000" & dsp; -- LINE 106: ????
		when "1111" => 
			if st1 < st0 then
				st02 <= X"FFFF";
			else
				st02 <= X"0000";
			end if;
		when others => st02 <= "XXXXXXXXXXXXXXXX";
		
		end case;
	end if;
end process;

-- LINE 126:
proc_stack_ctrl:
process(is_lit, is_alu, pc2, dd, rd, insn, st0, pc_plus_1, rsp, dsp)
begin
	if is_lit then			-- literal
		dsp2 <= dsp + 1;
		rsp2 <= rsp;
		rstkW2 <= '0';
		rstkD2 <= "000" & pc2;
	elsif is_alu then
		dsp2 <= dsp + (dd(1) & dd(1) & dd(1) & dd);
		rsp2 <= rsp + (rd(1) & rd(1) & rd(1) & rd);
		rstkW2 <= insn(6);
		rstkD2 <= st0;
	else					-- jump/call
		-- predicated jump is like DROP
		if insn(15 downto 13) = "001" then
			dsp2	<= dsp - 1;
		else
			dsp2	<= dsp;		-- *default
		end if;
		if insn(15 downto 13) = "010" then	-- call
			rsp2 <= rsp + 1;
			rstkW2 <= '1';
			rstkD2 <= pc_plus_1(14 downto 0) & '0';
		else
			rsp2 <= rsp;				-- *default
			rstkW2 <= '0';				-- *default
			rstkD2 <= "000" & pc2;		-- *default
		end if;
		
	end if;
end process;

-- LINE 157:
proc_pc:
process(sys_rst_i, is_alu, insn, pc, st0, rst0, pc_plus_1)
begin

	if sys_rst_i = '1' then
		pc2 <= pc;
	else
		if  ((insn(15 downto 13) = "000") 
		or  ((insn(15 downto 13) = "001")
		and (st0 = X"0000")) 
		or  (insn(15 downto 13) = "010")) then
			pc2 <= insn(12 downto 0);
		elsif is_alu and (insn(12) = '1') then	-- return
			pc2 <= rst0(13 downto 1);		-- LINE 167: ????
		else
			pc2	<= pc_plus_1(12 downto 0);	-- LINE 169: truncation ????
		end if;
	end if;
end process;

-- LINE 172:
proc_next:
process(sys_clk_i)
begin

	if rising_edge(sys_clk_i) then
		if sys_rst_i = '1' then
			pc	<= (others => '0');
			dsp	<= (others => '0');
			st0	<= (others => '0');
			rsp	<= (others => '0');
		else
			dsp <= dsp2;
			pc  <= pc2;
			st0 <= st02;
			rsp <= rsp2;
		end if;
	end if;
end process;

end behave;
