diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1ac78dc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+# Build artifacts
+*.o
+*.hi
+mips-converter
+mips-converter-test
+main.exe
+
+# Documentation (Claude Code internal)
+docs/
+
+# macOS
+.DS_Store
diff --git a/README.md b/README.md
index dcd12f3..5f8a7a4 100644
--- a/README.md
+++ b/README.md
@@ -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`
`beq $at, $0, label` | Branch if Less Than or Equal |
+| `blt $rs, $rt, label` | `slt $at, $rs, $rt`
`bne $at, $0, label` | Branch if Less Than |
+| `bge $rs, $rt, label` | `slt $at, $rs, $rt`
`beq $at, $0, label` | Branch if Greater Than or Equal |
+| `bgt $rs, $rt, label` | `slt $at, $rt, $rs`
`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`
or
`lui $rt, upper`
`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
diff --git a/src/instruction-code-translator.c b/src/instruction-code-translator.c
index e21b9a6..3e20bae 100644
--- a/src/instruction-code-translator.c
+++ b/src/instruction-code-translator.c
@@ -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);
}
\ No newline at end of file
diff --git a/src/instruction-code-translator.h b/src/instruction-code-translator.h
index ddc2b2d..d4e0d2b 100644
--- a/src/instruction-code-translator.h
+++ b/src/instruction-code-translator.h
@@ -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
\ No newline at end of file
diff --git a/src/main.c b/src/main.c
index 2b3939c..f03f256 100644
--- a/src/main.c
+++ b/src/main.c
@@ -3,6 +3,9 @@
#include
#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
@@ -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);
diff --git a/src/mips-parser.c b/src/mips-parser.c
new file mode 100644
index 0000000..6960a1d
--- /dev/null
+++ b/src/mips-parser.c
@@ -0,0 +1,113 @@
+#include "mips-parser.h"
+#include
+#include
+#include
+
+int extractRegisterNumber(char *reg) {
+ // Handle $0, $1, etc.
+ if (reg[1] >= '0' && reg[1] <= '9') {
+ int regNum = atoi(®[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;
+ }
+ }
+}
diff --git a/src/mips-parser.h b/src/mips-parser.h
new file mode 100644
index 0000000..3a1f5f4
--- /dev/null
+++ b/src/mips-parser.h
@@ -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
diff --git a/src/mips-to-binary.c b/src/mips-to-binary.c
new file mode 100644
index 0000000..1f090be
--- /dev/null
+++ b/src/mips-to-binary.c
@@ -0,0 +1,209 @@
+#include "mips-to-binary.h"
+#include "instruction-code-translator.h"
+#include "radix-translator.h"
+#include "mips-parser.h"
+#include
+#include
+
+void mipsToBinary(ParsedInstruction *parsed, char *binaryOutput) {
+ char opCode[7] = {0};
+ char type;
+
+ // Get opCode for this instruction
+ mipsToOpCode(parsed->instruction, opCode);
+
+ // Determine instruction type
+ type = getInstructionType(opCode);
+
+ // Initialize binary output with all zeros
+ strcpy(binaryOutput, "00000000000000000000000000000000");
+
+ if (type == 'R') {
+ // R-type: opcode(6) rs(5) rt(5) rd(5) shamt(5) funct(6)
+ char fnCode[7] = {0};
+ char rsBinary[6];
+ char rtBinary[6];
+ char rdBinary[6];
+ char shamtBinary[6];
+
+ // Initialize buffers with zeros
+ strcpy(rsBinary, "00000");
+ strcpy(rtBinary, "00000");
+ strcpy(rdBinary, "00000");
+ strcpy(shamtBinary, "00000");
+
+ mipsToFnCode(parsed->instruction, fnCode);
+
+ // Handle different R-type formats
+ if (strcmp(parsed->instruction, "jr") == 0) {
+ // jr $rs -> rs field only
+ int rs = extractRegisterNumber(parsed->operands[0]);
+ decimalToBinary(rs, rsBinary);
+
+ // Build: opcode(6) + rs(5) + 00000 + 00000 + 00000 + funct(6)
+ strncpy(binaryOutput, opCode, 6);
+ strncpy(binaryOutput + 6, rsBinary, 5);
+ strncpy(binaryOutput + 11, "00000", 5); // rt
+ strncpy(binaryOutput + 16, "00000", 5); // rd
+ strncpy(binaryOutput + 21, "00000", 5); // shamt
+ strncpy(binaryOutput + 26, fnCode, 6);
+
+ } else if (strcmp(parsed->instruction, "sll") == 0 || strcmp(parsed->instruction, "srl") == 0) {
+ // Shift: rd, rt, shamt -> rd, rt, shamt
+ int rd = extractRegisterNumber(parsed->operands[0]);
+ int rt = extractRegisterNumber(parsed->operands[1]);
+ int shamt = atoi(parsed->operands[2]);
+
+ decimalToBinary(rd, rdBinary);
+ decimalToBinary(rt, rtBinary);
+ decimalToBinary(shamt, shamtBinary);
+
+ // Build: opcode(6) + 00000 + rt(5) + rd(5) + shamt(5) + funct(6)
+ strncpy(binaryOutput, opCode, 6);
+ strncpy(binaryOutput + 6, "00000", 5); // rs
+ strncpy(binaryOutput + 11, rtBinary, 5);
+ strncpy(binaryOutput + 16, rdBinary, 5);
+ strncpy(binaryOutput + 21, shamtBinary, 5);
+ strncpy(binaryOutput + 26, fnCode, 6);
+
+ } else {
+ // Standard R-type: rd, rs, rt
+ int rd = extractRegisterNumber(parsed->operands[0]);
+ int rs = extractRegisterNumber(parsed->operands[1]);
+ int rt = extractRegisterNumber(parsed->operands[2]);
+
+ decimalToBinary(rs, rsBinary);
+ decimalToBinary(rt, rtBinary);
+ decimalToBinary(rd, rdBinary);
+
+ // Build: opcode(6) + rs(5) + rt(5) + rd(5) + shamt(5) + funct(6)
+ strncpy(binaryOutput, opCode, 6);
+ strncpy(binaryOutput + 6, rsBinary, 5);
+ strncpy(binaryOutput + 11, rtBinary, 5);
+ strncpy(binaryOutput + 16, rdBinary, 5);
+ strncpy(binaryOutput + 21, "00000", 5); // shamt
+ strncpy(binaryOutput + 26, fnCode, 6);
+ }
+
+ } else if (type == 'I') {
+ // I-type: opcode(6) rs(5) rt(5) immediate(16)
+ char rsBinary[6];
+ char rtBinary[6];
+ char immBinary[17];
+
+ // Initialize buffers with zeros
+ strcpy(rsBinary, "00000");
+ strcpy(rtBinary, "00000");
+ strcpy(immBinary, "0000000000000000");
+
+ if (strcmp(parsed->instruction, "lw") == 0 || strcmp(parsed->instruction, "sw") == 0 ||
+ strcmp(parsed->instruction, "lbu") == 0 || strcmp(parsed->instruction, "lhu") == 0 ||
+ strcmp(parsed->instruction, "sb") == 0 || strcmp(parsed->instruction, "sh") == 0 ||
+ strcmp(parsed->instruction, "ll") == 0 || strcmp(parsed->instruction, "sc") == 0) {
+ // Load/Store: rt, offset(rs) format
+ int rt = extractRegisterNumber(parsed->operands[0]);
+ int offset = atoi(parsed->operands[1]); // offset is first in operands[1]
+ int rs = extractRegisterNumber(parsed->operands[2]); // base register is in operands[2]
+
+ decimalToBinary(rs, rsBinary);
+ decimalToBinary(rt, rtBinary);
+
+ // Handle signed offset (16-bit two's complement)
+ if (offset < 0) {
+ // Convert negative to 16-bit two's complement
+ offset = (1 << 16) + offset; // Add 2^16 to get positive representation
+ }
+ decimalToBinary(offset, immBinary);
+
+ // Build: opcode(6) + rs(5) + rt(5) + immediate(16)
+ strncpy(binaryOutput, opCode, 6);
+ strncpy(binaryOutput + 6, rsBinary, 5);
+ strncpy(binaryOutput + 11, rtBinary, 5);
+ strncpy(binaryOutput + 16, immBinary, 16);
+
+ } else if (strcmp(parsed->instruction, "beq") == 0 || strcmp(parsed->instruction, "bne") == 0) {
+ // Branch: rs, rt, offset format
+ int rs = extractRegisterNumber(parsed->operands[0]);
+ int rt = extractRegisterNumber(parsed->operands[1]);
+ int offset = 0;
+
+ // Handle offset (could be label or immediate)
+ if (!parsed->isLabel) {
+ offset = atoi(parsed->operands[2]);
+
+ // Handle signed offset (16-bit two's complement)
+ if (offset < 0) {
+ offset = (1 << 16) + offset;
+ }
+ }
+
+ decimalToBinary(rs, rsBinary);
+ decimalToBinary(rt, rtBinary);
+ decimalToBinary(offset, immBinary);
+
+ // Build: opcode(6) + rs(5) + rt(5) + immediate(16)
+ strncpy(binaryOutput, opCode, 6);
+ strncpy(binaryOutput + 6, rsBinary, 5);
+ strncpy(binaryOutput + 11, rtBinary, 5);
+ strncpy(binaryOutput + 16, immBinary, 16);
+
+ } else if (strcmp(parsed->instruction, "lui") == 0) {
+ // lui: rt, immediate format (no rs)
+ int rt = extractRegisterNumber(parsed->operands[0]);
+ int imm = atoi(parsed->operands[1]);
+
+ decimalToBinary(rt, rtBinary);
+
+ if (imm < 0) {
+ imm = (1 << 16) + imm;
+ }
+ decimalToBinary(imm, immBinary);
+
+ // Build: opcode(6) + 00000 + rt(5) + immediate(16)
+ strncpy(binaryOutput, opCode, 6);
+ strncpy(binaryOutput + 6, "00000", 5); // rs is 0
+ strncpy(binaryOutput + 11, rtBinary, 5);
+ strncpy(binaryOutput + 16, immBinary, 16);
+
+ } else {
+ // Standard I-type (addi, andi, ori, etc.): rt, rs, immediate
+ int rt = extractRegisterNumber(parsed->operands[0]);
+ int rs = extractRegisterNumber(parsed->operands[1]);
+ int imm = atoi(parsed->operands[2]);
+
+ decimalToBinary(rs, rsBinary);
+ decimalToBinary(rt, rtBinary);
+
+ // Handle signed immediate (16-bit two's complement)
+ if (imm < 0) {
+ imm = (1 << 16) + imm;
+ }
+ decimalToBinary(imm, immBinary);
+
+ // Build: opcode(6) + rs(5) + rt(5) + immediate(16)
+ strncpy(binaryOutput, opCode, 6);
+ strncpy(binaryOutput + 6, rsBinary, 5);
+ strncpy(binaryOutput + 11, rtBinary, 5);
+ strncpy(binaryOutput + 16, immBinary, 16);
+ }
+
+ } else if (type == 'J') {
+ // J-type: opcode(6) address(26)
+ char addressBinary[27] = "00000000000000000000000000";
+ int address = 0;
+
+ // Handle address (could be label or immediate)
+ if (!parsed->isLabel) {
+ address = atoi(parsed->operands[0]);
+ }
+
+ decimalToBinary(address, addressBinary);
+
+ // Build: opcode(6) + address(26)
+ strncpy(binaryOutput, opCode, 6);
+ strncpy(binaryOutput + 6, addressBinary, 26);
+ }
+
+ // Ensure null termination
+ binaryOutput[32] = '\0';
+}
diff --git a/src/mips-to-binary.h b/src/mips-to-binary.h
new file mode 100644
index 0000000..5ed1af3
--- /dev/null
+++ b/src/mips-to-binary.h
@@ -0,0 +1,8 @@
+#ifndef MIPS_TO_BINARY_H
+#define MIPS_TO_BINARY_H
+
+#include "mips-parser.h"
+
+void mipsToBinary(ParsedInstruction *parsed, char *binaryOutput);
+
+#endif
diff --git a/src/pseudo-instruction-handler.c b/src/pseudo-instruction-handler.c
new file mode 100644
index 0000000..4398fd8
--- /dev/null
+++ b/src/pseudo-instruction-handler.c
@@ -0,0 +1,146 @@
+#include "pseudo-instruction-handler.h"
+#include
+#include
+#include
+
+int isPseudoInstruction(char *instruction) {
+ // Issue #3: Add NULL pointer check
+ if (instruction == NULL) {
+ return 0;
+ }
+
+ return strcmp(instruction, "ble") == 0 ||
+ strcmp(instruction, "blt") == 0 ||
+ strcmp(instruction, "bge") == 0 ||
+ strcmp(instruction, "bgt") == 0 ||
+ strcmp(instruction, "move") == 0 ||
+ strcmp(instruction, "li") == 0;
+}
+
+void expandPseudoInstruction(ParsedInstruction *parsed, PseudoExpansion *expansion) {
+ // Issue #3: Add NULL pointer checks
+ if (parsed == NULL || expansion == NULL) {
+ return;
+ }
+
+ expansion->isPseudo = 1;
+ expansion->numExpanded = 0;
+
+ char *inst = parsed->instruction;
+
+ // Issue #1: Don't unconditionally access operands - declare but don't assign yet
+ char *rs = NULL;
+ char *rt = NULL;
+ char *label = NULL;
+
+ // ble $rs, $rt, label → slt $at, $rt, $rs + beq $at, $0, label
+ if (strcmp(inst, "ble") == 0) {
+ // Issue #5: Validate operand count before accessing
+ if (parsed->numOperands < 3) {
+ return;
+ }
+ rs = parsed->operands[0];
+ rt = parsed->operands[1];
+ label = parsed->operands[2];
+
+ // Issue #2: Replace sprintf with snprintf
+ snprintf(expansion->expandedInstructions[0], 64, "slt $at, %s, %s", rt, rs);
+ snprintf(expansion->explanations[0], 128, "Set $at = 1 if %s < %s, else 0", rt, rs);
+ snprintf(expansion->expandedInstructions[1], 64, "beq $at, $0, %s", label);
+ snprintf(expansion->explanations[1], 128, "Branch if $at == 0 (i.e., %s <= %s)", rs, rt);
+ expansion->numExpanded = 2;
+ }
+ // blt $rs, $rt, label → slt $at, $rs, $rt + bne $at, $0, label
+ else if (strcmp(inst, "blt") == 0) {
+ // Issue #5: Validate operand count before accessing
+ if (parsed->numOperands < 3) {
+ return;
+ }
+ rs = parsed->operands[0];
+ rt = parsed->operands[1];
+ label = parsed->operands[2];
+
+ // Issue #2: Replace sprintf with snprintf
+ snprintf(expansion->expandedInstructions[0], 64, "slt $at, %s, %s", rs, rt);
+ snprintf(expansion->explanations[0], 128, "Set $at = 1 if %s < %s, else 0", rs, rt);
+ snprintf(expansion->expandedInstructions[1], 64, "bne $at, $0, %s", label);
+ snprintf(expansion->explanations[1], 128, "Branch if $at != 0 (i.e., %s < %s)", rs, rt);
+ expansion->numExpanded = 2;
+ }
+ // bge $rs, $rt, label → slt $at, $rs, $rt + beq $at, $0, label
+ else if (strcmp(inst, "bge") == 0) {
+ // Issue #5: Validate operand count before accessing
+ if (parsed->numOperands < 3) {
+ return;
+ }
+ rs = parsed->operands[0];
+ rt = parsed->operands[1];
+ label = parsed->operands[2];
+
+ // Issue #2: Replace sprintf with snprintf
+ snprintf(expansion->expandedInstructions[0], 64, "slt $at, %s, %s", rs, rt);
+ snprintf(expansion->explanations[0], 128, "Set $at = 1 if %s < %s, else 0", rs, rt);
+ snprintf(expansion->expandedInstructions[1], 64, "beq $at, $0, %s", label);
+ snprintf(expansion->explanations[1], 128, "Branch if $at == 0 (i.e., %s >= %s)", rs, rt);
+ expansion->numExpanded = 2;
+ }
+ // bgt $rs, $rt, label → slt $at, $rt, $rs + bne $at, $0, label
+ else if (strcmp(inst, "bgt") == 0) {
+ // Issue #5: Validate operand count before accessing
+ if (parsed->numOperands < 3) {
+ return;
+ }
+ rs = parsed->operands[0];
+ rt = parsed->operands[1];
+ label = parsed->operands[2];
+
+ // Issue #2: Replace sprintf with snprintf
+ snprintf(expansion->expandedInstructions[0], 64, "slt $at, %s, %s", rt, rs);
+ snprintf(expansion->explanations[0], 128, "Set $at = 1 if %s < %s, else 0", rt, rs);
+ snprintf(expansion->expandedInstructions[1], 64, "bne $at, $0, %s", label);
+ snprintf(expansion->explanations[1], 128, "Branch if $at != 0 (i.e., %s > %s)", rs, rt);
+ expansion->numExpanded = 2;
+ }
+ // move $rd, $rs → add $rd, $rs, $0
+ else if (strcmp(inst, "move") == 0) {
+ // Issue #5: Validate operand count before accessing
+ if (parsed->numOperands < 2) {
+ return;
+ }
+ char *rd = parsed->operands[0];
+ char *rs_move = parsed->operands[1];
+
+ // Issue #2: Replace sprintf with snprintf
+ snprintf(expansion->expandedInstructions[0], 64, "add %s, %s, $0", rd, rs_move);
+ snprintf(expansion->explanations[0], 128, "Copy %s to %s (add %s + 0)", rs_move, rd, rs_move);
+ expansion->numExpanded = 1;
+ }
+ // li $rt, imm → handle based on immediate size
+ else if (strcmp(inst, "li") == 0) {
+ // Issue #5: Validate operand count before accessing
+ if (parsed->numOperands < 2) {
+ return;
+ }
+ char *rd = parsed->operands[0];
+ int imm = atoi(parsed->operands[1]);
+
+ // Issue #4: Fix range check for signed 16-bit immediate
+ if (imm >= -32768 && imm <= 32767) {
+ // Issue #2: Replace sprintf with snprintf
+ snprintf(expansion->expandedInstructions[0], 64, "ori %s, $0, %d", rd, imm);
+ snprintf(expansion->explanations[0], 128, "Load immediate %d into %s", imm, rd);
+ expansion->numExpanded = 1;
+ } else {
+ // Need lui + ori for 32-bit immediate
+ unsigned int uimm = (unsigned int)imm;
+ int upper = (uimm >> 16) & 0xFFFF;
+ int lower = uimm & 0xFFFF;
+ // Issue #2: Replace sprintf with snprintf
+ snprintf(expansion->expandedInstructions[0], 64, "lui %s, %d", rd, upper);
+ snprintf(expansion->explanations[0], 128, "Load upper 16 bits (%d) into %s", upper, rd);
+ snprintf(expansion->expandedInstructions[1], 64, "ori %s, %s, %d", rd, rd, lower);
+ snprintf(expansion->explanations[1], 128, "OR lower 16 bits (%d) into %s", lower, rd);
+ expansion->numExpanded = 2;
+ }
+ }
+}
diff --git a/src/pseudo-instruction-handler.h b/src/pseudo-instruction-handler.h
new file mode 100644
index 0000000..74139a2
--- /dev/null
+++ b/src/pseudo-instruction-handler.h
@@ -0,0 +1,16 @@
+#ifndef PSEUDO_INSTRUCTION_HANDLER_H
+#define PSEUDO_INSTRUCTION_HANDLER_H
+
+#include "mips-parser.h"
+
+typedef struct {
+ int isPseudo;
+ int numExpanded;
+ char expandedInstructions[2][64]; // Max 2 instructions for expansion
+ char explanations[2][128]; // Explanation for each expanded instruction
+} PseudoExpansion;
+
+int isPseudoInstruction(char *instruction);
+void expandPseudoInstruction(ParsedInstruction *parsed, PseudoExpansion *expansion);
+
+#endif
diff --git a/test-pseudo-cases.txt b/test-pseudo-cases.txt
new file mode 100644
index 0000000..8487bb0
--- /dev/null
+++ b/test-pseudo-cases.txt
@@ -0,0 +1,39 @@
+// Pseudo-instruction test cases
+// Testing all pseudo-instructions: ble, blt, bge, bgt, move, li
+
+// BLE - Branch if Less Than or Equal
+// ble $rs, $rt, label → slt $at, $rt, $rs + beq $at, $0, label
+ble $2, $3, target1 -> slt $at, $3, $2 + beq $at, $0, target1
+
+// BLT - Branch if Less Than
+// blt $rs, $rt, label → slt $at, $rs, $rt + bne $at, $0, label
+blt $4, $5, target2 -> slt $at, $4, $5 + bne $at, $0, target2
+
+// BGE - Branch if Greater Than or Equal
+// bge $rs, $rt, label → slt $at, $rs, $rt + beq $at, $0, label
+bge $6, $7, target3 -> slt $at, $6, $7 + beq $at, $0, target3
+
+// BGT - Branch if Greater Than
+// bgt $rs, $rt, label → slt $at, $rt, $rs + bne $at, $0, label
+bgt $8, $9, target4 -> slt $at, $9, $8 + bne $at, $0, target4
+
+// MOVE - Move register to register
+// move $rd, $rs → add $rd, $rs, $0
+move $10, $11 -> add $10, $11, $0
+
+// LI - Load Immediate (16-bit fits in one instruction)
+// li $rt, imm → ori $rt, $0, imm (when -32768 ≤ imm ≤ 32767)
+li $12, 100 -> ori $12, $0, 100
+
+// LI - Load Immediate (32-bit requires two instructions)
+// li $rt, imm → lui $rt, upper + ori $rt, $rt, lower (when imm > 32767 or imm < -32768)
+li $13, 65536 -> lui $13, 1 + ori $13, $13, 0
+
+// Additional test cases for comprehensive coverage
+ble $t0, $t1, loop -> slt $at, $t1, $t0 + beq $at, $0, loop
+blt $s0, $s1, done -> slt $at, $s0, $s1 + bne $at, $0, done
+bge $a0, $a1, skip -> slt $at, $a0, $a1 + beq $at, $0, skip
+bgt $v0, $v1, exit -> slt $at, $v1, $v0 + bne $at, $0, exit
+move $t2, $t3 -> add $t2, $t3, $0
+li $t4, 255 -> ori $t4, $0, 255
+li $t5, 1048576 -> lui $t5, 16 + ori $t5, $t5, 0