-- a simple CPU for driving a vdu component
-- copyright: Frank Buss, 2006
--
-- available opcodes:
--
-- NOP
--   description: no operation
--   registers: -
--   flags: -
--
-- CLEAR
--   description: clear screen
--   registers: -
--   flags: -
--
-- WRITE null-terminated string
--   description: write null-terminated string
--   registers: -
--   flags: -
--
-- WRITEA
--   description: write accu
--   registers: -
--   flags: -
--
-- LDA number
--   description: load number in accu
--   registers: accu <= number
--   flags: -
--
-- LDS signal
--   description: load signal in accu
--   registers: accu <= signal
--   flags: -
--
-- STS signal
--   description: store accu in signal
--   registers: signal <= accu
--   flags: -
--
-- ROR count
--   description: rotate accu right by count
--   registers: accu <= accu ror count
--   flags: -
--
-- ROL count
--   description: rotate accu left by count
--   registers: accu <= accu rol count
--   flags: -
--
-- TEST bit
--   description: test bit in accu
--   registers: -
--   flags: z <= 1, if bit is set, 0 otherwise
--
-- LDX number
--   description: load number in x register
--   registers: x <= number
--   flags: -
--
-- LDY number
--   description: load number in y register
--   registers: y <= number
--   flags: -
--
-- INCX
--   description: increment x by 1
--   registers: x <= x + 1
--   flags: -
--
-- INCY
--   description: increment y by 1
--   registers: y <= y + 1
--   flags: -
--
-- INCA
--   description: increment accu by 1
--   registers: accu <= accu + 1
--   flags: -
--
-- INCS
--   description: increment signal by 1
--   registers: signal <= signal + 1
--   flags: -
--
-- CMPX number
--   description: compare x with number
--   registers: -
--   flags: z <= 1, if equal, 0 otherwise
--
-- CMPY number
--   description: compare y with number
--   registers: -
--   flags: z <= 1, if equal, 0 otherwise
--
-- CMPA number
--   description: compare accu with number
--   registers: -
--   flags: z <= 1, if equal, 0 otherwise
--
-- BEQ address
--   description: jump to address, if z flag is set
--   registers: pc <= address, if z=1
--   flags: -
--
-- BNE address
--   description: jump to address, if z flag is not set
--   registers: pc <= address, if z=0
--   flags: -
--
-- JUMP address
--   description: jump to address
--   registers: pc <= address, if z=0
--   flags: -
--
-- WAIT address
--   description: wait for 'number' tenths seconds
--   registers: -
--   flags: -
--
-- TXY
--   description: swap x and y
--   registers: x <= y, y <= x
--   flags: -
--
-- TAX
--   description: swap accu and x
--   registers: accu <= x, x <= accu
--   flags: -
--
-- TAY
--   description: swap accu and y
--   registers: accu <= y, y <= accu
--   flags: -

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity displaytest is
port(
  -- control register interface
  vdu_clk_in : in  std_logic;    -- 50MHz    System clock

  -- vga port connections
  vga_red_o : out std_logic;
  vga_green_o : out std_logic;
  vga_blue_o : out std_logic;
  vga_hsync_o : out std_logic;
  vga_vsync_o : out std_logic;

  -- LEDs
  led : out std_logic_vector(7 downto 0);

  -- switches
  switch : in std_logic_vector(7 downto 0);

  -- buttons
  button : in std_logic_vector(3 downto 0);

  -- 7-segement display 
  digit : out std_logic_vector(3 downto 0);
  segment : out std_logic_vector(7 downto 0)
);
end displaytest;

