--
-- Z80 compatible microprocessor core, preudo-asynchronous top level (by Sorgelig)
--
-- 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/
--
-- File history :
--
-- v1.0: convert to preudo-asynchronous model with original Z80 timings.
--
-- v2.0: rewritten for more precise timings.
--       support for both CEN_n and CEN_p set to 1. Effective clock will be CLK/2.
--
-- v2.1: Output Address 0 during non-bus MCycle (fix ZX contention)
--
-- v2.2: Interrupt acknowledge cycle has been corrected
--       WAIT_n is broken in T80.vhd. Simulate correct WAIT_n locally.
--
-- v2.3: Output last used Address during non-bus MCycle seems more correct.
--

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

entity T80pa is
	generic(
		Mode : integer := 0	-- 0 => Z80, 1 => Fast Z80, 2 => 8080, 3 => GB
	);
	port(
		RESET_n		: in  std_logic;
		CLK			: in  std_logic;
		CEN_p       : in  std_logic := '1';
		CEN_n       : in  std_logic := '1';
		WAIT_n		: in  std_logic := '1';
		INT_n			: in  std_logic := '1';
		NMI_n			: in  std_logic := '1';
		BUSRQ_n		: in  std_logic := '1';
		M1_n			: out std_logic;
		MREQ_n		: out std_logic;
		IORQ_n		: out std_logic;
		RD_n			: out std_logic;
		WR_n			: out std_logic;
		RFSH_n		: out std_logic;
		HALT_n		: out std_logic;
		BUSAK_n		: out std_logic;
		OUT0        : in  std_logic := '0';  -- 0 => OUT(C),0, 1 => OUT(C),255
		A				: out std_logic_vector(15 downto 0);
		DI				: in  std_logic_vector(7 downto 0);
		DO				: out std_logic_vector(7 downto 0);
		REG			: out std_logic_vector(207 downto 0) -- IY, HL', DE', BC', IX, HL, DE, BC, PC, SP, R, I, F', A', F, A
	);
end T80pa;

architecture rtl of T80pa is

	signal IntCycle_n		: std_logic;
	signal IntCycleD_n	: std_logic_vector(1 downto 0);
	signal IORQ				: std_logic;
	signal NoRead			: std_logic;
	signal Write			: std_logic;
	signal BUSAK			: std_logic;
	signal DI_Reg			: std_logic_vector (7 downto 0);	-- Input synchroniser
	signal MCycle			: std_logic_vector(2 downto 0);
	signal TState			: std_logic_vector(2 downto 0);
	signal CEN_pol			: std_logic;
	signal A_int		   : std_logic_vector(15 downto 0);
	signal A_last		   : std_logic_vector(15 downto 0);

begin

	A <= A_int when NoRead = '0' or Write = '1' else A_last;

	BUSAK_n <= BUSAK;

	u0 : work.T80
		generic map(
			Mode    => Mode,
			IOWait  => 1
		)
		port map(
			CEN     => CEN_p and not CEN_pol,
			M1_n    => M1_n,
			IORQ    => IORQ,
			NoRead  => NoRead,
			Write   => Write,
			RFSH_n  => RFSH_n,
			HALT_n  => HALT_n,
			WAIT_n  => '1',
			INT_n   => INT_n,
			NMI_n   => NMI_n,
			RESET_n => RESET_n,
			BUSRQ_n => BUSRQ_n,
			BUSAK_n => BUSAK,
			CLK_n   => CLK,
			A       => A_int,
			DInst   => DI,     -- valid   at beginning of T3
			DI      => DI_Reg, -- latched at middle    of T3
			DO      => DO,
			REG     => REG,
			MC      => MCycle,
			TS      => TState,
			OUT0    => OUT0,
			IntCycle_n => IntCycle_n
		);

	process(CLK)
	begin
		if rising_edge(CLK) then
			if RESET_n = '0' then
				WR_n    <= '1';
				RD_n    <= '1';
				IORQ_n  <= '1';
				MREQ_n  <= '1';
				DI_Reg  <= "00000000";
				CEN_pol <= '0';
			elsif CEN_p = '1' and CEN_pol = '0' then
				CEN_pol <= '1';
				if MCycle = "001" then
					if TState = "010" then
						IORQ_n <= '1';
						MREQ_n <= '1';
						RD_n   <= '1';
					end if;
				else
					if TState = "001" and IORQ = '1' then
						WR_n   <= not Write;
						RD_n   <= Write;
						IORQ_n <= '0';
					end if;
				end if;
			elsif CEN_n = '1' and CEN_pol = '1' then
				if TState = "010" then
					CEN_pol <= not WAIT_n;
				else
					CEN_pol <= '0';
				end if;
				if TState = "011" and BUSAK = '1' then
					DI_Reg <= DI;
				end if;
				if MCycle = "001" then
					if TState = "001" then
						IntCycleD_n <= IntCycleD_n(0) & IntCycle_n;
						RD_n   <= not IntCycle_n;
						MREQ_n <= not IntCycle_n;
						IORQ_n <= IntCycleD_n(1);
						A_last <= A_int;
					end if;
					if TState = "011" then
						IntCycleD_n <= "11";
						RD_n   <= '1';
						MREQ_n <= '0';
					end if;
					if TState = "100" then
						MREQ_n <= '1';
					end if;
				else
					if NoRead = '0' and IORQ = '0' then
						if TState = "001" then
							RD_n   <= Write;
							MREQ_n <= '0';
							A_last <= A_int;
						end if;
					end if;
					if TState = "010" then
						WR_n   <= not Write;
					end if;
					if TState = "011" then
						WR_n   <= '1';
						RD_n   <= '1';
						IORQ_n <= '1';
						MREQ_n <= '1';
					end if;
				end if;
			end if;
		end if;
	end process;
end;
