----------------------------------------------------------------------------------
-- Company: 
-- Engineer: Carsten Meyer
-- 
-- Create Date:  17:17:17 04/27/2008 
-- Design Name: SRAM Interface für AutoIncrement-LoadCore
-- Module Name:  MPX18 - Behavioral 
-- Project Name: ct-Lab AsteroidsClock
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity atom_glue is
    Port ( 	CLK_X4		: in  STD_LOGIC;
				RAM_ADR		: out STD_LOGIC_VECTOR (18 downto 0);
				CPU_ADR		: in  STD_LOGIC_VECTOR (18 downto 0);
				LC_ADR		: in  STD_LOGIC_VECTOR (18 downto 0);
				CPU_DATAo	: out STD_LOGIC_VECTOR (7 downto 0); 
				CPU_DATAi	: in  STD_LOGIC_VECTOR (7 downto 0); 
				CPU_RST		: in  STD_LOGIC;
				CPU_RDn		: in  STD_LOGIC;
				CPU_WRn		: in  STD_LOGIC;
				LC_WRn		: in  STD_LOGIC;
				RAM_OEn		: out STD_LOGIC;
				RAM_WEn		: out STD_LOGIC;
				VGA_WR		: out STD_LOGIC;
				IRQ_ACK		: out STD_LOGIC;	-- IRQ Acknowledge bei Aufrufen des IRQ-Vektors
				IRQ_CLR		: out STD_LOGIC;  -- IRQ-Clear in der IRQvec-Routine
				LC_ENA		: in  STD_LOGIC;
				LC_DATA  	: in  std_logic_vector (31 downto 0);
				RAM_DATAz	: inout  std_logic_vector (7 downto 0);
				CURSX			: out STD_LOGIC_VECTOR (7 downto 0) := x"00";
				CURSY			: out	STD_LOGIC_VECTOR (7 downto 0) := x"00";
				PBE00o		: out STD_LOGIC_VECTOR (7 downto 0); -- Ausgabe-Port auf $B600
				PBE00i		: in  STD_LOGIC_VECTOR (7 downto 0); -- Eingabe-Port auf $B700
				PBF00i		: in  STD_LOGIC_VECTOR (7 downto 0); -- RegSel-Port auf $BF00
				PBF00o		: out STD_LOGIC_VECTOR (7 downto 0); -- Flag-Port für F_Int
				PBC00i		: in  STD_LOGIC_VECTOR (7 downto 0); -- Row in Bit 0 bis 3, Col in 4 bis 6, Enable Bit 7
				PS2_CODE		: in  STD_LOGIC_VECTOR (7 downto 0); -- PS/2-Scancodes
				PS2_DWN		: in  STD_LOGIC;
				PS2_UP		: in  STD_LOGIC
			 );

end atom_glue;

architecture behave of atom_glue is

signal AuxC		: STD_LOGIC_VECTOR (3 downto 0) := x"0"; -- Port C Bit 0 bis 3, Aux und Speaker
signal GraphMode	: STD_LOGIC_VECTOR (7 downto 4) := x"0"; -- Grafik-Modus des 6847
signal KbdRow		: STD_LOGIC_VECTOR (3 downto 0) := "0000";
signal KbdCol		: STD_LOGIC_VECTOR (5 downto 0) := "111111";
signal KbdColMask	: STD_LOGIC_VECTOR (5 downto 0) := "111111";
signal KbdRowIn	: STD_LOGIC_VECTOR (3 downto 0) := "0000";
signal KbdColIn	: STD_LOGIC_VECTOR (2 downto 0) := "111";
signal KbdScanConv : STD_LOGIC_VECTOR (7 downto 0) := x"FF";
signal KbdScanConvTemp : STD_LOGIC_VECTOR (7 downto 0) := x"FF";
signal kbd_shift	: STD_LOGIC := '1';
signal kbd_ctrl	: STD_LOGIC := '1';
signal ps2_shift	: STD_LOGIC := '1';
signal ps2_ctrl	: STD_LOGIC := '1';
signal spi_shift	: STD_LOGIC := '1';
signal spi_ctrl	: STD_LOGIC := '1';
signal ps2_keydown	: STD_LOGIC := '0';
signal s2400Hz	: STD_LOGIC;
signal c2400Hz: STD_LOGIC_VECTOR (10 downto 0);
signal s60Hz	: STD_LOGIC;
signal c60Hz: STD_LOGIC_VECTOR (5 downto 0);