architecture arch of displaytest is

  -- mapped vdu signals
  
  signal clk : std_logic;    -- 12.5 MHz CPU Clock
  signal vdu_rst : std_logic;
  signal vdu_cs : std_logic;
  signal vdu_rw : std_logic;
  signal vdu_addr : std_logic_vector(2 downto 0);
  signal vdu_data_in : std_logic_vector(7 downto 0);
  signal vdu_data_out : std_logic_vector(7 downto 0);

  
  -- local signals
  
  -- some counters for testing
  signal counter_1000hz : std_logic_vector(31 downto 0) := (others => '0');
  signal clk_1000hz : std_logic;
  
  -- 7 segment position
  signal digit_position : std_logic_vector(3 downto 0) := (others => '0');
  
  -- vdu control signals
  signal vdu_delay_counter : std_logic_vector(31 downto 0) := (others => '0');
  signal vdu_delay_clk : std_logic := '0';
  signal cmd_count : std_logic_vector(7 downto 0) := (others => '0');
  signal bit_pos : std_logic_vector(7 downto 0) := (others => '0');
  signal line_bit_pos : std_logic_vector(7 downto 0) := (others => '0');
  signal bit_pos4 : std_logic_vector(3 downto 0) := (others => '0');
  signal shift_register : std_logic_vector(255 downto 0) := (others => '0');
  signal char : integer range 0 to 255 := 0;
  
  -- color: bit 7: unused, 6-4: background, 3: blink, 2-0 foreground
  signal color : std_logic_vector(7 downto 0) := (others => '0');

  constant NOP_CODE : integer := 0;
  constant CLEAR_CODE : integer := 1;
  constant WRITE_CODE : integer := 2;
  constant WRITEA_CODE : integer := 3;
  constant LDA_CODE : integer := 4;
  constant LDS_CODE : integer := 5;
  constant STS_CODE : integer := 6;
  constant ROR_CODE : integer := 7;
  constant ROL_CODE : integer := 8;
  constant TEST_CODE : integer := 9;
  constant LDX_CODE : integer := 10;
  constant LDY_CODE : integer := 11;
  constant INCX_CODE : integer := 12;
  constant INCY_CODE : integer := 13;
  constant INCA_CODE : integer := 14;
  constant INCS_CODE : integer := 15;
  constant CMPX_CODE : integer := 16;
  constant CMPY_CODE : integer := 17;
  constant CMPA_CODE : integer := 18;
  constant BEQ_CODE : integer := 19;
  constant BNE_CODE : integer := 20;
  constant JUMP_CODE : integer := 21;
  constant WAIT_CODE : integer := 22;
  constant TXY_CODE : integer := 23;
  constant TAX_CODE : integer := 24;
  constant TAY_CODE : integer := 25;
  
  constant SECONDS_SIGNAL : integer := 255;
  constant TIMER_SIGNAL : integer := 254;
  constant LED_SIGNAL : integer := 253;
  constant CURSOR_X_SIGNAL : integer := 252;
  constant CURSOR_Y_SIGNAL : integer := 251;
  constant COLOR_SIGNAL : integer := 250;

  constant BLACK_COLOR : integer := 0;
  constant CYAN_COLOR : integer := 6;
  constant CYAN_INVERSE_COLOR : integer := 96;
  
  type cpu_state_type is (load_data, execute_command, write_data);
  type execute_state_type is (
    decode_command, 
    clear_screen, clear_screen_step,
    write_string, write_string_step,
    write_accu, write_accu_step,
    lda_command,
    lds_command,
    sts_command,
    ror_command,
    rol_command,
    test_command,
    ldx_command,
    ldy_command, 
    incs_command,
    cmpx_command,
    cmpy_command,
    cmpa_command,
    beq_command,
    bne_command,
    jump_command,
    wait_command, wait_step
);

  -- cpu
  signal counter_1hz : std_logic_vector(31 downto 0) := (others => '0');
  signal seconds : std_logic_vector(31 downto 0) := (others => '0');
  signal free_counter : std_logic_vector(31 downto 0) := (others => '0');
  signal wait_counter : std_logic_vector(31 downto 0) := (others => '0');
  signal wait01_counter : integer range 0 to 255 := 0;
  signal z_flag : std_logic := '0';
  signal accu, accu_shift : std_logic_vector(31 downto 0) := (others => '0');
  signal x_register, y_register : std_logic_vector(31 downto 0) := (others => '0');
  signal led_cache : std_logic_vector(7 downto 0);
  signal mem0, mem1, mem2, mem3, mem4 : std_logic_vector(31 downto 0) := (others => '0');
  signal cpu_state : cpu_state_type := load_data;
  signal cpu_return_state : cpu_state_type := load_data;
  signal execute_state : execute_state_type := decode_command;
  signal write_state : integer range 0 to 15 := 0;
  signal pc : integer range 0 to 255 := 0;
  signal data : integer range 0 to 255 := 0;
  signal x_pos : integer range 0 to 79 := 0;
  signal y_pos : integer range 0 to 24 := 0;

  component vdu
    port(
      -- control register interface
      vdu_clk_in : in  std_logic;    -- 50MHz    System clock
      cpu_clk_out : out std_logic;    -- 12.5 MHz CPU Clock
      vdu_rst : in  std_logic;
      vdu_cs : in  std_logic;
      vdu_rw : in  std_logic;
      vdu_addr : in  std_logic_vector(2 downto 0);
      vdu_data_in : in  std_logic_vector(7 downto 0);
      vdu_data_out : out std_logic_vector(7 downto 0);

      -- vga port connections
      vga_red_o : out std_logic;
      vga_green_o : out std_logic;
      vga_blue_o : out std_logic;
      vga_hsync_o : out std_logic;
      vga_vsync_o : out std_logic
    );
  end component;


