# Yaroze.Core - PlayStation 1 Emulation Library A high-accuracy PlayStation 1 emulation core library for .NET 8, providing complete hardware emulation, analysis tools, and JIT compilation capabilities. ## Overview 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. ## Features ### Hardware Emulation - **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 ### Analysis & Debugging - **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 ### Performance - **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 ## Installation ### NuGet Package ```bash dotnet add package Yaroze.Core ``` ### From Source ```bash git clone https://github.com/apfelteesaft/Yaroze.git cd Yaroze dotnet build src/Yaroze.Core ``` ## Quick Start ### Basic Emulation ```csharp using 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"); ``` ### Loading CD-ROM Images ```csharp var emulator = new Emulator(); // Load disc image (supports .iso, .bin, .cue) emulator.LoadDisc("game.bin"); // Access CD-ROM device var cdrom = emulator.CdRom; ``` ### Memory Access ```csharp 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 command ``` ### Disassembly & Analysis ```csharp using 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); ``` ### Execution Tracing ```csharp 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 ``` ## Supported File Formats | 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 | ## Architecture ``` 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 ``` ## Testing The library includes over **400 comprehensive unit and integration tests**: ```bash dotnet test ``` Tests 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 ## Technical Details ### Accuracy Features - **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 ### Design Principles - **Clean Architecture** - Core emulation logic independent of UI or application code - **Interface-Based** - `IBusDevice` abstraction for memory-mapped devices - **Testable** - Comprehensive test coverage with deterministic execution - **Well-Documented** - Implementation backed by PSX-SPX and MIPS R3000A specifications ### Performance Characteristics - **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 ## Advanced Usage ### Custom Memory-Mapped Devices ```csharp 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()); ``` ### Symbol Management ```csharp 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); ``` ## BIOS Requirement **Legal Notice**: PlayStation BIOS files are copyrighted by Sony and cannot be distributed with this library. To use BIOS-dependent features, you must: 1. Legally obtain a BIOS dump from your own PlayStation console 2. Load it using the `LoadBios()` method The library provides BIOS interfaces but no BIOS data. ## Contributing 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 ## License Who tf needs Licensing Comrade? ## Acknowledgments - **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.*