--
--
-- Apple II+ toplevel for the MiST board
-- https://github.com/wsoltys/mist_apple2
--
-- Copyright (c) 2014 W. Soltys <wsoltys@gmail.com>
--
-- This source file 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 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/>.
--

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

entity apple2_top is port
(
	-- Clocks
	CLK_28M 			: in std_logic;
	CLK_14M 			: in std_logic;
	CPU_WAIT			: in std_logic;

	reset_in			: in std_logic;

	-- VGA output
	VGA_DE 			: out std_logic;
	VGA_CLK 			: out std_logic;
	VGA_HS 			: out std_logic;
	VGA_VS 			: out std_logic;
	VGA_R 			: out std_logic_vector(7 downto 0);
	VGA_G 			: out std_logic_vector(7 downto 0);
	VGA_B 			: out std_logic_vector(7 downto 0);
	SCREEN_MODE 	: in std_logic_vector(1 downto 0);  -- 00: Color, 01: B&W, 10:Green, 11: Amber

	-- Audio
	AUDIO_L 			: out std_logic_vector(7 downto 0);
	AUDIO_R 			: out std_logic_vector(7 downto 0);
	SPEAKER 			: out std_logic;

	ps2Clk     		: in std_logic;
	ps2Data    		: in std_logic;

	joy        		: in std_logic_vector(5 downto 0);
	joy_an     		: in std_logic_vector(15 downto 0);

	-- mocking board
	mb_enabled 		: in std_logic;

	-- disk control
	TRACK 			: out unsigned(5 downto 0);
	TRACK_RAM_ADDR : in unsigned(12 downto 0);
	TRACK_RAM_DI 	: in unsigned(7 downto 0);
	TRACK_RAM_WE 	: in std_logic;

	-- main RAM
	ram_addr 		: out std_logic_vector(17 downto 0);
	ram_dout 		: in std_logic_vector(7 downto 0);
	ram_din  		: out std_logic_vector(7 downto 0);
	ram_we 			: out std_logic;

	-- LEDG
	LED 				: out std_logic
);

end apple2_top;

architecture datapath of apple2_top is

  signal CLK_2M, PRE_PHASE_ZERO: std_logic;
  signal IO_SELECT, DEVICE_SELECT : std_logic_vector(7 downto 0);
  signal ADDR : unsigned(15 downto 0);
  signal D, PD: unsigned(7 downto 0);

  signal we_ram : std_logic;
  signal VIDEO, HBL, VBL, LD194 : std_logic;
  signal COLOR_LINE : std_logic;
  signal COLOR_LINE_CONTROL : std_logic;
  signal GAMEPORT : std_logic_vector(7 downto 0);
  signal cpu_pc : unsigned(15 downto 0);

  signal K : unsigned(7 downto 0);
  signal read_key : std_logic;

  signal flash_clk : unsigned(22 downto 0) := (others => '0');
  signal power_on_reset : std_logic := '1';
  signal reset : std_logic;

  signal a_ram: unsigned(17 downto 0);
  
  signal joyx       : std_logic;
  signal joyy       : std_logic;
  signal pdl_strobe : std_logic;

