]> git.the-white-hart.net Git - vhdl/commitdiff
Add attempt at resource-optimized CPU
authorrs <>
Sun, 21 Sep 2025 03:49:04 +0000 (22:49 -0500)
committerrs <>
Sun, 21 Sep 2025 03:49:04 +0000 (22:49 -0500)
projects/cpu_0/cpu_opt.vhd [new file with mode: 0644]

diff --git a/projects/cpu_0/cpu_opt.vhd b/projects/cpu_0/cpu_opt.vhd
new file mode 100644 (file)
index 0000000..c437863
--- /dev/null
@@ -0,0 +1,873 @@
+--------------------------------------------------------------------------------
+--
+--    +---+   +---+   +---+   +---+   +---+
+--    | T |   | R |   |MDR|   | I |   |PC |
+--    +---+   +---+   +---+   +---+   +---+
+--    | N |   :   :
+--    +---+
+--    :   :
+--
+--------------------------------------------------------------------------------
+-- WISHBONE DATASHEET
+--
+-- Wishbone specification used: Rev B.3
+-- Interface type: master
+-- Port size: 8-bit
+-- Operand sizes: 8-bit, 32-bit
+-- Endianness: little
+-- Data transfer sequence: undefined
+-- Clock constraints: none
+-- Signals:
+-- * rst_i
+-- * clk_i
+-- * cyc_o
+-- * stb_o
+-- * we_o
+-- * ack_i
+-- * adr_o (32-bit)
+-- * dat_i (8-bit)
+-- * dat_o (8-bit)
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_misc.all;
+use ieee.numeric_std.all;
+
+library unisim;
+use unisim.vcomponents.all;
+
+
+entity cpu_opt is
+       port (
+               rst_i: in  std_logic;
+               clk_i: in  std_logic;
+
+               cyc_o: out std_logic;
+               stb_o: out std_logic;
+               we_o:  out std_logic;
+               ack_i: in  std_logic;
+               adr_o: out std_logic_vector(31 downto 0);
+               dat_o: out std_logic_vector(7 downto 0);
+               dat_i: in  std_logic_vector(7 downto 0);
+
+               int_i: in  std_logic;
+               vec_i: in  std_logic_vector(7 downto 0);
+
+               -- Debugging
+               halt_i: in  std_logic;
+               step_i: in  std_logic;
+               pc_o:   out std_logic_vector(31 downto 0);
+               ins_o:  out std_logic_vector(7 downto 0);
+               t_o:    out std_logic_vector(31 downto 0);
+               n_o:    out std_logic_vector(31 downto 0);
+               r_o:    out std_logic_vector(31 downto 0)
+       );
+end cpu_opt;
+
+
+architecture behavioral of cpu_opt is
+
+       type state_t is (
+               S_WAIT,  S_FETCH2,
+               S_RESET, S_FETCH, S_MEMORY,
+                        S_IMM32_0, S_IMM32_1, S_IMM32_2, S_IMM32_3,
+               S_LD8,   S_LD32_0,  S_LD32_1,  S_LD32_2,  S_LD32_3,
+               S_ST8,   S_ST32_0,  S_ST32_1,  S_ST32_2,  S_ST32_3,
+               S_EXEC,  S_INT
+       );
+
+       type mem_op_t  is (MEM_IMM, MEM_LD, MEM_ST, MEM_NONE);
+       type adr_sel_t is (ADR_PC, ADR_T, ADR_T0, ADR_T1, ADR_T2, ADR_T3);
+       type dat_sel_t is (DAT_N0, DAT_N1, DAT_N2, DAT_N3);
+       -- type alu_op_t  is (ALU_T, ALU_N, ALU_ADD, ALU_SUB, ALU_AND, ALU_OR, ALU_XOR, ALU_NOT, ALU_R, ALU_MDR, ALU_INTVEC);
+       type r_sel_t   is (R_T, R_PC, R_NC);
+       type pc_sel_t  is (PC_MDR, PC_R, PC_INC, PC_NC, PC_INTVEC);
+       type stackop_t is (ST_INC, ST_DEC, ST_NC);
+       type cond_t    is (C_NEG, C_ZERO, C_POS, C_NONE);
+
+       constant D_NC:  std_logic_vector(1 downto 0) := "00";
+       constant D_DEC: std_logic_vector(1 downto 0) := "01";
+       constant D_INC: std_logic_vector(1 downto 0) := "10";
+       constant D_PSH: std_logic_vector(1 downto 0) := "11";
+
+       constant ALU_M8:  std_logic_vector(3 downto 0) := "0000";
+       constant ALU_M32: std_logic_vector(3 downto 0) := "0001";
+       constant ALU_N8:  std_logic_vector(3 downto 0) := "0010";
+       constant ALU_N32: std_logic_vector(3 downto 0) := "0011";
+       constant ALU_Tx:  std_logic_vector(3 downto 0) := "0100";  -- \_ for setting/clearing iflag with bit 0
+       constant ALU_T:   std_logic_vector(3 downto 0) := "0101";  -- /
+       constant ALU_ADD: std_logic_vector(3 downto 0) := "0110";
+       constant ALU_SUB: std_logic_vector(3 downto 0) := "0111";
+       constant ALU_AND: std_logic_vector(3 downto 0) := "1000";
+       constant ALU_OR:  std_logic_vector(3 downto 0) := "1001";
+       constant ALU_XOR: std_logic_vector(3 downto 0) := "1010";
+       constant ALU_NOT: std_logic_vector(3 downto 0) := "1011";
+       constant ALU_VEC: std_logic_vector(3 downto 0) := "1100";
+       constant ALU_R:   std_logic_vector(3 downto 0) := "1101";
+
+       type instr_decode_t is record
+               -- Memory operation
+               mem_op:    mem_op_t;
+
+               -- Stack updates
+               --s_op:      stackop_t;
+               s_op:      std_logic_vector(1 downto 0);
+               r_op:      stackop_t;
+
+               -- Datapath multiplexion
+               --alu_op:    alu_op_t;   -- selects new T
+               --alu_op:    std_logic_vector(3 downto 0);
+               --t_to_n:    std_logic;  -- T->N (push)
+               r_sel:     r_sel_t;    -- selects new R
+               pc_sel:    pc_sel_t;   -- selects new PC
+
+               imask_set: std_logic;
+               --imask_clr: std_logic;
+               --cond:      cond_t;
+       end record;
+
+       -- Control FSM
+       signal state_reg:  state_t;
+       signal state_next: state_t;
+       signal adr_sel:    adr_sel_t;
+
+       -- Instruction register
+       signal ins_reg:    std_logic_vector(7 downto 0);
+       signal ins_ld:     std_logic;
+       signal ins_decode: instr_decode_t;
+       signal dat_sel:    dat_sel_t;
+
+       -- Program counter
+       signal pc_reg:     std_logic_vector(31 downto 0);
+       signal pc_next:    std_logic_vector(31 downto 0);
+       signal pc_sel:     pc_sel_t;
+
+       -- Memory data register
+       signal mdr_reg:    std_logic_vector(31 downto 0);
+       signal mdr_ld_0:   std_logic;
+       signal mdr_ld_1:   std_logic;
+       signal mdr_ld_2:   std_logic;
+       signal mdr_ld_3:   std_logic;
+
+       -- Parameter stack
+       signal s_ptr_reg:  unsigned(7 downto 0);
+       signal s_ptr_next: unsigned(7 downto 0);
+       signal s_ptr_op:   std_logic_vector(1 downto 0);
+       signal s_ptr_ld:   std_logic;
+       signal s_write:    std_logic;
+       signal s_n_reg:    std_logic_vector(31 downto 0);
+       signal s_t_reg:    std_logic_vector(31 downto 0);
+       signal s_t_new:    std_logic_vector(31 downto 0);
+       signal s_t_ld:     std_logic;
+
+       -- Return stack
+       signal r_ptr_reg:  unsigned(7 downto 0);
+       signal r_ptr_next: unsigned(7 downto 0);
+       signal r_ptr_op:   stackop_t;
+       signal r_ptr_ld:   std_logic;
+       signal r_write:    std_logic;
+       signal r_tos_reg:  std_logic_vector(31 downto 0);
+       signal r_tos_new:  std_logic_vector(31 downto 0);
+
+       signal addra:      std_logic_vector(8 downto 0);
+       signal addrb:      std_logic_vector(8 downto 0);
+
+       -- Kludged-in to allow S_INT logic to override the values from ins_decode
+       signal r_sel:      r_sel_t;
+       signal alu_op:     std_logic_vector(3 downto 0); -- alu_op_t;
+       signal imask_reg:  std_logic;
+       signal imask_set:  std_logic;
+       signal imask_clr:  std_logic;
+
+       signal n:          std_logic;
+       signal z:          std_logic;
+       signal p:          std_logic;
+       signal cond_n:     std_logic;
+       signal cond_z:     std_logic;
+       signal cond_p:     std_logic;
+       signal cond:       std_logic;
+       signal ins_pc_sel: pc_sel_t;
+
+       signal t_to_n:     std_logic;
+       signal wordsize:   std_logic;
+       signal ins_decode_alu_op: std_logic_vector(3 downto 0);
+
+begin
+
+       -- Debugging signals
+       pc_o  <= pc_reg;
+       ins_o <= ins_reg;
+       t_o   <= s_t_reg;
+       n_o   <= s_n_reg;
+       r_o   <= r_tos_reg;
+
+       -- Control state machine
+       process (rst_i, clk_i, state_next)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               state_reg <= S_RESET;
+                       else
+                               state_reg <= state_next;
+                       end if;
+               end if;
+       end process;
+
+       process (state_reg, ack_i, ins_decode, int_i, ins_pc_sel, imask_reg, step_i, halt_i, wordsize, t_to_n, ins_decode_alu_op)
+       begin
+               state_next <= state_reg;
+               cyc_o      <= '0';
+               stb_o      <= '0';
+               we_o       <= '0';
+               adr_sel    <= ADR_PC;
+               ins_ld     <= '0';
+               pc_sel     <= PC_NC;
+               mdr_ld_0   <= '0';
+               mdr_ld_1   <= '0';
+               mdr_ld_2   <= '0';
+               mdr_ld_3   <= '0';
+               dat_sel    <= DAT_N0;
+               s_t_ld     <= '0';
+               s_write    <= '0';
+               r_write    <= '0';
+               s_ptr_op   <= D_NC;
+               r_ptr_op   <= ST_NC;
+               s_ptr_ld   <= '0';
+               r_ptr_ld   <= '0';
+               r_sel      <= ins_decode.r_sel;
+               alu_op     <= ins_decode_alu_op;
+               imask_set  <= ins_decode.imask_set; --'0';
+
+               case state_reg is
+                       when S_FETCH =>
+                               if halt_i = '0' then
+                                       state_next <= S_FETCH2;
+                               elsif step_i = '0' then
+                                       state_next <= S_WAIT;
+                               end if;
+                       when S_WAIT =>
+                               if step_i = '1' then
+                                       state_next <= S_FETCH2;
+                               end if;
+                       when S_FETCH2 =>
+                               adr_sel <= ADR_PC;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               ins_ld  <= '1';
+
+                               if ack_i = '1' then
+                                       pc_sel     <= PC_INC;
+                                       state_next <= S_MEMORY;
+                               end if;
+
+                       when S_MEMORY =>
+                               -- Mask interrupts now if the instruction requests it
+                               -- The mask flag register will be set in the following clock cycle,
+                               -- so this prevents us from having to check ins_decode.imask_set
+                               -- in the S_EXEC state, which would otherwise be necessary to make
+                               -- sure we don't enter an interrupt after a "mask-int" instruction
+                               -- and then return with interrupts unmasked into a critical section
+                               --imask_clr <= ins_decode.imask_clr;
+
+                               if wordsize = '1' then
+                                       case ins_decode.mem_op is
+                                               when MEM_IMM => state_next <= S_IMM32_0;
+                                               when MEM_LD  => state_next <= S_LD32_0;
+                                               when MEM_ST  => state_next <= S_ST32_0;
+                                               when others  => state_next <= S_EXEC;
+                                       end case;
+                               else
+                                       case ins_decode.mem_op is
+                                               when MEM_IMM => state_next <= S_IMM32_0;
+                                               when MEM_LD  => state_next <= S_LD8;
+                                               when MEM_ST  => state_next <= S_ST8;
+                                               when others  => state_next <= S_EXEC;
+                                       end case;
+                               end if;
+
+                       when S_IMM32_0 =>
+                               adr_sel  <= ADR_PC;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_0 <= '1';
+
+                               if ack_i = '1' then
+                                       pc_sel     <= PC_INC;
+                                       if wordsize = '1' then
+                                               state_next <= S_IMM32_1;
+                                       else
+                                               state_next <= S_EXEC;
+                                       end if;
+                               end if;
+
+                       when S_IMM32_1 =>
+                               adr_sel  <= ADR_PC;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_1 <= '1';
+
+                               if ack_i = '1' then
+                                       pc_sel     <= PC_INC;
+                                       state_next <= S_IMM32_2;
+                               end if;
+
+                       when S_IMM32_2 =>
+                               adr_sel  <= ADR_PC;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_2 <= '1';
+
+                               if ack_i = '1' then
+                                       pc_sel     <= PC_INC;
+                                       state_next <= S_IMM32_3;
+                               end if;
+
+                       when S_IMM32_3 =>
+                               adr_sel  <= ADR_PC;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_3 <= '1';
+
+                               if ack_i = '1' then
+                                       pc_sel     <= PC_INC;
+                                       state_next <= S_EXEC;
+                               end if;
+
+                       when S_LD8 =>
+                               adr_sel  <= ADR_T;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_0 <= '1';
+
+                               if ack_i = '1' then
+                                       state_next <= S_EXEC;
+                               end if;
+
+                       when S_LD32_0 =>
+                               adr_sel  <= ADR_T0;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_0 <= '1';
+
+                               if ack_i = '1' then
+                                       state_next <= S_LD32_1;
+                               end if;
+
+                       when S_LD32_1 =>
+                               adr_sel  <= ADR_T1;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_1 <= '1';
+
+                               if ack_i = '1' then
+                                       state_next <= S_LD32_2;
+                               end if;
+
+                       when S_LD32_2 =>
+                               adr_sel  <= ADR_T2;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_2 <= '1';
+
+                               if ack_i = '1' then
+                                       state_next <= S_LD32_3;
+                               end if;
+
+                       when S_LD32_3 =>
+                               adr_sel  <= ADR_T3;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_3 <= '1';
+
+                               if ack_i = '1' then
+                                       state_next <= S_EXEC;
+                               end if;
+
+                       when S_ST8 =>
+                               adr_sel <= ADR_T;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               we_o    <= '1';
+                               dat_sel <= DAT_N0;
+
+                               if ack_i = '1' then
+                                       state_next <= S_EXEC;
+                               end if;
+
+                       when S_ST32_0 =>
+                               adr_sel <= ADR_T0;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               we_o    <= '1';
+                               dat_sel <= DAT_N0;
+
+                               if ack_i = '1' then
+                                       state_next <= S_ST32_1;
+                               end if;
+
+                       when S_ST32_1 =>
+                               adr_sel <= ADR_T1;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               we_o    <= '1';
+                               dat_sel <= DAT_N1;
+
+                               if ack_i = '1' then
+                                       state_next <= S_ST32_2;
+                               end if;
+
+                       when S_ST32_2 =>
+                               adr_sel <= ADR_T2;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               we_o    <= '1';
+                               dat_sel <= DAT_N2;
+
+                               if ack_i = '1' then
+                                       state_next <= S_ST32_3;
+                               end if;
+
+                       when S_ST32_3 =>
+                               adr_sel <= ADR_T3;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               we_o    <= '1';
+                               dat_sel <= DAT_N3;
+
+                               if ack_i = '1' then
+                                       state_next <= S_EXEC;
+                               end if;
+
+                       when S_EXEC =>
+                               -- At this point, MDR will contain any immediate or loaded value and any stores have been performed
+
+                               -- Load T with new value
+                               s_t_ld <= '1';
+
+                               -- Load N with new value
+                               --if ins_decode.t_to_n = '1' then
+                               if t_to_n = '1' then
+                                       s_write <= '1';
+                               end if;
+
+                               -- Load R with new value
+                               if ins_decode.r_sel /= R_NC then
+                                       r_write <= '1';
+                               end if;
+
+                               -- Load PC with new value
+                               -- ins_pc_sel includes conditions
+                               pc_sel <= ins_pc_sel;
+
+                               -- Use updated stack pointers
+                               s_ptr_op <= ins_decode.s_op;
+                               r_ptr_op <= ins_decode.r_op;
+                               s_ptr_ld <= '1';
+                               r_ptr_ld <= '1';
+
+                               -- Clear the interrupt mask flag if necessary
+                               -- The flag will be cleared in the next clock cycle, so the next
+                               -- instruction is guaranteed to be executed if an interrupt is
+                               -- pending, useful for clearing the interrupt flag and then returning
+                               --imask_set <= ins_decode.imask_set;
+
+                               -- Check for interrupts during the execute step so we don't have any in-progress memory operations to cancel
+                               if int_i = '1' and imask_reg = '1' then
+                                       state_next <= S_INT;
+                               else
+                                       state_next <= S_FETCH;
+                               end if;
+
+                       when S_INT =>
+                               -- Load T with the vector value, load N with old T, push down the stack
+                               alu_op     <= ALU_VEC;
+                               s_t_ld     <= '1';
+                               s_write    <= '1';
+                               s_ptr_op   <= D_INC;
+                               s_ptr_ld   <= '1';
+
+                               -- Push PC onto the return stack
+                               r_sel      <= R_PC;
+                               r_write    <= '1';
+                               r_ptr_op   <= ST_INC;
+                               r_ptr_ld   <= '1';
+
+                               -- Load PC with the vector address
+                               pc_sel     <= PC_INTVEC;
+
+                               -- Mask further interruptions
+                               imask_clr  <= '1';
+
+                               state_next <= S_FETCH;
+
+                       when S_RESET =>
+                               state_next <= S_FETCH;
+
+                       when others =>
+                               state_next <= S_FETCH;
+               end case;
+       end process;
+
+
+       -- Interrupt mask flag
+       process (rst_i, clk_i, imask_set, imask_clr)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               imask_reg <= '0';
+                       elsif imask_set = '1' then
+                               imask_reg <= wordsize;
+                       end if;
+               end if;
+       end process;
+
+
+       -- Program counter
+       cond <= (cond_n and n) or (cond_z and z) or (cond_p and p);
+       process (cond, ins_decode)
+       begin
+               if ins_decode.pc_sel = PC_MDR and cond = '0' then
+                       ins_pc_sel <= PC_NC;
+               else
+                       ins_pc_sel <= ins_decode.pc_sel;
+               end if;
+
+               --if ins_decode.cond = C_NONE then
+               --      ins_pc_sel <= ins_decode.pc_sel;
+               --elsif ins_decode.cond = C_NEG then
+               --      if n = '1' then
+               --              ins_pc_sel <= ins_decode.pc_sel;
+               --      else
+               --              ins_pc_sel <= PC_NC;
+               --      end if;
+               --elsif ins_decode.cond = C_ZERO then
+               --      if z = '1' then
+               --              ins_pc_sel <= ins_decode.pc_sel;
+               --      else
+               --              ins_pc_sel <= PC_NC;
+               --      end if;
+               --elsif ins_decode.cond = C_POS then
+               --      if p = '1' then
+               --              ins_pc_sel <= ins_decode.pc_sel;
+               --      else
+               --              ins_pc_sel <= PC_NC;
+               --      end if;
+               --end if;
+       end process;
+
+       with pc_sel select pc_next <=
+               mdr_reg                                when PC_MDR,
+               r_tos_reg                              when PC_R,
+               std_logic_vector(unsigned(pc_reg) + 1) when PC_INC,
+               (others => '0')                        when PC_INTVEC,
+               pc_reg                                 when others;
+
+       process (rst_i, clk_i, pc_reg, pc_next)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               pc_reg <= (others => '0');
+                       else
+                               pc_reg <= pc_next;
+                       end if;
+               end if;
+       end process;
+
+
+       -- Instruction register and decode
+       process (clk_i, ins_ld, dat_i)
+       begin
+               if rising_edge(clk_i) then
+                       if ins_ld = '1' then
+                               ins_reg <= dat_i;
+                       end if;
+               end if;
+       end process;
+
+       with ins_reg(3 downto 0) select ins_decode <=
+               (  -- #8, #32
+                       mem_op => MEM_IMM,
+                       s_op   => D_INC,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0'
+               ) when "0001",
+               (  -- @8, @32
+                       mem_op => MEM_LD,
+                       s_op   => D_NC,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0'
+               ) when "0010",
+               (  -- !8, !32
+                       mem_op => MEM_ST,
+                       s_op   => D_DEC,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0'
+               ) when "0011",
+               (  -- r@, DUP
+                       mem_op => MEM_NONE,
+                       s_op   => D_INC,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0'
+               ) when "0100",
+               (  -- +, -, &, |, ^, DROP
+                       mem_op => MEM_NONE,
+                       s_op   => D_DEC,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0'
+               ) when "0101",
+               (  -- >r
+                       mem_op => MEM_NONE,
+                       s_op   => D_DEC,
+                       r_op   => ST_INC,
+                       r_sel  => R_T,
+                       pc_sel => PC_NC,
+                       imask_set => '0'
+               ) when "0110",
+               (  -- r>
+                       mem_op => MEM_NONE,
+                       s_op   => D_INC,
+                       r_op   => ST_DEC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0'
+               ) when "1000",
+               (  -- call
+                       mem_op => MEM_IMM,
+                       s_op   => D_NC,
+                       r_op   => ST_INC,
+                       r_sel  => R_PC,
+                       pc_sel => PC_MDR,
+                       imask_set => '0'
+               ) when "0111",
+               (  -- ;
+                       mem_op => MEM_NONE,
+                       s_op   => D_NC,
+                       r_op   => ST_DEC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_R,
+                       imask_set => '0'
+               ) when "1101",
+               (  -- jmp
+                       mem_op => MEM_IMM,
+                       s_op   => D_NC,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_MDR,
+                       imask_set => '0'
+               ) when "1111",
+               (  -- jz
+                       mem_op => MEM_IMM,
+                       s_op   => D_DEC,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_MDR,
+                       imask_set => '0'
+               ) when "1010",
+               (  -- jn
+                       mem_op => MEM_IMM,
+                       s_op   => D_DEC,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_MDR,
+                       imask_set => '0'
+               ) when "1001",
+               (  -- jp
+                       mem_op => MEM_IMM,
+                       s_op   => D_DEC,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_MDR,
+                       imask_set => '0'
+               ) when "1100",
+               (  -- SWAP
+                       mem_op => MEM_NONE,
+                       s_op   => D_PSH,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0'
+               ) when "1110",
+               (  -- imask_set, imask_clr
+                       mem_op => MEM_NONE,
+                       s_op   => D_NC,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '1'
+               ) when "1011",
+               (  -- NOP, ~
+                       mem_op => MEM_NONE,
+                       s_op   => D_NC,
+                       r_op   => ST_NC,
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0'
+               ) when others;
+
+       t_to_n <= ins_decode.s_op(1);
+       ins_decode_alu_op <= ins_reg(7 downto 4);
+       wordsize <= ins_decode_alu_op(0);
+       cond_n <= ins_reg(0);
+       cond_z <= ins_reg(1);
+       cond_p <= ins_reg(2);
+
+
+       -- Address multiplexer
+       with adr_sel select adr_o <=
+               pc_reg                      when ADR_PC,
+               s_t_reg                     when ADR_T,
+               s_t_reg(31 downto 2) & "00" when ADR_T0,
+               s_t_reg(31 downto 2) & "01" when ADR_T1,
+               s_t_reg(31 downto 2) & "10" when ADR_T2,
+               s_t_reg(31 downto 2) & "11" when ADR_T3,
+               (others => '0')             when others;
+
+
+       -- Memory data register
+       process (clk_i, mdr_ld_0, mdr_ld_1, mdr_ld_2, mdr_ld_3)
+       begin
+               if rising_edge(clk_i) then
+                       if mdr_ld_0 = '1' then
+                               -- Zero-extend for 8-bit loads
+                               -- Zeroes will be overwritten for 32-bit loads
+                               mdr_reg( 7 downto  0) <= dat_i;
+                               mdr_reg(31 downto  8) <= (others => '0');
+                       elsif mdr_ld_1 = '1' then
+                               mdr_reg(15 downto  8) <= dat_i;
+                       elsif mdr_ld_2 = '1' then
+                               mdr_reg(23 downto 16) <= dat_i;
+                       elsif mdr_ld_3 = '1' then
+                               mdr_reg(31 downto 24) <= dat_i;
+                       end if;
+               end if;
+       end process;
+
+       with dat_sel select dat_o <=
+               s_n_reg(31 downto 24) when DAT_N3,
+               s_n_reg(23 downto 16) when DAT_N2,
+               s_n_reg(15 downto  8) when DAT_N1,
+               s_n_reg( 7 downto  0) when others;
+
+
+       -- ALU
+       with alu_op select s_t_new <=
+               s_t_reg             when ALU_T,
+               s_t_reg             when ALU_Tx,
+                           s_n_reg when ALU_N8,
+                                       s_n_reg when ALU_N32,
+               std_logic_vector(signed(s_t_reg) + signed(s_n_reg)) when ALU_ADD,
+               std_logic_vector(signed(s_t_reg) - signed(s_n_reg)) when ALU_SUB,
+               s_t_reg and s_n_reg when ALU_AND,
+               s_t_reg or  s_n_reg when ALU_OR,
+               s_t_reg xor s_n_reg when ALU_XOR,
+               not s_t_reg         when ALU_NOT,
+               r_tos_reg           when ALU_R,
+               mdr_reg             when ALU_M8,
+               mdr_reg             when ALU_M32,
+               x"000000" & vec_i   when ALU_VEC,
+               (others => '0')     when others;
+
+
+       -- Next R
+       with r_sel select r_tos_new <=
+               s_t_reg   when R_T,
+               pc_reg    when R_PC,
+               r_tos_reg when others;
+
+
+       -- Stack memory
+       p_s_tos: process (clk_i, s_t_ld, s_t_new)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               -- Reset to zero as the "reset vector"
+                               s_t_reg <= (others => '0');
+                       elsif s_t_ld = '1' then
+                               s_t_reg <= s_t_new;
+                       end if;
+               end if;
+       end process;
+
+       n <= s_t_reg(s_t_reg'high);
+       z <= not or_reduce(s_t_reg);
+       p <= (not n) and (not z);
+
+       p_s_ptr: process (rst_i, clk_i, s_ptr_ld, s_ptr_next)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               s_ptr_reg <= (others => '0');
+                       else
+                               if s_ptr_ld = '1' then
+                                       s_ptr_reg <= s_ptr_next;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       with s_ptr_op select s_ptr_next <=
+               s_ptr_reg + 1 when D_INC,
+               s_ptr_reg - 1 when D_DEC,
+               s_ptr_reg     when others;
+
+       p_r_ptr: process (rst_i, clk_i, r_ptr_ld, r_ptr_next)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               r_ptr_reg <= (others => '0');
+                       else
+                               if r_ptr_ld = '1' then
+                                       r_ptr_reg <= r_ptr_next;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       with r_ptr_op select r_ptr_next <=
+               r_ptr_reg + 1 when ST_INC,
+               r_ptr_reg - 1 when ST_DEC,
+               r_ptr_reg     when others;
+
+       addra <= '0' & std_logic_vector(s_ptr_next);
+       addrb <= '1' & std_logic_vector(r_ptr_next);
+
+       e_stack_mem: ramb16_s36_s36
+               port map (
+                       -- Port A, parameter stack
+                       ssra  => rst_i,
+                       clka  => clk_i,
+                       ena   => '1',
+                       wea   => s_write,
+                       addra => addra,
+
+                       doa   => s_n_reg,
+                       dopa  => open,
+                       dia   => s_t_reg,
+                       dipa  => "0000",
+
+                       -- Port B, return stack
+                       ssrb  => rst_i,
+                       clkb  => clk_i,
+                       enb   => '1',
+                       web   => r_write,
+                       addrb => addrb,
+
+                       dob   => r_tos_reg,
+                       dopb  => open,
+                       dib   => r_tos_new,
+                       dipb  => "0000"
+               );
+
+
+end behavioral;