-------------------------------------------------------------------------------
--  HTL80186 - CPU core                                                      --
--                                                                           --
--  https://github.com/htminuslab                                            --  
--                                                                           --
-------------------------------------------------------------------------------
-- Project       : HTL80186                                                  --
-- Module        : ALU                                                       --
-- Library       : I80186                                                    --
--                                                                           --
-- Version       : 1.0  05/21/02   Created HT-LAB                            --
--               : 1.6  26/06/11   Simplified Flag assignment.               --
--               :      AAM/AAD updated for variable base.                   --
--               :      Updated/cleanup eflag register, default now defined  -- 
--               :      in cpu86pack.vhd file under RESET_CC_C constant.     --
--               : 1.1  07/05/2023   Uploaded to github under MIT license.   --
-------------------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_unsigned.ALL;
USE ieee.std_logic_arith.ALL;
LIBRARY I80186;
USE I80186.cpu86pack.ALL;

ENTITY ALU IS
   PORT( 
      alu_inbusa : IN     std_logic_vector (15 DOWNTO 0);
      alu_inbusb : IN     std_logic_vector (15 DOWNTO 0);
      aluopr     : IN     std_logic_vector (6 DOWNTO 0);
      ax_s       : IN     std_logic_vector (15 DOWNTO 0);
      clk        : IN     std_logic;
      dx_s       : IN     std_logic_vector (15 DOWNTO 0);
      reset      : IN     std_logic;
      w          : IN     std_logic;
      wralu      : IN     std_logic;
      wrcc       : IN     std_logic;
      wrtemp     : IN     std_logic;
      alu_ccbus  : OUT    std_logic_vector (2 DOWNTO 0);
      alubus     : OUT    std_logic_vector (15 DOWNTO 0);
      ccbus      : OUT    std_logic_vector (15 DOWNTO 0);
      div_err    : OUT    std_logic
   );
END ALU ;

architecture rtl of alu is

component divider is                                    -- Generic Divider

generic( 
        WIDTH_DIVID : Integer := 32;                    --  Width Dividend
        WIDTH_DIVIS : Integer := 16;                    --  Width Divisor
        WIDTH_SHORT : Integer := 8);                    --  Check Overflow against short Byte/Word
port( 
        clk         : in   std_logic;                   -- System Clock   
        reset       : in   std_logic;                   -- Active high
        dividend    : in   std_logic_vector (WIDTH_DIVID-1 DOWNTO 0);
        divisor     : in   std_logic_vector (WIDTH_DIVIS-1 DOWNTO 0);
        quotient    : out  std_logic_vector (WIDTH_DIVIS-1 DOWNTO 0);  -- changed to 16 bits!! (S not D)
        remainder   : out  std_logic_vector (WIDTH_DIVIS-1 DOWNTO 0);
        twocomp     : in   std_logic;                   -- '1' = 2's Complement, '0' = Unsigned
        w           : in   std_logic;                   -- '0'=byte, '1'=word (cpu processor)
        overflow    : out  std_logic;                   -- '1' if div by 0 or overflow
        start       : in   std_logic;                   -- not used here
        done        : out  std_logic);                  -- not used here
end component divider;

component multiplier is                                 -- Generic Multiplier
 generic (WIDTH     : integer := 16);                   
    port (clk         : in   std_logic;
        multiplicant  : in   std_logic_vector (WIDTH-1 downto 0); 
        multiplier    : in   std_logic_vector (WIDTH-1 downto 0); 
        product       : out  std_logic_vector (WIDTH+WIDTH-1 downto 0);-- result
        twocomp       : in   std_logic);
end component multiplier;

signal  product_s   : std_logic_vector(31 downto 0);    -- result multiplier 

signal  dividend_s  : std_logic_vector(31 downto 0);    -- Input divider
signal  remainder_s : std_logic_vector(15 downto 0);    -- Divider result
signal  quotient_s  : std_logic_vector(15 downto 0);    -- Divider result
signal  divresult_s : std_logic_vector(31 DOWNTO 0);    -- Output divider to alubus
--signal  div_err_s   : std_logic;                       -- Divide by 0 

signal  twocomp_s   : std_logic;                        -- Sign Extend for IMUL and IDIV
signal  wl_s        : std_logic;                        -- Latched w signal, used for muliplier/divider

signal  alubus_s    : std_logic_vector (15 DOWNTO 0);

signal  abus_s      : std_logic_vector(15 downto 0);        
signal  bbus_s      : std_logic_vector(15 downto 0);  
signal  dxbus_s     : std_logic_vector(15 downto 0);    -- DX register 

signal  addbbus_s   : std_logic_vector(15 downto 0);    -- bbus connected to full adder 
signal  cbus_s      : std_logic_vector(16 downto 0);    -- Carry Bus
signal  outbus_s    : std_logic_vector(15 downto 0);    -- outbus=abus+bbus

signal  sign16a_s   : std_logic_vector(15 downto 0);    -- sign extended alu_busa(7 downto 0)
signal  sign16b_s   : std_logic_vector(15 downto 0);    -- sign extended alu_busb(7 downto 0)
signal  sign32a_s   : std_logic_vector(15 downto 0);    -- 16 bits alu_busa(15) vector (CWD)

signal  aasbus_s    : std_logic_vector(15 downto 0);    -- used for AAS instruction
signal  aas1bus_s   : std_logic_vector(15 downto 0);    

signal  daabus_s    : std_logic_vector(7 downto 0);     -- used for DAA instruction
signal  dasbus_s    : std_logic_vector(7 downto 0);     -- used for DAS instruction

signal  aaabus_s    : std_logic_vector(15 downto 0);    -- used for AAA instruction
signal  aaa1bus_s   : std_logic_vector(15 downto 0);    

signal  aadbus_s    : std_logic_vector(15 downto 0);    -- used for AAD instruction
signal  aad1bus_s   : std_logic_vector(15 downto 0);    
signal  aad2bus_s   : std_logic_vector(7 downto 0);    

signal  setaas_s    : std_logic;                        -- '1' set CF & AF else both 0
signal  setaaa_s    : std_logic;                        -- '1' set CF & AF else both 0
signal  setdaa_s    : std_logic_vector(1 downto 0);     -- "11" set CF & AF 
signal  setdas_s    : std_logic_vector(1 downto 0);     -- "11" set CF & AF 