begin

  vdu_instance: vdu port map(
    vdu_clk_in => vdu_clk_in,
    cpu_clk_out => clk,
    vdu_rst => vdu_rst,
    vdu_cs => vdu_cs,
    vdu_rw => vdu_rw,
    vdu_addr => vdu_addr,
    vdu_data_in => vdu_data_in,
    vdu_data_out => vdu_data_out,
    vga_red_o => vga_red_o,
    vga_green_o => vga_green_o,
    vga_blue_o => vga_blue_o,
    vga_hsync_o => vga_hsync_o,
    vga_vsync_o => vga_vsync_o
  );

  vdu_delay_counter_update: process( clk, vdu_delay_counter )
  begin
    if clk'event and clk = '1' then
      if vdu_delay_counter = 10 then
        vdu_delay_counter <= x"00000000";
        vdu_delay_clk <= not vdu_delay_clk;
      else
        vdu_delay_counter <= vdu_delay_counter + 1;
      end if;
    end if;
  end process;

  cpu: process( clk )
  begin
    if vdu_delay_clk'event and vdu_delay_clk = '0' then
      -- update counters
      if counter_1hz = 625000 then
        counter_1hz <= x"00000000";
        seconds <= seconds + 1;
      else
        counter_1hz <= counter_1hz + 1;
      end if;
      free_counter <= free_counter + 1;
    
      -- CPU implementation
      case cpu_state is
        when load_data =>
          case pc is
            when   0 => data <= LDA_CODE;
            when   1 => data <= BLACK_COLOR;
            when   2 => data <= STS_CODE;
            when   3 => data <= COLOR_SIGNAL;
            when   4 => data <= CLEAR_CODE;
            -- draw "a simple CPU"
            when   5 => data <= LDA_CODE;
            when   6 => data <= CYAN_COLOR;
            when   7 => data <= STS_CODE;
            when   8 => data <= COLOR_SIGNAL;
            when   9 => data <= LDA_CODE;
            when  10 => data <= 0;
            when  11 => data <= STS_CODE;
            when  12 => data <= CURSOR_X_SIGNAL;
            when  13 => data <= LDA_CODE;
            when  14 => data <= 0;
            when  15 => data <= STS_CODE;
            when  16 => data <= CURSOR_Y_SIGNAL;
            when  17 => data <= WRITE_CODE;
            when  18 => data <= 97;
            when  19 => data <= 32;
            when  20 => data <= 115;
            when  21 => data <= 105;
            when  22 => data <= 109;
            when  23 => data <= 112;
            when  24 => data <= 108;
            when  25 => data <= 101;
            when  26 => data <= 32;
            when  27 => data <= 67;
            when  28 => data <= 80;
            when  29 => data <= 85;
            when  30 => data <= 0;
            -- draw "character set:" at the next line
            when  31 => data <= LDA_CODE;
            when  32 => data <= 0;
            when  33 => data <= STS_CODE;
            when  34 => data <= CURSOR_X_SIGNAL;
            when  35 => data <= INCS_CODE;
            when  36 => data <= CURSOR_Y_SIGNAL;
            when  37 => data <= WRITE_CODE;
            when  38 => data <= 99;
            when  39 => data <= 104;
            when  40 => data <= 97;
            when  41 => data <= 114;
            when  42 => data <= 115;
            when  43 => data <= 101;
            when  44 => data <= 116;
            when  45 => data <= 58;
            when  46 => data <= 0;
            -- draw the character set
            when  47 => data <= LDA_CODE;
            when  48 => data <= 0;
            when  49 => data <= LDY_CODE;
            when  50 => data <= 0;
            when  51 => data <= INCS_CODE;
            when  52 => data <= CURSOR_Y_SIGNAL;
            when  53 => data <= TAX_CODE;
            when  54 => data <= LDA_CODE;
            when  55 => data <= 0;
            when  56 => data <= STS_CODE;
            when  57 => data <= CURSOR_X_SIGNAL;
            when  58 => data <= TAX_CODE;
            when  59 => data <= LDX_CODE;
            when  60 => data <= 0;
            when  61 => data <= WRITEA_CODE;
            when  62 => data <= INCA_CODE;
            when  63 => data <= INCX_CODE;
            when  64 => data <= CMPX_CODE;
            when  65 => data <= 64;
            when  66 => data <= BNE_CODE;
            when  67 => data <= 61;
            when  68 => data <= WAIT_CODE;
            when  69 => data <= 5;
            when  70 => data <= INCY_CODE;
            when  71 => data <= CMPY_CODE;
            when  72 => data <= 4;
            when  73 => data <= BNE_CODE;
            when  74 => data <= 51;
            -- draw timer
            when  75 => data <= LDA_CODE;
            when  76 => data <= 0;
            when  77 => data <= STS_CODE;
            when  78 => data <= CURSOR_X_SIGNAL;
            when  79 => data <= LDA_CODE;
            when  80 => data <= 10;
            when  81 => data <= STS_CODE;
            when  82 => data <= CURSOR_Y_SIGNAL;
            when  83 => data <= LDX_CODE;
            when  84 => data <= 0;
            when  85 => data <= LDY_CODE;
            when  86 => data <= 0;
            when  87 => data <= LDS_CODE;
            when  88 => data <= SECONDS_SIGNAL;
            when  89 => data <= TEST_CODE;
            when  90 => data <= 31;
            when  91 => data <= TAY_CODE;
            when  92 => data <= LDA_CODE;
            when  93 => data <= 48;
            when  94 => data <= BNE_CODE;
            when  95 => data <= 98;
            when  96 => data <= LDA_CODE;
            when  97 => data <= 49;
            when  98 => data <= WRITEA_CODE;
            when  99 => data <= TAY_CODE;
            when 100 => data <= ROL_CODE;
            when 101 => data <= 1;
            when 102 => data <= INCX_CODE;
            when 103 => data <= CMPX_CODE;
            when 104 => data <= 32;
            when 105 => data <= BNE_CODE;
            when 106 => data <= 89;
            -- draw "tick/tack", with 'tick' or 'tack' inverted every second
            when 107 => data <= LDA_CODE;
            when 108 => data <= 0;
            when 109 => data <= STS_CODE;
            when 110 => data <= CURSOR_X_SIGNAL;
            when 111 => data <= LDA_CODE;
            when 112 => data <= 12;
            when 113 => data <= STS_CODE;
            when 114 => data <= CURSOR_Y_SIGNAL;
            when 115 => data <= LDS_CODE;
            when 116 => data <= SECONDS_SIGNAL;
            when 117 => data <= TEST_CODE;
            when 118 => data <= 0;
            when 119 => data <= LDX_CODE;
            when 120 => data <= CYAN_COLOR;
            when 121 => data <= LDY_CODE;
            when 122 => data <= CYAN_INVERSE_COLOR;
            when 123 => data <= BNE_CODE;
            when 124 => data <= 126;
            when 125 => data <= TXY_CODE;
            when 126 => data <= TAX_CODE;
            when 127 => data <= STS_CODE;
            when 128 => data <= COLOR_SIGNAL;
            when 129 => data <= TAX_CODE;
            when 130 => data <= WRITE_CODE;  -- tick
            when 131 => data <= 116;
            when 132 => data <= 105;
            when 133 => data <= 99;
            when 134 => data <= 107;
            when 135 => data <= 0;
            when 136 => data <= LDA_CODE;
            when 137 => data <= CYAN_COLOR;
            when 138 => data <= STS_CODE;
            when 139 => data <= COLOR_SIGNAL;
            when 140 => data <= LDA_CODE;
            when 141 => data <= 47;  -- '/'
            when 142 => data <= WRITEA_CODE;
            when 143 => data <= TXY_CODE;
            when 144 => data <= TAX_CODE;
            when 145 => data <= STS_CODE;
            when 146 => data <= COLOR_SIGNAL;
            when 147 => data <= WRITE_CODE;  -- tack
            when 148 => data <= 116;
            when 149 => data <= 97;
            when 150 => data <= 99;
            when 151 => data <= 107;
            when 152 => data <= 0;
            when 153 => data <= LDA_CODE;
            when 154 => data <= CYAN_COLOR;
            when 155 => data <= STS_CODE;
            when 156 => data <= COLOR_SIGNAL;
            when 157 => data <= JUMP_CODE;
            when 158 => data <= 77;
            when others =>
              pc <= 0;
              data <= 0;
          end case;
          cpu_state <= execute_command;
          pc <= pc + 1;
        when execute_command =>
          case execute_state is
            when decode_command =>
              case data is
                when NOP_CODE =>
                  execute_state <= decode_command;
                  cpu_state <= load_data;
                when CLEAR_CODE =>
                  x_pos <= 0;
                  y_pos <= 0;
                  char <= 32;
                  execute_state <= clear_screen;
                when WRITE_CODE =>
                  execute_state <= write_string;
                  cpu_state <= load_data;
                when WRITEA_CODE =>
                  execute_state <= write_accu;
                when LDA_CODE =>
                  execute_state <= lda_command;
                  cpu_state <= load_data;
                when LDS_CODE =>
                  execute_state <= lds_command;
                  cpu_state <= load_data;
                when STS_CODE =>
                  execute_state <= sts_command;
                  cpu_state <= load_data;
                when ROR_CODE =>
                  accu_shift <= accu;
                  execute_state <= ror_command;
                  cpu_state <= load_data;
                when ROL_CODE =>
                  accu_shift <= accu;
                  execute_state <= rol_command;
                  cpu_state <= load_data;
                when TEST_CODE =>
                  accu_shift <= accu;
                  execute_state <= test_command;
                  cpu_state <= load_data;
                when LDX_CODE =>
                  execute_state <= ldx_command;
                  cpu_state <= load_data;
                when LDY_CODE =>
                  execute_state <= ldy_command;
                  cpu_state <= load_data;
                when INCX_CODE =>
                  x_register <= x_register + 1;
                  execute_state <= decode_command;
                  cpu_state <= load_data;
                when INCY_CODE =>
                  y_register <= y_register + 1;
                  execute_state <= decode_command;
                  cpu_state <= load_data;
                when INCA_CODE =>
                  accu <= accu + 1;
                  execute_state <= decode_command;
                  cpu_state <= load_data;
                when INCS_CODE =>
                  execute_state <= incs_command;
                  cpu_state <= load_data;
                when CMPX_CODE =>
                  execute_state <= cmpx_command;
                  cpu_state <= load_data;
                when CMPY_CODE =>
                  execute_state <= cmpy_command;
                  cpu_state <= load_data;
                when CMPA_CODE =>
                  execute_state <= cmpa_command;
                  cpu_state <= load_data;
                when BEQ_CODE =>
                  execute_state <= beq_command;
                  cpu_state <= load_data;
                when BNE_CODE =>
                  execute_state <= bne_command;
                  cpu_state <= load_data;
                when JUMP_CODE =>
                  execute_state <= jump_command;
                  cpu_state <= load_data;
                when WAIT_CODE =>
                  execute_state <= wait_command;
                  cpu_state <= load_data;
                when TXY_CODE =>
                  x_register <= y_register;
                  y_register <= x_register;
                  execute_state <= decode_command;
                  cpu_state <= load_data;
                when TAX_CODE =>
                  accu <= x_register;
                  x_register <= accu;
                  execute_state <= decode_command;
                  cpu_state <= load_data;
                when TAY_CODE =>
                  accu <= y_register;
                  y_register <= accu;
                  execute_state <= decode_command;
                  cpu_state <= load_data;
                when others =>
                  cpu_state <= load_data;
                  pc <= 0;
              end case;

            -- CLEAR implementation
            when clear_screen =>
              execute_state <= clear_screen_step;
              cpu_return_state <= execute_command;
              cpu_state <= write_data;
            when clear_screen_step =>
              if x_pos = 79 then
                if y_pos = 24 then
                  cpu_state <= load_data;
                  execute_state <= decode_command;
                else
                  x_pos <= 0;
                  y_pos <= y_pos + 1;
                  execute_state <= clear_screen;
                end if;
              else
                x_pos <= x_pos + 1;
                execute_state <= clear_screen;
              end if;

            -- WRITE implementation
            when write_string =>
              if data = 0 then
                execute_state <= decode_command;
                cpu_state <= load_data;
              else
                char <= data;
                execute_state <= write_string_step;
                cpu_state <= write_data;
                cpu_return_state <= execute_command;
              end if;
            when write_string_step =>
              if x_pos = 79 then
                if y_pos = 24 then
                  y_pos <= 0;
                else
                  y_pos <= y_pos + 1;
                end if;
                x_pos <= 0;
              else
                x_pos <= x_pos + 1;
              end if;
              execute_state <= write_string;
              cpu_state <= load_data;

            -- WRITEA implementation
            when write_accu =>
              char <= conv_integer(accu(7 downto 0));
              execute_state <= write_accu_step;
              cpu_state <= write_data;
              cpu_return_state <= execute_command;
            when write_accu_step =>
              if x_pos = 79 then
                if y_pos = 24 then
                  y_pos <= 0;
                else
                  y_pos <= y_pos + 1;
                end if;
                x_pos <= 0;
              else
                x_pos <= x_pos + 1;
              end if;
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- LDA implementation
            when lda_command =>
              accu <= x"000000" & conv_std_logic_vector(data, 8);
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- LDS implementation
            when lds_command =>
              case data is
                when 0 => accu <= mem0;
                when 1 => accu <= mem1;
                when 2 => accu <= mem2;
                when 3 => accu <= mem3;
                when SECONDS_SIGNAL => accu <= seconds;
                when TIMER_SIGNAL => accu <= free_counter;
                when LED_SIGNAL => accu <= x"000000" & led_cache;
                when CURSOR_X_SIGNAL => accu <= x"000000" & conv_std_logic_vector(x_pos, 8);
                when CURSOR_Y_SIGNAL => accu <= x"000000" & conv_std_logic_vector(y_pos, 8);
                when COLOR_SIGNAL => accu <= x"000000" & color;
                when others => accu <= x"00000000";
              end case;
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- STS implementation
            when sts_command =>
              case data is
                when 0 => mem0 <= accu;
                when 1 => mem1 <= accu;
                when 2 => mem2 <= accu;
                when 3 => mem3 <= accu;
                when SECONDS_SIGNAL => seconds <= accu;
                when TIMER_SIGNAL => free_counter <= accu;
                when LED_SIGNAL =>
                  led_cache <= accu(7 downto 0);
                  led <= accu(7 downto 0);
                when CURSOR_X_SIGNAL => x_pos <= conv_integer(accu(7 downto 0));
                when CURSOR_Y_SIGNAL => y_pos <= conv_integer(accu(7 downto 0));
                when COLOR_SIGNAL => color <= accu(7 downto 0);
                when others => accu <= accu;
              end case;
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- ROR implementation
            when ror_command =>
              if data /= 0 then
                data <= data - 1;
                accu <= accu(0) & accu(31 downto 1);
              else
                execute_state <= decode_command;
                cpu_state <= load_data;
              end if;

            -- ROL implementation
            when rol_command =>
              if data /= 0 then
                data <= data - 1;
                accu <= accu(30 downto 0) & accu(31);
