--
-- A simulation model of VIC20 hardware
-- Copyright (c) MikeJ - March 2003
--
-- All rights reserved
--
-- Redistribution and use in source and synthezised forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- Redistributions in synthesized form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- Neither the name of the author nor the names of other contributors may
-- be used to endorse or promote products derived from this software without
-- specific prior written permission.
--
-- THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--
-- You are responsible for any legal issues arising from your use of this code.
--
-- The latest version of this file can be found at: www.fpgaarcade.com
--
-- Email vic20@fpgaarcade.com
--
--
-- Revision list
--
-- version 003 Clearing PB7 when T1C-H is written by hoglet
-- version 002 fix from Mark McDougall, untested
-- version 001 initial release
-- not very sure about the shift register, documentation is a bit light.

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

--library UNISIM;
--  use UNISIM.Vcomponents.all;

entity M6522 is
  port (

    I_RS              : in    std_logic_vector(3 downto 0);
    I_DATA            : in    std_logic_vector(7 downto 0);
    O_DATA            : out   std_logic_vector(7 downto 0);
    O_DATA_OE_L       : out   std_logic;

    I_RW_L            : in    std_logic;
    I_CS1             : in    std_logic;
    I_CS2_L           : in    std_logic;

    O_IRQ_L           : out   std_logic; -- note, not open drain
    -- port a
    I_CA1             : in    std_logic;
    I_CA2             : in    std_logic;
    O_CA2             : out   std_logic;
    O_CA2_OE_L        : out   std_logic;

    I_PA              : in    std_logic_vector(7 downto 0);
    O_PA              : out   std_logic_vector(7 downto 0);
    O_PA_OE_L         : out   std_logic_vector(7 downto 0);

    -- port b
    I_CB1             : in    std_logic;
    O_CB1             : out   std_logic;
    O_CB1_OE_L        : out   std_logic;

    I_CB2             : in    std_logic;
    O_CB2             : out   std_logic;
    O_CB2_OE_L        : out   std_logic;

    I_PB              : in    std_logic_vector(7 downto 0);
    O_PB              : out   std_logic_vector(7 downto 0);
    O_PB_OE_L         : out   std_logic_vector(7 downto 0);

    I_P2_H            : in    std_logic; -- high for phase 2 clock  ____----__
    RESET_L           : in    std_logic;
    ENA_4             : in    std_logic; -- clk enable
    CLK               : in    std_logic
    );
end;

