A high-performance MIPS assembly language implementation of core game logic functions. This project demonstrates low-level systems programming, register optimization, and assembly language mastery for the MIPS architecture.
This project implements critical game engine functions in pure MIPS assembly, achieving production-grade performance with minimal instruction overhead. The implementation showcases advanced assembly optimization techniques and demonstrates deep understanding of the MIPS instruction set architecture (ISA).
Three core functions implemented in MIPS assembly:
- printBoard: Efficient board rendering to output
- zeroOut: Optimized memory initialization
- place_tile: Validated tile placement with boundary checking
These functions form the foundation of a high-performance game system running on MIPS processors.
├── hw5.asm # Main assembly implementation
├── skeleton.asm # Reference template
├── tests/ # Comprehensive test suite
│ ├── test_*.asm # Individual test cases
│ └── outputs/ # Expected output validation
├── MarsFall2020.jar # MARS MIPS simulator
└── README.md # This file
MIPS (Microprocessor without Interlocked Pipeline Stages) is a RISC architecture featuring:
- 32-bit word size (classic MIPS32)
- Simple instruction set (~50 core instructions)
- Register-based operation (32 registers, $0-$31)
- Load/Store architecture (memory accessed via specific instructions)
- Efficient pipelining (predictable performance)
| Register | Name | Purpose | Convention |
|---|---|---|---|
| $zero | Zero | Always 0 | N/A |
| $v0-$v1 | Values | Return values | Caller-saved |
| $a0-$a3 | Arguments | Function arguments | Caller-saved |
| $t0-$t9 | Temp | Temporary work | Caller-saved |
| $s0-$s7 | Saved | Preserved values | Callee-saved |
| $sp | Stack Ptr | Stack pointer | Callee-saved |
| $ra | Return Addr | Return address | Callee-saved |
| $gp | Global Ptr | Global pointer | Callee-saved |
Key Rules:
- Caller-Saved: Function may modify; caller must preserve if needed
- Callee-Saved: Function must preserve; use
sw/lwif modified - Function arguments in $a0-$a3
- Return values in $v0-$v1
- Return via
jr $ra
Renders the game board to standard output with optimal performance.
# Function: printBoard
# Arguments: None (uses global variables)
# Returns: void
# Uses: board (char[]), board_width (int), board_height (int)
Functionality:
- Iterates through board array in row-major order
- Prints ASCII representation of each cell (0-9)
- Preserves all caller-saved registers
- Zero return value
Performance Target: <300 instructions for 5×5 board
Implementation Strategy:
1. Calculate total cells = width × height
2. Initialize loop counter and base address
3. Load byte from memory
4. Convert to ASCII ('0' for 0, '1' for 1, etc.)
5. Print via syscall
6. Increment address and counter
7. Branch if more cells remain
Clears board memory with optimized write performance.
# Function: zeroOut
# Arguments: None (uses global variables)
# Returns: void
# Uses: board (char[]), board_width (int), board_height (int)
Functionality:
- Iterates through entire board array
- Sets each byte to 0 (memory initialization)
- Direct memory write operation
- Preserves caller-saved registers
Performance Target: <200 instructions for 5×5 board
Implementation Strategy:
1. Load board base address from global
2. Load dimension globals (width, height)
3. Calculate total bytes = width × height
4. Initialize loop counter
5. Store zero byte (sb $zero, 0($t0))
6. Increment address pointer
7. Decrement counter
8. Loop until all bytes cleared
Places game tiles with comprehensive validation.
# Function: place_tile
# Arguments:
# $a0 - row (0 to height-1)
# $a1 - col (0 to width-1)
# $a2 - value (0-9)
#
# Returns:
# $v0 - Status code
# 0 = Success
# 1 = Cell occupied
# 2 = Out of bounds
#
# Uses: board (char[]), board_width (int), board_height (int)
Functionality:
- Validate row coordinate: 0 ≤ row < height
- Validate column coordinate: 0 ≤ col < width
- Check cell occupancy (non-zero = occupied)
- Place tile if all validations pass
- Return appropriate status code
Performance Target: <50 instructions for success case
Implementation Strategy:
1. Check row boundary: if row >= height, return 2
2. Check col boundary: if col >= width, return 2
3. Calculate offset = row × width + col
4. Load byte at offset
5. Check if occupied: if non-zero, return 1
6. Write value to address
7. Return 0 (success)
# Load globals
la $t0, board # Load address of board array
lw $t1, board_width # Load width value
lw $t2, board_height # Load height value
# Array indexing (row-major)
# offset = row * width + col
mul $t3, $a0, $t1 # row * width
add $t3, $t3, $a1 # + col
add $t4, $t0, $t3 # base + offset
# Byte access
lb $t5, 0($t4) # Load byte
sb $t5, 0($t4) # Store byte
# Count-up loop pattern
li $t0, 0 # counter = 0
loop_start:
# ... loop body ...
addi $t0, $t0, 1 # counter++
blt $t0, $t1, loop_start # if counter < limit, continue
# Count-down loop pattern (slightly more efficient)
li $t0, 100 # counter = size
loop:
# ... loop body ...
addi $t0, $t0, -1 # counter--
bne $t0, $zero, loop # if counter != 0, continue
# Check bounds
bge $a0, $t1, out_of_bounds # if row >= height
blt $a0, $zero, out_of_bounds # if row < 0
# Check occupancy
bne $t0, $zero, occupied # if cell != 0
beq $t0, $zero, empty # if cell == 0
# Print character (ASCII value in $a0)
li $v0, 11
int 10
# Print integer (value in $a0)
li $v0, 1
int 10
# Print string (address in $a0, null-terminated)
li $v0, 4
int 10
# Exit program (code in $a0)
li $v0, 10
int 10
# Launch MARS simulator
java -jar MarsFall2020.jar
# Load assembly file and run with debugger# Test individual file
java -jar MarsFall2020.jar tests/test_print_01.asm --noGui
# Run with output capture
java -jar MarsFall2020.jar test.asm --noGui > output.txt 2>&1- Load test file in MARS GUI
- Navigate to: Tools → Instruction Statistics
- Click "Connect to MIPS"
- Run your code
- Review instruction count (target: ≤500,000)
printBoard:
# Calculate total cells
lw $t0, board_width
lw $t1, board_height
mul $t0, $t0, $t1 # total = width * height
# Load board address
la $t1, board
li $t2, 0 # counter = 0
print_loop:
# Load byte
add $t3, $t1, $t2
lb $a0, 0($t3)
# Convert to ASCII
addi $a0, $a0, 48 # Add 48 to convert 0-9 to ASCII
# Print
li $v0, 11
int 10
# Continue
addi $t2, $t2, 1
blt $t2, $t0, print_loop
jr $ra
For row-major board (width=W, height=H):
Cell at (row, col) → offset = row × W + col
Memory address = board_base + offset
Each function tested independently:
- Boundary Tests: Min/max values
- Edge Cases: Empty boards, full boards
- Functionality: Normal operations
- Stress Tests: Large boards, repeated operations
- ✅ Variable board dimensions
- ✅ All 10 tile values (0-9)
- ✅ Boundary conditions
- ✅ Multiple sequential operations
- ✅ Error conditions
Target instruction counts on MARS simulator:
- printBoard(5×5): ~300 instructions
- zeroOut(5×5): ~200 instructions
- place_tile(success): ~50 instructions
- place_tile(out_of_bounds): ~20 instructions
- place_tile(occupied): ~40 instructions
- Set Breakpoints: Click line number in editor
- Single-Step: Execute one instruction at a time
- Register Watch: Monitor $t0-$t9, $a0-$a3
- Memory Inspector: View board array contents
- Stack Trace: Follow function calls
# Add syscalls for debugging
li $v0, 1 # Print integer
move $a0, $t0
int 10
- Use
addifor small constants (faster thanadd) - Eliminate unnecessary
moveinstructions - Combine related operations
- Use register variables for frequently accessed values
- Avoid register dependencies on consecutive instructions
- Structure code for branch prediction success
- Minimize branch instructions
- Use delayed branch slots effectively
- Load globals once, reuse values
- Cache frequently accessed values in registers
- Use
sb/lbonly when necessary (slower than word operations)
❌ Problem: Not preserving $ra for nested calls
✅ Solution: Push $ra to stack before calling other functions
❌ Problem: Off-by-one errors in loops
✅ Solution: Double-check loop termination: blt vs ble
❌ Problem: Incorrect address calculation
✅ Solution: Verify: offset = row * width + col (row-major)
❌ Problem: Forgetting return instruction
✅ Solution: Always end with jr $ra
❌ Problem: Register conflicts
✅ Solution: Track which registers are in use
MIPS assembly skills demonstrated here are used in:
- Embedded Systems: MIPS processors in IoT devices
- Network Equipment: Cisco routers (IOS runs on MIPS)
- Game Consoles: PlayStation, Nintendo systems
- Mobile: MIPS processors in some phones
- Education: Teaching low-level programming concepts
This project demonstrates mastery of:
- Low-level memory manipulation
- Assembly language programming
- Register allocation and optimization
- Function calling conventions
- Control flow in limited instruction sets
- Performance analysis and optimization
- Debugging at the instruction level
Efficient use of limited registers:
- Identify hot variables (used frequently)
- Allocate callee-saved registers for hot variables
- Use caller-saved registers for temporary values
- Minimize register spills to memory
Modern MIPS processors use branch prediction:
- Loops typically branch back (high prediction accuracy)
- Conditional branches may miss prediction
- Structure code to minimize mispredictions
Maximize pipeline efficiency:
- Avoid dependent instructions on consecutive lines
- Fill delay slots when possible
- Consider cache locality for memory access
- MARS Help: Built-in documentation
- MIPS ISA Reference: Course materials
- MIPS Programming Guides: Classic computer architecture texts
- Online MIPS Tutorials: Various educational sites
- Clear Comments: Explain non-obvious logic
- Consistent Formatting: Proper indentation
- Meaningful Labels: Self-documenting code
- Register Tracking: Document register usage
- Performance Notes: Mark optimization opportunities
MIT License - Open source, educational and professional use
Portfolio project demonstrating assembly language expertise, low-level optimization, and MIPS architecture mastery.