signal  bit4_s      : std_logic;                        -- used for AF flag
signal  cout_s      : std_logic;                    

signal  psrreg_s    : std_logic_vector(15 downto 0);    -- 16 bits flag register

signal  zflaglow_s  : std_logic;                        -- low byte zero flag (w=0)
signal  zflaghigh_s : std_logic;                        -- high byte zero flag (w=1)
signal  zeroflag_s  : std_logic;                        -- zero flag, asserted when zero

signal  zflagdx_s   : std_logic;                        -- Result (DX) zero flag, asserted when not zero (used for mul/imul)

signal  zflagah_s   : std_logic;                        -- '1' if IMUL(15..8)/=0
signal  hflagah_s   : std_logic;                        -- Used for IMUL
signal  hflagdx_s   : std_logic;                        -- Used for IMUL

signal  overflow_s  : std_logic;
signal  parityflag_s: std_logic; 
signal  signflag_s  : std_logic;                        

alias   OFLAG       : std_logic is psrreg_s(11);
alias   DFLAG       : std_logic is psrreg_s(10);
alias   IFLAG       : std_logic is psrreg_s(9);
alias   TFLAG       : std_logic is psrreg_s(8);                           
alias   SFLAG       : std_logic is psrreg_s(7);
alias   ZFLAG       : std_logic is psrreg_s(6);
alias   AFLAG       : std_logic is psrreg_s(4);
alias   PFLAG       : std_logic is psrreg_s(2);
alias   CFLAG       : std_logic is psrreg_s(0);

signal  alureg_s    : std_logic_vector(31 downto 0);    -- 31 bits temp register for alu_inbusa & alu_inbusb
signal  alucout_s   : std_logic;                        -- ALUREG Carry Out signal

signal  alu_temp_s  : std_logic_vector(15 downto 0);    -- Temp/scratchpad register, use ALU_TEMP to select

signal  done_s      : std_logic;                        -- Serial divider conversion done
signal  startdiv_s  : std_logic;                        -- Serial divider start pulse

begin

ALUU1 : divider
    generic map (WIDTH_DIVID =>  32,  WIDTH_DIVIS =>  16, WIDTH_SHORT => 8)
    port map   (clk         => clk,
                reset       => reset,
                dividend    => dividend_s,              -- DX:AX
                divisor     => alureg_s(15 downto 0),   -- 0&byte/word
                quotient    => quotient_s,              -- 16 bits
                remainder   => remainder_s,             -- 16 bits
                twocomp     => twocomp_s,
                w           => wl_s,                    -- Byte/Word
                overflow    => div_err,                 -- Divider Overflow. generate int0
                start       => startdiv_s,              -- start conversion, generated by proc      
                done        => done_s);                 -- conversion done, latch results

ALUU2 : multiplier
    generic map (WIDTH      =>  16)                     -- Result is 2*WIDTH bits
    port map   (clk         => clk,
                multiplicant=> alureg_s(31 downto 16),      
                multiplier  => alureg_s(15 downto 0),                   -- 0&byte/word              
                product     => product_s,               -- 32 bits!
                twocomp     => twocomp_s);

dividend_s  <= X"000000"&alureg_s(23 downto 16) when aluopr=ALU_AAM else dxbus_s & alureg_s(31 downto 16);-- DX is sign extended for byte IDIV

-- start serial divider 1 cycle after wralu pulse received. The reason is that the dividend is loaded into the
-- accumulator thus the data must be valid when this happens.
process (clk, reset)
    begin 
        if reset='1' then
            startdiv_s <= '0';
        elsif rising_edge(clk) then
            if  (wralu='1' AND (aluopr=ALU_DIV OR aluopr=ALU_IDIV OR aluopr=ALU_AAM)) then
                startdiv_s <= '1';  
            else 
                startdiv_s <= '0';
            end if;
        end if;
end process;

----------------------------------------------------------------------------
-- Create a Full adder
----------------------------------------------------------------------------   
fulladd: for bit_nr in 0 to 15 generate       
    outbus_s(bit_nr) <= abus_s(bit_nr) xor addbbus_s(bit_nr) xor cbus_s(bit_nr);

    cbus_s(bit_nr+1) <= (abus_s(bit_nr) and addbbus_s(bit_nr)) or
                        (abus_s(bit_nr) and cbus_s(bit_nr)) or
                        (addbbus_s(bit_nr) and cbus_s(bit_nr));
end generate fulladd;
      
bit4_s    <= cbus_s(4);  

sign16a_s <= alu_inbusa(7) &alu_inbusa(7) &alu_inbusa(7) &alu_inbusa(7)&alu_inbusa(7)&
             alu_inbusa(7) &alu_inbusa(7) &alu_inbusa(7) &alu_inbusa(7 downto 0);   
sign16b_s <= alu_inbusb(7) &alu_inbusb(7) &alu_inbusb(7) &alu_inbusb(7)&alu_inbusb(7)&
             alu_inbusb(7) &alu_inbusb(7) &alu_inbusb(7) &alu_inbusb(7 downto 0);
sign32a_s <= alu_inbusa(15)&alu_inbusa(15)&alu_inbusa(15)&alu_inbusa(15)&alu_inbusa(15)&
             alu_inbusa(15)&alu_inbusa(15)&alu_inbusa(15)&alu_inbusa(15)&alu_inbusa(15)&
             alu_inbusa(15)&alu_inbusa(15)&alu_inbusa(15)&alu_inbusa(15)&alu_inbusa(15)&
             alu_inbusa(15);

-- Invert bus for subtract instructions
addbbus_s <= not bbus_s when ((aluopr=ALU_CMP) or (aluopr=ALU_CMP_SE) or (aluopr=ALU_CMPS) or (aluopr=ALU_DEC) 
                           or (aluopr=ALU_SBB) or (aluopr=ALU_SBB_SE) or (aluopr=ALU_PUSH) or (aluopr=ALU_SUB) 
                           or (aluopr=ALU_SUB_SE) or (aluopr=ALU_SCAS)) else bbus_s;


