library ieee;
use ieee.std_logic_1164.all;

entity CONTROLLER is
  port (
    -- From top level design
    CLK: in STD_LOGIC;
    nRST: in STD_LOGIC;
    ENABLE: in STD_LOGIC;

    -- from shift register
    OPCODE: in STD_LOGIC_VECTOR(3 downto 0);
    DATA: in STD_LOGIC_VECTOR(511 downto 0);

    -- to and from shift register
    SR_BUSY: in STD_LOGIC;
    SR_READ: out STD_LOGIC;

    -- to and from matrix math
    MM0_ENABLE: out STD_LOGIC;
    MM0_OPCODE: out STD_LOGIC_VECTOR(2 downto 0);
    MM0_DATA: out STD_LOGIC_VECTOR(511 downto 0);
    MM0_RETURN: in STD_LOGIC_VECTOR(95 downto 0);
    MM1_ENABLE: out STD_LOGIC;
    MM1_OPCODE: out STD_LOGIC_VECTOR(2 downto 0);
    MM1_DATA: out STD_LOGIC_VECTOR(511 downto 0);
    MM1_RETURN: in STD_LOGIC_VECTOR(95 downto 0);
    MM_BUSY: in STD_LOGIC;

    -- to and from 3D projection
    TP0_ENABLE: out STD_LOGIC;
    TP0_OPCODE: out STD_LOGIC_VECTOR(2 downto 0);
    TP0_DATA: out STD_LOGIC_VECTOR(95 downto 0);
    TP0_RETURN: in STD_LOGIC_VECTOR(23 downto 0);
    TP1_ENABLE: out STD_LOGIC;
    TP1_OPCODE: out STD_LOGIC_VECTOR(2 downto 0);
    TP1_DATA: out STD_LOGIC_VECTOR(95 downto 0);
    TP1_RETURN: in STD_LOGIC_VECTOR(23 downto 0);
    TP_BUSY: in STD_LOGIC;

    -- to and from line rasterization
    LR_ENABLE: out STD_LOGIC;
    LR_OPCODE: out STD_LOGIC_VECTOR(1 downto 0);
    LR_DATA: out STD_LOGIC_VECTOR(47 downto 0);
    LR_BUSY: in STD_LOGIC;
    
    -- to and from screen buffer
    SB_ENABLE: out STD_LOGIC;
    SB_OPCODE: out STD_LOGIC_VECTOR(2 downto 0);
    SB_DATA: out STD_LOGIC_VECTOR(23 downto 0);
    SB_BUSY: in STD_LOGIC;

    -- to and from screen buffer
    LCD_FLIP: out STD_LOGIC;
    LCD_BUSY: in STD_LOGIC);
end CONTROLLER;

