1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-03-02 09:37:05 +00:00
Files
Gehstock.Mist_FPGA/common/CPU/cpu86/alu_rtl.vhd
2019-07-22 23:42:05 +02:00

849 lines
44 KiB
VHDL

-------------------------------------------------------------------------------
-- CPU86 - VHDL CPU8088 IP core --
-- Copyright (C) 2002-2008 HT-LAB --
-- --
-- Contact/bugs : http://www.ht-lab.com/misc/feedback.html --
-- Web : http://www.ht-lab.com --
-- --
-- CPU86 is released as open-source under the GNU GPL license. This means --
-- that designs based on CPU86 must be distributed in full source code --
-- under the same license. Contact HT-Lab for commercial applications where --
-- source-code distribution is not desirable. --
-- --
-------------------------------------------------------------------------------
-- --
-- This library is free software; you can redistribute it and/or --
-- modify it under the terms of the GNU Lesser General Public --
-- License as published by the Free Software Foundation; either --
-- version 2.1 of the License, or (at your option) any later version. --
-- --
-- This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of --
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU --
-- Lesser General Public License for more details. --
-- --
-- Full details of the license can be found in the file "copying.txt". --
-- --
-- You should have received a copy of the GNU Lesser General Public --
-- License along with this library; if not, write to the Free Software --
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --
-- --
-------------------------------------------------------------------------------
-- Ver 0.82 Fixed RCR X,CL --
-------------------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_unsigned.ALL;
USE ieee.std_logic_arith.ALL;
USE work.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;
cx_s : IN std_logic_vector (15 DOWNTO 0);
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;
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, not used in this architecture
reset : in std_logic; -- Active high, not used in this architecture
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 in this architecture
done : out std_logic); -- not used in this architecture
end component divider;
component multiplier is -- Generic Multiplier
generic (WIDTH : integer := 16);
port (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(10 downto 0);
signal aad2bus_s : std_logic_vector(10 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 c1flag_s : std_logic; -- Asserted when CX=1(w=1) or CL=1(w=0)
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
--divisor => bbus_s, -- byte/word
quotient => quotient_s, -- 16 bits
remainder => remainder_s, -- 16 bits
twocomp => twocomp_s,
w => wl_s, -- Byte/Word
overflow => div_err_s, -- 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 (multiplicant=> alureg_s(31 downto 16),
multiplier => alureg_s(15 downto 0),
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 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=>
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, c1flag_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_RCL1 | ALU_SHL1 => -- count=1 using constants as in rcl bx,1
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_ROL | ALU_RCL | ALU_SHL => -- cl/cx=1
if (( c1flag_s='1' and w='1' and (alureg_s(15)/=alucout_s)) or
( c1flag_s='1' and w='0' and (alureg_s(7) /=alucout_s))) then
overflow_s <= '1';
else
overflow_s <= '0';
end if;
when ALU_ROR1 | ALU_RCR1 | ALU_SHR1 | ALU_SAR1 =>
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;
when ALU_ROR | ALU_RCR | ALU_SHR | ALU_SAR => -- if cl/cx=1
if ((c1flag_s='1' and w='1' and (alureg_s(15)/=alureg_s(14))) or
(c1flag_s='1' and w='0' and (alureg_s(7) /=alureg_s(6)))) then
overflow_s <= '1';
else
overflow_s <= '0';
end if;
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);
----------------------------------------------------------------------------
-- c1flag asserted if CL or CX=1, used to update the OF flags during
-- rotate/shift instructions
----------------------------------------------------------------------------
c1flag_s <= '1' when (cx_s=X"0001" and w='1') OR (cx_s(7 downto 0)=X"01" and w='0') else '0';
----------------------------------------------------------------------------
-- Temp/ScratchPad Register
-- alureg_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 => alureg_s(15 downto 0) <= alureg_s(14 downto 0) & alureg_s(15);
alucout_s<= alureg_s(15);
when ALU_ROR | ALU_ROR1 => alureg_s(15 downto 0) <= alureg_s(0) & alureg_s(15 downto 1);
alucout_s<= alureg_s(0);
when ALU_RCL | ALU_RCL1 => 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 => alureg_s(15 downto 0) <= alucout_s & alureg_s(15 downto 1);
alucout_s<= alureg_s(0);
when ALU_SHL | ALU_SHL1 => alureg_s(15 downto 0) <= alureg_s(14 downto 0) & '0';
alucout_s<= alureg_s(15);
when ALU_SHR | ALU_SHR1 => alureg_s(15 downto 0) <= '0' & alureg_s(15 downto 1);
alucout_s<= alureg_s(0);
when ALU_SAR | ALU_SAR1 => 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;
alucout_s<= '-'; -- Don't care!
when ALU_AAM => alureg_s(15 downto 0) <= X"000A";
alucout_s<= '-'; -- Don't care!
when others => alureg_s(15 downto 0) <= bbus_s ;--alu_inbusb; -- ALU_PASSB
alucout_s<= CFLAG;
end case;
else
case aluopr is -- To aid resource sharing add MSB byte as above
when ALU_ROL | ALU_ROL1 => 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 => 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 => 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 => alureg_s(15 downto 0) <= alucout_s & alureg_s(15 downto 9) & (psrreg_s(0) & alureg_s(7 downto 1));
when ALU_RCR | ALU_RCR1 => alureg_s(15 downto 0) <= alucout_s & alureg_s(15 downto 9) & (alucout_s & alureg_s(7 downto 1)); -- Ver 0.82
alucout_s<= alureg_s(0);
when ALU_SHL | ALU_SHL1 => 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 => 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 => 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<= '-'; -- Don't care!
when ALU_AAM => alureg_s(15 downto 0) <= X"000A";
alucout_s<= '-'; -- Don't care!
when others => alureg_s(15 downto 0) <= bbus_s ;--alu_inbusb -- ALU_PASSB
alucout_s<= CFLAG;
end case;
end if;
end if;
end if;
end process;
----------------------------------------------------------------------------
-- AAS Instruction 3F
----------------------------------------------------------------------------
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
----------------------------------------------------------------------------
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
----------------------------------------------------------------------------
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
----------------------------------------------------------------------------
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') or (alu_inbusa(7 downto 0) > X"99")) 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
----------------------------------------------------------------------------
process (alu_inbusa,aad1bus_s,aad2bus_s)
begin
aad1bus_s <= ("00" & alu_inbusa(15 downto 8) & '0') + (alu_inbusa(15 downto 8) & "000"); -- AH*2 + AH*8
aad2bus_s <= aad1bus_s + ("000" & alu_inbusa(7 downto 0)); -- + AL
aadbus_s<= "00000000" & aad2bus_s(7 downto 0);
end process;
----------------------------------------------------------------------------
-- 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)&'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_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
=> 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;
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 <= "1111000000000010";
elsif rising_edge(clk) then
if (wrcc='1') then
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 =>
OFLAG <= overflow_s;
PFLAG <= parityflag_s;
SFLAG <= signflag_s;
ZFLAG <= zeroflag_s;
CFLAG <= alucout_s;
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 <= "1111" & alu_inbusa(11 downto 0);
when ALU_SAHF => -- Write all AH bits (not compatible!)
psrreg_s(7 downto 0) <= alu_inbusa(7 downto 6) & '0' & alu_inbusa(4) & '0' &
alu_inbusa(2) & '0' & alu_inbusa(0);-- SAHF only writes bits 7,6,4,2,0
when ALU_AAS =>
AFLAG <= setaas_s; -- set or clear CF/AF flag
CFLAG <= setaas_s;
SFLAG <= '0';
when ALU_AAA =>
AFLAG <= setaaa_s; -- set or clear CF/AF flag
CFLAG <= setaaa_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;
when ALU_AAD =>
SFLAG <= alubus_s(7); --signflag_s;
PFLAG <= parityflag_s;
ZFLAG <= zeroflag_s;
when ALU_AAM =>
SFLAG <= 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;
-- Shift Rotate Instructions
when ALU_ROL | ALU_ROR | ALU_RCL | ALU_RCR |
ALU_ROL1 | ALU_RCL1 | ALU_ROR1 | ALU_RCR1 =>
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
-- Latch Divide by 0 error flag & latched divresult.
-- Requires a MCP from all registers to these endpoint registers!
process (clk, reset)
begin
if reset='1' then
div_err <= '0';
divresult_s <= (others => '0');
elsif rising_edge(clk) then
if done_s='1' then -- Latched pulse generated by serial divider
div_err <= div_err_s; -- Divide Overflow
-- pragma synthesis_off
assert div_err_s='0' report "**** Divide Overflow ***" severity note;
-- pragma synthesis_on
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;
else
div_err <= '0';
end if;
end if;
end process;
end rtl;