Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Build artifacts
*.o
*.hi
mips-converter
mips-converter-test
main.exe

# Documentation (Claude Code internal)
docs/

# macOS
.DS_Store
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,71 @@ I made this as a fun project for my [Computer Organisation](https://www.comp.nus

- Decode binary/hex machine code into MIPS instructions
- Encode MIPS instructions into hex machine code
- Automatic pseudo-instruction expansion with detailed explanations
- Support for 6 common MIPS pseudo-instructions (ble, blt, bge, bgt, move, li)

## Supported Instructions

### Real MIPS Instructions

**R-Type Instructions:**
- add, addu, and, jr, nor, or, slt, sltu, sll, srl, sub, subu

**I-Type Instructions:**
- addi, addiu, andi, beq, bne, lbu, lhu, ll, lui, lw, ori, slti, sltiu, sb, sc, sh, sw

**J-Type Instructions:**
- j, jal

### Pseudo-Instructions

The converter automatically expands pseudo-instructions into their equivalent real MIPS instructions:

| Pseudo-Instruction | Expands To | Description |
|-------------------|------------|-------------|
| `ble $rs, $rt, label` | `slt $at, $rt, $rs`<br>`beq $at, $0, label` | Branch if Less Than or Equal |
| `blt $rs, $rt, label` | `slt $at, $rs, $rt`<br>`bne $at, $0, label` | Branch if Less Than |
| `bge $rs, $rt, label` | `slt $at, $rs, $rt`<br>`beq $at, $0, label` | Branch if Greater Than or Equal |
| `bgt $rs, $rt, label` | `slt $at, $rt, $rs`<br>`bne $at, $0, label` | Branch if Greater Than |
| `move $rd, $rs` | `add $rd, $rs, $0` | Copy register value |
| `li $rt, imm` | `ori $rt, $0, imm`<br>or<br>`lui $rt, upper`<br>`ori $rt, $rt, lower` | Load Immediate (1 or 2 instructions based on value) |

## Example Output

### Pseudo-Instruction Expansion

```
Input: ble $2, $3, target

Output:
Pseudo-instruction detected: ble
Expanding 'ble $2, $3, target' into:

1) slt $at, $3, $2
# Set $at = 1 if $3 < $2, else 0
2) beq $at, $0, target
# Branch if $at == 0 (i.e., $2 <= $3)

BINARY EQUIVALENT:
slt $at, $3, $2: 00000000011000100000100000101010
beq $at, $0, target: 00010000001000000000000000000000

HEX EQUIVALENT:
slt $at, $3, $2: 0062082A
beq $at, $0, target: 10200000
Note: Label 'target' uses placeholder offset 0
```

### Regular Instruction Conversion

```
Input: add $t0, $t1, $t2

Output:
MIPS: add $t0, $t1, $t2
BINARY: 00000001001010100100000000100000
HEX: 012A4020
```

## Reference Data

Expand Down
10 changes: 10 additions & 0 deletions src/instruction-code-translator.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,14 @@ void getInstruction(char *opCode, char *fnCode, char *output) {
strcpy(output, "subu");
}
}
}

void mipsToOpCode(char *instruction, char *output) {
// Same logic as getOpCode but takes instruction name
getOpCode(instruction, output);
}

void mipsToFnCode(char *instruction, char *output) {
// Same logic as getFnCode but takes instruction name
getFnCode(instruction, output);
}
2 changes: 2 additions & 0 deletions src/instruction-code-translator.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ void getOpCode(char*, char*);
void getFnCode(char*, char*);
char getInstructionType(char*);
void getInstruction(char*, char*, char*);
void mipsToOpCode(char *instruction, char *output);
void mipsToFnCode(char *instruction, char *output);

