--
--   Copyright (C) 2003 by J. Kearney, Bolton, Massachusetts
--
--   This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
--   This program is distributed in the hope that it will be useful, but
-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-- for more details.
--
--   You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
--

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;

library unisim;
use unisim.vcomponents.all;

use work.IOB_Config.ALL;
use work.VT52_cfg.ALL;

entity iob is
	port
	(clk_in: in std_logic;	-- 29.4912 MHz

	-- CPU bus interface
	cpu_ioclr_n : in std_logic;
	dx : inout std_logic_vector(0 to 11);
	clk_write_n_raw : in std_logic;			-- async
	cpu_read_n : in std_logic;
	cpu_sr_read_n : in std_logic;
	cpu_sr_write_n : in std_logic;
	clk_lxdar_n_raw : in std_logic;			-- async
	cpu_c0_n : out std_logic;
	cpu_c1_n : out std_logic;
	cpu_skip_n : out std_logic;
	cpu_intreq_n : out std_logic;
	cpu_intgrnt_n : inout std_logic;
	cpreq_n: inout std_logic;

	-- serial ports
	txd : out std_logic_vector(0 to NUMUARTS-1);
	rxd : in std_logic_vector(0 to NUMUARTS-1);

	-- LPT port
	lpt_ack: in std_logic;
	lpt_busy_n: in std_logic;
	lpt_paper_end_n: in std_logic;
	lpt_select_in_n: in std_logic;
	lpt_error: in std_logic;
	lpt_strobe: out std_logic;
	lpt_ddir: out std_logic;
	lpt_data: inout std_logic_vector(7 downto 0);
	lpt_init: out std_logic;
		
	-- Keyboard
	kb_clk: inout std_logic;
	kb_data: inout std_logic;

	-- VGA
	vga_RGB: out RGB;
	vga_HS: out std_logic;
	vga_VS: out std_logic;

	-- speaker
	spkr: out std_logic;

	-- real-time clock
	rtc_sclk: out std_logic;
	rtc_io: inout std_logic;
	rtc_rst: out std_logic;

	-- bits for custom applications
	iobits: inout std_logic_vector(0 to 35);

	-- special purpose
	reprogram: inout std_logic);	-- drive low to restart FPGA configuration

-- clock frequency and pin assignments, Xilinx-specific.
-- put here rather than UCF so they don't get lost
-- don't allow use of config pins for I/O by assigning to dummy signals

---- input clock
attribute period: string;
attribute period of clk_in : signal is "29.4912 MHz";

---- assign pins to match PCB
attribute loc: string;
attribute clock_buffer : string; 
attribute pullup : string;

attribute loc of clk_in : signal is "P88";
attribute loc of clk_lxdar_n_raw : signal is "P18";
attribute loc of clk_write_n_raw : signal is "P15";
attribute loc of cpu_c0_n : signal is "P79";
attribute loc of cpu_c1_n : signal is "P80";
attribute loc of cpu_intreq_n : signal is "P96";
attribute loc of cpu_ioclr_n : signal is "P87";
attribute loc of cpu_read_n : signal is "P78";
attribute loc of cpu_skip_n : signal is "P76";
attribute loc of cpu_sr_read_n : signal is "P77";
attribute loc of cpu_sr_write_n : signal is "P101";
attribute loc of dx : signal is "P122 P132 P129 P131 P67 P62" &
										  " P60 P57 P49 P46 P44 P39";