-- sign extend for IDIV and IMUL instructions           
twocomp_s <= '1' when  ((aluopr=ALU_IDIV) or (aluopr=ALU_IMUL) or
                        (aluopr=ALU_IDIV2)or (aluopr=ALU_IMUL2)) else '0';   

----------------------------------------------------------------------------
-- Sign Extend Logic abus & bbus & dxbus
----------------------------------------------------------------------------
process (w, alu_inbusa, alu_inbusb, sign16a_s, sign16b_s, aluopr, ax_s, alureg_s)
    begin 
        if (w='1') then                                 -- Word, no sign extend, unless signextend is specified
            case aluopr is  
                when ALU_CMPS =>
                    abus_s  <= alu_inbusa;              -- no sign extend
                    bbus_s  <= alureg_s(15 downto 0);   -- previous read ES:[DI]
                when ALU_NEG | ALU_NOT =>
                    abus_s  <= not(alu_inbusa);         -- NEG instruction, not(operand)+1
                    bbus_s  <= alu_inbusb;              -- 0001 (0000 for NOT)
                when ALU_ADD_SE | ALU_ADC_SE | ALU_SBB_SE | ALU_SUB_SE | ALU_CMP_SE |
                     ALU_OR_SE  | ALU_AND_SE | ALU_XOR_SE | ALU_IMUL8 => -- ALU_IMUL8 added Version 1.6, sign extend 8bits imm
                    abus_s  <= alu_inbusa;              -- no sign extend
                    bbus_s  <= sign16b_s;               -- Sign extend on 8 bits immediate values (see O80I2RM)                        
                when others =>
                    abus_s  <= alu_inbusa;              -- no sign extend
                    bbus_s  <= alu_inbusb;
            end case;
        else                                                
            case aluopr is  
                when ALU_CMPS =>
                    abus_s  <= alu_inbusa;
                    bbus_s  <= alureg_s(15 downto 0);       
                when ALU_DIV | ALU_DIV2  =>
                    abus_s  <= ax_s;
                    bbus_s  <= alu_inbusb;
                when ALU_IDIV| ALU_IDIV2 =>
                    abus_s  <= ax_s;
                    bbus_s  <= sign16b_s;
                when ALU_MUL | ALU_MUL2 | ALU_SCAS  =>
                    abus_s  <= alu_inbusa;
                    bbus_s  <= alu_inbusb;
                when ALU_NEG | ALU_NOT =>
                    abus_s  <= not(alu_inbusa);         -- NEG instruction, not(operand)+1
                    bbus_s  <= alu_inbusb;              -- 0001 (0000 for NOT)
                 when others =>
                    abus_s  <= sign16a_s;
                    bbus_s  <= sign16b_s;
            end case;                    
        end if;
end process; 

process (wl_s, aluopr, dx_s, alu_inbusa)                -- dxbus for DIV/IDIV only
    begin                   
        if (wl_s='1') then                              -- Word, no sign extend
            dxbus_s  <= dx_s;                   
        else                                            -- Byte  
            if (((aluopr=ALU_IDIV) or (aluopr=ALU_IDIV2)) and (alu_inbusa(15)='1')) then    -- signed DX<-SE(AX)/bbus<-SE(byte)
                dxbus_s <= X"FFFF";                     -- DX=FFFF (ignored for mul)
            else 
                dxbus_s <= X"0000";                     -- DX=0000 (ignored for mul)
            end if;
        end if;
end process;                         

----------------------------------------------------------------------------
-- Carry In logic
----------------------------------------------------------------------------
process (aluopr, psrreg_s)
    begin 
        case aluopr is
            when ALU_ADD | ALU_ADD_SE | ALU_INC | ALU_POP | ALU_NEG | ALU_NOT   
                            =>  cbus_s(0) <= '0';
            when ALU_SBB | ALU_SBB_SE   
                            =>  cbus_s(0) <= not CFLAG;
            when ALU_SUB | ALU_SUB_SE | ALU_DEC | ALU_PUSH | ALU_CMP | ALU_CMP_SE 
                         | ALU_CMPS   | ALU_SCAS    
                            =>  cbus_s(0) <= '1';
            when others     =>  cbus_s(0) <= CFLAG;     -- ALU_ADC, ALU_SUB, ALU_SBB
        end case;
end process; 

----------------------------------------------------------------------------
-- Carry Out logic
-- cout is inverted for ALU_SUB and ALU_SBB before written to psrreg_s
----------------------------------------------------------------------------
process (aluopr, w, psrreg_s, cbus_s, alu_inbusa)
    begin 
        case aluopr is  
            when ALU_ADD | ALU_ADD_SE | ALU_ADC | ALU_ADC_SE | ALU_SUB | ALU_SUB_SE | ALU_SBB | ALU_SBB_SE |
                 ALU_CMP | ALU_CMP_SE | ALU_CMPS| ALU_SCAS => 
                if (w='1') then cout_s <= cbus_s(16); 
                    else cout_s <= cbus_s(8); 
                end if;
            when ALU_NEG =>                             -- CF=0 if operand=0, else 1
                if (alu_inbusa=X"0000") then
                    cout_s <= '1';                      -- Note CFLAG=NOT(cout_s)
                else
                    cout_s <= '0';                      -- Note CFLAG=NOT(cout_s)
                end if;         
            when others  => 
                cout_s <= CFLAG;                        -- Keep previous value
        end case;       
end process;
 
