top of page
loading_reduced.jpg

1.9.1 / 21-09-2026

average rating is 2 out of 5

Performance

average rating is 2.7 out of 5

Features

average rating is 2 out of 5

Pureikyubu

Accuracy

competition

Pureikyubu (formally Dolwin) is an open-source Nintendo GameCube emulator which initially started development in 2004. It went into hiatus but is now active. With that in mind, it's not the most advanced but has interesting features and is able to boot and run some commercial games and demos.


  • Emulation requires DSP IROM / DROM dumps

  • A BIOS image dump is not required, but if you want to experiment with it, you can also add it in the settings. The BIOS is launched through the menu File -> Run Bootrom. Then you need to wait a bit and open the drive cover (File -> Swap Disk -> Open Cover). After that, IPL Menu will start :p

--

Build using Visual Studio 2019.  To build, open Dolwin.sln and click Build.

The executable will be at the root (Dolwin.exe)

Most Recent Changes

--1.9.1--

Version 1.9.1 is the maintenance release that finishes what 1.9 started. The integrated Game Boy Advance and Game Boy now sound and time the way the hardware does: the Game Boy's LCD runs on its own 4.194304 MHz clock, the Game Boy APU was rewritten against the manuals and has tests for the first time, the GBA's sound controller and its host-side BIOS sound driver match the official IPL, and the aging cartridge's timing checks pass. On the GameCube side the release is about the interface and the build: the legacy debug console and the Win32 front end are gone in favour of the single SDL2 one, the SDL port gains the settings and memory-card dialogs it was missing, the TEV gets its swap tables, and the recompilers run on 32-bit hosts and Windows 7. The portable suite closes at 362 of 362 tests.


Highlights

  • The portable machines sound right. The Game Boy's APU was wrong in almost every clock it has — the frame sequencer ran sixteen times too fast, the length counters counted backwards, the pulse and wave dividers were half their rate (every note an octave high), the duty phases were wrong and NR51 was swapped, which mirrored the whole stereo image. The suite that now pins it down (GbApu, fifteen tests) is new; the module had no tests at all. On the GBA, channel 3 was clocked by a timer, the mixer's levels were half of what the hardware gives the direct-sound FIFOs, the
    waveform RAM lost its second bank, and a decreasing sweep that underflowed stopped the channel.

  • The portable machines time right. The Game Boy's LCD advanced one dot per four clocks — a GBA rule — so a frame was 280896 clocks instead of 70224 and every device that counts clocks got four times its rate per frame of picture. The dot clock is now the machine's own 4.194304 MHz clock, one clock per dot. The GBA's memory model caught up with GBATEK: the internal memories charge their documented access cycles (including the undocumented 4000800h waitstate register), the Game Pak uses the first/second access times of WAITCNT with the 128 KByte non-sequential rule, a DMA transfer spends its cycles while it runs, and a transfer no longer rewrites the
    SAD/DAD registers.

  • The HLE BIOS is the official BIOS. The host-side BIOS calls have the official SWI numbers now (the sound driver is 1Ah..1Fh/28h/29h, not 28h..2Fh), the four decompressors were read out of the official image and fixed (HuffUnComp's bitstream starts at source + 4 + (treeSize + 1) * 2 and is read as a rotated word; RL's flag byte is one token, not eight), the sound driver mixes its twelve virtual channels, and the IRQ contract is the hardware's: acknowledging IF is the handler's job. Metroid Fusion on the high-level BIOS now draws the same frames as on the real image.

  • Nintendo's own aging cartridge runs. The AGB aging cartridge (AGB_CHECKER_TCHK10) is the release's oracle, exactly as the real BIOS image is for the HLE. Eleven of its checks were answered by the timing work — five MEMORY sub-tests, the H-Blank status and interrupt flags, the video capture window, KEY INTR, the DMA priority preemption and the address-control registers.

  • One front end and one debugger. The Win32 front end (ui.cpp, 4940 lines, with its DirectSound, GDI and GetAsyncKeyState back ends) and the legacy debug console are gone; the SDL2 front end is the only build, and the Debug menu has one item that opens the new debugger. The SDL
    port gains the two dialogs it was missing — Options → Settings… and Options → Memcards.

  • 32-bit and Windows 7. The recompilers exist as x86 modules next to the x86-64 ones: the 32-bit host no longer falls back to the interpreter, and the same sources build a 32-bit emulator.
    The Windows projects target Windows 7 (_WIN32_WINNT=0x0601), the oldest Windows SDL2 2.28 and
    the emulator run on.

  • The Flipper's TEV gets its swap tables. The two 2-bit fields of TEV_ALPHA_ENV are the raster and texel swap-table selects, not a mode and a fixed component pick; the four tables are the low four bits of TEV_KSEL_2k. The CP's BP write mask (0xFE) limits the very next register write, which is what keeps the library's shared payloads from clobbering each other. The demo tev-swap's doughnuts come out grey (0.0% saturated pixels, was 69%).


Added