-- doesn't work: accu <= rotate_left(accu, 1);
              else
                execute_state <= decode_command;
                cpu_state <= load_data;
              end if;

            -- TEST implementation
            when test_command =>
              if data /= 0 then
                data <= data - 1;
                accu_shift <= "0" & accu_shift(31 downto 1);
              else
                if accu_shift(0) = '1' then
                  z_flag <= '1';
                else
                  z_flag <= '0';
                end if;
                execute_state <= decode_command;
                cpu_state <= load_data;
              end if;

            -- LDX implementation
            when ldx_command =>
              x_register <= x"000000" & conv_std_logic_vector(data, 8);
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- LDY implementation
            when ldy_command =>
              y_register <= x"000000" & conv_std_logic_vector(data, 8);
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- INCS implementation
            when incs_command =>
              case data is
                when 0 => mem0 <= mem0 + 1;
                when 1 => mem1 <= mem1 + 1;
                when 2 => mem2 <= mem2 + 1;
                when 3 => mem3 <= mem3 + 1;
                when SECONDS_SIGNAL => seconds <= seconds + 1;
                when TIMER_SIGNAL => free_counter <= free_counter + 1;
                when LED_SIGNAL =>
                  led <= led_cache + 1;
                  led_cache <= led_cache + 1;
                when CURSOR_X_SIGNAL => x_pos <= x_pos + 1;
                when CURSOR_Y_SIGNAL => y_pos <= y_pos + 1;
                when COLOR_SIGNAL => color <= color + 1;
                when others => accu <= accu;
              end case;
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- CMPX implementation
            when cmpx_command =>
              if x_register = data then
                z_flag <= '1';
              else
                z_flag <= '0';
              end if;
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- CMPY implementation
            when cmpy_command =>
              if y_register = data then
                z_flag <= '1';
              else
                z_flag <= '0';
              end if;
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- CMPA implementation
            when cmpa_command =>
              if accu = data then
                z_flag <= '1';
              else
                z_flag <= '0';
              end if;
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- BEQ implementation
            when beq_command =>
              if z_flag = '1' then
                pc <= data;
              end if;
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- BNE implementation
            when bne_command =>
              if z_flag = '0' then
                pc <= data;
              end if;
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- JUMP implementation
            when jump_command =>
              pc <= data;
              execute_state <= decode_command;
              cpu_state <= load_data;

            -- WAIT implementation
            when wait_command =>
              wait01_counter <= data;
              wait_counter <= x"00000000";
              execute_state <= wait_step;
            when wait_step =>
              if wait_counter = 62500 then
                wait_counter <= x"00000000";
                if wait01_counter = 0 then
                  execute_state <= decode_command;
                  cpu_state <= load_data;
                else
                  wait01_counter <= wait01_counter - 1;
                end if;
              else
                wait_counter <= wait_counter + 1;
              end if;
          end case;
        when write_data =>
          case write_state is
            -- write x_pos
            when 0 =>
              vdu_cs <= '0';
              write_state <= 1;
            when 1 =>
              vdu_addr <= b"010";
              vdu_data_in <= conv_std_logic_vector(x_pos, 8);
              write_state <= 2;
            when 2 =>
              vdu_cs <= '1';
              write_state <= 3;
    
            -- write y_pos
            when 3 =>
              vdu_cs <= '0';
              write_state <= 4;
            when 4 =>
              vdu_addr <= b"011";
              vdu_data_in <= conv_std_logic_vector(y_pos, 8);
              write_state <= 5;
            when 5 =>
              vdu_cs <= '1';
              write_state <= 6;
    
            -- write color
            when 6 =>
              vdu_cs <= '0';
              write_state <= 7;
            when 7 =>
              vdu_addr <= b"001";
              vdu_data_in <= color;
              write_state <= 8;
            when 8 =>
              vdu_cs <= '1';
              write_state <= 9;
    
            -- write char
            when 9 =>
              vdu_cs <= '0';
              write_state <= 10;
            when 10 =>
              vdu_addr <= b"000";
              vdu_data_in <= conv_std_logic_vector(char, 8);
              write_state <= 11;
            when 11 =>
              vdu_cs <= '1';
              cpu_state <= cpu_return_state;
              write_state <= 0;
            when others =>
              cpu_state <= cpu_return_state;
          end case;
      end case;
    end if;
  end process;
   
  counter_1000hz_update: process ( clk )
  begin
    if clk'event and clk = '1' then
      if counter_1000hz = 6250 then
        counter_1000hz <= x"00000000";
        clk_1000hz <= not clk_1000hz;
      else
        counter_1000hz <= counter_1000hz + 1;
      end if;
    end if;
  end process;

  led_7segment_update: process ( clk )
  begin
    if clk_1000hz'event and clk_1000hz = '1' then
      if digit_position = 4 then
        digit_position <= x"0";
      else
        case digit_position is
          when x"0" =>
            digit <= b"1110";
            segment <= x"8c";
          when x"1" =>
            digit <= b"1101";
            segment <= x"92";
          when x"2" =>
            digit <= b"1011";
            segment <= x"cf";
          when x"3" =>
            digit <= b"0111";
            segment <= x"c7";
          when others =>
            digit_position <= x"0";
        end case;
        digit_position <= digit_position + 1;
      end if;
    end if;
  end process;
     
end arch;