----------------------------------------------------------------------------
-- Overflow Logic
----------------------------------------------------------------------------
process (aluopr, w, psrreg_s, cbus_s, alureg_s, alucout_s, zflaghigh_s, zflagdx_s,hflagdx_s,zflagah_s,
         hflagah_s, wl_s, product_s)--, shift1_s)
    begin 
        case aluopr is          
            when ALU_ADD | ALU_ADD_SE | ALU_ADC | ALU_ADC_SE | ALU_INC | ALU_DEC | ALU_SUB | ALU_SUB_SE |  
                 ALU_SBB | ALU_SBB_SE | ALU_CMP | ALU_CMP_SE | ALU_CMPS | ALU_SCAS | ALU_NEG =>
                if w='1' then                       -- 16 bits
                    overflow_s  <= cbus_s(16) xor cbus_s(15);
                else 
                    overflow_s  <= cbus_s(8) xor cbus_s(7);             
                end if;
            when ALU_ROL1 | ALU_ROL | ALU_ROLIMM |                      -- Version 1.6
                 ALU_RCL1 | ALU_RCL | ALU_RCLIMM |
                 ALU_SHL1 | ALU_SHL | ALU_SHLIMM =>
                 --ALU_SAR1 | ALU_SAR | ALU_SARIMM =>
                if (((w='1') and (alureg_s(15)/=alucout_s)) or
                    ((w='0') and (alureg_s(7) /=alucout_s))) then
                    overflow_s <= '1';  
                else
                    overflow_s <= '0'; 
                end if;
            when ALU_ROR1 | ALU_ROR | ALU_RORIMM |                       -- Version 1.6
                 ALU_RCR1 | ALU_RCR | ALU_RCRIMM |
                 ALU_SHR1 | ALU_SHR | ALU_SHRIMM =>
                if ((w='1' and (alureg_s(15)/=alureg_s(14))) or
                    (w='0' and (alureg_s(7) /=alureg_s(6)))) then  
                    overflow_s <= '1';  
                else
                    overflow_s <= '0'; 
                end if;   
        
            -- for the sar instruction, the of flag is always for 1 bit shifts
            when ALU_SAR1 | ALU_SAR | ALU_SARIMM =>
                overflow_s <= '0';
                        
            when ALU_MUL | ALU_MUL2 => 
                if (wl_s='0') then
                    overflow_s <= zflaghigh_s;  
                else
                    overflow_s <= zflagdx_s;            -- MSW multiply/divide result
                end if;
            when ALU_IMUL | ALU_IMUL2 =>                -- if MSbit(1)='1' & AH=FF/DX=FFFF 
                if ((wl_s='0' and product_s(7)='1'  and hflagah_s='1') or
                    (wl_s='0' and product_s(7)='0'  and zflagah_s='0') or
                    (wl_s='1' and product_s(15)='1' and hflagdx_s='1') or
                    (wl_s='1' and product_s(15)='0' and zflagdx_s='0')) then
                    overflow_s <= '0';  
                else
                    overflow_s <= '1';                  
                end if;
            when others     =>  
                overflow_s <= OFLAG;                    -- Keep previous value
        end case;
end process;

----------------------------------------------------------------------------
-- Zeroflag set if result=0, zflagdx_s=1 when dx/=0, zflagah_s=1 when ah/=0
----------------------------------------------------------------------------
zflaglow_s  <=  alubus_s(7)  or alubus_s(6)  or alubus_s(5)  or alubus_s(4)  or
                alubus_s(3)  or alubus_s(2)  or alubus_s(1)  or alubus_s(0);
zflaghigh_s <=  alubus_s(15) or alubus_s(14) or alubus_s(13) or alubus_s(12) or
                alubus_s(11) or alubus_s(10) or alubus_s(9)  or alubus_s(8);
zeroflag_s  <= not(zflaghigh_s or zflaglow_s) when w='1' else not(zflaglow_s);

zflagdx_s   <=  product_s(31) or product_s(30) or product_s(29) or product_s(28) or
                product_s(27) or product_s(26) or product_s(25) or product_s(24) or
                product_s(23) or product_s(22) or product_s(21) or product_s(20) or
                product_s(19) or product_s(18) or product_s(17) or product_s(16);

zflagah_s   <=  product_s(15) or product_s(14) or product_s(13) or product_s(12) or
                product_s(11) or product_s(10) or product_s(09) or product_s(08);

----------------------------------------------------------------------------
-- hflag set if IMUL result AH=FF or DX=FFFF 
----------------------------------------------------------------------------
hflagah_s  <=   product_s(15) and product_s(14) and product_s(13) and product_s(12) and
                product_s(11) and product_s(10) and product_s(9)  and product_s(8);

hflagdx_s  <=   product_s(31) and product_s(30) and product_s(29) and product_s(28) and
                product_s(27) and product_s(26) and product_s(25) and product_s(24) and
                product_s(23) and product_s(22) and product_s(21) and product_s(20) and
                product_s(19) and product_s(18) and product_s(17) and product_s(16);

----------------------------------------------------------------------------
-- Parity flag set if even number of bits in LSB
----------------------------------------------------------------------------
parityflag_s <=not(alubus_s(7) xor alubus_s(6) xor alubus_s(5) xor alubus_s(4)  xor
                   alubus_s(3) xor alubus_s(2) xor alubus_s(1) xor alubus_s(0));

----------------------------------------------------------------------------
-- Sign flag
----------------------------------------------------------------------------
signflag_s <= alubus_s(15) when w='1' else alubus_s(7);

-- process (clk)    -- removed Version 1.6
    -- begin 
        -- if rising_edge(clk) then
            alu_ccbus(0) <= zeroflag_s;                 -- Use unlatched??? flags for BOUND instruction.
            alu_ccbus(1) <= overflow_s;                 -- ccbus is only updated after wrcc pulse!
            alu_ccbus(2) <= signflag_s;
        -- end if;
-- end process;


----------------------------------------------------------------------------
-- Temp/ScratchPad Register
-- alu_temp_s can also be used as temp storage
-- temp<=bbus; 
----------------------------------------------------------------------------
process (clk, reset)
    begin 
        if reset='1' then
            alu_temp_s<= (others => '0');
        elsif rising_edge(clk) then
            if (wrtemp='1') then 
                alu_temp_s <= bbus_s;
            end if;
        end if;
end process;


