mirror of
https://github.com/Gehstock/Mist_FPGA.git
synced 2026-03-02 09:37:05 +00:00
849 lines
44 KiB
VHDL
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;
|