DEV Community

olayiwolaosho
olayiwolaosho

Posted on

Building a MIPS Emulator: Part 2 – The ALU

Quick Refresher on MIPS Instruction Format

A MIPS instruction is 32 bits. For R-type instructions:

| op (6) | rs (5) | rt (5) | rd (5) | shamt (5) | funct (6) |
Enter fullscreen mode Exit fullscreen mode
  • op (bits 31–26): the opcode (should be 000000 for R-type)
  • funct (bits 5–0): defines the actual operation (e.g., add, subtract)

For I-type instructions (like lw, sw, beq):

| op (6) | rs (5) | rt (5) | immediate (16) |
Enter fullscreen mode Exit fullscreen mode

The op field (bits 31–26) is used directly to determine the ALU operation in this case.


ALU Operation Code Mapping

Here’s how I use the 2-bit alu_op to decide what kind of operation we’re doing:

  • 0b00Add

    • Used for load/store and branch offset computation
  • 0b01Subtract

    • Used for branch-equal (beq) to compare register values
  • 0b10R-type instructions

    • The actual operation is determined by the function field

Function Field Mapping (for R-type with alu_op = 0b10):

funct (6-bit) Operation ALU Control (4-bit)
100000 Add 0010
100010 Subtract 0110
100100 AND 0000
100101 OR 0001
101010 Set on Less Than 0111

Design Decisions

I'm currently using int32_t instead of uint32_t for register values. This is intentional — I want to allow overflow behavior (which can happen in real MIPS instructions like add), and I want this emulator to remain simple and educational.

The ALU control returns a 4-bit signal that determines which operation the ALU will execute.


Here's the Code

#include <stdint.h>
#include <stdio.h>

uint8_t get_alu_ops_from_funct_field(uint8_t funct_field);

// Generates a 4-bit control signal for the ALU
uint8_t alu_control(uint8_t alu_op, uint8_t funct_field) {
    alu_op &= 0b11;

    switch (alu_op) {
        case 0b00:
            return 0b0010;  // ADD
        case 0b01:
            return 0b0110;  // SUB
        case 0b10:
            return get_alu_ops_from_funct_field(funct_field & 0b111111);
        default:
            printf("Invalid ALU op: 0x%02X\n", alu_op);
            return -1;
    }
}

uint8_t get_alu_ops_from_funct_field(uint8_t funct_field) {
    switch (funct_field) {
        case 0b100000: return 0b0010; // ADD
        case 0b100010: return 0b0110; // SUB
        case 0b100100: return 0b0000; // AND
        case 0b100101: return 0b0001; // OR
        case 0b101010: return 0b0111; // SLT
        default:
            printf("Invalid funct field: 0x%02X\n", funct_field);
            return -1;
    }
}

// Performs the actual ALU operation
int32_t alu(int32_t reg_s1, int32_t reg_s2, uint8_t alu_op, uint8_t funct_field) {
    switch (alu_control(alu_op, funct_field)) {
        case 0b0010: return reg_s1 + reg_s2;
        case 0b0110: return reg_s1 - reg_s2;
        case 0b0000: return reg_s1 & reg_s2;
        case 0b0001: return reg_s1 | reg_s2;
        case 0b0111: return reg_s1 < reg_s2;
        default:
            printf("Invalid ALU control signal.\n");
            return 0;
    }
}
Enter fullscreen mode Exit fullscreen mode

Final Note

This ALU implementation is a starting point and will likely evolve as I learn more and expand the emulator. I’m keeping things modular and easy to follow for now.


Top comments (0)