
1.9.1 / 21-09-2026
Performance
Features
Pureikyubu
Accuracy
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
4000800hwaitstate register), the Game Pak uses the first/second access times ofWAITCNTwith the 128 KByte non-sequential rule, a DMA transfer spends its cycles while it runs, and a transfer no longer rewrites the
SAD/DADregisters.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, not28h..2Fh), the four decompressors were read out of the official image and fixed (HuffUnComp's bitstream starts atsource + 4 + (treeSize + 1) * 2and 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: acknowledgingIFis 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 andGetAsyncKeyStateback 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_ENVare the raster and texel swap-table selects, not a mode and a fixed component pick; the four tables are the low four bits ofTEV_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 demotev-swap's doughnuts come out grey (0.0% saturated pixels, was 69%).
Added
The Game Boy side of the portable core
A
GbApusuite (fifteen tests) and aGbPpusuite 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-acid2on the DMG,dmg-acid2on a CGB in DMG mode (shade for shade through the emulator's own grey ramp) andcgb-acid2on 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 Nchanges the sample rate and--no-hpfturns 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 inHost::Present, so a.gbaand a.gb/.gbcrun get it from the same code, andvideo.lcdEffectturns it off (on by default).
The cores are not touched: the frame the screenshots and the debug interface read stays clean.--keysis 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.cppandgekkojit_layout_x64.hare 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 ingekkojit_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.txtcarry both module sets and let the
preprocessor pick; the emulator projects define_WIN32_WINNT=0x0601andWINVER=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_benchgrew theBus,HleBios,GbBus,GbApu,GbPpu,AudioandSettingssuites; 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) andpad.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.jsonand
Data/Settings.json); the Windows/SDL split is gone, and theDOLDEBUGkey, 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 sameUIReportcommand 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 callsDvdMountSDK.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 waySaveSettingsdoes, with the paths going back throughAddSelectorPath, 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
NR33and
not by a timer —SOUND3CNT_Xbit 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, andSOUNDCNT_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_Land optionallyDADon a repeat — notSAD, 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 theSAD/DADregisters, 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 atVCOUNT=162with 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-DFFFFFFhon 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 followsCNT_Hbit 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 undocumented4000800h.An idle
WAITCNTbit 15 (the read-only Game Pak Type Flag) can no longer be set by a write, andDISPCNTbit 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, withVSyncOff/VSyncOnat28h/29h. Before, the whole block was at28h..2Fhand1Ahwas calledDivArm2, so a game'sSoundDriverInitwas answered with a division and its music never
started.HuffUnComp,BitUnPack,LZ77(both write variants),RLand 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.MidiKey2Frequses 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
68736D53hand its0FB0hsize, thepcmbuflayout (two
0630hbyte halves at+0350hand+0980h), the two FIFO DMAs (B600h: repeating, 32-bit, with
an incrementing source),SOUNDCNT_H210Eh, the timer 0 reload for every playback frequency index, andSoundDriverMain, 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
IFbefore dispatching, because the official BIOS does not either. Metroid Fusion's handler readsIFto 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_ENVare the
swap-table selects (GXSetTevSwapMode's raster and texel selects) and the four tables are the low four bits ofTEV_KSEL_2k/2k+1;TEV_KSELresets 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 ofTEV_KSEL, the cull mode of
GEN_MODE, the blend mode ofPE_CMODE1— so without it the swap tables would be clobbered by
the firstGXSetTevKColorSel. 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), andDIVIDE_2truncates 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/s8aliases;
json.cppis self-contained (the standard library,verify.hand 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,
andF2opens and closes the window as before.
Fixed
The portable machines
The Game Boy's clock (found by capturing
zelda.gband 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 - NRx1and counted up to 64 — backwards. The pulse and wave dividers ran at
(2048 - x) * 2and(2048 - x)instead of* 4and* 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.
BeginVisibleLinecalledEnterMode(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 newLY— never reachedIF. 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=LYCthat 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 aLYCwrite 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:LCDCbit 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 (OPRIwas 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/OBP1were 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 readingHDMA5during an active HBlank transfer returned bit 7 set, which Pan Docs
defines as "Not Active". The mode 3 OBJ penalty11 - (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 needsDISPCNTbits 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 inBLDCNTand 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/BGxVOFShold 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 atY=240..243fill the gaps the wrapped lines leave.The V-Counter match interrupt is an edge, not a level.
UpdateVCountMatchrequested it
whenever the match condition was true on an evaluation, and aDISPSTATwrite evaluates it too,
so a program that wrote the register back whileVCOUNTstill equalled its setting took two match
interrupts for one line. Minish Cap writesDISPSTATat 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
itsDAMEDAMEfailure 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/STMIAskipped an instruction.ThumbMultipleTransfertreated 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 itsBitUnPackparameter block withLDMIA r1!,{r5,r7}, so the
instruction that followed was skipped, the block stayed zero,BitUnPackread 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.SVBKmaps 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 value0xF8
that aliased0xC000-0xCFFFand0xD000-0xDFFFonto 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 at0xDFFB, read back0x0000and 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. SinceAddUtf8Stringis
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::ReadSamplescall site in the tests handed amaxFramescount 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_Hbit 10 on every channel (see Changed), which is
why the Minish Cap engine's per-frame 32-bitDMA0block 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 atD000000h-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
DISPCNTbit 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.mdwith the addresses of its test table.The GBA's sound output stage is not modelled:
SOUNDBIASis 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 (
RCNTbit 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 twoSettingstests that
comparebuild/Data/GBASettings.jsonwith the defaults byte for byte need that file to have LF
endings; a Windows checkout withcore.autocrlfreports 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
PputestRegisterReadBackand thememorytest 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/gbais 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.gbaruns a
cartridge andpureikyubu game.gbcruns 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_PIPELINEand thegxpipelinecommand 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,F2starts and stops a session.An MCP server.
pureikyubu --mcpruns 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.cppand 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,--selftestreports 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 andHALT), the memory map with the open bus and theWAITCNTwaitstates, 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.binfor a real BIOS
image and--no-gba-bootromto skip the BIOS. The window, the sound and the input come from SDL2,
and the bindings live inData/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,gbcartand thegb*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 aSoftprefix: 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 withgxpipeline 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.cppdrives 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 andreport.pyputs 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 ofData/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,
cpregsanddspstate— each answering the decoded state of its block.A portable session (GBA and Game Boy) is its own front end:
F2starts and stops it, the panels
come from thegba*/gb*commands, and an MCP client that launchedpureikyubu --gbagets 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:DrawTextplaced 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 --mcppublishes 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.cppbuilds the tool table from thecanrecords 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 1start and stop the server from the debugger console, andMcpRequest <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 likeFileLoadcannot 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
hwprofileMarkdown table, thehwprofile imagePNG next to the session
and thehwprofile osdoverlay in the emulated picture (hwsod 1, stored asHW_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 repurposeduisimple.cpp) is the entry point and the whole UI of a console build
with no window: a bare image or--iplruns 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 isstd::chrono.gfxnull.*runs the GFX pipeline against a null GL layer underGFX_NULL, so the emulation is
untouched while nothing is drawn and no driver is needed. The Visual Studio headless target defines
GFX_OFFSCREENinstead and draws with a real OpenGL context on a window that is never shown, into
a framebuffer of its own, soGFX_DUMP/GFX_EFB_DUMP/gxshotstill produce frames; the Linux
CMakeHEADLESStarget keeps the null backend.scripts/VS2026/pureikyubu_headless.vcxprojis 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'sDispatchcalls, 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/LoadDromand the DSP-DMA paths.Dsp16::DoDmacopied 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 1in the debugger.testing/dsp_jit_test.cppchecks 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 stayswchar_t.
Util::WstringToStringandUtil::StringToWstringare 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::FileOpenis_wfopen_s
on Windows andfopenof 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/WriteConsoleOutputWon
Win32,SDL_TEXTINPUTin 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/.elfexecutables, the.iso/.gcm/.rvzdisc images,
the memory card saves, the command line, the guest's device registers and DMA engines, the console
scripts and the.mapsymbol files — and about ninety defects were found and fixed. The recurring
shape was a bound that existed only asassert(), 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.--selftestruns the whole startup sequence without a window and exits with the number of failed
steps, andtesting/startup_cases.shruns it against deliberately broken settings files and random
files renamed to.doland.rvz.testing/security_test.cpppins every rule down, including two
property tests. The full report is inwiki/security.mdand on the site (docs/security.html).
Tooling: the DolphinSDK demo sweep (issue #385)
DVD::MountSdkbuilds 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…, orMountSDK <path>inautoexec.cmd).testing/dolphinsdkholds the sweep methodology and its scripts:sweep.ps1runs a list of demos
and captures the video window,summarize.pyturns a sweep folder into a table andreport.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/lhzuxandlha/lhau/lhax/
lhaux) through aJitReadHalftrampoline. 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), notRA.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 currentMSR/BAT/SDR1/TLB state produces, soException,rfi,mtmsrand the
mtsprofSDR1/the BATs/HID0no longer callInvalidateAll. 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 theWPARtimeout 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 newcpfifocommand 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
GBAstatic library the emulator links the way it links SDL2,
plus thegba_benchconsole 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.vcxprojand the CMakeHEADLESSoption build the windowless
target;src/jdispecs.cppholds 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_TREFword 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.
GXLoadPosMtxIndxand 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 up2^24 / (b_mag - (z >> b_shf)), the reciprocal of a fraction of the depth range
rather than1 / b, so exponential and linear fog actually fade.PE bounding box (issue #385).
PE_XBOUND/PE_YBOUNDand 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 (theRGBthe VI
produces is in (Blue, Green, Red) order, and the XFB chroma of a pixel pair isY0 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;GXSetGPFifois 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,dcbstanddcbztook 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__OSInitMemoryProtectioninsideDCFlushRange; 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
waitstate.waitleft the program counter on the instruction, so an interrupt
handler'sretireturned 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
SUflag fields are still deliberately unimplemented and documented inwiki/gfx.md.Full JAudio microcode support (issue #71) is still in progress; the remaining DSP divergences from
the hardware are recorded intesting/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
CpSpecgroup 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 1in 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.dolwent 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/HID2orMSR, or anrfi,tlbie,icbi,dcbstor cache flush, drops all
blocks in O(1). Untranslated instructions run throughInterpreter::ExecuteDecodedand 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 anunordered_mapthat allocated aTLBEntryper page.Paired-Single translation (
src/gekkojit_ps.cpp,-DGEKKO_JIT_PS=0builds 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/11and the_drecording forms. The x86-64
encoder, host register roles andGekkoRegsoffsets moved tosrc/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, withHID2[PSE],HID2[LSQE]and theRA != 0update rule
decided at compile time. The GQR is read at run time byJit::PsqLoad/Jit::PsqStore, so
writing one invalidates nothing.Debugger command
jit 0/jit 1; 32-bit and non-x86 hosts, and-DGEKKO_JIT_DISABLEDbuilds,
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, theNoneandZstandardcompression 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..rvzis 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 inVKEY_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 overflowingint8_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;
PADLoadConfigis 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 insrc/res/pureikyubu_icon.hfor SDL_SetWindowIcon
(the SDL port has no.rcresources);pureikyubu.icoregenerated 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--helpprints the accepted options to the console and to the report
log, soEMU_LOG=<file> pureikyubu --helpworks 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=1adds a host cycle breakdown andBENCH_STATS=1a 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_PSand thepsqworkload mix;
build_win.batbuilds 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 andgx_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/TickNcallFlipper::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
emulatedtickPerFiforate.The AI DMA thread is woken by
DSP::AITickSync(about every 6750 ticks at 48 kHz) and parks itself
withSuspend()when no DMA is armed. The DSP thread is woken byDspCore::TickSynconce per
DspWakeTicksand 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).mtmsrandrfidrop the compiled blocks only whenMSR[IR]/[DR]actually change; the OS
togglesMSR[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
andDefaultSettingsSdl.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:
SICnOUTBUFis the CPU-visible
register with the shadow read by the transfer engine,SICOMCSRlatches CHANNEL and INLNGTH on an
ordinary write andTSTARTreads back as a pending bit, and the poll register drivesSIPoll(line)
directly.The Flipper device threads and the emulator use the same
Eventabstraction, 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 fromDSPUpdateInt()/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_srbranch; 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 everyrfi.
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, soRDSTINTwas
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
te3is closed is no longer lost.DSP/AI threads.
DspAIOpencleared its state withmemset, which also wiped theEventthe 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 explicitDspAIControl::Reset.
Graphics
Presentation (issue #361).
pe.cppswitched 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_COUNTwas 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 onCP_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'sVIGetTvFormat()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'sgxtexdumpread 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.
DecodeTexturecompares 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 withglTexSubImage2D, the sampler parameters and the mip chain
are only re-applied whenTX_SETMODE0/1really change, andTX_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 (theLASTFILEsetting 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.cppwere
normalised).The DSP unit tests link a counter double (
testing/dsp_test_support.cpp) now that the DSP sources
are built withoutgekko.cpp, and the mailbox hold-off tolerates the nullCorethe tests use.
Known issues
Bump mapping, indirect texturing, the Z-texture environment and Cpu2Efb are not emulated yet.
PE dither and a few
SUflag 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 intesting/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/lswxand the SI registers. A 2004 kernel reaches thegcnfb
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_DUMPdumps every n-th rendered frame to a BMP;EMU_LOG=<file>writesDebug::Reportoutput
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 intosrc/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 intopi.cpp; 8-bit hardware access was removed.VI_DISP_POSis read-only; the PI CONFIG register reflects the memory and DVD reset state
correctly.XF matrix and colour registers start from the state
GXInitestablishes (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
imgstorefolder moved intowiki; 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_DTTIDENTITYwere 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/SS1andGEN_MODE.flat_enare 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
mtcrfused 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 inip_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/lswxdropped 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.divlost the low quotient bit;normanddivwere stubs.lsf/asfshifted in the wrong direction in every register form.ModifyFlagsdid not mask to 40 bits;neg p, unsigned multiplies, the logic-family flags,
accumulator/immediate rules,clr,neg/negc,addp,lsl16,tst pand the
multiply/accumulate family were corrected.trapdid not advance the PC (retire-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-sidriver
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
SUflag 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 intesting/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
scriptcommandWiki 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).
--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.