MIPS Interpreter Implementation
Overview
This skill provides guidance for implementing MIPS interpreters/emulators that can load and execute MIPS ELF binaries. The core challenge involves parsing ELF files, decoding MIPS instructions, managing virtual memory, and handling system calls.
Critical Approach: Incremental Development
The most important principle for this task is incremental development over comprehensive analysis. Avoid spending excessive time analyzing before writing code. Instead:
- Start with a minimal working skeleton early
- Expand functionality iteratively
- Test frequently with partial implementations
- Debug and refine based on actual execution
Implementation Phases
Phase 1: Minimal ELF Loader
Start with the bare minimum to load an executable:
- Parse ELF header to extract:
- Magic number verification (0x7f, 'E', 'L', 'F')
- Architecture (MIPS32)
- Endianness (typically little-endian)
- Entry point address
- Parse program headers to identify loadable segments
- Load segments into virtual memory at specified addresses
- Set program counter to entry point
Key data structures needed:
- Memory array/map for virtual address space
- Registers array (32 general-purpose + PC + HI/LO)
Phase 2: Core Instruction Decoding
Implement instruction decoding for the three MIPS instruction formats:
R-type format (register operations):
- Bits 31-26: opcode (0x00 for R-type)
- Bits 25-21: rs (source register 1)
- Bits 20-16: rt (source register 2)
- Bits 15-11: rd (destination register)
- Bits 10-6: shamt (shift amount)
- Bits 5-0: funct (function code)
I-type format (immediate operations):
- Bits 31-26: opcode
- Bits 25-21: rs
- Bits 20-16: rt
- Bits 15-0: immediate value
J-type format (jump operations):
- Bits 31-26: opcode
- Bits 25-0: target address
Phase 3: Essential Instructions First
Implement instructions in priority order based on typical program needs:
High Priority (implement first):
- Arithmetic: ADD, ADDU, ADDI, ADDIU, SUB, SUBU
- Logical: AND, ANDI, OR, ORI, XOR, NOR
- Shifts: SLL, SRL, SRA, SLLV, SRLV, SRAV
- Comparison: SLT, SLTI, SLTU, SLTIU
- Memory: LW, SW, LB, LBU, SB, LH, LHU, SH
- Branches: BEQ, BNE, BGTZ, BLEZ, BLTZ, BGEZ
- Jumps: J, JAL, JR, JALR
- Load: LUI
Medium Priority:
- Multiply/Divide: MULT, MULTU, DIV, DIVU, MFHI, MFLO, MTHI, MTLO
Lower Priority:
- Coprocessor instructions (if needed)
- Floating point (if needed)
Phase 4: Syscall Handler
Implement system call interface based on the target environment:
- Detect SYSCALL instruction
- Read syscall number from register (typically $v0 or $2)
- Read arguments from registers ($a0-$a3 or $4-$7)
- Execute syscall and set return value in $v0
Common syscalls to implement:
- read (file descriptor, buffer, count)
- write (file descriptor, buffer, count)
- open (path, flags, mode)
- close (file descriptor)
- lseek (file descriptor, offset, whence)
- exit (status code)
Phase 5: I/O and File System
For programs requiring file access:
- Implement file descriptor table
- Handle standard streams (stdin=0, stdout=1, stderr=2)
- Support opening/reading external files (e.g., data files)
- Handle output file creation (e.g., frame buffers, results)
Verification Strategies
Incremental Testing
Test after each implementation phase:
- ELF loader test: Verify entry point and memory layout match expected values
- Instruction test: Create simple test sequences for each instruction group
- Syscall test: Test each syscall with known inputs/outputs
- Integration test: Run actual target binary
Debugging Techniques
- Add instruction tracing (PC, instruction, register changes)
- Log syscall invocations with arguments
- Verify memory reads/writes at expected addresses
- Compare register state against expected values at checkpoints
Common Validation Points
- Entry point address matches ELF header
- Stack pointer initialized correctly
- Memory segments loaded at correct addresses
- Register $0 always reads as zero
- Signed vs unsigned operations handled correctly
- Branch delay slots handled (if applicable to target)
Common Pitfalls
Analysis Paralysis
Problem: Spending too much time understanding every detail before writing code.
Solution: Start implementation after understanding ELF basics, entry point, and syscall numbers. Iterate and learn through building.
Missing Endianness Handling
Problem: Incorrect byte ordering when loading instructions or data.
Solution: Check ELF header for endianness flag and apply consistently when reading multi-byte values.
Register Zero Hardwiring
Problem: Allowing writes to register $0 to persist.
Solution: Always return 0 when reading $0, or ignore writes to $0.
Sign Extension Errors
Problem: Incorrect sign extension for immediate values or load operations.
Solution: Carefully distinguish signed vs unsigned operations. LB sign-extends, LBU zero-extends.
Branch/Jump Address Calculation
Problem: Incorrect target address computation.
Solution:
- Branches: PC + 4 + (sign-extended offset << 2)
- Jumps: (PC & 0xF0000000) | (target << 2)
Memory Alignment
Problem: Unaligned memory access causing errors.
Solution: Either enforce alignment or handle unaligned access appropriately for the target.
Syscall Return Values
Problem: Not setting error codes or return values correctly.
Solution: Set $v0 for return value, handle error cases consistently.
Incomplete Instruction Coverage
Problem: Missing instructions causing silent failures.
Solution: Log unimplemented instructions with their encodings for debugging.
Time Management Strategy
For complex interpreter tasks:
- First 25% of time: ELF loading + basic instruction loop skeleton
- Next 25% of time: Core arithmetic/logic/memory instructions
- Next 25% of time: Branches, jumps, and syscalls
- Final 25% of time: Testing, debugging, edge cases
Prioritize a running (even incomplete) interpreter over comprehensive analysis. A partial implementation that executes provides more debugging information than complete analysis without code.
1---2name: make-mips-interpreter3description: Guidance for building MIPS interpreters or emulators that execute MIPS binaries. This skill applies when implementing CPU emulation, ELF loaders, instruction decoders, or syscall handlers for MIPS architecture. Use when tasks involve creating virtual machines for MIPS executables, interpreting MIPS assembly instructions, or emulating MIPS system calls.4---5
6# MIPS Interpreter Implementation
7
8## Overview
9
10This skill provides guidance for implementing MIPS interpreters/emulators that can load and execute MIPS ELF binaries. The core challenge involves parsing ELF files, decoding MIPS instructions, managing virtual memory, and handling system calls.
11
12## Critical Approach: Incremental Development
13
14The most important principle for this task is **incremental development over comprehensive analysis**. Avoid spending excessive time analyzing before writing code. Instead:
15
161. Start with a minimal working skeleton early
172. Expand functionality iteratively
183. Test frequently with partial implementations
194. Debug and refine based on actual execution
20
21## Implementation Phases
22
23### Phase 1: Minimal ELF Loader
24
25Start with the bare minimum to load an executable:
26
271. Parse ELF header to extract:
28 - Magic number verification (0x7f, 'E', 'L', 'F')
29 - Architecture (MIPS32)
30 - Endianness (typically little-endian)
31 - Entry point address
322. Parse program headers to identify loadable segments
333. Load segments into virtual memory at specified addresses
344. Set program counter to entry point
35
36Key data structures needed:
37- Memory array/map for virtual address space
38- Registers array (32 general-purpose + PC + HI/LO)
39
40### Phase 2: Core Instruction Decoding
41
42Implement instruction decoding for the three MIPS instruction formats:
43
44**R-type format** (register operations):
45- Bits 31-26: opcode (0x00 for R-type)
46- Bits 25-21: rs (source register 1)
47- Bits 20-16: rt (source register 2)
48- Bits 15-11: rd (destination register)
49- Bits 10-6: shamt (shift amount)
50- Bits 5-0: funct (function code)
51
52**I-type format** (immediate operations):
53- Bits 31-26: opcode
54- Bits 25-21: rs
55- Bits 20-16: rt
56- Bits 15-0: immediate value
57
58**J-type format** (jump operations):
59- Bits 31-26: opcode
60- Bits 25-0: target address
61
62### Phase 3: Essential Instructions First
63
64Implement instructions in priority order based on typical program needs:
65
66**High Priority (implement first):**
67- Arithmetic: ADD, ADDU, ADDI, ADDIU, SUB, SUBU
68- Logical: AND, ANDI, OR, ORI, XOR, NOR
69- Shifts: SLL, SRL, SRA, SLLV, SRLV, SRAV
70- Comparison: SLT, SLTI, SLTU, SLTIU
71- Memory: LW, SW, LB, LBU, SB, LH, LHU, SH
72- Branches: BEQ, BNE, BGTZ, BLEZ, BLTZ, BGEZ
73- Jumps: J, JAL, JR, JALR
74- Load: LUI
75
76**Medium Priority:**
77- Multiply/Divide: MULT, MULTU, DIV, DIVU, MFHI, MFLO, MTHI, MTLO
78
79**Lower Priority:**
80- Coprocessor instructions (if needed)
81- Floating point (if needed)
82
83### Phase 4: Syscall Handler
84
85Implement system call interface based on the target environment:
86
871. Detect SYSCALL instruction
882. Read syscall number from register (typically $v0 or $2)
893. Read arguments from registers ($a0-$a3 or $4-$7)
904. Execute syscall and set return value in $v0
91
92Common syscalls to implement:
93- read (file descriptor, buffer, count)
94- write (file descriptor, buffer, count)
95- open (path, flags, mode)
96- close (file descriptor)
97- lseek (file descriptor, offset, whence)
98- exit (status code)
99
100### Phase 5: I/O and File System
101
102For programs requiring file access:
103- Implement file descriptor table
104- Handle standard streams (stdin=0, stdout=1, stderr=2)
105- Support opening/reading external files (e.g., data files)
106- Handle output file creation (e.g., frame buffers, results)
107
108## Verification Strategies
109
110### Incremental Testing
111
112Test after each implementation phase:
113
1141. **ELF loader test**: Verify entry point and memory layout match expected values
1152. **Instruction test**: Create simple test sequences for each instruction group
1163. **Syscall test**: Test each syscall with known inputs/outputs
1174. **Integration test**: Run actual target binary
118
119### Debugging Techniques
120
121- Add instruction tracing (PC, instruction, register changes)
122- Log syscall invocations with arguments
123- Verify memory reads/writes at expected addresses
124- Compare register state against expected values at checkpoints
125
126### Common Validation Points
127
128- Entry point address matches ELF header
129- Stack pointer initialized correctly
130- Memory segments loaded at correct addresses
131- Register $0 always reads as zero
132- Signed vs unsigned operations handled correctly
133- Branch delay slots handled (if applicable to target)
134
135## Common Pitfalls
136
137### Analysis Paralysis
138**Problem**: Spending too much time understanding every detail before writing code.
139**Solution**: Start implementation after understanding ELF basics, entry point, and syscall numbers. Iterate and learn through building.
140
141### Missing Endianness Handling
142**Problem**: Incorrect byte ordering when loading instructions or data.
143**Solution**: Check ELF header for endianness flag and apply consistently when reading multi-byte values.
144
145### Register Zero Hardwiring
146**Problem**: Allowing writes to register $0 to persist.
147**Solution**: Always return 0 when reading $0, or ignore writes to $0.
148
149### Sign Extension Errors
150**Problem**: Incorrect sign extension for immediate values or load operations.
151**Solution**: Carefully distinguish signed vs unsigned operations. LB sign-extends, LBU zero-extends.
152
153### Branch/Jump Address Calculation
154**Problem**: Incorrect target address computation.
155**Solution**:
156- Branches: PC + 4 + (sign-extended offset << 2)
157- Jumps: (PC & 0xF0000000) | (target << 2)
158
159### Memory Alignment
160**Problem**: Unaligned memory access causing errors.
161**Solution**: Either enforce alignment or handle unaligned access appropriately for the target.
162
163### Syscall Return Values
164**Problem**: Not setting error codes or return values correctly.
165**Solution**: Set $v0 for return value, handle error cases consistently.
166
167### Incomplete Instruction Coverage
168**Problem**: Missing instructions causing silent failures.
169**Solution**: Log unimplemented instructions with their encodings for debugging.
170
171## Time Management Strategy
172
173For complex interpreter tasks:
174
1751. **First 25% of time**: ELF loading + basic instruction loop skeleton
1762. **Next 25% of time**: Core arithmetic/logic/memory instructions
1773. **Next 25% of time**: Branches, jumps, and syscalls
1784. **Final 25% of time**: Testing, debugging, edge cases
179
180Prioritize a running (even incomplete) interpreter over comprehensive analysis. A partial implementation that executes provides more debugging information than complete analysis without code.