----------------------------------------------------------------------------------
-- Company: 
-- Engineer: Carsten Meyer
-- 
-- Create Date:	17:17:17 04/27/2008 
-- Design Name:	SRAM Interface für AutoIncrement-LoadCore und Glue-Logik
-- Module Name:	BASIC_Glue 
-- Project Name:	ct-Lab BASIC on Chip
----------------------------------------------------------------------------------

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 BASIC_glue is
	Port ( 	CLK_X4		: in  STD_LOGIC;
-- UART Interface
		Rx_full		: in  STD_LOGIC;
		Tx_full		: in  STD_LOGIC;
		Rx_valid	: in  STD_LOGIC;
		Rx_DATAi	: in  std_logic_vector (7 downto 0);
		Tx_write	: out STD_LOGIC;
		Rx_ack		: out STD_LOGIC;
		Rx_flush	: out STD_LOGIC;
		Tx_reset	: out STD_LOGIC;

		RAM_ADR		: out STD_LOGIC_VECTOR (18 downto 0);
		CPU_ADR		: in  STD_LOGIC_VECTOR (15 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_WEn		: in  STD_LOGIC;
		LC_OEn		: in  STD_LOGIC;	
		RAM_OEn		: out STD_LOGIC;
		RAM_WEn		: out STD_LOGIC;
		VGA_WR		: out STD_LOGIC;
		GCMD_WR		: out STD_LOGIC;
		TCMD_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
		Bd_SEL		: out STD_LOGIC_VECTOR (2 downto 0); 
		SER_SEL		: out STD_LOGIC;
		DMA			: out STD_LOGIC;	-- hier nur für TestLED
		LC_ENA		: in  STD_LOGIC;
		LC_DATA  	: in  std_logic_vector (31 downto 0);
		RAM_DATAz	: inout  std_logic_vector (7 downto 0);
		-- Acorn-Ports $B000 -> $0300, $B001 -> $0301, $B002 -> $0302 nur intern
		-- PIO_B/$0301 enthält Shift-Ctrl-Esc-Status
		-- PIO_C/$0302 Sync-Signale 60 Hz und 2400 Hz
		PS2_CODE	: in  STD_LOGIC_VECTOR (7 downto 0);	-- auf $0304, auch auf $0308 mit Shift im oberen Bit
		COMin			: in  STD_LOGIC_VECTOR (7 downto 0);	-- auf $0309 Lesen, war Eingabe-Port auf $B700
		COMout		: out STD_LOGIC_VECTOR (7 downto 0);	-- auf $0309 Schreiben, war Ausgabe-Port auf $B600
		IRQsrc		: in  STD_LOGIC_VECTOR (7 downto 0);	-- auf $030E Lesen, war RegSel-Port auf $BF00
		INTstr		: out STD_LOGIC_VECTOR (7 downto 0);	-- auf $030E Schreiben, war Flag-Port für F_Int
		ASCII			: in  STD_LOGIC_VECTOR (7 downto 0);  -- auf $0307 Lesen, ASCII-Code von SPI (Keyboard-Emulation)
		VSYNC			: in  STD_LOGIC;
		PORT340	: out STD_LOGIC_VECTOR (7 downto 0); 
		PORT341	: out STD_LOGIC_VECTOR (7 downto 0); 
		PORT342	: out STD_LOGIC_VECTOR (7 downto 0); 
		PORT343	: out STD_LOGIC_VECTOR (7 downto 0); 
		PIN340	: in  STD_LOGIC_VECTOR (7 downto 0);
		PIN341	: in  STD_LOGIC_VECTOR (7 downto 0);
		PIN342	: in  STD_LOGIC_VECTOR (7 downto 0);
		PIN343	: in  STD_LOGIC_VECTOR (7 downto 0);
		IO600_WEn	: out STD_LOGIC;
		IO600_RDn	: out STD_LOGIC;
		PLOTSYNC	: in  STD_LOGIC;
		PS2_DWN		: in  STD_LOGIC;
		PS2_UP		: in  STD_LOGIC
	 );

end BASIC_glue;

architecture behave of BASIC_glue is

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 kbd_esc	: STD_LOGIC := '1';
signal ps2_shift	: STD_LOGIC := '1';
signal ps2_ctrl	: STD_LOGIC := '1';
signal ps2_esc	: STD_LOGIC := '1';
signal spi_shift	: STD_LOGIC := '1';
signal spi_ctrl	: STD_LOGIC := '1';
signal ps2_keydown	: STD_LOGIC := '0';
--signal s4800Hz	: STD_LOGIC;
--signal s2400Hz	: STD_LOGIC;
--signal c4800Hz: STD_LOGIC_VECTOR (15 downto 0);
--signal PIO_A_s: STD_LOGIC_VECTOR (7 downto 0); -- können als Output-Port dienen
--signal PIO_B_s: STD_LOGIC_VECTOR (7 downto 0);
--signal PIO_C_s: STD_LOGIC_VECTOR (3 downto 0);
signal bank: STD_LOGIC_VECTOR (2 downto 0) :="000";
signal Mult_out : STD_LOGIC_VECTOR (31 downto 0) := x"00000000";
signal Mult_in1 : STD_LOGIC_VECTOR (15 downto 0) := x"0000";
signal Mult_in2 : STD_LOGIC_VECTOR (15 downto 0) := x"0000";


begin

Mult_Out <= Mult_in1 * Mult_in2;

process(CPU_ADR, LC_ADR, CPU_DATAi, CPU_RDn, CPU_WRn, LC_ENA, LC_WEn, CPU_RST)
-- variable cpu_data_temp	: STD_LOGIC_VECTOR (7 downto 0);

begin
	IRQ_CLR <= '0';
	if (LC_ENA='1' or CPU_RST='1') then -- AutoIncrement-Load durchschalten, gleichzeitig Reset
		RAM_ADR <= LC_ADR;
		if LC_OEn='0' then	-- es wird vom AVR gelesen
			RAM_DATAz <= (others => 'Z');
			RAM_WEn<='1';			-- Schreiben sperren
			RAM_OEn<=LC_OEn;
		else
			RAM_DATAz <= LC_DATA(7 downto 0);
			RAM_OEn<='1';			-- Lesen sperren
			RAM_WEn<=LC_WEn;	-- Schreiben zulassen
		end if;
		VGA_WR <= '0';
		SER_SEL <= '0';
		DMA <= '1';
		COMout<=x"00";
		INTstr<=x"00";
		bank<="000";
		Bd_SEL<="000";
		Mult_in1<=x"0000";
		Mult_in2<=x"0000";
	else 
		DMA <= '0';
		IRQ_ACK <= '0';
		RAM_ADR(15 downto 0) <= CPU_ADR;
		VGA_WR <= '0';
		GCMD_WR <= '0';
		TCMD_WR <= '0';
		RAM_DATAz <= (others => 'Z');	-- nur beim Schreiben freischalten!
		Tx_reset <='0';
		Rx_flush <='0';
		Tx_write <='0';
		Rx_ack <='1';
		IO600_WEn <= '1';	
		IO600_RDn <= '1';			
		RAM_WEn<='1';			
		RAM_OEn <= '1';	
	
		if CPU_ADR(15 downto 12) = "1011" then -- innerhalb VGA-Bereich xB000..xBFFF
			RAM_ADR(18 downto 16) <= "000";
			VGA_WR <= not CPU_WRn; -- active high
			RAM_WEn <= CPU_WRn;	
			RAM_OEn <= CPU_RDn;			
			if CPU_WRn ='0' then
				RAM_DATAz <= CPU_DATAi;
			else
				CPU_DATAo <= RAM_DATAz;
			end if;
		elsif CPU_ADR(15 downto 8) = x"03" then -- innerhalb internem IO-Bereich
--			if CPU_ADR(7 downto 0) = x"00" then  -- PIO 8255 Port PA0..7, Color Byte
--				if CPU_WRn='0' then	-- Schreiben auf Port C
--					PIO_A_s <= CPU_DATAi(7 downto 0);
--				end if;
--				if (CPU_RDn='0') then	-- Lesen von Port B
--					CPU_DATAo <= PIO_A_s;
--				end if;
--			elsif CPU_ADR(7 downto 0) = x"01" then  -- PIO 8255 Port PB0..7, nur Input
			if CPU_ADR(7 downto 0) = x"01" then  -- PIO 8255 Port PB0..7, nur Input
--				if CPU_WRn='0' then	-- Schreiben auf Port C
--					PIO_B_s <= CPU_DATAi(7 downto 0);
--				end if;
				if (CPU_RDn='0') then	-- Lesen von Port B
--					CPU_DATAo(4 downto 0) <= PIO_B_s(4 downto 0);
					CPU_DATAo(4 downto 0) <= "00000";
					CPU_DATAo(5) <= kbd_esc;
					CPU_DATAo(6) <= kbd_ctrl;
					CPU_DATAo(7) <= kbd_shift;
				end if;
			elsif CPU_ADR(7 downto 0) = x"02" then  -- PIO 8255 Port PC0..7, Input und Output
--				if CPU_WRn='0' then	-- Schreiben auf Port C
--					PIO_C_s <= CPU_DATAi(3 downto 0);
--				end if;
				if (CPU_RDn='0') then	-- Lesen von Port C
					CPU_DATAo(7) <= VSYNC;
					CPU_DATAo(6) <= PLOTSYNC; -- High während Grafik-Frame vertikal ON
					CPU_DATAo(5 downto 0) <= "000000";
--					CPU_DATAo(5) <= '0'; -- Cassette input
--					CPU_DATAo(4) <= s2400Hz; -- 2,4 kHz input
--					CPU_DATAo(3 downto 0) <= PIO_C_s;
				end if;
			elsif CPU_ADR(7 downto 0) = x"03" then -- Bank Select $0303
				if CPU_WRn='0' then	-- Schreiben auf Port
					bank<=CPU_DATAi(2 downto 0);
				end if;			
			elsif CPU_ADR(7 downto 0) = x"04" then  -- Port x0304, nur Input von PS/2
				if (CPU_RDn='0') then
					if ps2_keydown='1' then
						CPU_DATAo <= PS2_CODE;	-- Lesen von Keyboard-Scancode
					else
						CPU_DATAo <= x"00";
					end if;
				end if;
--			elsif CPU_ADR(7 downto 0) = x"05" then -- war Cursor-Port X
--				if CPU_WRn='0' then	-- Schreiben auf Port
--					CURSX<=CPU_DATAi;
--				end if;			
--			elsif CPU_ADR(7 downto 0) = x"06" then -- Cursor-Port Y
--				if CPU_WRn='0' then	-- Schreiben auf Port
--					CURSY<=CPU_DATAi;
--				end if;			
			elsif CPU_ADR(7 downto 0) = x"07" then -- Key-Simulator from AVR
				if (CPU_RDn='0') then
					CPU_DATAo <= ASCII;	-- Lesen von SPI-ASCII
				end if;
			elsif CPU_ADR(7 downto 0) = x"08" then -- Input from Kbd
				if (CPU_RDn='0') then
					if ps2_keydown='1' then
						CPU_DATAo (6 downto 0)<= PS2_CODE(6 downto 0);	-- Lesen von Keyboard-Scancode
						CPU_DATAo (7)<= not ps2_shift;	-- Shift-Bit in Scancode für Tabelle
					else
						CPU_DATAo <= x"00";
					end if;
				end if;
			elsif CPU_ADR(7 downto 0) = x"09" then -- Input from AVR
				if (CPU_RDn='0') then
					CPU_DATAo <= COMin;	-- Lesen von Port $0309
				end if;
				if CPU_WRn='0' then	-- Schreiben auf Port $0309
					COMout <=CPU_DATAi;
				end if;			
			elsif CPU_ADR(7 downto 0) = x"0D" then -- IRQ-Source, IntEna
				if (CPU_RDn='0') then
					CPU_DATAo <= IRQsrc;	-- Lesen von IRQ-Source
				end if;
				if CPU_WRn='0' then	-- Int-Flag F_INT
					INTstr<=CPU_DATAi;
				end if;
			elsif CPU_ADR(7 downto 0) = x"0E" then -- Zugriff auf Vector löscht IRQ-Anfrage
				if (CPU_RDn='0') then
					IRQ_ACK <= '1';	-- Lesen von IRQ-Source
				end if;
			elsif CPU_ADR(7 downto 0) = x"0F" then -- Clear IRQ Latch
				IRQ_CLR <= not CPU_RDn;
				
-- Ken Chapmans UART, 16 Byte FIFO ab $0310
			elsif CPU_ADR(7 downto 0) = x"10" then -- UART Datenregister $310=784
				if (CPU_RDn='0') then
					CPU_DATAo <= Rx_DATAi;
					Rx_ack <='0';	-- nächstes Byte für nächstes Lesen bereitstellen
				end if;
				if (CPU_WRn='0') then	-- Schreiben auf Port
					Tx_write <='1';
				end if;
--			elsif CPU_ADR(7 downto 0) = x"11" then -- UART Rx_ack-Register
--				if ((CPU_RDn='0') or (CPU_WRn='0')) then	
--					-- Lesen oder schreiben ACK, holt nächstes Byte aus FIFO
--					Rx_ack <='1';
--				end if;
			elsif CPU_ADR(7 downto 0) = x"12" then -- UART Rx-Statusregister $312=786
				if (CPU_RDn='0') then	-- Lesen 
					CPU_DATAo(7) <= Rx_valid;
					CPU_DATAo(6) <= Rx_full;
					CPU_DATAo(5 downto 0) <= "000000";
				end if;
			elsif CPU_ADR(7 downto 0) = x"13" then -- UART Tx-Statusregister $313=787
				if (CPU_RDn='0') then	-- Lesen 
					CPU_DATAo(7) <= '1';
					CPU_DATAo(6) <= Tx_full;
					CPU_DATAo(5 downto 0) <= "000000";
				end if;
			elsif CPU_ADR(7 downto 0) = x"14" then -- UART Rx-Clear-Register $314=788
				if (CPU_WRn='0') then	
					-- Schreiben Flush Rx-FIFO
					Rx_flush <='1';
				end if;
			elsif CPU_ADR(7 downto 0) = x"15" then -- UART Tx-Clear-Register $315=789
				if (CPU_WRn='0') then	
					-- Schreiben Reset Tx-FIFO
					Tx_reset <='1';
				end if;
			elsif CPU_ADR(7 downto 0) = x"1E" then -- UART PortSelect-Register $31F=799
				if (CPU_WRn='0') then	
					-- Serial Baud Select, 0 = 38400, 1 = 19200, 2 = 9600 ... 7 = 300 Bd
					Bd_SEL<=CPU_DATAi(2 downto 0);
				end if;
			elsif CPU_ADR(7 downto 0) = x"1F" then -- UART PortSelect-Register $31F=799
				if (CPU_WRn='0') then	
					-- Serial Port Select, 0 = OptoBusCore, 1 = SerBusCore
					SER_SEL <= CPU_DATAi(0);	
				end if;

-- extern end-dekodiert, Grafik- und Text-Register
			elsif CPU_ADR(7 downto 4) = x"2" then -- 16 Grafik-Register $32x
				-- Schreiben Grafik-Unit GTEXT
				GCMD_WR <= not CPU_WRn;
			elsif CPU_ADR(7 downto 4) = x"3" then -- 16 Text-Register $32x
				-- Schreiben Grafik-Unit GTEXT
				TCMD_WR <= not CPU_WRn;
				
-- interne Ports zur allgemeinen Verwendung				
			elsif CPU_ADR(7 downto 0) = x"40" then -- Port $340 usw.
				if CPU_WRn='0' then	-- Schreiben auf Port
					PORT340<=CPU_DATAi;
				end if;			
				if (CPU_RDn='0') then
					CPU_DATAo <= PIN340;	-- Lesen von Port IN
				end if;				
			elsif CPU_ADR(7 downto 0) = x"41" then -- Port $340 usw.
				if CPU_WRn='0' then	-- Schreiben auf Port OUT
					PORT341<=CPU_DATAi;
				end if;	
				if (CPU_RDn='0') then
					CPU_DATAo <= PIN341;	-- Lesen von Port IN
				end if;				
			elsif CPU_ADR(7 downto 0) = x"42" then -- Port $340 usw.
				if CPU_WRn='0' then	-- Schreiben auf Port
					PORT342<=CPU_DATAi;
				end if;			
				if (CPU_RDn='0') then
					CPU_DATAo <= PIN342;	-- Lesen von Port IN
				end if;				
			elsif CPU_ADR(7 downto 0) = x"43" then -- Port $340 usw.
				if CPU_WRn='0' then	-- Schreiben auf Port
					PORT343<=CPU_DATAi;
				end if;			
				if (CPU_RDn='0') then
					CPU_DATAo <= PIN343;	-- Lesen von Port IN
				end if;				

-- 16 Bit unsigned Multiplizierer, 32-Bit-Ergebnis
			elsif CPU_ADR(7 downto 0) = x"E8" then -- Port $3E8 = POKE 1000 usw.
				if CPU_WRn='0' then	-- Schreiben auf Mult_in1 low
					Mult_in1(7 downto 0)<=CPU_DATAi;
				end if;			
				if (CPU_RDn='0') then
					CPU_DATAo <= Mult_out(7 downto 0);	-- Lesen von Mult_out0 LSB
				end if;				
			elsif CPU_ADR(7 downto 0) = x"E9" then -- Port $3E9 = POKE 1001
				if CPU_WRn='0' then	-- Schreiben auf Mult_in1 high
					Mult_in1(15 downto 8)<=CPU_DATAi;
				end if;	
				if (CPU_RDn='0') then
					CPU_DATAo <= Mult_out(15 downto 8);	-- Lesen von Mult_out1
				end if;				
			elsif CPU_ADR(7 downto 0) = x"EA" then
				if CPU_WRn='0' then	-- Schreiben auf Mult_in2 low
					Mult_in2(7 downto 0)<=CPU_DATAi;
				end if;			
				if (CPU_RDn='0') then
					CPU_DATAo <= Mult_out(23 downto 16);	-- Lesen von Mult_out2
				end if;				
			elsif CPU_ADR(7 downto 0) = x"EB" then
				if CPU_WRn='0' then	-- Schreiben auf Mult_in2 high
					Mult_in2(15 downto 8)<=CPU_DATAi;
				end if;			
				if (CPU_RDn='0') then
					CPU_DATAo <= Mult_out(31 downto 24);	-- Lesen von Mult_out3 MSB
				end if;				
			
-- ggf. weitere $3xx Ports hier vor "end if" einfügen			
			end if;
		elsif CPU_ADR(15 downto 14) = "11" then	-- ab Block $C000 schreibgeschützt
			VGA_WR <='0';
			RAM_OEn <= CPU_RDn;			
			CPU_DATAo <= RAM_DATAz;
			RAM_ADR(18 downto 16) <= "000";
		else	-- normaler RAM-Bereich und externes IO600
			VGA_WR <='0';
			CPU_DATAo <= RAM_DATAz;
			if CPU_ADR(15 downto 8) = x"06" then -- innerhalb externem IO-Bereich
				IO600_WEn <= CPU_WRn;	
				IO600_RDn <= CPU_RDn;
			else
				RAM_WEn <= CPU_WRn;	
				RAM_OEn <= CPU_RDn;			
			end if;
			if CPU_WRn ='0' then
				RAM_DATAz <= CPU_DATAi;	-- überschreibt oben gesetzten Tristate-Zustand
			end if;
			if CPU_ADR(15 downto 11) = "00000" then	-- $0000 bis $07FF = Zeropage, Stack und Buffer
				RAM_ADR(18 downto 16) <= "000";
			else
				RAM_ADR(18 downto 16) <= bank;	-- zwischen $0800 und $AFFF
			end if;
		end if;
	end if;
end process;

-- Scancode-Umsetzer, liefert getrennte Shift-, ESC- und CTRL-Signale

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

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(6 downto 0)="0010100" then	-- Ctrl-Tasten $14, $94
				ps2_ctrl<='0';	-- neg. Logik!
			elsif PS2_CODE=x"76" then	-- ESC-Taste
				ps2_esc<='0';	-- neg. Logik!
				ps2_keydown<='1';
			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(6 downto 0)="0010100" then	-- Ctrl-Tasten $14, $94
				ps2_ctrl<='1';	-- neg. Logik!
			elsif PS2_CODE=x"76" then	-- ESC-Taste
				ps2_esc<='1';	-- neg. Logik!
				ps2_keydown<='0';
			else
				ps2_keydown<='0';
			end if;
		end if;
	end if;
end process;


--process (CLK_X4) -- 4800 Hz Timer für 50 MHz CLK_X4, für Port C Bit 4
--begin
--	if rising_edge(CLK_X4) then
--		if (c4800Hz = x"28B0") then -- 50 MHz / 4800
--			c4800Hz <= x"0000";
--			s4800Hz <= '0';
--		else
--		  c4800Hz <= c4800Hz+1;
--			s4800Hz <= '1';
--		end if;
--	end if;
--end process;
--
--process (s4800Hz) -- 2400 Hz Teiler, symm. Tastverhältnis
--begin
--	if rising_edge(s4800Hz) then
--		s2400Hz<= not s2400Hz;
--	end if;
--end process;

end behave;

