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