---------------------------------------------------------------------------
-- (c) 2018 mark watson
-- I am happy for anyone to use this for non-commercial use.
-- If my vhdl files are used commercially or otherwise sold,
-- please contact me for explicit permission at scrameta (gmail).
-- This applies for source and binary form and derived works.
---------------------------------------------------------------------------

LIBRARY ieee;
USE ieee.std_logic_1164.all; 
use ieee.numeric_std.all;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

LIBRARY work;

ENTITY pokeymax IS 
	GENERIC
	(
		stereo : integer := 1 -- 0=MONO,1=STEREO
	);
	PORT
	(
		PHI2 : IN STD_LOGIC;
		
		CLK_OUT : OUT STD_LOGIC; -- Use PHI2 and internal oscillator to create a clock, feed out here
		CLK_SLOW : IN STD_LOGIC; -- ... and back in here, then to pll!		
		
		D :  INOUT  STD_LOGIC_VECTOR(7 DOWNTO 0);
		A :  IN  STD_LOGIC_VECTOR(4 DOWNTO 0);
		W_N : IN STD_LOGIC;
		IRQ : INOUT STD_LOGIC;
		SOD : OUT STD_LOGIC;
		ACLK : OUT STD_LOGIC;
		BCLK : INOUT STD_LOGIC;
		SID : IN STD_LOGIC;
		CS_COMB : IN STD_LOGIC;

		AUD : OUT STD_LOGIC_VECTOR(4 DOWNTO 1);

		PADDLE : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
--set_location_assignment PIN_M1 -to PADDLE[0] (pin3/potch7/pot7/p0)
--set_location_assignment PIN_M2 -to PADDLE[1] (pin2/potch6/pot6/p1)
--set_location_assignment PIN_N2 -to PADDLE[3] (pin0/potch5/pot5/p3)
--set_location_assignment PIN_M3 -to PADDLE[2] (pin1/potch4/pot4/p2)
--set_location_assignment PIN_N3 -to PADDLE[5] (pin7/potch3/pot3/p5)
--set_location_assignment PIN_M4 -to PADDLE[7] (pin4/potch1/pot0/p7)
--set_location_assignment PIN_N4 -to PADDLE[4] (pin6/potch2/pot2/p4)
--set_location_assignment PIN_N5 -to PADDLE[6] (pin5/potch0/pot1/p6)


		IOX_RST : OUT STD_LOGIC;
		IOX_INT : IN STD_LOGIC;
		IOX_SDA : INOUT STD_LOGIC;
		IOX_SCL : INOUT STD_LOGIC
	);
END pokeymax;		
		
ARCHITECTURE vhdl OF pokeymax IS
	component int_osc is
	port (
		clkout : out std_logic;        -- clkout.clk
		oscena : in  std_logic := '0'  -- oscena.oscena
	);
	end component;

	component hq_dac
	port (
	  reset :in std_logic;
	  clk :in std_logic;
	  clk_ena : in std_logic;
	  pcm_in : in std_logic_vector(19 downto 0);
	  dac_out : out std_logic
	);
	end component;

	component pll
		port (
			inclk0   : in  std_logic := '0';
			c0 : out std_logic;
			locked   : out std_logic
		);
	end component;

	signal OSC_CLK : std_logic;
	signal PHI2_6X : std_logic;

	signal CLK : std_logic;
	signal RESET_N : std_logic;

	signal ENABLE_CYCLE : std_logic;

	-- POKEY
	SIGNAL	POKEY_DO :  STD_LOGIC_VECTOR(7 DOWNTO 0);
	SIGNAL	POKEY_WRITE_ENABLE :  STD_LOGIC;
	signal POKEY1_CHANNEL0 : std_logic_vector(3 downto 0);
	signal POKEY1_CHANNEL1 : std_logic_vector(3 downto 0);
	signal POKEY1_CHANNEL2 : std_logic_vector(3 downto 0);
	signal POKEY1_CHANNEL3 : std_logic_vector(3 downto 0);
	
	SIGNAL	POKEY2_DO :  STD_LOGIC_VECTOR(7 DOWNTO 0);
	SIGNAL	POKEY2_WRITE_ENABLE :  STD_LOGIC;
	signal POKEY2_CHANNEL0 : std_logic_vector(3 downto 0);
	signal POKEY2_CHANNEL1 : std_logic_vector(3 downto 0);
	signal POKEY2_CHANNEL2 : std_logic_vector(3 downto 0);
	signal POKEY2_CHANNEL3 : std_logic_vector(3 downto 0);

	signal SIO_CLOCKIN_IN : std_logic;
	signal SIO_CLOCKIN_OUT : std_logic;
	signal SIO_CLOCKIN_OE : std_logic;
	signal SIO_CLOCKOUT : std_logic;

	signal SIO_TXD : std_logic;
	signal SIO_RXD : std_logic;

	signal POKEY_IRQ : std_logic;

	signal POT_RESET : std_logic;

	signal ADDR_IN : std_logic_vector(4 downto 0);
	signal WRITE_DATA : std_logic_vector(7 downto 0);

	signal AUDIO_L : std_logic_vector(15 downto 0);
	signal AUDIO_R : std_logic_vector(15 downto 0);
	signal AUDIO_M : std_logic_vector(15 downto 0);

	signal AUDIO_LEFT : std_logic;
	signal AUDIO_RIGHT : std_logic;
	signal AUDIO_MIXED : std_logic;

	signal KEYBOARD_SCAN : std_logic_vector(5 downto 0);
	signal KEYBOARD_RESPONSE : std_logic_vector(1 downto 0);
	signal KEYBOARD_SCAN_ENABLE : std_logic;

	signal BUS_DATA : std_logic_vector(7 downto 0);
	signal BUS_OE : std_logic;

	signal REQUEST : std_logic;
	signal WRITE_N : std_logic;

	signal DO_MUX : std_logic_vector(7 downto 0);

	signal i2c0_ena : std_logic;
	signal i2c0_addr : std_logic_vector(7 downto 1);
	signal i2c0_rw : std_logic;
	signal i2c0_write_data : std_logic_vector(7 downto 0);
	signal i2c0_busy : std_logic;
	signal i2c0_read_data : std_logic_vector(7 downto 0);
	signal i2c0_error : std_logic;

	signal sel_pokey2 : std_logic;
