----------------------------------------------------------------------------------
-- Inital version 1.0 for xc9572xl-vq64
-- Engineer:   Mario Prato
-- Create Date:    10:07:18 11/22/2012
-- 
-- Modified for Altera EPM3128ATC100-10
-- Engineer:   valerium
-- Design Name:    divmmc ver. 1.1
-- Redesign Date:    02:54:18 10/05/2021 
--
-- Module Name:    divmmc - Behavioral 
-- Project Name:  divmmc
-- Target Devices: EPM3128ATC100-10
-- Tool versions: Quartus II 13.0SP1
-- Description: zx spectrum mmc sd interface
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity divmmc is
Port ( 

-- z80 cpu signals
	A        : in  std_logic_vector (15 downto 0);
	D        : inout  std_logic_vector (7 downto 0);
	iorq     : in  std_logic;
	mreq     : in  std_logic;
	wr       : in  std_logic;
	rd       : in  std_logic;
	m1       : in  std_logic;
	reset    : in  std_logic;
	clock    : in  std_logic; -- Z80 Clock from ula chip (must be negated from edge connector signal)
 
-- ram/rom signals
	romcs    : out  STD_LOGIC;		-- 1 -> page out spectrum rom
	romoe    : out STD_LOGIC;		-- eeprom oe pin
	romwr    : out STD_LOGIC;		-- eeprom wr pin
	ramoe    : out STD_LOGIC;		-- ram oe pin
	ramwr    : out  STD_LOGIC;		-- ram wr pin
	bankout  : out  STD_LOGIC_VECTOR (5 downto 0);	--ram bank no.
			  
-- spi interface 
	card         : out std_logic_vector(1 downto 0) :="11";	-- Cards CS
	spi_clock    : out std_logic :='1';	-- card clock
	spi_dataout  : out std_logic :='1';	-- card data in
	spi_datain   :	in std_logic :='1';	-- card data out

-- various 			  
	poweron      : in  STD_LOGIC;		-- low pulse on poweron
	eprom        : in  STD_LOGIC;		-- eprom jumper
	mapcondout   : out  std_logic		-- hi when divmmc mem paged in
);
end divmmc;


architecture Behavioral of divmmc is

	signal address  : std_logic_vector(7 downto 0) ;
	signal zxmmcio : std_logic;
	signal divideio : std_logic;

	signal bank     : std_logic_vector (5 downto 0) := "000000";

	signal mapterm  : std_logic := '0';
	signal mapcond  : std_logic := '0';
	signal conmem   : std_logic  := '0';
	signal mapram   : std_logic  := '0';
	signal automap  : std_logic  := '0';
 
	signal map3DXX  : std_logic;
	signal map1F00  : std_logic;

	signal bank3    : std_logic;
 
 
-- Transmission states
	type transStates is (
		IDLE, -- Wait for a WR or RD request on port 0xEB
		SAMPLE, -- As there is an I/O request, prepare the transmission; sample the CPU databus if required
		TRANSMIT); -- Transmission (SEND or RECEIVE)
	signal transState : transStates := IDLE; -- Transmission state (initially IDLE)
	
	signal TState : unsigned(3 downto 0) := (others => '0');		-- Counts the T-States during transmission
	
	signal fromSDByte : std_logic_vector(7 downto 0) := (others => '1');	-- Byte received from SD
	signal toSDByte : std_logic_vector(7 downto 0) := (others => '1');	-- Byte to send to SD
	signal toCPUByte : std_logic_vector(7 downto 0) := (others => '1');	-- Byte seen by the CPU after a byte read
 
-- dichiarazioni constanti 
 
	constant divide_control_port : std_logic_vector(7 downto 0) := x"E3";	-- port %11100011
	constant zxmmc_control_port : std_logic_vector(7 downto 0) := x"E7";	-- era la porta 31 nella zxmmc+
	constant zxmmc_spi_port : std_logic_vector(7 downto 0) := x"EB";	-- era la porta 63 nella zxmmc+

 
attribute PWR_MODE: string;
attribute FAST: string;
attribute BUFG: string;
 
