Skip to content

ApfelTeeSaft/Yaroze.NET

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

dotnet add package Yaroze.Core

From Source

git clone https://github.com/apfelteesaft/Yaroze.git
cd Yaroze
dotnet build src/Yaroze.Core

Quick Start

Basic Emulation

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

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

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

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

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:

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

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

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.

About

A PS1 JIT C# Core

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages