`timescale 1ns / 1ps

module ControlUnit (
    input [5:0] opcode,
    input [5:0] funct,
    output [1:0] PC_jump,
    output is_branch,
    output is_loadword,
    output write_ra,
    output ra_addr_source,
    output WB_source,
    output memory_write,
    output [4:0] ALU_function,
    output ALU_source1,
    output ALU_source2,
    output register_write,
    output register_write_destination_source,
    output [1:0] extendop
);

    assign PC_jump = (opcode == 6'h2 || opcode == 6'h3) ? 2'b01:
                     (opcode == 6'h0 && (funct == 6'h8 || funct == 6'h9)) ? 2'b10 : 2'b00;

    assign is_branch = (opcode == 6'h4 || opcode == 6'h5 || opcode == 6'h6 ||
                        opcode == 6'h7 || opcode == 6'h1) ? 1 : 0;

    assign is_loadword = (opcode == 6'h23) ? 1 : 0;

    assign write_ra = (opcode == 6'h3) ? 1 : (opcode == 6'h0 && funct == 6'h9) ? 1 : 0;

    assign ra_addr_source = (opcode == 6'h0 && funct == 6'h9) ? 1 : 0;

    assign register_write = (opcode == 6'h2b || opcode == 6'h4 || opcode == 6'h5 ||
                             opcode == 6'h6 || opcode == 6'h7 || opcode == 6'h1 ||
                             opcode == 6'h1 || opcode == 6'h2 || opcode == 6'h3 ||
                             (opcode == 6'h0 && (funct == 6'h8 || funct == 6'h9))) ? 0 : 1;

    assign WB_source = (opcode == 6'h23) ? 1 : 0;

    assign memory_write = (opcode == 6'h2b) ? 1 : 0;

    assign ALU_function = ((opcode == 6'h0 && funct == 6'h24) || opcode == 6'hc) ? 5'b00000:
                          ((opcode == 6'h0 && funct == 6'h25) || opcode == 6'hd) ? 5'b00001:
                          (opcode == 6'h0 && (funct == 6'h22 || funct == 6'h23)) ? 5'b00110:
                          (opcode == 6'ha || (opcode == 6'h0 && funct == 6'h2a)) ? 5'b00111:
                          (opcode == 6'hb || (opcode == 6'h0 && funct == 56'h2b)) ? 5'b01000:
                          (opcode == 6'h0 && funct == 6'h27) ? 5'b01100:
                          (opcode == 6'h0 && funct == 6'h26) ? 5'b01101:
                          (opcode == 6'h0 && funct == 6'h0) ? 5'b10000:
                          (opcode == 6'h4) ? 5'b10001:
                          (opcode == 6'h5) ? 5'b10010:
                          (opcode == 6'h7) ? 5'b10011:
                          (opcode == 6'h1) ? 5'b10100:
                          (opcode == 6'h6) ? 5'b10101:
                          (opcode == 6'h0 && funct == 6'h2) ? 5'b11000:
                          (opcode == 6'h0 && funct == 6'h3) ? 5'b11001:
                          (opcode == 6'h0 && funct == 6'h18) ? 5'b11010:
                          5'b00010;

    assign ALU_source1 = (opcode == 6'h0 &&
                          (funct == 6'h0 || funct == 6'h2 || funct == 6'h3)) ? 1 : 0;

    assign ALU_source2 = (opcode == 6'h23 || opcode == 6'h2b || opcode == 6'hf ||
                          opcode == 6'h8 || opcode == 6'h9 || opcode == 6'hc || opcode == 6'hd ||
                          opcode == 6'ha || opcode == 6'hb) ? 1 : 0;

    assign register_write_destination_source = (opcode == 6'h23 || opcode == 6'h8 ||
                                                opcode == 6'h9 || opcode == 6'hc ||
                                                opcode == 6'hd || opcode == 6'ha ||
                                                opcode == 6'hb || opcode == 6'hf ||
                                                (opcode == 6'h0 &&
                                                (funct == 6'h0 || funct == 6'h2 ||
                                                funct == 6'h3))) ? 0 : 1;

    assign extendop = (opcode == 6'hf) ? 2'b10 : (opcode == 6'hc || opcode == 6'hd) ? 2'b01 : 2'b00;
endmodule