begin
 
 address <= A(7 downto 0);
 
 bank3 <= '1' when bank ="000011" else '0';

 -- ROM RAM read write signals


 romoe <= rd or A(15) or A(14) or A(13) or (not conmem and mapram) or (not conmem and not automap) or (not conmem and  eprom);

 romwr <= '0' when wr ='0' and a(13)='0' and a(14)='0' and a(15)='0' and eprom='1' and conmem='1' else '1';
 
 ramoe <= rd or A(15) or A(14) or ( not A(13) and not mapram) or ( not A(13) and conmem) or (not conmem and not automap) or (not conmem and  eprom and not mapram);
 
 ramwr <= wr or A(15) or A(14) or not a(13) or (not conmem and mapram and bank3 ) or (not conmem and not automap) or (not conmem and  eprom and not mapram);

 romcs <= '1' when ((automap and not eprom) or (automap and mapram) or conmem )='1' else '0' ;
 
 --  
 -- Divide Automapping logic			  
 
 mapterm <= '1' when  A(15 downto 0) = x"0000"   or 
                      A(15 downto 0) = x"0008"   or 
                      A(15 downto 0) = x"0038"   or 
                      A(15 downto 0) = x"0066"   or 
                      A(15 downto 0) = x"04c6"   or 
                      A(15 downto 0) = x"0562"   else '0';

 map3DXX   <= '1' when A(15 downto 8) = "00111101"   else '0';		-- mappa 3D00 - 3DFF
 
 map1F00 <= '0' when A(15 downto 3) =   "0001111111111"  else '1';	-- 1ff8 - 1fff


 process(mreq)
  begin
  
   if falling_edge(mreq) then
		   if m1='0' then
				 mapcond <= mapterm or map3DXX or (mapcond and map1F00);
				 automap <= mapcond or map3DXX;
		  end if;
	end if;	  
 end process; 

 mapcondout <= mapcond;
 
-- divide control port
												
divideio <='0' when iorq='0' and wr='0' and M1='1' and address = divide_control_port else '1';  	
	
process(divideio,poweron)
 begin
--		if poweron ='0' then						-- originally by M.Prato
		if poweron ='0' or reset = '0' then	-- patch by valerium
		
			bank   <= "000000";
			mapram <= '0';
			conmem <= '0';
			
		elsif	rising_edge(divideio) then
		
			bank(5 downto 0) <= D(5 downto 0);
			mapram           <= D(6) or mapram;
		   conmem          <= D(7);
		end if;
	
end process;	
			
-- ram banks 

 bankout(0) <= bank(0) or not A(13);
 bankout(1) <= bank(1) or not A(13);
 bankout(2) <= bank(2) and    A(13);
 bankout(3) <= bank(3) and    A(13);
 bankout(4) <= bank(4) and    A(13);
 bankout(5) <= bank(5) and    A(13);

-- SD CS signal management
zxmmcio <= '0' when  address = zxmmc_control_port and iorq='0' and m1='1' and wr ='0' else '1';


	process(reset, zxmmcio)
	begin
		if reset = '0' then
				card(0) <= '1';
				card(1) <= '1';
		
		elsif rising_edge(zxmmcio) then
		
				card(0)  <= D(0);
				card(1)  <= D(1);
		
		end if;
	end process;



-- spi transmission/reception

	-- Update transmission state
	process(clock, reset)
	begin
		if reset = '0' then
			transState <= IDLE;
			TState <= (others => '0');
			fromSDByte <= (others => '1');
			toSDByte <= (others => '1');
			toCPUByte <= (others => '1');

		elsif falling_edge(clock) then
			case transState is
			
			when IDLE => -- Intercept a new transmission request (port 0x3F)
				if address = zxmmc_spi_port and iorq='0' and m1='1' then -- If there is a transmission request, prepare to SAMPLE the databus
					transState <= SAMPLE;
				end if;
			
			when SAMPLE =>
					if wr = '0' then -- If it is a SEND request, sample the CPU data bus
						toSDByte <= D;
					end if;
					transState <= TRANSMIT; -- then start the transmission
			
			when TRANSMIT =>
					TState <= TState + 1;

             if TState < 15 then
				 
					if TState(0) = '1' then
						toSDByte   <= toSDByte(6 downto 0)&'1';
						fromSDByte <= fromSDByte(6 downto 0)& spi_datain;
					end if;
             else 
				if TState = 15 then -- transmission is completed; intercept if there is a new transmission request
						if address = zxmmc_spi_port and iorq='0' and m1='1'and wr='0' then							
								toSDByte <= D;
							   transState <= TRANSMIT;
						else -- else we'll go in IDLE state.
							transState <= IDLE;
						--	TState <= "0000";
						end if;
						toCPUByte <= fromSDByte(6 downto 0)& spi_datain;
					end if;
             end if;
			when OTHERS =>
				null;
			end case;
		end if;
		  -- SPI SD Card pins
	SPI_clock <= TState(0);
	spi_dataout <= toSDByte(7);
	end process;
	
  

 D <= toCPUByte when (address = zxmmc_spi_port) and (iorq = '0') and (rd = '0') and m1='1' else "ZZZZZZZZ";


end Behavioral;

