----------------------------------------------------------------------------------
-- 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;
				KeyLED		: out STD_LOGIC;
				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
				SPI_KEY		: in  STD_LOGIC_VECTOR (7 downto 0); -- Row in Bit 0 bis 3, Col in 4 bis 6, Enable Bit 7
				SPI_KEYENA	: in  STD_LOGIC;
				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 spi_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);

-- Umsetzung PS/2 (erste Hälfte) oder ASCII (zweite Hälfte) auf Acorn-Scancodes
type rom_type is array (0 to 255) of std_logic_vector (7 downto 0);
constant ScanROM : rom_type :=
( -- 00    01    02    03    04    05    06    07     08    09    0A    0B    0C    0D    0E    0F
	x"FF",x"FF",x"FF",x"05",x"03",x"01",x"02",x"FF", x"FF",x"FF",x"08",x"06",x"04",x"FF",x"FF",x"FF", -- 0x ab hier PS/2-auf-Atom
	x"FF",x"FF",x"FF",x"FF",x"FF",x"40",x"12",x"FF", x"FF",x"FF",x"51",x"58",x"36",x"54",x"11",x"FF", -- 1x
	x"FF",x"34",x"53",x"33",x"32",x"29",x"10",x"FF", x"FF",x"09",x"55",x"31",x"57",x"59",x"28",x"FF", -- 2x
	x"FF",x"43",x"35",x"49",x"30",x"52",x"27",x"FF", x"FF",x"FF",x"44",x"47",x"56",x"26",x"25",x"FF", -- 3x
	x"FF",x"21",x"46",x"48",x"42",x"13",x"24",x"FF", x"FF",x"39",x"38",x"45",x"22",x"41",x"20",x"FF", -- 4x
	x"FF",x"FF",x"23",x"FF",x"08",x"FF",x"FF",x"FF", x"FF",x"FF",x"16",x"06",x"FF",x"07",x"FF",x"FF", -- 5x
	x"FF",x"FF",x"FF",x"FF",x"FF",x"FF",x"14",x"FF", x"FF",x"12",x"FF",x"29",x"26",x"FF",x"FF",x"FF", -- 6x
	x"13",x"39",x"11",x"28",x"27",x"25",x"50",x"05", x"FF",x"FF",x"10",x"FF",x"FF",x"24",x"FF",x"FF", -- 7x

	x"FF",x"FF",x"FF",x"07",x"FF",x"FF",x"FF",x"FF", x"14",x"15",x"FF",x"FF",x"FF",x"16",x"FF",x"FF", -- 0 -- 8x ab hier ASCII-auf-Atom
	x"FF",x"FF",x"FF",x"FF",x"FF",x"FF",x"FF",x"FF", x"FF",x"FF",x"FF",x"50",x"FF",x"FF",x"FF",x"FF", -- 1 -- 9x wenn Shift nötig, +$80
	x"FF",x"92",x"91",x"90",x"A9",x"A8",x"A7",x"A6", x"A5",x"A4",x"A2",x"FF",x"21",x"20",x"39",x"38", -- 2 -- Ax
	x"13",x"12",x"11",x"10",x"29",x"28",x"27",x"26", x"25",x"24",x"23",x"22",x"A1",x"A0",x"B9",x"B8", -- 3 -- Bx
	x"37",x"36",x"35",x"34",x"33",x"32",x"31",x"30", x"49",x"48",x"47",x"46",x"45",x"44",x"43",x"42", -- 4 -- Cx
	x"41",x"40",x"59",x"58",x"57",x"56",x"55",x"54", x"53",x"52",x"51",x"08",x"07",x"06",x"05",x"FF", -- 5 -- Dx
	x"B7",x"B6",x"B5",x"B4",x"B3",x"B2",x"B1",x"B0", x"C9",x"C8",x"C7",x"C6",x"C5",x"C4",x"C3",x"C2", -- 6 -- Ex
	x"C1",x"C0",x"D9",x"D8",x"D7",x"D6",x"D5",x"D4", x"D3",x"D2",x"D1",x"88",x"87",x"86",x"86",x"14"  -- 7 -- Fx
);

begin


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) := KbdCol;
					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"BD") then -- Input from AVR
			RAM_WEn<='1';	
			if (CPU_RDn='0') then
				if ps2_keydown='1' then
					cpu_data_temp:= PS2_CODE;	-- Lesen von Keyboard-Scancode
				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_keydown <= SPI_KEYENA; -- vom Fifo, war: '0' when (SPI_KEY=x"00") else '1'; -- active high!
spi_ctrl <= '0' when ((SPI_KEY < x"20") and (spi_keydown ='1')) else '1'; -- active low wenn Control-Codes unterhalb "Space"

KbdScanConvTemp <= PS2_CODE when (spi_keydown='0') else (SPI_KEY or x"80"); -- externer Befehl (Atom-Scancode), wenn dieser >0
KbdScanConv <= ScanROM(conv_integer(KbdScanConvTemp));

spi_shift <=  (not KbdScanConv(7)) when (spi_keydown ='1') else '1'; -- wenn Bit 7 gesetzt ist, Shift-Taste "drücken"

KbdRowIn <= KbdScanConv(3 downto 0);
KbdColIn <= KbdScanConv(6 downto 4);
KbdColMask <=	"111110" when KbdColIn = "000" else
					"111101" when KbdColIn = "001" else
					"111011" when KbdColIn = "010" else
					"110111" when KbdColIn = "011" else
					"101111" when KbdColIn = "100" else
					"011111" when KbdColIn = "101" else
					"111111";
KbdCol <= KbdColMask	when (((ps2_keydown or spi_keydown)='1') and (KbdRow = KbdRowIn)) else "111111";
kbd_ctrl <= ps2_ctrl when (ps2_keydown='1') else (spi_ctrl); -- active low!
kbd_shift <= ps2_shift when (ps2_keydown='1') else (spi_shift); -- 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
				KeyLED<= '0';
				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';
				KeyLED<= '1';
			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 bei 4 MHz CLK_X4, 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;

