library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity ALU is
    port(Sel     :  in  std_logic_vector(4 downto 0);
         CarryIn :  in  std_logic;
         A, B    :  in  std_logic_vector(7 downto 0);
         Y       :  out std_logic_vector(7 downto 0) );
end entity ALU;

architecture COND_DATA_FLOW of ALU is
begin

    ALU_AND_SHIFT:
    process (Sel, A, B, CarryIn)
        variable Sel0_1_CarryIn  : std_logic_vector(2 downto 0);
        variable LogicUnit, ArithUnit,
                 ALU_NoShift     : std_logic_vector(7 downto 0);
    begin
        ---------------------------
        -- Logic Unit 
        ---------------------------
        LOGIC_UNIT: case Sel(1 downto 0) is
            when "00"    => LogicUnit := A and B;
            when "01"    => LogicUnit := A or B;
            when "10"    => LogicUnit := A xor B;
            when "11"    => LogicUnit := not A;
            when others  => LogicUnit := (others => 'X');
        end case LOGIC_UNIT;
        ---------------------------
        -- Arithmetic Unit
        ---------------------------
        Sel0_1_CarryIN := Sel(1 downto 0) & CarryIN;
        ARITH_UNIT: case Sel0_1_CarryIn is
            when "000"   => ArithUnit := A;
            when "001"   => ArithUnit := A+1;
            when "010"   => ArithUnit := A+B;
            when "011"   => ArithUnit := A+B+1;
            when "100"   => ArithUnit := A + not B;
            when "101"   => ArithUnit := A-B;
            when "110"   => ArithUnit := A-1;
            when "111"   => ArithUnit := A;
            when others  => ArithUnit := (others => 'X');
        end case ARITH_UNIT;
        ---------------------------
        -- Mutiplex
        ---------------------------
        LA_MUX: if (Sel(2) = '1') then
            ALU_NoShift := LogicUnit;
        else 
            ALU_NoSHift := ArithUnit;
        end if LA_MUX;
        ---------------------------
        -- Shift operation
        ---------------------------
        SHIFT: case Sel(4 downto 3) is
            when "00"    =>  Y <= ALU_NoSHift;
            when "01"    =>  Y <= (ALU_NoShift(6 downto 0) & '0');
            when "10"    =>  Y <= ('0' & ALU_NoShift(7 downto 1));
            when "11"    =>  Y <= (others => '0');
            when others  =>  Y <= (others => 'X');
        end case SHIFT;
    end process ALU_AND_SHIFT;
end architecture COND_DATA_FLOW;