begin

IRQ_ACK <= '1' when (CPU_ADR = x"FFFF" and CPU_RDn='0') else '0'; -- Zugriff auf Vector löscht IRQ-Anfrage
IRQ_CLR <= '1' when (CPU_ADR = x"FF70" and CPU_RDn='0') else '0'; -- Zugriff auf sonst unbenutzte ("C" in "ACORN") Adresse löscht IRQ-Latch


process(CPU_ADR, LC_ADR, CPU_DATAi, CPU_RDn, CPU_WRn, LC_ENA, LC_WRn, CPU_RST)

variable cpu_data_temp	: STD_LOGIC_VECTOR (7 downto 0);

begin
	if (LC_ENA='1' or CPU_RST='1') then -- AutoIncrement-Load durchschalten, gleichzeitig Reset
      RAM_ADR<=LC_ADR;
		RAM_OEn<='1';
		RAM_WEn<=LC_WRn;
		RAM_DATAz <= LC_DATA(7 downto 0);
		VGA_WR <='0';
		PBE00o<=x"00";
		PBF00o<=x"00";
		KbdRow<="0000";
   else -- 8255-Emulation mit drei fest eingestellten Ports, Port C geteilt: LowNibble Out, HiNibble In
		RAM_ADR<=CPU_ADR;
		if CPU_ADR(15 downto 13) = "100" then -- innerhalb VGA-Bereich x8000..x9FFF
			VGA_WR <=not CPU_WRn; -- active high
		else
			VGA_WR <='0';
		end if;
		if CPU_ADR(15 downto 8) = x"B0" then -- innerhalb PIO-Bereich
			RAM_DATAz <= (others => 'Z');
			if CPU_ADR(7 downto 0) = x"00" then  -- PIO 8255 Port PA0..7, Output
				if falling_edge(CPU_WRn) then
					GraphMode <= CPU_DATAi(7 downto 4);
					KbdRow <= CPU_DATAi(3 downto 0);
				end if;
				if (CPU_RDn='0') then	-- Lesen von Port A
					cpu_data_temp(7 downto 4) := GraphMode;
					cpu_data_temp(3 downto 0) := KbdRow;
				end if;
			elsif CPU_ADR(7 downto 0) = x"01" then  -- PIO 8255 Port PB0..7, nur Input
				if (CPU_RDn='0') then	-- Lesen von Port B
					cpu_data_temp(5 downto 0) := "111111";
					cpu_data_temp(6) := kbd_ctrl;
					cpu_data_temp(7) := kbd_shift;
				end if;
			elsif CPU_ADR(7 downto 0) = x"02" then  -- PIO 8255 Port PC0..7, Input und Output
				if falling_edge(CPU_WRn) then	-- Schreiben auf Port C
					AuxC <= CPU_DATAi(3 downto 0);
				end if;
				if (CPU_RDn='0') then	-- Lesen von Port C
					cpu_data_temp(7) := s60Hz;
					cpu_data_temp(6) := '1'; -- REPT Key
					cpu_data_temp(5) := '0'; -- Cassette input
					cpu_data_temp(4) := s2400Hz; -- 2,4 kHz input
					cpu_data_temp(3 downto 0) := AuxC;
				end if;
			end if;
		elsif (CPU_ADR(15 downto 8) = x"B4") then -- Cursor-Port X
			RAM_WEn<='1';			
			if falling_edge(CPU_WRn) then	-- Schreiben auf Port
				CURSX<=CPU_DATAi;
			end if;			
		elsif (CPU_ADR(15 downto 8) = x"B5") then -- Cursor-Port Y
			RAM_WEn<='1';			
			if falling_edge(CPU_WRn) then	-- Schreiben auf Port
				CURSY<=CPU_DATAi;
			end if;			
		elsif (CPU_ADR(15 downto 8) = x"BC") then -- Key-Simulator from AVR
			RAM_WEn<='1';	
			if (CPU_RDn='0') then
				cpu_data_temp:= PBC00i;	-- Lesen von SPI-ASCII
			end if;
		elsif (CPU_ADR(15 downto 8) = x"BD") then -- Input from AVR
			RAM_WEn<='1';	
			if (CPU_RDn='0') then
				if ps2_keydown='1' then
					cpu_data_temp(6 downto 0):= PS2_CODE(6 downto 0);	-- Lesen von Keyboard-Scancode
					cpu_data_temp(7):= not ps2_shift;	-- Shift-Bit in Scancode für Tabelle
				else
					cpu_data_temp:= x"00";
				end if;
			end if;
		elsif (CPU_ADR(15 downto 8) = x"BE") then -- Input from AVR
			RAM_WEn<='1';	
			if (CPU_RDn='0') then
				cpu_data_temp:= PBE00i;	-- Lesen von Port $B700
			end if;
			if falling_edge(CPU_WRn) then	-- Lesen und Schreiben auf Port $BE00
				PBE00o <=CPU_DATAi;
			end if;			
		elsif (CPU_ADR(15 downto 8) = x"BF") then -- IRQ-Source, IntEna
			RAM_WEn<='1';	
			if (CPU_RDn='0') then
				cpu_data_temp:= PBF00i;	-- Lesen von IRQ-Source
			end if;
			if falling_edge(CPU_WRn) then	-- Int-Flag, Schreiben auf Port $BF00
				PBF00o<=CPU_DATAi;
			end if;
		else
			if (CPU_ADR(15 downto 13) = "101") or (CPU_ADR(15 downto 14) = "11") then -- im ROM-Bereich AXXX oder C000..FFFF
				RAM_WEn<='1';			
			else
				RAM_WEn<=CPU_WRn;	-- auch VGA-Bereich als Shadow-RAM zugelassen
			end if;
			RAM_WEn<=CPU_WRn;			
			if CPU_WRn ='0' then
				RAM_DATAz <= CPU_DATAi;
				RAM_OEn<='1';
			else
				RAM_DATAz <= (others => 'Z');
				cpu_data_temp := RAM_DATAz;
				RAM_OEn<=CPU_RDn;
			end if;
		end if;
   end if;
	CPU_DATAo <= cpu_data_temp;
