library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

-- MegaSpeedy for Atari 1050 floppy drive
-- (c) 2011-2014 Matthias Reichl <hias@horus.com>
--
-- 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., 675 Mass Ave, Cambridge, MA 02139, USA.
--
--
-- Changelog
-- 20.02.2011: initial proof-of-concept version from Wolfram Fischer
-- 03.05.2011: run A11 and A12 through CPLD
-- 22.06.2014: support 256k in super-speedy mode, fixed 8k ROM banks
-- 23.06.2014: fix 1050 io select
--             write-protect flash except for $C000 bank in banked-mode
-- 26.06.2014: add happy mode
-- 28.06.2014: add turbo mode, printer logic still missing
--             new ROM layout, use lower 256k for all slots
-- 15.07.2014: configure turbo printer pins
--             add Ultimate-II/SuperMax emulation
-- 22.07.2014: implement 1050 turbo centronics interface
--             rename ultimate-II to SuperMax
-- 23.07.2014: drop I2C, run A8-A10 RAM/ROM pins through CPLD
--             use MegaSpeedy SRAM instead of 6810
-- 24.07.2014: add US-doubler mode
-- 25.07.2014: fix ram adressing for superspeedy and supermax modes
--             experimental support for soft-config. lots of features
--             have been disabled in order to fit the logic into 9572
-- 27.07.2014: mode select pins for rotary encoder + switch
--             move i2c to turbo centronics interface
--					disable us-doubler and supermax modes
--					shrink floppy mode to 2 bits
-- 31.07.2014: set powerup / config bank to $7C000
--
-- new XC95144 logic
--
-- 13.08.2014: reassing A8-A10 and I2C pins for XC95144
--             reenable US-Doubler and SuperMax modes
--             set databus only when PHI2=1
-- 14.08.2014: distinguish between speedy, super-speedy and mega-speedy
--             new mode numbers
--             change config addresses
--             re-enable 1050 turbo centronics interface
-- 15.08.2014: add super archiver mode
--             add pass-through of FDC write and ready input
-- 16.08.2014: read all 5 mode inputs via a single address, using D0-D4
--             add duplicator mode (guessed memory layout)
-- 17.08.2014: refactor address decode and data line control
--             add running drive ROM from fixed RAM block
-- 18.08.2014: fix Super Archiver RAM selection, writing to ROM area
--             writes to RAM
-- 10.09.2014: change C000 bankswitching logic
--					enable register readback
-- 12.09.2014: add synchronizer for reset input
--             rename and rearrange config inputs
-- 23.10.2014: add config led
-- 05.11.2014: set RAM mode via bit 7 of rombank instead of mode
--             fix 1050 chips addressing of speedy, happy and duplicator
-- 11.11.2014: fix pinout to final version
-- 22.11.2014: enable access to FDC write gating, RPM speed select
--             and centronics interface in MegaSpeedy mode
--             make LED and 7-segment outputs open collector
--             allow reading state of archiver_a11 and turbo_speed_in
--             in MegaSpeedy mode
-- 23.11.2014: enable readback of fdc_write_in and riot_ready_in

entity MegaSpeedy is 
	port(
		data: inout std_logic_vector(7 downto 0);
		adr: in std_logic_vector(15 downto 0);

		cfg_sw2: in std_logic;
		cfg_enc_b: in std_logic;
		cfg_enc_a: in std_logic;
		cfg_enc_ok: in std_logic;
		cfg_sw1: in std_logic;
		cfg_led: out std_logic;

		d7_ram_rom: inout std_logic;
		rom_ce: out std_logic;
		ram_ce: out std_logic;
		ram_rom_oe: out std_logic;
		ram_rom_we: out std_logic;
		ram_rom_adr: out std_logic_vector(18 downto 8);
		io_1050:inout std_logic;
		turbo_speed_out: out std_logic;
		turbo_speed_in: in std_logic;				
		reset_in:in std_logic;
		rw:in std_logic;
		phi2:in std_logic;

		centronics_busy: in std_logic;
		centronics_clk: out std_logic;
		centronics_strobe: out std_logic;
		centronics_data: out std_logic;

		i2c_data_pin: inout std_logic;
		i2c_clk_pin: out std_logic;

		fdc_write_in: in std_logic;
		fdc_write_out: out std_logic;
		archiver_a11: in std_logic;
		riot_ready_in: in std_logic;
		-- used by super archiver as output pin to select slow disk rotation
		riot_ready_inout: inout std_logic;
		
		track_lo_out: out std_logic_vector(6 downto 0);
		track_hi_out: out std_logic_vector(6 downto 0);
		density_out: out std_logic_vector(2 downto 0);
		summer: inout std_logic
	);

	-- set the default output slew rate to "slow"
	attribute SLOW: String;
	attribute SLOW of MegaSpeedy: entity is "TRUE";

