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

entity Screen_Buffer is
  port(
    CLK : in std_logic;
    nRST : in std_logic;
    
-- From Line_Math
    DATA : in std_logic_vector(23 downto 0);

-- Controller Interface   
    OPCODE : in std_logic_vector(2 downto 0);
    ENABLE : in std_logic;
    BUSY : out std_logic;
    
--SRAM interface
    Address : out std_logic_vector(15 downto 0);
    SData : out std_logic_vector(15 downto 0);
    SDataIn : in std_logic_vector(15 downto 0);
    Store : out std_logic;
    Load : out std_logic;
    memwait :in std_logic);
end Screen_Buffer;

architecture sbuffer of Screen_Buffer is
  type statetype is (IDLE, COLOR, DRAW, DRAW2, DRAW3, DRAW4, DRAW5, CLEAR, CLEAR2, CLEAR3, CLEAR4, INIT);
  signal state, nextstate : statetype;
  signal X, Y, Z : std_logic_vector(7 downto 0);
  signal cur_color, next_color : std_logic_vector(7 downto 0);
  signal clear_loc, next_clear_loc : unsigned(15 downto 0);
  signal ClrDone, next_clrdone : std_logic;
begin  -- buffer

  -- purpose: Change states
  -- type   : sequential
  -- State changes on rising edge of clock
  Dostate: process (CLK, nRST)
  begin  -- process Dostate
    if nRST = '0' then                  -- asynchronous reset (active low)
      state <= INIT;
      cur_color <= X"00";
    elsif CLK'event and CLK = '1' then  -- rising clock edge
      state <= nextstate;
      clear_loc <= next_clear_loc;
      cur_color <= next_color;
      clrdone <= next_clrdone;
    end if;
  end process Dostate;

  -- purpose: Do the state-nextstate logic
  -- type   : combinational
  StateLogic: process (OPCODE, CLK, DATA, memwait, ENABLE, state, clear_loc, Z, sdatain, clrdone)
  begin  -- process StateLogic
    case state is
      when INIT =>
        nextstate <= IDLE;

      when IDLE =>
        if ENABLE = '1' then
          if OPCODE = "000" then
            nextstate <= IDLE;
          elsif OPCODE = "001" then
            nextstate <= COLOR;
          elsif OPCODE = "010" then
            nextstate <= DRAW;
          elsif OPCODE = "011" then
            nextstate <= CLEAR;
          else
            nextstate <= IDLE;
          end if;
        else
          nextstate <= IDLE;
        end if;
      
      when COLOR =>
        nextstate <= IDLE;
      
      when DRAW =>
        nextstate <= DRAW2;

      when DRAW2 =>
        if memWait = '1' then
          nextstate <= DRAW2;
        else
          nextstate <= DRAW3;
        end if;
        
      when DRAW3 =>
        if unsigned(Z) < clear_loc(7 downto 0) then
          nextstate <= DRAW4;
        else
          nextstate <= IDLE;
        end if;
        
      when DRAW4 =>
        nextstate <= DRAW5;
        
      when DRAW5 =>
        if memWait = '1' then
          nextstate <= DRAW5;
        else
          nextstate <= IDLE;
        end if;
      when CLEAR =>
        nextstate <= CLEAR2;
        
      when CLEAR2 =>
        nextstate <= CLEAR3;
        
      when CLEAR3 =>
        if memWait = '1' then
          nextstate <= CLEAR3;
        else
          nextstate <= CLEAR4;
        end if;

      when CLEAR4 =>
        if ClrDone = '1' then
          nextstate <= IDLE;
        else
          nextstate <= CLEAR2;
        end if;
      
      when others =>
        nextstate <= IDLE;
    end case;
  end process StateLogic;



  -- purpose: Do output stuff
  -- type   : combinational
  DoStuff: process (state, nextstate, CLK, nRST, clear_loc, cur_color, DATA, X, Y, Z, sdatain, clrdone)
  begin  -- process DoStuff
    case state is
      when INIT =>
        BUSY <= '0';
        SData <= X"0000";
        Address <= X"0000";
        next_color <= X"00";
        next_clear_loc <= X"0000";
        next_ClrDone <= '0';
        Load <= '0';
        Store <= '0';
        
      when IDLE =>
        next_ClrDone <= '0';      
        BUSY <= '0';
        SData <= X"0000";
        Store <= '0';
        Load <= '0';
        Address <= X"0000";
        next_clear_loc <= clear_loc;
        next_color <= cur_color;
        
      when COLOR =>
        next_ClrDone <= '0';
        Address <= X"0000";
        BUSY <= '1';
        next_color <= DATA(7 downto 0);
        SData <= X"0000";
        Load <= '0';
        Store <= '0';
        next_clear_loc <= clear_loc;
        
      when CLEAR => 
        next_ClrDone <= '0';
        Address <= X"0000";
        BUSY <= '1';
        Store <= '0';
        SData <= X"0000";
        Load <= '0';
        next_clear_loc <= X"0000";
        next_color <= cur_color;
        
      when CLEAR2 =>
        Address <= std_logic_vector(clear_loc);
        SData <= X"00FF";
        Store <= '1';
        if clear_loc = X"FFFF" then
          next_ClrDone <= '1';
          next_clear_loc <= clear_loc;
        else
          next_clear_loc <= clear_loc + 1;
          next_ClrDone <= '0';
        end if;
        BUSY <= '1';
        Load <= '0';
        next_color <= cur_color;
        
      when CLEAR3 =>
        Address <= std_logic_vector(clear_loc);
        Store <= '0';
        SData <= X"00FF";
        next_clear_loc <= clear_loc;
        BUSY <= '1';
        Load <= '0';
        next_ClrDone <= clrdone;
        next_color <= cur_color;
        
      when CLEAR4 =>
        Address <= std_logic_vector(clear_loc);
        Store <= '0';
        SData <= X"00FF";
        next_clear_loc <= clear_loc;
        BUSY <= '1';
        Load <= '0';
        next_ClrDone <= clrdone;
        next_color <= cur_color;
        
      when DRAW =>
        Address <= Y & X;
        SData <= cur_color & Z;
        BUSY <= '1';
        next_clrDone <= '0';
        Store <= '0';
        Load <= '1';
        next_clear_loc <= clear_loc;
        next_color <= cur_color;
        
      when DRAW2 =>
        Address <= Y & X;
        SData <= cur_color & Z;
        BUSY <= '1';
        next_clrDone <= '0';
        Store <= '0';
        Load <= '1';
        next_clear_loc <= unsigned(SDataIn);
        next_color <= cur_color;
        
      when DRAW3 =>
        Address <= Y & X;
        SData <= cur_color & Z;
        BUSY <= '1';
        next_clrDone <= '0';
        Store <= '0';
        Load <= '0';
        next_clear_loc <= clear_loc;
        next_color <= cur_color;
        
      when DRAW4 =>
        BUSY <= '1';
        next_ClrDone <= '0';
        Address <= Y & X;
        SData <= cur_color & Z;
        Store <= '1';
        Load <= '0';
        next_clear_loc <= clear_loc;
        next_color <= cur_color;

      when DRAW5 =>
        Address <= Y & X;      
        Busy <= '1';
        next_ClrDone <= '0';
        SData <= cur_color & Z;
        Store <= '0';
        Load <= '0';
        next_clear_loc <= clear_loc;
        next_color <= cur_color;
        
      when others =>
        Address <= Y & X;
        Busy <= '1';
        next_ClrDone <= '0';
        SData <= X"0000";
        Store <= '0';  
        Load <= '0';
        next_clear_loc <= clear_loc;
        next_color <= cur_color;
        next_ClrDone <= '0';
    end case;
  end process DoStuff;
  
  X <= DATA(23 downto 16);
  Y <= DATA(15 downto 8);
  Z <= DATA(7 downto 0);
end sbuffer;