BEGIN
	IOX_RST <= 'Z'; -- TODO weak pull up in pins (see TODO file)

	oscillator : int_osc
	port map 
	(
		clkout => OSC_CLK, 
		oscena => '1'
	);


	--phi_multiplier : entity work.phi_mult
	--port map 
	--(
	--	clkin => OSC_CLK,
	--	phi2 => PHI2,
	--	clkout => PHI2_6X -- 6x phi2, aligned!
	--);
	
	PHI2_6X <= OSC_CLK;

	pll_inst : pll
	PORT MAP(inclk0 => CLK_SLOW,
			 c0 => CLK, -- 27MHz 
			 locked => RESET_N);

bus_adapt : entity work.slave_timing_6502
	PORT MAP
	(
		CLK => CLK,
		RESET_N => RESET_N,
		
		-- input from the cart port
		PHI2 => PHI2,
		bus_addr => A,
		bus_data => D,
	
		-- output to the cart port
		bus_data_out => BUS_DATA,
		bus_drive => BUS_OE,
		bus_cs => CS_COMB,
		bus_rw_n => W_N,

		-- request for a memory bus cycle (read or write)
		BUS_REQUEST => REQUEST,
		ADDR_IN => ADDR_IN,
		DATA_IN => WRITE_DATA,
		RW_N => WRITE_N,

		-- end of cycle
		ENABLE_CYCLE => ENABLE_CYCLE,

		DATA_OUT => DO_MUX
	);
pokey_mixer_both : entity work.pokey_mixer_mux
PORT MAP(CLK => CLK,
		 ENABLE_179 => ENABLE_CYCLE,
		 CHANNEL_L_0 => POKEY1_CHANNEL0,
		 CHANNEL_L_1 => POKEY1_CHANNEL1,
		 CHANNEL_L_2 => POKEY1_CHANNEL2,
		 CHANNEL_L_3 => POKEY1_CHANNEL3,
		 CHANNEL_R_0 => POKEY2_CHANNEL0,
		 CHANNEL_R_1 => POKEY2_CHANNEL1,
		 CHANNEL_R_2 => POKEY2_CHANNEL2,
		 CHANNEL_R_3 => POKEY2_CHANNEL3,
		 VOLUME_OUT_M => AUDIO_M,
		 VOLUME_OUT_L => AUDIO_L,
		 VOLUME_OUT_R => AUDIO_R);
		 
pokey1 : entity work.pokey
GENERIC MAP
(
	custom_keyboard_scan => 1
)
PORT MAP(CLK => CLK,
		 ENABLE_179 => ENABLE_CYCLE,
		 WR_EN => POKEY_WRITE_ENABLE,
		 RESET_N => RESET_N,
		 SIO_IN1 => SIO_RXD,
		 SIO_IN2 => '1',
		 SIO_IN3 => '1',
		 SIO_CLOCKIN_IN => SIO_CLOCKIN_IN,
		 SIO_CLOCKIN_OUT => SIO_CLOCKIN_OUT,
		 SIO_CLOCKIN_OE => SIO_CLOCKIN_OE,
		 ADDR => ADDR_IN(3 DOWNTO 0),
		 DATA_IN => WRITE_DATA(7 DOWNTO 0),
		 keyboard_response => KEYBOARD_RESPONSE,
		 POT_IN => PADDLE,
		 IRQ_N_OUT => POKEY_IRQ,
		 SIO_OUT1 => SIO_TXD,
		 SIO_OUT2 => open,
		 SIO_OUT3 => open,
		 SIO_CLOCKOUT => SIO_CLOCKOUT,
		 POT_RESET => POT_RESET,
		 CHANNEL_0_OUT => POKEY1_CHANNEL0,
		 CHANNEL_1_OUT => POKEY1_CHANNEL1,
		 CHANNEL_2_OUT => POKEY1_CHANNEL2,
		 CHANNEL_3_OUT => POKEY1_CHANNEL3,
		 DATA_OUT => POKEY_DO,
		 keyboard_scan => KEYBOARD_SCAN,
		 keyboard_scan_enable => KEYBOARD_SCAN_ENABLE
		);