end;

architecture MegaSpeedy_arch of MegaSpeedy is

	subtype floppy_mode_type is std_logic_vector(3 downto 0);

	constant mode_config: 			floppy_mode_type := "0000";
	constant mode_speedy: 			floppy_mode_type := "0001";
	constant mode_super_speedy:	floppy_mode_type := "0010";
	constant mode_mega_speedy:		floppy_mode_type := "0011";
	constant mode_1050: 				floppy_mode_type := "0100";
	constant mode_turbo: 			floppy_mode_type := "0101";
	constant mode_happy: 			floppy_mode_type := "0110";
	constant mode_usdoubler: 		floppy_mode_type := "0111";
	constant mode_super_archiver:	floppy_mode_type := "1000";
	constant mode_supermax: 		floppy_mode_type := "1001";
	constant mode_duplicator: 		floppy_mode_type := "1010";
		
	signal floppy_mode: floppy_mode_type	:=	mode_mega_speedy;

	-- 4k rom base bank
	subtype rom_bank_type is std_logic_vector(18 downto 12);

	-- config ROM stored at $78000-$7FFFF in flash
	constant rom_bank_config: rom_bank_type := "1111000";
	
	-- base 4k bank of floppy rom
	signal rom_base_bank: rom_bank_type := rom_bank_config;
	
	-- switchable 8k rom bank, for flashing
	signal rom_bank_c000: std_logic_vector(18 downto 13);	-- 8k bank
	signal rom_bank_c000_enable: boolean;

	-- switchable 8k super speedy ram banks at $a000
	signal ram_bank: std_logic_vector(18 downto 13);	--  8K Bänke

	signal ram_access: boolean;
	signal rom_access: boolean;
	signal int_1050_access: boolean;
	
	-- A12 ROM address line for happy
	signal happy_a12: std_logic;
	
	-- 1050 turbo a12 and a11 address lines
	signal turbo_rom_adr: std_logic_vector(12 downto 11) := "11";

	-- 1050 turbo centronics interface
	signal turbo_centronics_data: std_logic := '1';
	signal turbo_centronics_clk: std_logic := '1';
	signal turbo_centronics_strobe: std_logic := '1';

	signal i2c_data: std_logic := '1';
	signal i2c_clk: std_logic :=  '1';

	signal rom_source_is_ram: std_logic := '0';

	signal reset_sync: std_logic := '0';
	signal reset: std_logic := '0';

	-- LEDs / 7-segment displays
	signal track_lo: std_logic_vector(6 downto 0) := (others => '1');
	signal track_hi: std_logic_vector(6 downto 0) := (others => '1');
	signal density: std_logic_vector(2 downto 0) := (others => '1');

	-- RAM layout:
	-- $00000 - $2FFFF	192k switchable superspeedy RAM
	-- $75F00 - $75FFF	128 bytes second 6810 replacement for US doubler
	-- $75E00 - $75EFF	128 bytes 6810 replacement
   -- $76000 - $77FFF	fixed 8k speedy/happy/supermax RAM
	-- $78000 - $7FFFF	32k reserved for OS in RAM
	constant ram_adr_base_256k:	std_logic 								:= '0';

	constant ram_adr_6810:			std_logic_vector(18 downto 8) 	:= "11101011110";
	constant ram_adr_6810_2:		std_logic_vector(18 downto 8) 	:= "11101011111";
	constant ram_adr_fixed_8k:		std_logic_vector(18 downto 13) 	:= "111011";
	constant ram_adr_os_32k:		std_logic_vector(18 downto 15) 	:= "1111";

	
	-- special mega speedy config addresses
	-- switchable 8k ROM bank at $C000
	constant cfg_adr_rom_c000_bank:		std_logic_vector(15 downto 0)		:= x"7000";
	-- base floppy rom bank
	constant cfg_adr_rom_base_bank:		std_logic_vector(15 downto 0)		:= x"7010";
	-- floppy mode
	constant cfg_adr_floppy_mode:			std_logic_vector(15 downto 0)		:= x"7011";

	-- encoder and config switches inputs
	constant cfg_adr_mode_select:			std_logic_vector(15 downto 0)		:= x"7018";
	-- config led output
	constant cfg_adr_config_led:			std_logic_vector(15 downto 0)		:= x"7019";
	
	-- i2c interface using mode_select_2 for data and mode_select_3 for clk
	constant i2c_adr_data_lo_clk_lo:		std_logic_vector(15 downto 0)		:= x"7020";
	constant i2c_adr_data_lo_clk_hi:		std_logic_vector(15 downto 0)		:= x"7021";
	constant i2c_adr_data_hi_clk_lo:		std_logic_vector(15 downto 0)		:= x"7022";
	constant i2c_adr_data_hi_clk_hi:		std_logic_vector(15 downto 0)		:= x"7023";

	-- centronics interface in megaspeedy mode
	-- write only
	constant ms_adr_centronics_data_lo_clk_lo:		std_logic_vector(15 downto 0)		:= x"7030";
	constant ms_adr_centronics_data_lo_clk_hi:		std_logic_vector(15 downto 0)		:= x"7031";
	constant ms_adr_centronics_data_hi_clk_lo:		std_logic_vector(15 downto 0)		:= x"7032";
	constant ms_adr_centronics_data_hi_clk_hi:		std_logic_vector(15 downto 0)		:= x"7033";

	-- write only
	constant ms_adr_centronics_strobe_lo:		std_logic_vector(15 downto 0)		:= x"7034";
	constant ms_adr_centronics_strobe_hi:		std_logic_vector(15 downto 0)		:= x"7035";
	
	-- read only, sets d7
	constant ms_adr_centronics_busy:		std_logic_vector(15 downto 0)		:= x"7038";

	-- speed select in megaspeedy mode
	-- set via d0: 0=slow 1=high
	constant ms_adr_speed_select:			std_logic_vector(15 downto 0)		:= x"7040";

	-- write enable select in megaspeedy mode
	-- set via d0: 0=disable 1=enable
	constant ms_adr_write_enable:			std_logic_vector(15 downto 0)		:= x"7041";

	-- read raw IOs for production testing:
	--	fdc_write_in, riot_ready_in, archiver_a11, turbo_speed_in
	constant ms_adr_test_raw_in:			std_logic_vector(15 downto 0)		:= x"7048";
	
	signal ms_write_enable:					std_logic := '1';
	signal ms_speed_select:					std_logic := '1';
	
begin

sync_reset: process(phi2)
begin
	if falling_edge(phi2) then
		reset_sync <= reset_in;
		reset <= reset_sync;
	end if;
end process sync_reset;

set_i2c_pins: process(i2c_data, i2c_clk)
begin
	i2c_data_pin <= 'Z';
	i2c_clk_pin <= 'Z';
	
	if i2c_data = '0' then
		i2c_data_pin <= '0';
	end if;
	if i2c_clk = '0' then
		i2c_clk_pin <= '0';
	end if;
end process set_i2c_pins;

address_decode: process(floppy_mode, adr, rw, ram_bank, rom_base_bank, rom_bank_c000, rom_bank_c000_enable,
			turbo_rom_adr, happy_a12, archiver_a11, rom_source_is_ram)
variable access_ram: boolean;
variable access_rom: boolean;
variable access_1050: boolean;
variable remap_rom_to_ram: boolean;
variable check_1050_6810_access: boolean;
begin
	access_ram := false;
	access_rom := false;
	access_1050 := false;
	check_1050_6810_access := false;
	
	if (rom_source_is_ram = '1') then
		-- default: rom source from 32k RAM bank
		remap_rom_to_ram := true;
		ram_rom_adr <= ram_adr_os_32k & "000" & adr(11 downto 8);
	else
		-- default: ROM address setup to rom_base_bank
		remap_rom_to_ram := false;
		ram_rom_adr <= rom_base_bank & adr(11 downto 8);
	end if;
		
	case floppy_mode is
	when mode_1050 =>
		if adr(12) = '1' then
			-- only allow read access
			if (rw = '1') then
				access_rom := true;
			end if;
		else
			check_1050_6810_access := true;
		end if;

	when mode_super_archiver =>
		case adr(12 downto 11) is
		when "11" =>
				-- 2k rom access
				if (rw = '1') then
					ram_rom_adr(11) <= archiver_a11;
					access_rom := true;
				else
					-- writing to the ROM area selects both ROM and RAM
					-- this seems to be used as a sort of copy protection and
					-- just sending writes to the RAM fixes the diagnostic test
					ram_rom_adr(18 downto 11) <= ram_adr_fixed_8k & "00";
					access_ram := true;
				end if;
		when "10" =>
				-- 2k ram access
				ram_rom_adr(18 downto 11) <= ram_adr_fixed_8k & "00";
				access_ram := true;
		when "01" | "00" =>
			check_1050_6810_access := true;
		when others =>
			null;
		end case;

	when mode_usdoubler =>
		if adr(12) = '1' then
			-- 4k read only ROM
			if (rw = '1') then
				access_rom := true;
			end if;
		else
			if adr(10) = '0' and adr(7) = '0' then
				if adr(9) = '0' then
					ram_rom_adr <= ram_adr_6810;
				else
					ram_rom_adr <= ram_adr_6810_2;
				end if;
				access_ram := true;
			else
				access_1050 := true;
			end if;
		end if;
		
	when mode_supermax =>
		if adr(12) = '1' then
			-- 4k ROM at $1xxx
			if (rw = '1') then
				access_rom := true;
			end if;
		else
			case adr(11 downto 10) is
			when "00" =>
				-- $000-$3FF, only access RIOT with A7=high
				if adr(7) = '1' then
					access_1050 := true;
				end if;
			when "01" | "11" =>
				-- FDC
				access_1050 := true;
			when "10" =>
				-- 1k RAM bank at $800-$BFF
				ram_rom_adr <= ram_adr_fixed_8k & "000" & adr(9 downto 8);
				access_ram := true;
			when others =>
				null;
			end case;
		end if;

	when mode_duplicator =>
		case adr(15 downto 13) is
		when "111" =>
			-- 8k ROM
			ram_rom_adr(12) <= adr(12); 
			if (rw = '1') then
				access_rom := true;
			end if;
		when "001" =>
			-- 8k RAM at $2000-$3FFF
			ram_rom_adr(18 downto 12) <= ram_adr_fixed_8k & adr(12); 
			access_ram := true;
		when "000" =>
			check_1050_6810_access := true;
		when others => 
			null;
		end case;

	when mode_turbo =>
		if adr(12) = '1' then
			-- 4k ROM, a11 set by register
			ram_rom_adr(12 downto 11) <= turbo_rom_adr;
			if (rw = '1') then
				access_rom := true;
			end if;
		else
			check_1050_6810_access := true;
		end if;
		
	when mode_happy =>
		case adr(15 downto 14) is
		when "11" =>
			-- ROM access at $C000-$FFFF
			ram_rom_adr(12) <= happy_a12;
			if (rw = '1') then
				access_rom := true;
			end if;
		when "10" =>
			-- 8k RAM at $8000-$CFFF
			ram_rom_adr(18 downto 12) <= ram_adr_fixed_8k & adr(12);
			access_ram := true;
		when "01" =>
			-- optional display at $4000-$7FFF, handled in separate code
			null;
		when "00" =>
			check_1050_6810_access := true;
		when others =>
			null;
		end case;

	when mode_speedy | mode_super_speedy | mode_mega_speedy | mode_config =>
		-- setup for 16k ROM addresses
		ram_rom_adr(13 downto 12) <= adr(13 downto 12);
		if (floppy_mode = mode_mega_speedy or floppy_mode = mode_config) then
			-- upper half of 32k block
			ram_rom_adr(14) <= '1';
		end if;
		case adr(15 downto 13) is
		when "111" =>
			-- 8k ROM $E000-$FFFF
			if (rw = '1') then
				access_rom := true;
			end if;
		when "110" =>
			-- 8k ROM $C000-$DFFF, lower 8k block
			-- switchable in mega speedy mode
			ram_rom_adr(13) <= '0';
			if (floppy_mode = mode_mega_speedy or floppy_mode = mode_config) then
				-- switchable 8k ROM block
				if (rom_bank_c000_enable) then
					ram_rom_adr(18 downto 13) <= rom_bank_c000;
					access_rom := true;
					-- always map in ROM if non-default bank is selected
					remap_rom_to_ram := false;
				else
					-- only use 2 lowest bits for bank select
					ram_rom_adr(14 downto 13) <= rom_bank_c000(14 downto 13);
					if (rw = '1') then
						access_rom := true;
					end if;
				end if;
			else
				if (rw = '1') then
					access_rom := true;
				end if;
			end if;
		when "101" =>
			-- 8k superspeedy RAM at $A000-$BFFF
			ram_rom_adr(18 downto 13) <= ram_bank;
			case floppy_mode is
			when mode_super_speedy =>
				-- limit to 256k
				ram_rom_adr(18) <= '0';
				if ram_bank(17 downto 16) /= "11" then
					-- super speedy has only 192k RAM
					access_ram := true;
				end if;
			when mode_mega_speedy | mode_config =>
				-- full 512k RAM access
				access_ram := true;
			when others => 
				null;
			end case;
		when "100" =>
			-- $8000 - $9FFF 8K Speedy RAM
			ram_rom_adr(18 downto 13) <= ram_adr_fixed_8k;
			access_ram := true;
		when "001" | "000" =>
			-- $0000-$3FFF selects 1050 chip area
			check_1050_6810_access := true;
		when others =>
			null;
		end case;
	when others =>
		null;
	end case;

	if check_1050_6810_access then
		if adr(10) = '0' and adr(9) = '0' and adr(7) = '0' then
			-- emulated 6810
			ram_rom_adr <= ram_adr_6810;
			access_ram := true;
		else
			access_1050 := true;
		end if;
	end if;
	
	if (access_rom and remap_rom_to_ram) then
		access_rom := false;
		access_ram := true;
	end if;
	
	ram_access <= access_ram;
	rom_access <= access_rom;
	int_1050_access <= access_1050;
end process address_decode;

set_control_lines: process(rw, phi2, ram_access, rom_access, int_1050_access)
begin
	-- default: all chip selects off
	rom_ce <= '1';
	ram_ce <= '1';
	ram_rom_oe <= '1';
	ram_rom_we <= '1';
	io_1050 <= '1';

	if (phi2 = '1') then
		if (rw = '1') then
			ram_rom_oe <= '0';
			ram_rom_we <= '1';
		else
			ram_rom_we <= '0';
			ram_rom_oe <= '1';
		end if;
	end if;
	
	if (ram_access) then
		ram_ce <= '0';
	end if;

	if (rom_access) then
		rom_ce <= '0';
	end if;
	
	if (int_1050_access) then
		io_1050 <= '0';
	end if;
end process set_control_lines;

-------------------------------------------------------------------------------------------------------

-- pass through data(7) from/to rom to/from CPU and read inputs and config registers
set_data: process(ram_access, rom_access, phi2, rw, adr, data, d7_ram_rom, floppy_mode, centronics_busy, i2c_data_pin,
	rom_bank_c000, rom_bank_c000_enable, rom_base_bank, floppy_mode, rom_source_is_ram,
	cfg_sw1, cfg_sw2, cfg_enc_a, cfg_enc_b, cfg_enc_ok,
	ms_speed_select, ms_write_enable, archiver_a11, turbo_speed_in, riot_ready_in, fdc_write_in)
begin
	data <= (others => 'Z');
	d7_ram_rom <= 'Z';
	if phi2 = '1' then
		if ram_access or rom_access then
			-- default: pass through d7 on ram/rom access
			if rw = '1' then
				data(7) <= d7_ram_rom;
			else
				d7_ram_rom <= data(7);
			end if;
		end if;
		case floppy_mode is
		when mode_turbo =>
			-- special d7 handling if reading ROM with A11 = 0, set D7 to centronics busy signal
			if adr(12 downto 11) = "10" and rw = '1' then
					data(7) <= centronics_busy;
			end if;
		when mode_mega_speedy | mode_config =>
			if (rw = '1') then
				case adr is
				when cfg_adr_mode_select =>
					-- read mode select
					data <= "000" & cfg_sw2 & cfg_sw1 & cfg_enc_ok & cfg_enc_b & cfg_enc_a;
				when i2c_adr_data_hi_clk_hi | i2c_adr_data_hi_clk_lo | i2c_adr_data_lo_clk_hi | i2c_adr_data_lo_clk_lo =>
					-- read I2C data line
					data(7) <= i2c_data_pin;
				when cfg_adr_rom_c000_bank =>
					if rom_bank_c000_enable then
						data <= "00" & rom_bank_c000;
					else
						data <= "10" & rom_base_bank(18 downto 15) & rom_bank_c000(14 downto 13);
					end if;
				when cfg_adr_rom_base_bank =>
					data <= rom_source_is_ram & rom_base_bank;
				when cfg_adr_floppy_mode =>
					data <= "0000" & floppy_mode;
				when ms_adr_centronics_busy =>
					data <= centronics_busy & "0000000";
				when ms_adr_speed_select =>
					data <= "0000000" & ms_speed_select;
				when ms_adr_write_enable =>
					data <= "0000000" & ms_write_enable;
				when ms_adr_test_raw_in =>
					data <= "0000" & fdc_write_in & riot_ready_in & archiver_a11 & turbo_speed_in;
				when others => null;
				end case;
			end if;
		when others =>
			null;
		end case;
	end if;
end process set_data;

-- control disk rotation speed (269/288 RPM)
set_slow_speed: process(floppy_mode, turbo_speed_in, riot_ready_inout, ms_speed_select)
begin
	turbo_speed_out <= '0';
	case floppy_mode is
	when mode_turbo =>
		turbo_speed_out <= turbo_speed_in;
	when mode_super_archiver =>
		if riot_ready_inout = '1' then
			turbo_speed_out <= '0';
		else
			turbo_speed_out <= '1';
		end if;
	when mode_mega_speedy | mode_config =>
		turbo_speed_out <= not ms_speed_select;
	when others =>
		null;
	end case;
end process set_slow_speed;

-------------------------------------------------------------------------------------------------------

-- set superspeedy ram bank
set_super_ram_bank: process(phi2)
begin
	if (falling_edge(phi2)) then
		if (reset = '0') then
			ram_bank <= (others => '0');
		else
			case floppy_mode is
			when mode_super_speedy =>
				if (rw = '0') and (adr(15 downto 13) = "011") then
					-- bank select via $6xxx/$7xxx
					ram_bank <= '0' & data(4 downto 0);
				end if;
			when mode_mega_speedy | mode_config =>
				if (rw = '0') and (adr(15 downto 12) = x"6") then
					-- bank select via $6xxx
					ram_bank <= data(5 downto 0);
				end if;
			when others => null;
			end case;
		end if;
	end if;
end process set_super_ram_bank;

-- set i2c interface
set_i2c: process(phi2)
begin
	if (falling_edge(phi2)) then
		if (reset = '0') then
			i2c_data <= '1';
			i2c_clk <= '1';
		else
			case floppy_mode is
			when mode_mega_speedy | mode_config =>
				if rw = '0' then
					case adr is
					when i2c_adr_data_hi_clk_hi =>
						i2c_data <= '1';
						i2c_clk <= '1';
					when i2c_adr_data_hi_clk_lo =>
						i2c_data <= '1';
						i2c_clk <= '0';
					when i2c_adr_data_lo_clk_hi =>
						i2c_data <= '0';
						i2c_clk <= '1';
					when i2c_adr_data_lo_clk_lo =>
						i2c_data <= '0';
						i2c_clk <= '0';
					when others => null;
					end case;
				end if;
			when others => null;
			end case;
		end if;
	end if;
end process set_i2c;

-- set switchable rom bank ($C000-$DFFF)in megaspeedy mode
set_rom_bank_c000: process(phi2)
begin
	if (falling_edge(phi2)) then
		if (reset = '0') then
			-- default: use bits 18-15 from rom_base_bank and select sub-bank 2
			rom_bank_c000 <= "000010";
			rom_bank_c000_enable <= false;
		else
			case floppy_mode is
			when mode_mega_speedy | mode_config =>
				if (rw = '0') and (adr = cfg_adr_rom_c000_bank) then
					-- bank select via $70xx
					-- enable with D7 = 0
					-- set bank with D0..D5
					rom_bank_c000 <= data(5 downto 0);
					rom_bank_c000_enable <= (data(7) = '0');
				end if;
			when others =>	null;
			end case;
		end if;
	end if;
end process set_rom_bank_c000;

