----------------------------------------------------------------------------------
-- Company: 
-- Engineer: Carsten Meyer
-- 
-- Create Date:  17:17:17 04/27/2008 
-- Design Name: SRAM Interface für AutoIncrement-LoadCore
-- Module Name:  BAIC_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;
--		BUS_DISABLE	: 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;
		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);
		-- 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;
		CURSX	: out STD_LOGIC_VECTOR (7 downto 0); 
		CURSY	: out STD_LOGIC_VECTOR (7 downto 0); 
		COLOR	: out STD_LOGIC_VECTOR (3 downto 0); 
		PORT3CX_WR	: out STD_LOGIC; 
		PORT3CX	: out STD_LOGIC_VECTOR (7 downto 0); 
		PORT3CX_SEL	: out STD_LOGIC_VECTOR (3 downto 0); 
		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); 
--		PORT344	: out STD_LOGIC_VECTOR (7 downto 0); 
--		PORT345	: out STD_LOGIC_VECTOR (7 downto 0); 
--		PORT346	: out STD_LOGIC_VECTOR (7 downto 0); 
--		PORT347	: 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);
		PIN3C0	: in  STD_LOGIC_VECTOR (7 downto 0);
		IO600_WEn	: out STD_LOGIC;
		IO600_RDn	: out 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 BUS_DISABLE_delayed	: 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";

begin

-- Ports $3C0 bis $3CF, dez. 960 bis 975
PORT3CX_WR <='1' when (CPU_ADR(15 downto 4) = x"03C") and (CPU_WRn='0') else '0';	-- Write Strobe für 380er Ports
--O <= A when (X > 10) else
--	B when (X < 0) else
--	C;

RAM_DATAz <= LC_DATA(7 downto 0) when (LC_WEn='0' and LC_ENA='1') else	-- LC schreibt
				CPU_DATAi when (CPU_WRn='0' and LC_ENA='0') else	-- CPU schreibt
				(others => 'Z');	-- CPU liest

process(CPU_ADR, LC_ADR, CPU_DATAi, CPU_RDn, CPU_WRn, LC_ENA, LC_WEn, CPU_RST) --, BUS_DISABLE)

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_WEn<='1';			-- Schreiben sperren
			RAM_OEn<=LC_OEn;
		else
			RAM_OEn<='1';			-- Lesen sperren
			RAM_WEn<=LC_WEn;	-- Schreiben zulassen
		end if;
		VGA_WR <= '0';
		COMout<=x"00";
		INTstr<=x"00";
		bank<="000";
--	elsif LC_ENA='0' and BUS_DISABLE='1' then
--		RAM_OEn<='1';			-- Lesen sperren
--		RAM_WEn<='1';			-- Schreiben sperren
--		RAM_DATAz <= (others => 'Z');
--		RAM_ADR <= (others => 'Z');
	else -- rising_edge geht nicht, asynchroner Prozess!
		IRQ_ACK <= '0';
		RAM_ADR(15 downto 0) <= CPU_ADR;
		VGA_WR <= '0';
		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;			
			CPU_DATAo <= RAM_DATAz;
		elsif CPU_ADR(15 downto 8) = x"03" then -- innerhalb PIO-Bereich
		-- 8255-Emulation mit drei fest eingestellten Ports, Port C geteilt: LowNibble Out, HiNibble In
			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_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(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) <= '1'; -- REPT Key
					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"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"30" then -- Cursor-Port X
				if CPU_WRn='0' then	-- Schreiben auf Port
					CURSX<=CPU_DATAi;
				end if;			
			elsif CPU_ADR(7 downto 0) = x"31" then -- Cursor-Port Y
				if CPU_WRn='0' then	-- Schreiben auf Port
					CURSY<=CPU_DATAi;
				end if;			
			elsif CPU_ADR(7 downto 0) = x"33" then -- Color Text
				if CPU_WRn='0' then	-- Schreiben auf Port
					COLOR<=CPU_DATAi(3 downto 0);
				end if;			
				
-- 8 interne 340er Ports, 4 mit Eingängen
			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;
-- weitere Ports nach Bedarf				
--			elsif CPU_ADR(7 downto 0) = x"44" then -- Port $340 usw.
--				if CPU_WRn='0' then	-- Schreiben auf Port
--					PORT344<=CPU_DATAi;
--				end if;			
--			elsif CPU_ADR(7 downto 0) = x"45" then -- Port $340 usw.
--				if CPU_WRn='0' then	-- Schreiben auf Port OUT
--					PORT345<=CPU_DATAi;
--				end if;	
--			elsif CPU_ADR(7 downto 0) = x"46" then -- Port $340 usw.
--				if CPU_WRn='0' then	-- Schreiben auf Port
--					PORT346<=CPU_DATAi;
--				end if;			
--			elsif CPU_ADR(7 downto 0) = x"47" then -- Port $340 usw.
--				if CPU_WRn='0' then	-- Schreiben auf Port
--					PORT347<=CPU_DATAi;
--				end if;
-- 16 Portadressen für Picoblaze-Core			
			elsif CPU_ADR(7 downto 4) = x"C" then -- Ports $3Cx mit gelatchter Adresse
				if CPU_WRn='0' then	-- Schreiben auf Port
					PORT3CX<=CPU_DATAi;
					PORT3CX_SEL<=CPU_ADR(3 downto 0);
				end if;			
				if (CPU_RDn='0') then
					CPU_DATAo <= PIN3C0;	-- Lesen von Port IN
				end if;
			end if;
		elsif CPU_ADR(15 downto 14) = "11" then	-- ab Block $C000 schreibgeschützt
			RAM_OEn <= CPU_RDn;			
			CPU_DATAo <= RAM_DATAz;
			RAM_ADR(18 downto 16) <= "000";
		else	-- normaler RAM-Bereich
			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_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
--		BUS_DISABLE_delayed<=BUS_DISABLE;
		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;

