Step 06 · Emulation foundations

What an emulator is

Before any of the detail: what problem is an emulator solving, what are the two ways to solve it, and which one is KytyPS5? Answer that and the rest of the project stops looking arbitrary.

Before this page: Instructions, addresses and the guest. View the learning route.

Read

An emulator makes one computer behave like another, so that software built for the second runs on the first. That is the whole idea. Everything else is engineering trade-offs about where you cut.

The simplest possible emulator

Imagine a toy machine with four registers and a handful of instructions. An emulator for it is a loop:

uint8_t memory[65536]{}; // load the toy program before executing
uint32_t reg[4]{};
uint16_t pc = 0;
auto fetch = [&]() { return memory[pc++]; }; // 16-bit PC wraps

for (;;) {
    switch (fetch()) {
        case 0x01: { // LOAD reg, little-endian imm32
            const uint8_t r = fetch();
            if (r >= 4) return; // invalid register
            uint32_t value = 0;
            for (unsigned byte = 0; byte < 4; ++byte)
                value |= uint32_t(fetch()) << (8 * byte);
            reg[r] = value;
            break;
        }
        case 0x02: { // ADD dst, src
            const uint8_t d = fetch(), s = fetch();
            if (d >= 4 || s >= 4) return;
            reg[d] += reg[s];
            break;
        }
        case 0xFF: return; // HALT
        default: return;  // invalid opcode
    }
}

This illustrates an interpreter: fetch an instruction, decode it, perform its operation, repeat. A complete implementation also defines invalid instructions, exceptions and every other part of the guest machine.

The loop adds decoding and dispatch work to every guest instruction. Its cost depends on the interpreter, host and workload; CPU clock rates alone cannot predict emulation speed. Caching translated code or executing compatible guest instructions natively can avoid much of that repeated work.

Execution strategies and HLE

StrategyHow it worksCostUsed by
Interpretation Decode and execute one guest instruction at a time, every time. Repeated decode/dispatch overhead. Instruction stepping is straightforward, but accuracy still depends on the implementation. Reference implementations, fallback paths and systems whose workloads fit the host’s available performance.
Dynamic recompilation (JIT) Translate a block of guest instructions into host instructions once, cache the result, then jump into the cached block whenever that address is reached again. Amortises translation cost. Self-modifying code, precise exceptions and flags require careful handling. Common in emulators for systems with a different guest CPU, including PS2 and GameCube.
Native execution Guest and host share an instruction set, so guest code is simply mapped into memory and jumped to. Avoids general CPU translation. Host feature gaps, ABI crossings, services and faults still have costs. KytyPS5 on a compatible x86-64 execution environment.
High-level emulation (HLE) Not a CPU strategy but a library strategy: instead of running the console's operating system, reimplement what its functions do in native code. Performance varies. Correctness depends on matching guest-visible results, side effects, ordering and timing. KytyPS5’s service replacements; graphics also requires packet interpretation and shader translation.
Jargon · LLE versus HLE

Low-level emulation (LLE) reproduces lower-level hardware interfaces, often allowing original firmware or libraries to execute. It can preserve behavior that an HLE replacement omits, but neither accuracy nor speed follows from the label alone.

High-level emulation (HLE) replaces those libraries with reimplementations. It can avoid executing those system-library binaries, but each replacement must match the guest-visible contract. A missing function can fail immediately or fall back to a logging zero-return stub; silent incorrect results can cause failures much later. HLE does not by itself establish compatibility.

KytyPS5 primarily replaces system services with HLE. Its README says no external low-level emulation modules are required. The loader can still execute a game-supplied /app0/sce_module/libc.prx; “HLE” does not mean every instruction outside the main executable is replaced.

What an emulator must actually reproduce

A CPU is only one of the things a program depends on. To run an unmodified console game you must reproduce all of:

  1. The instruction set — so the code executes at all.
  2. The calling convention — so a function's arguments arrive where it expects them.
  3. The memory layout — so pointers, allocations and alignment assumptions hold.
  4. The operating system's services — threads, files, memory, timers, networking, audio, input.
  5. The GPU — including its command format, its register set, its memory layouts and its shader instruction set.
  6. The timing model — vertical blank, frame pacing, audio clocks. Games are full of code that assumes 60 Hz.

Most ordinary guest CPU instructions execute directly in a compatible x86-64 environment. Targeted instruction compatibility is still necessary; the remaining responsibilities account for most of this project.

