-- ****
-- T65(b) core. In an effort to merge and maintain bug fixes ....
--
-- See list of changes in T65 top file (T65.vhd)...
--
-- ****
-- 65xx compatible microprocessor core
--
-- FPGAARCADE SVN: $Id: T65_MCode.vhd 1234 2015-02-28 20:14:50Z wolfgang.scherr $
--
-- Copyright (c) 2002...2015
--               Daniel Wallner (jesus <at> opencores <dot> org)
--               Mike Johnson   (mikej <at> fpgaarcade <dot> com)
--               Wolfgang Scherr (WoS <at> pin4 <dot> at>
--               Morten Leikvoll ()
--
-- 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 SOFTWARE 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.
--
-- Please report bugs to the author(s), but before you do so, please
-- make sure that this is not a derivative work and that
-- you have the latest version of this file.
--
-- Limitations :
--   See in T65 top file (T65.vhd)...

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use ieee.std_logic_unsigned.all; 
use work.T65_Pack.all;

entity T65_MCode is
  port(
    Mode                    : in  std_logic_vector(1 downto 0);      -- "00" => 6502, "01" => 65C02, "10" => 65816
    IR                      : in  std_logic_vector(7 downto 0);
    MCycle                  : in  T_Lcycle;
    P                       : in  std_logic_vector(7 downto 0);
    LCycle                  : out T_Lcycle;
    ALU_Op                  : out T_ALU_Op;
    Set_BusA_To             : out T_Set_BusA_To; -- DI,A,X,Y,S,P,DA,DAO,DAX,AAX
    Set_Addr_To             : out T_Set_Addr_To; -- PC Adder,S,AD,BA
    Write_Data              : out T_Write_Data;  -- DL,A,X,Y,S,P,PCL,PCH,AX,AXB,XB,YB
    Jump                    : out std_logic_vector(1 downto 0); -- PC,++,DIDL,Rel
    BAAdd                   : out std_logic_vector(1 downto 0); -- None,DB Inc,BA Add,BA Adj
    BreakAtNA               : out std_logic;
    ADAdd                   : out std_logic;
    AddY                    : out std_logic;
    PCAdd                   : out std_logic;
    Inc_S                   : out std_logic;
    Dec_S                   : out std_logic;
    LDA                     : out std_logic;
    LDP                     : out std_logic;
    LDX                     : out std_logic;
    LDY                     : out std_logic;
    LDS                     : out std_logic;
    LDDI                    : out std_logic;
    LDALU                   : out std_logic;
    LDAD                    : out std_logic;
    LDBAL                   : out std_logic;
    LDBAH                   : out std_logic;
    SaveP                   : out std_logic;
    Write                   : out std_logic
  );
end T65_MCode;

architecture rtl of T65_MCode is

  signal Branch : std_logic;
  signal ALUmore:std_logic;

begin

  with IR(7 downto 5) select
    Branch <= not P(Flag_N) when "000",
                  P(Flag_N) when "001",
              not P(Flag_V) when "010",
                  P(Flag_V) when "011",
              not P(Flag_C) when "100",
                  P(Flag_C) when "101",
              not P(Flag_Z) when "110",
                  P(Flag_Z) when others;

  process (IR, MCycle, P, Branch, Mode)
  begin
    lCycle      <= Cycle_1;
    Set_BusA_To <= Set_BusA_To_ABC;
    Set_Addr_To <= Set_Addr_To_PBR;
    Write_Data  <= Write_Data_DL;
    Jump        <= (others => '0');
    BAAdd       <= "00";
    BreakAtNA   <= '0';
    ADAdd       <= '0';
    PCAdd       <= '0';
    Inc_S       <= '0';
    Dec_S       <= '0';
    LDA         <= '0';
    LDP         <= '0';
    LDX         <= '0';
    LDY         <= '0';
    LDS         <= '0';
    LDDI        <= '0';
    LDALU       <= '0';
    LDAD        <= '0';
    LDBAL       <= '0';
    LDBAH       <= '0';
    SaveP       <= '0';
    Write       <= '0';
    AddY        <= '0';
    ALUmore     <= '0';

    case IR(7 downto 5) is
      when "100" => -- covers $8x,$9x
        case IR(1 downto 0) is
          when "00" => -- IR: $80,$84,$88,$8C,$90,$94,$98,$9C
            Set_BusA_To <= Set_BusA_To_Y;
            if IR(4 downto 2)="111" then   --  SYA ($9C)
              Write_Data <= Write_Data_YB;
            else
              Write_Data <= Write_Data_Y;
            end if;
          when "10" => -- IR: $82,$86,$8A,$8E,$92,$96,$9A,$9E
            Set_BusA_To <= Set_BusA_To_X;
            if IR(4 downto 2)="111" then   --  SXA ($9E)
              Write_Data <= Write_Data_XB;
            else
              Write_Data <= Write_Data_X;
            end if;
          when "11" => -- IR: $83,$87,$8B,$8F,$93,$97,$9B,$9F
            if IR(4 downto 2)="110" then   --  SHS ($9B)
              Set_BusA_To <= Set_BusA_To_AAX;
              LDS <= '1';
            else
              Set_BusA_To <= Set_BusA_To_ABC;
            end if;
            if IR(4 downto 2)="111" or IR(4 downto 2)="110" or IR(4 downto 2)="100" then   --  SHA ($9F, $93), SHS ($9B)
              Write_Data <= Write_Data_AXB;
            else
              Write_Data <= Write_Data_AX;
            end if;
          when others => -- IR: $81,$85,$89,$8D,$91,$95,$99,$9D
            Write_Data <= Write_Data_ABC;
        end case;
      when "101" => -- covers $Ax,$Bx
        Set_BusA_To <= Set_BusA_To_DI;
        case IR(1 downto 0) is
          when "00" => -- IR: $A0,$A4,$A8,$AC,$B0,$B4,$B8,$BC
            if IR(4) /= '1' or IR(2) /= '0' then--only for $A0,$A4,$A8,$AC or $B4,$BC
              LDY <= '1';
            end if;
          when "01" => -- IR: $A1,$A5,$A9,$AD,$B1,$B5,$B9,$BD
            LDA <= '1';
          when "10" => -- IR: $A2,$A6,$AA,$AE,$B2,$B6,$BA,$BE
            LDX <= '1';
          when others => -- IR: $A3,$A7,$AB,$AF,$B3,$B7,$BB,$BF (undoc)
            LDX <= '1';
            LDA <= '1';
            if IR(4 downto 2)="110" then   --  LAS (BB)
              Set_BusA_To <= Set_BusA_To_S;
              LDS <= '1';
            end if;
        end case;
      when "110" => -- covers $Cx,$Dx
        case IR(1 downto 0) is
          when "00" => -- IR: $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC
            if IR(4) = '0' then--only for $Cx
              LDY <= '1';
            end if;
            Set_BusA_To <= Set_BusA_To_Y;
          when others => -- IR: $C1,$C5,$C9,$CD,$D1,$D5,$D9,$DD, $C2,$C6,$CA,$CE,$D2,$D6,$DA,$DE, $C3,$C7,$CB,$CF,$D3,$D7,$DB,$DF
            Set_BusA_To <= Set_BusA_To_ABC;
        end case;
      when "111" => -- covers $Ex,$Fx
        case IR(1 downto 0) is
        when "00" => -- IR: $E0,$E4,$E8,$EC,$F0,$F4,$F8,$FC
          if IR(4) = '0' then -- only $Ex
            LDX <= '1';
          end if;
          Set_BusA_To <= Set_BusA_To_X;
        when others => -- IR: $E1,$E5,$E9,$ED,$F1,$F5,$F9,$FD, $E2,$E6,$EA,$EE,$F2,$F6,$FA,$FE, $E3,$E7,$EB,$EF,$F3,$F7,$FB,$FF
          Set_BusA_To <= Set_BusA_To_ABC;
        end case;
      when others =>
    end case;

    if IR(7 downto 6) /= "10" and IR(1) = '1' and (mode="00" or IR(0)='0') then--covers $0x-$7x, $Cx-$Fx x=2,3,6,7,A,B,E,F, for 6502 undocs
      if IR=x"eb" then
        Set_BusA_To <= Set_BusA_To_ABC; -- alternate SBC ($EB)
      else
        Set_BusA_To <= Set_BusA_To_DI;
      end if;
    end if;

    case IR(4 downto 0) is
      -- IR: $00,$20,$40,$60,$80,$A0,$C0,$E0
      --     $08,$28,$48,$68,$88,$A8,$C8,$E8
      --     $0A,$2A,$4A,$6A,$8A,$AA,$CA,$EA
      --     $18,$38,$58,$78,$98,$B8,$D8,$F8
      --     $1A,$3A,$5A,$7A,$9A,$BA,$DA,$FA
      when "00000" | "01000" | "01010" | "11000" | "11010" =>
        -- Implied
        case IR is
          when x"00" =>
            -- BRK ($00)
            lCycle <= Cycle_6;
            case MCycle is
              when Cycle_1 =>
                Set_Addr_To <= Set_Addr_To_SP;
                Write_Data <= Write_Data_PCH;
                Write <= '1';
              when Cycle_2 =>
                Dec_S <= '1';
                Set_Addr_To <= Set_Addr_To_SP;
                Write_Data <= Write_Data_PCL;
                Write <= '1';
              when Cycle_3 =>
                Dec_S <= '1';
                Set_Addr_To <= Set_Addr_To_SP;
                Write_Data <= Write_Data_P;
                Write <= '1';
              when Cycle_4 =>
                Dec_S <= '1';
                Set_Addr_To <= Set_Addr_To_BA;
              when Cycle_5 =>
                LDDI <= '1';
                Set_Addr_To <= Set_Addr_To_BA;
              when Cycle_6 =>
                Jump <= "10";
              when others =>
            end case;
          when x"20" => -- JSR ($20)
            lCycle <= Cycle_5;
            case MCycle is
              when Cycle_1 =>
                Jump <= "01";
                LDDI <= '1';
                Set_Addr_To <= Set_Addr_To_SP;
              when Cycle_2 =>
                Set_Addr_To <= Set_Addr_To_SP;
                Write_Data <= Write_Data_PCH;
                Write <= '1';
              when Cycle_3 =>
                Dec_S <= '1';
                Set_Addr_To <= Set_Addr_To_SP;
                Write_Data <= Write_Data_PCL;
                Write <= '1';
              when Cycle_4 =>
                Dec_S <= '1';
              when Cycle_5 =>
                Jump <= "10";
              when others =>
              end case;
            when x"40" => -- RTI ($40)
              lCycle <= Cycle_5;
              case MCycle is
              when Cycle_1 =>
                Set_Addr_To <= Set_Addr_To_SP;
              when Cycle_2 =>
                Inc_S <= '1';
                Set_Addr_To <= Set_Addr_To_SP;
              when Cycle_3 =>
                Inc_S <= '1';
                Set_Addr_To <= Set_Addr_To_SP;
                Set_BusA_To <= Set_BusA_To_DI;
              when Cycle_4 =>
                LDP <= '1';
                Inc_S <= '1';
                LDDI <= '1';
                Set_Addr_To <= Set_Addr_To_SP;
              when Cycle_5 =>
                Jump <= "10";
              when others =>
            end case;
          when x"60" => -- RTS ($60)
            lCycle <= Cycle_5;
            case MCycle is
              when Cycle_1 =>
                Set_Addr_To <= Set_Addr_To_SP;
              when Cycle_2 =>
                Inc_S <= '1';
                Set_Addr_To <= Set_Addr_To_SP;
              when Cycle_3 =>
                Inc_S <= '1';
                LDDI <= '1';
                Set_Addr_To <= Set_Addr_To_SP;
              when Cycle_4 =>
                Jump <= "10";
              when Cycle_5 =>
                Jump <= "01";
              when others =>
              end case;
            when x"08" | x"48" | x"5a" | x"da" => -- PHP, PHA, PHY*, PHX*  ($08,$48,$5A,$DA)
              lCycle <= Cycle_2;
              if Mode = "00" and IR(1) = '1' then--2 cycle nop
                lCycle <= Cycle_1;
              end if;
              case MCycle is
              when Cycle_1 =>
                if mode/="00" or IR(1)='0' then --wrong on 6502
                  Write <= '1';
                  case IR(7 downto 4) is
                  when "0000" =>
                    Write_Data <= Write_Data_P;
                  when "0100" =>
                    Write_Data <= Write_Data_ABC;
                  when "0101" =>
                    if Mode /= "00" then
                      Write_Data <= Write_Data_Y;
                    else
                      Write <= '0';
                    end if;
                  when "1101" =>
                    if Mode /= "00" then
                      Write_Data <= Write_Data_X;
                    else
                      Write <= '0';
                    end if;
                  when others =>
                  end case;
                  Set_Addr_To <= Set_Addr_To_SP;
                end if;
              when Cycle_2 =>
                Dec_S <= '1';
              when others =>
            end case;
          when  x"28" | x"68" | x"7a" | x"fa" => -- PLP, PLA, PLY*, PLX* ($28,$68,$7A,$FA)
            lCycle <= Cycle_3;
            if Mode = "00" and IR(1) = '1' then--2 cycle nop
              lCycle <= Cycle_1;
            end if;
            case IR(7 downto 4) is
              when "0010" =>--plp
                LDP <= '1';
              when "0110" =>--pla
                LDA <= '1';
              when "0111" =>--ply not for 6502
                if Mode /= "00" then
                  LDY <= '1';
                end if;
              when "1111" =>--plx not for 6502
                if Mode /= "00" then
                  LDX <= '1';
                end if;
              when others =>
            end case;
            case MCycle is
              when Cycle_sync =>
                if Mode /= "00" or IR(1) = '0' then--wrong on 6502
                  SaveP <= '1';
                end if;
              when Cycle_1 =>
                if Mode /= "00" or IR(1) = '0' then--wrong on 6502
                  Set_Addr_To <= Set_Addr_To_SP;
                  LDP <= '0';
                end if;
              when Cycle_2 =>
                Inc_S <= '1';
                Set_Addr_To <= Set_Addr_To_SP;
                LDP <= '0';
              when Cycle_3 =>
                Set_BusA_To <= Set_BusA_To_DI;
              when others =>
            end case;
          when x"a0" | x"c0" | x"e0" => -- LDY, CPY, CPX ($A0,$C0,$E0)
            -- Immediate
            case MCycle is
              when Cycle_sync =>
              when Cycle_1 =>
                Jump <= "01";
              when others =>
            end case;
          when x"88" => -- DEY ($88)
            LDY <= '1';
            case MCycle is
              when Cycle_sync =>
              when Cycle_1 =>
                Set_BusA_To <= Set_BusA_To_Y;
              when others =>
            end case;
          when x"ca" => -- DEX ($CA)
            LDX <= '1';
            case MCycle is
              when Cycle_sync =>
              when Cycle_1 =>
                Set_BusA_To <= Set_BusA_To_X;
              when others =>
            end case;
          when x"1a" | x"3a" => -- INC*, DEC* ($1A,$3A)
            if Mode /= "00" then
              LDA <= '1'; -- A
            else
              lCycle <= Cycle_1;--undoc 2 cycle nop
            end if;
            case MCycle is
              when Cycle_sync =>
              when Cycle_1 =>
                Set_BusA_To <= Set_BusA_To_S;
              when others =>
            end case;
          when x"0a" | x"2a" | x"4a" | x"6a" => -- ASL, ROL, LSR, ROR ($0A,$2A,$4A,$6A)
            LDA <= '1'; -- A
            Set_BusA_To <= Set_BusA_To_ABC;
            case MCycle is
              when Cycle_sync =>
              when Cycle_1 =>
              when others =>
            end case;
          when x"8a" | x"98" => -- TYA, TXA ($8A,$98)
            LDA <= '1';
            case MCycle is
              when Cycle_sync =>
              when Cycle_1 =>
              when others =>
            end case;
          when x"aa" | x"a8" => -- TAX, TAY ($AA,$A8)
            case MCycle is
              when Cycle_sync =>
              when Cycle_1 =>
                Set_BusA_To <= Set_BusA_To_ABC;
              when others =>
            end case;
          when x"9a" => -- TXS ($9A)
            LDS <= '1'; -- will be set only in Cycle_sync
          when x"ba" => -- TSX ($BA)
            LDX <= '1';
            case MCycle is
              when Cycle_sync =>
              when Cycle_1 =>
                Set_BusA_To <= Set_BusA_To_S;
              when others =>
            end case;
          when x"80" => -- undoc: NOP imm2 ($80)
            case MCycle is
              when Cycle_sync =>
              when Cycle_1 =>
                Jump <= "01";
              when others =>
            end case;
          when others => -- others ($0A,$EA, $18,$38,$58,$78,$B8,$C8,$D8,$E8,$F8)
            case MCycle is
              when Cycle_sync =>
              when others =>
            end case;
        end case;

      -- IR: $01,$21,$41,$61,$81,$A1,$C1,$E1
      --     $03,$23,$43,$63,$83,$A3,$C3,$E3
      when "00001" | "00011" =>
        -- Zero Page Indexed Indirect (d,x)
        lCycle <= Cycle_5;
        if IR(7 downto 6) /= "10" then -- ($01,$21,$41,$61,$C1,$E1,$03,$23,$43,$63,$C3,$E3)
          LDA <= '1';
          if Mode="00" and IR(1)='1' then
            lCycle <= Cycle_7;
          end if;
        end if;
        case MCycle is
          when Cycle_1 =>
            Jump <= "01";
            LDAD <= '1';
            Set_Addr_To <= Set_Addr_To_ZPG;
          when Cycle_2 =>
            ADAdd <= '1';
            Set_Addr_To <= Set_Addr_To_ZPG;
          when Cycle_3 =>
            BAAdd <= "01";
            LDBAL <= '1';
            Set_Addr_To <= Set_Addr_To_ZPG;
          when Cycle_4 =>
            LDBAH <= '1';
            if IR(7 downto 5) = "100" then
              Write <= '1';
            end if;
            Set_Addr_To <= Set_Addr_To_BA;
          when Cycle_5=>
            if Mode="00" and IR(1)='1' and IR(7 downto 6)/="10" then
              Set_Addr_To <= Set_Addr_To_BA;
              Write <= '1';
              LDDI<='1';
            end if;
          when Cycle_6=>
              Write <= '1';
              LDALU<='1';
              SaveP<='1';
              Set_Addr_To <= Set_Addr_To_BA;
          when Cycle_7 =>
            ALUmore <= '1';
            Set_BusA_To <= Set_BusA_To_ABC;
          when others =>
        end case;

      -- IR: $09,$29,$49,$69,$89,$A9,$C9,$E9
      when "01001" =>
        -- Immediate
        if IR(7 downto 5)/="100" then -- all except undoc. NOP imm2 (not $89)
          LDA <= '1';
        end if;
        case MCycle is
          when Cycle_1 =>
            Jump <= "01";
          when others =>
        end case;

      -- IR: $0B,$2B,$4B,$6B,$8B,$AB,$CB,$EB
      when "01011" =>
        if Mode="00" then
        -- Immediate undoc for 6500
          case IR(7 downto 5) is 
            when "010"|"011"|"000"|"001" =>--ALR,ARR
              Set_BusA_To<=Set_BusA_To_DA;
              LDA <= '1';
            when "100" =>--XAA
              Set_BusA_To<=Set_BusA_To_DAX;
              LDA <= '1';
            when "110" =>--SAX (SBX)
              Set_BusA_To<=Set_BusA_To_AAX;
              LDX <= '1';
            when "101" =>--OAL
              Set_BusA_To<=Set_BusA_To_DAO;
              LDA <= '1';
            when others=>
              LDA <= '1';
          end case;
          case MCycle is
            when Cycle_1 =>
              Jump <= "01";
            when others =>
          end case;
        end if;

      -- IR: $02,$22,$42,$62,$82,$A2,$C2,$E2
      --     $12,$32,$52,$72,$92,$B2,$D2,$F2
      when "00010" | "10010" =>
        -- Immediate, SKB, KIL
        case MCycle is
          when Cycle_sync =>
          when Cycle_1 =>
            if IR = "10100010" then
              -- LDX ($A2)
              Jump <= "01";
              LDX <= '1'; -- Moved, Lorenz test showed X changing on SKB (NOPx)
            elsif IR(7 downto 4)="1000" or IR(7 downto 4)="1100" or IR(7 downto 4)="1110" then
              -- undoc: NOP imm2
              Jump <= "01";
            else
              -- KIL !!!
            end if;
          when others =>
        end case;

      -- IR: $04,$24,$44,$64,$84,$A4,$C4,$E4
      when "00100" =>
        -- Zero Page
        lCycle <= Cycle_2;
        case MCycle is
          when Cycle_sync =>
            if IR(7 downto 5) = "001" then--24=BIT zpg
              SaveP <= '1';
            end if;
          when Cycle_1 =>
            Jump <= "01";
            LDAD <= '1';
            if IR(7 downto 5) = "100" then--84=sty zpg (the only write in this group)
              Write <= '1';
            end if;
            Set_Addr_To <= Set_Addr_To_ZPG;
          when Cycle_2 =>
          when others =>
        end case;

      -- IR: $05,$25,$45,$65,$85,$A5,$C5,$E5
      --     $06,$26,$46,$66,$86,$A6,$C6,$E6
      --     $07,$27,$47,$67,$87,$A7,$C7,$E7
      when "00101" | "00110" | "00111" =>
        -- Zero Page
        if IR(7 downto 6) /= "10" and IR(1) = '1' and (mode="00" or IR(0)='0') then--covers 0x-7x,cx-fx x=2,3,6,7,a,b,e,f, for 6502 undocs
          -- Read-Modify-Write
          lCycle <= Cycle_4;
          if Mode="00" and IR(0)='1' then
            LDA<='1';
          end if;
          case MCycle is
            when Cycle_1 =>
              Jump <= "01";
              LDAD <= '1';
              Set_Addr_To <= Set_Addr_To_ZPG;
            when Cycle_2 =>
              LDDI <= '1';
              if Mode="00"  then--The old 6500 writes back what is just read, before changing. The 65c does another read
                Write <= '1';
              end if;
              Set_Addr_To <= Set_Addr_To_ZPG;
            when Cycle_3 =>
              LDALU <= '1';
              SaveP <= '1';
              Write <= '1';
              Set_Addr_To <= Set_Addr_To_ZPG;
            when Cycle_4 =>
              if Mode="00" and IR(0)='1' then
                Set_BusA_To<=Set_BusA_To_ABC;
                ALUmore <= '1'; -- For undoc DCP/DCM support
                LDDI <= '1';    -- requires DIN to reflect DOUT!
              end if;
            when others =>
          end case;
        else
          lCycle <= Cycle_2;
          if IR(7 downto 6) /= "10" then
            LDA <= '1';
          end if;
          case MCycle is
            when Cycle_sync =>
            when Cycle_1 =>
              Jump <= "01";
              LDAD <= '1';
              if IR(7 downto 5) = "100" then
                Write <= '1';
              end if;
              Set_Addr_To <= Set_Addr_To_ZPG;
            when Cycle_2 =>
            when others =>
          end case;
        end if;

      -- IR: $0C,$2C,$4C,$6C,$8C,$AC,$CC,$EC
      when "01100" =>
        -- Absolute
        if IR(7 downto 6) = "01" and IR(4 downto 0) = "01100" then -- JMP ($4C,$6C)
          if IR(5) = '0' then
            lCycle <= Cycle_2;
            case MCycle is
              when Cycle_1 =>
                Jump <= "01";
                LDDI <= '1';
              when Cycle_2 =>
                Jump <= "10";
              when others =>
            end case;
          else
            lCycle <= Cycle_4;
            case MCycle is
              when Cycle_1 =>
                Jump <= "01";
                LDDI <= '1';
                LDBAL <= '1';
              when Cycle_2 =>
                LDBAH <= '1';
                if Mode /= "00" then
                  Jump <= "10";
                end if;
                if Mode = "00" then
                  Set_Addr_To <= Set_Addr_To_BA;
                end if;
              when Cycle_3 =>
                LDDI <= '1';
                if Mode = "00" then
                  Set_Addr_To <= Set_Addr_To_BA;
                  BAAdd <= "01";      -- DB Inc
                else
                  Jump <= "01";
                end if;
              when Cycle_4 =>
                Jump <= "10";
              when others =>
            end case;
          end if;
        else
          lCycle <= Cycle_3;
          case MCycle is
            when Cycle_sync =>
              if IR(7 downto 5) = "001" then--2c-BIT
                SaveP <= '1';
              end if;
            when Cycle_1 =>
              Jump <= "01";
              LDBAL <= '1';
            when Cycle_2 =>
              Jump <= "01";
              LDBAH <= '1';
              if IR(7 downto 5) = "100" then--80, sty, the only write in this group
                Write <= '1';
              end if;
              Set_Addr_To <= Set_Addr_To_BA;
            when Cycle_3 =>
            when others =>
          end case;
        end if;

      -- IR: $0D,$2D,$4D,$6D,$8D,$AD,$CD,$ED
      --     $0E,$2E,$4E,$6E,$8E,$AE,$CE,$EE
      --     $0F,$2F,$4F,$6F,$8F,$AF,$CF,$EF
      when "01101" | "01110" | "01111" =>
        -- Absolute
        if IR(7 downto 6) /= "10" and IR(1) = '1' and (mode="00" or IR(0)='0') then -- ($0E,$2E,$4E,$6E,$CE,$EE, $0F,$2F,$4F,$6F,$CF,$EF)
          -- Read-Modify-Write
          lCycle <= Cycle_5;
          if Mode="00" and IR(0) = '1' then
            LDA <= '1';
          end if;
          case MCycle is
            when Cycle_1 =>
              Jump <= "01";
              LDBAL <= '1';
            when Cycle_2 =>
              Jump <= "01";
              LDBAH <= '1';
              Set_Addr_To <= Set_Addr_To_BA;
            when Cycle_3 =>
              LDDI <= '1';
              if Mode="00" then--The old 6500 writes back what is just read, before changing. The 65c does another read
                Write <= '1';
              end if;
              Set_Addr_To <= Set_Addr_To_BA;
            when Cycle_4 =>
              Write <= '1';
              LDALU <= '1';
              SaveP <= '1';
              Set_Addr_To <= Set_Addr_To_BA;
            when Cycle_5 =>
              if Mode="00" and IR(0)='1' then
                ALUmore <= '1'; -- For undoc DCP/DCM support
                Set_BusA_To<=Set_BusA_To_ABC;
              end if;
            when others =>
          end case;
        else
          lCycle <= Cycle_3;
          if IR(7 downto 6) /= "10" then -- all but $8D, $8E, $8F, $AD, $AE, $AF ($AD does set LDA in an earlier case statement)
            LDA <= '1';
          end if;
          case MCycle is
            when Cycle_sync =>
            when Cycle_1 =>
              Jump <= "01";
              LDBAL <= '1';
            when Cycle_2 =>
              Jump <= "01";
              LDBAH <= '1';
              if IR(7 downto 5) = "100" then--8d
                Write <= '1';
              end if;
              Set_Addr_To <= Set_Addr_To_BA;
            when Cycle_3 =>
            when others =>
          end case;
        end if;

      -- IR: $10,$30,$50,$70,$90,$B0,$D0,$F0
      when "10000" =>
        -- Relative
              -- This circuit dictates when the last
              -- microcycle occurs for the branch depending on
              -- whether or not the branch is taken and if a page
              -- is crossed...
        if (Branch = '1') then
                 lCycle <= Cycle_3; -- We're done @ T3 if branching...upper
                          -- level logic will stop at T2 if no page cross
                          -- (See the Break signal)
        else
                 lCycle <= Cycle_1;
        end if;
              -- This decodes the current microcycle and takes the
              -- proper course of action...
        case MCycle is
                -- On the T1 microcycle, increment the program counter
                -- and instruct the upper level logic to fetch the offset
                -- from the Din bus and store it in the data latches. This
                -- will be the last microcycle if the branch isn't taken.
          when Cycle_1 =>
            Jump <= "01"; -- Increments the PC by one (PC will now be PC+2)
                          -- from microcycle T0.
            LDDI <= '1';  -- Tells logic in top level (T65.vhd) to route
                          -- the Din bus to the memory data latch (DL)
                          -- so that the branch offset is fetched.
                -- In microcycle T2, tell the logic in the top level to
                -- add the offset.  If the most significant byte of the
                -- program counter (i.e. the current "page") does not need
                -- updating, we are done here...the Break signal at the
                -- T65.vhd level takes care of that...
          when Cycle_2 =>
            Jump    <= "11"; -- Tell the PC Jump logic to use relative mode.
            PCAdd   <= '1';  -- This tells the PC adder to update itself with
                             -- the current offset recently fetched from
                             -- memory.
                -- The following is microcycle T3 :
                -- The program counter should be completely updated
                -- on this cycle after the page cross is detected.
                -- We don't need to do anything here...
          when Cycle_3 =>
          when others => null; -- Do nothing.
        end case;

      -- IR: $11,$31,$51,$71,$91,$B1,$D1,$F1
      --     $13,$33,$53,$73,$93,$B3,$D3,$F3
      when "10001" | "10011" =>
        lCycle <= Cycle_5;
        if IR(7 downto 6) /= "10" then -- ($11,$31,$51,$71,$D1,$F1,$13,$33,$53,$73,$D3,$F3)
          LDA <= '1';
          if Mode="00" and IR(1)='1' then
            lCycle <= Cycle_7;
          end if;
        end if;
        case MCycle is
          when Cycle_1 =>
            Jump <= "01";
            LDAD <= '1';
            Set_Addr_To <= Set_Addr_To_ZPG;
          when Cycle_2 =>
            LDBAL <= '1';
            BAAdd <= "01";  -- DB Inc
            Set_Addr_To <= Set_Addr_To_ZPG;
          when Cycle_3 =>
            Set_BusA_To <= Set_BusA_To_Y;
            BAAdd <= "10";  -- BA Add
            LDBAH <= '1';
            Set_Addr_To <= Set_Addr_To_BA;
          when Cycle_4 =>
            BAAdd <= "11";  -- BA Adj
            if IR(7 downto 5) = "100" then
              Write <= '1';
            elsif IR(1)='0' or IR=x"B3" then -- Dont do this on $x3, except undoc LAXiy $B3 (says real CPU and Lorenz tests)
              BreakAtNA <= '1';
            end if;
              Set_Addr_To <= Set_Addr_To_BA;
          when Cycle_5 =>
            if Mode="00" and IR(1)='1' and IR(7 downto 6)/="10" then
              Set_Addr_To <= Set_Addr_To_BA;
              LDDI<='1';
              Write <= '1';
            end if;
          when Cycle_6 =>
            LDALU<='1';
            SaveP<='1';
            Write <= '1';
            Set_Addr_To <= Set_Addr_To_BA;
          when Cycle_7 =>
            ALUmore <= '1';
            Set_BusA_To<=Set_BusA_To_ABC;
          when others =>
        end case;

      -- IR: $14,$34,$54,$74,$94,$B4,$D4,$F4
      --     $15,$35,$55,$75,$95,$B5,$D5,$F5
      --     $16,$36,$56,$76,$96,$B6,$D6,$F6
      --     $17,$37,$57,$77,$97,$B7,$D7,$F7
      when "10100" | "10101" | "10110" | "10111" =>
        -- Zero Page, X
        if IR(7 downto 6) /= "10" and IR(1) = '1' and (Mode="00" or IR(0)='0') then -- ($16,$36,$56,$76,$D6,$F6, $17,$37,$57,$77,$D7,$F7)
          -- Read-Modify-Write
          if Mode="00" and IR(0)='1' then
            LDA<='1';
          end if;
          lCycle <= Cycle_5;
          case MCycle is
            when Cycle_1 =>
              Jump <= "01";
              LDAD <= '1';
              Set_Addr_To <= Set_Addr_To_ZPG;
            when Cycle_2 =>
              ADAdd <= '1';
              Set_Addr_To <= Set_Addr_To_ZPG;
            when Cycle_3 =>
              LDDI <= '1';
              if Mode="00" then -- The old 6500 writes back what is just read, before changing. The 65c does another read
                Write <= '1';
              end if;
              Set_Addr_To <= Set_Addr_To_ZPG;
            when Cycle_4 =>
              LDALU <= '1';
              SaveP <= '1';
              Write <= '1';
              Set_Addr_To <= Set_Addr_To_ZPG;
              if Mode="00" and IR(0)='1' then
                LDDI<='1';
              end if;
            when Cycle_5 =>
              if Mode="00" and IR(0)='1' then
                ALUmore <= '1'; -- For undoc DCP/DCM support
                Set_BusA_To<=Set_BusA_To_ABC;
              end if;
            when others =>
          end case;
        else
          lCycle <= Cycle_3;
          if IR(7 downto 6) /= "10" and IR(0)='1' then -- dont LDA on undoc skip
            LDA <= '1';
          end if;
          case MCycle is
            when Cycle_sync =>
            when Cycle_1 =>
              Jump <= "01";
              LDAD <= '1';
              Set_Addr_To <= Set_Addr_To_ZPG;
            when Cycle_2 =>
              ADAdd <= '1';
              -- Added this check for Y reg. use, added undocs
              if (IR(3 downto 1) = "011") then -- ($16,$36,$56,$76,$96,$B6,$D6,$F6,$17,$37,$57,$77,$97,$B7,$D7,$F7)
                AddY <= '1';
              end if;
              if IR(7 downto 5) = "100" then -- ($14,$34,$15,$35,$16,$36,$17,$37) the only write instruction
                Write <= '1';
              end if;
              Set_Addr_To <= Set_Addr_To_ZPG;
            when Cycle_3 => null;
            when others =>
          end case;
        end if;

      -- IR: $19,$39,$59,$79,$99,$B9,$D9,$F9
      --     $1B,$3B,$5B,$7B,$9B,$BB,$DB,$FB
      when "11001" | "11011" =>
        -- Absolute Y
        lCycle <= Cycle_4;
        if IR(7 downto 6) /= "10" then
          LDA <= '1';
          if Mode="00" and IR(1)='1' then
            lCycle <= Cycle_6;
          end if;
        end if;
        case MCycle is
          when Cycle_1 =>
            Jump <= "01";
            LDBAL <= '1';
          when Cycle_2 =>
            Jump <= "01";
            Set_BusA_To <= Set_BusA_To_Y;
            BAAdd <= "10";  -- BA Add
            LDBAH <= '1';
            Set_Addr_To <= Set_Addr_To_BA;
          when Cycle_3 =>
            BAAdd <= "11";  -- BA adj
            if IR(7 downto 5) = "100" then--99/9b
              Write <= '1';
            elsif IR(1)='0' or IR=x"BB" then -- Dont do this on $xB, except undoc $BB (says real CPU and Lorenz tests)
              BreakAtNA <= '1';
            end if;
            Set_Addr_To <= Set_Addr_To_BA;
          when Cycle_4 => -- just for undoc
            if Mode="00" and IR(1)='1' and IR(7 downto 6)/="10" then
              Set_Addr_To <= Set_Addr_To_BA;
              LDDI<='1';
              Write <= '1';
            end if;
          when Cycle_5 =>
            Write <= '1';
            LDALU<='1';
            Set_Addr_To <= Set_Addr_To_BA;
            SaveP<='1';
          when Cycle_6 =>
            ALUmore <= '1';
            Set_BusA_To <= Set_BusA_To_ABC;
          when others =>
        end case;

      -- IR: $1C,$3C,$5C,$7C,$9C,$BC,$DC,$FC
      --     $1D,$3D,$5D,$7D,$9D,$BD,$DD,$FD
      --     $1E,$3E,$5E,$7E,$9E,$BE,$DE,$FE
      --     $1F,$3F,$5F,$7F,$9F,$BF,$DF,$FF
      when "11100" | "11101" | "11110" | "11111" =>
        -- Absolute X
        if IR(7 downto 6) /= "10" and IR(1) = '1' and (Mode="00" or IR(0)='0') then -- ($1E,$3E,$5E,$7E,$DE,$FE, $1F,$3F,$5F,$7F,$DF,$FF)
          -- Read-Modify-Write
          lCycle <= Cycle_6;
          if Mode="00" and IR(0)='1' then
            LDA <= '1';
          end if;
          case MCycle is
            when Cycle_1 =>
              Jump <= "01";
              LDBAL <= '1';
            when Cycle_2 =>
              Jump <= "01";
              Set_BusA_To <= Set_BusA_To_X;
              BAAdd <= "10";      -- BA Add
              LDBAH <= '1';
              Set_Addr_To <= Set_Addr_To_BA;
            when Cycle_3 =>
              BAAdd <= "11";      -- BA adj
              Set_Addr_To <= Set_Addr_To_BA;
            when Cycle_4 =>
              LDDI <= '1';
              if Mode="00" then--The old 6500 writes back what is just read, before changing. The 65c does another read
                Write <= '1';
              end if;
              Set_Addr_To <= Set_Addr_To_BA;
            when Cycle_5 =>
              LDALU <= '1';
              SaveP <= '1';
              Write <= '1';
              Set_Addr_To <= Set_Addr_To_BA;
            when Cycle_6 =>
              if Mode="00" and IR(0)='1' then
                ALUmore <= '1';
                Set_BusA_To <= Set_BusA_To_ABC;
              end if;
            when others =>
          end case;
        else -- ($1C,$3C,$5C,$7C,$9C,$BC,$DC,$FC, $1D,$3D,$5D,$7D,$9D,$BD,$DD,$FD, $9E,$BE,$9F,$BF)
          lCycle <= Cycle_4;--Or 3 if not page crossing
          if IR(7 downto 6) /= "10" then
            if Mode/="00" or IR(4)='0' or IR(1 downto 0)/="00" then
              LDA <= '1';
            end if;
          end if;
          case MCycle is
            when Cycle_sync =>
            when Cycle_1 =>
              Jump <= "01";
              LDBAL <= '1';
            when Cycle_2 =>
              Jump <= "01";
              -- special case $BE which uses Y reg as index!!
              if(IR(7 downto 6)="10" and IR(4 downto 1)="1111") then
                Set_BusA_To <= Set_BusA_To_Y;
              else
                Set_BusA_To <= Set_BusA_To_X;
              end if;
              BAAdd <= "10";      -- BA Add
              LDBAH <= '1';
              Set_Addr_To <= Set_Addr_To_BA;
            when Cycle_3 =>
              BAAdd <= "11";      -- BA adj
              if IR(7 downto 5) = "100" then -- ($9E,$9F)
                Write <= '1';
              else
                BreakAtNA <= '1';
              end if;
              Set_Addr_To <= Set_Addr_To_BA;
            when Cycle_4 =>
            when others =>
          end case;
        end if;
      when others =>
    end case;
  end process;

  process (IR, MCycle, Mode,ALUmore)
  begin
    -- ORA, AND, EOR, ADC, NOP, LD, CMP, SBC
    -- ASL, ROL, LSR, ROR, BIT, LD, DEC, INC
    case IR(1 downto 0) is
      when "00" =>
        case IR(4 downto 2) is
        -- IR: $00,$20,$40,$60,$80,$A0,$C0,$E0
        --     $04,$24,$44,$64,$84,$A4,$C4,$E4
        --     $0C,$2C,$4C,$6C,$8C,$AC,$CC,$EC
        when "000" | "001" | "011" =>
          case IR(7 downto 5) is
            when "110" | "111" => -- CP ($C0,$C4,$CC,$E0,$E4,$EC)
              ALU_Op <= ALU_OP_CMP;
            when "101" => -- LD ($A0,$A4,$AC)
              ALU_Op <= ALU_OP_EQ2;
            when "001" => -- BIT ($20,$24,$2C - $20 is ignored, as its a jmp)
              ALU_Op <= ALU_OP_BIT;
            when others => -- other, NOP/ST ($x0,$x4,$xC)
              ALU_Op <= ALU_OP_EQ1;
          end case;

        -- IR: $08,$28,$48,$68,$88,$A8,$C8,$E8
        when "010" =>
          case IR(7 downto 5) is
            when "111" | "110" => -- IN ($C8,$E8)
              ALU_Op <= ALU_OP_INC;
            when "100" => -- DEY ($88)
              ALU_Op <= ALU_OP_DEC;
            when others => -- LD
              ALU_Op <= ALU_OP_EQ2;
          end case;

        -- IR: $18,$38,$58,$78,$98,$B8,$D8,$F8
        when "110" =>
          case IR(7 downto 5) is
            when "100" => -- TYA ($98)
              ALU_Op <= ALU_OP_EQ2;
            when others =>
              ALU_Op <= ALU_OP_EQ1;
          end case;

        -- IR: $10,$30,$50,$70,$90,$B0,$D0,$F0
        --     $14,$34,$54,$74,$94,$B4,$D4,$F4
        --     $1C,$3C,$5C,$7C,$9C,$BC,$DC,$FC
        when others =>
          case IR(7 downto 5) is
            when "101" => -- LD ($B0,$B4,$BC)
              ALU_Op <= ALU_OP_EQ2;
            when others =>
              ALU_Op <= ALU_OP_EQ1;
          end case;
        end case;

      when "01" => -- OR
        case(to_integer(unsigned(IR(7 downto 5)))) is
          when 0=> -- IR: $01,$05,$09,$0D,$11,$15,$19,$1D
            ALU_Op<=ALU_OP_OR;
          when 1=> -- IR: $21,$25,$29,$2D,$31,$35,$39,$3D
            ALU_Op<=ALU_OP_AND;
          when 2=> -- IR: $41,$45,$49,$4D,$51,$55,$59,$5D
            ALU_Op<=ALU_OP_EOR;
          when 3=> -- IR: $61,$65,$69,$6D,$71,$75,$79,$7D
            ALU_Op<=ALU_OP_ADC; 
          when 4=>-- IR: $81,$85,$89,$8D,$91,$95,$99,$9D
            ALU_Op<=ALU_OP_EQ1; -- STA
          when 5=> -- IR: $A1,$A5,$A9,$AD,$B1,$B5,$B9,$BD
            ALU_Op<=ALU_OP_EQ2; -- LDA
          when 6=> -- IR: $C1,$C5,$C9,$CD,$D1,$D5,$D9,$DD
            ALU_Op<=ALU_OP_CMP;
          when others=> -- IR: $E1,$E5,$E9,$ED,$F1,$F5,$F9,$FD
            ALU_Op<=ALU_OP_SBC;
        end case;

      when "10" =>
        case(to_integer(unsigned(IR(7 downto 5)))) is
          when 0=> -- IR: $02,$06,$0A,$0E,$12,$16,$1A,$1E
            ALU_Op<=ALU_OP_ASL;
            if IR(4 downto 2) = "110" and Mode/="00" then -- 00011010,$1A -> INC acc, not on 6502
              ALU_Op <= ALU_OP_INC;
            end if;
          when 1=> -- IR: $22,$26,$2A,$2E,$32,$36,$3A,$3E
            ALU_Op<=ALU_OP_ROL;
            if IR(4 downto 2) = "110" and Mode/="00" then -- 00111010,$3A -> DEC acc, not on 6502
              ALU_Op <= ALU_OP_DEC;
            end if;
          when 2=> -- IR: $42,$46,$4A,$4E,$52,$56,$5A,$5E
            ALU_Op<=ALU_OP_LSR;
          when 3=> -- IR: $62,$66,$6A,$6E,$72,$76,$7A,$7E
            ALU_Op<=ALU_OP_ROR;
          when 4=> -- IR: $82,$86,$8A,$8E,$92,$96,$9A,$9E
            ALU_Op<=ALU_OP_BIT;
            if IR(4 downto 2) = "010" then   -- 10001010, $8A -> TXA
              ALU_Op <= ALU_OP_EQ2;
            else                             -- 100xxx10, $82,$86,$8E,$92,$96,$9A,$9E
              ALU_Op <= ALU_OP_EQ1;
            end if;
          when 5=> -- IR: $A2,$A6,$AA,$AE,$B2,$B6,$BA,$BE
            ALU_Op<=ALU_OP_EQ2; -- LDX
          when 6=> -- IR: $C2,$C6,$CA,$CE,$D2,$D6,$DA,$DE
            ALU_Op<=ALU_OP_DEC;
          when others=> -- IR: $E2,$E6,$EA,$EE,$F2,$F6,$FA,$FE
            ALU_Op<=ALU_OP_INC;
        end case;

      when others => -- "11" undoc double alu ops
        case(to_integer(unsigned(IR(7 downto 5)))) is
          -- IR: $A3,$A7,$AB,$AF,$B3,$B7,$BB,$BF
          when 5 =>
            if IR=x"bb" then--LAS
              ALU_Op <= ALU_OP_AND;
            else
              ALU_Op <= ALU_OP_EQ2;
            end if;

          -- IR: $03,$07,$0B,$0F,$13,$17,$1B,$1F
          --     $23,$27,$2B,$2F,$33,$37,$3B,$3F
          --     $43,$47,$4B,$4F,$53,$57,$5B,$5F
          --     $63,$67,$6B,$6F,$73,$77,$7B,$7F
          --     $83,$87,$8B,$8F,$93,$97,$9B,$9F
          --     $C3,$C7,$CB,$CF,$D3,$D7,$DB,$DF
          --     $E3,$E7,$EB,$EF,$F3,$F7,$FB,$FF
          when others =>
            if IR=x"6b" then -- ARR
              ALU_Op<=ALU_OP_ARR;
            elsif IR=x"8b" then -- ARR
              ALU_Op<=ALU_OP_XAA;   -- we can't use the bit operation as we don't set all flags...
            elsif IR=x"0b" or IR=x"2b" then -- ANC
              ALU_Op<=ALU_OP_ANC;
            elsif IR=x"eb" then -- alternate SBC
              ALU_Op<=ALU_OP_SBC;
            elsif ALUmore='1'  then 
              case(to_integer(unsigned(IR(7 downto 5)))) is
                when 0=>
                  ALU_Op<=ALU_OP_OR;
                when 1=>
                  ALU_Op<=ALU_OP_AND;
                when 2=>
                  ALU_Op<=ALU_OP_EOR;
                when 3=>
                  ALU_Op<=ALU_OP_ADC;
                when 4=>
                  ALU_Op<=ALU_OP_EQ1; -- STA
                when 5=>
                  ALU_Op<=ALU_OP_EQ2; -- LDA
                when 6=>
                  ALU_Op<=ALU_OP_CMP;
                when others=>
                  ALU_Op<=ALU_OP_SBC;
              end case;
            else
              case(to_integer(unsigned(IR(7 downto 5)))) is
                when 0=>
                  ALU_Op<=ALU_OP_ASL;
                when 1=>
                  ALU_Op<=ALU_OP_ROL;
                when 2=>
                  ALU_Op<=ALU_OP_LSR;
                when 3=>
                  ALU_Op<=ALU_OP_ROR;
                when 4=>
                  ALU_Op<=ALU_OP_BIT;
                when 5=>
                  ALU_Op<=ALU_OP_EQ2; -- LDX
                when 6=>
                  ALU_Op<=ALU_OP_DEC;
                  if IR(4 downto 2)="010" then  -- $6B
                    ALU_Op<=ALU_OP_SAX; -- special SAX (SBX) case
                  end if;
                when others=>
                   ALU_Op<=ALU_OP_INC;
              end case;
            end if;
        end case;
    end case;
  end process;

end;