architecture CONTROLLER_ARCH of CONTROLLER is
  -- opcode constants for nextstate assignment
  constant OPCODE_SETMAT: STD_LOGIC_VECTOR(3 downto 0) := "0000";
  constant OPCODE_MULMAT: STD_LOGIC_VECTOR(3 downto 0) := "0001";
  constant OPCODE_RSTMAT: STD_LOGIC_VECTOR(3 downto 0) := "0010";
  constant OPCODE_XFOV: STD_LOGIC_VECTOR(3 downto 0) := "0011";
  constant OPCODE_YFOV: STD_LOGIC_VECTOR(3 downto 0) := "0100";
  constant OPCODE_PT3D: STD_LOGIC_VECTOR(3 downto 0) := "0101";
  constant OPCODE_LN3D: STD_LOGIC_VECTOR(3 downto 0) := "0110";
  constant OPCODE_COLOR: STD_LOGIC_VECTOR(3 downto 0) := "0111";
  constant OPCODE_CLR: STD_LOGIC_VECTOR(3 downto 0) := "1000";
  constant OPCODE_FLIP: STD_LOGIC_VECTOR(3 downto 0) := "1001";
  constant OPCODE_PT2D: STD_LOGIC_VECTOR(3 downto 0) := "1010";
  constant OPCODE_LN2D: STD_LOGIC_VECTOR(3 downto 0) := "1011";

  -- opcode constants for module assignment
  constant MM_OPCODE_SETMAT: STD_LOGIC_VECTOR(2 downto 0) := "010";
  constant MM_OPCODE_MULMAT: STD_LOGIC_VECTOR(2 downto 0) := "100";
  constant MM_OPCODE_MULVEC: STD_LOGIC_VECTOR(2 downto 0) := "001";
  constant MM_OPCODE_RSTMAT: STD_LOGIC_VECTOR(2 downto 0) := "011";
  constant TP_OPCODE_XFOV: STD_LOGIC_VECTOR(2 downto 0) := "010";
  constant TP_OPCODE_YFOV: STD_LOGIC_VECTOR(2 downto 0) := "011";
  constant TP_OPCODE_PROJ: STD_LOGIC_VECTOR(2 downto 0) := "001";
  constant LR_OPCODE_RAST: STD_LOGIC_VECTOR(1 downto 0) := "01";
  constant SB_OPCODE_SET: STD_LOGIC_VECTOR(2 downto 0) := "001";
  constant SB_OPCODE_PLOT: STD_LOGIC_VECTOR(2 downto 0) := "010";
  constant SB_OPCODE_CLR: STD_LOGIC_VECTOR(2 downto 0) := "011";
  
  -- ZERO and Z constants for signal assignment
  constant DATA_512_ZEROS: STD_LOGIC_VECTOR(511 downto 0) := x"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
  constant DATA_96_ZEROS: STD_LOGIC_VECTOR(95 downto 0) := x"000000000000000000000000";
  constant DATA_48_ZEROS: STD_LOGIC_VECTOR(47 downto 0) := x"000000000000";
  constant DATA_24_ZEROS: STD_LOGIC_VECTOR(23 downto 0) := x"000000";
  constant DATA_24_ZZ: STD_LOGIC_VECTOR(23 downto 0) := "ZZZZZZZZZZZZZZZZZZZZZZZZ";
  
  type CONTROLLER_STATE is (
    IDLE, SETUP, SETMAT0, SETMAT0a, SETMAT1, MULMAT0, MULMAT0a, MULMAT1, RSTMAT0, RSTMAT0a, RSTMAT1, 
    XFOV0, XFOV0a, XFOV1, YFOV0, YFOV0a, YFOV1, PT3D0, PT3D0a, PT3D1, PT3D1a, PT3D2, PT3D2a, PT3D3,
    LN3D0, LN3D0a, LN3D1, LN3D1a, LN3D2, LN3D3, LN3D4, COLOR0, COLOR0a, COLOR1, CLR0, CLR1, 
    FLIP0, FLIP1, PT2D0, PT2D0a, PT2D1, LN2D0, LN2D0a, LN2D1, PT3D1b, LN3D1b);
    
  -- Signals for internal latching and nextstate
  signal OPCODE_LATCH: STD_LOGIC_VECTOR(3 downto 0);
  signal DATA_LATCH: STD_LOGIC_VECTOR(511 downto 0);
  signal STATE: CONTROLLER_STATE;
  signal NEXT_STATE: CONTROLLER_STATE;
  signal STATE_LATCH: CONTROLLER_STATE;
  signal SETUP_FLAG: STD_LOGIC;