end process;

--
-- Atom-Scancode-Umsetzer, Masken für Column-Eingang
--
spi_ctrl <= '0' when (PBC00i < x"20")  else '1'; -- active low wenn Control-Codes unterhalb "Space"

kbd_ctrl <= ps2_ctrl when (ps2_keydown='1') else (spi_ctrl); -- active low!
kbd_shift <= ps2_shift when (ps2_keydown='1') else ('1'); -- active low!

-- PS/2-Tastatur ansatzweise auf Acorn-Scancodes umbelegen:

process(CLK_X4, LC_ENA)	-- LC_ENA auch als Reset; Achtung: gleicher Takt CLK_X4 wie ps2kbd-Modul!

begin
	if LC_ENA='1' then	-- auch als Reset
		ps2_keydown<= '0';
		ps2_shift<='1';	-- neg. Logik!
		ps2_ctrl<='1';	-- neg. Logik!		
	elsif rising_edge(CLK_X4) then -- gleicher Takt wie ps2kbd-Modul
		if PS2_DWN='1' then
			if (PS2_CODE=x"12") or (PS2_CODE=x"59") then	-- zwei Shift-Tasten
				ps2_shift<='0';	-- neg. Logik!
			elsif PS2_CODE=x"14" then	-- Ctrl-Taste
				ps2_ctrl<='0';	-- neg. Logik!
			else
				ps2_keydown<='1';
			end if;
		end if;
		if PS2_UP='1' then
			if (PS2_CODE=x"12") or (PS2_CODE=x"59") then	-- zwei Shift-Tasten
				ps2_shift<='1';	-- neg. Logik!
			elsif PS2_CODE=x"14" then	-- Ctrl-Taste
				ps2_ctrl<='1';	-- neg. Logik!
			else
				ps2_keydown<='0';
			end if;
		end if;
	end if;
end process;



process (CLK_X4) -- 2400 Hz Timer bei 4 MHz CLK_X4, für Port C Bit 4
begin
	if rising_edge(CLK_X4) then
		if (c2400Hz = "11010000000") then -- 416*4
			c2400Hz <= "00000000000";
		else
		  c2400Hz <= c2400Hz+1;
		end if;
		s2400Hz<=c2400Hz(10);
	end if;
end process;

process (s2400Hz) -- 60 Hz Timer für Port C Bit 4
begin
	if rising_edge(s2400Hz) then
		if (c60Hz = "110100") then -- 40 +12 für sym. Ausgang
			c60Hz <= "001100";
		else
		  c60Hz <= c60Hz+1;
		end if;
		s60Hz<=c60Hz(5);
	end if;
end process;

end behave;