attribute loc of kb_clk : signal is "P138";
attribute pullup of kb_clk : signal is "yes";
attribute loc of kb_data : signal is "P133";
attribute pullup of kb_data : signal is "yes";
attribute loc of lpt_ack : signal is "P91";
attribute loc of lpt_busy_n : signal is "P121";
attribute loc of lpt_data : signal is "P66 P63 P59 P56 P51 P43 P47 P38";
attribute loc of lpt_ddir : signal is "P28";
attribute loc of lpt_error : signal is "P137";
attribute loc of lpt_init : signal is "P7";
attribute loc of lpt_paper_end_n : signal is "P136";
attribute loc of lpt_select_in_n : signal is "P124";
attribute loc of lpt_strobe : signal is "P10";
attribute loc of reprogram : signal is "P85";
attribute loc of rxd : signal is "P23 P26 P27";
attribute loc of txd : signal is "P11 P21 P115";
attribute loc of vga_hs : signal is "P117";
attribute loc of vga_rgb : signal is "P99 P126 P120";
attribute loc of vga_vs : signal is "P102";
attribute loc of cpu_intgrnt_n : signal is "P141";
attribute loc of cpreq_n : signal is "P113";
attribute loc of iobits : signal is "P12 P5 P3 P20 P103 P93 P100 P94 P19 " &
											"P13 P4 P6 P134 P114 P139 P116 P74 P64 " &
											"P42 P84 P83 P40 P48 P65 P130 P123 P140 " &
											"P118 P75 P41 P58 P86 P22 P29 P50 P54";
attribute loc of spkr : signal is "P95";
attribute loc of rtc_rst : signal is "P112";
attribute loc of rtc_sclk : signal is "P68";
attribute loc of rtc_io : signal is "P30";

end iob;

architecture RTL of iob is

	-- PosEdge: in this implementation, most processes are
	-- synchronous to clk, so this component is used to sample
	-- slower signals, and generates a 1-clock pulse on their rising edge
	component PosEdge
		port
		  (reset: in boolean;
			clk: in std_logic;
			inp: in std_logic;          
			outp: out std_logic);
	end component;

	component KL8JA is
		generic
			(r_addr: DevID;
		 	 t_addr: DevID;
			 stop_bits: integer;
			 data_bits: integer;
			 parity: ParitySel;
			 default_baud_sel: integer;
          instance: integer);
		port
			( -- clocks
			clk: in std_logic;
			brg: in unsigned(18 downto 0);

			--DEBUGIO: inout std_logic_vector(0 to 11);

			-- CPU bus interface
			reset : in boolean;
			IOTact: in boolean;
			IOTdev: in DevID;
			IOTcmd: in DevCmd;
			dx : inout std_logic_vector(0 to 11);
			cpu_write_n : in std_logic;
			clk_write_n : in std_logic;
			clk_lxdar_n : in std_logic;
			IOTread : in boolean;
			cpu_c0_n : out std_logic;
			cpu_c1_n : out std_logic;
			cpu_skip_n : out std_logic;

			-- communication
			IRQ: out std_logic;

			-- serial port
			txd : out std_logic;
			rxd : in std_logic;
			rts : out std_logic;
			cts : in std_logic
			);
	end component;

	component LC8E is
		generic
			(print_addr: DevID);
		port
			(reset: in boolean;							-- true when reset (IOCLR)
			 IOTact: in boolean;							-- true when in IOT (LXDAR)
			 IOTdev: in DevID;							-- latched IOT device code
			 IOTcmd: in DevCmd;							-- latched IOT command

		 	 cpu_write_n : in std_logic;
			 clk_write_n : in std_logic;
			 dx : inout std_logic_vector(7 downto 0);
			 cpu_skip_n : out std_logic;
			 pIRQ: out std_logic;

			 -- LPT port
			 lpt_ack: in std_logic;
			 lpt_busy_n: in std_logic;
			 lpt_paper_end_n: in std_logic;
			 lpt_select_in_n: in std_logic;
			 lpt_error: in std_logic;
			 lpt_strobe: buffer std_logic;
			 lpt_ddir: out std_logic;
			 lpt_data: inout std_logic_vector(7 downto 0);
			 lpt_init: out std_logic);
	end component;

	component VT52 is
		port
		  (-- addresses
			keyb_addr: in DevID;
		   print_addr: in DevID;

			-- clocks
			clk : in std_logic;
			clkdiv24: in std_logic;
			vidclk: in std_logic;

			--DEBUGIO: inout std_logic_vector(0 to 11);

			-- CPU bus interface
			reset : in boolean;
			IOTact: in boolean;
			IOTdev: in DevID;
			IOTcmd: in DevCmd;

			cpu_write_n : in std_logic;
			clk_write_n : in std_logic;
			IOTread : in boolean;
			dx : inout std_logic_vector(0 to 11);
			cpu_c0_n : out std_logic;
			cpu_c1_n : out std_logic;
			cpu_skip_n : out std_logic;
			IRQ: out std_logic;

			-- Keyboard
			kb_clk: inout std_logic;
			kb_data: inout std_logic;

			-- VGA
			vga_RGB: out std_logic_vector(0 to 2);
			vga_HS: out std_logic;
			vga_VS: out std_logic;
		 
		 	-- bell
		 	bell: out boolean;
		 
		   -- break
		   break: out boolean);
	end component;

	component RTC is
		generic
			(addr: DevID := RTCBASE);
		port
			(reset: in boolean;
			 clk: in std_logic;	-- system clk
			 timebase: in std_logic;	-- counting clock 4800 Hz

			 -- CPU bus interface
			 IOTact: in boolean;
			 IOTdev: in DevID;
			 IOTcmd: in DevCmd;

		 	 cpu_write_n : in std_logic;
			 clk_write_n : in std_logic;
			 dx11 : in std_logic;
			 cpu_skip_n : out std_logic;
			 IRQ: out std_logic);
	end component;

	component DS1302ClkCal is
		generic
			(addr: DevID := CLKCALBASE);
		port
			( -- clocks
			 clk: in std_logic;
			 bitclk: in std_logic;

			 -- CPU bus interface
			 reset : in boolean;
			 IOTact: in boolean;
			 IOTdev: in DevID;
			 IOTcmd: in DevCmd;

			 cpu_write_n : in std_logic;
			 clk_write_n : in std_logic;
			 IOTread : in boolean;
			 dx : inout std_logic_vector(0 to 11);
			 cpu_c0_n : out std_logic;
			 cpu_c1_n : out std_logic;
			 cpu_skip_n : out std_logic;

			 -- I/O
			 RST_n : out std_logic;
			 SCLK : out std_logic;
			 SIO : inout std_logic);
	end component;

