----------------------------------------------------------------------------------
-- Company: 
-- Engineer: Jim Plusquellic
-- 
-- Create Date:    16:04:58 09/23/2009 
-- Design Name: 
-- Module Name:    InstructionDecoder
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.all;

-- opcode and subcode are 3 bit fields of the leftmost portion of the instruction register. 
-- d_bit is direction bit (the next field of the IR) and prog_mode is a special external
-- input that allows the microcontroller to be placed into program mode. Here, it forces
-- the opcode to be no-op which controls the PC incrementing operations to add to the
-- address sequentially so the instruction memory can be loaded up externally. The rest
-- of the signals are 1-bit outputs that control the datapath.
entity InstrDecoder is
   Port ( 
      opcode : in std_logic_vector(2 downto 0);
      subcode : in std_logic_vector(2 downto 0);
      d_bit, prog_mode : in std_logic;
      aluwf_ins, movf_ins, call_ins, movlw_ins, goto_ins, retn_ins, noop_ins, skwcf_ins : out std_logic;
      add_ins, sub_ins, and_ins, or_ins, xor_ins, inv_ins, shltf_ins, shrtf_ins : out std_logic;
      skwgf_ins, skwlf_ins, skwef_ins, skwnf_ins, movwf_ins, movfw_ins : out std_logic;
      read_ctrl, write_ctrl : out std_logic
   );
end InstrDecoder;

architecture beh of InstrDecoder is
   signal alu_instruction, skip_instruction, move_instruction, move_literal_instruction : std_logic;
   signal opc : std_logic_vector(2 downto 0);
   begin

-- Force the opcode to NOOP (110) when in program mode, else pass opcode unchanged 
-- this simplifies the PC increment operation. 
   with prog_mode select
      opc <= "110" when '1',
             opcode when others;

-- Opcode decoder 3-bits - 8 instructions
   alu_instruction <= '1' when opc = "000" else '0';
   move_instruction <= '1' when opc = "001" else '0';
   call_ins <= '1' when opc = "010" else '0';
   move_literal_instruction <= '1' when opc = "011" else '0';
   goto_ins <= '1' when opc = "100" else '0';
   retn_ins <= '1' when opc = "101" else '0';
   noop_ins <= '1' when opc = "110" else '0';
   skip_instruction <= '1' when opc = "111" else '0';

-- aluwf_ins instructions -- subcode is three bits
   add_ins <= '1' when alu_instruction = '1' and subcode = "000" else '0';
   sub_ins <= '1' when alu_instruction = '1' and subcode = "001" else '0';
   and_ins <= '1' when alu_instruction = '1' and subcode = "010" else '0';
   or_ins <= '1' when alu_instruction = '1' and subcode = "011" else '0';
   xor_ins <= '1' when alu_instruction = '1' and subcode = "100" else '0';
   inv_ins <= '1' when alu_instruction = '1' and subcode = "101" else '0';
   shltf_ins <= '1' when alu_instruction = '1' and subcode = "110" else '0';
   shrtf_ins <= '1' when alu_instruction = '1' and subcode = "111" else '0';

-- skwcf_ins instruction decoder 
   skwgf_ins <= '1' when skip_instruction = '1' and subcode(1 downto 0) = "00" else '0';
   skwlf_ins <= '1' when skip_instruction = '1' and subcode(1 downto 0) = "01" else '0';
   skwef_ins <= '1' when skip_instruction = '1' and subcode(1 downto 0) = "10" else '0';
   skwnf_ins <= '1' when skip_instruction = '1' and subcode(1 downto 0) = "11" else '0';

-- movf_ins: uses D bit to determine if move from W to F (0) or F to W (1). 
   movwf_ins <= '1' when move_instruction = '1' and d_bit = '0' else '0';
   movfw_ins <= '1' when move_instruction = '1' and d_bit = '1' else '0';

-- Memory Control Signlas
   read_ctrl <= skip_instruction or alu_instruction or move_instruction;
   write_ctrl <= alu_instruction or move_instruction or move_literal_instruction;

   aluwf_ins <= alu_instruction;
   skwcf_ins <= skip_instruction;
   movf_ins <= move_instruction;
   movlw_ins <= move_literal_instruction;

end beh;