architecture RTL of M6522 is

  signal phase             : std_logic_vector(1 downto 0);
  signal p2_h_t1           : std_logic;
  signal cs                : std_logic;

  -- registers
  signal r_ddra            : std_logic_vector(7 downto 0);
  signal r_ora             : std_logic_vector(7 downto 0);
  signal r_ira             : std_logic_vector(7 downto 0);

  signal r_ddrb            : std_logic_vector(7 downto 0);
  signal r_orb             : std_logic_vector(7 downto 0);
  signal r_irb             : std_logic_vector(7 downto 0);

  signal r_t1l_l           : std_logic_vector(7 downto 0);
  signal r_t1l_h           : std_logic_vector(7 downto 0);
  signal r_t2l_l           : std_logic_vector(7 downto 0);
  signal r_t2l_h           : std_logic_vector(7 downto 0); -- not in real chip
  signal r_sr              : std_logic_vector(7 downto 0);
  signal r_acr             : std_logic_vector(7 downto 0);
  signal r_pcr             : std_logic_vector(7 downto 0);
  signal r_ifr             : std_logic_vector(7 downto 0);
  signal r_ier             : std_logic_vector(6 downto 0);

  signal sr_write_ena      : boolean;
  signal sr_read_ena       : boolean;
  signal ifr_write_ena     : boolean;
  signal ier_write_ena     : boolean;
  signal clear_irq         : std_logic_vector(7 downto 0);
  signal load_data         : std_logic_vector(7 downto 0);

  -- timer 1
  signal t1c               : std_logic_vector(15 downto 0);
  signal t1c_active        : boolean;
  signal t1c_done          : boolean;
  signal t1_w_reset_int    : boolean;
  signal t1_r_reset_int    : boolean;
  signal t1_load_counter   : boolean;
  signal t1_reload_counter : boolean;
  signal t1_toggle         : std_logic;
  signal t1_irq            : std_logic := '0';

  -- timer 2
  signal t2c               : std_logic_vector(15 downto 0);
  signal t2c_active        : boolean;
  signal t2c_done          : boolean;
  signal t2_pb6            : std_logic;
  signal t2_pb6_t1         : std_logic;
  signal t2_w_reset_int    : boolean;
  signal t2_r_reset_int    : boolean;
  signal t2_load_counter   : boolean;
  signal t2_reload_counter : boolean;
  signal t2_irq            : std_logic := '0';
  signal t2_sr_ena         : boolean;

  -- shift reg
  signal sr_cnt            : std_logic_vector(3 downto 0);
  signal sr_cb1_oe_l       : std_logic;
  signal sr_cb1_out        : std_logic;
  signal sr_drive_cb2      : std_logic;
  signal sr_strobe         : std_logic;
  signal sr_strobe_t1      : std_logic;
  signal sr_strobe_falling : boolean;
  signal sr_strobe_rising  : boolean;
  signal sr_irq            : std_logic;
  signal sr_out            : std_logic;
  signal sr_off_delay      : std_logic;

  -- io
  signal w_orb_hs          : std_logic;
  signal w_ora_hs          : std_logic;
  signal r_irb_hs          : std_logic;
  signal r_ira_hs          : std_logic;

  signal ca_hs_sr          : std_logic;
  signal ca_hs_pulse       : std_logic;
  signal cb_hs_sr          : std_logic;
  signal cb_hs_pulse       : std_logic;

  signal cb1_in_mux        : std_logic;
  signal ca1_ip_reg        : std_logic;
  signal cb1_ip_reg        : std_logic;
  signal ca1_int           : boolean;
  signal cb1_int           : boolean;
  signal ca1_irq           : std_logic;
  signal cb1_irq           : std_logic;

  signal ca2_ip_reg        : std_logic;
  signal cb2_ip_reg        : std_logic;
  signal ca2_int           : boolean;
  signal cb2_int           : boolean;
  signal ca2_irq           : std_logic;
  signal cb2_irq           : std_logic;

  signal final_irq         : std_logic;
