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) |
-
op
(bits 31–26): the opcode (should be000000
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) |
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:
-
0b00
→ Add- Used for load/store and branch offset computation
-
0b01
→ Subtract- Used for branch-equal (beq) to compare register values
-
0b10
→ R-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;
}
}
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)