-- **** -- T80(b) core. In an effort to merge and maintain bug fixes .... -- -- -- Ver 301 parity flag is just parity for 8080, also overflow for Z80, by Sean Riddle -- Ver 300 started tidyup -- MikeJ March 2005 -- Latest version from www.fpgaarcade.com (original www.opencores.org) -- -- **** -- -- Z80 compatible microprocessor core -- -- Version : 0247 -- -- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org) -- -- All rights reserved -- -- Redistribution and use in source and synthezised forms, with or without -- modification, are permitted provided that the following conditions are met: -- -- Redistributions of source code must retain the above copyright notice, -- this list of conditions and the following disclaimer. -- -- Redistributions in synthesized form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- Neither the name of the author nor the names of other contributors may -- be used to endorse or promote products derived from this software without -- specific prior written permission. -- -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE -- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -- POSSIBILITY OF SUCH DAMAGE. -- -- Please report bugs to the author, but before you do so, please -- make sure that this is not a derivative work and that -- you have the latest version of this file. -- -- The latest version of this file can be found at: -- http://www.opencores.org/cvsweb.shtml/t80/ -- -- Limitations : -- -- File history : -- -- 0214 : Fixed mostly flags, only the block instructions now fail the zex regression test -- -- 0238 : Fixed zero flag for 16 bit SBC and ADC -- -- 0240 : Added GB operations -- -- 0242 : Cleanup -- -- 0247 : Cleanup -- library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; entity T80_ALU is generic( Mode : integer := 0; Flag_C : integer := 0; Flag_N : integer := 1; Flag_P : integer := 2; Flag_X : integer := 3; Flag_H : integer := 4; Flag_Y : integer := 5; Flag_Z : integer := 6; Flag_S : integer := 7 ); port( Arith16 : in std_logic; Z16 : in std_logic; ALU_Op : in std_logic_vector(3 downto 0); IR : in std_logic_vector(5 downto 0); ISet : in std_logic_vector(1 downto 0); BusA : in std_logic_vector(7 downto 0); BusB : in std_logic_vector(7 downto 0); F_In : in std_logic_vector(7 downto 0); Q : out std_logic_vector(7 downto 0); F_Out : out std_logic_vector(7 downto 0) ); end T80_ALU; architecture rtl of T80_ALU is procedure AddSub(A : std_logic_vector; B : std_logic_vector; Sub : std_logic; Carry_In : std_logic; signal Res : out std_logic_vector; signal Carry : out std_logic) is variable B_i : unsigned(A'length - 1 downto 0); variable Res_i : unsigned(A'length + 1 downto 0); begin if Sub = '1' then B_i := not unsigned(B); else B_i := unsigned(B); end if; Res_i := unsigned("0" & A & Carry_In) + unsigned("0" & B_i & "1"); Carry <= Res_i(A'length + 1); Res <= std_logic_vector(Res_i(A'length downto 1)); end; -- AddSub variables (temporary signals) signal UseCarry : std_logic; signal Carry7_v : std_logic; signal Overflow_v : std_logic; signal HalfCarry_v : std_logic; signal Carry_v : std_logic; signal Q_v : std_logic_vector(7 downto 0); signal BitMask : std_logic_vector(7 downto 0); begin with IR(5 downto 3) select BitMask <= "00000001" when "000", "00000010" when "001", "00000100" when "010", "00001000" when "011", "00010000" when "100", "00100000" when "101", "01000000" when "110", "10000000" when others; UseCarry <= not ALU_Op(2) and ALU_Op(0); AddSub(BusA(3 downto 0), BusB(3 downto 0), ALU_Op(1), ALU_Op(1) xor (UseCarry and F_In(Flag_C)), Q_v(3 downto 0), HalfCarry_v); AddSub(BusA(6 downto 4), BusB(6 downto 4), ALU_Op(1), HalfCarry_v, Q_v(6 downto 4), Carry7_v); AddSub(BusA(7 downto 7), BusB(7 downto 7), ALU_Op(1), Carry7_v, Q_v(7 downto 7), Carry_v); -- bug fix - parity flag is just parity for 8080, also overflow for Z80 process (Carry_v, Carry7_v, Q_v) begin if(Mode=2) then OverFlow_v <= not (Q_v(0) xor Q_v(1) xor Q_v(2) xor Q_v(3) xor Q_v(4) xor Q_v(5) xor Q_v(6) xor Q_v(7)); else OverFlow_v <= Carry_v xor Carry7_v; end if; end process; process (Arith16, ALU_OP, F_In, BusA, BusB, IR, Q_v, Carry_v, HalfCarry_v, OverFlow_v, BitMask, ISet, Z16) variable Q_t : std_logic_vector(7 downto 0); variable DAA_Q : unsigned(8 downto 0); begin Q_t := "--------"; F_Out <= F_In; DAA_Q := "---------"; case ALU_Op is when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" | "0110" | "0111" => F_Out(Flag_N) <= '0'; F_Out(Flag_C) <= '0'; case ALU_OP(2 downto 0) is when "000" | "001" => -- ADD, ADC Q_t := Q_v; F_Out(Flag_C) <= Carry_v; F_Out(Flag_H) <= HalfCarry_v; F_Out(Flag_P) <= OverFlow_v; when "010" | "011" | "111" => -- SUB, SBC, CP Q_t := Q_v; F_Out(Flag_N) <= '1'; F_Out(Flag_C) <= not Carry_v; F_Out(Flag_H) <= not HalfCarry_v; F_Out(Flag_P) <= OverFlow_v; when "100" => -- AND Q_t(7 downto 0) := BusA and BusB; F_Out(Flag_H) <= '1'; when "101" => -- XOR Q_t(7 downto 0) := BusA xor BusB; F_Out(Flag_H) <= '0'; when others => -- OR "110" Q_t(7 downto 0) := BusA or BusB; F_Out(Flag_H) <= '0'; end case; if ALU_Op(2 downto 0) = "111" then -- CP F_Out(Flag_X) <= BusB(3); F_Out(Flag_Y) <= BusB(5); else F_Out(Flag_X) <= Q_t(3); F_Out(Flag_Y) <= Q_t(5); end if; if Q_t(7 downto 0) = "00000000" then F_Out(Flag_Z) <= '1'; if Z16 = '1' then F_Out(Flag_Z) <= F_In(Flag_Z); -- 16 bit ADC,SBC end if; else F_Out(Flag_Z) <= '0'; end if; F_Out(Flag_S) <= Q_t(7); case ALU_Op(2 downto 0) is when "000" | "001" | "010" | "011" | "111" => -- ADD, ADC, SUB, SBC, CP when others => F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7)); end case; if Arith16 = '1' then F_Out(Flag_S) <= F_In(Flag_S); F_Out(Flag_Z) <= F_In(Flag_Z); F_Out(Flag_P) <= F_In(Flag_P); end if; when "1100" => -- DAA F_Out(Flag_H) <= F_In(Flag_H); F_Out(Flag_C) <= F_In(Flag_C); DAA_Q(7 downto 0) := unsigned(BusA); DAA_Q(8) := '0'; if F_In(Flag_N) = '0' then -- After addition -- Alow > 9 or H = 1 if DAA_Q(3 downto 0) > 9 or F_In(Flag_H) = '1' then if (DAA_Q(3 downto 0) > 9) then F_Out(Flag_H) <= '1'; else F_Out(Flag_H) <= '0'; end if; DAA_Q := DAA_Q + 6; end if; -- new Ahigh > 9 or C = 1 if DAA_Q(8 downto 4) > 9 or F_In(Flag_C) = '1' then DAA_Q := DAA_Q + 96; -- 0x60 end if; else -- After subtraction if DAA_Q(3 downto 0) > 9 or F_In(Flag_H) = '1' then if DAA_Q(3 downto 0) > 5 then F_Out(Flag_H) <= '0'; end if; DAA_Q(7 downto 0) := DAA_Q(7 downto 0) - 6; end if; if unsigned(BusA) > 153 or F_In(Flag_C) = '1' then DAA_Q := DAA_Q - 352; -- 0x160 end if; end if; F_Out(Flag_X) <= DAA_Q(3); F_Out(Flag_Y) <= DAA_Q(5); F_Out(Flag_C) <= F_In(Flag_C) or DAA_Q(8); Q_t := std_logic_vector(DAA_Q(7 downto 0)); if DAA_Q(7 downto 0) = "00000000" then F_Out(Flag_Z) <= '1'; else F_Out(Flag_Z) <= '0'; end if; F_Out(Flag_S) <= DAA_Q(7); F_Out(Flag_P) <= not (DAA_Q(0) xor DAA_Q(1) xor DAA_Q(2) xor DAA_Q(3) xor DAA_Q(4) xor DAA_Q(5) xor DAA_Q(6) xor DAA_Q(7)); when "1101" | "1110" => -- RLD, RRD Q_t(7 downto 4) := BusA(7 downto 4); if ALU_Op(0) = '1' then Q_t(3 downto 0) := BusB(7 downto 4); else Q_t(3 downto 0) := BusB(3 downto 0); end if; F_Out(Flag_H) <= '0'; F_Out(Flag_N) <= '0'; F_Out(Flag_X) <= Q_t(3); F_Out(Flag_Y) <= Q_t(5); if Q_t(7 downto 0) = "00000000" then F_Out(Flag_Z) <= '1'; else F_Out(Flag_Z) <= '0'; end if; F_Out(Flag_S) <= Q_t(7); F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7)); when "1001" => -- BIT Q_t(7 downto 0) := BusB and BitMask; F_Out(Flag_S) <= Q_t(7); if Q_t(7 downto 0) = "00000000" then F_Out(Flag_Z) <= '1'; F_Out(Flag_P) <= '1'; else F_Out(Flag_Z) <= '0'; F_Out(Flag_P) <= '0'; end if; F_Out(Flag_H) <= '1'; F_Out(Flag_N) <= '0'; F_Out(Flag_X) <= '0'; F_Out(Flag_Y) <= '0'; if IR(2 downto 0) /= "110" then F_Out(Flag_X) <= BusB(3); F_Out(Flag_Y) <= BusB(5); end if; when "1010" => -- SET Q_t(7 downto 0) := BusB or BitMask; when "1011" => -- RES Q_t(7 downto 0) := BusB and not BitMask; when "1000" => -- ROT case IR(5 downto 3) is when "000" => -- RLC Q_t(7 downto 1) := BusA(6 downto 0); Q_t(0) := BusA(7); F_Out(Flag_C) <= BusA(7); when "010" => -- RL Q_t(7 downto 1) := BusA(6 downto 0); Q_t(0) := F_In(Flag_C); F_Out(Flag_C) <= BusA(7); when "001" => -- RRC Q_t(6 downto 0) := BusA(7 downto 1); Q_t(7) := BusA(0); F_Out(Flag_C) <= BusA(0); when "011" => -- RR Q_t(6 downto 0) := BusA(7 downto 1); Q_t(7) := F_In(Flag_C); F_Out(Flag_C) <= BusA(0); when "100" => -- SLA Q_t(7 downto 1) := BusA(6 downto 0); Q_t(0) := '0'; F_Out(Flag_C) <= BusA(7); when "110" => -- SLL (Undocumented) / SWAP if Mode = 3 then Q_t(7 downto 4) := BusA(3 downto 0); Q_t(3 downto 0) := BusA(7 downto 4); F_Out(Flag_C) <= '0'; else Q_t(7 downto 1) := BusA(6 downto 0); Q_t(0) := '1'; F_Out(Flag_C) <= BusA(7); end if; when "101" => -- SRA Q_t(6 downto 0) := BusA(7 downto 1); Q_t(7) := BusA(7); F_Out(Flag_C) <= BusA(0); when others => -- SRL Q_t(6 downto 0) := BusA(7 downto 1); Q_t(7) := '0'; F_Out(Flag_C) <= BusA(0); end case; F_Out(Flag_H) <= '0'; F_Out(Flag_N) <= '0'; F_Out(Flag_X) <= Q_t(3); F_Out(Flag_Y) <= Q_t(5); F_Out(Flag_S) <= Q_t(7); if Q_t(7 downto 0) = "00000000" then F_Out(Flag_Z) <= '1'; else F_Out(Flag_Z) <= '0'; end if; F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7)); if ISet = "00" then F_Out(Flag_P) <= F_In(Flag_P); F_Out(Flag_S) <= F_In(Flag_S); F_Out(Flag_Z) <= F_In(Flag_Z); end if; when others => null; end case; Q <= Q_t; end process; end;