gen_stereo : if stereo=1 generate
	process(SEL_POKEY2,POKEY_DO,POKEY2_DO)
	begin
		DO_MUX <= (others =>'0');
		if (SEL_POKEY2='1') then
			DO_MUX <= POKEY2_DO;
		else
			DO_MUX <= POKEY_DO;
		end if;
	end process;

	isstereo : ENTITY work.stereo_detect
	PORT MAP
	( 
		CLK => clk,
		RESET_N => reset_n,
	
		A => A, -- raw...
		ADDR_IN => ADDR_IN, -- on request
	
		SEL_POKEY2 => sel_pokey2
	);

	POKEY_WRITE_ENABLE <= NOT(WRITE_N) and REQUEST and NOT(SEL_POKEY2);
	POKEY2_WRITE_ENABLE <= NOT(WRITE_N) and REQUEST and SEL_POKEY2;

pokey2 : entity work.pokey
PORT MAP(CLK => CLK,
		 ENABLE_179 => ENABLE_CYCLE,
		 WR_EN => POKEY2_WRITE_ENABLE,
		 RESET_N => RESET_N,
		 ADDR => ADDR_IN(3 DOWNTO 0),
		 DATA_IN => WRITE_DATA(7 DOWNTO 0),
		 CHANNEL_0_OUT => POKEY2_CHANNEL0,
		 CHANNEL_1_OUT => POKEY2_CHANNEL1,
		 CHANNEL_2_OUT => POKEY2_CHANNEL2,
		 CHANNEL_3_OUT => POKEY2_CHANNEL3,
		 DATA_OUT => POKEY2_DO,
		 SIO_IN1 => '1',
		 SIO_IN2 => '1',
		 SIO_IN3 => '1',
		 keyboard_response => "00",
		 pot_in=>"00000000");
end generate;

gen_mono : if stereo=0 generate
	POKEY2_CHANNEL0 <= "0000";
	POKEY2_CHANNEL1 <= "0000";
	POKEY2_CHANNEL2 <= "0000";
	POKEY2_CHANNEL3 <= "0000";

	DO_MUX <= POKEY_DO;

	POKEY_WRITE_ENABLE <= NOT(WRITE_N) and REQUEST;
	
end generate;

dac_left : hq_dac
port map
(
  reset => not(reset_n),
  clk => clk,
  clk_ena => '1',
  pcm_in => AUDIO_L&"0000",
  dac_out => AUDIO_LEFT
);

dac_right : hq_dac
port map
(
  reset => not(reset_n),
  clk => clk,
  clk_ena => '1',
  pcm_in => AUDIO_R&"0000",
  dac_out => AUDIO_RIGHT
);

dac_mixed : hq_dac
port map
(
  reset => not(reset_n),
  clk => clk,
  clk_ena => '1',
  pcm_in => AUDIO_M&"0000",
  dac_out => AUDIO_MIXED
);

-- io extension
-- drive to 0 for pot reset (otherwise high imp)
-- drive keyboard lines
	i2c_master0 : entity work.i2c_master
 	generic map(input_clk=>58_000_000, bus_clk=>400_000)
	port map(
		clk=>clk,
		reset_n=>reset_n,

		ena=>i2c0_ena,
		addr=>i2c0_addr,
		rw=>i2c0_rw,
		data_wr=>i2c0_write_data,
		busy=>i2c0_busy,
		data_rd=>i2c0_read_data,
		ack_error=>i2c0_error,

		sda=>IOX_SDA,
		scl=>IOX_SCL
	);

	iox_glue : entity work.iox_glue
	port map(
		clk=>clk,
		reset_n=>reset_n,

		ena=>i2c0_ena,
		addr=>i2c0_addr,
		rw=>i2c0_rw,
		write_data=>i2c0_write_data,
		busy=>i2c0_busy,
		read_data=>i2c0_read_data,
		error=>i2c0_error,

		int=>iox_int,

		pot_reset=>pot_reset,
		keyboard_scan=>keyboard_scan,
		keyboard_scan_enable=>keyboard_scan_enable,
		keyboard_response=>keyboard_response
	);

-- Wire up pins
CLK_OUT <= PHI2_6X;

ACLK <= SIO_CLOCKOUT;
BCLK <= '0' when (SIO_CLOCKIN_OE='1' and SIO_CLOCKIN_OUT='0') else 'Z';
SIO_CLOCKIN_IN <= BCLK;

SOD <= '0' when SIO_TXD='0' else 'Z';
SIO_RXD <= SID;

--3
AUD(3) <= AUDIO_LEFT;
--4
AUD(4) <= AUDIO_RIGHT;
--2
AUD(2) <= AUDIO_MIXED;
--gnd
--
--1->pin37
AUD(1) <= AUDIO_MIXED;

IRQ <= '0' when POKEY_IRQ='0' else 'Z';

D <= BUS_DATA when BUS_OE='1' else (others=>'Z');

END vhdl;