attribute INIT : string;

-- registers & signals shared between all devices

signal cpu_ioclr: std_logic;
signal reset: boolean;							-- true when reset (IOCLR)
signal resetctr: integer range 0 to 7;

attribute INIT of resetctr: signal is "0";

signal clk_lxdar_n_unsync, clk_lxdar_n, cpu_lxdar_n: std_logic;
signal clk_write_n_unsync, clk_write_n, cpu_write_n: std_logic;

attribute CLOCK_SIGNAL : string;
attribute CLOCK_SIGNAL of clk_lxdar_n : signal is "yes";
attribute CLOCK_SIGNAL of clk_write_n : signal is "yes";

attribute USELOWSKEWLINES : string;
attribute USELOWSKEWLINES of cpu_lxdar_n : signal is "yes";
attribute USELOWSKEWLINES of cpu_write_n : signal is "yes";

signal IOTact: boolean;							-- true when in IOT (LXDAR)
signal IOTdev: DevID;							-- latched IOT device code
signal IOTcmd: DevCmd;							-- latched IOT command
signal IOTread: boolean;						-- true during IOT read cycle

signal clk, clk0, clkfb, clkdiv24: std_logic;
signal prescale: integer range 0 to 23;
signal brg: unsigned(18 downto 0);			-- baud rate generator
signal brg1: unsigned(9 downto 0);			-- /24 based rates
signal brg2: unsigned(15 downto 0);			-- /1 based rates

signal bell: boolean;							-- terminal bell
signal break: boolean;							-- terminal break

attribute clock_signal of clk : signal is "yes";
attribute clock_signal of clkdiv24 : signal is "yes";
attribute clock_signal of brg : signal is "yes";
attribute clock_signal of brg1 : signal is "yes";
attribute clock_signal of brg2 : signal is "yes";