begin
  p_phase : process
  begin
    -- internal clock phase
    wait until rising_edge(CLK);
    if (ENA_4 = '1') then
      p2_h_t1 <= I_P2_H;
      if (p2_h_t1 = '0') and (I_P2_H = '1') then
        phase <= "11";
      else
        phase <= phase + "1";
      end if;
    end if;
  end process;

  p_cs : process(I_CS1, I_CS2_L, I_P2_H)
  begin
    cs <= '0';
    if (I_CS1 = '1') and (I_CS2_L = '0') and (I_P2_H = '1') then
      cs <= '1';
    end if;
  end process;

  -- peripheral control reg (pcr)
  -- 0      ca1 interrupt control (0 +ve edge, 1 -ve edge)
  -- 3..1   ca2 operation
  --        000 input -ve edge
  --        001 independend interrupt input -ve edge
  --        010 input +ve edge
  --        011 independend interrupt input +ve edge
  --        100 handshake output
  --        101 pulse output
  --        110 low output
  --        111 high output
  -- 7..4   as 3..0 for cb1,cb2

  -- auxiliary control reg (acr)
  -- 0      input latch PA (0 disable, 1 enable)
  -- 1      input latch PB (0 disable, 1 enable)
  -- 4..2   shift reg control
  --        000 disable
  --        001 shift in using t2
  --        010 shift in using o2
  --        011 shift in using ext clk
  --        100 shift out free running t2 rate
  --        101 shift out using t2
  --        101 shift out using o2
  --        101 shift out using ext clk
  -- 5      t2 timer control (0 timed interrupt, 1 count down with pulses on pb6)
  -- 7..6   t1 timer control
  --        00 timed interrupt each time t1 is loaded   pb7 disable
  --        01 continuous interrupts                    pb7 disable
  --        00 timed interrupt each time t1 is loaded   pb7 one shot output
  --        01 continuous interrupts                    pb7 square wave output
  --

  p_write_reg_reset : process(RESET_L, CLK)
  begin
    if (RESET_L = '0') then
      r_ora   <= x"00";    r_orb   <= x"00";
      r_ddra  <= x"00";    r_ddrb  <= x"00";
      r_acr   <= x"00";    r_pcr   <= x"00";

      w_orb_hs <= '0';
      w_ora_hs <= '0';
    elsif rising_edge(CLK) then
      if (ENA_4 = '1') then
        w_orb_hs <= '0';
        w_ora_hs <= '0';
        if (cs = '1') and (I_RW_L = '0') then
          case I_RS is
            when x"0" => r_orb     <= I_DATA; w_orb_hs <= '1';
            when x"1" => r_ora     <= I_DATA; w_ora_hs <= '1';
            when x"2" => r_ddrb    <= I_DATA;
            when x"3" => r_ddra    <= I_DATA;

            when x"B" => r_acr     <= I_DATA;
            when x"C" => r_pcr     <= I_DATA;
            when x"F" => r_ora     <= I_DATA;

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

        if (r_acr(7) = '1') then
          if t1_load_counter then
            r_orb(7) <= '0'; -- writing T1C-H resets bit 7
          elsif (t1_toggle = '1') then
            r_orb(7) <= not r_orb(7); -- toggle
          end if;
        end if;
      end if;
    end if;
  end process;

  p_write_reg : process
  begin
    wait until rising_edge(CLK);
    if (ENA_4 = '1') then
      t1_w_reset_int  <= false;
      t1_load_counter <= false;

      t2_w_reset_int  <= false;
      t2_load_counter <= false;

      load_data <= x"00";
      sr_write_ena <= false;
      ifr_write_ena <= false;
      ier_write_ena <= false;

      if (cs = '1') and (I_RW_L = '0') then
        load_data <= I_DATA;
        case I_RS is
          when x"4" => r_t1l_l   <= I_DATA;
          when x"5" => r_t1l_h   <= I_DATA; t1_w_reset_int  <= true;
                                            t1_load_counter <= true;

          when x"6" => r_t1l_l   <= I_DATA;
          when x"7" => r_t1l_h   <= I_DATA; t1_w_reset_int  <= true;

          when x"8" => r_t2l_l   <= I_DATA;
          when x"9" => r_t2l_h   <= I_DATA; t2_w_reset_int  <= true;
                                            t2_load_counter <= true;

          when x"A" => sr_write_ena  <= true;
          when x"D" => ifr_write_ena <= true;
          when x"E" => ier_write_ena <= true;

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

  p_oe : process(cs, I_RW_L)
  begin
    O_DATA_OE_L <= '1';
    if (cs = '1') and (I_RW_L = '1') then
      O_DATA_OE_L <= '0';
    end if;
  end process;

  p_read : process
  begin
	wait until rising_edge(CLK);
	
	if ENA_4 = '1' then
		t1_r_reset_int <= false;
		t2_r_reset_int <= false;
		sr_read_ena <= false;
		r_irb_hs <= '0';
		r_ira_hs <= '0';
		
		if (cs = '1') and (I_RW_L = '1') then
		  case I_RS is
			--when x"0" => O_DATA <= r_irb; r_irb_hs <= '1';
			-- fix from Mark McDougall, untested
			when x"0" => O_DATA <= (r_irb and not r_ddrb) or (r_orb and r_ddrb); r_irb_hs <= '1';
			when x"1" => O_DATA <= r_ira; r_ira_hs <= '1';
			when x"2" => O_DATA <= r_ddrb;
			when x"3" => O_DATA <= r_ddra;
			when x"4" => O_DATA <= t1c( 7 downto 0);  t1_r_reset_int <= true;
			when x"5" => O_DATA <= t1c(15 downto 8);
			when x"6" => O_DATA <= r_t1l_l;
			when x"7" => O_DATA <= r_t1l_h;
			when x"8" => O_DATA <= t2c( 7 downto 0);  t2_r_reset_int <= true;
			when x"9" => O_DATA <= t2c(15 downto 8);
			when x"A" => O_DATA <= r_sr;              sr_read_ena <= true;
			when x"B" => O_DATA <= r_acr;
			when x"C" => O_DATA <= r_pcr;
			when x"D" => O_DATA <= r_ifr;
			when x"E" => O_DATA <= ('0' & r_ier);
			when x"F" => O_DATA <= r_ira;
			when others => null;
		  end case;
		end if;
	end if;

  end process;
  --
  -- IO
  --
  p_ca1_cb1_sel : process(sr_cb1_oe_l, sr_cb1_out, I_CB1)
  begin
    -- if the shift register is enabled, cb1 may be an output
    -- in this case, we should listen to the CB1_OUT for the interrupt
    if (sr_cb1_oe_l = '1') then
      cb1_in_mux <= I_CB1;
    else
      cb1_in_mux <= sr_cb1_out;
    end if;
  end process;

  p_ca1_cb1_int : process(r_pcr, ca1_ip_reg, I_CA1, cb1_ip_reg, cb1_in_mux)
  begin
    if (r_pcr(0) = '0') then -- ca1 control
      -- negative edge
      ca1_int <= (ca1_ip_reg = '1') and (I_CA1 = '0');
    else
      -- positive edge
      ca1_int <= (ca1_ip_reg = '0') and (I_CA1 = '1');
    end if;

    if (r_pcr(4) = '0') then -- cb1 control
      -- negative edge
      cb1_int <= (cb1_ip_reg = '1') and (cb1_in_mux = '0');
    else
      -- positive edge
      cb1_int <= (cb1_ip_reg = '0') and (cb1_in_mux = '1');
    end if;
  end process;

  p_ca2_cb2_int : process(r_pcr, ca2_ip_reg, I_CA2, cb2_ip_reg, I_CB2)
  begin
    ca2_int <= false;
    if (r_pcr(3) = '0') then -- ca2 input
      if (r_pcr(2) = '0') then -- ca2 edge
        -- negative edge
        ca2_int <= (ca2_ip_reg = '1') and (I_CA2 = '0');
      else
        -- positive edge
        ca2_int <= (ca2_ip_reg = '0') and (I_CA2 = '1');
      end if;
    end if;

    cb2_int <= false;
    if (r_pcr(7) = '0') then -- cb2 input
      if (r_pcr(6) = '0') then -- cb2 edge
        -- negative edge
        cb2_int <= (cb2_ip_reg = '1') and (I_CB2 = '0');
      else
        -- positive edge
        cb2_int <= (cb2_ip_reg = '0') and (I_CB2 = '1');
      end if;
    end if;
  end process;

  p_ca2_cb2 : process(RESET_L, CLK)
  begin
    if (RESET_L = '0') then
      O_CA2       <= '0';
      O_CA2_OE_L  <= '1';
      O_CB2       <= '0';
      O_CB2_OE_L  <= '1';

      ca_hs_sr <= '0';
      ca_hs_pulse <= '0';
      cb_hs_sr <= '0';
      cb_hs_pulse <= '0';
    elsif rising_edge(CLK) then
      if (ENA_4 = '1') then
      -- ca
        if (phase = "00") and ((w_ora_hs = '1') or (r_ira_hs = '1')) then
          ca_hs_sr <= '1';
        elsif ca1_int then
          ca_hs_sr <= '0';
        end if;

        if (phase = "00") then
          ca_hs_pulse <= w_ora_hs or r_ira_hs;
        end if;

        O_CA2_OE_L <= not r_pcr(3); -- ca2 output
        case r_pcr(3 downto 1) is
          when "000" => O_CA2 <= '0'; -- input
          when "001" => O_CA2 <= '0'; -- input
          when "010" => O_CA2 <= '0'; -- input
          when "011" => O_CA2 <= '0'; -- input
          when "100" => O_CA2 <= not (ca_hs_sr); -- handshake
          when "101" => O_CA2 <= not (ca_hs_pulse); -- pulse
          when "110" => O_CA2 <= '0'; -- low
          when "111" => O_CA2 <= '1'; -- high
          when others => null;
        end case;

        -- cb
        if (phase = "00") and (w_orb_hs = '1') then
          cb_hs_sr <= '1';
        elsif cb1_int then
          cb_hs_sr <= '0';
        end if;

        if (phase = "00") then
          cb_hs_pulse <= w_orb_hs;
        end if;

        O_CB2_OE_L <= not (r_pcr(7) or sr_drive_cb2); -- cb2 output or serial
        if (sr_drive_cb2 = '1') then -- serial output
          O_CB2 <= sr_out;
        else
          case r_pcr(7 downto 5) is
            when "000" => O_CB2 <= '0'; -- input
            when "001" => O_CB2 <= '0'; -- input
            when "010" => O_CB2 <= '0'; -- input
            when "011" => O_CB2 <= '0'; -- input
            when "100" => O_CB2 <= not (cb_hs_sr); -- handshake
            when "101" => O_CB2 <= not (cb_hs_pulse); -- pulse
            when "110" => O_CB2 <= '0'; -- low
            when "111" => O_CB2 <= '1'; -- high
            when others => null;
          end case;
        end if;
      end if;
    end if;
  end process;
  O_CB1      <= sr_cb1_out;
  O_CB1_OE_L <= sr_cb1_oe_l;

  p_ca_cb_irq : process(RESET_L, CLK)
  begin
    if (RESET_L = '0') then
      ca1_irq <= '0';
      ca2_irq <= '0';
      cb1_irq <= '0';
      cb2_irq <= '0';
    elsif rising_edge(CLK) then
      if (ENA_4 = '1') then
        -- not pretty
        if ca1_int then
          ca1_irq <= '1';
        elsif (r_ira_hs = '1') or (w_ora_hs = '1') or (clear_irq(1) = '1') then
          ca1_irq <= '0';
        end if;

        if ca2_int then
          ca2_irq <= '1';
        else
          if (((r_ira_hs = '1') or (w_ora_hs = '1')) and (r_pcr(1) = '0')) or
             (clear_irq(0) = '1') then
            ca2_irq <= '0';
          end if;
        end if;

        if cb1_int then
          cb1_irq <= '1';
        elsif (r_irb_hs = '1') or (w_orb_hs = '1') or (clear_irq(4) = '1') then
          cb1_irq <= '0';
        end if;

        if cb2_int then
          cb2_irq <= '1';
        else
          if (((r_irb_hs = '1') or (w_orb_hs = '1')) and (r_pcr(5) = '0')) or
             (clear_irq(3) = '1') then
            cb2_irq <= '0';
          end if;
        end if;
      end if;
    end if;
  end process;

  p_input_reg : process(RESET_L, CLK)
  begin
    if (RESET_L = '0') then
      ca1_ip_reg <= '0';
      cb1_ip_reg <= '0';

      ca2_ip_reg <= '0';
      cb2_ip_reg <= '0';

      r_ira <= x"00";
      r_irb <= x"00";

    elsif rising_edge(CLK) then
      if (ENA_4 = '1') then
        -- we have a fast clock, so we can have input registers
        ca1_ip_reg <= I_CA1;
        cb1_ip_reg <= cb1_in_mux;

        ca2_ip_reg <= I_CA2;
        cb2_ip_reg <= I_CB2;

        if (r_acr(0) = '0') then
          r_ira <= I_PA;
        else -- enable latching
          if ca1_int then
            r_ira <= I_PA;
          end if;
        end if;

        if (r_acr(1) = '0') then
          r_irb <= I_PB;
        else -- enable latching
          if cb1_int then
            r_irb <= I_PB;
          end if;
        end if;
      end if;
    end if;
  end process;


  p_buffers : process(r_ddra, r_ora, r_ddrb, r_acr, r_orb)
  begin
    -- data direction reg (ddr) 0 = input, 1 = output
    O_PA      <= r_ora;
    O_PA_OE_L <= not r_ddra;

    if (r_acr(7) = '1') then -- not clear if r_ddrb(7) must be 1 as well
      O_PB_OE_L(7) <= '0'; -- an output if under t1 control
    else
      O_PB_OE_L(7) <= not (r_ddrb(7));
    end if;

    O_PB_OE_L(6 downto 0) <= not r_ddrb(6 downto 0);
    O_PB(7 downto 0)      <= r_orb(7 downto 0);

  end process;
  --
  -- Timer 1
  --
  p_timer1_done : process(t1c,phase,r_acr)
    variable done : boolean;
  begin
      done := (t1c = x"0000");
      t1c_done <= done and (phase = "11");
      --if (phase = "11") then
        t1_reload_counter <= done and (r_acr(6) = '1');
      --end if;
  end process;

  p_timer1 : process
  begin
    wait until rising_edge(CLK);
    if (ENA_4 = '1') then
      if t1_load_counter or (t1_reload_counter and phase = "11") then
        t1c( 7 downto 0) <= r_t1l_l;
        t1c(15 downto 8) <= r_t1l_h;
      elsif (phase="11") then
        t1c <= t1c - "1";
      end if;

      if t1_load_counter or t1_reload_counter then
        t1c_active <= true;
      elsif t1c_done then
        t1c_active <= false;
      end if;
      if RESET_L = '0' then
        t1c_active <= false;
      end if;

      t1_toggle <= '0';
      if t1c_active and t1c_done then
        t1_toggle <= '1';
        t1_irq <= '1';
      elsif RESET_L = '0' or t1_w_reset_int or t1_r_reset_int or (clear_irq(6) = '1') then
        t1_irq <= '0';
      end if;
    end if;
  end process;
  --
  -- Timer2
  --
  p_timer2_pb6_input : process
  begin
    wait until rising_edge(CLK);
    if (ENA_4 = '1') then
      if (phase = "01") then -- leading edge p2_h
        t2_pb6    <= I_PB(6);
        t2_pb6_t1 <= t2_pb6;
      end if;
    end if;
  end process;

  p_timer2_done : process(t2c,phase)
    variable done : boolean;
  begin
      done := (t2c = x"0000");
      t2c_done <= done and (phase = "11");
      --if (phase = "11") then
        t2_reload_counter <= done;
      --end if;
  end process;

  p_timer2 : process
    variable ena : boolean;
  begin
    wait until rising_edge(CLK);
    if (ENA_4 = '1') then
      if (r_acr(5) = '0') then
        ena := true;
      else
        ena := (t2_pb6_t1 = '1') and (t2_pb6 = '0'); -- falling edge
      end if;

      if t2_load_counter or (t2_reload_counter and phase = "11") then
      -- not sure if t2c_reload should be here. Does timer2 just continue to
      -- count down, or is it reloaded ? Reloaded makes more sense if using
      -- it to generate a clock for the shift register.
        t2c( 7 downto 0) <= r_t2l_l;
        t2c(15 downto 8) <= r_t2l_h;
      else
        if (phase="11") and ena then -- or count mode
            t2c <= t2c - "1";
        end if;
      end if;

      t2_sr_ena <= (t2c(7 downto 0) = x"00") and (phase = "11");

      if t2_load_counter then
        t2c_active <= true;
      elsif t2c_done then
        t2c_active <= false;
      end if;
      if RESET_L = '0' then
		t2c_active <= false;
	  end if;

      if t2c_active and t2c_done then
        t2_irq <= '1';
      elsif RESET_L = '0' or t2_w_reset_int or t2_r_reset_int or (clear_irq(5) = '1') then
        t2_irq <= '0';
      end if;
    end if;
  end process;
  --
  -- Shift Register
  --
  p_sr : process(RESET_L, CLK)
    variable dir_out      : std_logic;
    variable ena          : std_logic;
    variable cb1_op       : std_logic;
    variable cb1_ip       : std_logic;
    variable use_t2       : std_logic;
    variable free_run     : std_logic;
    variable sr_count_ena : boolean;
  begin
    if (RESET_L = '0') then
      r_sr <= x"00";
      sr_drive_cb2 <= '0';
      sr_cb1_oe_l <= '1';
      sr_cb1_out <= '0';
      sr_strobe <= '1';
      sr_cnt <= "0000";
      sr_irq <= '0';
      sr_out <= '1';
      sr_off_delay <= '0';
    elsif rising_edge(CLK) then
      if (ENA_4 = '1') then
        -- decode mode
        dir_out  := r_acr(4); -- output on cb2
        cb1_op   := '0';
        cb1_ip   := '0';
        use_t2   := '0';
        free_run := '0';

        case r_acr(4 downto 2) is
          when "000" => ena := '0'; cb1_ip := '1';
          when "001" => ena := '1'; cb1_op := '1'; use_t2 := '1';
          when "010" => ena := '1'; cb1_op := '1';
          when "011" => ena := '1'; cb1_ip := '1';
          when "100" => ena := '1';                use_t2 := '1'; free_run := '1';
          when "101" => ena := '1'; cb1_op := '1'; use_t2 := '1';
          when "110" => ena := '1';
          when "111" => ena := '1'; cb1_ip := '1';
          when others => null;
        end case;

        -- clock select
        -- SR still runs even in disabled mode (on rising edge of CB1).  It
        -- just doesn't generate any interrupts.
        -- Ref BBC micro advanced user guide p409
		if (cb1_ip = '1') then
		  sr_strobe <= I_CB1;
		else
		  if (sr_cnt(3) = '0') and (free_run = '0') then
		    sr_strobe <= '1';
		  else
			if ((use_t2 = '1') and t2_sr_ena) or
			  ((use_t2 = '0') and (phase = "00")) then
			    sr_strobe <= not sr_strobe;
			end if;
		  end if;
		end if;

        -- latch on rising edge, shift on falling edge
        if sr_write_ena then
          r_sr <= load_data;
        else
          if (dir_out = '0') then
            -- input
            if (sr_cnt(3) = '1') or (cb1_ip = '1') then
              if sr_strobe_rising then
                r_sr(0) <= I_CB2;
              elsif sr_strobe_falling then
                r_sr(7 downto 1) <= r_sr(6 downto 0);
              end if;
            end if;
            sr_out <= '1';
          else
            -- output
            if (sr_cnt(3) = '1') or (sr_off_delay = '1') or (cb1_ip = '1') or (free_run = '1') then
              if sr_strobe_falling then
                r_sr(7 downto 1) <= r_sr(6 downto 0);
                r_sr(0) <= r_sr(7);
                sr_out <= r_sr(7);
              end if;
            else
              sr_out <= '1';
            end if;
          end if;
        end if;

        sr_count_ena := sr_strobe_rising;

        if ena = '1' and (sr_write_ena or sr_read_ena) then
        -- some documentation says sr bit in IFR must be set as well ?
          sr_cnt <= "1000";
        elsif sr_count_ena and (sr_cnt(3) = '1') then
          sr_cnt <= sr_cnt + "1";
        end if;

        if (phase = "00") then
          sr_off_delay <= sr_cnt(3); -- give some hold time when shifting out
        end if;

        if sr_count_ena and (sr_cnt = "1111") and (ena = '1') and (free_run = '0') then
          sr_irq <= '1';
        elsif sr_write_ena or sr_read_ena or (clear_irq(2) = '1') then
          sr_irq <= '0';
        end if;

        -- assign ops
        sr_drive_cb2 <= dir_out;
        sr_cb1_oe_l  <= not cb1_op;
        sr_cb1_out   <= sr_strobe;
      end if;
    end if;
  end process;

  p_sr_strobe_rise_fall : process
  begin
    wait until rising_edge(CLK);
    if (ENA_4 = '1') then
      sr_strobe_t1 <= sr_strobe;
      sr_strobe_rising  <= (sr_strobe_t1 = '0') and (sr_strobe = '1');
      sr_strobe_falling <= (sr_strobe_t1 = '1') and (sr_strobe = '0');
    end if;
  end process;
  --
  -- Interrupts
  --
  p_ier : process(RESET_L, CLK)
  begin
    if (RESET_L = '0') then
      r_ier <= "0000000";
    elsif rising_edge(CLK) then
      if (ENA_4 = '1') then
        if ier_write_ena then
          if (load_data(7) = '1') then
            -- set
            r_ier <= r_ier or load_data(6 downto 0);
          else
            -- clear
            r_ier <= r_ier and not load_data(6 downto 0);
          end if;
        end if;
      end if;
    end if;
  end process;

  p_ifr : process(t1_irq, t2_irq, final_irq, ca1_irq, ca2_irq, sr_irq,
                  cb1_irq, cb2_irq)
  begin
    r_ifr(7) <= final_irq;
    r_ifr(6) <= t1_irq;
    r_ifr(5) <= t2_irq;
    r_ifr(4) <= cb1_irq;
    r_ifr(3) <= cb2_irq;
    r_ifr(2) <= sr_irq;
    r_ifr(1) <= ca1_irq;
    r_ifr(0) <= ca2_irq;

    O_IRQ_L <= not final_irq;
  end process;

  p_irq : process(RESET_L, CLK)
  begin
    if (RESET_L = '0') then
      final_irq <= '0';
    elsif rising_edge(CLK) then
      if (ENA_4 = '1') then
        if ((r_ifr(6 downto 0) and r_ier(6 downto 0)) = "0000000") then
          final_irq <= '0'; -- no interrupts
        else
          final_irq <= '1';
        end if;
      end if;
    end if;
  end process;

  p_clear_irq : process(ifr_write_ena, load_data)
  begin
    clear_irq <= x"00";
    if ifr_write_ena then
      clear_irq <= load_data;
    end if;
  end process;

end architecture RTL;