begin

  reset <= power_on_reset;

  power_on : process(CLK_14M, reset_in)
  begin
    if reset_in = '1' then
       power_on_reset <= '1';
    elsif rising_edge(CLK_14M) then
      if flash_clk(22) = '1' then
        power_on_reset <= '0';
      end if;
    end if;
  end process;
  
  -- In the Apple ][, this was a 555 timer
  flash_clkgen : process (CLK_14M, reset_in)
  begin
    if reset_in = '1' then
       flash_clk <= (others=>'0');
    elsif rising_edge(CLK_14M) then
		flash_clk <= flash_clk + 1;
    end if;     
  end process;

  -- Paddle buttons
  -- GAMEPORT input bits:
  --  7    6    5    4    3   2   1    0
  -- pdl3 pdl2 pdl1 pdl0 pb3 pb2 pb1 casette
  GAMEPORT <=  "00" & joyy & joyx & "0" & joy(5) & joy(4) & "0";
  
  process(CLK_2M, pdl_strobe)
    variable cx, cy : integer range -100 to 5800 := 0;
  begin
    if rising_edge(CLK_2M) then
      if cx > 0 then
        cx := cx -1;
        joyx <= '1';
      else
        joyx <= '0';
      end if;
      if cy > 0 then
        cy := cy -1;
        joyy <= '1';
      else
        joyy <= '0';
      end if;
      if pdl_strobe = '1' then
        cx := 2800+(22*to_integer(signed(joy_an(7 downto 0))));
        cy := 2800+(22*to_integer(signed(joy_an(15 downto 8)))); -- max 5650
        if cx < 0 then
          cx := 0;
        elsif cx >= 5590 then
          cx := 5650;
        end if;
        if cy < 0 then
          cy := 0;
        elsif cy >= 5590 then
          cy := 5650;
        end if;
      end if;
    end if;
  end process;

  COLOR_LINE_CONTROL <= COLOR_LINE and not (SCREEN_MODE(0) or SCREEN_MODE(1));  -- Color or B&W mode
  
  -- Simulate power up on cold reset to go to the disk boot routine
  ram_we   <= we_ram; -- when reset_in = '0' else '1';
  ram_addr <= std_logic_vector(a_ram); -- when reset_in = '0' else std_logic_vector(to_unsigned(1012,ram_addr'length)); -- $3F4
  ram_din  <= std_logic_vector(D); -- when reset_in = '0' else "00000000";

  core : entity work.apple2 port map (
    CLK_14M        => CLK_14M,
    CLK_2M         => CLK_2M,
	 CPU_WAIT       => CPU_WAIT,
    PRE_PHASE_ZERO => PRE_PHASE_ZERO,
    FLASH_CLK      => flash_clk(22),
    reset          => reset,
    ADDR           => ADDR,
    ram_addr       => a_ram,
    D              => D,
    ram_do         => unsigned(ram_dout),
    PD             => PD,
    ram_we         => we_ram,
    VIDEO          => VIDEO,
    COLOR_LINE     => COLOR_LINE,
    HBL            => HBL,
    VBL            => VBL,
    LD194          => LD194,
    K              => K,
    read_key       => read_key,
    AN             => open,
    GAMEPORT       => GAMEPORT,
    PDL_strobe     => pdl_strobe,
    IO_SELECT      => IO_SELECT,
    DEVICE_SELECT  => DEVICE_SELECT,
    pcDebugOut     => cpu_pc,
    speaker        => SPEAKER,
    laudio         => AUDIO_L,
    raudio         => AUDIO_R,
    mb_enabled     => mb_enabled
    );

  vga : entity work.vga_controller port map (
    CLK_28M    => CLK_28M,
    VIDEO      => VIDEO,
    COLOR_LINE => COLOR_LINE_CONTROL,
	 SCREEN_MODE => SCREEN_MODE,
    HBL        => HBL,
    VBL        => VBL,
    LD194      => LD194,
    VGA_CLK    => VGA_CLK,
    VGA_HS     => VGA_HS,
    VGA_VS     => VGA_VS,
    VGA_DE     => VGA_DE,
    std_logic_vector(VGA_R) => VGA_R,
    std_logic_vector(VGA_G) => VGA_G,
    std_logic_vector(VGA_B) => VGA_B
    );

  keyboard : entity work.keyboard port map (
    PS2_Clk  => ps2Clk,
    PS2_Data => ps2Data,
    CLK_14M  => CLK_14M,
    reset    => reset,
    reads     => read_key,
    K        => K
    );

  disk : entity work.disk_ii port map (
    CLK_14M        => CLK_14M,
    CLK_2M         => CLK_2M,
    PRE_PHASE_ZERO => PRE_PHASE_ZERO,
    IO_SELECT      => IO_SELECT(6),
    DEVICE_SELECT  => DEVICE_SELECT(6),
    RESET          => reset,
    A              => ADDR,
    D_IN           => D,
    D_OUT          => PD,
    TRACK          => TRACK,
    TRACK_ADDR     => open,
    D1_ACTIVE      => LED,
    D2_ACTIVE      => open,
    ram_write_addr => TRACK_RAM_ADDR,
    ram_di         => TRACK_RAM_DI,
    ram_we         => TRACK_RAM_WE
    );

end datapath;