`timescale 1ns / 1ps

module ALU(
    input wire [3:0] opcode, // NOT the same as the instruction set opcode
    input wire [8:0] operand0,
    input wire [8:0] operand1,
    output wire [8:0] result 
    );
    
    // Wires for connecting the modules to the mux
    wire [8:0] result_A,result_B,result_C,result_D,result_E,result_F,result_G,result_H,result_I,result_J,result_K,result_L,result_M,result_N,result_O,result_P;
    wire cout;

    // A (0000) - Add    
    add_9bit add0(
        .A(operand0),
        .B(operand1),
        .Cin(1'b0),
        .Sum(result_A),
        .Cout(cout));
    
    // B (0001) - Subtract
    sub_9bit sub0(
        .A(operand0),
        .B(operand1),
        .C(result_B));
    // C (0010) - OR
    or_9bit or0(
        .A(operand0),
        .B(operand1),
        .C(result_C));
    // D (0011) - NOR
    nor_9bit nor0(
        .A(operand0),
        .B(operand1),
        .C(result_D));
    // E (0100) - AND
    and_9bit and0(
        .A(operand0),
        .B(operand1),
        .C(result_E));
    // F (0101) - Shift Left
    shift_left sl(
        .A(operand0[7:0]),
        .B(result_F));
    // G (0110) - Shift Right Logical
    shift_right_logical srl(
        .A(operand0[8:1]),
        .B(result_G));
    // H (0111) - Shift Right Arithmetic
    shift_right_arithmetic sra(
        .A(operand0[8:1]),
        .B(result_H));
    // I (1000) - NOT
    not_9bit not0(
        .A(operand0),
        .B(result_I));
    // J (1001) - Less Than
    less_than less0(
        .A(operand0),
        .B(operand1),
        .C(result_J));
    // K (1010) - Zero
    BEQ zero0(
        .A(operand0),
        .B(result_K));
    // L (1011)
    // M (1100)
    // N (1101)
    // O (1110)
    // P (1111)
    
    // MUX chooses which result to show based on the ALU's opcode
    mux_16_1 mux0(
        .switch(opcode),
        .A(result_A),
        .B(result_B),
        .C(result_C),
        .D(result_D),
        .E(result_E),
        .F(result_F),
        .G(result_G),
        .H(result_H),
        .I(result_I),
        .J(result_J),
        .K(result_K),
        .L(9'b000000000),
        .M(9'b111111111), // Currently not used
        .N(9'b111111111), // Currently not used
        .O(9'b111111111), // Currently not used
        .P(9'b111111111), // Currently not used
        .out(result));



endmodule

//testbench
module alu_tb();
    reg [8:0] a;
    reg [8:0] b;
    reg [3:0] c;
    wire [8:0] d;

    ALU alu0(
        .operand0(a),
        .operand1(b),
        .opcode(c),
        .result(d));
    
    initial begin
        a = 9'b000000111;
        b = 9'b000111000;
        c = 4'b0000;
        #5
        a = 9'b000011000;
        b = 9'b000011000;
        c = 4'b0001;
        #5
        a = 9'b101010100;
        b = 9'b010101011;
        c = 4'b0010;
        #5
        a = 9'b101010100;
        b = 9'b010101000;
        c = 4'b0011;
        #5
        a = 9'b000110000;
        b = 9'b000111000;
        c = 4'b0100;
        #5
        a = 9'b01011000;
        c = 4'b0101;
        #5
        a = 9'b00001010;
        c = 4'b0110;
        #5
        $finish;

    end
endmodule