signal uIRQ: unsigned(0 to NUMUARTS-1);	-- masked flags from the serial ports
signal uIRQ_all: std_logic;
signal pIRQ: std_logic;							-- masked flag from the printer port
signal vIRQ: std_logic;							-- masked flag from VT52
signal rtcIRQ: std_logic;						-- masked flag from RTC

-- dummy switch register registers & signals 

signal switchreg: std_logic_vector(0 to 11);
signal sr_write: std_logic;

-- system configuration and test signals

signal SPCunlock: std_logic;
signal VT52_kb, VT52_pr: DevID;
signal testdata: std_logic_vector(0 to 11);
signal testread: boolean;
signal reprogram_int: boolean;

signal init_counter: integer range 0 to 2;
attribute INIT of init_counter: signal is "0";

begin

-----------------------------------------------------------------------------------------
-- drive our internal clock through a clock DLL
-----------------------------------------------------------------------------------------

	clk1: clkdll port map (RST => cpu_ioclr, CLKIN => clk_in,
							  CLK0 => clk0, CLKFB => clk);

	cknet: BUFG port map (I => clk0, O => clk);

-----------------------------------------------------------------------------------------
-- generate our own reset pulse in addition to the one from the system,
-- particularly in case of reconfiguration
-----------------------------------------------------------------------------------------

reset <= (cpu_ioclr_n = '0') or (resetctr /= 7);

-- this depends on resetctr = 0 on configuration

process (clk, resetctr)
begin
	if rising_edge(clk) then
		if resetctr /= 7 then
			resetctr <= resetctr + 1;
		end if;
	end if;
end process;

-----------------------------------------------------------------------------------------
-- create non-clock versions of some clock nets, and synchronize them to
-- our system clock.  This is done because in some places we use their
-- edges, and others their levels, and FPGAs have separate routing for these
-- two types of signals
-----------------------------------------------------------------------------------------

lxdaribuf: IBUFG port map (I => clk_lxdar_n_raw, O => clk_lxdar_n_unsync);
writeibuf: IBUFG port map (I => clk_write_n_raw, O => clk_write_n_unsync);
lxdarbufg: BUFG port map (I => cpu_lxdar_n, O => clk_lxdar_n);
writebufg: BUFG port map (I => cpu_write_n, O => clk_write_n);

process (reset, clk, clk_lxdar_n_unsync, clk_write_n_unsync)
begin
	if reset then
		cpu_lxdar_n <= '1';
		cpu_write_n <= '1';
	elsif rising_edge(clk) then
		cpu_lxdar_n <= clk_lxdar_n_unsync;
		cpu_write_n <= clk_write_n_unsync;
	end if;
end process;

-----------------------------------------------------------------------------------------
-- utility and shared logic
-----------------------------------------------------------------------------------------

cpu_intgrnt_n <= 'Z';

-- common IOT decoding

cpu_ioclr <= not cpu_ioclr_n;


-- latch IOT device and command at start of LXDAR
process (clk_lxdar_n)
begin
	if falling_edge(clk_lxdar_n) then
		IOTdev <= unsigned(dx(3 to 8));
		IOTcmd <= unsigned(dx(9 to 11));
	end if;
end process;

IOTact <= (cpu_lxdar_n = '0');

-- here we stretch the read signal from when it is active
-- until the end of LXDAR; IM6120 wants IOT read data
-- up to end of LXDAR, not READ, according to the timing chart

process (reset, IOTact, clk, cpu_read_n)
begin
	if reset or not IOTact then
		IOTread <= false;
	elsif rising_edge(clk) then
		if cpu_read_n = '0' then
			IOTread <= true;
		end if;
	end if;
end process;

--- control IOTs: initiate reprogram, set VT52 address, tests

reprogram <= '0' when reprogram_int else 'Z';

  -- async read part of test logic
process (IOTread, IOTdev, IOTcmd, testdata)
begin
	if IOTread and (IOTdev = SPCBASE) then
		dx <= testdata;
	else
		dx <= (others => 'Z');
	end if;
