A high-accuracy PlayStation 1 emulation core library for .NET 8, providing complete hardware emulation, analysis tools, and JIT compilation capabilities.
Yaroze.Core is a comprehensive PS1 emulation library designed for accuracy, testability, and ease of integration. It provides both interpretation and JIT compilation of MIPS R3000A code, along with powerful static analysis tools for reverse engineering PS1 software.
- MIPS R3000A CPU - Complete instruction set (55+ instructions) with accurate delay slots, exceptions, and overflow handling
- GPU - 1MB VRAM, GP0/GP1 command processing, display control, CPU↔VRAM transfers
- CD-ROM - Full disc image support (ISO/BIN/CUE) with multi-track parsing and sector reading
- DMA Controller - 7 channels supporting burst, slice, and linked-list transfer modes
- Timers - 3 root counters with multiple clock sources and IRQ generation
- Interrupt Controller - Hardware interrupt routing with masking
- GTE (COP2) - Geometry Transformation Engine register interface
- Memory System - RAM, Scratchpad, BIOS, memory-mapped I/O with proper mirroring
- MIPS Disassembler - Full R3000A instruction set with register names and symbolic references
- Pseudo-C Decompiler - Converts assembly to readable C-like pseudocode
- Function Analyzer - Control flow analysis, function discovery, and call graph generation
- Cross-Reference Tracker - Tracks all calls, jumps, and branch targets
- Symbol Manager - Label and comment management with import/export
- Execution Tracing - Hook-based tracing for instruction and memory access analysis
- JIT Compiler - Compiles MIPS code to native x64 using .NET Expression Trees
- Lockstep Verification - Validates JIT output against interpreter for correctness
- Basic Block Scanner - Identifies compilation units for optimal performance
dotnet add package Yaroze.Coregit clone https://github.com/apfelteesaft/Yaroze.git
cd Yaroze
dotnet build src/Yaroze.Coreusing Yaroze.Core;
// Create emulator instance
var emulator = new Emulator();
// Load a PS-EXE file
emulator.LoadExeFromFile("GAME.EXE");
// Execute instructions
emulator.Step(); // Single step
emulator.StepN(1000); // Execute 1000 instructions
emulator.Run(); // Continuous execution (blocking)
// Access CPU state
uint pc = emulator.Cpu.Registers.PC;
uint v0 = emulator.Cpu.Registers.ReadGPR(2);
// Get execution statistics
var stats = emulator.GetStats();
Console.WriteLine($"Executed {stats.InstructionsExecuted} instructions");var emulator = new Emulator();
// Load disc image (supports .iso, .bin, .cue)
emulator.LoadDisc("game.bin");
// Access CD-ROM device
var cdrom = emulator.CdRom;var emulator = new Emulator();
// Access RAM directly
emulator.Bus.Ram.Write32(0x80000000, 0x12345678);
uint value = emulator.Bus.Ram.Read32(0x80000000);
// Write to memory via bus (handles all devices)
emulator.Bus.Write32(0x1F801810, 0xA0000000); // GPU GP0 commandusing Yaroze.Core.Disassembly;
using Yaroze.Core.Analysis;
var emulator = new Emulator();
emulator.LoadExeFromFile("GAME.EXE");
// Disassemble at address
var disasm = new MipsDisassembler();
uint instructionWord = emulator.Bus.Read32(0x80000000);
string assembly = disasm.Disassemble(0x80000000, instructionWord);
// Analyze functions
var analyzer = new FunctionAnalyzer(emulator.Bus.Ram);
analyzer.AnalyzeFrom(0x80000000);
foreach (var func in analyzer.GetAllFunctions())
{
Console.WriteLine($"Function at 0x{func.StartAddress:X8}");
Console.WriteLine($" Calls: {func.CallCount}");
}
// Decompile to pseudo-C
var decompiler = new PseudoCDecompiler(emulator.Bus.Ram.Data, 0x80000000);
string pseudoC = decompiler.DecompileFunction(0x80000000, 0x100);
Console.WriteLine(pseudoC);using Yaroze.Core.Interfaces;
class MyTracer : ITraceSink
{
public void TraceInstruction(uint pc, uint instruction, string? disassembly)
{
Console.WriteLine($"[{pc:X8}] {disassembly}");
}
public void TraceMemoryRead(uint address, uint value, int size)
{
Console.WriteLine($" Read [{address:X8}] = 0x{value:X}");
}
public void TraceMemoryWrite(uint address, uint value, int size)
{
Console.WriteLine($" Write [{address:X8}] = 0x{value:X}");
}
}
var emulator = new Emulator();
emulator.SetTraceSink(new MyTracer());
emulator.LoadExeFromFile("GAME.EXE");
emulator.StepN(10); // Trace first 10 instructions| Format | Extension | Description |
|---|---|---|
| PS-EXE | .exe, .psx |
PlayStation executables with header |
| BIN | .bin |
Raw CD-ROM images (2352 bytes/sector) |
| ISO | .iso |
ISO 9660 filesystem images (2048 bytes/sector) |
| CUE | .cue |
Cue sheet descriptors for multi-track discs |
Yaroze.Core/
├── CPU/ # MIPS R3000A CPU interpreter
│ ├── Cpu.cs # Main CPU implementation
│ ├── Registers.cs # Register file with delay slots
│ ├── Instruction.cs # Instruction decoding
│ ├── Coprocessor0.cs # System control coprocessor
│ └── Gte.cs # Geometry Transformation Engine
├── JIT/ # Just-In-Time compiler
│ ├── JitCompiler.cs # Expression tree based compiler
│ ├── BasicBlockScanner.cs
│ └── LockstepVerifier.cs
├── GPU/ # Graphics processing unit
│ └── Gpu.cs # VRAM, GP0/GP1 commands
├── CDROM/ # CD-ROM drive emulation
│ ├── CdRomDevice.cs # Drive controller
│ ├── DiscImage.cs # Disc image loader
│ └── CueSheet.cs # CUE file parser
├── DMA/ # DMA controller
│ └── DmaController.cs
├── Memory/ # Memory subsystem
│ ├── Bus.cs # Memory-mapped I/O bus
│ ├── Ram.cs # Main RAM
│ ├── Bios.cs # BIOS ROM
│ └── Scratchpad.cs # Fast scratchpad RAM
├── Timers/ # Root counters
│ └── Timer.cs
├── Interrupts/ # Interrupt controller
│ └── InterruptController.cs
├── Disassembly/ # Static analysis
│ ├── MipsDisassembler.cs
│ └── PseudoCDecompiler.cs
├── Analysis/ # Code analysis tools
│ ├── FunctionAnalyzer.cs
│ ├── CrossReferenceTracker.cs
│ └── SymbolManager.cs
└── Emulator.cs # Top-level emulator class
The library includes over 400 comprehensive unit and integration tests:
dotnet testTests cover:
- All 55+ MIPS R3000A instructions
- Exception handling and COP0 operations
- Load/branch delay slot behavior
- Memory operations and DMA transfers
- GPU command processing
- CD-ROM disc image parsing
- JIT compiler correctness (lockstep verification)
- Static analysis tools
- Load Delay Slots - Correctly implements MIPS load delay behavior where the loaded value is not available until after the next instruction
- Branch Delay Slots - Accurate handling of the instruction following a branch/jump
- Exception Precision - Proper exception timing, COP0 state management, and EPC calculation
- Overflow Detection - ADD/SUB/ADDI trigger overflow exceptions on signed overflow
- Lockstep Verification - JIT compiler output validated instruction-by-instruction against interpreter
- Clean Architecture - Core emulation logic independent of UI or application code
- Interface-Based -
IBusDeviceabstraction for memory-mapped devices - Testable - Comprehensive test coverage with deterministic execution
- Well-Documented - Implementation backed by PSX-SPX and MIPS R3000A specifications
- Interpreter Mode - ~1 cycle per instruction (variable timing not yet implemented)
- JIT Mode - Native code execution with verification overhead
- Memory Access - Direct array access for RAM, virtual dispatch for I/O devices
using Yaroze.Core.Interfaces;
public class CustomDevice : IBusDevice
{
public bool Contains(uint address) => address >= 0x1F802000 && address < 0x1F802100;
public uint Read32(uint address)
{
// Handle read
return 0;
}
public void Write32(uint address, uint value)
{
// Handle write
}
// Implement other IBusDevice methods...
}
var emulator = new Emulator();
emulator.Bus.AddDevice(new CustomDevice());var symbols = new SymbolManager();
// Add labels
symbols.AddLabel(0x80000000, "main");
symbols.AddLabel(0x80001000, "gameLoop");
// Add comments
symbols.AddComment(0x80000000, "Entry point");
// Export/Import
string json = symbols.ExportToJson();
SymbolManager.ImportFromJson(json);Legal Notice: PlayStation BIOS files are copyrighted by Sony and cannot be distributed with this library.
To use BIOS-dependent features, you must:
- Legally obtain a BIOS dump from your own PlayStation console
- Load it using the
LoadBios()method
The library provides BIOS interfaces but no BIOS data.
Contributions are welcome! Please ensure:
- All tests pass (
dotnet test) - New features include comprehensive tests
- Code follows existing architectural patterns
- Public APIs are documented with XML comments
Who tf needs Licensing Comrade?
- Martin "nocash" Korth - Comprehensive PSX-SPX documentation
- MIPS Technologies - MIPS R3000A architecture documentation
- PS1 Emulation Community - Collective reverse engineering knowledge
Philosophy: Accuracy over performance. Testability over cleverness. Documentation over assumptions.