----------------------------------------------------------------------------
-- ALU Register used for xchg and rotate/shift instruction
-- latch Carry Out alucout_s signal
----------------------------------------------------------------------------
process (clk, reset)
    begin 
        if reset='1' then
            alureg_s  <= (others => '0'); 
            alucout_s <= '0';                                             
            wl_s      <= '0';       
        elsif rising_edge(clk) then
            if (wralu='1') then 
                alureg_s(31 downto 16) <= abus_s;       -- alu_inbusa;
                wl_s <= w;                              -- Latched w version
                if w='1' then                           -- word operation
                    case aluopr is
                        when ALU_ROL | ALU_ROL1 | ALU_ROLIMM => alureg_s(15 downto 0) <= alureg_s(14 downto 0) & alureg_s(15);
                                                   alucout_s<= alureg_s(15); 
                        when ALU_ROR | ALU_ROR1 | ALU_RORIMM => alureg_s(15 downto 0) <= alureg_s(0) & alureg_s(15 downto 1); 
                                                   alucout_s<= alureg_s(0);     
                        when ALU_RCL | ALU_RCL1 | ALU_RCLIMM => alureg_s(15 downto 0) <= alureg_s(14 downto 0) & alucout_s; -- shift carry in
                                                   alucout_s<= alureg_s(15);    
                        when ALU_RCR | ALU_RCR1 | ALU_RCRIMM => alureg_s(15 downto 0) <= alucout_s & alureg_s(15 downto 1);
                                                   alucout_s<= alureg_s(0);     
                        when ALU_SHL | ALU_SHL1 | ALU_SHLIMM => alureg_s(15 downto 0) <= alureg_s(14 downto 0) & '0';
                                                   alucout_s<= alureg_s(15);    
                        when ALU_SHR | ALU_SHR1 | ALU_SHRIMM => alureg_s(15 downto 0) <= '0' & alureg_s(15 downto 1);
                                                   alucout_s<= alureg_s(0);     
                        when ALU_SAR | ALU_SAR1 | ALU_SARIMM => alureg_s(15 downto 0) <= alureg_s(15) & alureg_s(15 downto 1);
                                                   alucout_s<= alureg_s(0);
                        when ALU_TEMP           => alureg_s(15 downto 0) <= bbus_s;      -- Same as ALU_REGL?
                                                   alucout_s<= '-';         
                        when ALU_AAM            => alureg_s(15 downto 0) <= bbus_s;     -- X"000A";    -- Version 1.6 fixed to variable base
                                                   alucout_s<= alureg_s(15);             -- Don't care
                        when others             => alureg_s(15 downto 0) <= bbus_s ;    
                                                   alucout_s<= CFLAG; 
                    end case; 
                else
                    case aluopr is                    -- To aid resource sharing add MSB byte as above!!
                        when ALU_ROL | ALU_ROL1 | ALU_ROLIMM => alureg_s(15 downto 0) <= alureg_s(14 downto 7)               & (alureg_s(6 downto 0) & alureg_s(7)); 
                                                   alucout_s<= alureg_s(7);
                        when ALU_ROR | ALU_ROR1 | ALU_RORIMM => alureg_s(15 downto 0) <= alureg_s(0) & alureg_s(15 downto 9) & (alureg_s(0) & alureg_s(7 downto 1));     
                                                   alucout_s<= alureg_s(0);
                        when ALU_RCL | ALU_RCL1 | ALU_RCLIMM => alureg_s(15 downto 0) <= alureg_s(14 downto 7)               & (alureg_s(6 downto 0) & alucout_s); -- shift carry in 
                                                   alucout_s<= alureg_s(7);
                        when ALU_RCR | ALU_RCR1 | ALU_RCRIMM => alureg_s(15 downto 0) <= alucout_s & alureg_s(15 downto 9) & (alucout_s & alureg_s(7 downto 1));   
                                                   alucout_s<= alureg_s(0);
                        when ALU_SHL | ALU_SHL1 | ALU_SHLIMM => alureg_s(15 downto 0) <= alureg_s(14 downto 7)               & (alureg_s(6 downto 0) & '0'); 
                                                   alucout_s<= alureg_s(7);
                        when ALU_SHR | ALU_SHR1 | ALU_SHRIMM => alureg_s(15 downto 0) <= '0' & alureg_s(15 downto 9)         & ('0' & alureg_s(7 downto 1)); 
                                                   alucout_s<= alureg_s(0);
                        when ALU_SAR | ALU_SAR1 | ALU_SARIMM => alureg_s(15 downto 0) <= alureg_s(15) & alureg_s(15 downto 9)& (alureg_s(7) & alureg_s(7 downto 1)); 
                                                   alucout_s<= alureg_s(0);
                        when ALU_TEMP           => alureg_s(15 downto 0) <= bbus_s;
                                                   alucout_s<= '-';         
                        when ALU_AAM            => alureg_s(15 downto 0) <= bbus_s;        -- X"000A";    -- Version 1.6 fixed to variable base
                                                   alucout_s<= alureg_s(7);                -- Don't care!
                        when others             => alureg_s(15 downto 0) <= bbus_s ;                
                                                   alucout_s<= CFLAG;
                    end case; 
                end if;
            end if; 
        end if; 
end process;  

----------------------------------------------------------------------------
-- AAS Instruction  3F
----------------------------------------------------------------------------
-- if (((AL & 0x0F) > 9) || (AF == 1))     
--      AL = AL - 6;
--      AH = AH - 1;
--      CF=1, AF=1;
-- else CF=0, AF=0;
-- AL=AL&0x0F
----------------------------------------------------------------------------
process (alu_inbusa,psrreg_s,aas1bus_s)
    begin
        aas1bus_s<=alu_inbusa-X"0106";
        if ((alu_inbusa(3 downto 0) > "1001") or (psrreg_s(4)='1')) then    
            aasbus_s <= aas1bus_s(15 downto 8)&X"0"&aas1bus_s(3 downto 0);
            setaas_s <= '1';                            -- Set CF and AF flag
        else
            aasbus_s(7 downto 0) <= X"0"&(alu_inbusa(3 downto 0));  -- AL=AL&0Fh
            aasbus_s(15 downto 8)<= alu_inbusa(15 downto 8); -- leave AH unchanged
            setaas_s <= '0';                            -- Clear CF and AF flag
        end if;
end process;