Learn more · emulation fundamentals

You cannot emulate what you do not understand. Here is the PlayStation 5, described in the terms that matter to an emulator author rather than to a shopper.

The hardware

ComponentWhat it isEmulation consequence
CPU8-core AMD Zen 2, x86-64, 16 hardware threads, ~3.5 GHz.Shares the x86-64 ISA with an x86-64 PC; individual instruction features still need checking.
GPUAMD RDNA 2, 36 compute units, custom. Programmed through a command-packet interface, not a driver API.Must be modelled in software: packet parser, register file, and a shader compiler from RDNA 2 to SPIR-V.
Memory16 GB GDDR6, unified — CPU and GPU address the same physical memory.Host Vulkan resources can have separate backing or layouts. Kyty must keep CPU-visible guest bytes and cached GPU resources coherent, including on unified-memory hosts.
Page size16 KB, not the 4 KB PCs use.Guest VM APIs use 16 KB granularity where required. The renderer’s tracker uses 4 KB pages; one guest page spans four of them.
StorageCustom NVMe with hardware decompression.Implemented through file I/O, asynchronous requests and supported decompression paths. Ordering, completion and error behavior matter alongside throughput.
Audio"Tempest" 3D audio engine plus a fixed-function decode block (AJM).AJM takes batched decode jobs, so Kyty parses the job format and routes to FFmpeg / LibAtrac9.

The software stack

The PS5's operating system is a descendant of FreeBSD. That heritage shows everywhere in the API surface:

Game / engineguestUnreal, Unity, or an in-house engine, compiled to x86-64 with Sony's Clang-based toolchain.
↓ calls
System modules (.prx / .sprx)guestlibkernel, Graphics5Driver (graphics), libSceAudioOut, libSceNet, libSceSaveData… Dozens of shared libraries, each exporting hashed symbols.
↓ syscalls
Kernel (FreeBSD-derived)guestProcesses, threads (pthreads), kqueue-style event queues, mmap-style memory, a Unix-like file system.
↓ writes packets to
GPU command processorguestReads PM4 packets straight out of memory. There is no "driver" between the game and the hardware in the PC sense.

KytyPS5 replaces the middle two layers wholesale with native C++, and models the fourth in software. The game executes natively, with relocations and targeted compatibility patches applied by the loader.

Naming you will meet

Prospero is Sony's internal codename for the PS5, and it is the namespace name used throughout Kyty's graphics code (Libs::Graphics::Prospero). AGC is the PS5's low-level graphics API, referred to as Gen5 in the source. GNM was its PS4 predecessor, and some symbol names still carry that lineage.

What a game file actually is

A PS5 title directory contains, at minimum:

MyGame/
├─ eboot.bin              the main executable — a SELF (Signed ELF)
├─ sce_sys/
│   ├─ param.json         title ID, version, memory budget, capabilities
│   ├─ icon0.png
│   └─ …
├─ *.prx / *.sprx         optional extra modules shipped with the game
└─ (game data files)

Kyty reads param.json for the title ID and the flexible-memory budget, mounts the directory as /app0, then loads eboot.bin.

src/emulator.cppsrc/loader/systemContent.cpp
Learn more · console internals
  • Article
    Architecture of Consoles — Rodrigo CopettiDeep, readable hardware analyses of essentially every console. Read the PlayStation entries to see how the design lineage arrives at the PS5. Widely recommended as the first thing to read before emulating any system.
  • Video
    Console Hacking 2016 — "PS4: PC Master Race" (33C3)Hector Martin and fail0verflow on PS4 hardware and how they reverse-engineered it far enough to run Linux with 3D acceleration. The closest thing to a primary source on how this hardware actually differs from a PC. Also on YouTube, with slides.
  • Article
    fail0verflow blogWrite-ups behind the talks, including the liner notes for each Console Hacking presentation.
Watch

The whole machine in one picture. Every later topic is one of these boxes opened up — come back to this animation whenever you lose your bearings.

Two ways to build an emulator, side by side. Step it and watch what each approach commits you to writing.

Do

You do not need the emulator running for this one. It is about getting the vocabulary straight, because everything after it assumes these words mean something specific.

Checkpoint

Where this goes next

The single most consequential design decision follows immediately: there is no CPU emulator at all. That is the next topic, and it explains the shape of everything else.