-- set happy A12 ROM address line
set_happy_a12: process(phi2)
begin
	if (falling_edge(phi2)) then
		if (reset = '0') then
			happy_a12 <= '1';
		else
			case floppy_mode is
			when mode_happy =>
				if adr(15 downto 14) = "11" and adr(11 downto 4) = x"ff" then
					-- rom access at $C000-$FFFF isn't fully decoded, A13 and A12 are missing
					-- bank select via $xff8 and $xff9
					case adr(3 downto 0) is
					when x"8" =>
						happy_a12 <= '0';
					when x"9" =>
						happy_a12 <= '1';
					when others =>
						null;
					end case;
				end if;
			when others => null;
			end case;
		end if;
	end if;
end process set_happy_a12;

-- set centronics registers, serial interface to LS164 and strobe
set_centronics_registers: process(phi2)
begin
	if (falling_edge(phi2)) then
		if (reset = '0') then
			turbo_centronics_strobe <= '1';
			turbo_centronics_data <= '1';
			turbo_centronics_clk <= '1';
		else
			case floppy_mode is
			when mode_turbo =>
				if adr(12 downto 11) = "10" then
					turbo_centronics_strobe <= (not d7_ram_rom) or data(6);
					turbo_centronics_data <= d7_ram_rom and data(5);
					turbo_centronics_clk <= d7_ram_rom and data(4);
				end if;
			when mode_mega_speedy | mode_config =>
				if (rw = '0') then
					case adr is 
					when ms_adr_centronics_data_lo_clk_lo =>
						turbo_centronics_data <= '0';
						turbo_centronics_clk <= '0';
					when ms_adr_centronics_data_lo_clk_hi =>
						turbo_centronics_data <= '0';
						turbo_centronics_clk <= '1';
					when ms_adr_centronics_data_hi_clk_lo =>
						turbo_centronics_data <= '1';
						turbo_centronics_clk <= '0';
					when ms_adr_centronics_data_hi_clk_hi =>
						turbo_centronics_data <= '1';
						turbo_centronics_clk <= '1';
					when ms_adr_centronics_strobe_lo =>
						turbo_centronics_strobe <= '0';
					when ms_adr_centronics_strobe_hi =>
						turbo_centronics_strobe <= '1';
					when others => null;
					end case;
				end if;
			when others => null;
			end case;
		end if;
	end if;
end process set_centronics_registers;

-- set turbo rom bank, a11 and a12 address lines
set_turbo_rom_bank: process(phi2)
begin
	if (falling_edge(phi2)) then
		if (reset = '0') then
			turbo_rom_adr <= "11";
		else
			case floppy_mode is
			when mode_turbo =>
				if adr(12 downto 11) = "10" then
					if (d7_ram_rom = '0') then
						turbo_rom_adr(12) <= not data(5);
						turbo_rom_adr(11) <= data(4) or data(6);
					end if;
				end if;
			when others => null;
			end case;
		end if;
	end if;
end process set_turbo_rom_bank;

-- set slow RPM mode and write enable in megaspeedy mode
set_megaspeedy_registers: process(phi2)
begin
	if (falling_edge(phi2)) then
		if (reset = '0') then
			ms_write_enable <= '1';
			ms_speed_select <= '1';
		else
			case floppy_mode is
			when mode_mega_speedy | mode_config =>
				if (rw = '0') then
					case adr is
					when ms_adr_speed_select =>
						ms_speed_select <= data(0);
					when ms_adr_write_enable =>
						ms_write_enable <= data(0);
					when others => null;
					end case;
				end if;
			when others => null;
			end case;
		end if;
	end if;
end process set_megaspeedy_registers;

