-- -----------------------------------------------------------------------
--
-- Turbo Chameleon
--
-- Multi purpose FPGA expansion for the Commodore 64 computer
--
-- -----------------------------------------------------------------------
-- Copyright 2005-2013 by Peter Wendrich (pwsoft@syntiac.com)
-- http://www.syntiac.com
--
-- This source file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as published
-- by the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This source file 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, see <http://www.gnu.org/licenses/>.
--
-- -----------------------------------------------------------------------
--
-- For better understanding what this entity does in detail, please refer
-- to the Chameleon core-developers manual. It has documentation about
-- the CPLD MUX, signal timing, docking-station protocol and the cartridge port access.
--
-- Chameleon timing and I/O driver. Handles all the timing and multiplexing
-- details of the cartridge port and the CPLD mux.
--   - Detects the type of mode the chameleon is running in.
--   - Multiplexes the PS/2 keyboard and mouse signals.
--   - Gives access to joysticks and keyboard on a C64 in cartridge mode.
--   - Gives access to joysticks and keyboard on a docking-station
--   - Gives access to MMC card and serial-flash through the CPLD MUX.
--   - Drives the two LEDs on the Chameleon (or an optional Amiga keyboard).
--   - Can optionally give access to the IEC bus
--   - Can optionally give access to other C64 resources like the SID.
--
-- -----------------------------------------------------------------------
-- enable_docking_station - Enable support for the docking-station.
-- enable_c64_joykeyb     - Automatically read joystick and keyboard on the C64 bus.
--                          Take note this disables the C64 bus access feature on this entity.
-- enable_c64_4player     - Enable 4player support on the user-port of the C64.
--                          The flag enable_c64_joykeyb must be true for this to work.
-- enable_raw_spi         - SPI controller inside this entity is switched off.
--                          And the actual SPI lines are exposed. The maximum speed is limited
--                          as the signals are time multiplexed. The maximum spi speed usable
--                          is around 1/12 of clk. (One line transition each 6 clk cycles)
-- enable_iec_access      - Enables support for the IEC bus on the break-out cable.
--                          Set this to 'false' when the IEC bus is not used to save some logic.
-- -----------------------------------------------------------------------
-- clk             - system clock
-- ena_1mhz        - Enable must be '1' one clk cycle each 1 Mhz.
-- reset           - Perform a reset of the subsystems
-- reset_ext       - Hardware reset from the cartridge-port (eg. a C64 reset button)
--
-- no_clock        - '0' when connected to C64 cartridge port.
--                   '1' when in standalone mode or docking-station connected.
-- docking_station - '0' standalone/cartrdige mode
--                   '1' when docking-station is connected.
--
-- to_usb_rx
--
-- The following timing signals are only useful when writing C64 related designs.
-- They can be left unconnected in all other FPGA designs.
-- phi_mode        - Selects timing in standalone mode ('0' is PAL, '1' is NTSC).
-- phi_out         - Regenerated or synthesized phi2 clock.
-- phi_cnt         - Counting the system-clock cycles within one phi2 cycle.
-- phi_end_0       - The half of the cycle where phi_out is low ends.
-- phi_end_1       - The half of the cycle where phi_out is high ends.
-- phi_post_1      - Triggers when phi changes
-- phi_post_2      - Triggers one cycle after phi changed
-- phi_post_3      - Triggers two cycles phi changed
-- phi_post_4      - Triggers three cycles phi changed
--
-- c64_irq_n       - Status of the C64 IRQ line (cartridge mode only)
-- c64_nmi_n       - Status of the C64 NMI line
-- c64_ba          - status of the C64 BA line
--
-- The following signals should be synchronised to the phi_out signal
-- c64_vic         - When set data on c64_d is send to the VIC-II chip.
-- c64_cs          - When set it accesses the C64 databus (uses Ultimax mode, no memory is mapped)
-- c64_cs_roms     - Enables access to the C64 Kernal and Basic ROMs (disables Ultimax mode)
-- c64_clockport   - When set it accesses the clockport.
-- c64_we          - Access is a write when set (note polarity is the inverse of R/W on cartridge port)
-- c64_a           - C64 address bus
-- c64_d           - Data to the C64
-- c64_q           - Data from the C64 (only valid when phi_end_1 is set)
--
-- spi_speed       - 0 SPI bus runs at slow speed (250 Kbit), SPI bus runs at fast speed (8 Mbit)
-- spi_req         - Toggle to request SPI transfer.
-- spi_ack         - Is made equal to spi_req after transfer is complete.
-- spi_d           - Data input into SPI controller.
-- spi_q           - Data output from SPI controller.
--
-- led_green       - Control the green LED (0 off, 1 on). Also power LED on Amiga keyboard.
-- led_red         - Control the red LED (0 off, 1 on). Also drive LED on Amiga keyboard.
-- ir              - ir signal. Input for the chameleon_cdtv_remote entity.
--
-- ps2_*           - PS2 signals for both keyboard and mouse.
-- button_reset_n  - Status of blue reset button (right button) on the Chameleon. Low active.
-- joystick*       - Joystick ports of both docking-station and C64.  Bits: fire2, fire1, right, left, down, up
--                   The C64 only supports one button fire1. The signals are low active
-- keys            - C64 keyboard. One bit for each key on the keyboard. Low active.
-- restore_key_n   - Trigger for restore key on docking-station.
--                   On a C64 the restore key is wired to the NMI line instead.
-- iec_*           - IEC signals. Only valid when enable_iec_access is set to true.
-- -----------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;

