--
-- 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 :
--
--	0208 : First complete release
--
--	0210 : Fixed wait and halt
--
--	0211 : Fixed Refresh addition and IM 1
--
--	0214 : Fixed mostly flags, only the block instructions now fail the zex regression test
--
--	0232 : Removed refresh address output for Mode > 1 and added DJNZ M1_n fix by Mike Johnson
--
--	0235 : Added clock enable and IM 2 fix by Mike Johnson
--
--	0237 : Changed 8080 I/O address output, added IntE output
--
--	0238 : Fixed (IX/IY+d) timing and 16 bit ADC and SBC zero flag
--
--	0240 : Added interrupt ack fix by Mike Johnson, changed (IX/IY+d) timing and changed flags in GB mode
--
--	0242 : Added I/O wait, fixed refresh address, moved some registers to RAM
--
--	0247 : Fixed bus req/ack cycle
--

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.T80_Pack.all;

entity T80 is
	generic(
		Mode : integer := 0;	-- 0 => Z80, 1 => Fast Z80, 2 => 8080, 3 => GB
		IOWait : integer := 0;	-- 1 => Single cycle I/O, 1 => Std I/O cycle
		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(
		RESET_n		: in std_logic;
		CLK_n		: in std_logic;
		CEN			: in std_logic;
		WAIT_n		: in std_logic;
		INT_n		: in std_logic;
		NMI_n		: in std_logic;
		BUSRQ_n		: in std_logic;
		M1_n		: out std_logic;
		IORQ		: out std_logic;
		NoRead		: out std_logic;
		Write		: out std_logic;
		RFSH_n		: out std_logic;
		HALT_n		: out std_logic;
		BUSAK_n		: out std_logic;
		A			: out std_logic_vector(15 downto 0);
		DInst		: in std_logic_vector(7 downto 0);
		DI			: in std_logic_vector(7 downto 0);
		DO			: out std_logic_vector(7 downto 0);
		MC			: out std_logic_vector(2 downto 0);
		TS			: out std_logic_vector(2 downto 0);
		IntCycle_n	: out std_logic;
		IntE		: out std_logic;
		Stop		: out std_logic
	);
end T80;

architecture rtl of T80 is

	constant aNone	: std_logic_vector(2 downto 0) := "111";
	constant aBC	: std_logic_vector(2 downto 0) := "000";
	constant aDE	: std_logic_vector(2 downto 0) := "001";
	constant aXY	: std_logic_vector(2 downto 0) := "010";
	constant aIOA	: std_logic_vector(2 downto 0) := "100";
	constant aSP	: std_logic_vector(2 downto 0) := "101";
	constant aZI	: std_logic_vector(2 downto 0) := "110";

	-- Registers
	signal ACC, F			: std_logic_vector(7 downto 0);
	signal Ap, Fp			: std_logic_vector(7 downto 0);
	signal I				: std_logic_vector(7 downto 0);
	signal R				: unsigned(7 downto 0);
	signal SP, PC			: unsigned(15 downto 0);
	signal RegDIH			: std_logic_vector(7 downto 0);
	signal RegDIL			: std_logic_vector(7 downto 0);
	signal RegBusA			: std_logic_vector(15 downto 0);
	signal RegBusB			: std_logic_vector(15 downto 0);
	signal RegBusC			: std_logic_vector(15 downto 0);
	signal RegAddrA_r		: std_logic_vector(2 downto 0);
	signal RegAddrA			: std_logic_vector(2 downto 0);
	signal RegAddrB_r		: std_logic_vector(2 downto 0);
	signal RegAddrB			: std_logic_vector(2 downto 0);
	signal RegAddrC			: std_logic_vector(2 downto 0);
	signal RegWEH			: std_logic;
	signal RegWEL			: std_logic;
	signal Alternate		: std_logic;

	-- Help Registers
	signal TmpAddr			: std_logic_vector(15 downto 0);	-- Temporary address register
	signal IR				: std_logic_vector(7 downto 0);		-- Instruction register
	signal ISet				: std_logic_vector(1 downto 0);		-- Instruction set selector
	signal RegBusA_r		: std_logic_vector(15 downto 0);

	signal ID16				: signed(15 downto 0);
	signal Save_Mux			: std_logic_vector(7 downto 0);

	signal TState			: unsigned(2 downto 0);
	signal MCycle			: std_logic_vector(2 downto 0);
	signal IntE_FF1			: std_logic;
	signal IntE_FF2			: std_logic;
	signal Halt_FF			: std_logic;
	signal BusReq_s			: std_logic;
	signal BusAck			: std_logic;
	signal ClkEn			: std_logic;
	signal NMI_s			: std_logic;
	signal INT_s			: std_logic;
	signal IStatus			: std_logic_vector(1 downto 0);

	signal DI_Reg			: std_logic_vector(7 downto 0);
	signal T_Res			: std_logic;
	signal XY_State			: std_logic_vector(1 downto 0);
	signal Pre_XY_F_M		: std_logic_vector(2 downto 0);
	signal NextIs_XY_Fetch	: std_logic;
	signal XY_Ind			: std_logic;
	signal No_BTR			: std_logic;
	signal BTR_r			: std_logic;
	signal Auto_Wait		: std_logic;
	signal Auto_Wait_t1		: std_logic;
	signal Auto_Wait_t2		: std_logic;
	signal IncDecZ			: std_logic;

	-- ALU signals
	signal BusB				: std_logic_vector(7 downto 0);
	signal BusA				: std_logic_vector(7 downto 0);
	signal ALU_Q			: std_logic_vector(7 downto 0);
	signal F_Out			: std_logic_vector(7 downto 0);

	-- Registered micro code outputs
	signal Read_To_Reg_r	: std_logic_vector(4 downto 0);
	signal Arith16_r		: std_logic;
	signal Z16_r			: std_logic;
	signal ALU_Op_r			: std_logic_vector(3 downto 0);
	signal Save_ALU_r		: std_logic;
	signal PreserveC_r		: std_logic;
	signal MCycles			: std_logic_vector(2 downto 0);

	-- Micro code outputs
	signal MCycles_d		: std_logic_vector(2 downto 0);
	signal TStates			: std_logic_vector(2 downto 0);
	signal IntCycle			: std_logic;
	signal NMICycle			: std_logic;
	signal Inc_PC			: std_logic;
	signal Inc_WZ			: std_logic;
	signal IncDec_16		: std_logic_vector(3 downto 0);
	signal Prefix			: std_logic_vector(1 downto 0);
	signal Read_To_Acc		: std_logic;
	signal Read_To_Reg		: std_logic;
	signal Set_BusB_To		: std_logic_vector(3 downto 0);
	signal Set_BusA_To		: std_logic_vector(3 downto 0);
	signal ALU_Op			: std_logic_vector(3 downto 0);
	signal Save_ALU			: std_logic;
	signal PreserveC		: std_logic;
	signal Arith16			: std_logic;
	signal Set_Addr_To		: std_logic_vector(2 downto 0);
	signal Jump				: std_logic;
	signal JumpE			: std_logic;
	signal JumpXY			: std_logic;
	signal Call				: std_logic;
	signal RstP				: std_logic;
	signal LDZ				: std_logic;
	signal LDW				: std_logic;
	signal LDSPHL			: std_logic;
	signal IORQ_i			: std_logic;
	signal Special_LD		: std_logic_vector(2 downto 0);
	signal ExchangeDH		: std_logic;
	signal ExchangeRp		: std_logic;
	signal ExchangeAF		: std_logic;
	signal ExchangeRS		: std_logic;
	signal I_DJNZ			: std_logic;
	signal I_CPL			: std_logic;
	signal I_CCF			: std_logic;
	signal I_SCF			: std_logic;
	signal I_RETN			: std_logic;
	signal I_BT				: std_logic;
	signal I_BC				: std_logic;
	signal I_BTR			: std_logic;
	signal I_RLD			: std_logic;
	signal I_RRD			: std_logic;
	signal I_INRC			: std_logic;
	signal SetDI			: std_logic;
	signal SetEI			: std_logic;
	signal IMode			: std_logic_vector(1 downto 0);
	signal Halt				: std_logic;

begin

	mcode : T80_MCode
		generic map(
			Mode => Mode,
			Flag_C => Flag_C,
			Flag_N => Flag_N,
			Flag_P => Flag_P,
			Flag_X => Flag_X,
			Flag_H => Flag_H,
			Flag_Y => Flag_Y,
			Flag_Z => Flag_Z,
			Flag_S => Flag_S)
		port map(
			IR => IR,
			ISet => ISet,
			MCycle => MCycle,
			F => F,
			NMICycle => NMICycle,
			IntCycle => IntCycle,
			MCycles => MCycles_d,
			TStates => TStates,
			Prefix => Prefix,
			Inc_PC => Inc_PC,
			Inc_WZ => Inc_WZ,
			IncDec_16 => IncDec_16,
			Read_To_Acc => Read_To_Acc,
			Read_To_Reg => Read_To_Reg,
			Set_BusB_To => Set_BusB_To,
			Set_BusA_To => Set_BusA_To,
			ALU_Op => ALU_Op,
			Save_ALU => Save_ALU,
			PreserveC => PreserveC,
			Arith16 => Arith16,
			Set_Addr_To => Set_Addr_To,
			IORQ => IORQ_i,
			Jump => Jump,
			JumpE => JumpE,
			JumpXY => JumpXY,
			Call => Call,
			RstP => RstP,
			LDZ => LDZ,
			LDW => LDW,
			LDSPHL => LDSPHL,
			Special_LD => Special_LD,
			ExchangeDH => ExchangeDH,
			ExchangeRp => ExchangeRp,
			ExchangeAF => ExchangeAF,
			ExchangeRS => ExchangeRS,
			I_DJNZ => I_DJNZ,
			I_CPL => I_CPL,
			I_CCF => I_CCF,
			I_SCF => I_SCF,
			I_RETN => I_RETN,
			I_BT => I_BT,
			I_BC => I_BC,
			I_BTR => I_BTR,
			I_RLD => I_RLD,
			I_RRD => I_RRD,
			I_INRC => I_INRC,
			SetDI => SetDI,
			SetEI => SetEI,
			IMode => IMode,
			Halt => Halt,
			NoRead => NoRead,
			Write => Write);

	alu : T80_ALU
		generic map(
			Mode => Mode,
			Flag_C => Flag_C,
			Flag_N => Flag_N,
			Flag_P => Flag_P,
			Flag_X => Flag_X,
			Flag_H => Flag_H,
			Flag_Y => Flag_Y,
			Flag_Z => Flag_Z,
			Flag_S => Flag_S)
		port map(
			Arith16 => Arith16_r,
			Z16 => Z16_r,
			ALU_Op => ALU_Op_r,
			IR => IR(5 downto 0),
			ISet => ISet,
			BusA => BusA,
			BusB => BusB,
			F_In => F,
			Q => ALU_Q,
			F_Out => F_Out);

	ClkEn <= CEN and not BusAck;

	T_Res <= '1' when TState = unsigned(TStates) else '0';

	NextIs_XY_Fetch <= '1' when XY_State /= "00" and XY_Ind = '0' and
							((Set_Addr_To = aXY) or
							(MCycle = "001" and IR = "11001011") or
							(MCycle = "001" and IR = "00110110")) else '0';

	Save_Mux <= BusB when ExchangeRp = '1' else
		DI_Reg when Save_ALU_r = '0' else
		ALU_Q;

	process (RESET_n, CLK_n)
	begin
		if RESET_n = '0' then
			PC <= (others => '0');  -- Program Counter
			A <= (others => '0');
			TmpAddr <= (others => '0');
			IR <= "00000000";
			ISet <= "00";
			XY_State <= "00";
			IStatus <= "00";
			MCycles <= "000";
			DO <= "00000000";

			ACC <= (others => '1');
			F <= (others => '1');
			Ap <= (others => '1');
			Fp <= (others => '1');
			I <= (others => '0');
			R <= (others => '0');
			SP <= (others => '1');
			Alternate <= '0';

			Read_To_Reg_r <= "00000";
			F <= (others => '1');
			Arith16_r <= '0';
			BTR_r <= '0';
			Z16_r <= '0';
			ALU_Op_r <= "0000";
			Save_ALU_r <= '0';
			PreserveC_r <= '0';
			XY_Ind <= '0';

		elsif CLK_n'event and CLK_n = '1' then

			if ClkEn = '1' then

			ALU_Op_r <= "0000";
			Save_ALU_r <= '0';
			Read_To_Reg_r <= "00000";

			MCycles <= MCycles_d;

			if IMode /= "11" then
				IStatus <= IMode;
			end if;

			Arith16_r <= Arith16;
			PreserveC_r <= PreserveC;
			if ISet = "10" and ALU_OP(2) = '0' and ALU_OP(0) = '1' and MCycle = "011" then
				Z16_r <= '1';
			else
				Z16_r <= '0';
			end if;

			if MCycle  = "001" and TState(2) = '0' then
			-- MCycle = 1 and TState = 1, 2, or 3

				if TState = 2 and Wait_n = '1' then
					if Mode < 2 then
						A(7 downto 0) <= std_logic_vector(R);
						A(15 downto 8) <= I;
						R(6 downto 0) <= R(6 downto 0) + 1;
					end if;

					if Jump = '0' and Call = '0' and NMICycle = '0' and IntCycle = '0' and not (Halt_FF = '1' or Halt = '1') then
						PC <= PC + 1;
					end if;

					if IntCycle = '1' and IStatus = "01" then
						IR <= "11111111";
					elsif Halt_FF = '1' or (IntCycle = '1' and IStatus = "10") or NMICycle = '1' then
						IR <= "00000000";
					else
						IR <= DInst;
					end if;

					ISet <= "00";
					if Prefix /= "00" then
						if Prefix = "11" then
							if IR(5) = '1' then
								XY_State <= "10";
							else
								XY_State <= "01";
							end if;
						else
							if Prefix = "10" then
								XY_State <= "00";
								XY_Ind <= '0';
							end if;
							ISet <= Prefix;
						end if;
					else
						XY_State <= "00";
						XY_Ind <= '0';
					end if;
				end if;

			else
			-- either (MCycle > 1) OR (MCycle = 1 AND TState > 3)

				if MCycle = "110" then
					XY_Ind <= '1';
					if Prefix = "01" then
						ISet <= "01";
					end if;
				end if;

				if T_Res = '1' then
					BTR_r <= (I_BT or I_BC or I_BTR) and not No_BTR;
					if Jump = '1' then
						A(15 downto 8) <= DI_Reg;
						A(7 downto 0) <= TmpAddr(7 downto 0);
						PC(15 downto 8) <= unsigned(DI_Reg);
						PC(7 downto 0) <= unsigned(TmpAddr(7 downto 0));
					elsif JumpXY = '1' then
						A <= RegBusC;
						PC <= unsigned(RegBusC);
					elsif Call = '1' or RstP = '1' then
						A <= TmpAddr;
						PC <= unsigned(TmpAddr);
					elsif MCycle = MCycles and NMICycle = '1' then
						A <= "0000000001100110";
						PC <= "0000000001100110";
					elsif MCycle = "011" and IntCycle = '1' and IStatus = "10" then
						A(15 downto 8) <= I;
						A(7 downto 0) <= TmpAddr(7 downto 0);
						PC(15 downto 8) <= unsigned(I);
						PC(7 downto 0) <= unsigned(TmpAddr(7 downto 0));
					else
						case Set_Addr_To is
						when aXY =>
							if XY_State = "00" then
								A <= RegBusC;
							else
								if NextIs_XY_Fetch = '1' then
									A <= std_logic_vector(PC);
								else
									A <= TmpAddr;
								end if;
							end if;
						when aIOA =>
							if Mode = 3 then
								-- Memory map I/O on GBZ80
								A(15 downto 8) <= (others => '1');
							elsif Mode = 2 then
								-- Duplicate I/O address on 8080
								A(15 downto 8) <= DI_Reg;
							else
								A(15 downto 8) <= ACC;
							end if;
							A(7 downto 0) <= DI_Reg;
						when aSP =>
							A <= std_logic_vector(SP);
						when aBC =>
							if Mode = 3 and IORQ_i = '1' then
								-- Memory map I/O on GBZ80
								A(15 downto 8) <= (others => '1');
								A(7 downto 0) <= RegBusC(7 downto 0);
							else
								A <= RegBusC;
							end if;
						when aDE =>
							A <= RegBusC;
						when aZI =>
							if Inc_WZ = '1' then
								A <= std_logic_vector(unsigned(TmpAddr) + 1);
							else
								A(15 downto 8) <= DI_Reg;
								A(7 downto 0) <= TmpAddr(7 downto 0);
							end if;
						when others =>
							A <= std_logic_vector(PC);
						end case;
					end if;

					Save_ALU_r <= Save_ALU;
					ALU_Op_r <= ALU_Op;

					if I_CPL = '1' then
						-- CPL
						ACC <= not ACC;
						F(Flag_Y) <= not ACC(5);
						F(Flag_H) <= '1';
						F(Flag_X) <= not ACC(3);
						F(Flag_N) <= '1';
					end if;
					if I_CCF = '1' then
						-- CCF
						F(Flag_C) <= not F(Flag_C);
						F(Flag_Y) <= ACC(5);
						F(Flag_H) <= F(Flag_C);
						F(Flag_X) <= ACC(3);
						F(Flag_N) <= '0';
					end if;
					if I_SCF = '1' then
						-- SCF
						F(Flag_C) <= '1';
						F(Flag_Y) <= ACC(5);
						F(Flag_H) <= '0';
						F(Flag_X) <= ACC(3);
						F(Flag_N) <= '0';
					end if;
				end if;

				if TState = 2 and Wait_n = '1' then
					if ISet = "01" and MCycle = "111" then
						IR <= DInst;
					end if;
					if JumpE = '1' then
						PC <= unsigned(signed(PC) + signed(DI_Reg));
					elsif Inc_PC = '1' then
						PC <= PC + 1;
					end if;
					if BTR_r = '1' then
						PC <= PC - 2;
					end if;
					if RstP = '1' then
						TmpAddr <= (others =>'0');
						TmpAddr(5 downto 3) <= IR(5 downto 3);
					end if;
				end if;
				if TState = 3 and MCycle = "110" then
					TmpAddr <= std_logic_vector(signed(RegBusC) + signed(DI_Reg));
				end if;

				if (TState = 2 and Wait_n = '1') or (TState = 4 and MCycle = "001") then
					if IncDec_16(2 downto 0) = "111" then
						if IncDec_16(3) = '1' then
							SP <= SP - 1;
						else
							SP <= SP + 1;
						end if;
					end if;
				end if;

				if LDSPHL = '1' then
					SP <= unsigned(RegBusC);
				end if;
				if ExchangeAF = '1' then
					Ap <= ACC;
					ACC <= Ap;
					Fp <= F;
					F <= Fp;
				end if;
				if ExchangeRS = '1' then
					Alternate <= not Alternate;
				end if;
			end if;

			if TState = 3 then
				if LDZ = '1' then
					TmpAddr(7 downto 0) <= DI_Reg;
				end if;
				if LDW = '1' then
					TmpAddr(15 downto 8) <= DI_Reg;
				end if;

				if Special_LD(2) = '1' then
					case Special_LD(1 downto 0) is
					when "00" =>
						ACC <= I;
						F(Flag_P) <= IntE_FF2;
					when "01" =>
						ACC <= std_logic_vector(R);
						F(Flag_P) <= IntE_FF2;
					when "10" =>
						I <= ACC;
					when others =>
						R <= unsigned(ACC);
					end case;
				end if;
			end if;

			if (I_DJNZ = '0' and Save_ALU_r = '1') or ALU_Op_r = "1001" then
				if Mode = 3 then
					F(6) <= F_Out(6);
					F(5) <= F_Out(5);
					F(7) <= F_Out(7);
					if PreserveC_r = '0' then
						F(4) <= F_Out(4);
					end if;
				else
					F(7 downto 1) <= F_Out(7 downto 1);
					if PreserveC_r = '0' then
						F(Flag_C) <= F_Out(0);
					end if;
				end if;
			end if;
			if T_Res = '1' and I_INRC = '1' then
				F(Flag_H) <= '0';
				F(Flag_N) <= '0';
				if DI_Reg(7 downto 0) = "00000000" then
					F(Flag_Z) <= '1';
				else
					F(Flag_Z) <= '0';
				end if;
				F(Flag_S) <= DI_Reg(7);
				F(Flag_P) <= not (DI_Reg(0) xor DI_Reg(1) xor DI_Reg(2) xor DI_Reg(3) xor
					DI_Reg(4) xor DI_Reg(5) xor DI_Reg(6) xor DI_Reg(7));
			end if;

			if TState = 1 and Auto_Wait_t1 = '0' then
				DO <= BusB;
				if I_RLD = '1' then
					DO(3 downto 0) <= BusA(3 downto 0);
					DO(7 downto 4) <= BusB(3 downto 0);
				end if;
				if I_RRD = '1' then
					DO(3 downto 0) <= BusB(7 downto 4);
					DO(7 downto 4) <= BusA(3 downto 0);
				end if;
			end if;

			if T_Res = '1' then
				Read_To_Reg_r(3 downto 0) <= Set_BusA_To;
				Read_To_Reg_r(4) <= Read_To_Reg;
				if Read_To_Acc = '1' then
					Read_To_Reg_r(3 downto 0) <= "0111";
					Read_To_Reg_r(4) <= '1';
				end if;
			end if;

			if TState = 1 and I_BT = '1' then
				F(Flag_X) <= ALU_Q(3);
				F(Flag_Y) <= ALU_Q(1);
				F(Flag_H) <= '0';
				F(Flag_N) <= '0';
			end if;
			if I_BC = '1' or I_BT = '1' then
				F(Flag_P) <= IncDecZ;
			end if;

			if (TState = 1 and Save_ALU_r = '0' and Auto_Wait_t1 = '0') or
				(Save_ALU_r = '1' and ALU_OP_r /= "0111") then
				case Read_To_Reg_r is
				when "10111" =>
					ACC <= Save_Mux;
				when "10110" =>
					DO <= Save_Mux;
				when "11000" =>
					SP(7 downto 0) <= unsigned(Save_Mux);
				when "11001" =>
					SP(15 downto 8) <= unsigned(Save_Mux);
				when "11011" =>
					F <= Save_Mux;
				when others =>
				end case;
			end if;

		end if;

		end if;

	end process;

---------------------------------------------------------------------------
--
-- BC('), DE('), HL('), IX and IY
--
---------------------------------------------------------------------------
	process (CLK_n)
	begin
		if CLK_n'event and CLK_n = '1' then
			if ClkEn = '1' then
				-- Bus A / Write
				RegAddrA_r <= Alternate & Set_BusA_To(2 downto 1);
				if XY_Ind = '0' and XY_State /= "00" and Set_BusA_To(2 downto 1) = "10" then
					RegAddrA_r <= XY_State(1) & "11";
				end if;

				-- Bus B
				RegAddrB_r <= Alternate & Set_BusB_To(2 downto 1);
				if XY_Ind = '0' and XY_State /= "00" and Set_BusB_To(2 downto 1) = "10" then
					RegAddrB_r <= XY_State(1) & "11";
				end if;

				-- Address from register
				RegAddrC <= Alternate & Set_Addr_To(1 downto 0);
				-- Jump (HL), LD SP,HL
				if (JumpXY = '1' or LDSPHL = '1') then
					RegAddrC <= Alternate & "10";
				end if;
				if ((JumpXY = '1' or LDSPHL = '1') and XY_State /= "00") or (MCycle = "110") then
					RegAddrC <= XY_State(1) & "11";
				end if;

				if I_DJNZ = '1' and Save_ALU_r = '1' and Mode < 2 then
					IncDecZ <= F_Out(Flag_Z);
				end if;
				if (TState = 2 or (TState = 3 and MCycle = "001")) and IncDec_16(2 downto 0) = "100" then
					if ID16 = 0 then
						IncDecZ <= '0';
					else
						IncDecZ <= '1';
					end if;
				end if;

				RegBusA_r <= RegBusA;
			end if;
		end if;
	end process;

	RegAddrA <=
			-- 16 bit increment/decrement
			Alternate & IncDec_16(1 downto 0) when (TState = 2 or
				(TState = 3 and MCycle = "001" and IncDec_16(2) = '1')) and XY_State = "00" else
			XY_State(1) & "11" when (TState = 2 or
				(TState = 3 and MCycle = "001" and IncDec_16(2) = '1')) and IncDec_16(1 downto 0) = "10" else
			-- EX HL,DL
			Alternate & "10" when ExchangeDH = '1' and TState = 3 else
			Alternate & "01" when ExchangeDH = '1' and TState = 4 else
			-- Bus A / Write
			RegAddrA_r;

	RegAddrB <=
			-- EX HL,DL
			Alternate & "01" when ExchangeDH = '1' and TState = 3 else
			-- Bus B
			RegAddrB_r;

	ID16 <= signed(RegBusA) - 1 when IncDec_16(3) = '1' else
			signed(RegBusA) + 1;

	process (Save_ALU_r, Auto_Wait_t1, ALU_OP_r, Read_To_Reg_r,
			ExchangeDH, IncDec_16, MCycle, TState, Wait_n)
	begin
		RegWEH <= '0';
		RegWEL <= '0';
		if (TState = 1 and Save_ALU_r = '0' and Auto_Wait_t1 = '0') or
			(Save_ALU_r = '1' and ALU_OP_r /= "0111") then
			case Read_To_Reg_r is
			when "10000" | "10001" | "10010" | "10011" | "10100" | "10101" =>
				RegWEH <= not Read_To_Reg_r(0);
				RegWEL <= Read_To_Reg_r(0);
			when others =>
			end case;
		end if;

		if ExchangeDH = '1' and (TState = 3 or TState = 4) then
			RegWEH <= '1';
			RegWEL <= '1';
		end if;

		if IncDec_16(2) = '1' and ((TState = 2 and Wait_n = '1' and MCycle /= "001") or (TState = 3 and MCycle = "001")) then
			case IncDec_16(1 downto 0) is
			when "00" | "01" | "10" =>
				RegWEH <= '1';
				RegWEL <= '1';
			when others =>
			end case;
		end if;
	end process;

	process (Save_Mux, RegBusB, RegBusA_r, ID16,
			ExchangeDH, IncDec_16, MCycle, TState, Wait_n)
	begin
		RegDIH <= Save_Mux;
		RegDIL <= Save_Mux;

		if ExchangeDH = '1' and TState = 3 then
			RegDIH <= RegBusB(15 downto 8);
			RegDIL <= RegBusB(7 downto 0);
		end if;
		if ExchangeDH = '1' and TState = 4 then
			RegDIH <= RegBusA_r(15 downto 8);
			RegDIL <= RegBusA_r(7 downto 0);
		end if;

		if IncDec_16(2) = '1' and ((TState = 2 and MCycle /= "001") or (TState = 3 and MCycle = "001")) then
			RegDIH <= std_logic_vector(ID16(15 downto 8));
			RegDIL <= std_logic_vector(ID16(7 downto 0));
		end if;
	end process;

	Regs : T80_Reg
		port map(
			Clk => CLK_n,
			CEN => ClkEn,
			WEH => RegWEH,
			WEL => RegWEL,
			AddrA => RegAddrA,
			AddrB => RegAddrB,
			AddrC => RegAddrC,
			DIH => RegDIH,
			DIL => RegDIL,
			DOAH => RegBusA(15 downto 8),
			DOAL => RegBusA(7 downto 0),
			DOBH => RegBusB(15 downto 8),
			DOBL => RegBusB(7 downto 0),
			DOCH => RegBusC(15 downto 8),
			DOCL => RegBusC(7 downto 0));

---------------------------------------------------------------------------
--
-- Buses
--
---------------------------------------------------------------------------
	process (CLK_n)
	begin
		if CLK_n'event and CLK_n = '1' then
			if ClkEn = '1' then
			case Set_BusB_To is
			when "0111" =>
				BusB <= ACC;
			when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" =>
				if Set_BusB_To(0) = '1' then
					BusB <= RegBusB(7 downto 0);
				else
					BusB <= RegBusB(15 downto 8);
				end if;
			when "0110" =>
				BusB <= DI_Reg;
			when "1000" =>
				BusB <= std_logic_vector(SP(7 downto 0));
			when "1001" =>
				BusB <= std_logic_vector(SP(15 downto 8));
			when "1010" =>
				BusB <= "00000001";
			when "1011" =>
				BusB <= F;
			when "1100" =>
				BusB <= std_logic_vector(PC(7 downto 0));
			when "1101" =>
				BusB <= std_logic_vector(PC(15 downto 8));
			when "1110" =>
				BusB <= "00000000";
			when others =>
				BusB <= "--------";
			end case;

			case Set_BusA_To is
			when "0111" =>
				BusA <= ACC;
			when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" =>
				if Set_BusA_To(0) = '1' then
					BusA <= RegBusA(7 downto 0);
				else
					BusA <= RegBusA(15 downto 8);
				end if;
			when "0110" =>
				BusA <= DI_Reg;
			when "1000" =>
				BusA <= std_logic_vector(SP(7 downto 0));
			when "1001" =>
				BusA <= std_logic_vector(SP(15 downto 8));
			when "1010" =>
				BusA <= "00000000";
			when others =>
				BusB <= "--------";
			end case;
			end if;
		end if;
	end process;

---------------------------------------------------------------------------
--
-- Generate external control signals
--
---------------------------------------------------------------------------
	process (RESET_n,CLK_n)
	begin
		if RESET_n = '0' then
			RFSH_n <= '1';
		elsif CLK_n'event and CLK_n = '1' then
			if CEN = '1' then
			if MCycle = "001" and ((TState = 2  and Wait_n = '1') or TState = 3) then
				RFSH_n <= '0';
			else
				RFSH_n <= '1';
			end if;
			end if;
		end if;
	end process;

	MC <= std_logic_vector(MCycle);
	TS <= std_logic_vector(TState);
	DI_Reg <= DI;
	HALT_n <= not Halt_FF;
	BUSAK_n <= not BusAck;
	IntCycle_n <= not IntCycle;
	IntE <= IntE_FF1;
	IORQ <= IORQ_i;
	Stop <= I_DJNZ;

-------------------------------------------------------------------------
--
-- Syncronise inputs
--
-------------------------------------------------------------------------
	process (RESET_n, CLK_n)
		variable OldNMI_n : std_logic;
	begin
		if RESET_n = '0' then
			BusReq_s <= '0';
			INT_s <= '0';
			NMI_s <= '0';
			OldNMI_n := '0';
		elsif CLK_n'event and CLK_n = '1' then
			if CEN = '1' then
			BusReq_s <= not BUSRQ_n;
			INT_s <= not INT_n;
			if NMICycle = '1' then
				NMI_s <= '0';
			elsif NMI_n = '0' and OldNMI_n = '1' then
				NMI_s <= '1';
			end if;
			OldNMI_n := NMI_n;
			end if;
		end if;
	end process;

-------------------------------------------------------------------------
--
-- Main state machine
--
-------------------------------------------------------------------------
	process (RESET_n, CLK_n)
	begin
		if RESET_n = '0' then
			MCycle <= "001";
			TState <= "000";
			Pre_XY_F_M <= "000";
			Halt_FF <= '0';
			BusAck <= '0';
			NMICycle <= '0';
			IntCycle <= '0';
			IntE_FF1 <= '0';
			IntE_FF2 <= '0';
			No_BTR <= '0';
			Auto_Wait_t1 <= '0';
			Auto_Wait_t2 <= '0';
			M1_n <= '1';
		elsif CLK_n'event and CLK_n = '1' then
			if CEN = '1' then
			if T_Res = '1' then
				Auto_Wait_t1 <= '0';
			else
				Auto_Wait_t1 <= Auto_Wait or IORQ_i;
			end if;
			Auto_Wait_t2 <= Auto_Wait_t1;
			No_BTR <= (I_BT and (not IR(4) or not F(Flag_P))) or
					(I_BC and (not IR(4) or F(Flag_Z) or not F(Flag_P))) or
					(I_BTR and (not IR(4) or F(Flag_Z)));
			if TState = 2 then
				if SetEI = '1' then
					IntE_FF1 <= '1';
					IntE_FF2 <= '1';
				end if;
				if I_RETN = '1' then
					IntE_FF1 <= IntE_FF2;
				end if;
			end if;
			if TState = 3 then
				if SetDI = '1' then
					IntE_FF1 <= '0';
					IntE_FF2 <= '0';
				end if;
			end if;
			if IntCycle = '1' or NMICycle = '1' then
				Halt_FF <= '0';
			end if;
			if MCycle = "001" and TState = 2 and Wait_n = '1' then
				M1_n <= '1';
			end if;
			if BusReq_s = '1' and BusAck = '1' then
			else
				BusAck <= '0';
				if TState = 2 and Wait_n = '0' then
				elsif T_Res = '1' then
					if Halt = '1' then
						Halt_FF <= '1';
					end if;
					if BusReq_s = '1' then
						BusAck <= '1';
					else
						TState <= "001";
						if NextIs_XY_Fetch = '1' then
							MCycle <= "110";
							Pre_XY_F_M <= MCycle;
							if IR = "00110110" and Mode = 0 then
								Pre_XY_F_M <= "010";
							end if;
						elsif (MCycle = "111") or
							(MCycle = "110" and Mode = 1 and ISet /= "01") then
							MCycle <= std_logic_vector(unsigned(Pre_XY_F_M) + 1);
						elsif (MCycle = MCycles) or
							No_BTR = '1' or
							(MCycle = "010" and I_DJNZ = '1' and IncDecZ = '1') then
							M1_n <= '0';
							MCycle <= "001";
							IntCycle <= '0';
							NMICycle <= '0';
							if NMI_s = '1' and Prefix = "00" then
								NMICycle <= '1';
								IntE_FF1 <= '0';
							elsif (IntE_FF1 = '1' and INT_s = '1') and Prefix = "00" and SetEI = '0' then
								IntCycle <= '1';
								IntE_FF1 <= '0';
								IntE_FF2 <= '0';
							end if;
						else
							MCycle <= std_logic_vector(unsigned(MCycle) + 1);
						end if;
					end if;
				else
					if (Auto_Wait = '1' and Auto_Wait_t2 = '0') nor
						(IOWait = 1 and IORQ_i = '1' and Auto_Wait_t1 = '0') then
						TState <= TState + 1;
					end if;
				end if;
			end if;
			if TState = 0 then
				M1_n <= '0';
			end if;
			end if;
		end if;
	end process;

	process (IntCycle, NMICycle, MCycle)
	begin
		Auto_Wait <= '0';
		if IntCycle = '1' or NMICycle = '1' then
			if MCycle = "001" then
				Auto_Wait <= '1';
			end if;
		end if;
	end process;

end;