-- set track display and density LEDs
-- common-anode configuration is used, outputs are active low
set_display: process(phi2)
begin
	if (falling_edge(phi2)) then
		if (reset = '0') then
			track_lo <= "1111111";
			track_hi <= "1111111";
			density <= "111";
		else
			case floppy_mode is
			when mode_speedy | mode_super_speedy | mode_mega_speedy | mode_config =>
				-- select display at $4000-$5fff, or also $6000-$7fff in speedy mode
				if (rw = '0') and (adr(15 downto 13) = "010" or (floppy_mode = mode_speedy and adr(15 downto 13) = "011")) then
					case adr(1 downto 0) is
						when "00" => track_lo <= data(6 downto 0) xor "1111111";
						when "01" => track_hi <= data(6 downto 0) xor "1111111";
						when "10" => density <= data(2 downto 0) xor "111";
						when "11" => summer <= not summer;
						when others => null;
					end case;
				end if;
			when mode_happy =>
				-- select display at $4000-$7fff, emulate 7-segment encoder
				if (rw = '0') and adr(15 downto 14) = "01" then
					case data(3 downto 0) is
					when x"0" => track_lo <= "1000000";
					when x"1" => track_lo <= "1111001";
					when x"2" => track_lo <= "0100100";
					when x"3" => track_lo <= "0110000";
					when x"4" => track_lo <= "0011001";
					when x"5" => track_lo <= "0010010";
					when x"6" => track_lo <= "0000010";
					when x"7" => track_lo <= "1111000";
					when x"8" => track_lo <= "0000000";
					when x"9" => track_lo <= "0010000";
					when x"a" => track_lo <= "0001000";
					when x"b" => track_lo <= "0000011";
					when x"c" => track_lo <= "1000110";
					when x"d" => track_lo <= "0100001";
					when x"e" => track_lo <= "0000110";
					when x"f" => track_lo <= "0001110";
					when others =>
						track_lo <= "1111111";
					end case;
					
					case data(5 downto 4) is
					when "00" => track_hi <= "1000000";
					when "01" => track_hi <= "1111001";
					when "10" => track_hi <= "0100100";
					when "11" => track_hi <= "0110000";
					when others =>
						track_hi <= "1111111";
					end case;
					
					density(2) <= not data(6);
					density(0) <= data(6) or data(7);
					density(1) <= not data(7);
				end if;

			when others =>
				null;
			end case;
		end if;
	end if;
end process set_display;

-- config LED output connects to anode, output is active-high
set_config_led: process(phi2)
begin
	if (falling_edge(phi2)) then
		if (reset = '0') then
			cfg_led <= '0';
		else
			case floppy_mode is
			when mode_mega_speedy | mode_config =>
				case adr is
				when cfg_adr_config_led =>
					cfg_led <= data(0);
				when others => null;
				end case;
			when others => null;
			end case;
		end if;
	end if;
end process set_config_led;

set_floppy_mode: process(phi2)
begin
	if (falling_edge(phi2)) then
		if (reset = '0') then
			floppy_mode <= mode_config;
			rom_base_bank <= rom_bank_config;
			rom_source_is_ram <= '0';
		else
			case floppy_mode is
			when mode_mega_speedy | mode_config =>
				case adr is
				when cfg_adr_rom_base_bank =>
					rom_source_is_ram <= data(7);
					rom_base_bank <= data(6 downto 0);
				when cfg_adr_floppy_mode =>
					floppy_mode <= data(3 downto 0);
				when others => null;
				end case;
			when others => null;
			end case;
		end if;
	end if;
end process set_floppy_mode;

set_riot_ready: process(floppy_mode, riot_ready_in, riot_ready_inout)
begin
	case floppy_mode is
	when mode_super_archiver =>
		-- this pin is used as input to control slow disk rotation
		riot_ready_inout <= 'Z';
	when others =>
		-- pass through signal so drive firmware can read the ready signal
		riot_ready_inout <= riot_ready_in;
	end case;
end process set_riot_ready;

set_fdc_write_out: process(floppy_mode, archiver_a11, fdc_write_in, ms_write_enable)
begin
	case floppy_mode is
	when mode_super_archiver =>
		-- archiver_a11 input gates FDC write signal
		fdc_write_out <= fdc_write_in and archiver_a11;
	when mode_mega_speedy | mode_config =>
		if (ms_write_enable = '1') then
			fdc_write_out <= fdc_write_in;
		else
			fdc_write_out <= '0';
		end if;
	when others =>
		fdc_write_out <= fdc_write_in;
	end case;
end process set_fdc_write_out;

centronics_data <= turbo_centronics_data;
centronics_clk <= turbo_centronics_clk;
centronics_strobe <= turbo_centronics_strobe;

-- make 7-segment and LED outputs open collector
gen_track: for I in 0 to 6 generate
		track_lo_out(I) <= '0' when track_lo(I) = '0' else 'Z';
		track_hi_out(I) <= '0' when track_hi(I) = '0' else 'Z';
end generate gen_track;

gen_density: for I in 0 to 2 generate
		density_out(I) <= '0' when density(I) = '0' else 'Z';
end generate gen_density;

end;