The Game Boy side of the portable core

  • A GbApu suite (fifteen tests) and a GbPpu suite for the Game Boy machine, which had none: the
    divider rates of the four channels against the manuals' frequency formulas, the duty waveform phases, the length timer, the 512 Hz frame sequencer with the envelope at 64 Hz and the sweep at 128 Hz, the wave RAM access order and volume shifts, the noise LFSR sequence, NR50/NR51 mixing,
    the exact sample clock, PCM12/PCM34 (the CGB's view of the four generation circuits), the CGB's own high pass filter and the setting that turns it off. On the LCD side: the mode timings in the machine's own clocks (one clock to a dot, 456 to a line), the STAT/LYC interrupts, the CGB attribute map, the DMG compatibility rules and OPRI, the HDMA destination bank, and the LCD-off blank.

  • The two Acid2 pictures are pixel for pixel identical to Matt Currie's reference images
    dmg-acid2 on the DMG, dmg-acid2 on a CGB in DMG mode (shade for shade through the emulator's own grey ramp) and cgb-acid2 on the CGB. They are what found the lost line-start STAT interrupts, and they are the end-to-end check of the one-line-at-a-time renderer.

  • A way to listen and to compare: --wav <file> records the mix in both harnesses, --rate N changes the sample rate and --no-hpf turns the DMG/CGB high pass filter off
    (audio.highPassFilter), which is what a recording that will be filtered later wants. The GBA harness also got --log (the core's own warnings, e.g. an unimplemented SWI, used to be thrown away) and --bios.

  • The Game Boy frontend blends each shown frame with the one before it — dmgemu's lcd_effect, the trail an LCD leaves while the picture moves. It lives in Host::Present, so a .gba and a .gb/.gbc run get it from the same code, and video.lcdEffect turns it off (on by default).
    The cores are not touched: the frame the screenshots and the debug interface read stays clean.

  • --keys is pushed through to the Game Boy machine, which is how the Metroid II run below was
    checked.


Build and tooling

  • The recompilers on 32-bit hosts (issue #417). gekkojit_x64.cpp, gekkojit_ps_x64.cpp,
    dspjit_x64.cpp and gekkojit_layout_x64.h are marked x86-64-only and each got a 32-bit sibling

  • (jit_x86.h, gekkojit_layout_x86.h, gekkojit_x86.cpp, gekkojit_ps_x86.cpp,dspjit_x86.cpp). The translator is the x86-64 one adapted to eight general purpose registers,
    four of them callee-saved, with the layout written down in gekkojit_layout_x86.h, and the state
    hash of a workload is identical on all four JIT builds — MSVC and GCC, x86-64 and i386.

  • Windows 7. The Visual Studio projects and CMakeLists.txt carry both module sets and let the
    preprocessor pick; the emulator projects define _WIN32_WINNT=0x0601 and WINVER=0x0601, and no
    emulator source calls an API newer than that, so the Windows 7 target of SDL2 2.28 is the oldest
    the emulator runs on.

  • SDL2 is linked as a static library from the bundled tree.

  • testing/gba_bench grew the Bus, HleBios, GbBus, GbApu, GbPpu, Audio and Settings suites; the Visual Studio project and the Readme follow, so the Windows build of the harness
    compiles the tests that compare the host BIOS with the official image.


Changed

The SDL front end is the only one (issues #421, #422, #420)

  • The Win32 front end and its back ends are deleted: ui.cpp (the main window, the menu, the game selector, the settings property sheet, the controller and memory-card dialogs), audio.cpp/audio.h (DirectSound), video.cpp (GDI) and pad.cpp (GetAsyncKeyState),
    together with the dialog, menu, accelerator and bitmap templates only they used. The interfaces the rest of the emulator speaks stay where they were; the application icon stays in the resource
    script.

  • The settings of the two ports are one pair of files again (Data/DefaultSettings.json and
    Data/Settings.json); the Windows/SDL split is gone, and the DOLDEBUG key, unread since the
    debug console was removed, is gone with it.

  • The legacy debug console is gone (cui.*, cuinull.cpp, cuisdl.cpp, debugui.*). The Debug
    menu of both ports carries a single item, Open Debugger…, which opens or closes a DebugUI2
    session; the crash report reaches the front end with the same UIReport command it used when no
    console was open.

  • The SDL port can mount a DolphinSDK folder again — File → Mount DolphinSDK as DVD… was a stub
    there and now opens the directory browser and calls DvdMountSDK.

  • The settings dialog and the memory cards (issue #420). Options → Settings… is one window
    with a tab per Win32 property-sheet page: the directories the selector scans and the file filter (GUI/Selector), and the emulated console version plus the three firmware images (GCN Hardware). The version combo carries a "User defined" entry so a hand-edited configuration is shown as it is; Apply writes the configuration the way SaveSettings does, with the paths going back through AddSelectorPath, and Cancel drops the copy. Options → Memcards is a window per
    slot: the connection flag, the save policy (SyncSave) and the card file, with Create New…
    asking for one of the six sizes the hardware has. Unlike the Win32 dialog, which left the running card alone until the next boot, OK re-points (flushing the card it replaces) or connects a card
    that is already open.


The GBA/Game Boy core

  • The sound controller against GBATEK and the AGB manual. Channel 3 is clocked by NR33 and
    not by a timer — SOUND3CNT_X bit 14 is the length flag and nothing else, and only the two direct-sound FIFOs are timer driven; the waveform RAM is two banks of 16 bytes with the CPU reaching the one that is not playing; the mixer follows GBATEK's "Max Output Levels" (a PSG channel spans a quarter of the output range, a FIFO the whole of it, and SOUNDCNT_L's level has "no effect on direct sound"); a decreasing sweep that would underflow keeps the frequency instead of stopping the channel; and the sum is clipped by the 10-bit output stage as the hardware clips
    it.

  • The device is the clock of the machine. The machine pushes every sample it mixes into the mixer buffer and the device plays it from its own audio callback. The buffer keeps a cushion of three video frames, throws away what does not fit, fills a period it cannot fill with silence instead of stale samples, and reports its delay, gaps and drops in the window title. The frame loop holds the machine back only when the buffer is genuinely ahead, and fast forward throws its sound away. The rate correction is a slow PI controller on a filtered level, gains worked out from the loop: the old one-liner answered a sawtooth with an undamped integrator whose period
    worked out at half a frame, which swung the full ±1% between neighbouring frames and threw away
    9450 frames a minute.

  • A repeating DMA streams its source. GBATEK reloads CNT_L and optionally DAD on a repeat — not SAD, so the source pointer carries on. A sound DMA is always repeating (the FIFO asks for 16 bytes at a time), so the old code restarted the stream at the same address on every refill and the FIFO played one 16-byte block over and over — a high buzzy squeak that vaguely followed the tune. A transfer also does not change the SAD/DAD registers, which hold the values the CPU wrote; the running pointers live in the engine.

  • The video capture runs on the lines GBATEK gives it — started at VCOUNT=2, repeated each scanline, stopped at VCOUNT=162 with the channel's enable bit self-cleared — and a
    higher-priority DMA request that arrives while a transfer is running is serviced at the next unit boundary instead of being lost until the channel's next trigger.

  • The EEPROM answers only in its own window. GBATEK puts the chip at D000000h-DFFFFFFh on a cartridge of 16 MByte or less (or in the last 256 bytes of a full 32 MByte image); everywhere else the ROM still drives the bus. That is what lets a game run its own EEPROM routine from the cartridge — Minish Cap fetches instructions between the read request and the data transfer. The chip's idle read also drives bit 0, the ready line the games poll after a write, instead of returning the mirrored ROM.

  • The host-side sound driver's channel array is twelve entries of 40h bytes at
    50h + n * 40h (GBATEK: 1-12 channels), not sixteen of 30h; a DMA's unit size follows CNT_H bit 10 on every channel, not just DMA3; the DMA's per-unit cost follows the bus width of the
    source and destination; and the internal memories charge their documented cycles, with the on-board 256K WRAM's waitstates taken from the undocumented 4000800h.

  • An idle WAITCNT bit 15 (the read-only Game Pak Type Flag) can no longer be set by a write, and DISPCNT bit 5 — the "H-Blank Interval Free" flag — no longer silently means something else to the renderer.


The GBA HLE BIOS

  • The SWI numbers are the official ones: the sound driver at 1Ah..1Fh, with VSyncOff/VSyncOn at 28h/29h. Before, the whole block was at 28h..2Fh and 1Ah was called DivArm2, so a game's SoundDriverInit was answered with a division and its music never
    started.

  • HuffUnComp, BitUnPack, LZ77 (both write variants), RL and the two delta filters were re-read out of the official image's own code and are now covered by a differential test that
    encodes random data into valid streams and compares the bytes both implementations wrote. MidiKey2Freq uses the BIOS's own fixed point — a 16.16 semitone table, the octave as a shift, the product truncated, the fine value as a slope — instead of a double-precision guess.

  • The sound driver's entry points are implemented from what a probe can measure out of the real BIOS: the work area's identifier 68736D53h and its 0FB0h size, the pcmbuf layout (two
    0630h byte halves at +0350h and +0980h), the two FIFO DMAs (B600h: repeating, 32-bit, with
    an incrementing source), SOUNDCNT_H 210Eh, the timer 0 reload for every playback frequency index, and SoundDriverMain, which mixes the twelve virtual channels with the driver's own
    envelope state machine and fixed-point levels. The reverb is not modelled, and the sample stepping is a 16.16 accumulator rather than the driver's exact arithmetic.

  • The IRQ contract is the hardware's: the custom boot ROM no longer acknowledges IF before dispatching, because the official BIOS does not either. Metroid Fusion's handler reads IF to see what happened, so a pre-acknowledged IF left it doing nothing every frame and the game froze on
    its title screen. The SIO link driver now installs its own acknowledge-and-return handler, which
    is what a program enabling that interrupt has to do.


Gekko, the TEV and the build

  • TEV swap tables and the compare operation. The two 2-bit fields of TEV_ALPHA_ENV are the
    swap-table selects (GXSetTevSwapMode's raster and texel selects) and the four tables are the low four bits of TEV_KSEL_2k/2k+1; TEV_KSEL resets to the tables GX initialisation programs
    (RGBA/RRRA/GGGA/BBBA). The compare operation shares the same bits, so it is decoded the way the
    driver encodes it — the bias field's fourth value marks it, the sub bit picks the comparison — and
    the mask tests the pre-shift stage value.

  • The CP's BP write mask (0xFE). A write to it limits which bits of the very next BP register
    write are updated and then clears itself. The library uses it for the registers whose payload two
    features share — the K constant selects and the swap tables of TEV_KSEL, the cull mode of
    GEN_MODE, the blend mode of PE_CMODE1 — so without it the swap tables would be clobbered by
    the first GXSetTevKColorSel. The mask travels down the CP chain with the write and each block
    merges it into its own register value.

  • Two TEV arithmetic details. The interpolated blend factor is a u0.8 fraction normalised over
    256, so 255 is exactly 1.0 and 128 is 129/256 (all three copies of the model divided by 255, half
    an LSB away), and DIVIDE_2 truncates without rounding. A bit-exact model of the design's stage
    then matches the demo on all 64 argument-sweep states, all 24 arithmetic states and the 11 states
    that used to separate the two models.

  • The disassembler. The halfword transfer's bit 22 selects the offset form and was inverted, so
    every immediate offset printed as a register and every register offset as an immediate — the
    official BIOS's own decompressors were unreadable. A register offset's shift is printed now, and a
    pre-indexed writeback is outside the bracket ([r1, #4]!, not [r1, #4!]). Both are what made
    the HLE BIOS work above possible.

  • The GBA module uses the stdint types (issue #419) instead of the project's u8/s8 aliases;
    json.cpp is self-contained (the standard library, verify.h and its own UTF-8 codec), so the
    portable core compiles it next to its own sources without SDL, OpenGL or ImGui, and the GBA
    settings reader uses the shared Json engine (issue #423) instead of its hand-written parser.

  • The debugger window is opt-in in --gba/--gb: emulation.debugger (false by default)
    decides whether it opens with the machine; the JDI node and the MCP transport come up either way,
    and F2 opens and closes the window as before.


Fixed

The portable machines

  • The Game Boy's clock (found by capturing zelda.gb and comparing it with another emulator).
    The LCD advanced one dot per four clocks, so a frame was 280896 clocks instead of 70224: the APU
    mixed 2940 samples a frame where the device plays 738.35, so three quarters of the music was
    thrown away in bursts (the rattle that would not go away no matter how the buffer was steered);
    DIV ran at 64 kHz instead of 16384 Hz and the serial port at four times its baud rate; and the CPU
    had four frames of work to get through in one. The emitter now runs on one clock per dot.

  • The Game Boy APU's every rate. The frame sequencer stepped every 512 system clocks instead of
    8192, so the length, the envelope and the sweep all ran sixteen times too fast, and it clocked the
    length on step 7 as well (320 Hz with jitter instead of 256). The length counters loaded
    64 - NRx1 and counted up to 64 — backwards. The pulse and wave dividers ran at
    (2048 - x) * 2 and (2048 - x) instead of * 4 and * 2. The four duty waveforms had the
    right ratios but the wrong phases. NR50's master volume was never applied to the mix, and NR51's
    halves
    were swapped, which mirrored the whole stereo image. The sample clock now keeps the fraction of a
    clock it used to round away.

  • The line-start STAT interrupts. BeginVisibleLine called EnterMode(2) and threw its return
    value away, so the STAT request a line start produces — the mode 2 source turning on, and an LYC
    that matches the new LY — never reached IF. Every visible line's mode 2 and LYC interrupt was
    lost (the ones inside VBlank still worked, which is why it hid), and the raster effects driven by
    LY=LYC that draw the Acid2 hair, eye, mouth and footer never ran. A register write can raise the
    STAT line now (enabling a source whose condition is already true, or a LYC write that makes it
    match "constantly"), and STAT bit 2 stays live while the LCD is off.

  • A CGB running a monochrome cartridge (the frontend's default for a DMG game) ignored the
    DMG's own display rules: LCDC bit 0 was the CGB's master priority instead of blanking the
    background and the window, the window bit was not overridden by it, objects were prioritised by
    OAM position instead of X (OPRI was stored but never reached the PPU), the bank 1 attribute map
    was read although the manual says that bank "is not present in this mode", and
    BGP/OBP0/OBP1 were ignored instead of indexing the CGB palettes.

  • The CGB's VRAM DMA always wrote bank 0, while the manual is explicit that the destination is
    VBK, and reading HDMA5 during an active HBlank transfer returned bit 7 set, which Pan Docs
    defines as "Not Active". The mode 3 OBJ penalty 11 - (X mod 8) dropped to 4 or 5 on the last two
    columns of a tile where Pan Docs floors it at 6. Turning the LCD off left the last picture in the
    frame buffer instead of the blank white of the disabled screen.

  • The GBA LCD audited against the manuals. BG2 is affine in mode 1 (Final Fantasy V Advance's
    "SQUARE ENIX PRESENTS" screen was a field of noise); the bitmap modes sample through the BG2
    matrix (the boot ROM and the no-BIOS start leave the identity matrix, as the real BIOS programs
    it); mode 5's bitmap line is 320 bytes and not mode 3's 480; a window's garbage dimensions reach
    the screen edge; the OBJ window needs DISPCNT bits 12 and 15; the Green Swap exchanges the
    green of each pair of dots instead of byte-swapping every pixel; a semi-transparent OBJ needs a
    2nd target selected in BLDCNT and the window's effect bit gates the alpha blend as well as the
    brightness; an OBJ whose 8-bit Y range runs past line 255 wraps to the top; and the 28-bit affine
    reference point is kept sign-extended.

  • The text layers' scroll offsets. BGxHOFS/BGxVOFS hold a nine-bit offset (0-511) but were
    sampled through the register's readable form, which the PPU trimmed to eight bits. Castlevania:
    Circle of the Moon's attract demo scrolled a room to dot 296, the renderer used 40, and the top of
    the screen showed whatever the map's other half happens to hold — a band of green "corrupt
    background" — while the camera appeared frozen relative to the player.

  • A double-sized affine OBJ is anchored in the middle of its doubled area. The X/Y attributes
    are the upper-left corner of the display area and the rotation/scaling centre is the middle of
    that area — half a base size right and below the reference point normally, a whole one when the
    double-size flag is set. Every doubled OBJ sat half a base size too high and too far left; the
    real BIOS's boot animation is what pinned it (the 64x64 letters snapped down by 32 pixels on their
    landing frame). Final Fantasy V Advance's letterbox also pins the OBJ Y wrap: its rows of 16x16
    OBJs at Y=240..243 fill the gaps the wrapped lines leave.

  • The V-Counter match interrupt is an edge, not a level. UpdateVCountMatch requested it
    whenever the match condition was true on an evaluation, and a DISPSTAT write evaluates it too,
    so a program that wrote the register back while VCOUNT still equalled its setting took two match
    interrupts for one line. Minish Cap writes DISPSTAT at line 80 of every frame — in the handler
    of the match it has just taken — and its own copy of the BIOS sound driver advances its sequencer
    once per match, so the intro music ticked twice per frame: the notes came twice as fast and the
    track ran out of sequence at 11 s. It is now the edge of the gated (match AND enable) condition,
    which is what GBATEK's "requested when the flag becomes set" means; a program that enables the
    interrupt while the counter already matches still gets it.

  • The Save file of a fresh Minish Cap cartridge came up corrupted. The EEPROM's ready line
    (bit 0 of the ROM bus in the chip's window) was never driven, so the save library's poll after a
    write always saw a busy chip, every write timed out, and after three attempts the library stamped
    its DAMEDAME failure marker over the block it was writing — file headers included. With the
    ready line on the bus every write is verified on the first attempt.

  • The HLE sound driver's channel array was misaligned. Sixteen entries of 30h bytes spans the
    same 300h bytes as the real twelve of 40h, which hid the mistake: only channel 0 landed where the
    official driver looks, so channels 1..11 were mixed from the next channel's envelope and volume
    bytes — heard as wrong notes, crackle and an apparent speed change.

  • The halfword transfer offset form in the disassembler (see Changed), which is what made the
    decompressors readable.


The core, the harness and the shell

  • Thumb LDMIA/STMIA skipped an instruction. ThumbMultipleTransfer treated bit 7 of the
    register list as "r15 in the list", but a Thumb register list is eight bits wide (r0-r7) and can
    never name r15. The BIOS loads its BitUnPack parameter block with LDMIA r1!,{r5,r7}, so the
    instruction that followed was skipped, the block stayed zero, BitUnPack read a zero item count
    and bailed out: the seven letter sprites had no glyph data and the boot animation was invisible.
    This is why the emulator's own boot ROM did not draw.

  • SVBK maps a written 0 to WRAM bank 1. A DMG-only cartridge runs on the emulated CGB in
    compatibility mode, and the register mapped a written 0 to bank 0. With the power-up value 0xF8
    that aliased 0xC000-0xCFFF and 0xD000-0xDFFF onto one 4 KByte page, so a game whose variables
    or stack live in the upper bank had its own low-RAM scratch overwrite them: Metroid II kept a
    return address at 0xDFFB, read back 0x0000 and restarted from the reset vector for ever.

  • Json's one-byte UTF-8 sequences. The local decoder's shortest-form table had entries for two,
    three and four bytes only, so every ASCII character came out as U+FFFD. Since AddUtf8String is
    how the whole debug interface builds its Json, the regression corrupted every answer made that way
    — the Markdown panels of the debugger, the disassembly and the symbol names, the reports, the MCP
    tool answers and the OS time of the status bar (which showed a row of question marks). Of the 16
    unit-test failures at the parent commit, 15 were this.

  • The Apu::ReadSamples call site in the tests handed a maxFrames count to an
    int16_t buffer[128] while the call writes two samples per frame: the 256-byte stack buffer was
    overflowed and the run died later, in the middle of another suite. Found with AddressSanitizer;
    the buffers are sized for stereo frames now.

  • The DMA transfer's unit size follows CNT_H bit 10 on every channel (see Changed), which is
    why the Minish Cap engine's per-frame 32-bit DMA0 block was half copied before.


Known issues

  • The official GBA BIOS runs its boot and its animation is now drawn: the mode 2 BG3 that is
    only visible inside the OBJ window, with the nine 32x64/64x64 window sprites, WINOUT = 0x3F27
    and alpha blending around it. Both the emulator's own boot ROM and the real image hand over to a
    cartridge. What is left to model on the cartridge side is the EEPROM window of a full 32 MByte
    image (the last 256 bytes); the harness exercises the 16 MByte layout, where the chip answers
    anywhere at D000000h-DFFFFFFh.

  • A real Game Boy Color cartridge's picture is still work in progress: the machine boots, runs
    its own boot ROM and passes its tests, but at some moments its frame does not match VRAM. The
    remaining difference is read as the mode 3 line length (a line is composed at once rather than dot
    by dot), the HBlank/HDMA timing and the PPU-internal VRAM read-block windows. DMG and CGB in DMG
    mode are exact against the Acid2 references.

  • The GBA's per-line OBJ cycle budget and DISPCNT bit 5 are not modelled, and the
    VRAM/OAM/Palette contention cycle ("+1 cycle if the GBA accesses video memory at the same time")
    is not: the accesses take their documented 1/1/2 cycles but never the extra one. The cartridge
    prefetch buffer is modelled as a waitstate rule rather than as a real 8-halfword buffer.

  • A preempted DMA transfer runs to its end before the preempted one resumes instead of the two
    interleaving a unit at a time. Nintendo's aging cartridge's remaining failures (the
    waitstate/prefetch checks, KEY INPUT SIMPLE) are in this area, and are recorded in
    testing/gba_bench/Readme.md with the addresses of its test table.

  • The GBA's sound output stage is not modelled: SOUNDBIAS is stored and read back, but neither its
    bias level nor the PWM amplitude resolution changes the mix, which is produced as clean 16-bit
    samples. The wave RAM is plain memory rather than the hardware's shift register.

  • The sound driver's reverb is not modelled on the HLE path (the mode's reverb bits are stored, the
    driver's delay line is not), and the pitch stepping is a 16.16 accumulator rather than the
    driver's exact fixed point.

  • Save states, rewind, the EEPROM's "last byte is the AND of the old and new value" quirk and the
    RTC's per-minute interrupt register are not implemented on the portable machines.

  • The serial port's JOY bus mode (RCNT bit 15, a GameCube controller on the link port) is decoded
    but not driven; UART mode works register-wise but has no peer. The emulator-to-emulator cable
    (normal and multiplayer) is implemented and tested.

  • The Linux build still has no sound and no input for the GameCube side, and the Linux headless
    target has no OpenGL offscreen backend (the Visual Studio one does). The two Settings tests that
    compare build/Data/GBASettings.json with the defaults byte for byte need that file to have LF
    endings; a Windows checkout with core.autocrlf reports the \rs as a difference.

  • Nintendo's aging cartridge is not fully green: ten checks still fail, all of them in the waitstate/prefetch area above. The GBA's Ppu test RegisterReadBack and the memory test ROM
    pin the data rules where the timing is approximated.



--1.9--

The emulator gains a second machine — a from-scratch Game Boy Advance and Game Boy core — a second rendering path — a complete software implementation of the Flipper graphics blocks — and a new debugger, a local MCP server for LLM agents, a hardware-interface profiler, a headless build and a DSP recompiler. The whole source tree was audited for the data it
loads and the debug interface speaks UTF-8. On the compatibility side the Metroid Prime intro movie runs several times faster and the release reaches the game's title screen, Super Mario Sunshine boots and plays its intro, and titles such as Star Wars Rogue Squadron III, Super Monkey Ball 2, Prince of
Persia and Soul Calibur II get their pictures.


Highlights

  • An integrated Game Boy Advance and Game Boy. src/gba is a second machine written from the
    public hardware specifications (the ARM Architecture Reference Manual for the ARM7TDMI, GBATEK for the GBA peripherals, the
    Pan Docs for the Game Boy). It has its own free boot ROM whose animation draws the pureikyubu
    logo, its own SDL2 front end and its own test harness; pureikyubu --gba game.gba runs a
    cartridge and pureikyubu game.gbc runs the Game Boy machine inside it. It exists to be a
    GBA Link peer and a Game Boy Player stand-in for the GameCube side.

  • A software GFX pipeline. The Flipper graphics blocks (XF, SU, RAS, TX, TEV and PE) now have a
    CPU implementation next to the OpenGL one. It draws into a real EFB memory array, a real TMEM, and
    the copy engine turns the finished frame into the XFB the video interface scans out — no OpenGL
    context at all. hardware.GFX_PIPELINE and the gxpipeline command switch pipelines on the fly.

  • A new debugger (DebugUI2). Its own thread, its own OpenGL 3.3 window and a session folder per
    run; every panel is the Markdown of a JDI command, so the whole Flipper — thirteen tabs from Gekko
    to the profiler — and both portable machines are inspectable in place. Debug → Test New Debugger
    opens it, F2 starts and stops a session.

  • An MCP server. pureikyubu --mcp runs the emulator as a local Model Context Protocol server:
    every command of the debug interface becomes a tool an LLM agent can call (~150 before a machine is
    loaded, ~170 after), over stdin/stdout JSON-RPC.

  • A hardware-interface profiler. The status bar could say how fast the CPU runs; the profiler
    says whether the data moves at all, and where: the 60x bus, the Flipper/Splash bus, the PI and CP
    FIFOs, the write-gather buffer, every DMA engine, the audio mixer input, the GFX primitives, the VI
    frames — per emulated second.

  • A headless build. uinull.cpp and a null GFX layer make the emulator a windowless console
    application for benchmarks, test sweeps and the MCP server; the Visual Studio headless target also
    draws with a real OpenGL context on a hidden window, so frames can still be read back.

  • A DSP recompiler. The DSP core got a basic-block compiler that shares the common x86-64
    emitter with the Gekko one; it is 1.5–1.8x faster than the interpreter and off by default.

  • A security review and a UTF-8 pass. About ninety defects reachable from the artifacts the
    emulator loads were found and fixed behind a small verifier layer, --selftest reports a startup
    failure instead of disappearing, and the debug interface, the console and both front ends now
    speak UTF-8.

  • Compatibility. Metroid Prime's intro FMV went from ~3.5 to ~9–11 frames a second and the run
    now reaches the title screen inside 100 seconds; Super Mario Sunshine boots, plays its intro and
    reaches the scene after it; Super Monkey Ball 2 stops spinning before its first frame; Star Wars
    Rogue Squadron III no longer faults while its MMU handler maps the page; Prince of Persia's memory
    card dialog is readable.


Added

The integrated Game Boy Advance and Game Boy (issue #388)

  • A second machine in src/gba, written from the public specifications and independent of the
    GameCube side. The Game Boy Advance core implements the ARM7TDMI (ARM and Thumb, all seven modes,
    the exceptions and HALT), the memory map with the open bus and the WAITCNT waitstates, the LCD
    (tile modes 0–2, bitmap modes 3–5, sprites, windows, mosaic, blending, forced blank), the four
    timers, the four DMA channels including the sound-FIFO and video-capture timings, the keypad, the
    interrupt controller, the sound (the four legacy channels and the two direct-sound FIFOs), the
    serial port with an emulated link cable, the cartridge (ROM plus SRAM, Flash and EEPROM saves and
    the GPIO/RTC port) and the BIOS service functions in the host.

  • A free boot ROM, because the official IPL is copyrighted: the boot animation is emulated ARM code
    assembled at run time by a small emitter (src/gba/gba_armasm.cpp). A wireframe hypercube
    collapses into the pureikyubu cube mark while the wordmark scrolls in, and the boot ROM then hands
    the machine to the cartridge the way the real BIOS does. gba_bench --dump-bootrom bootrom.bin
    writes the image and its assembly listing out for review.

  • The Game Boy machine the GBA carries inside it (src/gba/gb_*.cpp): the LR35902, the DMG/CGB LCD
    with the CGB palettes, VRAM banks and attribute map, the four sound channels and the MBC1/2/3/5
    mappers with battery saves, with its own 256-byte boot ROM.

  • The modes: pureikyubu --gba game.gba, pureikyubu --gba (no cartridge — the boot ROM's SIO link
    driver, i.e. the "GBA Link" state), pureikyubu game.gbc, --gba-bios bios.bin for a real BIOS
    image and --no-gba-bootrom to skip the BIOS. The window, the sound and the input come from SDL2,
    and the bindings live in Data/GBASettings.json.

  • A harness of its own (testing/gba_bench), because the core has no SDL, OpenGL or ImGui
    dependency: unit tests, a headless ROM runner with per-frame hashes and PNG dumps, a speed
    measurement and a link test that plugs two instances into each other. The public MIT test ROMs
    (jsmolka/gba-tests) run through it — the memory, ARM and Thumb suites report "All tests passed",
    and they are what found the VRAM mirroring, the byte-store rules of the video memory and the
    register-shift-by-zero carry rule. The suite is at 263 tests and all of them pass.

  • The GBA and GB cores are debugged from the host like everything else: gba, gbaregs, gbacpu,
    gbamem, gbappu, gbadma, gbtimers, gbsio, gbcart and the gb* equivalents are JDI
    commands, and DebugUI2 builds its panels for both machines.


The software GFX pipeline (issue #384)

  • A complete CPU implementation of the Flipper graphics blocks in the same modules as the shader
    backend, with the software methods carrying a Soft prefix: the transform unit (the matrix
    multiplies, the projection combine, per-vertex lighting, texture-coordinate generation, the
    guard-band clipper and the divide plus viewport mapping into window space), the setup unit
    (primitive assembly, edge coefficients, interpolation planes, culling, zfreeze), the rasterizers
    (the 2×2-pixel quad grid with a 12-bit coverage mask per quad, the top-left rule, the scissor and
    perspective-correct interpolation), the texture unit (a real TMEM, the load commands, a tag cache,
    the LOD computation and the point, bilinear and trilinear filters over every texel format), the
    TEV (16 combine stages, the Rev-B K constants, alpha compare, the Z-texture environment, fog and
    the final alpha function) and the pixel engine (a real EFB memory array, the Z test, blending,
    logic ops, write masks and the copy engine).

  • The pipeline is a configuration variable (hardware.GFX_PIPELINE, 0 = shader, 1 = software) and
    can be switched at run time with gxpipeline soft / gxpipeline shader; the choice is stored in
    the settings. The two paths share no rendering state.

  • Indirect (bump) texturing is implemented in the software TEV with the same arithmetic as the
    shader backend, so the two pipelines produce the same picture.

  • testing/gfx_soft_test.cpp drives the pipeline through the hardware API only — the XF and BP
    register spaces and object-space vertices — and reads the result out of the EFB memory and the
    XFB. The DolphinSDK sweep renders the same 87 demos through it and report.py puts the two
    pipelines side by side. The pipeline stays experimental: a few titles still have picture
    defects (the anti-aliased framebuffer demos among them), and the default remains the shader
    backend.


The new debugger - DebugUI2 (issues #371, #397, #407)

  • A debugger that runs in its own thread and talks to the emulated machine only through JDI and the
    Debug API; the window is a separate SDL window drawn with OpenGL 3.3 and a glyph atlas rasterized
    out of Data/DebugUiMono.ttf.

  • A session per run (Data/Sessions/<image>_<ordinal>, a JDI entity of its own). Every panel is
    the Markdown answer of a command, so the message history, the command line and a snapshot of the
    emulated state can be reviewed offline.

  • The GameCube session has thirteen tabs: Gekko, the three processor panels (registers,
    disassembly, memory), the nine Flipper subsystems and the profiler. The subsystem reports are one
    command per block — airegs, viregs, piregs, miregs, diregs, siregs, exiregs,
    cpregs and dspstate — each answering the decoded state of its block.

  • A portable session (GBA and Game Boy) is its own front end: F2 starts and stops it, the panels
    come from the gba* / gb* commands, and an MCP client that launched pureikyubu --gba gets the
    portable commands rather than the GameCube ones.

  • Focus, scrollbars and tabs: the panel under the pointer takes the focus, wheels and scrolls, a
    panel whose content does not fit gets a draggable scrollbar, and the tab strip selects between
    stacked sub-panels. Fixed along the way: DrawText placed every line an ascent too high, the
    performance counters crashed the debugger when it was opened with no machine loaded, and the
    scroll and tab state now survives the snapshots.


The MCP server (issue #383)

  • pureikyubu --mcp publishes the whole debug interface as MCP tools over stdin/stdout (one
    JSON-RPC 2.0 message per line). The client starts the emulator as its own child process, so there
    is no port, no listener and no authentication.

  • The tools are not a second interface: src/mcp.cpp builds the tool table from the can records of
    the registered nodes, so a command added to the emulator appears in the tool list by itself. Every
    tool carries the help text, the usage and the arguments of its command; a failing command is
    reported as a failed tool so the model can correct itself.

  • mcp 0 / mcp 1 start and stop the server from the debugger console, and McpRequest <json>
    drives the whole protocol as one command, which is how it is tested without a transport. The
    answers are capped at 4 MB so a command like FileLoad cannot push a binary into the model's
    context.


The hardware-interface profiler (issue #394)

  • src/hwprof.* watches the console's information-exchange channels and reports what each one
    carried per emulated second: the 60x bus, the Flipper/Splash bus, the PI interrupt assertions, the
    write-gather buffer, the PI→CP command FIFO, the audio mixer input, the EXI/DI/DSP/AI/ARAM DMA
    engines, the GFX primitives and vertices, the VI frames and the instructions the two cores retired.

  • The window is one emulated second rather than a wall-clock one, so a channel that moves 4 MB per
    console frame reports the same rate whether the host runs at 0.2x or at 5x. The instruction
    counters are the ones the cores already keep, so nothing is added to the Gekko hot path; a DSP
    paired instruction counts as one.

  • Three presentations: the hwprofile Markdown table, the hwprofile image PNG next to the session
    and the hwprofile osd overlay in the emulated picture (hwsod 1, stored as HW_OSD, off by
    default). The overlay asks the debug interface for the report and hands the rasterized picture to
    whichever back end presents the frame — the OpenGL pipeline draws it after the frame dump, and the
    software pipeline's video blitter puts it into the RGB output buffer.

  • DebugUI2 gets a Profiler tab next to Registers, Disassembly and Memory.


The headless build (issue #382)

  • uinull.cpp (the repurposed uisimple.cpp) is the entry point and the whole UI of a console build
    with no window: a bare image or --ipl runs until Ctrl+C, --bench <file> [sec] is a first-class
    headless mode, and the reports are echoed to the console.

  • The measurement loop moved out of the SDL front end into bench.cpp/bench.h, so both front ends
    measure with the same code and the wall clock is std::chrono.

  • gfxnull.* runs the GFX pipeline against a null GL layer under GFX_NULL, so the emulation is
    untouched while nothing is drawn and no driver is needed. The Visual Studio headless target defines
    GFX_OFFSCREEN instead and draws with a real OpenGL context on a window that is never shown, into
    a framebuffer of its own, so GFX_DUMP / GFX_EFB_DUMP / gxshot still produce frames; the Linux
    CMake HEADLESS target keeps the null backend.

  • scripts/VS2026/pureikyubu_headless.vcxproj is part of the solution but is not built by "Build
    Solution"; cmake -DHEADLESS=ON .. builds the same target on Linux. The null back ends
    (audionull.cpp, cuinull.cpp, padnull.cpp) were brought back to the current interfaces.


The DSP recompiler (issue #375)

  • src/dspjit.* compiles straight-line runs of DSP instruction words into x86-64. The semantics are
    not reimplemented: a word becomes one direct call (two for a parallel word) to the very handler the
    interpreter's Dispatch calls, so the decoder and the dispatch switch run once per block, at
    compile time. The emitter is shared with the Gekko recompiler (src/jit_x64.h).

  • Blocks re-check the halfwords they were built from on every entry and leave as soon as the code
    generation changes, an interrupt is pending or a breakpoint is armed; invalidation hooks cover
    HardReset, LoadIrom/LoadDrom and the DSP-DMA paths. Dsp16::DoDma copied a 16-bit block size
    blindly and overran the DSP memory arrays (AddressSanitizer found it); the transfer is clipped now.

  • The recompiler is 1.5–1.8x faster than the interpreter and off by default: --dspjit, or
    dspjit 1 in the debugger. testing/dsp_jit_test.cpp checks it differentially against the
    interpreter, including an exhaustive sweep of all 65536 instruction words (twice), the DSP-DMA
    microcode upload, an interrupt round trip and the block cache being dropped when the stream
    changes.


The UTF-8 pass (issue #372)

  • The narrow string of the project is UTF-8 — the JDI command line and its arguments, the Json
    documents, the reports and both front ends — while the emulator's own text stays wchar_t.
    Util::WstringToString and Util::StringToWstring are real converters now, including the
    surrogate pairs of the code points outside the BMP, and a byte sequence that is not valid UTF-8 is
    carried through instead of being dropped.

  • The tokenizer skips the byte order mark a script may start with; Json keeps member names as UTF-8
    (a non-ASCII key used to be truncated to one byte per character); Util::FileOpen is _wfopen_s
    on Windows and fopen of the UTF-8 name elsewhere, so a file name outside the ANSI code page
    travels through the interface in one piece.

  • The console reads and writes wide characters (ReadConsoleInputW / WriteConsoleOutputW on
    Win32, SDL_TEXTINPUT in the SDL build), and the command line moves the cursor, the deletion and
    the word search by code point. DebugUI2 decodes a panel's text and the command line into code
    points as well. The sources are UTF-8 without a BOM and the MSVC projects pass /utf-8.


Input verification and the security review (issue #381)

  • Every artifact the emulator reads from the outside world was audited — the settings JSON, the
    Bootrom and DSP ROM images, the .dol/.elf executables, the .iso/.gcm/.rvz disc images,
    the memory card saves, the command line, the guest's device registers and DMA engines, the console
    scripts and the .map symbol files — and about ninety defects were found and fixed. The recurring
    shape was a bound that existed only as assert(), which the Release configurations compile out.

  • The checks that repeat are one layer now (src/verify.h): the overflow-safe range test and the
    rules for a main-memory window, an image section, a disc read, an FST entry, a memory card transfer
    and a console script line. The memory interface gained length-aware accessors next to the
    start-only ones, so a block copy can no longer begin inside RAM and end outside it.

  • --selftest runs the whole startup sequence without a window and exits with the number of failed
    steps, and testing/startup_cases.sh runs it against deliberately broken settings files and random
    files renamed to .dol and .rvz. testing/security_test.cpp pins every rule down, including two
    property tests. The full report is in wiki/security.md and on the site (docs/security.html).


Tooling: the DolphinSDK demo sweep (issue #385)

  • DVD::MountSdk builds a whole GameCube disc image in memory from a DolphinSDK folder, so the
    SDK's demos can be read through an FST as if they were on a disc (File → Mount DolphinSDK as
    DVD…
    , or MountSDK <path> in autoexec.cmd).

  • testing/dolphinsdk holds the sweep methodology and its scripts: sweep.ps1 runs a list of demos
    and captures the video window, summarize.py turns a sweep folder into a table and report.py
    builds an HTML report with both GFX pipelines side by side. The 87 GX demos were run and the bugs
    they exposed were fixed.


Changed

Gekko and the command processor

  • The recompiler translates the halfword loads (lhz/lhzu/lhzx/lhzux and lha/lhau/lhax/
    lhaux) through a JitReadHalf trampoline. Falling back to the interpreter drops from 13.11% to
    3.89% of the instructions and the same 100-second Metroid Prime run goes from 289 to ~301 seconds
    of emulated time (103.1 to ~107 MIPS). Corrected on the way: the effective address of every indexed
    form is (RA|0), not RA.

  • A branch condition is emitted inline for the forms a compiler actually emits (a CR test or a CTR
    decrement), and a conditional branch no longer ends the block: the taken path leaves through the
    branch epilogue and the fall-through is compiled with the block, so a block covers the whole loop
    body. Blocks run per second more than halve and the same run reaches 131.8 MIPS and 11.64
    instructions per block (was 103.1 and 4.47).

  • Address-translation events no longer drop the block cache. A block bakes in the instruction stream
    it was compiled from, and the lookup already compares the physical address it was compiled for with
    the one the current MSR/BAT/SDR1/TLB state produces, so Exception, rfi, mtmsr and the
    mtspr of SDR1/the BATs/HID0 no longer call InvalidateAll. Metroid Prime's intro goes from
    131.8 to 181–191 MIPS, from 26.8M translations in 100 seconds to 17K, and from ~5.7 to ~9–11 frames
    a second; the run now reaches the title screen inside the 100 seconds.

  • The CP's read unit follows the emulated time base instead of guessing at the host scheduler's
    progress, and the WPAR timeout it never had on the real Gekko is gone. A FIFO repoint
    (GXSetGPFifo) keeps the entries the emulated reader has not fetched yet, which removes the
    intermittent boot failure of Metroid Prime's intro. The read unit reports its idle state from the
    ring's current state rather than as a side effect of the last fetch, which is what Super Monkey
    Ball 2 was spinning on. A draw command waits for all of its vertex data (the 16-bit vertex count
    was truncated to eight bits), the stream buffer is sized for the largest command a title can issue,
    and the new cpfifo command dumps the CP FIFO registers.


Timing units

  • The time base counts the Gekko clock (486 MHz), so a tick is one instruction and the decrementer
    steps by one; the old two-tick counter was a slow-down hack from the interpreter days.

  • The DSP runs at 81 MHz — one instruction per six time base ticks — so its anchor is a plain tick
    count and its batch is measured in instructions. Fixing the units is what removed the Star Wars
    Rogue Squadron III instruction-storage fault raised while its MMU handler was still mapping the
    page.


Build and layout

  • The GBA is a project of its own: a GBA static library the emulator links the way it links SDL2,
    plus the gba_bench console target. The portable debug interface (gba_debug.cpp) sits next to
    the SDL front end, which is the only file of the module that speaks JDI.

  • scripts/VS2026/pureikyubu_headless.vcxproj and the CMake HEADLESS option build the windowless
    target; src/jdispecs.cpp holds the JSON specifications of every JDI node in one place.


Fixed

Graphics

  • The EFB texture copy (issue #378). The copy engine's texture copy was missing entirely, so
    every title that renders through the EFB drew from textures that stayed zeroed. The rectangle is
    re-packed into the destination format, the tile rows follow the destination stride, and the copy
    destination's own 4-bit format set (and the alpha plane the single-channel formats need) is used.

  • A copy's clear belongs to the copy. A texture copy clears the rectangle it read; a display copy
    clears the whole colour buffer at the frame begin. A title that renders its frame in several passes
    through the copy engine (Metroid Prime) used to lose every pass drawn before a clear.

  • TEV texture bindings. A RAS1_TREF word binds a pair of TEV stages and its halves are consumed
    by the enabled stages in order, not by a fixed half per stage parity. This is what turned every
    string of Prince of Persia's memory card dialog into a white bar.

  • Indexed XF block loads. GXLoadPosMtxIndx and friends read guest (big-endian) words out of an
    index array; the bytes were handed to the XF as they sat in memory, so every matrix loaded that way
    was garbage. The cartoon-outline demo's whole model was invisible.

  • Shader pipeline depth. The GL backend's own frame-begin clear took its depth from
    PE_COPY_CLEAR_Z, which is still 0 on the first frame, so the whole first frame failed the depth
    compare and the indirect bump demos lost their light map. The GX clip-space z is now mapped onto
    GL's clip volume (z' = 2z + w) instead of being handed over raw, so the programmed viewport depth
    range matches GL and geometry on the near plane is not dropped.

  • Lighting and fog. The vertex shader negated the light direction in the spotlight attenuation,
    so every lit spot surface came out black (the software pipeline had the same bug). The perspective
    fog now looks up 2^24 / (b_mag - (z >> b_shf)), the reciprocal of a fraction of the depth range
    rather than 1 / b, so exponential and linear fog actually fade.

  • PE bounding box (issue #385). PE_XBOUND/PE_YBOUND and the CPU-visible read-backs were never
    extended from the drawn geometry; the box is extended once per primitive with the window extent of
    the vertices.

  • Video interface. The blitter assumed a fixed 640×480 picture instead of decoding
    VI_VERT_TIMING, so a title that copies 448 visible lines showed stale memory below them; the
    active lines are scaled over the window now, and interlaced modes carry half the height per field.
    The YUV→RGB conversion put red and blue in the wrong bytes of the output word (the RGB the VI
    produces is in (Blue, Green, Red) order, and the XFB chroma of a pixel pair is Y0 U0 Y1 V0).

  • The software pipeline's primitives. The line and point emitters built their quad by moving the
    window X/Y, which the clipper recomputes from the clip position, so lines and points could not be
    drawn at all; the offsets are formed in window space and carried into the clip position, and the
    point size is floored at one pixel. The clipper cut a hair in front of the near plane and threw
    away every primitive that lies exactly on it — the plane is inclusive on the hardware.

  • Other GFX fixes. The light-map render's scissor rectangle is handled; a draw-sync token is a
    synchronisation marker rather than a frame boundary; GXSetGPFifo is atomic with respect to the
    reader so a repoint cannot tear a command; the TEV register file keeps the 11-bit signed stage
    result (the wide alpha); the pixel engine's display copy clears the whole colour buffer while a
    texture copy clears its own rectangle.


CPU, DSP and interrupts

  • Cache management on direct-store segments (issue #300). dcbf, dcbst and dcbz took the
    DSI of a direct-store segment, which is for loads and stores, not for cache maintenance. Super
    Mario Sunshine hung in the SDK's __OSInitMemoryProtection inside DCFlushRange; with the fix it
    boots, plays its intro FMV and reaches the scene after it (~870 finished GX frames and 160M Gekko
    instructions in the first 45 emulated seconds, where the machine used to stop 0.3 s in).

  • The DSP wait state. wait left the program counter on the instruction, so an interrupt
    handler's reti returned to the same wait and the code after it never ran. A wait now ends when
    the interrupt it waited for arrives. Found with Super Mario Sunshine, whose audio microcode parked
    on a wait after answering the CPU: with the fix the phase after the intro runs at ~0.8x real time
    instead of ~0.1x.

  • The Linux thread port. Thread::Suspend() called from the worker itself deadlocked on the
    non-recursive pthread mutex (the AI thread parks itself exactly that way), DduCore's DVD-audio
    thread looped inside itself and could never be suspended, and the ringleader called
    pthread_yield() once per basic block. Super Mario Sunshine ran at 3.7 MIPS with it and 81.9 MIPS
    without; a thread that has to wait now parks on the condition variable or blocks in its device.

  • The DSP-DMA copy is clipped to the region it starts in, and the AX DirectSound backend runs
    without a sound device instead of asserting.


Games

  • Metroid Prime. The intro FMV runs about three times faster than in 1.8 and the run reaches the
    title screen. The remaining gap to full speed is the game's AX audio driver, which is a large
    share of the retired instructions.

  • Super Mario Sunshine. Boots, plays its intro FMV and reaches the scene after it. The title
    screen that follows is still not drawn; that hang is a separate finding.

  • Super Monkey Ball 2. Showed no picture at all because it waited for the CP's FIFO read-idle
    bit, which the emulator never set; it now reaches ~371 finished frames in 15 seconds.

  • Star Wars Rogue Squadron III. No longer faults on an instruction page while its MMU handler
    maps the page.

  • Soul Calibur II. Renders its scene through the shader pipeline.


Known issues

  • The integrated GBA is not complete: the JOY bus (a GameCube controller on the link port) and the
    Game Boy Player's own boot protocol are not implemented, the official BIOS runs its boot but its
    picture does not appear (the emulator's own boot ROM is what boots cartridges globally), and a real
    Game Boy Color cartridge's picture is still work in progress. The full list is in
    testing/gba_bench/Readme.md.

  • The software GFX pipeline is experimental: anisotropic filtering, the round/field_predict
    motion-compensation modes, the anti-aliased EFB, EFB pixel types other than RGB8, the display
    copy's vertical filter and the YUV/4:2:0 modes, and the EFB CPU window (Cpu2Efb) are not
    implemented; the default remains the shader backend.

  • Bump mapping on the shader backend, the Z-texture environment in some paths, PE dither and a few
    SU flag fields are still deliberately unimplemented and documented in wiki/gfx.md.

  • Full JAudio microcode support (issue #71) is still in progress; the remaining DSP divergences from
    the hardware are recorded in testing/Readme.md.

  • The DSP recompiler is experimental and off by default. The Gekko recompiler needs a 64-bit x86
    host; 32-bit and non-x86 builds fall back to the interpreter.

  • The Linux build still has no sound and no input, and the Linux headless target has no OpenGL
    offscreen backend (the Visual Studio one does).

  • RVZ support is read-only: bzip2/LZMA groups and Wii images are not implemented.

  • The CpSpec group of the native test suite hangs when two of its cases run in one process (each
    passes alone); the native suite is otherwise at 510 tests.

  • Some titles still fail to render or hang; compatibility is a work in progress.


Up next

Release 2.0 will be the next major version, the peripheral release: it is aimed at the emulation of the console's peripheral devices — the EXI bus and the devices on it (memory cards, the Broadband Adapter, the RTC), the serial interface and the pads, the disk interface and the drive, and the link port the GBA side already has — and it should close out many of the emulator's features, so that the list of "not implemented yet" above shrinks to what genuinely does not matter.

After that the project moves into its steady state: planned, methodical improvement - performance work and bug fixing release after release, rather than another feature push. The
architecture is in place; what remains is making what is there faster, more exact and more complete.



--1.8--

Version 1.8 is the performance release, and most of the speed came from removing work rather than adding it. Gekko gained an x86-64 recompiler that lifts the emulated CPU to over 150 MIPS, the periodic device threads stopped polling the time base in tight loops, and the interrupt lines of the DSP, the disk interface and the serial interface were re-derived from the hardware documentation.
The last part matters more than the numbers: with the interrupt storms gone, Zelda: The Wind Waker boots to its title screen and Animal Crossing finally leaves its exception handler. The release also adds compressed RVZ disc images, a full controller-settings dialog for the SDL build, and a new application icon.


Highlights

  • Gekko recompiler. A new x86-64 basic-block compiler (src/gekkojit.*) keeps the guest GPRs in
    host registers and shares the interpreter's decode cache for everything it does not translate.
    Measured on the differential harness: 39.7 MIPS for the original interpreter, 58.6 for the inlined
    interpreter and 155.9 with the recompiler; ALU-heavy code gains 8.6x. jit 0 / jit 1 in the
    debugger switches engines at run time.

  • Paired-Single in SSE. Every arithmetic PS instruction and both quantised loads and stores
    (psq_l / psq_st) are translated to SSE/SSE2. The PS-heavy workload goes from 59 MIPS
    (interpreter) to 435 MIPS, the quantised-load workload from 57 to 191.

  • No more busy-waiting. The VI scan-out and the serial poll now run on the CPU thread from the
    tick it already advances, and the CP, AI DMA and DSP threads block on events instead of spinning on
    Core->GetTicks(). pong.dol went from 1.47x to 3.66x real time, Ikaruga from 2.16x to 4.58x and
    Luigi's Mansion from 0.62x (stalled) to 4.26x.

  • The aggregate interrupt lines now work. The DSP and disk-interface Processor-Interface lines
    ANDed the cause bits with their own mask bits, which is always zero; both now follow the OR of the
    latched, unmasked causes. The serial interface polls on the video line schedule instead of a fixed
    tick, which removes an interrupt storm that pinned titles inside their handlers.

  • Games. Zelda: The Wind Waker boots to its title screen and Metroid Prime presents its title
    screen; Animal Crossing gets past the audio ARQ handshake, reads its disc and displays the Nintendo
    logo.

  • RVZ disc images. Compressed GameCube images in Dolphin's RVZ container (Zstandard) can be
    mounted read-only, the same way an ISO or GCM is.

  • SDL controller settings. The SDL build has a pad backend of its own and the full
    "Configure Controller N" dialog: keyboard and game controller bindings side by side, analog sticks
    and triggers, Clear / Default / OK / Cancel.

  • New icon. The cube icon was recoloured with the blue palette of the logo and regenerated as
    pureikyubu.ico (16–256 px, 32 bpp); the SDL builds embed the same pixels and call
    SDL_SetWindowIcon.


Added

Gekko

  • x86-64 recompiler for Gekko basic blocks (src/gekkojit.h, src/gekkojit.cpp). Every block
    records the translation state (generation counter) it was compiled under, so a write to a BAT,
    SDR1, HID0/HID2 or MSR, or an rfi, tlbie, icbi, dcbst or cache flush, drops all
    blocks in O(1). Untranslated instructions run through Interpreter::ExecuteDecoded and share the
    interpreter's decode cache, so both engines decode identically.

  • 8192-entry decode cache keyed on the pc and the fetched instruction word, and a direct-mapped TLB
    of 2048 page translations instead of an unordered_map that allocated a TLBEntry per page.

  • Paired-Single translation (src/gekkojit_ps.cpp, -DGEKKO_JIT_PS=0 builds it out): add, sub,
    mul, div, res, rsqrte, madd, msub, nmadd, nmsub, muls0/1, madds0/1, sum0/1,
    sel, mr, neg, abs, nabs, merge00/01/10/11 and the _d recording forms. The x86-64
    encoder, host register roles and GekkoRegs offsets moved to src/gekkojit_x64.h.

  • Quantised paired loads and stores (psq_l / psq_st): all eight indexed/non-indexed,
    update/non-update and W = 0/1 forms, with HID2[PSE], HID2[LSQE] and the RA != 0 update rule
    decided at compile time. The GQR is read at run time by Jit::PsqLoad / Jit::PsqStore, so
    writing one invalidates nothing.

  • Debugger command jit 0 / jit 1; 32-bit and non-x86 hosts, and -DGEKKO_JIT_DISABLED builds,
    fall back to the interpreter.


Disc images

  • Read-only support for Dolphin's RVZ container (src/rvz.cpp): the container header and its SHA-1
    checksums, the data-entry and group tables, the None and Zstandard compression methods and the
    "packed" representation that regenerates runs of pseudo-random disc junk from an LFG seed.
    bzip2/LZMA groups and Wii images are reported as unsupported. Zstandard is bundled under
    thirdparty/zstd.

  • .rvz is accepted everywhere a disc image was accepted: the command line, the game selector and
    the file dialogs.


Input and user interface

  • SDL pad backend (src/padsdl.cpp): a pad can be driven by the keyboard (SDL_GetKeyboardState,
    SDL scancodes in VKEY_FOR_*) and by an SDL game controller (GCKEY_FOR_* button and axis
    identifiers) at the same time; the Nth connected controller drives the Nth port. The left stick
    drives the main stick, the right stick the C stick and the triggers the analog L/R (digital L/R
    fire at 50%); the keyboard 50%/100% keys still work, and both sources are summed and clamped
    instead of overflowing int8_t.

  • The controller-settings dialog in the SDL UI (uisdl.cpp, ported from the Win32
    PADConfigDialogProc): Options → Controllers → Port 1..4 opens "Configure Controller N" with the
    Buttons table on the left and the Control Stick / C Stick tables on the right, each with its own
    Control | Keyboard | Gamepad columns. Clicking a binding arms capture, the next key, controller
    button or stick/trigger deflection becomes the binding and the event is swallowed; Esc cancels and
    modifier keys and F1–F12 are skipped for the keyboard.

  • Game controllers are opened and closed by the thread that pumps SDL events
    (PADUpdateControllers) and read by the emulation thread through a spin-locked snapshot;
    PADLoadConfig is public so the dialog can apply the bindings without restarting emulation.

  • New application icon: the cube artwork recoloured to the logo palette
    (src/res/pureikyubu_icon.svg) and embedded in src/res/pureikyubu_icon.h for SDL_SetWindowIcon
    (the SDL port has no .rc resources); pureikyubu.ico regenerated at 16–256 px, 32 bpp.


Command line and tooling

  • pureikyubu <file> (or --image <file>) loads and runs the image immediately instead of stopping
    in the game selector, and --help prints the accepted options to the console and to the report
    log, so EMU_LOG=<file> pureikyubu --help works too.

  • --bench <file> [seconds] runs the emulator unattended and reports throughput plus
    Gekko::CpuStats (translated vs interpreted instructions, basic-block length, re-translations and
    their causes, cache-line fills and every access that leaves the cache for the PI/MEM);
    BENCH_PROFILE=1 adds a host cycle breakdown and BENCH_STATS=1 a guest instruction histogram.

  • The Gekko differential harness gained BENCH_SPIN* modes (which reproduce and dissect the thread
    interference on the time base), BENCH_PS_TEST, BENCH_FUZZ_PS and the psq workload mix;
    build_win.bat builds the harness with MSVC so the two compilers can be compared on one workload.


Testing

  • Serial-interface spec tests (testing/): 17 new cases covering the SIPOLL round trip and its
    independent halves, per-channel output buffers, read-only input buffers, COMCSR bit positions and
    TSTART, the poll interval / budget / vertical-blank anchor, per-channel enables and the read-status
    interrupt with its mask.

  • Command Processor spec tests (testing/gfx_cp_spec_test.cpp, 19 cases) written from
    specs/architecture/command-processor.md: the VCD/VAT field positions, the VAT component and
    colour byte counts, the vertex attribute order and gx_vtxsize.

  • Five aggregation tests for the DSP interrupt line (each cause raises the line only through its own
    mask, a real ARAM DMA completion reaches the PI, a masked cause latches without a line, an
    acknowledgement drops it, and the mailbox interrupt reaches the PI), plus the corrected DSP vector
    test and the latched CPU→DSP request test. The suite is at 367 tests.


Changed

Performance and threading

  • The VI scan-out and the serial poll are executed by the CPU thread from the tick it already
    advances (GekkoCore::Tick / TickN call Flipper::Update), so VI timing is exact instead of
    sampled by a thread.

  • The Command Processor thread blocks on a new portable Event (utils.h) that the CPU thread
    signals when a batch of FIFO entries is due (CommandProcessor::TickSync); the drain stays at the
    emulated tickPerFifo rate.

  • The AI DMA thread is woken by DSP::AITickSync (about every 6750 ticks at 48 kHz) and parks itself
    with Suspend() when no DMA is armed. The DSP thread is woken by DspCore::TickSync once per
    DspWakeTicks and drains a batch, so the emulated DSP no longer self-throttles by re-anchoring its
    deadline on every poll; the mailbox hold-off is now a tick deadline
    (DspCore::MailboxHoldTicks).

  • mtmsr and rfi drop the compiled blocks only when MSR[IR]/[DR] actually change; the OS
    toggles MSR[EE] around every critical section and dropping the whole cache for it cost ~85,000
    needless re-translations per second.

  • Address translation, the tick and the branch-condition helpers are really inlined now
    (GEKKO_INLINE).


Behaviour and configuration

  • The Windows and SDL builds keep separate settings: DefaultSettingsWin.json / SettingsWin.json
    and DefaultSettingsSdl.json / SettingsSdl.json. The two ports run from the same directory and
    used to overwrite each other's configuration; each executable now embeds its own file names.

  • The serial interface follows the documented register layout: SICnOUTBUF is the CPU-visible
    register with the shadow read by the transfer engine, SICOMCSR latches CHANNEL and INLNGTH on an
    ordinary write and TSTART reads back as a pending bit, and the poll register drives SIPoll(line)
    directly.

  • The Flipper device threads and the emulator use the same Event abstraction, so the timing code is
    portable between Win32 and SDL.


Fixed

Interrupts and timers

  • DSP/DI aggregate interrupt lines. Every cause in CDCR and DI_SR has its own mask bit, and the
    pairs are not adjacent; both re-evaluation helpers ANDed the group of causes with the group of
    masks, so the lines were never asserted at all. Animal Crossing hung in its ARQ wait because the
    ARAM DMA completion never reached the CPU. Both lines now follow the OR of (cause && its own mask) and are re-evaluated from DSPUpdateInt() / DIUpdateInt() whenever a cause or a mask
    changes.

  • A cleared cause can drop the line. The lines were only ever dropped inside one write_cdcr /
    write_sr branch; clearing one cause while another was still latched left the line high with
    nothing left to clear it, so the CPU re-entered the external-interrupt handler on every rfi.
    Animal Crossing was executing ~11,500 exceptions a second.

  • Serial-interface poll storm. The emulator polled on a fixed 0x10000-tick interval (about ten
    times the hardware frame rate) instead of the SIPOLL video-line schedule, so RDSTINT was
    re-raised continuously. On Animal Crossing the emulated time for a fixed wall clock went from 3.8 s
    to 74 s, exception entries from 165,000 to 2,213 and VI interrupts from 0 to 2,178; Zelda's
    exception count dropped from 49,635 to 2,157.

  • DSP microcode vectors. The interrupt vector base was taken from the live CDCR use-rom bit
    instead of the program that is running; Zelda's microcode installs a CPU→DSP vector at 0x000E in
    IRAM and the loader's vectors made it bounce the command. CDCR bit 1 is now latched as a request
    and re-tried until the core accepts it through the TE3/ET gate, so a request that arrives while
    te3 is closed is no longer lost.

  • DSP/AI threads. DspAIOpen cleared its state with memset, which also wiped the Event the AI
    thread waits on (a wait on a zeroed handle returns immediately, so the thread silently went back to
    spinning); the clear is now an explicit DspAIControl::Reset.


Graphics

  • Presentation (issue #361). pe.cpp switched the VI output off after the first
    GXDrawDone/PE token, which stopped scan-out for the rest of the run in titles that keep
    presenting; the gate had no hardware meaning and is gone. In the SDL build the VI picture was
    written into the same window that carries the OpenGL context, so the two presenters alternated; the
    GL backend owns the window while it runs and the VI still decodes the XFB and counts frames.

  • Command Processor. CP_FIFO_COUNT was a stale software register and is now the live FIFO
    occupancy ("the distance between the write and the read pointer"). The break-point status bit was
    gated on CP_ENABLE[FIFOBRKINT] instead of [FIFOBRK], so a title that armed the break point
    without the interrupt never saw the flag. CommandProcessor::ResetFifoProcessor() drops the
    CP-side command buffer so a new stream starts clean.

  • VI. VI_DTV_STATUS (0xCC00206E) is implemented instead of falling through to the default case,
    where it returned 0 and made the SDK's VIGetTvFormat() always report NTSC.

  • Texture decoder (issue #359). IA4 walked the tiles column first while the tiles of an image are
    stored row by row, so a map wider than one tile came out transposed (and the report encoder mirrored
    the same walk, which is why the gallery never showed it); the debugger's gxtexdump read the colour
    fields after the decoder had serialised the texels into the R,G,B,A byte order GL wants, which
    rotated the channels of every non-grey format; C14X2 masked its palette index with 0x3ff instead of
    0x3fff; and the transparent fourth colour of the CMPR three-colour mode lost the RGB of the
    endpoint average.

  • Texture uploads. DecodeTexture compares the description and an FNV-1a hash of the raw texture
    bytes (plus a TLUT generation for the palette) and keeps the GL image when nothing changed; the
    upload reuses the image storage with glTexSubImage2D, the sampler parameters and the mip chain
    are only re-applied when TX_SETMODE0/1 really change, and TX_INVTAGS (GXInvalidateTexAll)
    marks every map for a decode again.


Interface and platform

  • File → Reopen, a dead menu entry, is wired to the last loaded image (the LASTFILE setting the
    selector already maintains) with F3 as its accelerator.

  • Writes to the SI input buffer registers no longer halt the emulation.

  • The Gekko sources are valid UTF-8 (two CP1252 comment bytes in gekko.cpp / gekkoc.cpp were
    normalised).

  • The DSP unit tests link a counter double (testing/dsp_test_support.cpp) now that the DSP sources
    are built without gekko.cpp, and the mailbox hold-off tolerates the null Core the tests use.


Known issues

  • Bump mapping, indirect texturing, the Z-texture environment and Cpu2Efb are not emulated yet.

  • PE dither and a few SU flag fields are deliberately unimplemented and documented in
    wiki/gfx.md.

  • Full JAudio microcode support (issue #71) is still in progress; the remaining DSP divergences from
    the hardware are recorded in testing/Readme.md.

  • RVZ support is read-only: bzip2/LZMA groups and Wii images are not implemented.

  • The recompiler needs a 64-bit x86 host; 32-bit and non-x86 builds, and GEKKO_JIT_DISABLED, fall
    back to the interpreter.

  • The Linux build still has no sound and no input.

  • Some titles still fail to render or hang; compatibility is a work in progress.



--1.7--

Version 1.7 is the largest update since the project started. The graphics backend was rebuilt around the two programmable stages of Flipper, the emulator finally has automated tests for its two most complex subsystems, the SDL build became a first-class front end with sound and a game selector, and a set of CPU and memory bugs was cleared that lets a Linux kernel boot on the emulated console.


Highlights

  • Shader-based graphics. The Flipper Transform Unit and Texture Environment Unit are emulated by
    GLSL 3.30 vertex and fragment shaders on the OpenGL 3.3 core profile; the fixed-function pipeline is
    gone. The Command Processor now feeds the XF instead of reaching into the graphics blocks.

  • The GFX pipeline is tested. 340 unit tests run the real CP → XF → SU → RAS → TX → TEV → PE
    pipeline against an OpenGL context and publish an HTML report with 262 rendered reference pictures.
    The suite found and fixed a large batch of long-standing rendering bugs.

  • The DSP core is tested against the hardware. 177 tests, 1822 golden ALU vectors and 435
    circular-buffer vectors; the DSP IROM is fully disassembled and analysed.

  • GC-Linux boots. Fixed GQR/paired-single decoding, the cache-coherent MMU hash-table walk, the
    decrementer model, mtcrf, lswi/lswx and the SI registers. A 2004 kernel reaches the gcnfb
    console with the Broadband Adapter driver loaded.

  • Games. The Metroid Prime intro movie plays and the game reaches its title screen; Zelda: The
    Wind Waker gets past its boot; Ikaruga's THP title movie has correct colours; PONG runs again.

  • SDL front end. Sound, an integrated file browser, message boxes and the full game selector
    (banners, Game IDs, sorting, filters, jump-to-letter) are available in the cross-platform build.


Added

Graphics

  • GLSL 3.30 vertex shader emulating the XF: geometry and texture matrix multiplies, the projection
    combine, per-vertex lighting for both colour channels and all texture coordinate generation modes.

  • GLSL 3.30 fragment shader emulating the TEV: up to 16 combine stages, the full
    operand/bias/sub/clamp/shift datapath, Rev-B K constants, fog and the final alpha function. Both
    shaders are static and receive the register state as uniforms.

  • Independent decoding and binding of all eight texture maps, mip-level generation and the corrected
    sampler wrap/filter modes.

  • Full XF register read-back over CP_XF_ADDR / CP_XF_DATAL / CP_XF_DATAH, and the
    CP → XF → SU → RAS command path.

  • GFX_DUMP dumps every n-th rendered frame to a BMP; EMU_LOG=<file> writes Debug::Report output
    to a file.


Audio

  • SDL audio backend (audiosdl.cpp) for the cross-platform build.

  • ARAM and AI behaviour brought in line with the hardware documentation (ACRS/ACWE register names,
    the 32-byte block transfer, address masks, CDCR, the AIVR streaming volume, the ARAM DMA performed
    to completion inside the AMBL write).


CPU and memory

  • GQR and paired-single extensions decoded with the ISA-manual bit numbering, with the conversion math
    factored into src/gqr.h.

  • Cache-coherent MMU hash-table access (ReadHashPte / WriteHashPte).

  • Hardware-accurate decrementer: one exception per underflow, the request latched across MSR[EE], a
    positive value clearing a pending request, a negative value at reset.

  • Command-line options --ipl (boot the IPL menu without a disc) and --no-disc (start with the DVD
    lid open).

  • Typed register views for every Flipper block; the Command Processor is a standalone Flipper entity;
    memory reset (MemRst).


User interface and debugger

  • Game selector in the SDL build: DOL/ELF/ISO discovery from PATH, banner decoding (RGB5A3 with
    alpha, SJIS titles), Game IDs, the Icon/Title/Size/Game ID/Comment columns, all Win32 sort rules,
    small icons, file filters and Add Directory.

  • Jump-to-letter navigation in the selector (issue #92).

  • Independent video-output window with filtered events so that a covered selector ignores clicks meant
    for the game.

  • JDI (JSON Debugger Interface) server, its UI view and the Flipper instance passed through the debug
    tree.

  • ImGui file browser and message boxes in the SDL UI.


Testing and tooling

  • GFX unit-test rig with a real OpenGL context, transform-feedback probing and an HTML report
    (gfx_report.html) with rendered pictures; 340 tests.

  • DSP unit tests: instruction coverage, an independent flag reference model, 1822 golden ALU vectors,
    435 circular-buffer vectors, the mailbox protocol and the IROM boot path; 177 tests.

  • GQR tests (17 cases) and the published IROM disassembly in testing/DspIrom.md.

  • Visual Studio 2026 projects (MSVC v145); the test project references the emulator sources instead of
    copying them.

  • JSON tables embedded into the source, so no external data files are required (PR #332).


Changed

  • The graphics backend no longer uses the fixed-function OpenGL pipeline; the immediate-mode draw
    paths were removed and quads are expanded into triangles.

  • Vertices are accumulated into a VBO and drawn with glDrawArrays / glDrawElements.

  • The CP pushes command words into the XF and polls it for readiness instead of driving the rasterizers
    itself; register loads are word-wise, so a partial load no longer halts.

  • The Flipper register access was refactored so every block owns a typed register view; the PI FIFO
    moved into pi.cpp; 8-bit hardware access was removed.

  • VI_DISP_POS is read-only; the PI CONFIG register reflects the memory and DVD reset state
    correctly.

  • XF matrix and colour registers start from the state GXInit establishes (verified against a real
    bootrom run).

  • A whole-frame display copy is a frame boundary of its own; partial copies and texture copies are
    not.

  • The imgstore folder moved into wiki; the wiki was extended and reorganised.

  • The project adopted Karpathy-style behavioural guidelines in .clinerules.


Fixed

Graphics

  • Black bootrom screen: the copy-engine clear erased the frame that was still to be displayed.

  • Metroid Prime: the intro FMV stayed black because movie frames were never presented (the display
    copy is now a frame boundary); whole frames stayed black because the frame-begin clear used the live
    PE_COPY_CLEAR_* registers (now captured when the copy command is issued) and because
    GX_IDENTITY / GX_DTTIDENTITY were never initialised.

  • Ikaruga THP movie: garbled colours from the wrong GQR field numbering, the shared
    K-constant/colour register storage and the swapped channel/register fields of the single-channel
    K selectors.

  • CMPR decoding (uninitialised colour table, endpoint alpha, the transparent fourth colour of the
    3-colour mode).

  • Texture maps 4–7 could never be programmed (the I4–I7 register block was decoded as I0–I3), and
    multi-texture applied each map to the previous texture unit.

  • The SU scissor was compiled out; TexMode0.lodbias, TexMode1.minlod/maxlod, SU_LPSIZE,
    SU_SSIZE/SU_TSIZE, RAS1_SS0/SS1 and GEN_MODE.flat_en are now applied; TEV fog F-select 1 and 3
    gained their law; the PE colour/alpha update bits are applied as documented.

  • R and B are swapped correctly when dumping a frame to BMP.

  • A draw command is no longer stalled by a partial register load.


CPU

  • mtcrf used the RS field as the CR mask and the CRM field as the register number (out-of-bounds
    gpr[128] access), which crashed the Linux kernel in ip_auto_config_setup.

  • The MMU hash-table walk was incoherent with the write-back data cache, so freshly created PTEs were
    invisible and the kernel looped on the same DSI fault.

  • The decrementer request was edge-triggered and lost with interrupts disabled; a fully
    level-sensitive variant livelocked the Bootrom. It is now a latched per-underflow request.

  • lswi / lswx dropped the last accumulated word when the byte count was a multiple of four,
    corrupting GCC struct copies.

  • Every non-zero GQR was decoded with the wrong bit numbering; the paired-single conversion rules and
    the HID2 gating were corrected.


Audio / DSP

  • The sliced ARAM DMA dropped Metroid Prime's audio DMA requests and hung the game right after the
    intro movie; the block is now performed to completion inside the AMBL low-word write.

  • div lost the low quotient bit; norm and div were stubs.

  • lsf / asf shifted in the wrong direction in every register form.

  • ModifyFlags did not mask to 40 bits; neg p, unsigned multiplies, the logic-family flags,
    accumulator/immediate rules, clr, neg/negc, addp, lsl16, tst p and the
    multiply/accumulate family were corrected.

  • trap did not advance the PC (reti re-trapped forever) and the interrupt vectors ignored the
    program base.

  • Circular addressing wrapped at the modifier length instead of the 2^n aligned block; the decoder
    accepted reserved words; the mailbox could tear a message pair; packed memory accesses latched the
    wrong operand.


Interface and platform

  • Writes to the SI input buffer registers no longer halt the emulation (the Linux gcn-si driver
    resets them during initialisation).

  • The DOL/ELF loader was fixed; PONG runs again.

  • The PI FIFO wrap bit, the pad stick, the DVD banner loading on Linux and the Linux build were
    fixed.

  • Project files migrated to Visual Studio 2026; warnings cleaned up; a conflicting copy of fmt
    removed.


Known issues

  • Bump mapping, indirect texturing, the Z-texture environment and Cpu2Efb are not emulated yet.

  • PE dither and a few SU flag fields are deliberately unimplemented and documented in
    wiki/gfx.md.

  • Full JAudio microcode support (issue #71) is still in progress; the remaining DSP divergences from
    the hardware are recorded in testing/Readme.md.

  • The Linux build has no sound and no input yet.

  • Some titles still fail to render or hang; compatibility is a work in progress.



--1.6--

Fixed major cache crashes, resulting in more games launching.

  • Fixed DSI/ISI crashes

  • Disabled mailbox DSP delay

  • Fixed mtsr instruction decoding

  • PE Regs



--1.5--

Fixed CPU-DSP interrupt handling causing some games to load

Also some work has been done to improve the GFX engine (experimental shader support, disabled for now).

  • Fixed debugger script command

  • Wiki main.md

  • DMEM/IMEM moved to DspCore as it should

  • gfx refactoring 

  • DI log commands 

  • Fixed CPU->DSP interrupt



--1.4--

All Gekko caches are enabled by default, so there may be issues in games that used to work. To be fixed.

  • Fixed step-over in Gekko debug (F10)

  • Fixed locked cache

  • User-friendly Debug::Hal

  • DSP debugger is intergrated into common system-wide debugger

  • Added imgui+SDL2 as submodules

  • Cui + imgui

  • Simplified uisimple.cpp

  • The debug console is ported for Linux using imgui (used by uisimple.cpp by default)



--1.3--

  • TEV regs

  • Simplified ras.cpp

  • Fixed Luigi's model spikes

  • Cache hack

  • Fixed errordemo

  • PI unmapped access debug messages + DecrementerStep

  • Fixed Gekko reset + GEKKOCORE_CACHE_DISABLE_HACK macro



--1.2--

The emulator goes through a complete boot cycle from the reset vector (BS1), IPL (BS2) and loading the game from the DVD (Apploader) without applying HLE.

  • Fixed short ARAM DMA issue

  • BS2 PAL Revision boot

  • wiki stub

  • UI cleanup

  • Next VI / Next PE debug commands

  • Gekko cache support

  • Fixed Linux build



--1.1--

  • Removed old GX code (integrated in core)

  • DSP small refactoring and notes

  • Portable debugui.cpp

  • GFX refactoring

  • Fifo fixes after refactoring



--1.0--

We've done a little rebranding. Dolwin is now called pureikyubu (PlayCube in Japanese).

By this event the code base was cleaned up and to somehow get out of the black hole of 0.xxx versions I made version 1.0 (About dialog updated).


Show Previous Changes


--0.1545--

Fixed a nasty bug with restarting.



--0.15--

The release work was evenly spread over previous releases, but the bulk of the new DSP core was implemented in this version.

For example, Luigi's Mansion now comes to the forest scene.


PS. Also keep in mind that there is a nasty bug exists - after starting the game, you need to restart the emulator every time --(Dolwin.exe)


--0.14--

This release is the beginning of work on improving the graphics system.


A new component has been added to the emulator - GX, which contains the general code for emulating the command processor (CP)

Old DolwinVideo code has been cleaned up from old stuff. It is still used as a reserve parachute until a more advanced backend appears.


In parallel, improvements have been made in DSP emulation, but there is still a lot of work to be done in this area

Fixed minor bugs that got out after splitting the emulator core and user interface.


Some games show slight progress: Luigi's Mansion now shows 1 screen further from the main menu, and Wario Ware even reaches the game, but with big slowdowns and graphical bugs. You can even hear how Wario is unhappy with this circumstance (there is sound in the game).


--0.132--

This is the first attempt at porting Dolwin to Linux-like operating systems.


Let me remind you that Playground is a "smaller" version of Dolwin, without a graphical interface and with Null backends (that is, no graphics, sound, etc.). All that is visible to the user is the emulator debug messages.


Porting was done in WSL2 environment on Windows, Ubuntu version 18.04.


--0.131--

Intermediate release related to source code refactoring. It was decided to make the emulator cross-platform.


Now the whole core is in a separate executable module (for Windows it is DolwinEmu.dll), and the user interface and debugger in the main executable file.


From the user side no special improvements are noticeable, games still show only splash screens at best :-)


There may be various bugs in the interface after refactoring, they will be fixed over time.


--0.13--

The release with unlucky number 13 was split into two releases: 0.130 and 0.131. These superstitious programmers..

  • MMU support

  • Support cache emulation

  • Dynamic recompiler (JITC)

  • Improved emulation of graphics FIFO

  • Many other minor improvements


All these things were added experimentally and at the moment the cache and recompiler are temporarily disabled. If you are a developer, you can rebuild Dolwin with the cache and recompiler turned on.


The cache is enabled with the command CacheDebugDisable 0.


The recompiler is turned on in SRC\Core\Gekko.cpp, line 20 (but the interpreter must be disabled).


Between 0.130 and 0.131, I will try to fix all incomprehensible bugs with cache and recompiler so that they are included in the next release.

bottom of page