#endif
62 changes: 60 additions & 2 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#include <string.h>
#include "instruction-code-translator.h"
#include "radix-translator.h"
#include "mips-parser.h"
#include "pseudo-instruction-handler.h"
#include "mips-to-binary.h"
#define MAX_LENGTH 64
#define BINARY_LENGTH 32
#define HEX_LENGTH 8
Expand Down Expand Up @@ -45,9 +48,64 @@ int main() {
printf("OUTPUT:\n\n");

switch (inputType) {
case INPUT_MIPS:
// TODO
case INPUT_MIPS: {
ParsedInstruction parsed;
parseMipsInstruction(userInput, &parsed);

// Check if pseudo-instruction
if (isPseudoInstruction(parsed.instruction)) {
PseudoExpansion expansion;
expandPseudoInstruction(&parsed, &expansion);

printf("Pseudo-instruction detected: %s\n", parsed.instruction);
printf("Expanding '%s' into:\n\n", userInput);

// Display expansion with explanations
for (int i = 0; i < expansion.numExpanded; i++) {
printf(" %d) %s\n", i + 1, expansion.expandedInstructions[i]);
printf(" # %s\n", expansion.explanations[i]);
}

printf("\nBINARY EQUIVALENT:\n");
printf("HEX EQUIVALENT:\n");

// Convert each expanded instruction
for (int i = 0; i < expansion.numExpanded; i++) {
ParsedInstruction expandedParsed;
parseMipsInstruction(expansion.expandedInstructions[i], &expandedParsed);

char binary[33];
mipsToBinary(&expandedParsed, binary);

char hex[9];
binaryToHex(binary, hex);

printf(" %s: %s\n", expansion.expandedInstructions[i], binary);
printf(" %s: %s\n", expansion.expandedInstructions[i], hex);

if (expandedParsed.isLabel) {
printf(" Note: Label '%s' uses placeholder offset 0\n",
expandedParsed.operands[expandedParsed.numOperands - 1]);
}
}
} else {
// Regular instruction - convert directly
char binary[33];
mipsToBinary(&parsed, binary);

char hex[9];
binaryToHex(binary, hex);

printf("BINARY EQUIVALENT: %s\n", binary);
printf("HEX EQUIVALENT: %s\n", hex);

if (parsed.isLabel) {
printf("Note: Label '%s' uses placeholder offset 0\n",
parsed.operands[parsed.numOperands - 1]);
}
}
break;
}
case INPUT_BINARY:
binaryToHex(userInput, hexRep);
printf("HEX EQUIVALENT: %s\n", hexRep);
Expand Down
113 changes: 113 additions & 0 deletions src/mips-parser.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include "mips-parser.h"
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

int extractRegisterNumber(char *reg) {
// Handle $0, $1, etc.
if (reg[1] >= '0' && reg[1] <= '9') {
int regNum = atoi(&reg[1]);
// Bounds check: valid MIPS registers are 0-31
if (regNum < 0 || regNum > 31) {
return 0; // Return $0 if out of bounds
}
return regNum;
}

// Handle $zero, $at, $v0, $a0, $t0, $s0, etc.
if (strcmp(reg, "$zero") == 0) return 0;
if (strcmp(reg, "$at") == 0) return 1;
if (strcmp(reg, "$v0") == 0) return 2;
if (strcmp(reg, "$v1") == 0) return 3;
if (strcmp(reg, "$a0") == 0) return 4;
if (strcmp(reg, "$a1") == 0) return 5;
if (strcmp(reg, "$a2") == 0) return 6;
if (strcmp(reg, "$a3") == 0) return 7;
if (strcmp(reg, "$t0") == 0) return 8;
if (strcmp(reg, "$t1") == 0) return 9;
if (strcmp(reg, "$t2") == 0) return 10;
if (strcmp(reg, "$t3") == 0) return 11;
if (strcmp(reg, "$t4") == 0) return 12;
if (strcmp(reg, "$t5") == 0) return 13;
if (strcmp(reg, "$t6") == 0) return 14;
if (strcmp(reg, "$t7") == 0) return 15;
if (strcmp(reg, "$s0") == 0) return 16;
if (strcmp(reg, "$s1") == 0) return 17;
if (strcmp(reg, "$s2") == 0) return 18;
if (strcmp(reg, "$s3") == 0) return 19;
if (strcmp(reg, "$s4") == 0) return 20;
if (strcmp(reg, "$s5") == 0) return 21;
if (strcmp(reg, "$s6") == 0) return 22;
if (strcmp(reg, "$s7") == 0) return 23;
if (strcmp(reg, "$t8") == 0) return 24;
if (strcmp(reg, "$t9") == 0) return 25;
if (strcmp(reg, "$k0") == 0) return 26;
if (strcmp(reg, "$k1") == 0) return 27;
if (strcmp(reg, "$gp") == 0) return 28;
if (strcmp(reg, "$sp") == 0) return 29;
if (strcmp(reg, "$fp") == 0) return 30;
if (strcmp(reg, "$ra") == 0) return 31;

return 0; // Default to $0 if unknown
}

int parseImmediate(char *imm, int *isLabel) {
*isLabel = 0;

// Check if it's a label (starts with letter or contains non-digit chars besides '-')
int i = 0;
if (imm[0] == '-') i = 1; // Skip negative sign

int hasNonDigit = 0;
for (; imm[i] != '\0'; i++) {
if (!isdigit(imm[i])) {
hasNonDigit = 1;
break;
}
}

if (hasNonDigit) {
*isLabel = 1;
return 0; // Return placeholder offset for labels
}

return atoi(imm);
}

void parseMipsInstruction(char *mips, ParsedInstruction *output) {
char temp[128];
strncpy(temp, mips, sizeof(temp) - 1);
temp[sizeof(temp) - 1] = '\0'; // Ensure null termination

// Extract instruction (first token)
char *token = strtok(temp, " ,()");

// Check for null pointer before using token
if (token == NULL) {
output->instruction[0] = '\0';
output->numOperands = 0;
output->isLabel = 0;
return;
}

strncpy(output->instruction, token, sizeof(output->instruction) - 1);
output->instruction[sizeof(output->instruction) - 1] = '\0'; // Ensure null termination

output->numOperands = 0;
output->isLabel = 0;

// Extract operands
while ((token = strtok(NULL, " ,()")) != NULL && output->numOperands < MAX_OPERANDS) {
strncpy(output->operands[output->numOperands], token, sizeof(output->operands[output->numOperands]) - 1);
output->operands[output->numOperands][sizeof(output->operands[output->numOperands]) - 1] = '\0'; // Ensure null termination
output->numOperands++;
}

// Check if last operand is a label (for branch instructions)
if (output->numOperands > 0) {
char *lastOp = output->operands[output->numOperands - 1];
if (lastOp[0] != '$' && lastOp[0] != '-' && !isdigit(lastOp[0])) {
output->isLabel = 1;
}
}
}
18 changes: 18 additions & 0 deletions src/mips-parser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef MIPS_PARSER_H
#define MIPS_PARSER_H

#define MAX_INSTRUCTION_LENGTH 10
#define MAX_OPERANDS 3

typedef struct {
char instruction[MAX_INSTRUCTION_LENGTH];
int numOperands;
char operands[MAX_OPERANDS][32]; // Store operands as strings (e.g., "$9", "12", "End")
int isLabel; // 1 if last operand is a label (non-numeric)
} ParsedInstruction;

void parseMipsInstruction(char *mips, ParsedInstruction *output);
int extractRegisterNumber(char *reg); // Extracts number from "$9" or "$t0"
int parseImmediate(char *imm, int *isLabel); // Parse immediate/offset, sets isLabel if symbolic

#endif
Loading