-- -----------------------------------------------------------------------

entity chameleon_io is
	generic (
		enable_docking_station : boolean := true;
		enable_c64_joykeyb : boolean := false;
		enable_c64_4player : boolean := false;
		enable_raw_spi : boolean := false;
		enable_iec_access : boolean := false
	);
	port (
-- Clocks
		clk : in std_logic;
		clk_mux : in std_logic;
		ena_1mhz : in std_logic;
		reset : in std_logic;
		reset_ext : out std_logic;

-- Config
		no_clock : out std_logic;
		docking_station : out std_logic;

-- Chameleon FPGA pins
		-- C64 Clocks
		phi2_n : in std_logic;
		dotclock_n : in std_logic;
		-- C64 cartridge control lines
		io_ef_n : in std_logic;
		rom_lh_n : in std_logic;
		-- SPI bus
		spi_miso : in std_logic;
		-- CPLD multiplexer
		mux_clk : out std_logic;
		mux : out unsigned(3 downto 0);
		mux_d : out unsigned(3 downto 0);
		mux_q : in unsigned(3 downto 0);

-- USB microcontroller (To RX of micro)
		to_usb_rx : in std_logic := '1';

-- C64 timing (only for C64 related cores)
		phi_mode : in std_logic := '0';
		phi_out : out std_logic;
		phi_cnt : out unsigned(7 downto 0);
		phi_end_0 : out std_logic;
		phi_end_1 : out std_logic;
		phi_post_1 : out std_logic;
		phi_post_2 : out std_logic;
		phi_post_3 : out std_logic;
		phi_post_4 : out std_logic;

-- C64 bus
		c64_irq_n : out std_logic;
		c64_nmi_n : out std_logic;
		c64_ba : out std_logic;

		c64_vic : in std_logic := '0';
		c64_cs : in std_logic := '0';
		c64_cs_roms : in std_logic := '0';
		c64_clockport : in std_logic := '0';
		c64_we : in std_logic := '0';
		c64_a : in unsigned(15 downto 0) := (others => '0');
		c64_d : in unsigned(7 downto 0) := (others => '1');
		c64_q : out unsigned(7 downto 0);

-- SPI chip-selects
		mmc_cs_n : in std_logic := '1';
		flash_cs_n : in std_logic := '1';
		rtc_cs : in std_logic := '0';

-- SPI controller (enable_raw_spi must be set to false)
		spi_speed : in std_logic := '1';
		spi_req : in std_logic := '0';
		spi_ack : out std_logic;
		spi_d : in unsigned(7 downto 0) := (others => '-');
		spi_q : out unsigned(7 downto 0);

-- SPI raw signals (enable_raw_spi must be set to true)
		spi_raw_clk : in std_logic := '1';
		spi_raw_mosi : in std_logic := '1';
		spi_raw_ack : out std_logic;  -- Added by AMR
		
-- LEDs
		led_green : in std_logic := '0';
		led_red : in std_logic := '0';
		ir : out std_logic;

-- PS/2 Keyboard
		ps2_keyboard_clk_out: in std_logic := '1';
		ps2_keyboard_dat_out: in std_logic := '1';
		ps2_keyboard_clk_in: out std_logic;
		ps2_keyboard_dat_in: out std_logic;

-- PS/2 Mouse
		ps2_mouse_clk_out: in std_logic := '1';
		ps2_mouse_dat_out: in std_logic := '1';
		ps2_mouse_clk_in: out std_logic;
		ps2_mouse_dat_in: out std_logic;

-- Buttons
		button_reset_n : out std_logic;
		
-- Joysticks
		joystick1 : out unsigned(5 downto 0);
		joystick2 : out unsigned(5 downto 0);
		joystick3 : out unsigned(5 downto 0);
		joystick4 : out unsigned(5 downto 0);

-- Keyboards
		--  0 = col0, row0
		--  1 = col1, row0
		--  8 = col0, row1
		-- 63 = col7, row7
		keys : out unsigned(63 downto 0);
		restore_key_n : out std_logic;
		amiga_reset_n : out std_logic;
		amiga_trigger : out std_logic;
		amiga_scancode : out unsigned(7 downto 0);

-- IEC bus
		iec_clk_out : in std_logic := '1';
		iec_dat_out : in std_logic := '1';
		iec_atn_out : in std_logic := '1';
		iec_srq_out : in std_logic := '1';
		iec_clk_in : out std_logic;
		iec_dat_in : out std_logic;
		iec_atn_in : out std_logic;
		iec_srq_in : out std_logic
	);