begin
  Clock : process (CLK, nRST)
  begin
    if (nRST = '0') then
      -- asynchronous latch values
      STATE <= IDLE;
      SR_READ <= '0';
      DATA_LATCH <= DATA_512_ZEROS;
      OPCODE_LATCH <= "0000";
      MM0_ENABLE <= '0';
      MM1_ENABLE <= '0';
      TP0_ENABLE <= '0';
      TP1_ENABLE <= '0';
      LR_ENABLE <= '0';
      SB_ENABLE <= '0';
      LCD_FLIP <= '0';
      SETUP_FLAG <= '0';
    elsif (CLK'event and CLK = '1') then
      if (ENABLE = '1') then
        STATE <= NEXT_STATE;
        if (STATE = IDLE and SR_BUSY = '1') then
          -- shift register has data for the controller, and previous operation is completed
          DATA_LATCH <= DATA;
          OPCODE_LATCH <= OPCODE;
          SR_READ <= '1';
          SETUP_FLAG <= '1';
          case OPCODE is
            when OPCODE_SETMAT => STATE_LATCH <= SETMAT0;
            when OPCODE_MULMAT => STATE_LATCH <= MULMAT0;
            when OPCODE_RSTMAT => STATE_LATCH <= RSTMAT0;
            when OPCODE_XFOV   => STATE_LATCH <= XFOV0;
            when OPCODE_YFOV   => STATE_LATCH <= YFOV0;
            when OPCODE_PT3D   => STATE_LATCH <= PT3D0;
            when OPCODE_LN3D   => STATE_LATCH <= LN3D0;
            when OPCODE_COLOR  => STATE_LATCH <= COLOR0;
            when OPCODE_CLR    => STATE_LATCH <= CLR0;
            when OPCODE_FLIP   => STATE_LATCH <= FLIP0;
            when OPCODE_PT2D   => STATE_LATCH <= PT2D0;
            when OPCODE_LN2D   => STATE_LATCH <= LN2D0;
            when others        => STATE_LATCH <= IDLE;
          end case;
        else
          -- latch other values while waiting for shift register, or running operations
          SETUP_FLAG <= '0';
          SR_READ <= '0';
        end if;
        
        -- appropriate enables for each state entry
        
        if (NEXT_STATE = IDLE) then
          MM0_ENABLE <= '0';
          MM1_ENABLE <= '0';
          TP0_ENABLE <= '0';
          TP1_ENABLE <= '0';
          LR_ENABLE <= '0';
          SB_ENABLE <= '0';
          LCD_FLIP <= '0';
        elsif (NEXT_STATE = SETMAT0) then
          MM0_ENABLE <= '1';
          MM1_ENABLE <= '1';
        elsif (NEXT_STATE = SETMAT0a) then
          MM0_ENABLE <= '1';
          MM1_ENABLE <= '1';
        elsif (NEXT_STATE = SETMAT1) then
          if (MM_BUSY = '0') then
            MM0_ENABLE <= '0';
            MM1_ENABLE <= '0';
          end if;
        elsif (NEXT_STATE = MULMAT0) then
          MM0_ENABLE <= '1';
          MM1_ENABLE <= '1';
        elsif (NEXT_STATE = MULMAT0a) then
          MM0_ENABLE <= '1';
          MM1_ENABLE <= '1';
        elsif (NEXT_STATE = MULMAT1) then
          if (MM_BUSY = '0') then
            MM0_ENABLE <= '0';
            MM1_ENABLE <= '0';
          end if;
        elsif (NEXT_STATE = RSTMAT0) then
          MM0_ENABLE <= '1';
          MM1_ENABLE <= '1';
        elsif (NEXT_STATE = RSTMAT0a) then
          MM0_ENABLE <= '1';
          MM1_ENABLE <= '1';
        elsif (NEXT_STATE = RSTMAT1) then
          if (MM_BUSY = '0') then
            MM0_ENABLE <= '0';
            MM1_ENABLE <= '0';
          end if;
        elsif (NEXT_STATE = XFOV0) then
          TP0_ENABLE <= '1';
          TP1_ENABLE <= '1';
        elsif (NEXT_STATE = XFOV0a) then
          TP0_ENABLE <= '1';
          TP1_ENABLE <= '1';
        elsif (NEXT_STATE = XFOV1) then
          if (TP_BUSY = '0') then
            TP0_ENABLE <= '0';
            TP1_ENABLE <= '0';
          end if;
        elsif (NEXT_STATE = YFOV0) then
          TP0_ENABLE <= '1';
          TP1_ENABLE <= '1';
        elsif (NEXT_STATE = YFOV0a) then
          TP0_ENABLE <= '1';
          TP1_ENABLE <= '1';
        elsif (NEXT_STATE = YFOV1) then
          if (SB_BUSY = '0') then
            TP0_ENABLE <= '0';
            TP1_ENABLE <= '0';
          end if;
        elsif (NEXT_STATE = PT3D0) then
          MM0_ENABLE <= '1';
        elsif (NEXT_STATE = PT3D0a) then
          MM0_ENABLE <= '1';
        elsif (NEXT_STATE = PT3D1) then
          if (MM_BUSY = '0') then
            MM0_ENABLE <= '0';
            TP0_ENABLE <= '1';
          end if;
        elsif (NEXT_STATE = PT3D1a) then
          if (MM_BUSY = '0') then
            MM0_ENABLE <= '0';
            TP0_ENABLE <= '1';
          end if;
        elsif (NEXT_STATE = PT3D1b) then
          if (MM_BUSY = '0') then
            MM0_ENABLE <= '0';
            TP0_ENABLE <= '1';
          end if;
        elsif (NEXT_STATE = PT3D2) then
          if (TP_BUSY = '0') then
            TP0_ENABLE <= '0';
            SB_ENABLE <= '1';
          end if;
        elsif (NEXT_STATE = PT3D2a) then
          if (TP_BUSY = '0') then
            TP0_ENABLE <= '0';
            SB_ENABLE <= '1';
          end if;
        elsif (NEXT_STATE = PT3D3) then
          if (SB_BUSY = '0') then
            SB_ENABLE <= '0';
          end if;
        elsif (NEXT_STATE = LN3D0) then
            MM0_ENABLE <= '1';
            MM1_ENABLE <= '1';
        elsif (NEXT_STATE = LN3D0a) then
            MM0_ENABLE <= '1';
            MM1_ENABLE <= '1';
        elsif (NEXT_STATE = LN3D1) then
          if (MM_BUSY = '0') then
            MM0_ENABLE <= '0';
            MM1_ENABLE <= '0';
            TP0_ENABLE <= '1';
            TP1_ENABLE <= '1';
          end if;
        elsif (NEXT_STATE = LN3D1a) then
          if (MM_BUSY = '0') then
            MM0_ENABLE <= '0';
            MM1_ENABLE <= '0';
            TP0_ENABLE <= '1';
            TP1_ENABLE <= '1';
          end if;
        elsif (NEXT_STATE = LN3D1b) then
          if (MM_BUSY = '0') then
            MM0_ENABLE <= '0';
            MM1_ENABLE <= '0';
            TP0_ENABLE <= '1';
            TP1_ENABLE <= '1';
          end if;
        elsif (NEXT_STATE = LN3D2) then
          if (TP_BUSY = '0') then
            TP0_ENABLE <= '0';
            TP1_ENABLE <= '0';
            LR_ENABLE <= '1';
            SB_ENABLE <= 'Z';
          end if;
        elsif (NEXT_STATE = LN3D3) then
          TP0_ENABLE <= '0';
          TP1_ENABLE <= '0';
          LR_ENABLE <= '1';
          SB_ENABLE <= 'Z';
        elsif (NEXT_STATE = LN3D4) then
          if (LR_BUSY = '0') then
            LR_ENABLE <= '0';
            SB_ENABLE <= '0';
          end if;
        elsif (NEXT_STATE = COLOR0) then
          SB_ENABLE <= '1';
        elsif (NEXT_STATE = COLOR1) then
          if (SB_BUSY = '0') then 
            SB_ENABLE <= '0';
          end if;
        elsif (NEXT_STATE = CLR0) then
          SB_ENABLE <= '1';
        elsif (NEXT_STATE = CLR1) then
          if (SB_BUSY = '0') then
            SB_ENABLE <= '0';
          end if;
        elsif (NEXT_STATE = FLIP0) then
          LCD_FLIP <= '1';
        elsif (NEXT_STATE = FLIP1) then
          if (LCD_BUSY = '0') then
            LCD_FLIP <= '0';
          end if;
        elsif (NEXT_STATE = PT2D0) then
          SB_ENABLE <= '1';
        elsif (NEXT_STATE = PT2D0a) then
          SB_ENABLE <= '1';
        elsif (NEXT_STATE = PT2D1) then
          if (SB_BUSY = '0') then
            SB_ENABLE <= '0';
          end if;
        elsif (NEXT_STATE = LN2D0) then
          LR_ENABLE <= '1';
          SB_ENABLE <= 'Z';
        elsif (NEXT_STATE = LN2D0a) then
          LR_ENABLE <= '1';
          SB_ENABLE <= 'Z';
        elsif (NEXT_STATE = LN2D1) then
          if (LR_BUSY = '0') then
            LR_ENABLE <= '0';
            SB_ENABLE <= '0';
          end if;
        end if;
      end if;
    end if;
  end process Clock;

  -- process for determining state logic
  process (STATE, SR_BUSY, CLK, nRST, DATA_LATCH, OPCODE, DATA, OPCODE_LATCH, MM_BUSY, TP_BUSY,
            MM0_RETURN, TP0_RETURN, SB_BUSY, MM1_RETURN, TP1_RETURN, LR_BUSY, LCD_BUSY, STATE_LATCH)
  begin
    if(nRST = '0') then
      -- defaulting to reset value
      NEXT_STATE <= IDLE;
      MM0_OPCODE <= "000";
      MM1_OPCODE <= "000";
      TP0_OPCODE <= "000";
      TP1_OPCODE <= "000";
      LR_OPCODE <= "00";
      SB_OPCODE <= "000";
      MM0_DATA <= DATA_512_ZEROS;
      MM1_DATA <= DATA_512_ZEROS;
      TP0_DATA <= DATA_96_ZEROS;
      TP1_DATA <= DATA_96_ZEROS;
      LR_DATA <= DATA_48_ZEROS;
      SB_DATA <= DATA_24_ZEROS;
    else
      case STATE is
        when IDLE =>
          if (SETUP_FLAG = '1') then
            -- Preparing to run a new opcode
            NEXT_STATE <= SETUP;
          else
            -- Waiting for a new opcode
            NEXT_STATE <= IDLE;
          end if;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          MM0_DATA <= DATA_LATCH;
          MM1_DATA <= DATA_LATCH;
          TP0_DATA <= DATA_LATCH(95 downto 0);
          TP1_DATA <= DATA_LATCH(95 downto 0);
          LR_DATA <= DATA_LATCH(47 downto 0);
          SB_DATA <= DATA_24_ZEROS;
          
        when SETUP =>
            -- Starting a new opcode
            NEXT_STATE <= STATE_LATCH;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            MM0_DATA <= DATA_LATCH;
            MM1_DATA <= DATA_LATCH;
            TP0_DATA <= DATA_LATCH(95 downto 0);
            TP1_DATA <= DATA_LATCH(95 downto 0);
            LR_DATA <= DATA_LATCH(47 downto 0);
            SB_DATA <= DATA_24_ZEROS;
                    
        when SETMAT0 =>
          -- Set matrix opcode
          MM0_DATA <= DATA_LATCH;
          MM0_OPCODE <= MM_OPCODE_SETMAT;
          MM1_DATA <= DATA_LATCH;
          MM1_OPCODE <= MM_OPCODE_SETMAT;
          NEXT_STATE <= SETMAT0a;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
  
        when SETMAT0a =>
          -- Set matrix opcode
          MM0_DATA <= DATA_LATCH;
          MM0_OPCODE <= MM_OPCODE_SETMAT;
          MM1_DATA <= DATA_LATCH;
          MM1_OPCODE <= MM_OPCODE_SETMAT;
          NEXT_STATE <= SETMAT1;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;

        when SETMAT1 =>
          if (MM_BUSY = '0') then
            NEXT_STATE <= IDLE;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          else
            NEXT_STATE <= SETMAT1;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          end if;
  
        when MULMAT0 =>
          -- Multiply matrix opcode
          MM0_DATA <= DATA_LATCH;
          MM0_OPCODE <= MM_OPCODE_MULMAT;
          MM1_DATA <= DATA_LATCH;
          MM1_OPCODE <= MM_OPCODE_MULMAT;
          NEXT_STATE <= MULMAT0a;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
  
        when MULMAT0a =>
          -- Multiply matrix opcode
          MM0_DATA <= DATA_LATCH;
          MM0_OPCODE <= MM_OPCODE_MULMAT;
          MM1_DATA <= DATA_LATCH;
          MM1_OPCODE <= MM_OPCODE_MULMAT;
          NEXT_STATE <= MULMAT1;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;

        when MULMAT1 =>
          if (MM_BUSY = '0') then
            NEXT_STATE <= IDLE;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          else
            NEXT_STATE <= MULMAT1;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          end if;
  
        when RSTMAT0 =>
          -- Reset matrix opcode
          MM0_OPCODE <= MM_OPCODE_RSTMAT;
          MM1_OPCODE <= MM_OPCODE_RSTMAT;
          NEXT_STATE <= RSTMAT0a;
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
  
        when RSTMAT0a =>
          -- Reset matrix opcode
          MM0_OPCODE <= MM_OPCODE_RSTMAT;
          MM1_OPCODE <= MM_OPCODE_RSTMAT;
          NEXT_STATE <= RSTMAT1;
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
  
        when RSTMAT1 =>
          if (MM_BUSY = '0') then
            NEXT_STATE <= IDLE;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          else
            NEXT_STATE <= RSTMAT1;
            MM0_OPCODE <= MM_OPCODE_RSTMAT;
            MM1_OPCODE <= MM_OPCODE_RSTMAT;
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          end if;
  
        when XFOV0 =>
          -- Set 3d projection X field of view opcode
          TP0_OPCODE <= TP_OPCODE_XFOV;
          TP0_DATA <= DATA_LATCH(95 downto 0);
          TP1_OPCODE <= TP_OPCODE_XFOV;
          TP1_DATA <= DATA_LATCH(95 downto 0);
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
          NEXT_STATE <= XFOV0a;

        when XFOV0a =>
          -- Set 3d projection X field of view opcode
          TP0_OPCODE <= TP_OPCODE_XFOV;
          TP0_DATA <= DATA_LATCH(95 downto 0);
          TP1_OPCODE <= TP_OPCODE_XFOV;
          TP1_DATA <= DATA_LATCH(95 downto 0);
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
          NEXT_STATE <= XFOV1;
            
        when XFOV1 =>
          if (TP_BUSY = '0') then
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            NEXT_STATE <= IDLE;
          else
            NEXT_STATE <= XFOV1;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= TP_OPCODE_XFOV;
            TP1_OPCODE <= TP_OPCODE_XFOV;
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_LATCH(95 downto 0);
            TP1_DATA <= DATA_LATCH(95 downto 0);
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          end if;
          
        when YFOV0 =>
          -- Set 3d projection Y field of view opcode
          TP0_OPCODE <= TP_OPCODE_YFOV;
          TP0_DATA <= DATA_LATCH(95 downto 0);
          TP1_OPCODE <= TP_OPCODE_YFOV;
          TP1_DATA <= DATA_LATCH(95 downto 0);
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
          NEXT_STATE <= YFOV0a;

        when YFOV0a =>
          -- Set 3d projection Y field of view opcode
          TP0_OPCODE <= TP_OPCODE_YFOV;
          TP0_DATA <= DATA_LATCH(95 downto 0);
          TP1_OPCODE <= TP_OPCODE_YFOV;
          TP1_DATA <= DATA_LATCH(95 downto 0);
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
          NEXT_STATE <= YFOV1;  
          
        when YFOV1 =>
          if (TP_BUSY = '0') then
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
            NEXT_STATE <= IDLE;
          else
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= TP_OPCODE_YFOV;
            TP1_OPCODE <= TP_OPCODE_YFOV;
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_LATCH(95 downto 0);
            TP1_DATA <= DATA_LATCH(95 downto 0);
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
            NEXT_STATE <= YFOV1;
          end if;
  
        when PT3D0 =>
          -- Draw 1 3d point opcode
          MM0_OPCODE <= MM_OPCODE_MULVEC;
          MM0_DATA <= DATA_LATCH;
          MM1_OPCODE <= "000";
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
          NEXT_STATE <= PT3D0a;
                   
        when PT3D0a =>
          -- Draw 1 3d point opcode
          MM0_OPCODE <= MM_OPCODE_MULVEC;
          MM0_DATA <= DATA_LATCH;
          MM1_OPCODE <= "000";
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
          NEXT_STATE <= PT3D1;

        when PT3D1 =>
          if (MM_BUSY = '0') then
            TP0_OPCODE <= TP_OPCODE_PROJ;
            TP0_DATA <= MM0_RETURN;
            NEXT_STATE <= PT3D1a;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_LATCH;
            MM1_DATA <= DATA_512_ZEROS;
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          else
            MM0_OPCODE <= MM_OPCODE_MULVEC;
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_LATCH;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
            NEXT_STATE <= PT3D1;
          end if;

        when PT3D1a =>
          TP0_OPCODE <= TP_OPCODE_PROJ;
          TP0_DATA <= MM0_RETURN;
          NEXT_STATE <= PT3D1b;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_LATCH;
          MM1_DATA <= DATA_512_ZEROS;
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
            
        when PT3D1b =>
          TP0_OPCODE <= TP_OPCODE_PROJ;
          TP0_DATA <= MM0_RETURN;
          NEXT_STATE <= PT3D2;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_LATCH;
          MM1_DATA <= DATA_512_ZEROS;
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;

        when PT3D2 =>
          if (TP_BUSY = '0') then
            SB_OPCODE <= SB_OPCODE_PLOT;
            SB_DATA <= TP0_RETURN;
            NEXT_STATE <= PT3D2a;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_LATCH;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
          else
            NEXT_STATE <= PT3D2;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_LATCH;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          end if;
          
        when PT3D2a =>
          SB_OPCODE <= SB_OPCODE_PLOT;
          SB_DATA <= TP0_RETURN;
          NEXT_STATE <= PT3D3;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_LATCH;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;


        when PT3D3 =>
          if (SB_BUSY = '0') then
            NEXT_STATE <= IDLE;
            MM0_DATA <= DATA_LATCH;
            MM1_DATA <= DATA_512_ZEROS;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          else
            NEXT_STATE <= PT3D3;
            MM0_DATA <= DATA_LATCH;
            MM1_DATA <= DATA_512_ZEROS;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";          
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          end if;
          
        when LN3D0 =>
          -- Draw a 3d line opcode
          MM0_OPCODE <= MM_OPCODE_MULVEC;
          MM1_OPCODE <= MM_OPCODE_MULVEC;
          MM0_DATA <= DATA_512_ZEROS(511 downto 96) & DATA_LATCH(95 downto 0);
          MM1_DATA <= DATA_512_ZEROS(511 downto 96) & DATA_LATCH(191 downto 96);
          NEXT_STATE <= LN3D0a;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
                   
        when LN3D0a =>
          -- Draw a 3d line opcode
          MM0_OPCODE <= MM_OPCODE_MULVEC;
          MM1_OPCODE <= MM_OPCODE_MULVEC;
          MM0_DATA <= DATA_512_ZEROS(511 downto 96) & DATA_LATCH(95 downto 0);
          MM1_DATA <= DATA_512_ZEROS(511 downto 96) & DATA_LATCH(191 downto 96);
          NEXT_STATE <= LN3D1;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;

        when LN3D1 =>
          if (MM_BUSY = '0') then
            TP0_DATA <= MM0_RETURN;
            TP1_DATA <= MM1_RETURN;
            TP0_OPCODE <= TP_OPCODE_PROJ;
            TP1_OPCODE <= TP_OPCODE_PROJ;
            NEXT_STATE <= LN3D1a;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          else
            NEXT_STATE <= LN3D1;
            MM0_OPCODE <= MM_OPCODE_MULVEC;
            MM1_OPCODE <= MM_OPCODE_MULVEC;
            MM0_DATA <= DATA_512_ZEROS(511 downto 96) & DATA_LATCH(95 downto 0);
            MM1_DATA <= DATA_512_ZEROS(511 downto 96) & DATA_LATCH(191 downto 96);
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          end if;

        when LN3D1a =>
          TP0_DATA <= MM0_RETURN;
          TP1_DATA <= MM1_RETURN;
          TP0_OPCODE <= TP_OPCODE_PROJ;
          TP1_OPCODE <= TP_OPCODE_PROJ;
          NEXT_STATE <= LN3D1b;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;

        when LN3D1b =>
          TP0_DATA <= MM0_RETURN;
          TP1_DATA <= MM1_RETURN;
          TP0_OPCODE <= TP_OPCODE_PROJ;
          TP1_OPCODE <= TP_OPCODE_PROJ;
          NEXT_STATE <= LN3D2;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
            
        when LN3D2 =>
          if (TP_BUSY = '0') then
            LR_OPCODE <= LR_OPCODE_RAST;
            LR_DATA <= TP1_RETURN & TP0_RETURN;
            NEXT_STATE <= LN3D3;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            SB_OPCODE <= SB_OPCODE_PLOT;
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            --SB_DATA <= DATA_24_ZEROS;
            SB_DATA <= DATA_24_ZZ;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
          else
            NEXT_STATE <= LN3D2;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= TP_OPCODE_PROJ;
            TP1_OPCODE <= TP_OPCODE_PROJ;
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          end if;

        when LN3D3 =>
            NEXT_STATE <= LN3D4;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= SB_OPCODE_PLOT;
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            --SB_DATA <= DATA_24_ZEROS;
            SB_DATA <= DATA_24_ZZ;
        when LN3D4 =>
          if (LR_BUSY = '0') then
            NEXT_STATE <= IDLE;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          else
            NEXT_STATE <= LN3D4;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= SB_OPCODE_PLOT;
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            --SB_DATA <= DATA_24_ZEROS;
            SB_DATA <= DATA_24_ZZ;
          end if;
  
        when COLOR0 =>
          -- Set Screen buffer opcode
          SB_OPCODE <= SB_OPCODE_SET;
          SB_DATA <= DATA_LATCH(23 downto 0);
          NEXT_STATE <= COLOR0a;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          
        when COLOR0a =>
          -- Set Screen buffer opcode
          SB_OPCODE <= SB_OPCODE_SET;
          SB_DATA <= DATA_LATCH(23 downto 0);
          NEXT_STATE <= COLOR1;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          
        when COLOR1 =>
          if (SB_BUSY = '0') then
            NEXT_STATE <= IDLE;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          else
            NEXT_STATE <= COLOR1;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_LATCH(23 downto 0);
          end if;
          
        when CLR0 =>
          -- Clear Screen Buffer opcode
          SB_OPCODE <= SB_OPCODE_CLR;
          SB_DATA <= DATA_LATCH(23 downto 0);
          NEXT_STATE <= CLR1;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          
        when CLR1 =>
          if (SB_BUSY = '0') then
            NEXT_STATE <= IDLE;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          else
            NEXT_STATE <= CLR1;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_LATCH(23 downto 0);
          end if;
          
        when FLIP0 =>
          -- Flip SRAM Screen Buffer opcode
          NEXT_STATE <= FLIP1;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          --DATA_LATCH <= DATA_512_ZEROS;
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
          
        when FLIP1 =>
          if (LCD_BUSY = '0') then
            NEXT_STATE <= IDLE;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          else
            NEXT_STATE <= FLIP1;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
         end if;
        
        when PT2D0 =>
          -- Draw 1 2D Point opcode
          SB_OPCODE <= SB_OPCODE_SET;
          SB_DATA <= DATA_LATCH(15 downto 0) & x"00";
          NEXT_STATE <= PT2D0a;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
                   
        when PT2D0a =>
          -- Draw 1 2D Point opcode
          SB_OPCODE <= SB_OPCODE_SET;
          SB_DATA <= DATA_LATCH(23 downto 0);
          NEXT_STATE <= PT2D1;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;

        when PT2D1 =>
          if (SB_BUSY = '0') then
            NEXT_STATE <= IDLE;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_LATCH(23 downto 0);
          else
            NEXT_STATE <= PT2D1;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          end if;
            
  
        when LN2D0 =>
          LR_OPCODE <= LR_OPCODE_RAST;
          LR_DATA <= DATA_LATCH(31 downto 16) & x"00" & DATA_LATCH(15 downto 0) & x"00";
          NEXT_STATE <= LN2D0a;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          SB_OPCODE <= SB_OPCODE_PLOT;
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          --SB_DATA <= DATA_24_ZEROS;
          SB_DATA <= DATA_24_ZZ;
          
        when LN2D0a =>
          LR_OPCODE <= LR_OPCODE_RAST;
          LR_DATA <= DATA_LATCH(31 downto 16) & x"00" & DATA_LATCH(15 downto 0) & x"00";
          NEXT_STATE <= LN2D1;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          SB_OPCODE <= SB_OPCODE_PLOT;
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          --SB_DATA <= DATA_24_ZEROS;
          SB_DATA <= DATA_24_ZZ;

        when LN2D1 =>
          -- Draw 1 2D Line opcode
          if (LR_BUSY = '0') then
            NEXT_STATE <= IDLE;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= "000";
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            SB_DATA <= DATA_24_ZEROS;
          else
            NEXT_STATE <= LN2D1;
            MM0_OPCODE <= "000";
            MM1_OPCODE <= "000";
            MM0_DATA <= DATA_512_ZEROS;
            MM1_DATA <= DATA_512_ZEROS;
            TP0_OPCODE <= "000";
            TP1_OPCODE <= "000";
            LR_OPCODE <= "00";
            SB_OPCODE <= SB_OPCODE_PLOT;
            TP0_DATA <= DATA_96_ZEROS;
            TP1_DATA <= DATA_96_ZEROS;
            LR_DATA <= DATA_48_ZEROS;
            --SB_DATA <= DATA_24_ZEROS;
            SB_DATA <= DATA_24_ZZ;
          end if;
  
        when others =>
          -- "This should never happen" opcode
          NEXT_STATE <= IDLE;
          MM0_OPCODE <= "000";
          MM1_OPCODE <= "000";
          MM0_DATA <= DATA_512_ZEROS;
          MM1_DATA <= DATA_512_ZEROS;
          TP0_OPCODE <= "000";
          TP1_OPCODE <= "000";
          LR_OPCODE <= "00";
          SB_OPCODE <= "000";
          TP0_DATA <= DATA_96_ZEROS;
          TP1_DATA <= DATA_96_ZEROS;
          LR_DATA <= DATA_48_ZEROS;
          SB_DATA <= DATA_24_ZEROS;
      end case;
    end if;
  end process;
end CONTROLLER_ARCH;
