Claude-skill-registry-data make-doom-for-mips
Guidance for cross-compiling complex C programs (like Doom) to run on custom MIPS virtual machines or emulators. This skill should be used when tasked with building software for non-standard MIPS targets that use custom syscall interfaces, require freestanding compilation (-nostdlib), or involve running binaries in JavaScript/custom VM environments.
git clone https://github.com/majiayu000/claude-skill-registry-data
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry-data "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/make-doom-for-mips" ~/.claude/skills/majiayu000-claude-skill-registry-data-make-doom-for-mips && rm -rf "$T"
data/make-doom-for-mips/SKILL.mdCross-Compiling Complex C Programs for Custom MIPS VMs
Overview
This skill provides guidance for cross-compiling complex C codebases (such as Doom) to run on custom MIPS virtual machines or emulators. The primary challenge is bridging the gap between standard C library expectations and a custom runtime environment with its own syscall interface, memory layout, and instruction support.
Critical First Steps: Understanding the Target Environment
Before writing any code, thoroughly analyze the target VM to understand its requirements:
1. Analyze the VM's Syscall Interface
Read the VM source code to extract the exact syscall interface:
- Identify syscall numbers (e.g., SYS_read=0, SYS_write=1, etc.)
- Understand the calling convention (which registers hold arguments and return values)
- Document all supported syscalls and their signatures
- Note any custom syscalls specific to the VM
2. Determine Endianness and ABI
Verify the target architecture details by examining the VM source:
- Endianness (little-endian vs big-endian MIPS)
- Floating-point support (hardware vs soft-float)
- Word size (32-bit vs 64-bit)
- ABI variant (o32, n32, n64)
3. Understand Memory Layout
From the VM source, extract:
- Expected entry point address
- Stack location and size
- Heap boundaries
- Text/data segment placement
- Any memory-mapped I/O regions
4. Identify Supported Instructions
Check for instruction limitations:
- Floating-point instruction support
- Privileged instructions
- Branch delay slot handling
- Any unimplemented instructions that will cause VM termination
Systematic Approach to Freestanding Compilation
Phase 1: Comprehensive Header Analysis
Before compiling, scan all source files to identify required headers:
# Find all included headers grep -rh '#include' src/ | sort -u
Create stub headers for ALL identified system headers upfront rather than reactively fixing one error at a time. Common headers needed for complex C programs:
,<stdio.h>
,<stdlib.h>
,<string.h><stdint.h>
,<limits.h>
,<stddef.h><stdarg.h>
,<ctype.h>
,<math.h><time.h>
,<errno.h>
,<assert.h><fcntl.h>
,<unistd.h>
,<sys/types.h><sys/stat.h>
Phase 2: Standard Library Implementation
Create a comprehensive stdlib replacement with COMPLETE implementations:
Critical functions that must work correctly (not return 0 or stub):
,printf
,fprintf
,sprintf
- Used for debug output and parsingsnprintf
,sscanf
- Used for configuration parsingfscanf
,malloc
,free
- Memory managementrealloc
,memcpy
,memset
,memmove
,strlen
,strcmp
- String/memory operationsstrcpy
,atoi
,atol
- Number parsingstrtol
,fopen
,fread
,fwrite
,fclose
,fseek
- File I/Oftell
Warning signs of incomplete implementations:
- Functions returning 0 unconditionally
- Empty function bodies
- "TODO" or stub comments
- Missing format specifier handling in printf-family functions
Phase 3: Linker Script Configuration
Create a linker script that matches the VM's expectations:
- Entry point symbol must match what the VM looks for
- Section addresses must align with VM memory layout
- Stack initialization must be included in startup code
- Verify
,.text
,.data
,.bss
placement.rodata
Verification Strategies
1. Syscall Verification
After implementing syscalls, verify each one works:
// Test write syscall const char *msg = "Syscall test\n"; write(1, msg, strlen(msg)); // Test read syscall char buf[100]; int n = read(0, buf, sizeof(buf)); // Test file operations int fd = open("/path/to/file", O_RDONLY); // etc.
2. Entry Point Verification
Confirm the entry point matches:
# Check the ELF entry point mips-*-readelf -h output.elf | grep Entry # Verify symbol is at expected address mips-*-nm output.elf | grep __start
3. Map File Analysis for Debugging
Generate and use map files to debug crashes:
# Generate map file during linking -Wl,-Map=output.map # When program terminates at specific PC, find the function: grep "0x40b7b0" output.map
4. Incremental Testing
Test components in isolation before full integration:
- Minimal "hello world" to verify basic execution
- Memory allocation test program
- File I/O test program
- Individual library function tests
- Finally, the full application
Common Pitfalls and Solutions
Pitfall 1: ABI Mismatch Warnings
Problem: Linking soft-float compiled code with hard-float libgcc produces warnings:
warning: linking soft-float module with hard-float module
Impact: Can cause runtime crashes when floating-point values are passed/returned.
Solution: Ensure consistent ABI across all compilation units:
-msoft-float -mfloat-abi=soft
Or provide soft-float libgcc, or implement soft-float routines.
Pitfall 2: Incomplete printf/scanf Implementation
Problem: Returning 0 or doing nothing in printf-family functions.
Impact: Configuration parsing fails silently; debug output disappears.
Solution: Implement at least these format specifiers:
,%d
,%i
,%u
,%x
- integers%X
- strings%s
- characters%c
- pointers%p- Width and precision modifiers
Pitfall 3: Missing Function Implementations
Problem: Compiler warnings about implicit function declarations.
Impact: Undefined behavior at runtime; usually crashes.
Solution: Treat ALL implicit declaration warnings as errors:
-Werror=implicit-function-declaration
Pitfall 4: Incorrect Syscall Numbers
Problem: Using standard Linux syscall numbers when VM uses different numbers.
Impact: Wrong operations executed; silent corruption or crashes.
Solution: Extract exact syscall numbers from VM source before implementation.
Pitfall 5: Premature Termination Without Investigation
Problem: Program terminates early; assuming it's due to missing data files.
Impact: Real bugs (unimplemented instructions, invalid memory access, failed syscalls) go undiagnosed.
Solution: Always investigate termination:
- Check the PC address in the map file
- Examine the VM's termination handling code
- Add instrumentation to log syscalls and critical operations
- Verify data files are accessible at expected paths
Pitfall 6: Reactive Header Creation
Problem: Creating stub headers one-by-one as compilation errors appear.
Impact: Slow iteration; easy to miss dependencies.
Solution: Analyze all includes upfront and create comprehensive stubs before first compile attempt.
Debugging Strategies
When the Binary Terminates Early
- Get the termination address from VM output
- Look up address in map file to identify the function
- Check for unimplemented instructions at that location
- Verify syscall was valid if termination is in syscall handler
- Check memory access if termination is in memory operation
When Output is Missing/Incorrect
- Verify write syscall reaches the correct output
- Check file descriptors are mapped correctly
- Trace printf calls to ensure they reach write syscall
- Verify buffer handling in I/O functions
When the Program Crashes Silently
- Add diagnostic output at key checkpoints
- Implement signal handlers if VM supports them
- Check stack overflow - increase stack size
- Verify heap operations - add malloc/free tracing
Compilation Flags Reference
Essential flags for freestanding MIPS compilation:
-nostdlib # No standard library -ffreestanding # Freestanding environment -mips32 # MIPS32 instruction set (or appropriate variant) -EL # Little-endian (or -EB for big-endian) -msoft-float # Software floating-point -mno-abicalls # No PIC/GOT (if VM doesn't support) -fno-pic # No position-independent code -static # Static linking -Wl,-Map=output.map # Generate map file for debugging