--  The!Cart main CPLD logic
--
--  Copyright (C) 2011-2026 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.
--
--  Compiler used for development: Xilinx ISE 12.4
--  Target device: Xilinx XC95144XL TQ100
--
--  Version history:
--
--  2026-01-30:
--    * refactor set_config and config_io to save space
--    * add MegaCart 4MB mode
--
--  2014-02-09:
--    * move constraints to UCF file
--    * set databus only when PHI2=1
--    * fix readback of SIC registers
--
--  2013-12-01: internal testing version
--    * add configuration lockout
--    * add SIC cart register readback
--    * don't limit OSS bankswitching to $D50x
--
--  2013-10-27: initial release version
--
--  2011-11-03: first development version

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library work;
use work.all;
use CartDef.all;

entity TheCart is
    port(
        adr:  in std_logic_vector(12 downto 0);
        data: inout std_logic_vector(7 downto 0);
        rw: in std_logic;
        phi2: in std_logic;
        phi2short: in std_logic;
        reset_n: in std_logic;
        cctl: in std_logic;
        s4: in std_logic;
        s5: in std_logic;
        rd4: out std_logic;
        rd5: out std_logic;
 
        ram_rom_adr: out std_logic_vector(26 downto 0);
        ram_rom_data: inout std_logic_vector(7 downto 0);

        ram_rom_oe: out std_logic;
        ram_rom_we: out std_logic;

        ram_ce: out std_logic;
        rom_ce: out std_logic;

        rom_reset: out std_logic := '0';

        -- test led on development board
	mod_en: out std_logic;

        eeprom_cs: out std_logic := '1';
        eeprom_sck: out std_logic := '0';
        eeprom_so: in std_logic;
        eeprom_si: out std_logic
    );

end TheCart;

architecture Behavioral of TheCart is

signal cart_mem: cart_mem_output;
signal cart_data: data_output;
signal spi_data: data_output;

signal reset_n_sync1: std_logic := '0';
signal reset_n_sync: std_logic := '0';

signal powerup_n: std_logic := '0';

-- lock configuration
signal cfg_lock: std_logic := '0';

signal cctl_access: CartControlState;

begin
	-- two-stage synchronizer for reset input
	sync_reset: process(phi2short)
	begin
		if falling_edge(phi2short) then
			reset_n_sync <= reset_n_sync1;
			reset_n_sync1 <= reset_n;
		end if;
	end process sync_reset;

	-- powerup signal: initialized to 0 by default,
	-- set to 1 once reset_n goes high
	powerup: process(phi2short)
	begin
		if falling_edge(phi2short) then
			if (reset_n_sync = '1') then
				powerup_n <= '1';
			end if;
		end if;
	end process powerup;

	check_cctl: process(adr, cctl, cfg_lock)
	begin
		if (cctl = '0') then
			if (cfg_lock = '0') and (adr(7 downto 4) = cfg_base_adr(7 downto 4)) then
				cctl_access <= access_cctl_thecart;
			else
				cctl_access <= access_cctl_emulated;
			end if;
		else
			cctl_access <= no_cctl_access;
		end if;
	end process check_cctl;

	config_lock: process(phi2short)
	begin
		if falling_edge(phi2short) then
			if (reset_n_sync = '0') then
				cfg_lock <= '0';
			else
				if (cctl_access = access_cctl_thecart) and (rw = '0') and (adr(3 downto 0) = cfg_lock_adr) then
					cfg_lock <= '1';
				end if;
			end if;
		end if;
	end process config_lock;

	use_cart_logic: entity CartLogic
	port map(
		clk_register => phi2short,
		a => adr,
		cctl_access => cctl_access,
		d_in => data,
		rw => rw,
		reset_n => reset_n_sync,
		s4 => s4,
		s5 => s5,
		mem_out => cart_mem,
		data_out => cart_data
	);

	use_cart_spi: entity CartSPI
	port map(
		clk_register => phi2short,
		a => adr,
		cctl_access => cctl_access,
		d_in => data,
		rw => rw,
		reset_n => reset_n_sync,
		cs => eeprom_cs,
		sck => eeprom_sck,
		si => eeprom_si,
		so => eeprom_so,
		output => spi_data
	);

	set_ram_rom: process(cart_mem, cart_data, spi_data, phi2, phi2short, rw)
	begin
		ram_rom_adr <= cart_mem.adr;
		ram_rom_oe <= '1';
		ram_rom_we <= '1';
		ram_ce <= '1';
		rom_ce <= '1';
		rd4 <= '0';
		rd5 <= '0';
		data <= (others => 'Z');
		ram_rom_data <= (others => 'Z');

		if (rw = '1') then
			ram_rom_oe <= NOT phi2;
		else
			ram_rom_we <= NOT phi2short;
		end if;
	    
		if cart_mem.ram_access then
			ram_ce <= '0';
		end if;
	    
		if cart_mem.rom_access then
			rom_ce <= '0';
		end if;

		if (rw = '1') then
			if (phi2 = '1') then
				if (cart_data.dout_enable or spi_data.dout_enable) then
					data <= cart_data.dout or spi_data.dout;
				else
					if (cart_mem.ram_access or cart_mem.rom_access) then
						data <= ram_rom_data;
					end if;
				end if;
			end if;
		else
			ram_rom_data <= data;
		end if;

		if (cart_mem.rd4) then
			rd4 <= '1';
		end if;
		if (cart_mem.rd5) then
			rd5 <= '1';
		end if;

	end process set_ram_rom;

	rom_reset <= powerup_n;

	mod_en <= cfg_lock;

end Behavioral;

