
r/EmuDev

Building “EZPU” — a tiny 4-bit fantasy computer to teach emulator development fundamentals (TypeScript)
Hi everyone,
I’ve started working on a small educational project called EZPU, a deliberately tiny “fantasy computer” designed as an introduction to emulator development fundamentals rather than a serious fantasy console.
The goal is to create something small enough that a beginner (like me !) could realistically understand the entire stack:
- CPU architecture
- memory layout
- fetch/decode/execute loop
- assembly language
- assembler
- eventually a tiny compiler
…without drowning in complexity.
Current design constraints are intentionally absurdly small:
- 4-bit architecture
- 16 banks × 16 addresses
- 4 general purpose registers
- fixed-width 4-nibble instructions
- 4×4 monochrome framebuffer
- no stack
- no pointers
- no interrupts
- no hidden flags register
The idea is to make the emulator code read almost like a textbook.
The emulator itself is being written in TypeScript with a very explicit architecture:
MemoryCPUDisplay- classic
switch(opcode)decoder
I’m intentionally avoiding “clever” abstractions and trying to keep everything visible and educational.
The immediate goal is simply:
“Can someone learn emulator fundamentals from this project?”
I’d really love feedback from experienced emudev people on:
- ISA design mistakes
- educational pitfalls
- missing fundamental instructions
- architecture decisions
- things that become painful later if not designed early
Especially interested in whether the “extreme simplicity” approach actually sounds useful from a teaching perspective.
Game Boy interrupt_time Blargg test
I'm trying to pass the Blargg tests for my Game Boy emulator, how do I reed this?
I will also share the other tests I've tried and debugged
I built an 8-bit CPU emulator in Python from scratch with a custom ISA. Here’s a Pac-Man ghost demo showing interrupts and sprite flipping.
I’ve been working on a personal project to learn computer architecture, assembly, and low-level systems design by building a complete 8-bit CPU emulator from scratch in Python.
The demo GIF shows the load and execution of a simple animation program written on custom assembly code, using interrupts and two sprites with horizontal flipping.
Current specs:
- 8-bit CPU architecture
- 4 general-purpose registers (A, B, C, D, and 16-bit DC register pair)
- 64KB RAM
- Memory-mapped 256x192 2bpp framebuffer
- Custom ISA with ALU, Load/Store, Flow Control, and Special instruction groups
- Interrupts (IRQ/NMI), stack pointer, branches, subroutines
- Clock speed: ~1.2 MHz (throttled via cycle batching and micro-sleeps)
- ~60Hz framebuffer refresh via Tkinter, synchronized to cycle batches
Source code + assembly examples: https://github.com/mcurzi/custom-8bit-cpu
Any feedback is welcome, especially on the ISA design and architecture decisions.
Gecko, a new GameCube and Wii emulator, is now public!
I'm here to present "Gecko" a new, cross-platform and modern GameCube and Wii emulator written in Rust! It's been a work-in-progress project for a while now and I'm extremely happy to finally share it publicly :)
In case you don't care about my ramblings, here's a link to the GitHub repository!
Many games already work extremely well, but it's very far from being on Dolphin's level! GameCube games tend to work better than Wii games currently, but I know of at least one game that is perfectly playable even on the Wii. More to this a bit later!
Here are some highlighted features:
- GameCube & Wii support
- Cranelift based JIT recompiler (PPC, DSP, vertex parser)
- WGPU based renderer supporting all major platforms
- Modular audio backend
- ISO, RVZ, ZIP support
- Frame pacing
- and much more!
Gecko aims to offer a playable and accurate experience for the average user. However, Gecko is also tailored to reverse engineers and homebrew developers. It's packed with tons of goodies that aren't mentioned in the above list! To name a few more (if you're not interested skip ahead!):
- Lua scripting
- An advanced yet beautiful debugger
- Rich logging system
- Symbol parsing from ELFs and IDA Pro databases
- RenderDoc captures with tons of debug markers
- Multitool to decode/encode IPLs, extract DVD filesystems, disassemble PPC/DSP code
- Recording audio to .wav files
- Built-in diagnostics for JIT and GX (GPU)
The emulator runs on all major platforms, it has been tested on: Windows, Linux, macOS and the browser (incl. the debugger). Here's also an old YouTube video of it (poorly) running on an iPad: https://www.youtube.com/watch?v=ACW36TXlUhM, because why not?
I also automated screenshot gathering over a selection of games, these are hosted here (mainly used to catch regressions/improvements, but also useful to gauge compatibility):
I'd be more than happy to see people try it out, share their experience and perhaps even contribute!!
To finish up the post I'd like to also share the "Why?". Why does this emulator even exist? I've always been very interested in low level stuff and figured I could do something to make 12yo me proud: Create an emulator that can play my childhood favorite game (Final Fantasy Crystal Chronicles: The Crystal Bearers on the Wii). I ended up going on a multi-year journey learning about emulator development, with the goal to improve my skills and ultimately do Wii.
I mentioned earlier that a game is already perfectly playable on the Wii... That is my childhood game, so to me, this project is already a huge success despite it not being anywhere as mature as Dolphin! :)
(First C++ Project) RISC-V CPU simulator | Feedback
EDIT: Comment claims its AI with no proof -> I prove its not AI -> I get downvoted. Classic reddit.
More Proof its not AI:
Check out the repo at this commit, at this point the structure of my code was quite bad, as you'd expect from a beginner.
- The fetch/decode/execute loop was not inside the CPU class but inside
main.cpp, furthermore thefetchfunction was also in main when it should be inside the CPU class.- You can see how
fetch,decode,executeandSteptook arguments whereas it would be unnecessary if they were a part of the CPU class so they had access to the internal state - I was using
main.cppto write temporary tests before I added an actual testing framework (gtest).
- You can see how
- In
execute.cpp, theexecutefunction took the CPU class and Memory itself as arguments when it should just be a part of the CPU class so it can access the internal state (as is in the latest version of the repo).- The switch statement, although at this point only had 3 instructions, was going to be wildly repetitive, I was reading/writing operands from the register file in each case instead of once in the beginning and end.
- In
decode.cpp, theextract_fieldsfunction took boolean values to determine which fields to extract but in the latest version there are separate functions for each instruction type.- Confusing variable names like
instruction.instruction.
- Confusing variable names like
Hi all,
I built my first C++ project (second ever project) and would appreciate honest feedback. Here's the GitHub.
CPU simulator
- An instruction set simulator that simulates a RISC-V architecture CPU
- Object-oriented design
- Implemented all base RV32I instructions
- Implemented Multiply/Divide instructions
- GoogleTest suite
I'm new to programming in general and only starting learning a few months ago (CS50P, CS50x). So I'd love any feedback on how I can improve my code and become a better (c++) programmer.
I'd also love to hear suggestions on what I should add, more extensions? Test against official RISC-V tests?
Thank you!
I built an open-source SaaS that compiles TypeScript into Z80/6502 Assembly for GameBoy and NES natively in the browser.
Hi folks,
Writing homebrew games for the GameBoy or Mega Drive usually requires dealing with pure Assembly or very specific C toolchains. I wanted to see if I could bring modern web dev DX (Developer Experience) to retro consoles.
I built RE-trolab, a multi-stage compiler that runs 100% locally in your browser.
The Architecture:
- You write strict TypeScript (AssemblyScript) or Lua in a Monaco editor on the web.
- The core engine (written in Rust and compiled to WebAssembly) parses the AST (Abstract Syntax Tree).
- It acts as a transpiler/compiler, emitting pure Z80 (for GameBoy/Master System) or 6502 (for NES) instructions.
- It outputs the .gb or .nes ROM directly to a web-based emulator running right next to your code.
The best part? Since the Rust compiler runs in WASM, there are zero server costs to host this IDE. It also has a decentralized package manager for assets/sprites.
Is anyone here working with TS to Assembly translation? Would love feedback on the memory mapping approach.
Just putting the finishing touches to my Commodore C64 emulator
My Commodore 64 emulator written in C# (.Net 8.0) using SDL2, ImGui & OpenGL for display/input/audio. Key features include:
- Cycle-driven CPU/VIC/CIA integration
- VIC raster stepping with bus-steal/stall accounting
- SID synthesis with MODE/VOL semantics
- CIA timer/ICR/TOD handling, serial shift behaviour, and NMI/IRQ paths
- Keyboard and SDL-compatible game controller joystick input
- IEC + virtual 1541 D64 file loading support, including selected command/status and direct block-access operations
- TAP cassette pulse playback with motor/sense/read behaviour
- Host PRG/T64/TAP/D64 loading and PRG saving, including ImGui window for bundled software
It is a cycle-accurate MOS 6510 CPU emulator with a full instruction set including all documented and undocumented opcodes.
I did most of the heavy lifting myself but in the spirit of open honesty I also used AI when confronted with some display & sound timing issues. I also used AI to do most of the UI polishing since I was keen to get it closed out... it's been a long journey.
ViceSharp update: it boots, renders, takes input, and is now being tested against x64sc
About a month ago I posted here about ViceSharp, a clean-sheet, library-first reimplementation of the VICE Commodore emulator family in modern C#/.NET:
https://www.reddit.com/r/c64/comments/1so0wmq/introducing_vicesharp/
At the time, the fair criticism was: this was mostly architecture, not an emulator yet. No cycles were running. No ROM was booting. A few people also pushed on the choice of .NET, the AI-assisted development process, and whether claims like "zero-allocation hot paths" were real engineering goals or just fancy words.
That feedback was useful. This is the follow-up.
Current state
ViceSharp has moved from foundations-only into actual C64 bring-up.
The current project state is:
- C64 ROM wiring is implemented.
- The managed emulator reaches the Commodore BASIC
READY.prompt in the test harness. - The Avalonia desktop shell can display the emulator output.
- Keyboard input is being routed through a machine-owned keyboard path, with VICE
.vkmkeymap support under active work. - Disk, tape, and cartridge attach surfaces exist and are being moved behind host-owned services.
- Host control is being exposed through a gRPC control/configuration boundary.
- The in-process renderer is intentionally allowed to read frames directly from the local emulator path; gRPC is for control, media, input, settings, monitor operations, and future remote UIs.
- x64sc parity is now the explicit target, not just "make a C64-ish emulator."
This is still not a VICE replacement. It is not at broad game/demo compatibility. It is not x64sc parity yet. The useful milestone is that the project has crossed from "architecture and scaffolding" into "booting, rendering, input/control integration, and reference validation."
On .NET instead of Rust or Zig
The short answer is still: because this project is intended to be a library-first emulator and tooling platform, not only a standalone emulator binary.
.NET gives me:
- C# as the implementation language I know best.
- Good desktop UI options, especially Avalonia.
- NativeAOT for single-file native executables.
- Strong tooling for test harnesses, source generators, analyzers, and IDE integration.
- A realistic path to embedding the emulator in other .NET applications.
Rust and Zig are both reasonable choices for emulator work. I am not arguing otherwise. The goal here is to find out how far a carefully written, deterministic, aggressively tested managed implementation can go, while still keeping a clean embedding API.
On AI-assisted development
Yes, AI agents are part of the development workflow.
They are not treated as an authority. They are treated more like fast junior or mid-level engineers that must leave evidence. The process I am using is intentionally strict:
- Small implementation slices.
- State the intent before the slice.
- Make the narrow change.
- Run focused validation.
- Run broader validation when shared behavior changes.
- Record the decision, files changed, failures, fixes, and remaining risk in MCP session logs.
- Do not expand the slice until the current gate is understood.
Internally I have been calling that the Byrd Development Process. It is basically "make the work auditable and force validation before scope expands."
That matters because emulator development is full of traps where something appears to work for the wrong reason. AI can make that worse if you let it. The countermeasure is not pretending AI is not involved; it is keeping the process test-first, evidence-heavy, and skeptical.
What lockstep testing means
The most important validation direction right now is lockstep testing against classic VICE, specifically x64sc.
In plain terms:
- Start native VICE/x64sc and managed ViceSharp from equivalent initial conditions.
- Advance both machines in a controlled way.
- Compare observable state at checkpoints.
The comparison can include:
- CPU registers and flags.
- Cycle count.
- Program counter.
- Selected RAM and ROM-visible memory windows.
- CIA/VIC/SID observable register state.
- IRQ/NMI state.
- Raster/frame checkpoints.
- Screen memory and BASIC prompt state.
This is not the same as saying "it renders a blue screen, so it works." The point is to catch cases where ViceSharp reaches a superficially similar result while drifting internally.
Earlier lockstep work focused on reset state, early CPU execution, ROM boot, and getting to stable checkpoints. The current direction is broader x64sc parity across the C64-family variants supported by x64sc: C64, C64C, old/new PAL and NTSC models, PAL-N/Drean, SX-64, PET64, Ultimax/MAX, C64GS, and Japanese C64.
Final parity means those variants pass without skipped, stubbed, or "unsupported" cases. The project is not there yet.
How classic VICE is being used
VICE is the reference, not a hidden runtime dependency.
ViceSharp is not a wrapper around the VICE binaries. The managed emulator is its own implementation. Classic VICE is used in three main ways:
- As behavioral documentation.
- As a source of functional requirements.
- As the native reference for lockstep validation.
That last part is important. VICE/x64sc has earned its reputation the hard way. If ViceSharp disagrees with x64sc, the default assumption is not "ViceSharp found something clever." The default assumption is "ViceSharp is probably wrong until proven otherwise."
Progress timeline
Approximate timeline from repo history and MCP session-log evidence:
- April 13, 2026: Iteration 0 foundations completed. Solution structure, public abstractions, source-generation direction, ROM/tooling layout, and documentation baseline were in place.
- April 17, 2026: Original r/c64 announcement. Around this time the CPU/chip skeletons, abstraction contracts, source generator project, VICE native integration layer, and early lockstep validation infrastructure were landing.
- April 18-19, 2026: C64 memory map, system bus, ArchitectureBuilder, ROM provider wiring, SID/VIC-II/CIA/keyboard/joystick surfaces, and early Avalonia wiring expanded quickly.
- May 8, 2026: Work resumed around ROM wiring, BASIC boot proof, and hardening the native VICE shim used by lockstep validation.
- May 12, 2026: C64 boot-to-
READY.and 100k-cycle VICE-backed lockstep gates were recorded in project handoff/session evidence. - May 14-15, 2026: x64sc parity work expanded into model profiles, keyboard/VKM handling, media attach, host control/status, settings UI, monitor RPCs, and broader x64sc variant validation.
The MCP logs do track a lot of the chronology. Token accounting was not consistently captured as reliable nonzero totals, so I am not going to invent a token count.
What is done vs not done
Done or substantially underway:
- Library-first architecture.
- Core C64 boot path.
- ROM loading/wiring.
- BASIC prompt proof.
- Native VICE/x64sc reference harness.
- Avalonia display shell.
- gRPC control boundary.
- Keyboard/media/settings/monitor host-control surfaces.
- Requirements and traceability imported from classic VICE documentation where they describe observable emulator behavior.
Still not done:
- Full x64sc parity.
- Broad game/demo compatibility.
- Full VIC-II edge-case behavior.
- Full SID accuracy.
- True 1541/datasette/cartridge ecosystem parity.
- Final performance proof for all hot-path allocation and throughput goals.
- Final validation across all x64sc C64-family variants.
So the honest status is: early alpha / C64 bring-up, with a serious validation strategy now in place.
Why continue this when VICE exists?
Because VICE is excellent, and that is exactly why it is the reference.
ViceSharp is aiming at a different shape:
- A modern .NET emulator core that can be embedded as a library.
- A clean host/control API for tooling and alternative UIs.
- Deterministic test harnesses that can be used by other .NET projects.
- A platform for experiments around C64 development tools, monitor integration, game tooling, and hybrid host/emulated workflows.
If all you want today is to play C64 software accurately, use VICE. Seriously.
If you are interested in emulator internals, .NET performance work, C64 tooling, or watching a clean-sheet implementation try to climb toward x64sc parity in public, ViceSharp is now far enough along that the work is concrete instead of theoretical.
Repo:
https://github.com/sharpninja/vice-sharp
Feedback is welcome, especially the skeptical kind. The last round of skeptical comments turned into better requirements and better tests.
Thoughts on Emu-Sync? How does it compare to Syncthing?
While I initially looked into moving rom save files onto external storage devices like SD cards (so I may easily access my saves from either my Bazzite PC, Steam Deck, and hopefully eventual Steam Machine), I recently found out about using cloud syncing. I've heard of two examples: EmuSync and Syncthing. (Here's the link to the farmer's github https://github.com/emu-sync/EmuSync)
I'm inclined to try out the former since, well, it seems more in line with emudeck. But I need more insight. What are your thoughts on EmuSync? How does it compare to Syncthing?
Atari console emulator
IRIS EMULATOR: Since the day before yesterday, I've been working on an emulator to improve the experience of emulating the Atari 2600, Atari Lynx, and Atari Jaguar, because I feel there isn't an emulator with the level of excellence that PCSX2 has for the most important Atari consoles. The emulator doesn't emulate 100% of the games for each console; I'd guess the Atari 2600 has 60% of the games, the Jaguar only has excellent 2D games (Doom is broken, with distorted sound), and the Lynx about 35%, but I'm improving it slowly. If anyone is interested in testing it, or contributing with tips or improvements, feel free to comment. I'll leave the project's GitHub link below! A picture of its current state is shown below.
Brovan: Binary user-mode emulator for x86_64
After months of work, I’m excited to finally share Brovan, my user-mode binary emulator.
Brovan can emulate:
* PE binaries
* ELF binaries
* Memory dumps
* Even partially unknown or unrecognized binaries
The goal is to make binary analysis, malware analysis and general binary research more flexible by giving full control over execution, memory, and runtime behavior in a contained environment.
Building this involved a lot of work around emulation, syscall handling, memory management, binary loading and parsing, and there’s still much more to improve, but it’s finally at a stage where I’m happy to share it.
I built a custom assembly language / emulator (along with virtual CPU, RAM, PPU, and ROM) on Python. (pyasm)
Full information about pyasm can be found in README of my repo: https://github.com/windowssandbox/pyasm
Since I've added PPU yesterday, I'm wondering if making games on it is possible (like ping pong for example).
It's currently just 8-bit.
I'm gonna add more stuff to PPU and instruction set later. (instruction set opcodes are ordered based on when they are added)
Now the question is: how accurate is it compared to actual assembly?
(you can share your own pyasm code below)
I wrote a ZX Spectrum emulator in Go
Hi everyone,
I decided to take on the challenge of writing a full 8-bit emulator (Sinclair ZX Spectrum) from scratch using Go.
It’s been a great project for diving deep into things like Z80 instruction sets, memory mapping, and managing state at a low level without using C. Handling the timing-critical aspects of the Speccy in Go was an interesting hurdle, but it's coming along well.
It’s called zx_go. It can currently load tape files and execute the original ROM code. I’m sharing it now because I’d love some fresh eyes on the code - specifically how I’m handling the CPU loop and memory access.
If you’re into retro tech or systems programming in Go, I’d appreciate any thoughts or feedback you have on the implementation.
I've been working on this project (and compilers / vm's in general) for a while now, and I thought it was finally proper enough to showcase.
Some of the features:
- A custom systems language designed for explicit memory control, featuring manual memory management, pointers, (inline) structs, and generics.
- A full interactive compiler pipeline that you can watch in real-time.
- A built-in 5-stage pipelined CPU to execute the code.
- Compiles to actual RISC-V machine code (RV64IMAFD).
- Visualised live in browser using EGUI.
Github: https://github.com/LPC4/Full-Stack
Check out the live demo website here: lpc4.github.io/Full-Stack/
Yet another CHIP-8 emulator, but this time it's bulletproof (formal verification)
Made it work in online Win98 emulator made with Claude Code:
https://wine-assembly.berrry.app
Story how it's made:
https://wine-assembly.berrry.app/story.html
LeapPCX, the first standalone Leapster emulator, IS ALIVE
Going through the toadster172 MAME fork source, I just discovered that the ROMs named "leapster2_1004.bin", "leapster2_1006.bin", and "leapster2_1008.bin" were actually not Leapster2 BIOSes. So I fixed the known ROMs list and recompiled LeapPCX, and then for fun tried to boot "152-12206_0.6.bin" (an actual Leapster2 BIOS) and now LeapPCX has booted the Leapster2 BIOS up to the epilepsy warning screen despite the fact that Leapster 1 BIOSes still just display a black screen. I assume this is because the epilepsy warning screen is written to VRAM before MQX even starts, but still, very ironic how the BIOS of the slightly upgraded console from 2008 boots up to a recognizable and correct frame but the 2003 Leapster's BIOS cannot boot up to touchscreen calibration or the boot screen after it (presumably done after MQX has booted). How common are super ironic scenarios like this in emudev?
Also if you are wondering what the "toadster172 MAME fork" is: it is the only software that can currently boot Leapster games, and it is the definitive source of info used to create LeapPCX. It can also boot the LS2 BIOS up to the epilepsy warning screen but not much further than that.
As for why I am compiling and running on Windows XP, it is because I want to allow people as many choices for computers to run LeapPCX on as possible, despite the fact that no XP PC would be fast enough to run LeapPCX at full speed
Is it possible for a GB emulator to pass Blargg's instruction timing test ROM with machine cycle accuracy?
Hello! I'm stuck trying to debug Blargg's instruction timing test ROM. Specifically, the timer test, which corresponds to this piece of assembly:
; Ensures timer works
test_timer:
call start_timer
call stop_timer
or a
ret z
set_test 2,"Timer doesn't work properly"
jp test_failed
; Starts timer
; Preserved: AF, BC, DE, HL
start_timer:
push af
- xor a ; 1
sta TIMA ; 3
lda TIMA ; 3
or a ; 1
jr nz,- ; 3
pop af
ret
; Stops timer and determines cycles since
; it was started. A = cycles (0 to 255).
; Preserved: BC, DE, HL
stop_timer:
push de
call stop_timer_word
ld a,e
sub 10
pop de
ret
; Same as stop_timer, but with greater range.
; DE = cycles (0 to 1019).
; Preserved: BC, HL
stop_timer_word:
ld d,0
; Get main count (TIMA*4)
lda TIMA
sub 5
add a
rl d
add a
rl d
ld e,a
; One iteration per remaining cycle
- xor a ; 1
sta TIMA ; 3
lda TIMA ; 3
dec de ; 2
or a ; 1
jr nz,- ; 3
ret
jr nz,- ; 3
pop af
ret
The first lda TIMA in stop_timer_word should write 0x09 to register A in order for the test to pass. I found that the value of TIMA before the instruction is executed is 0x08, and 0x09 after. This is no problem in a clock-cycle-accurate emulator, as TIMA would be increased mid-execution before the instruction actually copies the value from memory[TIMA] into A, but I can't do this. In addition, increasing the cycle count before executing the instruction is not an option, as the duration of many instructions depends on what is executed (like the JR instructions). Thus, I'm unsure whether it is impossible to pass this test ROM with a machine-cycle-accurate emulator or if the error lies elsewhere...
I leave the execution trace of the code snippet below in case anyone wants to have a further look. For each instruction, the value of TIMA before and after the execution is shown, as well as register values after the execution.
Thanks in advance!
> TIMA pre: 0005 (cycle = 24994072)
TIMA: 0x6 (cycle = 24994096)
C318 [CD,D5,C2] CALL C2D5 | AF:FFD0 BC:00FF DE:C877 HL:9000 SP:DFF9 IME0
> TIMA pre: 0006 (cycle = 24994096)
TIMA: 0x7 (cycle = 24994112)
C2D5 [F5] PUSH AF | AF:FFD0 BC:00FF DE:C877 HL:9000 SP:DFF7 IME0
> TIMA pre: 0007 (cycle = 24994112)
TIMA: 0x8 (cycle = 24994116)
C2D6 [AF] XOR A | AF:0080 BC:00FF DE:C877 HL:9000 SP:DFF7 IME0
> TIMA pre: 0008 (cycle = 24994116)
TIMA: 0x0 (cycle = 24994128)
C2D7 [E0,05] LDH (FF00+05), A | AF:0080 BC:00FF DE:C877 HL:9000 SP:DFF7 IME0
> TIMA pre: 0000 (cycle = 24994128)
TIMA: 0x1 (cycle = 24994140)
C2D9 [F0,05] LDH A,(FF00+05) | AF:0080 BC:00FF DE:C877 HL:9000 SP:DFF7 IME0
> TIMA pre: 0001 (cycle = 24994140)
TIMA: 0x1 (cycle = 24994144)
C2DB [B7] OR A | AF:0080 BC:00FF DE:C877 HL:9000 SP:DFF7 IME0
> TIMA pre: 0001 (cycle = 24994144)
TIMA: 0x2 (cycle = 24994152)
C2DC [20,F8] JR NZ, -8 | AF:0080 BC:00FF DE:C877 HL:9000 SP:DFF7 IME0
> TIMA pre: 0002 (cycle = 24994152)
TIMA: 0x3 (cycle = 24994164)
C2DE [F1] POP AF | AF:FFD0 BC:00FF DE:C877 HL:9000 SP:DFF9 IME0
> TIMA pre: 0003 (cycle = 24994164)
TIMA: 0x4 (cycle = 24994180)
C2DF [C9] RET | AF:FFD0 BC:00FF DE:C877 HL:9000 SP:DFFB IME0
> TIMA pre: 0004 (cycle = 24994180)
TIMA: 0x5 (cycle = 24994204)
C31B [CD,E0,C2] CALL C2E0 | AF:FFD0 BC:00FF DE:C877 HL:9000 SP:DFF9 IME0
> TIMA pre: 0005 (cycle = 24994204)
TIMA: 0x6 (cycle = 24994220)
C2E0 [D5] PUSH DE | AF:FFD0 BC:00FF DE:C877 HL:9000 SP:DFF7 IME0
> TIMA pre: 0006 (cycle = 24994220)
TIMA: 0x8 (cycle = 24994244)
C2E1 [CD,E9,C2] CALL C2E9 | AF:FFD0 BC:00FF DE:C877 HL:9000 SP:DFF5 IME0
> TIMA pre: 0008 (cycle = 24994244)
TIMA: 0x8 (cycle = 24994252)
C2E9 [16,00] LD D, 00 | AF:FFD0 BC:00FF DE:0077 HL:9000 SP:DFF5 IME0
> TIMA pre: 0008 (cycle = 24994252)
TIMA: 0x9 (cycle = 24994264)
C2EB [F0,05] LDH A,(FF00+05) | AF:08D0 BC:00FF DE:0077 HL:9000 SP:DFF5 IME0