----------------------------------------------------------------------------
-- AAA Instruction  37
----------------------------------------------------------------------------
-- if (((AL & 0x0F) > 9) || (AF == 1))
--      AL = AL + 6 
--      AH = AH + 1;
--      CF=1, AF=1;
-- else CF=0, AF=0;
-- AL=AL&0x0F
----------------------------------------------------------------------------
process (alu_inbusa,psrreg_s,aaa1bus_s)
    begin
        aaa1bus_s<=alu_inbusa+X"0106";
        if ((alu_inbusa(3 downto 0) > "1001") or (psrreg_s(4)='1')) then   
            aaabus_s <= aaa1bus_s(15 downto 8)&X"0"&aaa1bus_s(3 downto 0);
            setaaa_s <= '1';                            -- Set CF and AF flag
        else
            aaabus_s(7 downto 0) <= X"0"&alu_inbusa(3 downto 0); -- AL=AL&0Fh
            aaabus_s(15 downto 8)<= alu_inbusa(15 downto 8);    -- AH Unchanged
            setaaa_s <= '0';                            -- Clear CF and AF flag
        end if;
end process;

----------------------------------------------------------------------------
-- DAA Instruction  27
----------------------------------------------------------------------------
-- if (((AL & 0x0F) > 9) || (AF == 1))
--      AL = AL + 6;
--      AF = 1;
-- else AF = 0;
-- if ((AL > 0x9F) || (CF == 1))
--      AL = AL + 0x60;
--      CF = 1;
-- else CF = 0;
----------------------------------------------------------------------------
process (alu_inbusa,psrreg_s,setdaa_s)
    begin
        if ((alu_inbusa(3 downto 0) > X"9") or (psrreg_s(4)='1')) then  
            setdaa_s(0) <= '1';                         -- set AF
        else
            setdaa_s(0) <= '0';                         -- clr AF
        end if;
        if ((alu_inbusa(7 downto 0) > X"9F") or (psrreg_s(0)='1') or (alu_inbusa(7 downto 0) > X"99")) then 
            setdaa_s(1) <= '1';                         -- set CF
        else
            setdaa_s(1) <= '0';                         -- clr CF
        end if;
        case setdaa_s is
            when "00"   => daabus_s <= alu_inbusa(7 downto 0);
            when "01"   => daabus_s <= alu_inbusa(7 downto 0) + X"06";
            when "10"   => daabus_s <= alu_inbusa(7 downto 0) + X"60";
            when others => daabus_s <= alu_inbusa(7 downto 0) + X"66";
        end case;
end process;

----------------------------------------------------------------------------
-- DAS Instruction  2F
----------------------------------------------------------------------------
-- if (((AL & 0x0F) > 9) || (AF == 1))
--      AL = AL - 6;
--      AF = 1;
-- else AF = 0;
-- if ((AL > 0x9F) || (CF == 1))
--      AL = AL - 0x60;
--      CF = 1;
-- else CF = 0;
----------------------------------------------------------------------------
process (alu_inbusa,psrreg_s)
    begin
        if ((alu_inbusa(3 downto 0) > X"9") or (psrreg_s(4)='1')) then  
            setdas_s(0) <= '1';                         -- set AF
            dasbus_s <= alu_inbusa(7 downto 0) - X"06";
            if (alu_inbusa(7 downto 0) < X"06") then
                setdas_s(1) <= '1';                     -- set CF
            else 
                setdas_s(1) <= '0';                     -- clr CF
            end if;            
        else
            dasbus_s <= alu_inbusa(7 downto 0);
            setdas_s(0) <= '0';                         -- clr AF
            setdas_s(1) <= '0';                         -- clr CF
        end if;
        if ((alu_inbusa(7 downto 0) > X"99") OR (psrreg_s(0)='1')) then
            if ((alu_inbusa(3 downto 0) > X"9") or (psrreg_s(4)='1')) then
                dasbus_s <= alu_inbusa(7 downto 0) - X"66";
            else
                dasbus_s <= alu_inbusa(7 downto 0) - X"60";
            end if;
            setdas_s(1) <= '1';                         -- set CF
        end if;
end process;

-- process (alu_inbusa,psrreg_s,setdas_s)
    -- begin
        -- if ((alu_inbusa(3 downto 0) > X"9") or (psrreg_s(4)='1')) then  
            -- setdas_s(0) <= '1';                         -- set AF
        -- else
            -- setdas_s(0) <= '0';                         -- clr AF
        -- end if;
       -- -- if ((alu_inbusa(7 downto 0) > X"9F") OR (psrreg_s(0)='1')) then
        -- if ((alu_inbusa(7 downto 0) > X"99") or (psrreg_s(0)='1')) then
            -- setdas_s(1) <= '1';                         -- set CF
        -- else
            -- setdas_s(1) <= '0';                         -- clr CF
        -- end if;
        -- case setdas_s is
            -- when "00"   => dasbus_s <= alu_inbusa(7 downto 0);
            -- when "01"   => dasbus_s <= alu_inbusa(7 downto 0) - X"06";
            -- when "10"   => dasbus_s <= alu_inbusa(7 downto 0) - X"60";
            -- when others => dasbus_s <= alu_inbusa(7 downto 0) - X"66";
        -- end case;
-- end process;

----------------------------------------------------------------------------
-- AAD Instruction  5D 0A
----------------------------------------------------------------------------
-- AL = (AH * 10) + AL;
-- AH = 0;
-- SF=1 if result is 0 or positive
-- SF=0 if result is negative
-- ZF=1 if result equal to 0
-- ZF=0 if result not equal to 0
-- PF=1 if low byte of result has even number of set bits
-- PF=0 otherwise
-- AF=?
----------------------------------------------------------------------------
process (alu_inbusa,alu_inbusb,aad1bus_s,aad2bus_s)
    begin
        aad1bus_s <= alu_inbusa(15 downto 8) * alu_inbusb(7 downto 0); -- AH*imm8
        aad2bus_s <= aad1bus_s(7 downto 0) + alu_inbusa(7 downto 0);       -- + AL
        aadbus_s<= X"00" & aad2bus_s;
end process;


----------------------------------------------------------------------------
-- AAM Instruction  D4 0A
----------------------------------------------------------------------------
-- AH = AL / 10;
-- AL = AL % 10;
-- SF=1 if result is 0 or positive
-- SF=0 if result is negative
-- ZF=1 if result equal to 0
-- ZF=0 if result not equal to 0
-- PF=1 if low byte of result has even number of set bits
-- PF=0 otherwise
----------------------------------------------------------------------------
-- Implemented directly, see divider output