end process;

  -- async write part of test logic
process (IOTact, IOTdev, cpu_write_n, dx, IOTcmd)
begin
	if IOTact and (IOTdev = SPCBASE) and (cpu_write_n = '0') then

		case IOTcmd is

			when SPCTESTSIGNALS =>
				if dx(9) = '1' then
					cpu_c0_n <= '0';
				else
					cpu_c0_n <= 'Z';
				end if;
				if dx(10) = '1' then
					cpu_c1_n <= '0';
				else
					cpu_c1_n <= 'Z';
				end if;
				if dx(11) = '1' then
					cpu_skip_n <= '0';
				else
					cpu_skip_n <= 'Z';
				end if;

			when SPCTESTBUS | SPCVERSION | SPCUNLOCKKEY =>
				cpu_c0_n <= '0';
				cpu_c1_n <= '0';
				cpu_skip_n <= 'Z';

			when others =>
				cpu_c0_n <= 'Z';
				cpu_c1_n <= 'Z';
				cpu_skip_n <= 'Z';

		end case;

	else
		cpu_c0_n <= 'Z';
		cpu_c1_n <= 'Z';
		cpu_skip_n <= 'Z';
	end if;
end process;

  -- sync write part of test logic, and config functions
process (reset, clk_write_n, dx)
begin
	if reset then

		reprogram_int <= false;
		SPCunlock <= '0';

	elsif rising_edge(clk_write_n) then

		-- the first time any write of any kind happens, init
		-- the VT52 port address.  We don't want this to happen
		-- on reset, because then we'd lose the console
		if init_counter = 0 then
			VT52_pr <= VT52PBASE;	-- initial addresses
			VT52_kb <= VT52KBASE;
			init_counter <= 1;
		end if;

		-- FPGA control functions are protected here:
		-- the configuration changing IOTs require that
		-- an SPCUNLOCKKEY IOT be done first
		if IOTact then
			SPCunlock <= '0';
		end if;

		if IOTact and (IOTdev = SPCBASE) then

			case IOTCmd is

				when SPCVERSION =>
					testdata <= std_logic_vector(to_unsigned(VERSION, dx'length));

				when SPCUNLOCKKEY =>
					if dx = O"5253" then
						SPCunlock <= '1';
					end if;

					-- configuration
					testdata(0 to 1) <= std_logic_vector(to_unsigned(NUMUARTS, 2));
					testdata(2 to 2) <= std_logic_vector(to_unsigned(boolean'pos(PARPTOBASE /= O"00"), 1));
					testdata(3 to 3) <= std_logic_vector(to_unsigned(boolean'pos(VT52KBASE /= O"00"), 1));
					testdata(4 to 4) <= std_logic_vector(to_unsigned(boolean'pos(PIOBASE /= O"00"), 1));
					testdata(5 to 5) <= std_logic_vector(to_unsigned(boolean'pos(RTCBASE /= O"00"), 1));
					testdata(6 to 6) <= std_logic_vector(to_unsigned(boolean'pos(CLKCALBASE /= O"00"), 1));
					testdata(7 to 7) <= std_logic_vector(to_unsigned(boolean'pos(XYBASE /= O"00"), 1));

					-- interrupts
					testdata( 8) <= pIRQ;
					testdata( 9) <= vIRQ;
					testdata(10) <= uIRQ_all;
					testdata(11) <= rtcIRQ;

				when SPCREPROG => 	-- initiate our own reprogramming
					if SPCunlock = '1' then
						reprogram_int <= (dx = O"1234");
					end if;

				when SPCSETCON =>
					if SPCunlock = '1' then
						VT52_kb  <= DevID(dx( 0 to  5));
						VT52_pr <= DevID(dx( 6 to 11));
					end if;

				when SPCTESTSIGNALS =>
					testdata <= O"5252";

				when SPCTESTBUS =>
					testdata <= not dx;

				when others =>
					null;

			end case;

		end if;
	end if;
end process;

---- bring CPU IRQ low when printer or serial ports request it
uIRQ_all <= '1' when uIRQ /= 0 else '0';

cpu_intreq_n <= '0' when pIRQ = '1' or
								 vIRQ = '1' or	 
								 uIRQ_all = '1' or
								 rtcIRQ = '1'
					 else 'Z';

-- baud rate generator for uarts

-- to simplify the circuit, we use one oscillator for both
-- the video and the serial clocks.  The frequency chosen
-- is one that's close to the required video dot clock and
-- also dividable down to max_baud_rate*16 with little or no
-- error (2% is the max we want).  As it happens, the fairly
-- standard 29.4912MHz oscillator meets both these criteria
-- pretty well.

-- The baud rate clock is 38400*16, which is the input clock / 24.
-- we then divide this by powers of two for the various baud rates;
-- the UARTs tap these dividers by indexing into the brg() array.
-- the /24 clock is also exposed for places that a slower clock
-- is useful, like delay counters.

process (reset, clk)
begin
	if reset then
		brg1 <= (others => '0');
		brg2 <= (others => '0');
		prescale <= 0;
		clkdiv24 <= '0';
	elsif rising_edge(clk) then

		brg2 <= brg2 + 1;

		if prescale = 23 then
			prescale <= 0;
			clkdiv24 <= '1';
			brg1 <= brg1 + 1;
		else
			prescale <= prescale + 1;
			clkdiv24 <= '0';
		end if;

	end if;
end process;

-- here the various rate dividers are mapped to the VT278 baud codes:
brg(0)  <= brg2(14);	-- 50 (56.25)
brg(1)  <= brg1(9);	-- 75
brg(2)  <= brg2(13);	-- 110 (112.5)
brg(3)  <= '0';		-- 134.5, not supported
brg(4)  <= brg1(8);	-- 150
brg(5)  <= brg1(7);	-- 300
brg(6)  <= brg1(6);	-- 600
brg(7)  <= brg1(5);	-- 1200
brg(8)  <= brg2(9);	-- 1800
brg(9)  <= '0';		-- 2000, not supported
brg(10) <= brg1(4);	-- 2400
brg(11) <= brg2(8);	-- 3600
brg(12) <= brg1(3);	-- 4800
brg(13) <= brg2(7);	-- 7200
brg(14) <= brg1(2);	-- 9600
brg(15) <= brg1(1);	-- 19200
brg(16) <= brg1(0);	-- 38400
brg(17) <= brg2(4);	-- 56400
brg(18) <= brg2(3);	-- 115200

-----------------------------------------------------------------------------------------
-- switch register dummy implementation - just a latch
-----------------------------------------------------------------------------------------

--process (cpu_sr_read_n, dx, switchreg)
--begin
--	if cpu_sr_read_n = '0' then
--		dx <= switchreg;
--	else
--		dx <= (others => 'Z');
--	end if;
--end process;

--sr_w_detect: PosEdge port map (reset, clk, cpu_sr_write_n, sr_write);

--process (clk, sr_write)
--begin
--	if rising_edge(clk) then
--		if sr_write = '1' then
--			switchreg <= dx;
--		end if;
--	end if;
--end process;

-----------------------------------------------------------------------------------------
-- KL8JA serial ports
-----------------------------------------------------------------------------------------

KL8JAs: for idx in 0 to NUMUARTS-1 generate

begin

	sp: KL8JA
		generic map
		(r_addr		=> UARTIBASE(idx),			-- settings from iob_cfg.vhd package
		 t_addr		=> UARTOBASE(idx),
		 data_bits	=> DataBitsSetting(idx),
		 stop_bits	=> StopBitsSetting(idx),
		 parity		=> ParitySetting(idx),
		 default_baud_sel
						=> BaudRateSetting(idx),

		 instance => idx)
		port map
		(clk => clk, reset => reset,
       --DEBUGIO => iobits(12 to 23),
		 brg => brg,
		 IOTact => IOTact, IOTdev => IOTdev, IOTread => IOTread,
		 IOTcmd => IOTcmd, dx => dx,
		 cpu_write_n => cpu_write_n, clk_write_n => clk_write_n,
		 clk_lxdar_n => clk_lxdar_n,
		 cpu_c0_n => cpu_c0_n, cpu_c1_n => cpu_c1_n, cpu_skip_n => cpu_skip_n,
		 IRQ => uIRQ(idx),
		 txd => txd(idx), rxd => rxd(idx),
		 rts => open, cts => '0');

end generate;

-----------------------------------------------------------------------------------------
-- LC8E printer interface with Centronics output
-----------------------------------------------------------------------------------------

parpt_if: if PARPTOBASE /= O"00" generate
signal lpt_strobe_i: std_logic;
signal dx_lpt: std_logic_vector(7 downto 0);
begin

lpt_strobe <= lpt_strobe_i;

-- output 7 bit ASCII to the printer.  A lot of software (like OS/8)
-- sets the upper bit, which results in funny characters on modern
-- printers.
lpt: for bit in 7 downto 0 generate
begin
	dx_lpt(bit) <= '0' when bit = 7 else dx(11 - bit);
end generate;

pp: LC8E
	generic map
		(print_addr => PARPTOBASE)
	port map
		(reset => reset, IOTact => IOTact,
		 cpu_write_n => cpu_write_n, clk_write_n => clk_write_n,
		 IOTdev => IOTdev, IOTcmd => IOTcmd,
		 dx => dx_lpt,
		 cpu_skip_n => cpu_skip_n, pIRQ => pIRQ,
		 lpt_ack => lpt_ack, lpt_busy_n => lpt_busy_n,
		 lpt_paper_end_n => lpt_paper_end_n,
		 lpt_select_in_n => lpt_select_in_n, lpt_error => lpt_error,
		 lpt_strobe => lpt_strobe_i, lpt_ddir => lpt_ddir,
		 lpt_data => lpt_data, lpt_init => lpt_init);

end generate;

-----------------------------------------------------------------------------------------
-- VT52 via KL8E interface
-----------------------------------------------------------------------------------------

vt52_if: if (VT52PBASE /= O"00") and (VT52KBASE /= O"00") generate
begin

vt: VT52
	port map
		(keyb_addr => VT52_kb, print_addr => VT52_pr,
		 reset => reset, clk => clk, clkdiv24 => clkdiv24, vidclk => clk,
		 --DEBUGIO => iobits(0 to 11),
		 IOTact => IOTact, IOTdev => IOTdev, IOTcmd => IOTcmd,
		 cpu_write_n => cpu_write_n, clk_write_n => clk_write_n,
		 IOTread => IOTread, dx => dx,
		 cpu_c0_n => cpu_c0_n, cpu_c1_n => cpu_c1_n,
		 cpu_skip_n => cpu_skip_n, IRQ => vIRQ,
		 kb_clk => kb_clk, kb_data => kb_data,
		 vga_RGB => vga_RGB, vga_HS => vga_HS, vga_VS => vga_VS,
		 bell => bell, break => break);

spkr <= brg(2) when bell else '0';
cpreq_n <= '0' when break else 'Z';

end generate;

-----------------------------------------------------------------------------------------
-- RTC IOTs
-----------------------------------------------------------------------------------------

rtc_if: if RTCBASE /= O"00" generate
begin

theclock: RTC
	generic map
		(addr => RTCBASE)
	port map
		(reset => reset, clk => clk,
		 timebase => BRG(0),
		 IOTact => IOTact, IOTdev => IOTdev, IOTcmd => IOTcmd,
		 cpu_write_n => cpu_write_n, clk_write_n => clk_write_n,
		 dx11 => dx(11),
		 cpu_skip_n => cpu_skip_n, IRQ => rtcIRQ);

end generate;

-----------------------------------------------------------------------------------------
-- Clock/Calendar IOTs
-----------------------------------------------------------------------------------------

clkcal_if: if CLKCALBASE /= O"00" generate
begin

theCalendar: DS1302ClkCal
	generic map
		(addr => CLKCALBASE)
	port map
		(clk => clk, bitclk => clkdiv24, reset => reset,
		 IOTact => IOTact, IOTread => IOTread,
		 IOTdev => IOTdev, IOTcmd => IOTcmd,
		 cpu_write_n => cpu_write_n, clk_write_n => clk_write_n,
		 cpu_c0_n => cpu_c0_n, cpu_c1_n => cpu_c1_n,
		 dx => dx, cpu_skip_n => cpu_skip_n,
		 RST_n => rtc_rst, SCLK => rtc_sclk, SIO => rtc_io);

end generate;

-----------------------------------------------------------------------------------------
-- Port bits IOT
-- for now, just implement programmable I/O ports, 3x 12 bit
-----------------------------------------------------------------------------------------

iobits_if: if (PIOBASE /= O"00") and (not DEBUG) generate

	signal bits_bank: integer range 0 to 2;
	signal bits_sel: boolean;
	signal bits_OD : std_logic_vector(iobits'range);
	signal bits_out: std_logic_vector(iobits'range);

begin

	bits_sel <= IOTact and (IOTdev = PIOBASE);

	-- each I/O pin has two control bits:
	--		bits_OD = 0 for push/pull, else it is open drain
	--		bits_out = 0 to drive 0, else it is driven 1 or Z (depending on bits_OD)
	--	thus:
	--		00	drive 0
	--		01	drive 1
	--		10	drive 0
	--		11	drive 'Z' (input) (initial setting)
	process (bits_out, bits_OD)
	begin
		for i in iobits'range loop
			if bits_out(i) = '1' then
				if bits_OD(i) = '1' then
					iobits(i) <= 'Z';
				else
					iobits(i) <= '1';
				end if;
			else
				iobits(i) <= '0';
			end if;
		end loop;
	end process;

	-- reading
	process (bits_sel, IOTcmd, cpu_write_n, IOTread, bits_bank, iobits)
	begin
		if bits_sel and (IOTcmd = PIOGETBITS) then

			if cpu_write_n = '0' then
				cpu_c0_n <= '0';
				cpu_c1_n <= '0';
			else
				cpu_c0_n <= 'Z';
				cpu_c1_n <= 'Z';
			end if;

			if IOTread then
				case bits_bank is
					when 0 => dx <= iobits( 0 to 11);
					when 1 => dx <= iobits(12 to 23);
					when 2 => dx <= iobits(24 to 35);
				end case;
			else
				dx <= (others => 'Z');
			end if;

		else

			cpu_c0_n <= 'Z';
			cpu_c1_n <= 'Z';
			dx <= (others => 'Z');

		end if;
	end process;

	-- writing
	process (reset, clk_write_n, bits_sel, IOTact, IOTcmd, dx)
	begin
		if reset then
			bits_out <= (others => '1');	-- all inputs on reset
			bits_OD  <= (others => '1');
			bits_bank <= 0;
		elsif rising_edge(clk_write_n) then
			if bits_sel and IOTact then

				case IOTcmd is

					when PIOSETBANK =>	-- select bank
						bits_bank <= to_integer(unsigned(dx(10 to 11)));

					when PIOSETBITS => -- write bits
						case bits_bank is
							when 0 => bits_out( 0 to 11) <= dx;
							when 1 => bits_out(12 to 23) <= dx;
							when 2 => bits_out(24 to 35) <= dx;
						end case;

					when PIOSETOD => -- set Open Drain
						case bits_bank is
	 						when 0 => bits_OD( 0 to 11) <= dx;
	 						when 1 => bits_OD(12 to 23) <= dx;
	 						when 2 => bits_OD(24 to 35) <= dx;
	 					end case;

					when others =>
						null;

				end case;
			end if;
		end if;
	end process;

end generate; -- if PIOBASE /= O"00"

end RTL;