end entity;
-- -----------------------------------------------------------------------

architecture rtl of chameleon_io is
-- Clocks
	signal no_clock_loc : std_logic;
	signal phi : std_logic;
	signal end_of_phi_0 : std_logic;
	signal end_of_phi_1 : std_logic;
	
-- State
	signal reset_pending : std_logic := '0';
	signal reset_in : std_logic := '0';

-- MUX
	type muxstate_t is (
		-- Reset phase
		MUX_RESET,
		-- MMC
		MUX_MMC0L, MUX_MMC0H, MUX_MMC1L, MUX_MMC1H, MUX_MMC2L, MUX_MMC2H, MUX_MMC3L, MUX_MMC3H,
		MUX_MMC4L, MUX_MMC4H, MUX_MMC5L, MUX_MMC5H, MUX_MMC6L, MUX_MMC6H, MUX_MMC7L, MUX_MMC7H,
		-- IEC
		MUX_IEC1, MUX_IEC2, MUX_IEC3, MUX_IEC4,
		-- PS2
		MUX_PS2,
		-- LED
		MUX_LED,
		-- PHI=0
		MUX_WAIT0,
		MUX_A3_C, MUX_BUSVIC,
		MUX_D0VIC, MUX_D1VIC,
		MUX_NMIIRQ1, MUX_NMIIRQ2,
		MUX_ULTIMAX,
		MUX_END0,
		-- PHI=1
		MUX_WAIT1,
		MUX_BUS, MUX_CLKPORT,
		MUX_A0, MUX_A1, MUX_A2, MUX_A3,
		MUX_D0WR, MUX_D1WR, MUX_D0WR_1, MUX_D1WR_1, MUX_D0WR_2, MUX_D1WR_2,
		MUX_D0RD_1, MUX_D1RD_1, MUX_D0RD_2, MUX_D1RD_2
	);
	signal mux_state : muxstate_t;
	signal mux_toggle : std_logic := '0';

	signal mux_c128_timeout : unsigned(7 downto 0) := (others => '1');
	signal mux_clk_reg : std_logic := '0';
	signal mux_d_reg : unsigned(mux_d'range) := X"F";
	signal mux_reg : unsigned(mux'range) := X"F";
	signal mux_d_mmc : unsigned(1 downto 0) := "11";

-- C64 bus
	signal c64_reset_reg : std_logic := '0';
	signal c64_ba_reg : std_logic := '1';
	signal c64_data_reg : unsigned(7 downto 0) := (others => '1');
	signal c64_addr : unsigned(15 downto 0) := (others => '0');
	signal c64_to_io : unsigned(7 downto 0) := (others => '0');
	
	signal c64_we_loc : std_logic := '0';
	signal c64_vic_loc : std_logic := '0';
	signal c64_cs_loc : std_logic := '0';
	signal c64_roms_loc : std_logic := '0';
	signal c64_clockport_loc : std_logic := '0';
	
-- C64 joystick/keyboard
	signal c64_kb_req : std_logic := '0';
	signal c64_kb_ack : std_logic := '0';
	signal c64_kb_we : std_logic := '1';
	signal c64_kb_a : unsigned(15 downto 0) := (others => '0');
	signal c64_kb_q : unsigned(7 downto 0) := (others => '1');
	signal c64_joystick1 : unsigned(4 downto 0);
	signal c64_joystick2 : unsigned(4 downto 0);
	signal c64_joystick3 : unsigned(4 downto 0);
	signal c64_joystick4 : unsigned(4 downto 0);
	signal c64_keys : unsigned(63 downto 0);

-- Docking-station
	signal docking_station_loc : std_logic;
	signal docking_irq : std_logic;
	signal docking_joystick1 : unsigned(5 downto 0);
	signal docking_joystick2 : unsigned(5 downto 0);
	signal docking_joystick3 : unsigned(5 downto 0);
	signal docking_joystick4 : unsigned(5 downto 0);
	signal docking_keys : unsigned(63 downto 0);
	signal docking_amiga_reset_n : std_logic;
	signal docking_amiga_scancode : unsigned(7 downto 0);

-- MMC
	signal mmc_state : unsigned(5 downto 0) := (others => '0');
	signal spi_q_reg : unsigned(7 downto 0) := (others => '1');
	signal spi_run : std_logic := '0';
	signal spi_sample : std_logic := '0';
	signal mmc_shift_req : std_logic;
	signal mmc_shift_ack : std_logic := '0';

-- IEC
	signal iec_clk_reg : std_logic := '1';
	signal iec_dat_reg : std_logic := '1';
	signal iec_atn_reg : std_logic := '1';
	signal iec_srq_reg : std_logic := '1';
begin
	reset_ext <= reset_in;
	no_clock <= no_clock_loc;
	docking_station <= docking_station_loc;
	--
	phi_out <= phi;
	phi_end_0 <= end_of_phi_0;
	phi_end_1 <= end_of_phi_1;
	--
	c64_ba <= c64_ba_reg;
	c64_q <= c64_data_reg;
	--
	spi_q <= spi_q_reg;
	--
	joystick1 <= docking_joystick1 and ("1" & c64_joystick1);
	joystick2 <= docking_joystick2 and ("1" & c64_joystick2);
	joystick3 <= docking_joystick3 and ("1" & c64_joystick3);
	joystick4 <= docking_joystick4 and ("1" & c64_joystick4);
	keys <= docking_keys and c64_keys;

-- -----------------------------------------------------------------------
-- PHI2 clock sync
-- -----------------------------------------------------------------------
	phiInstance : entity work.chameleon_phi_clock
		port map (
			clk => clk,
			phi2_n => phi2_n,
			mode => phi_mode,

			no_clock => no_clock_loc,
			docking_station => docking_station_loc,

			phiLocal => phi,
			phiCnt => phi_cnt,
			phiPreHalf => end_of_phi_0,
			phiPreEnd => end_of_phi_1,
			phiPost1 => phi_post_1,
			phiPost2 => phi_post_2,
			phiPost3 => phi_post_3,
			phiPost4 => phi_post_4
		);

-- -----------------------------------------------------------------------
-- Docking-station
-- To enable set enable_docking_station to true.
-- -----------------------------------------------------------------------
	genDockingStation : if enable_docking_station generate
		myDockingStation : entity work.chameleon_docking_station
			port map (
				clk => clk,
				
				docking_station => docking_station_loc,
				
				dotclock_n => dotclock_n,
				io_ef_n => io_ef_n,
				rom_lh_n => rom_lh_n,
				irq_q => docking_irq,
				
				joystick1 => docking_joystick1,
				joystick2 => docking_joystick2,
				joystick3 => docking_joystick3,
				joystick4 => docking_joystick4,
				keys => docking_keys,
				restore_key_n => restore_key_n,
				
				amiga_power_led => led_green,
				amiga_drive_led => led_red,
				amiga_reset_n => amiga_reset_n,
				amiga_trigger => amiga_trigger,
				amiga_scancode => amiga_scancode
			);
	end generate;

	noDockingStation : if not enable_docking_station generate
		docking_joystick1 <= (others => '1');
		docking_joystick2 <= (others => '1');
		docking_joystick3 <= (others => '1');
		docking_joystick4 <= (others => '1');
		docking_keys <= (others => '1');
	end generate;

-- -----------------------------------------------------------------------
-- C64 keyboard and joystick support
-- To enable set enable_c64_joykeyb to true.
-- -----------------------------------------------------------------------
	genC64JoyKeyb : if enable_c64_joykeyb generate
		myC64JoyKeyb : entity work.chameleon_c64_joykeyb
			generic map (
				enable_4player => enable_c64_4player
			)
			port map (
				clk => clk,
				ena_1mhz => ena_1mhz,
				no_clock => no_clock_loc,
				reset => reset,
				
				ba => c64_ba_reg,
				req => c64_kb_req,
				ack => c64_kb_ack,
				we => c64_kb_we,
				a => c64_kb_a,
				d => c64_data_reg,
				q => c64_kb_q,
				
				joystick1 => c64_joystick1,
				joystick2 => c64_joystick2,
				joystick3 => c64_joystick3,
				joystick4 => c64_joystick4,
				keys => c64_keys
			);

		c64_addr <= c64_kb_a;
		c64_to_io <= c64_kb_q;
		c64_vic_loc <= '0';
		c64_roms_loc <= '0';
		c64_clockport_loc <= '0';
		c64_we_loc <= c64_kb_we;

		process(clk)
		begin
			if rising_edge(clk) then
				if end_of_phi_1 = '1' then
					c64_cs_loc <= '0';
					if c64_kb_req /= c64_kb_ack then
						if c64_cs_loc = '1' then
							c64_kb_ack <= c64_kb_req;
						else
							c64_cs_loc <= '1';
						end if;
					end if;
				end if;
			end if;
		end process;
	end generate;
	
	noC64JoyKeyb : if not enable_c64_joykeyb generate
		c64_joystick1 <= (others => '1');
		c64_joystick2 <= (others => '1');
		c64_joystick3 <= (others => '1');
		c64_joystick4 <= (others => '1');
		c64_keys <= (others => '1');
		
		c64_addr <= c64_a;
		c64_to_io <= c64_d;

		c64_vic_loc <= c64_vic;
		c64_cs_loc <= c64_cs;
		c64_roms_loc <= c64_cs_roms;
		c64_clockport_loc <= c64_clockport;
		c64_we_loc <= c64_we;
	end generate;

-- -----------------------------------------------------------------------
-- MUX CPLD
-- -----------------------------------------------------------------------
	-- MUX clock
	process(clk_mux)
	begin
		if rising_edge(clk_mux) then
			mux_clk_reg <= not mux_clk_reg;
		end if;
	end process;

	-- MUX sequence
	process(clk_mux)
	begin
		if rising_edge(clk_mux) then
			if mux_clk_reg = '1' then
				mux_toggle <= not mux_toggle;
				case mux_state is
				when MUX_RESET   =>
					if phi = '1' then
						mux_state <= MUX_WAIT0;
					end if;
-- PHI2  0
				when MUX_WAIT0   =>
					if phi = '0' then
						mux_state <= MUX_MMC0L;
						if mux_c128_timeout /= 0 then
							mux_c128_timeout <= mux_c128_timeout - 1;
						end if;
					end if;
				when MUX_MMC0L   => mux_state <= MUX_A3_C;
				when MUX_A3_C    => mux_state <= MUX_ULTIMAX;
				when MUX_ULTIMAX => mux_state <= MUX_MMC0H;
				when MUX_MMC0H   => mux_state <= MUX_BUSVIC;
				when MUX_BUSVIC  => mux_state <= MUX_IEC1;
				when MUX_IEC1    => mux_state <= MUX_MMC1L;
				when MUX_MMC1L   => mux_state <= MUX_PS2;
				when MUX_PS2     => mux_state <= MUX_A2;
				when MUX_A2      => mux_state <= MUX_MMC1H;
				when MUX_MMC1H   => mux_state <= MUX_NMIIRQ1;
				when MUX_NMIIRQ1 => mux_state <= MUX_LED;
				when MUX_LED     => mux_state <= MUX_MMC2L;
				when MUX_MMC2L   => mux_state <= MUX_A0;
				when MUX_A0      => mux_state <= MUX_IEC4;
				when MUX_IEC4    => mux_state <= MUX_MMC2H;
				when MUX_MMC2H   => mux_state <= MUX_A1;
				when MUX_A1      => mux_state <= MUX_D0VIC;
				when MUX_D0VIC   => mux_state <= MUX_D1VIC;
				when MUX_D1VIC   => mux_state <= MUX_MMC3L;
				when MUX_MMC3L   => mux_state <= MUX_IEC3;
				when MUX_IEC3    => mux_state <= MUX_MMC3H;
				when MUX_MMC3H   => mux_state <= MUX_END0;
				when MUX_END0    => mux_state <= MUX_WAIT1;
-- PHI2  1
				when MUX_WAIT1 =>
					if phi = '1' then
						mux_state <= MUX_MMC4L;
					end if;
				when MUX_MMC4L   => mux_state <= MUX_BUS;
				when MUX_BUS     => mux_state <= MUX_D0WR;
				when MUX_D0WR    => mux_state <= MUX_D1WR;
				when MUX_D1WR    => mux_state <= MUX_MMC4H;
				when MUX_MMC4H   => mux_state <= MUX_A3;
				when MUX_A3      => mux_state <= MUX_CLKPORT;
				when MUX_CLKPORT => mux_state <= MUX_MMC5L;
				when MUX_MMC5L   => mux_state <= MUX_NMIIRQ2;
				when MUX_NMIIRQ2 => mux_state <= MUX_MMC5H;
				when MUX_MMC5H   => mux_state <= MUX_D0WR_1;
				when MUX_D0WR_1  => mux_state <= MUX_D1WR_1;
				when MUX_D1WR_1  => mux_state <= MUX_MMC6L;
				when MUX_MMC6L   => mux_state <= MUX_IEC2;
				when MUX_IEC2    => mux_state <= MUX_MMC6H;
				--when MUX_LED     => mux_state <= MUX_MMC6H;
				when MUX_MMC6H   => mux_state <= MUX_D0WR_2;
				when MUX_D0WR_2  => mux_state <= MUX_D1WR_2;
				when MUX_D1WR_2  => mux_state <= MUX_MMC7L;
				when MUX_MMC7L   => mux_state <= MUX_D0RD_1;
				when MUX_D0RD_1  => mux_state <= MUX_D1RD_1;
				when MUX_D1RD_1  => mux_state <= MUX_MMC7H;
				when MUX_MMC7H   => mux_state <= MUX_D0RD_2;
				when MUX_D0RD_2  => mux_state <= MUX_D1RD_2;
				when MUX_D1RD_2  => mux_state <= MUX_WAIT0;
				end case;
			end if;
			if reset = '1' then
				mux_c128_timeout <= (others => '1');
-- 				system_wait <= '1';
			end if;
		end if;
	end process;

	-- MUX read
	process(clk_mux)
	begin
		if rising_edge(clk_mux) then
			if mux_clk_reg = '1' then
				case mux_reg is
				when X"0" =>
					c64_data_reg(3 downto 0) <= mux_q;
				when X"1" =>
					c64_data_reg(7 downto 4) <= mux_q;
				when X"6" =>
					c64_reset_reg <= not mux_q(0);
					c64_irq_n <= mux_q(2);
					c64_nmi_n <= mux_q(3);
					reset_pending <= reset or c64_reset_reg;
					if reset_pending = '0' then
						reset_in <= c64_reset_reg;
					else
						reset_in <= '0';
					end if;
					if no_clock_loc = '1' then
						c64_irq_n <= '1';
					end if;
				when X"7" =>
					c64_ba_reg <= mux_q(1);
					if no_clock_loc = '1' then
						c64_ba_reg <= '1';
					end if;
				when X"B" =>
					button_reset_n <= mux_q(1);
					ir <= mux_q(3);
				when X"D" =>
					iec_dat_reg <= mux_q(0);
					iec_clk_reg <= mux_q(1);
					iec_srq_reg <= mux_q(2);
					iec_atn_reg <= mux_q(3);
				when X"E" =>
					ps2_keyboard_dat_in <= mux_q(0);
					ps2_keyboard_clk_in <= mux_q(1);
					ps2_mouse_dat_in <= mux_q(2);
					ps2_mouse_clk_in <= mux_q(3);
				when others =>
					null;
				end case;
				if spi_sample = '1' then
					spi_q_reg <= spi_q_reg(6 downto 0) & spi_miso;
				end if;
			end if;
			iec_dat_in <= iec_dat_reg;
			iec_clk_in <= iec_clk_reg;
			iec_srq_in <= iec_srq_reg;
			iec_atn_in <= iec_atn_reg;
		end if;
	end process;

	-- MUX write
	process(clk_mux)
	begin
		if rising_edge(clk_mux) then
			spi_raw_ack <= '0';
			if mux_clk_reg = '1' then
				spi_sample <= '0';
				case mux_state is
--
-- RESET
				when MUX_RESET =>
					mux_d_reg <= (others => '-');
					mux_reg <= X"F";
--
-- MMC
				when MUX_MMC0L =>
					-- Remember current state for lowspeed transfer.
					-- Register is accessed another 15 times in
					-- system cycle, but should not be updated when running on 250khz speed.
					mux_d_mmc(0) <= mmc_state(1) or (not mmc_state(5));
					mux_d_mmc(1) <= spi_d(7 - to_integer(mmc_state(4 downto 2)));
					-- Update register
					mux_d_reg(0) <= mmc_state(1) or (not mmc_state(5));
					mux_d_reg(1) <= spi_d(7 - to_integer(mmc_state(4 downto 2)));
					mux_d_reg(2) <= mmc_cs_n; 
					mux_d_reg(3) <= to_usb_rx;
					mux_reg <= X"C";
					if mmc_state(5) = '1' then
						if spi_speed = '0' then
							-- Slow speed. Only toggle once in two cycles
							mmc_state <= mmc_state + "000001";
							if mmc_state(1 downto 0) = "11" then
								spi_sample <= '1';
							end if;
						else
							-- Fast speed. Toggle 16 times in a cycle
							mmc_state <= mmc_state + "000010";
							if mmc_state(1) = '1' then
								spi_sample <= '1';
							end if;
						end if;
					end if;
					if enable_raw_spi then
						mux_d_reg(0) <= spi_raw_clk;
						mux_d_reg(1) <= spi_raw_mosi;
						mux_d_reg(2) <= mmc_cs_n; 
						mux_d_reg(3) <= to_usb_rx;
						mux_reg <= X"C";
						spi_raw_ack <= '1';
					end if;
				when MUX_MMC1L | MUX_MMC2L | MUX_MMC3L
				   | MUX_MMC4L | MUX_MMC5L | MUX_MMC6L | MUX_MMC7L =>
					mux_d_reg(0) <= mux_d_mmc(0);
					mux_d_reg(1) <= mux_d_mmc(1);
					mux_d_reg(2) <= mmc_cs_n; 
					mux_d_reg(3) <= to_usb_rx;
					mux_reg <= X"C";
					-- Only update register on when running at fast speed (8Mhz).
					if (mmc_state(5) = '1') and (spi_speed = '1') then
						mux_d_reg(0) <= mmc_state(1);
						mux_d_reg(1) <= spi_d(7 - to_integer(mmc_state(4 downto 2)));
						-- Fast speed. Toggle 16 times in a cycle
						mmc_state <= mmc_state + "000010";
						if mmc_state(1) = '1' then
							spi_sample <= '1';
						end if;
					end if;
					if enable_raw_spi then
						mux_d_reg(0) <= spi_raw_clk;
						mux_d_reg(1) <= spi_raw_mosi;
						mux_d_reg(2) <= mmc_cs_n;
						mux_d_reg(3) <= to_usb_rx;
						mux_reg <= X"C";
						spi_raw_ack <= '1';
					end if;

				when MUX_MMC0H | MUX_MMC1H | MUX_MMC2H | MUX_MMC3H
				   | MUX_MMC4H | MUX_MMC5H | MUX_MMC6H | MUX_MMC7H =>
					mux_d_reg(0) <= mux_d_mmc(0);
					mux_d_reg(1) <= mux_d_mmc(1);
					mux_d_reg(2) <= mmc_cs_n;
					mux_d_reg(3) <= to_usb_rx;
					mux_reg <= X"C";
					if (mmc_state(5) = '1') and (spi_speed = '1') then
						-- Only update register on when running at fast speed (8Mhz).
						mux_d_reg(0) <= mmc_state(1);
						mux_d_reg(1) <= spi_d(7 - to_integer(mmc_state(4 downto 2)));
						-- Fast speed. Toggle 16 times in a cycle
						mmc_state <= mmc_state + "000010";
						if mmc_state(1) = '1' then
							spi_sample <= '1';
						end if;
					elsif enable_iec_access then
						-- When MMC transfer is not pending use some of the MMC cycles for IEC transfers.
						mux_d_reg(0) <= iec_dat_out;
						mux_d_reg(1) <= iec_clk_out;
						mux_d_reg(2) <= iec_srq_out;
						mux_d_reg(3) <= iec_atn_out;
						mux_reg <= X"D";
					end if;
					if enable_raw_spi then
						mux_d_reg(0) <= spi_raw_clk;
						mux_d_reg(1) <= spi_raw_mosi;
						mux_d_reg(2) <= mmc_cs_n;
						mux_d_reg(3) <= to_usb_rx;
						mux_reg <= X"C";
						spi_raw_ack <= '1';
					end if;
				when MUX_NMIIRQ1 | MUX_NMIIRQ2=>
					mux_d_reg <= "110" & (not reset);
					mux_reg <= X"6";
					if docking_station_loc = '1' then
						mux_d_reg(2) <= docking_irq;
					end if;
--
-- IEC
				when MUX_IEC1 | MUX_IEC2 | MUX_IEC3 | MUX_IEC4 =>
					mux_d_reg(0) <= iec_dat_out;
					mux_d_reg(1) <= iec_clk_out;
					mux_d_reg(2) <= iec_srq_out;
					mux_d_reg(3) <= iec_atn_out;
					mux_reg <= X"D";
--
-- USART, LEDs and IR
				when MUX_LED =>
					mux_d_reg <= flash_cs_n & rtc_cs & led_green & led_red;
					mux_reg <= X"B";
--
-- PS2
				when MUX_PS2 =>
					mux_d_reg(0) <= ps2_keyboard_dat_out;
					mux_d_reg(1) <= ps2_keyboard_clk_out;
					mux_d_reg(2) <= ps2_mouse_dat_out;
					mux_d_reg(3) <= ps2_mouse_clk_out;
					mux_reg <= X"E";
--
-- WAITS
				when MUX_WAIT0 =>
					if spi_req /= spi_run then
						spi_run <= spi_req;
						mmc_state <= "100000";
					end if;
					-- Use dead time to do IEC reads/writes
					mux_d_reg(0) <= iec_dat_out;
					mux_d_reg(1) <= iec_clk_out;
					mux_d_reg(2) <= iec_srq_out;
					mux_d_reg(3) <= iec_atn_out;
					mux_reg <= X"D";
					if enable_raw_spi then
						mux_d_reg(0) <= spi_raw_clk;
						mux_d_reg(1) <= spi_raw_mosi;
						mux_d_reg(2) <= mmc_cs_n;
						mux_d_reg(3) <= to_usb_rx;
						mux_reg <= X"C";
						spi_raw_ack <= '1';
					end if;
				when MUX_WAIT1 =>
					-- Continue BUSVIC output at end of phi2=0, so we sample BA a few times.
					mux_d_reg <= "0101";
					if docking_station_loc = '1' then
						mux_d_reg <= "1111";
					end if;
					mux_reg <= X"7";
					-- Toggle between SPI/IEC and updating BUSVIC.
					if mux_toggle = '1' then
						if enable_iec_access then
							mux_d_reg(0) <= iec_dat_out;
							mux_d_reg(1) <= iec_clk_out;
							mux_d_reg(2) <= iec_srq_out;
							mux_d_reg(3) <= iec_atn_out;
							mux_reg <= X"D";
						end if;
						if enable_raw_spi then
							mux_d_reg(0) <= spi_raw_clk;
							mux_d_reg(1) <= spi_raw_mosi;
							mux_d_reg(2) <= mmc_cs_n;
							mux_d_reg(3) <= to_usb_rx;
							mux_reg <= X"C";
							spi_raw_ack <= '1';
						end if;
					end if;
--
-- PHI2  0
				when MUX_A3_C =>
					mux_d_reg <= X"C";
					mux_reg <= X"5";
				when MUX_BUSVIC =>
					mux_d_reg <= "0101";
					if docking_station_loc = '1' then
						mux_d_reg <= "1111";
					end if;
					mux_reg <= X"7";
				when MUX_ULTIMAX =>
					mux_d_reg <= "1011";
					mux_reg <= X"8";
				when MUX_D0VIC =>
					mux_d_reg <= c64_to_io(3 downto 0);
					mux_reg <= X"0";
				when MUX_D1VIC =>
					mux_d_reg <= c64_to_io(7 downto 4);
					mux_reg <= X"1";
				when MUX_END0 =>
					mux_d_reg <= "0111";
					if docking_station_loc = '1' then
						mux_d_reg <= "1111";
					end if;
					mux_reg <= X"7";
--
-- PHI2  1
				when MUX_A0 =>
					mux_d_reg <= c64_addr(3 downto 0);
					mux_reg <= X"2";
				when MUX_A1 =>
					mux_d_reg <= c64_addr(7 downto 4);
					mux_reg <= X"3";
				when MUX_A2 =>
					mux_d_reg <= c64_addr(11 downto 8);
					mux_reg <= X"4";
				when MUX_BUS =>
					if c64_vic_loc = '0' then
						if c64_cs_loc = '1' then
							mux_d_reg <= "00" & (not c64_we_loc) & (not c64_we_loc);
							mux_reg <= X"7";
						end if;
					else
						-- A15..12 driven, A11..0 not driven, Data driven, no write.
						mux_d_reg <= "0101";
						mux_reg <= X"7";
					end if;
				when MUX_CLKPORT =>
					-- GAME = low unless accessing roms
					mux_d_reg <= "1" & c64_roms_loc & "11";
					if c64_clockport_loc = '1' then
						if c64_we_loc = '0' then
							-- Clockport read
							mux_d_reg <= "1010";
						else
							-- Clockport write
							mux_d_reg <= "1001";
						end if;
					end if;
					mux_reg <= X"8";
				when MUX_A3 =>
					if c64_vic_loc = '0' then
						if c64_cs_loc = '1' then
							mux_d_reg <= c64_addr(15 downto 12);
							mux_reg <= X"5";
						end if;
					end if;
--				when MUX_D0WR | MUX_D0WR_1 | MUX_D0WR_2 =>
				when MUX_D0WR | MUX_D0WR_1 | MUX_D0WR_2 | MUX_D0RD_1 | MUX_D0RD_2 =>
					mux_d_reg <= c64_to_io(3 downto 0);
					mux_reg <= X"0";
--				when MUX_D1WR | MUX_D1WR_1 | MUX_D1WR_2 =>
				when MUX_D1WR | MUX_D1WR_1 | MUX_D1WR_2 | MUX_D1RD_1 | MUX_D1RD_2 =>
					mux_d_reg <= c64_to_io(7 downto 4);
					mux_reg <= X"1";
				when others =>
					null;
				end case;
			end if;
		end if;
	end process;

	process(clk_mux)
	begin
		if rising_edge(clk_mux) then
			if mmc_state(5) = '0' then
				spi_ack <= spi_run;
			end if;
		end if;
	end process;

	mux_clk <= mux_clk_reg;
	mux_d <= mux_d_reg;
	mux <= mux_reg;
end architecture;