----------------------------------------------------------------------------
-- ALU Operation
----------------------------------------------------------------------------
process (aluopr,abus_s,bbus_s,outbus_s,psrreg_s,alureg_s,aasbus_s,aaabus_s,daabus_s,sign16a_s,
         sign16b_s,sign32a_s,dasbus_s,product_s,divresult_s,alu_temp_s,aadbus_s,quotient_s,remainder_s) 
    begin
        case aluopr is
    
            when ALU_ADD | ALU_ADD_SE | ALU_INC | ALU_POP | ALU_SUB | ALU_SUB_SE | ALU_DEC | ALU_PUSH | ALU_CMP | ALU_CMP_SE | 
                 ALU_CMPS | ALU_ADC | ALU_ADC_SE | ALU_SBB | ALU_SBB_SE | ALU_SCAS | ALU_NEG | ALU_NOT
                            => alubus_s <= outbus_s;                
    
            when ALU_OR | ALU_OR_SE     
                            => alubus_s <= abus_s OR bbus_s;
            when ALU_AND | ALU_AND_SE | ALU_TEST0 | ALU_TEST1 | ALU_TEST2   
                            => alubus_s <= abus_s AND bbus_s;
            when ALU_XOR | ALU_XOR_SE   
                            => alubus_s <= abus_s XOR bbus_s;

            when ALU_LAHF   => alubus_s <= psrreg_s(15 downto 2)&RESET_CC_C(1)&psrreg_s(0);-- flags onto ALUBUS, note reserved bit1=1
                        
            when ALU_MUL | ALU_IMUL 
                            => alubus_s <= product_s(15 downto 0);  -- AX of Multiplier
            when ALU_MUL2| ALU_IMUL2
                            => alubus_s <= product_s(31 downto 16); -- DX of Multiplier
                        
            when ALU_DIV | ALU_IDIV                                 
                            => alubus_s <= divresult_s(15 downto 0);-- AX of Divider (quotient)   
            when ALU_DIV2| ALU_IDIV2
                            => alubus_s <= divresult_s(31 downto 16);-- DX of Divider (remainder) 
                                                                                
            when ALU_SEXT   => alubus_s <= sign16a_s;               -- Used for CBW Instruction
            when ALU_SEXTB  => alubus_s <= sign16b_s;               -- Used for PUSH IMM8 instruction
            when ALU_SEXTW  => alubus_s <= sign32a_s;               -- Used for CWD Instruction

            when ALU_AAS    => alubus_s <= aasbus_s;                -- Used for AAS Instruction
            when ALU_AAA    => alubus_s <= aaabus_s;                -- Used for AAA Instruction
            when ALU_DAA    => alubus_s <= abus_s(15 downto 8) & daabus_s;-- Used for DAA Instruction
            when ALU_DAS    => alubus_s <= abus_s(15 downto 8) & dasbus_s;-- Used for DAS Instruction
            when ALU_AAD    => alubus_s <= aadbus_s;                -- Used for AAD Instruction
            when ALU_AAM    => alubus_s <= quotient_s(7 downto 0) & remainder_s(7 downto 0); -- Used for AAM Instruction

            when ALU_ROL | ALU_ROL1 | ALU_ROR | ALU_ROR1 | ALU_RCL | ALU_RCL1 | ALU_RCR | ALU_RCR1 |    
                 ALU_SHL | ALU_SHL1 | ALU_SHR | ALU_SHR1 | ALU_SAR | ALU_SAR1 | ALU_REGL | ALU_ROLIMM |
                 ALU_RORIMM | ALU_RCLIMM | ALU_RCRIMM | ALU_SHLIMM | ALU_SHRIMM | ALU_SARIMM 
                            => alubus_s <= alureg_s(15 downto 0);   -- alu_inbusb to output

            when ALU_REGH   => alubus_s <= alureg_s(31 downto 16);  -- alu_inbusa to output

            when ALU_PASSA  => alubus_s <= abus_s;
            when ALU_PASSB  => alubus_s <= bbus_s;

            when ALU_TEMP   => alubus_s <= alu_temp_s;              -- Used to store SP during PUSHA e.g.

            when others     => alubus_s <= DONTCARE(15 downto 0);           
        end case;
