DEV Community

olayiwolaosho
olayiwolaosho

Posted on

Building a MIPS Emulator (Part 1): Loading and Reading Raw Binary

"I didn’t know how anything would work when I started—just had a vague sense I’d figure it out along the way."


Why I’m Doing This

I wanted to understand how computers really work—beyond just writing high-level code. I needed to deepen my knowledge and prove to myself that these low-level systems aren’t inherently difficult; you just need the right concepts.

So, I decided to build a MIPS emulator.

I broke it down into three parts:

  1. Loading the binary code from a MIPS program
  2. Decoding the binary instructions
  3. Executing them

At first, I didn’t even know how to load a file, let alone parse instructions. So that’s where I began.


Step 1: Getting the Binary File

We need a binary file that contains MIPS instructions in raw binary format. There are other formats like ELF, but I chose to keep it simple with a .bin file.

Fun fact: I didn’t know until recently that .bin files can directly contain raw binary instructions. You learn something new every day.

To generate one, I used MARS 4.5, a MIPS simulator written in Java. I wrote a simple program:

addiu $t0, $zero, 5    # $t0 = 5  
addiu $t1, $zero, 7    # $t1 = 7  
addu  $t2, $t0, $t1    # $t2 = $t0 + $t1  
Enter fullscreen mode Exit fullscreen mode

Then I assembled it and used File → Dump Memory to export the instructions. Make sure you:

  • Set the format to Binary
  • Save it with a .bin extension

Step 2: Reading the Binary File in C

Once I had the .bin file, I wrote a simple C program to read and print each 32-bit MIPS instruction. Here's a snippet:

FILE *file = fopen(filename, "rb");

if (!file) {
    perror("Failed to open file");
    return 1;
}
Enter fullscreen mode Exit fullscreen mode

The "rb" mode means we’re opening the file for reading in binary mode. If the file doesn’t exist, fopen() returns NULL.

Then I read the file 4 bytes at a time (since MIPS instructions are 32-bit = 4 bytes):

while (fread(&instruction, sizeof(u_int32_t), 1, file) == 1) {
    printf("0x%08X | 0x%08X          | %u\n", offset, instruction, instruction);
    offset += 4;
}
Enter fullscreen mode Exit fullscreen mode

I set the counter to one so I could overwrite whatever was in the instruction and store the value read from the file stream


Full Code

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

int loadMipsBinary(const char *filename) {
    FILE *file = fopen(filename, "rb");

    if (!file) {
        perror("Failed to open file");
        return 1;
    }

    printf("Address (byte offset)  | Instruction (Hex)  | Instruction (Dec)\n");
    printf("-----------------------|---------------------|---------------------\n");

    uint32_t instruction;
    size_t offset = 0;

    while (fread(&instruction, sizeof(uint32_t), 1, file) == 1) {
        printf("0x%08X               | 0x%08X          | %u\n",
               (unsigned int)offset, instruction, instruction);
        offset += 4;
    }

    fclose(file);
    return 0;
}

int main() {
    loadMipsBinary("mipstest.bin");
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Let me know when you're ready for Part 2—decoding the binary into actual MIPS operations.

Top comments (0)