end process;
alubus <= alubus_s;                 -- Connect to entity

                  
----------------------------------------------------------------------------
-- Processor Status Register  (Flags)
-- bit   Flag
-- 15    Reserved
-- 14    Reserved
-- 13    Reserved               Set to 1?
-- 12    Reserved               Set to 1?
-- 11    Overflow Flag OF
-- 10    Direction Flag DF
-- 9     Interrupt Flag IF
-- 8     Trace Flag TF
-- 7     Sign Flag SF
-- 6     Zero Flag ZF
-- 5     Reserved
-- 4     Auxiliary Carry AF
-- 3     Reserved
-- 2     Parity Flag PF
-- 1     Reserved               Set to 1 ????
-- 0     Carry Flag
----------------------------------------------------------------------------
process (clk, reset)
    begin 
        if reset='1' then
            psrreg_s <= RESET_CC_C;                                     -- see cpu86pack.vhd
        elsif rising_edge(clk) then
            if (wrcc='1') then  
                psrreg_s(1)<=RESET_CC_C(1);                             -- default values
                psrreg_s(3)<=RESET_CC_C(3);
                psrreg_s(5)<=RESET_CC_C(5);
                psrreg_s(15 downto 12)<=RESET_CC_C(15 downto 12);
                case aluopr is
                    when ALU_ADD | ALU_ADD_SE | ALU_ADC | ALU_ADC_SE | ALU_INC  =>
                            OFLAG <= overflow_s; 
                            SFLAG <= signflag_s; 
                            ZFLAG <= zeroflag_s;
                            AFLAG <= bit4_s;
                            PFLAG <= parityflag_s;
                            CFLAG <= cout_s;    
                    when ALU_DEC =>                                     -- Same as for ALU_SUB exclusing the CFLAG :-(
                            OFLAG <= overflow_s; 
                            SFLAG <= signflag_s; 
                            ZFLAG <= zeroflag_s;
                            AFLAG <= not bit4_s;
                            PFLAG <= parityflag_s;
                    when ALU_SUB | ALU_SUB_SE | ALU_SBB | ALU_SBB_SE | ALU_CMP | 
                         ALU_CMP_SE | ALU_CMPS | ALU_SCAS | ALU_NEG =>
                            OFLAG <= overflow_s; 
                            SFLAG <= signflag_s; 
                            ZFLAG <= zeroflag_s;
                            AFLAG <= not bit4_s;
                            PFLAG <= parityflag_s;
                            CFLAG <= not cout_s;                                                                

                    when ALU_OR | ALU_OR_SE |  ALU_AND | ALU_AND_SE | ALU_XOR | ALU_XOR_SE | 
                         ALU_TEST0 | ALU_TEST1 | ALU_TEST2 => 
                            OFLAG <= '0'; 
                            SFLAG <= signflag_s;        
                            ZFLAG <= zeroflag_s;
                            AFLAG <= '0';                               -- None defined, set to 0 to be compatible with debug
                            PFLAG <= parityflag_s;
                            CFLAG <= '0';                            
                            
                    when ALU_SHL  | ALU_SHR  | ALU_SAR | ALU_SHR1 | ALU_SAR1 | ALU_SHL1 | 
                         ALU_SHLIMM | ALU_SHRIMM | ALU_SARIMM =>                          
                            OFLAG <= overflow_s;
                            PFLAG <= parityflag_s;
                            SFLAG <= signflag_s;
                            ZFLAG <= zeroflag_s;
                            CFLAG <= alucout_s;                                                                                       
                            AFLAG <= '0';                                -- *** Undefined, added to match ISS **** Version 1.6                               
                            
                    when ALU_CLC =>
                            CFLAG <= '0';
                    when ALU_CMC =>
                            CFLAG <= not CFLAG;
                    when ALU_STC =>
                            CFLAG <= '1';
                    when ALU_CLD =>
                            DFLAG <= '0';
                    when ALU_STD =>
                            DFLAG <= '1';
                    when ALU_CLI =>
                            IFLAG <= '0';
                    when ALU_STI =>
                            IFLAG <= '1';
                    when ALU_POP =>                                     -- Note only POPF executes a WRCC command, thus save for other pops
                            psrreg_s(11 downto 6) <= alu_inbusa(11 downto 6);-- Write memory operand (MDBUS) to CC register
                            psrreg_s(4) <= alu_inbusa(4);
                            psrreg_s(2) <= alu_inbusa(2);
                            psrreg_s(0) <= alu_inbusa(0);
                    when ALU_SAHF =>                                    -- Write all AH bits (not compatible!)
                              psrreg_s(7 downto 6) <= alu_inbusa(7 downto 6);-- SAHF only writes bits 7,6,4,2,0
                            psrreg_s(4) <= alu_inbusa(4);
                            psrreg_s(2) <= alu_inbusa(2);
                            psrreg_s(0) <= alu_inbusa(0);
                    
                    when ALU_AAA  =>
                            AFLAG <= setaaa_s;                          -- set or clear CF/AF flag
                            CFLAG <= setaaa_s;
                            
                    when ALU_AAS  =>
                            AFLAG <= setaas_s;                          -- set or clear CF/AF flag
                            CFLAG <= setaas_s;

                    when ALU_DAA  =>
                            AFLAG <= setdaa_s(0);                       -- set or clear CF/AF flag
                            CFLAG <= setdaa_s(1);
                            PFLAG <= parityflag_s;
                            SFLAG <= signflag_s; 
                            ZFLAG <= zeroflag_s;
                            
                            OFLAG <= '0';                               -- *** Undefined, added to match ISS **** Version 1.6
                            
                    when ALU_AAD  =>
                            SFLAG <= alubus_s(7);                       --signflag_s; 
                            PFLAG <= parityflag_s;
                            ZFLAG <= zeroflag_s;
                            
                    when ALU_AAM  =>
                            SFLAG <= alubus_s(7); --signflag_s;
                            PFLAG <= parityflag_s;
                            ZFLAG <= not(zflaglow_s);                   -- signflag on AL only
                            
                    when ALU_DAS  =>
                            AFLAG <= setdas_s(0);                       -- set or clear CF/AF flag
                            CFLAG <= setdas_s(1);
                            PFLAG <= parityflag_s;
                            SFLAG <= signflag_s; 
                            ZFLAG <= zeroflag_s;
                            
                            OFLAG <= '0';                               -- *** Undefined, added to match ISS **** Version 1.6
                            
                                                                        -- Shift Rotate Instructions
                    when ALU_ROL  | ALU_ROR  | ALU_RCL    | ALU_RCR    | ALU_ROL1   | ALU_RCL1 | 
                         ALU_ROR1 | ALU_RCR1 | ALU_ROLIMM | ALU_RORIMM | ALU_RCLIMM | ALU_RCRIMM  => -- Version 1.6 added ROL/ROR/RCL/RCR Imm
                            CFLAG <= alucout_s;
                            OFLAG <= overflow_s; 

                    when ALU_MUL | ALU_MUL2 | ALU_IMUL | ALU_IMUL2 =>      -- Multiply affects CF&OF only
                            CFLAG <= overflow_s;
                            OFLAG <= overflow_s; 
                            
                    when ALU_CLRTIF  =>                                 -- Clear TF and IF flag
                            IFLAG <= '0';              
                            TFLAG <= '0'; 

                    when others =>
                            psrreg_s <= psrreg_s;
                end case;
                
            end if; 
        end if; 
end process;  

ccbus <= psrreg_s;                                                      -- Connect to entity


process (clk, reset)
    begin 
        if reset='1' then
            divresult_s <= (others => '0');
        elsif rising_edge(clk) then        
            if done_s='1' then                                          -- Latched pulse generated by serial divider  
                if wl_s='1' then                                         -- Latched version required?
                    divresult_s <= remainder_s & quotient_s; 
                else 
                    divresult_s <= remainder_s & remainder_s(7 downto 0) & quotient_s(7 downto 0);
                end if;
            end if;
        end if; 
end process;  

end rtl;
