Work in progress
Write-ups of the problems met along the way and how they were solved, and what changed in each release.
Dev log
-
2026-10-09
Full-Precision Textures: Undoing the 4444 Cut Inside the Game's Own Driver (WIP)
Run pumpitea and the faces on the song select screen and the folds in the BGA look blotchy. It looks like an emulator defect, but the cause is inside the game's executable. PIU draws through OpenGL and links a whole Mesa 3.x driver into the executable to turn that OpenGL into Glide; that driver cuts every color texture to 4 bits (ARGB4444) or 5/6 bits (RGB565) per channel before the hardware sees it. The original 8-bit image stays in the game's memory afterwards. This post records finding that original and adding a quality option that uploads it instead, only when it is verified to be exactly the same texture.
-
2026-10-05
Post-Processing Shaders: An Arcade Monitor on Top of the Glide Frame (WIP)
The original PIU board sent its 640×480 picture to an arcade CRT monitor. rePIU draws that picture at 2x in a window on a modern monitor, and the more exact it is, the further it gets from "the screen of the day". This post records adding post-processing shaders that put an arcade-monitor look over the finished frame at presentation only; the framebuffer the guest sees does not change by a byte.
-
2026-08-12
The Four Bytes That Were Never There: A Colour Profile, a Missing Checksum, and the Wrong Suspect
pumpit8 (Pump It Up: The Rebirth) cleared ROM/CHD mounting, executable loading, JAMMA, and security-chip initialization, then terminated with a 0xC0000005 access violation about 97 seconds in while reading a background animation.
-
2026-08-06
The Clock That Ran at Half Speed: From a Note-Jumping Report to a One-Line Default
The reported symptom was one line: "the notes and the BGA lose sync with the music and jump."
-
2026-07-28
Hunting the Missing Difficulty Dots: Four Wrong Answers and Two Real Bugs
On the MUSIC SELECT screen, the difficulty dots are large crisp red circles on original hardware and a few blue pixels in rePIU. Everything else on that screen — the font, the background, the four discs — looked right. Tracking down that single symptom took five instrumented runs and two wrong conclusions, and ended up fixing three unrelated defects.
-
2026-07-21
Glide Render Pipeline: From Black Screen to First Pixels — Work in Progress 3
The previous post was about how fast execution runs — introducing a native AOT dynamic translator to cut single-step trap overhead.
-
2026-07-13
[WIP] rePIU Progress: Native AOT Dynamic Translation and Performance Improvements
Until now, rePIU has largely focused on basic initialization in Legacy mode and establishing the surrounding HLE (High-Level Emulation) environment. However, relying entirely on single-step traps for every original x86 instruction brings substantial overhead. To overcome this, the recent work branch heavily focused on introducing and refining a Native AOT (Ahead-of-Time) dynamic translator.
-
2026-07-10
Preserving DOS4GW Execution: Work in Progress 1
The core of this progress is a step-by-step foundation for running the original 32-bit x86 DOS/4G code inside a Win32 process without rewriting the game logic in C++. The work started with a non-executing analyzer for PIU.EXE, then grew into LE object mapping, fixup decoding, internal relocation dry-runs, relocated image buffers, Win32 process memory placement, guest stack switching, and observation-driven HLE trap handling.
Release timeline
-
2026-10-16
v0.0.225 LATEST
Changes since v0.0.224 (issue #76). The game can be played with the keyboard and gamepads on the web page, with the same key layout as on Windows and Linux. Release builds (Windows, Linux) behave as before.
Full release notes
Changes since v0.0.224 (issue #76). The game can be played with the keyboard and gamepads on the web page, with the same key layout as on Windows and Linux. Release builds (Windows, Linux) behave as before.
Resolved issues
issue Summary Commit #76 keyboard and gamepad input on the browser page (the input part of phase 4 unit 2); the issue stays open for the in-game check 3f4c8e8Changed
Keyboard. Until now the web build had no input, so F5 (COIN1) reloaded the page.
- While the game runs, the page sends keys to the engine, which takes them as a native window's keys, so the layout is
the same: coin
F5, testF1, serviceF2, clearF3, P1QESZC, P2 keypad79513, the OSDTab. - Keys go by the key's position (
KeyboardEvent.code), not the character, so the same key is the same input whatever the keyboard layout. - The browser's default action for game keys is stopped: F5 does not reload and Tab does not move the focus. Ctrl and Meta combinations, F11 (fullscreen) and F12 (developer tools) stay the browser's.
- When the page loses focus or is hidden, every held key is let go of (as a native window does).
- The page lists the keys.
Gamepads. A pad the browser recognises with the standard mapping (most XInput pads) is handed to SDL as a gamepad, so the native pad bindings and chords apply as they are: coin on Back, the OSD on LT+RT+Y, exit on LT+RT+L3+R3. A pad without the standard mapping is not used, and the console says so once.
Verified
Configuration Check Browser in headless Edge, F5 does not reload the page Key name table core probe browser_key_code(ranges, named keys, every key of the default layout, unknown names): Win32, Linux x64 and i386, webWin32 directReleasecore probe 45/45, pumpitea stops normally after 40 s Linux x64 and i386 Release core probe 47/47 and 45/45 Web ( repiu,repiu_browser,repiu_core_probe)build; Node core probe 37/37 CI the Win32 Debug build and probes Known
- Whether the coin and the OSD react in the game, and whether gamepads work, has not been checked in a browser yet. On this PC headless browsers' file reads had slowed down and the CHD unpack stalled (the build before this change as well). #76 stays open for the user's check.
- The web build has no sound yet (the next unit).
- On Win32 a GL error is sometimes recorded at pumpitea's first texture upload. v0.0.224 has it too; it is #78.
- While the game runs, the page sends keys to the engine, which takes them as a native window's keys, so the layout is
the same: coin
-
2026-10-16
v0.0.224
Changes since v0.0.223 (a follow-up to issue #72). The browser page is easier to use: the ROMs can be picked as one folder, and what the engine is doing after Start shows on the page. Release builds (Windows, Linux) are unchanged apart from extra progress lines on a first run.
Full release notes
Changes since v0.0.223 (a follow-up to issue #72). The browser page is easier to use: the ROMs can be picked as one folder, and what the engine is doing after Start shows on the page. Release builds (Windows, Linux) are unchanged apart from extra progress lines on a first run.
Resolved issues
issue Summary Commit #72 follow-up pick the ROMs as one folder or as the ZIP and the CHD separately; show the CHD unpack's progress on the page 67b6e43Changed
Picking the ROMs. Until now one file field had to pick the ZIP and the CHD together, but the two usually sit in different folders (
roms/pumpitea.zip,roms/pumpitea/010209_1821.chd), so they could not be picked at once.- One folder: pick a folder holding the game's ZIP and its CHD, such as
roms, and the game is found; with several games, one is chosen from a list. A ZIP and a CHD side by side in one folder work too. The folder's other ZIPs go along, so a game that needs its parent ROM set (pumpite.zipand the like) works as well. - Separately: a ZIP field and a CHD field.
- Nothing is uploaded; the browser only reads the files.
A page that shows its progress. Nothing appeared for over a minute after Start because the engine printed no line while it unpacked the game's files from the CHD (736 files, 409 MB for pumpitea).
- The engine reports the unpack's start and its progress about every 16 MB, for example
pumpitea unpacked 47% (194/736 files, 192/409 MB). Windows and Linux print the same lines on a first run. - The page shows the engine's output beside the canvas (below it on a narrow screen), with the elapsed time, the line count and the latest line kept current, and a button that copies the whole log.
Verified
Browser: the user ran the game with the new page; in headless Edge, picking by folder and separately, starting, and progress lines within 3 s of starting. The picking rules: a Node test 11/11, and all 16 games found in this PC's real
roms/. Win32directRelease: 26 progress lines from an empty cache, the unpack done in 21 s. Web (repiu,repiu_browser): builds. CI: the Win32 Debug build and probes.Known
- The web build has no keyboard or gamepad input and no sound yet (the next unit), so F5 (COIN1) reloads the page.
- One folder: pick a folder holding the game's ZIP and its CHD, such as
-
2026-10-16
v0.0.221
Changes since v0.0.220 (issue #67). The browser host's second phase. The development
emulatedengine and the rex86 CPU emulator are built as WebAssembly, and run pumpitea under Node with no screen. Release builds (Windows, Linux) behave as before.Full release notes
Changes since v0.0.220 (issue #67). The browser host's second phase. The development
emulatedengine and the rex86 CPU emulator are built as WebAssembly, and run pumpitea under Node with no screen. Release builds (Windows, Linux) behave as before.Resolved issues
issue Summary Commit #67 Web port 2B phase 2: the Emscripten build of the emulatedengine and rex86, run headless under Node27e894dChanged
The web build runs the game. Until now the web build built only the shared core, which runs no game. It now builds the whole engine on the
emulatedmodel, with the game's code run by rex86 inside wasm.- Result (pumpitea, Node): about 6.1 MIPS over 30 s, close to native
emulatedon the same PC. The HLE boundary trace matches native up to the first wall-clock dependence (the image's placement aside). The screen is the Glide dummy and there is no sound (picture and sound come in phases 3 and 4). - Memory: the game's addresses sit at the same addresses in wasm memory, with Emscripten's data, stack and heap moved
above
0x10000000. - Web platform layer: virtual memory (stub → implementation), a synthesised host entry state (the selector values a Windows thread sees), a thread layer without threads, and "none" answers for the diagnostics. The 11 fixed-function GL calls the Glide backend still makes are temporary stubs that stop the run if reached.
- Running: build with
./scripts/build_web_wasm.sh --build-dir ~/repiu-weband runnode ~/repiu-web/repiu.js pumpiteafrom the repository root. - The unused
noneexecution model is deleted.
Verified
Web (Node):
repiulinks, core probe 36/36, pumpitea for 30 s, the boundary trace comparison. Win32directandemulatedRelease: core probe 44/44, pumpitea runs. Linux i386 and x64 (WSL): build and core probe. CI: the Win32 Debug build and probes.Known
- It does not run in a browser yet; next is phase 3, moving the Glide renderer to WebGL2.
- The launcher cannot be used on the web (its update check uses a thread).
- The heap-corruption crash in
emulatedteardown (one user run) has not reproduced and is being looked into (#65).
- Result (pumpitea, Node): about 6.1 MIPS over 30 s, close to native
-
2026-10-16
v0.0.220
Changes since v0.0.219 (issue #64, and #61 follow-ups). The browser host's first phase. The browser's single Worker has no threads, so the development
emulatedmodel now runs the game's code, the screen, the timer, the sound and the input on one thread, taking turns. Release builds (Windows, Linux) behave as before.Full release notes
Changes since v0.0.219 (issue #64, and #61 follow-ups). The browser host's first phase. The browser's single Worker has no threads, so the development
emulatedmodel now runs the game's code, the screen, the timer, the sound and the input on one thread, taking turns. Release builds (Windows, Linux) behave as before.Resolved issues
issue Summary Commit #64 Web port 2B: the browser host's design and phase 1 (the single-threaded host loop of emulated)3288e17#61 follow-up The three execution models in the rules and architecture documents; the Linux i386 build fixed 3288e17Changed
The single-threaded host loop. rePIU used to split into a thread running the game's code, a main thread for the screen, the timer and the window, and audio threads. On
emulatedone main thread now runs the game's code on rex86 for a while and, when the next timer tick is due or 2 ms have passed, does the host's work (window events, the screen, the timer, filling the audio), over and over.- Design: the browser host comes in four phases: 1) the single-threaded loop (this release), 2) the Emscripten build and a headless run under Node, 3) the Glide renderer on WebGL2, 4) the browser page (Worker, OPFS, AudioWorklet, input).
- Result (Windows, pumpitea): the user confirmed the logo, the opening video and the music. The HLE boundary trace before and after phase 1 matches up to the first wall-clock dependence.
- Throughput: the instructions run fell by 29%, but the buffer swaps are equal and the game's progress is equal or
ahead; what went is the loops the game runs while waiting (inferred). Instructions per second is therefore not a
measure of
emulated's progress. - Shared code: one turn of the host's work (
HostTurn) was taken out of the poll loop, and the three audio outputs and the input script can be run one step at a time.directandcacherepeat them on threads as before.
#61 follow-ups. The Linux i386 build, which had not compiled since v0.0.219 (
_mm_getcsr), is fixed. AGENTS.md, CODING_STYLE.md and ARCHITECTURE.md name three execution models.Verified
Win32
directRelease: core probe 44/44, pumpitea 3,600 frames in 70 s, clean exit. Win32emulatedRelease: core probe 44/44, the boundary trace comparison, the input script, window close and timeout, and the user's check (logo, opening, music). Linux i386 and x64 (WSL): build and core probe. CI: the Win32 Debug build and probes.Known
emulatedis still slow; on the measuring PC it did not reach the title.- One user run of
emulatedpast the music crashed with heap corruption during teardown. It has not reproduced and is the next task's (#65).
-
2026-10-15
v0.0.223
Changes since v0.0.222 (issue #72). The first unit of the browser host's fourth phase. The development web build puts the game's picture on a browser page for the first time. Release builds (Windows, Linux) behave as before.
Full release notes
Changes since v0.0.222 (issue #72). The first unit of the browser host's fourth phase. The development web build puts the game's picture on a browser page for the first time. Release builds (Windows, Linux) behave as before.
Resolved issues
issue Summary Commit #72 Web port 2B phase 4: the browser page, the engine in a Worker drawing to a WebGL2 canvas 59f2445Changed
pumpitea runs in a browser. Pick the ROM ZIP and the CHD on the page, and the engine runs in the page's Worker and draws to a WebGL2 canvas.
- Result (pumpitea, Chrome): the title about 4 minutes after start, demo play at about 400 s. The first 55 s or so go to unpacking 428 MB of game files from the CHD into memory. No sound or input yet.
- Files: the CHD is never loaded whole; only the parts asked for are read. ROMs are not hosted; the user picks them.
- Picture: the window and GL context work sits behind one boundary, so Windows and Linux use an SDL window and the browser the page's canvas.
- Running: the engine runs on one thread and hands the browser its turn once a frame.
- How to run: build with
./scripts/build_web_wasm.sh --build-dir ~/repiu-web --target repiu_browser, serve that directory withpython3 -m http.server, and open it in a browser.
Checked
Configuration Check Browser (Chrome) title and demo play on screen, no Worker errors Win32 Debug and Release render probe scenes pixel for pixel, post-process, letterbox, fullscreen, core probe Win32 emulated, Linux x64 and i386, web under Nodebuild, core probe, run CI Win32 Debug build and probes Known
- No sound or input (next unit). The game files are unpacked again every time (persistent storage is the unit after).
- Far slower than real time; playing properly needs wider translation in the CPU emulator, a separate piece of work.
-
2026-10-15
v0.0.222
Changes since v0.0.221 (issue #69). The browser host's third phase. The Glide renderer was rewritten to use only what OpenGL 3.3 core and WebGL2 (OpenGL ES 3.0) share. The Windows and Linux picture is as before, and a GPU driver with OpenGL 3.3 is now required (nearly every GPU since 2010).
Full release notes
Changes since v0.0.221 (issue #69). The browser host's third phase. The Glide renderer was rewritten to use only what OpenGL 3.3 core and WebGL2 (OpenGL ES 3.0) share. The Windows and Linux picture is as before, and a GPU driver with OpenGL 3.3 is now required (nearly every GPU since 2010).
Resolved issues
issue Summary Commit #69 Web port 2B phase 3: the Glide renderer on the GLES 3 / WebGL2 subset, checked on native GL 3.3 core 85071caChanged
The renderer now has a shape that can run in a browser. Until now it used old OpenGL's fixed function (immediate mode, the matrix stack, the alpha test and so on), which WebGL does not have.
- Drawing: immediate mode became a vertex buffer, and the projection, point size and alpha test moved into the shader. The color combine and fog calculations are unchanged.
- Screen buffers: the game draws into an offscreen pair of front and back buffers, and only finished pictures reach the window at the buffer swap. A browser clearing the screen after showing it, or showing a half-drawn frame, cannot touch the game's picture.
- Post-processing (crt, scanline, user shaders): reads the picture directly instead of copying the screen. User shader files are converted to the newer GLSL automatically and work as they are.
- OpenGL context: Windows and Linux ask for OpenGL 3.3 core. Without it the game runs with no picture and standard error says why.
Checked
Configuration Check Render probe zero differing pixels against four reference scenes captured on the renderer before the port (Debug and Release) Win32 Release pumpitea and pumpit8 pictures compared before and after, played by the user; vsync-OFF throughput as before (about 1,100 fps) Linux x64 and i386 (WSL) build, core probe, the title on 4.6 / 4.5 Core Profile Web (Node) links without the fixed-function stubs, core probe, pumpitea for 30 s CI Win32 Debug build and probes Known
- It does not run in a browser yet. Next is phase 4, the browser page.
-
2026-10-11
v0.0.218
Changes since v0.0.217 (issue #58). In the PIU10 games the arrows no longer step back during play or fall behind the music.
Full release notes
Changes since v0.0.217 (issue #58). In the PIU10 games the arrows no longer step back during play or fall behind the music.
Resolved issues
issue Summary Commit #58 Arrows stall, step back or jump during play: the song clock's MP3 position correction 27677cbChanged
The MP3 position is answered at tick time. The PIU10 games (pumpipx3 and others) advance a song clock on timer ticks, count the song position by the changes of the MP3 chip's frame-sync bit, and every 256 frames (about 6.7 s) set the song clock to that position; the arrows follow the song clock. The original timer runs evenly every 4 ms, but in rePIU the ticks arrive late and bunched (four at once per frame under Windows vsync). The game then read the bit at one instant several times, two changes cancelled, and the song position fell 52 ms behind each time: at the next correction the arrows stepped back and stayed late for the rest of the song.
A read inside the game's timer handler is now answered with the bit at the time its tick was due, and one outside with the bit at the latest tick the game received, the way the input keys already are.
Measured (Windows, pumpipx3, 300 s) Arrow move at a correction Before, with a screen capture running −194.6 to +5.4 ms Now, the same all +1.1 / −2.9 ms (within one tick) Now, eight songs played by hand 46 of 47 at +1.1 / −2.9 ms (the race behind the other one is fixed too) REPIU_PIU10_FRAME_SYNC_REPLAY=0restores the previous behavior.Diagnostics.
REPIU_PIU10_SONG_CLOCK_LOG=1prints how far the arrows moved at every correction, and the final report givesPIU10 song clock …and the per-frame tick histogramPIU10 song frame ticks …(moves of 8 ms or more are printed even when it is off).REPIU_TIMER_LATENCY_TRACE=<path>records where ticks are held.Verified
- Win32 Release: builds, the core probe (including the new
piu10_song_clock_censusandpiu10_frame_sync_history), the JAMMA timeline probe, automated plays and a hand-played session. - Linux x64 Release (WSL): build and the core probe.
Known
- Not played on Linux hardware or a Steam Deck (on WSL the MP3 does not advance).
- Ticks still arrive bunched once a frame, and when the game stalls for hundreds of milliseconds dropped ticks can move the arrows ahead; both continue in #59.
- Win32 Release: builds, the core probe (including the new
-
2026-10-10
v0.0.217
Changes since v0.0.216 (issue #55). A gamepad opens and drives the OSD, so in-game settings such as fullscreen, aspect, shader and textures can be changed on a Steam Deck with no keyboard.
Full release notes
Changes since v0.0.216 (issue #55). A gamepad opens and drives the OSD, so in-game settings such as fullscreen, aspect, shader and textures can be changed on a Steam Deck with no keyboard.
Resolved issues
issue Summary Commit #55 Open and drive the in-game OSD with a gamepad (LT+RT+Y) 33eba35Changed
Gamepad OSD. Pressing LT + RT + Y together on one pad opens and closes the OSD as
Tabdoes. While it is open the pad drives it: the D-pad moves between items,Atoggles a checkbox or opens and picks from a menu (the shader, for one),Bcloses an open menu or otherwise the OSD, and LT + RT + Y closes it too. Meanwhile pad input does not reach the game, and game inputs held as it opens are released; a button still held as it closes (such as theBthat closed it, the down-right panel by default) stays away from the game until released. The keyboard still reaches the game while the OSD is open. Steam Deck steps are in the launcher update guide.Verified
- Linux x64 and i386 Release: builds and the core probe, including the new
host_pad_input_osd_chord. - Win32: the CI Debug build and probes.
- A real pad (Xbox Series X, Linux x64): opened and closed twice with LT+RT+Y, keep-aspect and the shader changed
with the pad, no pad input reached the game while it was open, and the closing
Bdid not leak into the game.
Known
- Not checked on a real Steam Deck.
- An OSD opened with
Tabfollows the same rule, so the pad cannot play while it is open (the keyboard can).
- Linux x64 and i386 Release: builds and the core probe, including the new
-
2026-10-10
v0.0.216
Changes since v0.0.215 (issue #52). A gamepad alone can now quit the game and the launcher, a chord for the Steam Deck, which has no keyboard or close button. If you run v0.0.215, this is the first release its launcher's Update button can install.
Full release notes
Changes since v0.0.215 (issue #52). A gamepad alone can now quit the game and the launcher, a chord for the Steam Deck, which has no keyboard or close button. If you run v0.0.215, this is the first release its launcher's Update button can install.
Resolved issues
issue Summary Commit #52 Gamepad exit chord LT+RT+L3+R3 held for one second 8d0b378Changed
Gamepad exit chord. Holding LT + RT + left stick click + right stick click together on one pad for one second quits: in a game it ends the game as closing the window does and returns to the launcher (an argument run ends the process); in the launcher it closes the launcher and ends rePIU (back to Steam on a Steam Deck). Releasing sooner does nothing. The right stick click is bound to
CLEARby default, so the game sees CLEAR while the chord is held. The launcher's hint line names the chord, and the Steam Deck steps are in the launcher update guide.Verified
- Linux x64 and i386 Release: builds and the core probe, including the new
host_pad_input_exit_chord. - Win32: the CI Debug build and probes.
- A real pad (Xbox Series X, Linux x64): a one-second hold in a game ended it normally and returned to the launcher; the user confirmed the short press and the launcher quit on screen.
- The v0.0.215 release run passed every job on its first attempt, with no Node.js 20 warning and every Linux x64
host_thread_interruptclaim true (#50, #12).
Known
- Not checked on a real Steam Deck.
- The game sees CLEAR during the chord (the decided behavior).
- Linux x64 and i386 Release: builds and the core probe, including the new
-
2026-10-10
v0.0.215
Changes since v0.0.214 (issues #48, #50, a #12 follow-up). The launcher announces a new release and updates with one button, for setups like the Steam Deck where downloading and unpacking a release by hand is tedious; from this version on, the next release can be taken from the launcher.
Full release notes
Changes since v0.0.214 (issues #48, #50, a #12 follow-up). The launcher announces a new release and updates with one button, for setups like the Steam Deck where downloading and unpacking a release by hand is tedious; from this version on, the next release can be taken from the launcher.
Resolved issues
issue Summary Commit #48 Launcher update check and in-place install 8a1cc84#50 Move GitHub Actions off the Node.js 20 runtimes 8a1cc84#12 (follow-up, still open) Diagnosing the intermittent host_thread_interruptfailure in the Linux x64 release CI8a1cc84Changed
Launcher updates. When the launcher (
repiuwith no arguments) opens, it checks GitHub's latest release in the background. A newer one shows "rePIU v… is available." with an Update button at the top, reachable by gamepad, which downloads this build's archive, unpacks it only when it matches the SHA-256 GitHub reports, replaces only the archive's files, and restarts the launcher;roms\,cfg\andnvram\stay, and a failure puts the old files back. Win32 downloads through WinHTTP and Linux through the systemcurl; Linux turns the same process into the new executable (so Steam sees it still running) and Win32 runs the new launcher as a child. The button appears only in a folder unpacked from a release archive (aVERSIONnext to the executable equal to the running version); a build tree only gets the notice.[Launcher] check_updates = 0incfg\repiu.iniorREPIU_UPDATE_CHECK=0turns it off. Installing on a Steam Deck, troubleshooting and a test procedure are in the launcher update guide.CI. The workflows' actions moved to their Node 24 versions (
checkout@v7,cache@v6,upload-artifact@v7,download-artifact@v8). Thehost_thread_interruptprobe that intermittently stopped the Linux x64 release job prints its seven claims separately, and its target alternates between two functions so a biased interrupt position alone cannot fail it.Verified
- Linux x64 and i386 Release: builds and the core probe (including
launcher_update's 13 checks), locally and on GitHub's runners. - Win32: the CI Debug build and probes, with no warning from the new files.
- A real update on Linux x64, from a test folder pretending v0.0.212 to v0.0.213: download, verify, 10 files replaced, restart; old files cleaned at the next start; no install button in a build tree.
- v0.0.213's real tar.gz and zip unpacked by the new code byte-identically to Python's.
Known
- The update feature ships in this version, so an actual update from the launcher first happens with the next release.
- A real Win32 replacement and restart, and a real Steam Deck, were not checked.
- #12 stays open: one pass does not settle an intermittent failure, and a later failure will name its claim.
- Linux x64 and i386 Release: builds and the core probe (including
-
2026-10-10
v0.0.214
Changes since v0.0.213 (issue #45, a #34 follow-up). Fullscreen and the aspect ratio can be switched from the in-game OSD, and the choice lasts to the next run. In the launcher, a gamepad's
Astarts a game.Full release notes
Changes since v0.0.213 (issue #45, a #34 follow-up). Fullscreen and the aspect ratio can be switched from the in-game OSD, and the choice lasts to the next run. In the launcher, a gamepad's
Astarts a game.Resolved issues
issue Summary Commit #45 Fullscreen and keep-aspect options in the OSD and launcher, stored in cfg\repiu.inif21ac2e#34 (follow-up) A gamepad's Astarts a ROM set from the launcher listf21ac2eChanged
Fullscreen and keep aspect ratio. The top of the
TabOSD now has "Fullscreen" and "Keep aspect ratio". Keep aspect is on by default, keeping today's 4:3 picture with black bars; off, it ignores the ratio and fills the window or screen, windowed mode included. A change from the OSD,Alt+Enteror a double click is stored at once as[Video] fullscreenandkeep_aspectincfg\repiu.ini; the two checkboxes at the top of the launcher's Options use the same keys;REPIU_GLIDE_FULLSCREENandREPIU_GLIDE_KEEP_ASPECTwin over the stored values for that run. The next run starts the way it was left. The launcher window follows both values too: it opens fullscreen, switches withAlt+Enter, and with keep aspect on is drawn centred with black around it. The launcher's Options now read Fullscreen, Keep aspect ratio, vsync, Full-precision textures, Screen shader, Sound gain.Starting from the launcher with a gamepad. On the ROM set list, choose with the D-pad and press
Ato start, as Enter does.Verified
- Linux x64 and i386 Release: builds; the core probe, including the letterbox probe's stretch case and the launcher settings' two keys.
- Win32: a CI Debug build of every target,
repiu_glide_issue_probeandrepiu_aot_probe --timer-safe-point; the core and launcher probes and a run were not done on Win32. - Real runs (Linux x64, x11), confirmed on screen by the user: one store per OSD switch (14/14), the stored state
restored at start, leaving fullscreen on close not stored, the launcher's checkboxes, window and Options order,
and starting with the pad's
A.
Known
- No game was run on the i386 build (build and core probe only); the changed code is architecture-neutral.
- Switching under the Wayland video driver was not checked (every run used x11).
- Whether an OSD change in a game started from the launcher shows in the launcher it returns to was not checked separately.
-
2026-10-10
v0.0.213
Changes since v0.0.212 (issue #37). Textures are drawn with the game's original 8-bit color, so faces, clothes and backgrounds whose gradients broke into bands are smooth.
Full release notes
Changes since v0.0.212 (issue #37). Textures are drawn with the game's original 8-bit color, so faces, clothes and backgrounds whose gradients broke into bands are smooth.
Resolved issues
issue Summary Commit #37 Full-precision textures bypassing the embedded Mesa driver's 4444 cut f31b7ccChanged
Full-precision textures (on by default). The PIU executables carry a Mesa 3.x driver that turns OpenGL into Glide, and it cuts every color texture to 4 bits (ARGB4444) or 5/6 bits (RGB565) per channel before the hardware sees it. rePIU finds the original 8-bit image the driver still holds and uses it instead only when cutting it again equals the game's data byte for byte; any texture not confirmed is drawn as before. The memory and behavior the game sees do not change.
The arcade board showed the 4444 picture too, so this option goes beyond the original; turn it off for the arcade look. Switch it with "Full-precision textures (8-bit)" in the in-game
TabOSD (that run, at once), "Full-precision textures" in the launcher's Options ([Video] texture_full_precisionincfg\repiu.ini), orREPIU_GLIDE_TEXTURE_FULL_PRECISION=0(that run, over the launcher's value).Comparison screenshots and how it works are in the dev log.
Verified
- Win32 Release: build, the new
mesa_fx_texture_sourceprobe, the launcher probe and the core probe. - Linux x64 Release: build and the core probe.
- Real game: every upload replaced by its original in pumpitea (61/61) and pumpit8 (130/130) with no fallbacks, and the OSD toggle works.
Byte signatures in the executables show all 19 ROM sets carry the same Mesa build.
Known
- ROM sets other than pumpitea and pumpit8 have not been run; a texture that fails the check is drawn as before.
- Each full-precision texture keeps two extra copies in host memory (about 32 MB at most); GPU memory is unchanged.
- The Linux i386 build was not checked.
- Win32 Release: build, the new
-
2026-10-09
v0.0.212
Changes since v0.0.211 (issue #34). Gamepads and joysticks, USB dance pads included, now drive the game and the launcher.
Full release notes
Changes since v0.0.211 (issue #34). Gamepads and joysticks, USB dance pads included, now drive the game and the launcher.
Resolved issues
issue Summary Commit #34 Gamepad and joystick input for the game and the launcher 6e9cba3Changed
A standard gamepad works with no configuration: the first one connected is P1 and the second P2. LB and RB are the upper-left and upper-right panels, D-pad down and left the lower-left one, B and D-pad right the lower-right one, A and Start the center, Back inserts a coin, and the right stick click is CLEAR. TEST and SERVICE have no gamepad default, so a pad cannot open the operator menus by accident (they stay on F1 and F2).
To change the layout or use another device, mix pad names with key names in the
[Input]values ofcfg/<rom-set>.ini; devices without a standard mapping (USB dance pads and the like) are bound by button or hat number, as inP1_CENTER = S, Pad1_A, Joy1_Button5orP1_UP_LEFT = Q, Joy1_Hat1Up. Devices are numbered in connection order (Pad1–Pad4,Joy1–Joy8) and announce themselves on stderr as[repiu-pad] Pad1 connected: <name>. An input held by both a key and a pad stays held until both release, and losing window focus releases pads too. The full list of names is in §6.1 of the config file guide.The launcher works with a gamepad too: move with the D-pad, choose with A.
Existing config files: an input line you wrote yourself replaces the gamepad default as well; lines left commented out follow the new defaults. Newly generated config files list the gamepad names in their comments.
Verified
- Win32 Release: build, the new
host_pad_inputprobe, 94 config probe checks, the fullrepiu_aot_probechain and the full core probe. - Linux x64 Release: build and the full core probe.
- Real hardware: the user checked the game, the launcher and the default layout on a standard gamepad.
Known
- Analog stick directions and joystick axes are not supported.
- Dance-pad joysticks (
Joy<N>_names) were not tried on a real device. - The Linux i386 build was not checked.
- Win32 Release: build, the new
-
2026-10-09
v0.0.211
Changes since v0.0.210 (issues #30, #8, #31). The deferred guarded segment kill switches are gone, and two fixes that had been lost with an unmerged branch — the Win32 Release probe fix and the
getchHLE — are back.Full release notes
Changes since v0.0.210 (issues #30, #8, #31). The deferred guarded segment kill switches are gone, and two fixes that had been lost with an unmerged branch — the Win32 Release probe fix and the
getchHLE — are back.Resolved issues
issue Summary Commit #30 Retire the guarded segment load/pop/read kill switches (environment toggle cleanup, deferred item) cf3c73b#8 stack_bridge_contractfails in Win32 Release builds ofrepiu_core_probecf3c73b#31 INT 21h AH=07h/08h console input without echo ( getch)cf3c73bChanged
A fatal path waiting on
getchnow takes a key. The game's fatal handler waits with Watcom'sgetch(INT 21h AH=08h), and without that service reaching the path died on a nameless fault.AH=07h/08hnow read the BIOS keyboard buffer and, with nothing queued, let the guest ask again. This was Task 764 on 2026-10-01, missing because its branch was never merged.Three variables are no longer read:
REPIU_AOT_GUARDED_SEGMENT_LOAD,_POPand_READ; the paths are always on for the dynamic backend. They were kept as an escape hatch after v0.0.206 changed the slot internals, and v0.0.207 to v0.0.210 showed no regression.The Win32 Release core probe passes in full. The
stack_bridge_contractrefusal check relied on whatever the compiler left in EAX and failed only in Release; the engine was fine (Task 765's fix, missing for the same reason).New probe:
repiu_aot_probe --dos-console-input(also in the core probe).Development rules: AGENTS.md now says work continues on the current branch when it is not main, and how a pull request that resolves several issues closes them.
Verified
- Win32 Release: build; the full core probe including
stack_bridge; the fullrepiu_aot_probechain (exit 0); both Glide probes. - Linux x64 Release: build; all core probes pass.
- Linux i386 Release: all core probes pass.
Known
- No game run: the guarded segment defaults are unchanged, and
getchis reached only on a fatal path (checked on pumpipx3 back in Task 764).
- Win32 Release: build; the full core probe including
-
2026-10-09
v0.0.210
Changes since v0.0.209 (issue #25). Nine one-off diagnostics added for investigations that are already answered are gone. Every deleted block ran only with its variable set, so a run with nothing set behaves as v0.0.209 did.
Full release notes
Changes since v0.0.209 (issue #25). Nine one-off diagnostics added for investigations that are already answered are gone. Every deleted block ran only with its variable set, so a run with nothing set behaves as v0.0.209 did.
Resolved issues
issue Summary Commit #25 Delete one-off diagnostics for answered questions (environment toggle cleanup follow-up) b24ca84Changed
Nine variables are no longer read; setting them has no effect:
REPIU_GLIDE_CALL_AUDIT,REPIU_GLIDE_TEX_CENSUS,REPIU_GLIDE_DRAW_CENSUS,REPIU_GLIDE_TRI_CENSUS,REPIU_GLIDE_FRAME_DUMP,REPIU_DUMP_LFB_BMP,REPIU_GLIDE_VERTEX_DEPTH_CENSUS,REPIU_LOWMEM_TRACEandREPIU_AOT_PROBE_GUEST. Per-ordinal call counts and first arguments remain in the final log'sGlide call tracelines, texture format and size counts in the default texture census, and texture dumps inREPIU_GLIDE_TEX_DUMP(TGA).Final log: the
Glide vertex depth census …andAOT runtime cache probe …lines, printed only with those variables set, are gone; default output is unchanged.Performance:
REPIU_DUMP_LFB_BMPcalledgetenvon every LFB present; that call is gone.Kept: the candidates
REPIU_AOT_DBT_CALL_TRACE,REPIU_AOT_RETIRED_TRAP_PROFILE,REPIU_GLIDE_SETTER_CENSUSandREPIU_GLIDE_SETTER_PHASEstill work, since they measure a live mechanism or a guide or script uses them.Verified
- Win32 Release: build; the full
repiu_aot_probechain (exit 0); both Glide probes. - Linux x64 Release: build; all core probes pass.
Known
- No game run; the deleted code is not on the default execution path.
- The Linux i386 build was not checked.
- The Win32 core probe's
stack_bridgefailure predates this release (#8).
- Win32 Release: build; the full
-
2026-10-09
v0.0.209
Changes since v0.0.208 (issue #24). Three kill switches hidden behind diagnostic names and two default-off DOS/DPMI experiments are gone. A run with nothing set behaves as v0.0.208 did.
Full release notes
Changes since v0.0.208 (issue #24). Three kill switches hidden behind diagnostic names and two default-off DOS/DPMI experiments are gone. A run with nothing set behaves as v0.0.208 did.
Resolved issues
issue Summary Commit #24 Retire the kill switches hidden behind diagnostic names and two DOS/DPMI experiments (environment toggle cleanup, group 3) 5444d28Changed
Six variables are no longer read; setting them has no effect. The features stay always on and only the off switch is gone for
REPIU_GLIDE_DRAW_ENTRY_POINTS(drawing the point, AA and polygon entry points),REPIU_TIMER_TICK_BACKLOG(keeping owed timer ticks) andREPIU_JAMMA_SNAPSHOT(the input snapshot); the snapshot intervalREPIU_JAMMA_SNAPSHOT_USstill works, and0queries on every read. The experiments themselves are gone forREPIU_DOS4GW_MEMORY_PATH_PROBE(a PharLap marker on the DOS version reply) andREPIU_DPMI_1E7F_PROBE_SUCCESS/REPIU_DPMI_1E7F_TRACE(a fake success forINT 31h AX=1E7Fh, a call that came from stack corruption fixed long ago).Diagnostic output changed format; every removed value was always zero in a default run. The final log's tick line becomes
timer tick delivery due/injected/dropped/deferred/max-backlog/remaining(thebacklog-enabledandcoalescedcolumns go) and the gate line becomestimer tick in-gate due: N; the CD audio position census file loses itsticks_coalescedandticks_coalesced_in_gatecolumns — update any script of your own that reads these formats.Probe: the
repiu_aot_probeexpectation left stale by v0.0.208's removal of an execution time bucket is fixed, so the probe chain runs to the end on Win32.Scripts and docs: the backlog A/B script
task366is deleted andtask414follows the new log line; the frontier's toggle table shows the current state, and README's texture dump entry names the real variable,REPIU_GLIDE_TEX_DUMP.Verified
- Win32 Release: build; the full
repiu_aot_probechain (exit 0); the timer tick probe; both Glide probes; two alternating 60 s pumpit2a pairs against the v0.0.207 release (zero faults, 3,202 / 3,203 frames in the second pair). - Linux x64 Release: build; all 39 core probes pass.
Known
- The Linux i386 build was not checked.
- The Win32 core probe's
stack_bridgefailure predates this release (#8). - In the first pumpit2a pair the new build fell about a second behind after two hitches; the second pair matched down to the per-second fps, so it was judged run-to-run variation.
- Win32 Release: build; the full
-
2026-10-07
v0.0.207
Changes since v0.0.206 (issue #20). The kill-switch environment variables of 15 long-promoted features are gone. Every feature stays on, and a run with nothing set behaves as v0.0.206 did.
Full release notes
Changes since v0.0.206 (issue #20). The kill-switch environment variables of 15 long-promoted features are gone. Every feature stays on, and a run with nothing set behaves as v0.0.206 did.
Resolved issues
issue Summary Commit #20 Retire the kill switches of long-promoted features (environment toggle cleanup, group 1) 8f84450Changed
Fifteen variables are no longer read; setting them has no effect and the features are always on:
REPIU_AOT_DBT_PORT_IO_DISPATCH,REPIU_AOT_DBT_RETURN_MISS_DISPATCH,REPIU_AOT_DBT_DIRECT_EDGE_DISPATCH,REPIU_AOT_DBT_TIMER_SAFE_POINTS,REPIU_AOT_INLINE_CACHE_PATCH_INLINE,REPIU_AOT_DIRECT_RETURN_TABLE(the table sizeREPIU_AOT_DIRECT_RETURN_TABLE_BITSstill applies),REPIU_GLIDE_SETTER_ELIDE,REPIU_GLIDE_SETTER_ELIDE_TEXTURE,REPIU_GLIDE_SETTER_ELIDE_BATCH3,REPIU_GLIDE_SETTER_ELIDE_BATCH4,REPIU_GLIDE_DRAW_BATCH,REPIU_GLIDE_HOST_WAIT,REPIU_GLIDE_GATE_PUMP,REPIU_PORT_IO_DELAY_LOOPandREPIU_NATIVE_LINEAR_SPAN_REJECT_CACHE.Two summary lines changed format:
AOT inline cache patch direct/worker: N/0is nowAOT inline cache patches: N(the worker round-trip patch path is gone), and theGlide setter elision …line drops its three always-true batch flags.One behavior change: on the legacy backend (
REPIU_EXECUTION_BACKEND=legacy, kept for diagnosis) the reject cache can no longer be turned on withREPIU_NATIVE_LINEAR_SPAN_REJECT_CACHE=1. The defaultdynamicbackend is unchanged.Docs: the full survey is in
docs/analysis/environment-toggle-inventory.md; the deferred items (removing the abandoned opt-in experiments, and theREPIU_AOT_GUARDED_SEGMENT_LOAD/POP/READswitches) are indocs/TODO.mdwith resume conditions; the A/B procedures in three guides, includingglide-setter-elision-testing.md, are marked as concluded records — setter elision can no longer be switched off with an environment variable, so please report any visual problem as an issue with the log.Diagnostics:
repiu_aot_probegains standalone--inline-cache,--native-linear-span,--glide-draw-batchand--glide-setter-state-cacheflags.Verified
Win32 x86 Release: build; six probes (the four above plus
--selector-guardand--segment-restore); a 16-romset 30 s smoke alternating the v0.0.206 build and this one per game (zero exceptions on both sides, matching frames on 15 romsets); a 90 s pumpitea pair (post-logo gap 2.78 s / 2.79 s, identical port I/O delay-loop statistics).Known
- One pumpitp2 30 s smoke run ended with zero frames (no exception, guest in a wait loop). An interleaved retest ran clean three times on the new build and twice on the old one, and the same thing happened once during the v0.0.206 checks, so it is taken as an intermittent issue unrelated to this change.
- Linux was not built; only platform-neutral engine code changed.
-
2026-10-07
v0.0.206
Changes since v0.0.205 (issue #18). pumpitea's loading gap after the ANDAMIRO logo drops from ~29 s to ~1.5–2.7 s — shorter than the pre-regression baseline (v0.0.180, 5.0 s) — and the second stall around 47 s shrinks from 2.5–6.5 s to 1.4–2.1 s.
Full release notes
Changes since v0.0.205 (issue #18). pumpitea's loading gap after the ANDAMIRO logo drops from ~29 s to ~1.5–2.7 s — shorter than the pre-regression baseline (v0.0.180, 5.0 s) — and the second stall around 47 s shrinks from 2.5–6.5 s to 1.4–2.1 s.
Resolved: #18 (whole-cache segment re-patch on every ES change) — commits
612251c,804df40,7a5c88c,c6426c2,39c0620.New: shadow-authoritative i386 guarded segment slots (a dedicated probe proved a Win32 VEH resume never installs a guest selector into a physical segment register; the load, pop and memory-source-load slots now compare against the shadow and a per-register accepted pair and write the shadow natively, the read slot reads it unguarded, and timer injection enters the INT8 handler through its cache block); host-direct dispatch for hot INT 21h services (AH=2Ch/42h; AH=3Fh read is excluded because its guest-memory writes followed by the dispatcher's direct cache jump skip the reentry funnel's retirement checks and broke eight titles), promoted to default-on following the Port-I/O precedent (
REPIU_AOT_DBT_SUPERBLOCK=0restores INT3-only boundaries); newrepiu_aot_probe --selector-guardand--segment-restoreflags; and the full record of the three rejected re-patch-cheapening variants indocs/analysis/pumpitea-loading-segment-flip.md.Verified: Win32 x86 Release build, both probes, interleaved 90 s pumpitea measurements (gap 1.5–2.7 s, second stall 1.4–2.1 s, zero exceptions), and a 16-romset 30 s smoke matrix default-on.
Known: the fast/slow mode selector remains unidentified (EEPROM contents and pure warm-up excluded); an intermittent pumpit3a guest AV during loading (~1 in 4, EBX=0) is suspected to be a pre-existing race; the far-strcmp fallbacks (2,530 per 90 s, ~0.1 s) remain; the promotion and new slots are measured on Win32 only, with long-mode slots unchanged.
-
2026-10-05
v0.0.205
Changes since the previous release, v0.0.204 (issue #15). The in-game
TabOSD now spans the top of the screen and opens with the name, version, build date and Target Profile, and OSD and launcher text grows with the window.Full release notes
Changes since the previous release, v0.0.204 (issue #15). The in-game
TabOSD now spans the top of the screen and opens with the name, version, build date and Target Profile, and OSD and launcher text grows with the window.Resolved issues
Issue Summary Commit #15 The OSD in re2DJ's layout; OSD and launcher text that scales with the window d44a1e5New
OSD layout (#15). The same layout as re2DJ's OSD.
- Across the full width at the top of the screen, as tall as its content.
- First line:
rePIU v0.0.205 (Win/x86 Release) - Build …(written as in the window title). Second line: the running ROM set, as inTarget Profile : pumpit1. v0.0.204's Renderer section, the LFB toggle and the screen shader menu follow.
Text that scales with the window (#15).
- OSD text and spacing grow with the window height: 0.75x in a 1x window (640×480), 1.5x in the default 2x window, 3.375x in 4K fullscreen. Before, the text stayed at 13 px however large the window, and so looked ever smaller.
- The launcher shown without arguments can now be resized and maximised, and its text, table and buttons grow with it. The default size looks as before, and the vertical scroll bar it always showed is gone.
Font. The OSD and the launcher both draw with ProggyForever, Dear ImGui's scalable default font, as re2DJ does. Dear ImGui moves from v1.92.1 to v1.92.9 for it.
Verified
Configuration Check Win32 x86 Release build, repiu_core_probeandrepiu_aot_probe --imgui-ui-scale, pumpit1 OSD captures (1x, 2x, 4K fullscreen), launcher captures (default 960×640, maximised 3840×2054)Linux x64 Debug (WSL) build; the whole repiu_core_probepassesWin32 x86 Debug (CI) build and probes Known
- The new OSD layout was not checked on screen on Linux (it is the same shared code, and v0.0.204's OSD was checked on screen on Linux).
- At the launcher's default size, long titles reach the Status column's edge (as before); enlarging the window clears it.
- Earlier known issues remain: #7 i386 on Wayland (NVIDIA) failing to create
its window, #8 the Win32 Release core probe's
stack_bridge_contract, and #12 the intermittent Linux x64host_thread_interruptfailure in the release CI.
-
2026-10-05
v0.0.204
Changes since the previous release, v0.0.203 (issue #5). The in-game
TabOSD now opens with the OpenGL renderer drawing the picture, and warns when it is a software renderer.Full release notes
Changes since the previous release, v0.0.203 (issue #5). The in-game
TabOSD now opens with the OpenGL renderer drawing the picture, and warns when it is a software renderer.Resolved issues
Issue Summary Commit #5 The OpenGL renderer in the in-game OSD 7edcfa2New
A Renderer section in the OSD (#5). The top of the OSD opened with
Tabshows:- The renderer (for example
NVIDIA GeForce RTX 4090/PCIe/SSE2), its vendor and the OpenGL version. - The SDL video driver:
windowson Windows,x11orwaylandon Linux, noting when the engine chose Mesa's D3D12 driver on WSL. - A software renderer such as llvmpipe, softpipe, swrast, GDI Generic (Windows without a GPU driver), WARP or SwiftShader is shown in red with "Software rendering: no 3D acceleration". If the game runs unusually slowly, look here first.
It only displays; no GL call is added. The log gains one line,
GL vendor/version/video driver/software: …, next to the existingGL renderer:line.Verified
Configuration Check Win32 x86 Release build, repiu_core_probeandrepiu_aot_probe --gl-renderer-identity, the OSD captured in pumpit1 (RTX 4090,windows)Linux x64 Debug (WSL) build, the whole repiu_core_probepasses, the warning captured in pumpit1 withGALLIUM_DRIVER=llvmpipe(x11)Win32 x86 Debug (CI) build and probes Known
- The software check is a name list; a software renderer not on it shows its name without the warning.
- This feature was not run on Linux i386 or under Wayland (the code is the same).
- v0.0.203's known issues remain: i386 on Wayland (NVIDIA) failing to create its window
(#7) and the Win32 Release core probe's
stack_bridge_contract(#8). The intermittent Linux x64host_thread_interruptfailure in the release CI (#12) is being tracked.
- The renderer (for example
-
2026-10-05
v0.0.203
Changes since the previous release, v0.0.200 (Tasks 771–773, issues #4 and #6). The v0.0.201 release build failed and was never published, and v0.0.202 changed only documentation rules, so both are described here. Linux i386 is much faster (pumpit1 from about 1,990 to 3,190 frames in 60 s), and minimising the window no longer slows the game's time. The screen shader can be chosen on the command line.
Full release notes
Changes since the previous release, v0.0.200 (Tasks 771–773, issues #4 and #6). The v0.0.201 release build failed and was never published, and v0.0.202 changed only documentation rules, so both are described here. Linux i386 is much faster (pumpit1 from about 1,990 to 3,190 frames in 60 s), and minimising the window no longer slows the game's time. The screen shader can be chosen on the command line.
Resolved issues
Issue Summary Commit #6 Direct model (i386): timer ticks dropped while a hidden window blocks the vsync swap 6574861#4 Tasks as GitHub issues, and main merges through squash-merged pull requests dcc73e5Work from before the switch to issues: Task 771
77117bf(v0.0.201), Tasks 772–7736574861(part of v0.0.203).New
Glide calls linked on Linux i386 (Task 773). On Linux i386 the code cache sits near
0xE8000000, so thecall rel32that links a game's Glide call straight to its gate was never written, and every Glide call took a boundary path costing about 160 µs. A 32-bit instruction pointer wraps at 2^32, so the wrapped displacement is now written. Windows, whose cache sits low, never had this problem.- pumpit1, 60 s: about 1,990 → 3,190 frames (vsync off: 2,100 → 53,197); guest thread CPU 99.4% → 16.3%.
- pumpit8, 60 s: 439 → 3,288 frames.
Game time kept while the window is minimised (#6). A hidden window has its vsync swaps slowed by the compositor to about one a second, and Linux i386 dropped the timer ticks that piled up meanwhile, so the game's time fell behind (more than 4,200 in 12 s minimised). Once a swap wait passes 50 ms, ticks are now delivered during the wait. Behaviour and frames with a visible window are unchanged.
- Ticks dropped in 12 s minimised: pumpit1 4,456 → 21, pumpitea 4,451 → 54.
- Windows does not block the swap of a minimised window, so it never dropped ticks in bulk. The same path is on there, with the same behaviour.
Screen shader on the command line (v0.0.201, Task 771).
repiu pumpit1 --post-shader crt(or--post-shader=crt). It wins overREPIU_POST_SHADERand the launcher setting; a missing value is an error.New switches (default behaviour when unset)
--post-shader <id>— the screen shader for that run.REPIU_GLIDE_SWAP_WAIT_TICKS=0— turns off tick delivery during the swap wait (the earlier behaviour).REPIU_GLIDE_SWAP_WAIT_TICK_HOLD_MS— how long a wait lasts before delivery starts. Defaults to 50 ms on the direct model (Win32, Linux i386) and 0 ms on Linux x64.
Verified
Configuration Check Linux i386 Release (Ubuntu 26.04, RTX 4090, x11) core probe, normal runs and 12 s minimised runs of pumpit1, pumpit8 and pumpitea, alternating with the switch off Linux x64 Debug (same machine) build, core probe, no regression in pumpit1, three --post-shadercases, fullscreen round trips (Wayland and x11), the v0.0.200 archivesWin32 x86 Release (Windows 11) build, the two new probes (core and AOT), normal runs and 12 s minimised runs of pumpit1, pumpit8 and pumpitea, a 0 ms hold stress run (3,804 injections, same frames) Win32 x86 Debug (CI) build and probes Known
- Linux i386 on Wayland (NVIDIA) cannot create its window (#7).
With the 32-bit Wayland libraries installed, SDL picks Wayland and fails to create the EGL surface. Run
with
SDL_VIDEO_DRIVER=x11for now. - The Win32 Release core probe's
stack_bridge_contract=falselooks like a problem in the probe and is tracked in #8. - Glide calls the game makes indirectly (about 1.5%) still take the boundary path.
- v0.0.200's known issues (one frame right after a Linux window resize, leaving fullscreen on WSLg Wayland) remain.
-
2026-10-04
v0.0.200
Changes since the previous release, v0.0.197 (Tasks 764–770). v0.0.198 and v0.0.199 were tagged without release notes, so they are described here as well. Screen post-processing shaders (
crt,scanline) and a fullscreen toggle are in, and the original 4:3 picture is kept whatever shape the window takes. Vertical sync is on by default, and the GitHub release carries Linux i386 and x64 archives.Full release notes
Changes since the previous release, v0.0.197 (Tasks 764–770). v0.0.198 and v0.0.199 were tagged without release notes, so they are described here as well. Screen post-processing shaders (
crt,scanline) and a fullscreen toggle are in, and the original 4:3 picture is kept whatever shape the window takes. Vertical sync is on by default, and the GitHub release carries Linux i386 and x64 archives.New
Screen post-processing shaders (Task 768). One presentation-only pass runs just before the buffer swap; the framebuffer the guest reads is unchanged.
- Built-in
crt(curvature, beam-shaped scanlines, a mask and a vignette) andscanline..glslfiles in the libretro single-pass layout dropped intoshaders\join the list. - Chosen in the launcher's Options "Screen shader" (stored in
cfg\repiu.ini), withREPIU_POST_SHADER=crt(that run, over the launcher's value), or in game from theTabOSD (that run only, with Reload and parameter sliders). - The default is
none, which adds no GL call. - The brightness profile of a line is aligned to output pixels so scanlines show in the default 2x
window. The
crtcurvature defaults to 0.01. - Format and checks: the post-processing shader guide. Dev log: the WIP post.
Fullscreen toggle and aspect ratio (Task 769). Double-click the game window or press
Alt+Enterto switch between borderless fullscreen and windowed mode; the display resolution is left alone. Whether the window is resized or fullscreen, the picture keeps its original ratio with black bars around it, and the bars never reach what the guest reads back.Alt+1toAlt+4pick a 1x to 4x window in windowed mode. Before, there was no fullscreen and a stretched window stretched the picture with it.Vertical sync on by default (v0.0.198, Task 766). Without
REPIU_GLIDE_SWAP_INTERVALthe engine never requested a swap interval, so SDL's Wayland backend drew without vsync. It now always requests one once a GL window exists (the variable's value when valid, 1 otherwise).REPIU_GLIDE_SWAP_INTERVAL=0or unchecking "Wait for vertical sync" in the launcher turns it off.Linux release archives (v0.0.199, Task 767). The release workflow builds Linux i386 and x64 Release on ubuntu-22.04 next to Win32 and attaches them as
rePIU-v<version>-linux-<arch>.tar.gz. The C++ runtime is linked statically, so they need only libGL and glibc 2.35 or newer.Screenshots (Task 770). Screens from 16 ROM sets are in
docs/screenshots/, shown in the README and on the project site's introduction; the shader comparison shots are indocs/screenshots/shaders/.Site and credits (Tasks 764–765). The site gained a Pump It Up Exceed arcade layer, and
LICENSE(BSD 3-Clause),CREDITS.mdand a site Credits page are in.New switches (default behaviour when unset)
REPIU_POST_SHADER—none(default),crt,scanline, or a file name inshaders\.[Video] post_shaderincfg\repiu.ini— stored by the launcher.
Verified
Configuration Check Win32 x86 Debug build, repiu_aot_probe --post-shader,--glide-letterboxand--launcher,repiu_glide_render_probe --opengl-post-shaderand--opengl-lfb(Intel Arc), pumpit1 15 s withnone,crtandscanlineWin32 x86 Release build, 16 ROM sets captured for 90 s each (the screenshots), pumpit8 with an input script captured under none,crtandscanlineLinux x64 Debug (WSL) build, the three probes above (ad hoc build), the GL probe under x11 and Wayland × d3d12 and llvmpipe, pumpit1 30 s under x11 with four fullscreen round trips by real X input Known
- On Linux, the one frame right after a window resize can be drawn with the new viewport into the
old-size buffer, because Mesa resizes the buffer only at the next swap; the next frame is right. For
the same reason the letterbox check of
repiu_glide_render_probe --opengl-post-shaderfails on Linux (a problem in the check). - Leaving fullscreen on WSLg Wayland is slow and erratic (13 to 368 swaps, once not within 600). The engine requests it correctly; under x11 and on Windows it leaves at once. A real Wayland desktop was not checked.
repiu_aot_probeandrepiu_glide_render_probeare not built on Linux.- The Linux i386 build and the web build were not checked this time.
- v0.0.197's known issues (the Win32 Release core probe's
stack_bridge_contract=false, among others) remain.
- Built-in
-
2026-09-30
v0.0.197
Changes since the previous release, v0.0.195 (Tasks 761–762). v0.0.196 was tagged without a release note, so it is described here as well. pumpit8 on Win32 no longer stops after loading its first song and pumpipx3 no longer dies at start, and the Glide LFB high-precision presentation and the first in-game OSD are in.
Full release notes
Changes since the previous release, v0.0.195 (Tasks 761–762). v0.0.196 was tagged without a release note, so it is described here as well. pumpit8 on Win32 no longer stops after loading its first song and pumpipx3 no longer dies at start, and the Glide LFB high-precision presentation and the first in-game OSD are in.
Fixed (v0.0.197, Task 762)
pumpit8 on Win32 stopped after loading its first song. From v0.0.191 to v0.0.196 it stopped at the same place: having read the song, the game waits for the timer tick counter to reach 1200 (five seconds), and no tick reached that wait loop.
- Cause. Win32 (and Linux i386) run the guest's
iretas it is, so the engine never saw a timer handler return. The code a handler returned to ran in place unsupervised, and once it entered a loop without a trap there was no place to inject the next tick. Up to v0.0.190 the trace flag (TF) left in the injected frame happened to keep the code supervised; v0.0.191 (Task 736) removed it and the path was cut. Restoring that rule made the next run nest handlers until the stack had dropped 320 KiB, so it was not restored. - Fix. An injected timer frame returns to the return pad, a reserved page that faults when executed. From that fault the engine restores the real return address and execution state, sends code that runs in place back into the code cache and, now that the return is seen, switches on for Win32 the nesting, turn and chain rules (Task 751) proven on Linux x64.
- Result (Win32 Release, input script). pumpit8 for 100 seconds: 5,004 frames and the song playing at 60 fps with 22,655 of 22,757 ticks delivered, against 2,086 frames, a stop and 9,573 of 22,771 in v0.0.196; the song's file opens 11 seconds earlier. pumpitea, pumpit2a and pumpitpc for 60 seconds: 99.5% or more of the ticks delivered.
pumpipx3 on Win32 died with
Fatal error: unable to find entry point in DLL.It went away with the same change: three 30-second runs completed, and with the switch off two runs died as before. The change did not aim at it, and why the tick injection path decides that failure is not explained.Loading looked slow. With the dropped ticks now delivered the game's clock runs at its own speed; pumpit8 opens its song's file 11 seconds earlier under the same input.
New (v0.0.196, Task 761)
- Glide LFB high-precision presentation. 24/32-bit sources of
grLfbWriteRegionkeep their precision to the screen instead of being quantized to 565; the 565 buffer the guest reads is unchanged. On by default;REPIU_GLIDE_LFB_HIGH_PRECISION=0|off|falseturns it off. - In-game OSD.
Tabopens and closes it and never reaches game input. Its one item is the high-precision toggle, which takes effect at the next LFB write. - The PTX header census and the per-version asset generations are recorded (pumpitea is generation 2, PNG and PTX).
New switches and diagnostics (default behaviour when unset)
REPIU_TIMER_RETURN_PAD—0writes the real return address into the injected frame as v0.0.196 did (and pumpit8 stops again). The final report'stimer return pad pushed/returned/supervised/overflow/unmatched/abandoned/depth-max/depth-at-endline says how many returns were taken.REPIU_GLIDE_LFB_HIGH_PRECISION— above.- Core probe
timer_return_pad,repiu_aot_probe --timer-return-pad.
Verified
Configuration Check Win32 x86 Release pumpit8 100 s ×2, pumpitea, pumpit2a and pumpitpc 60 s, pumpipx3 30 s ×3, control runs with the switch off Win32 x86 Debug build, every core probe passes, aot probes ( --timer-return-pad,--pic-timer-in-service,--jamma-input-timeline,--shutdown-recovery-policy)Linux x64 Release and Debug build, every core probe passes, pumpit8 50 s (no change in behaviour) Linux i386 (WSL) build, every core probe passes, pumpit8 70 s without a fault The picture and the sound were checked by numbers only. No survey of every ROM set.
Known
- The core probe of a Win32 Release build fails with
stack_bridge_contract=false. v0.0.195's Release build fails the same way, so it is not new in this release. Debug builds pass. - Why pumpipx3 came back is not explained (above).
- Linux i386 under WSL runs at 4 to 13 fps, as before.
- The web build was not checked.
- v0.0.194's known issues (the music slowing down under WSL, among others) remain.
- Cause. Win32 (and Linux i386) run the guest's
-
2026-09-28
v0.0.195
Changes since the previous release, v0.0.194 (Tasks 756–760). A release that puts the source directories' rule in order; the games behave as they did. Code that depends on the OS is gathered in the platform layer, and the engine's difference by CPU architecture in an execution model. Two defects found while verifying are fixed with it (the Linux i386 build could not open its window on WSL, and the Win32 core probe aborted part way).
Full release notes
Changes since the previous release, v0.0.194 (Tasks 756–760). A release that puts the source directories' rule in order; the games behave as they did. Code that depends on the OS is gathered in the platform layer, and the engine's difference by CPU architecture in an execution model. Two defects found while verifying are fixed with it (the Linux i386 build could not open its window on WSL, and the Win32 core probe aborted part way).
Structure
v0.0.194 v0.0.195 Platform and architecture branches outside the platform layer ( src,include)162 102 Of those, in the engine, the runtime and headers 93 33 #ifasking about the architecture in the engineover 60 9 #errorchecks of model and diagnostic filesThe rule (Tasks 757–759)
- The engine, runtime, HLE and tool directories have no platform or architecture subdirectories.
- Code that depends on the OS is in
src/platform/<OS>/[<arch>/], and the platform layer uses none of the engine's types. Inside the platform layer, code splits by architecture intox86/andx64/. - The engine's difference by architecture is the execution model.
directruns the guest's bytes as they are (Win32, Linux i386),cacheruns the translated long-mode code cache (Linux x64), andnoneruns no guest (the web build). Shared code asks throughinclude/repiu/runtime/execution_model.h, and implementations that differ by model sit next to their shared file as<name>_direct.cppand<name>_cache.cpp. - A diagnostic built on one host, and a guard around a whole probe, are not turned into directories.
What moved
- The Linux platform layer is split into shared,
x86/andx64/, and the Linux x64 public headers are underinclude/repiu/platform/linux/x64/(Task 757). - The platform layer's
host_thread,host_time,host_error_streamandbuild_identity, and the compiler shim headers, are split by OS (Task 758, phase 1). - The five MSVC inline-assembly thunks and the stack switch moved from the engine to
src/platform/win32/. - The crash report, the host's own detail of a fault report, the choice of WSL's driver, symbol names, the trace dump, the telemetry's host reads and stopping a thread became functions of the platform layer.
- The execution trampoline's 742 lines for x86-64 (the return resolver, the entry into the cache, the
return traces) moved to
execution_trampoline_cache.cpp, and its x86 entries toexecution_trampoline_direct.cpp.
Fixed
The Linux i386 build could not open its window on WSL (Task 760) — v0.0.194's choice of the D3D12 driver looked for the driver file in a list that did not tell architectures apart, so an i386 process saw the x86-64 driver and set
GALLIUM_DRIVER=d3d12. The choice is now made only when the driver of the process's own architecture exists.The Win32 core probe ended part way (Task 758) — the
dos_console_deviceprobe threw afilesystem_erroron theCON:path, the core probe ended with exit code 3, and no probe after it ran.Also
- A GitHub Pages project site, in Korean and English (Task 756).
Checked
Configuration Check Linux x64 Release and Debug, Linux i386 built; core probe result lines the same as before the directory work Win32 x86 Debug built; results of the core probe, 18 aot probe modes and the glide issue probe the same as before the directory work Runs (short) pumpit8, pumpitpc and pumpit1 on Linux x64, pumpit1 on Linux i386, pumpit8 on Win32: no faults Known
- The web build was not checked. The machine this release was made on has no tools for it.
- Win32 code remains in the engine: the Win32 thread procedure (SEH) of the execution trampoline, the exception entry and the shutdown recovery's vectored handlers. Moving them means changing how Win32 receives faults, so it is left as a task of its own.
- The 69 platform branches of the tools and probes are as they were; about 30 are guards around a probe that runs on Win32 only.
- Two functions nothing calls (
PushX87Float,CaptureSuspendedThreadSnapshot) were kept. - v0.0.194's known items (the music slowing on WSL among them) stand.
-
2026-09-27
v0.0.194
Changes since the previous release, v0.0.193 (Tasks 751–754). On Linux x64 all 16 ROM sets that have a disc image run, and pumpit8's arrows no longer part from the music and go back. The engine keeps the guest's
cli, draws with the GPU on WSL, ends an input record at its handler's return, and runs the timer ticks on the clock the audio follows. The arrows were confirmed by the user in play on WSLg (wayland).Full release notes
Changes since the previous release, v0.0.193 (Tasks 751–754). On Linux x64 all 16 ROM sets that have a disc image run, and pumpit8's arrows no longer part from the music and go back. The engine keeps the guest's
cli, draws with the GPU on WSL, ends an input record at its handler's return, and runs the timer ticks on the clock the audio follows. The arrows were confirmed by the user in play on WSLg (wayland).In numbers (WSL Linux x64 Release)
v0.0.193 v0.0.194 ROM sets running their 60 s (of the 16 with a disc image) 15 (pumpitpc died at 5 s) 16 Tick storms right after start (18,000 ticks a second, no frames) 2 of 6 starts 0 of 32 pumpit8's selection screens, fps 36–42 60 Median fps over the last 20 s, pumpit8 and pumpitp2 39 60 Presses the guest read in pumpit8 (the input script holds 139) 1,389–2,406 139 Tick rate in pumpit8's play (on the audio's clock; 240 expected) 241–264 239.5–240.7 Fixed
The guest's
cliis kept (Task 751) — user mode cannot clear IF, so timer ticks were injected even in the middle of a section the game wrapped incli…sti. In pumpitpc the handler changed a board register in the middle of a transaction with the security chip (CAT702) and the boot check failed. A hold raised byclinow defers injection untilstior theiretof an injected frame (with a 100 ms valve). The rules of injection were set in order too: a chain right after a handler's return is at most 3 and at periods of 1 ms or more, nesting is one level deep, and the interrupted code gets a turn as long as the last handler took. The tick storm right after start is gone with these rules.aamandaadare emulated (Task 751) — long mode does not have them. They were why pumpitpc died at its first score display.The pacer no longer holds a late frame one period longer (Task 752) — its resynchronisation test compared against the frame's own deadline, so a frame 1 ms late became a 33 ms frame. Only a frame more than a period late resynchronises now, and a late frame passes without a sleep.
The GPU draws on WSL (Task 752) — WSL offers the GPU only through Mesa's D3D12 driver, and Mesa picks llvmpipe by itself. With
/dev/dxgandd3d12_dri.sopresent and no driver chosen by the user, the engine setsGALLIUM_DRIVER=d3d12.An input record ends at its handler's return (Task 753) — the input replay frame pushed when a tick is injected was retired by a stack test alone, so the frame of a tick injected at a stack level the guest does not come back to stayed to the end, and reads outside a handler were answered with the keys of an old time (a SERVICE press read 60 times a second). A frame now ends at the handler's
iret, and Win32, which cannot see the return, retires it after more than 64 later injections.Timer ticks follow the audio's clock (Task 754) — the tick schedule alone used SDL's clock (
CLOCK_MONOTONIC_RAWon Linux) while the audio and the rest usedCLOCK_MONOTONIC. While WSL2 slowedCLOCK_MONOTONICby 1–7%, the guest's time ran up to 9% ahead of the music, so the arrows ran ahead and came back. The tick schedule and the input timeline now followsteady_clock, and the engine says so when the two clocks part.New switches and diagnostics (default behaviour when unset)
Variable / output What it is REPIU_GUEST_CLI_HOLD=0turns the guest clihold offguest cli hold cli/sti/blocked/expired/max-hold-us/held-at-end(final report)the hold's counts and its longest timer IRQ0 after-return …,timer IRQ0 nesting …,timer IRQ0 turn …(final report)deferrals by injection rule REPIU_PIU10_CAT702_TRACE=1[repiu-cat702] #N bits= in= out=per CAT702 transaction[repiu-linexe] get-proc miss name=…a Glide name lookup that failed REPIU_WSL_D3D12=0leaves Mesa's choice alone on WSL [repiu-glide] GL renderer: …,Glide GL renderer/wsl-d3d12-chosen(final report)what draws REPIU_GLIDE_LONG_FRAME_LOG=2every frame as sleep, guest and present REPIU_JAMMA_REPLAY_FRAME_END=0input frames are not ended by return (stack test and age only) REPIU_JAMMA_TIMELINE_TRACE=1the ESP and interrupted EIP of each input frame retired by age JAMMA timeline frames ended-by-return/retired-stale/latest-state-reads(final report)how input frames were retired REPIU_EVENT_CLOCK=sdlthe tick schedule follows SDL's clock as before [repiu-clock] …,host clock raw-against-steady …(final report)the two clocks' divergence (windows of 1 s, 0.5% limit) scripts/survey_romsets.sh <binary> <tag> [seconds] [profile…]a 60 s run per ROM set, summarised in build/survey/<tag>/summary.txtscripts/input_scripts/pumpit8_play.txt,survey_generic.txtinput that drives pumpit8 into a song; the survey's common input Known
- On WSL the music slows and comes back. This machine's WSL slows
CLOCK_MONOTONICby 1–7% against real time (measured against the Windows performance counter; what slows it is not established), and audio is consumed by that clock. It remained after WSL was restarted. It is taken to be outside the engine, and checking on real hardware (an actual Linux machine) is the follow-up. Arrows and music agree with each other. - Fps and tick rates measured on WSL are measured with
CLOCK_MONOTONICand read 1–9% high while that clock is being slowed. Read the final report'shost clock …line first. - pumpitp3 and pumpipx3 on Win32 die intermittently with
Fatal error: unable to find entry point in DLL.Their CAT702 transactions are correct and the cause is not established. - Six profiles (pumpit2, pumpit3, pumpite, pumpitp3a, pumpipx3a, pumpipx3b) have no disc image (CHD) and cannot be run.
- INT 21h AH=08h (
getch) is absent. It is reached only behind a game's error screen. - Win32 cannot see the guest's
iret, so the nesting and turn rules are off there and input frames are retired by age (reads at an old time can remain for about 0.27 s). Tasks 752–754 were checked on Win32 by build and probes only. - The teardown segfault after the budget remains. In pumpit8's song selection the guest stalls half a second per preview load. Some frames in play present in 23–43 ms (the host's display side, not investigated).
- The scripted runs of Tasks 748–752 ran under Task 753's defect (the guest received more input than the script held).
- On WSL the music slows and comes back. This machine's WSL slows
-
2026-09-27
v0.0.193
Changes since v0.0.192 (Tasks 745–750). On Linux x64 the arrows and the BGA no longer hitch with vsync on. When the driver refuses vsync the engine paces the swaps at the refresh rate, the MP3 playback clock no longer follows the device's pull jitter, and the timer ISR runs while a swap is waited out. A run started with arguments applies
cfg/repiu.initoo. Confirmed by the user playing on WSLg (wayland, real vsync).Full release notes
Changes since v0.0.192 (Tasks 745–750). On Linux x64 the arrows and the BGA no longer hitch with vsync on. When the driver refuses vsync the engine paces the swaps at the refresh rate, the MP3 playback clock no longer follows the device's pull jitter, and the timer ISR runs while a swap is waited out. A run started with arguments applies
cfg/repiu.initoo. Confirmed by the user playing on WSLg (wayland, real vsync).In numbers (pumpitea inside a song, 62 s plays with in-song input, WSL Linux x64 Release)
v0.0.192 v0.0.193 fps at swap_interval = 1(llvmpipe refuses vsync)58–98 59.9–60.1 MP3 frame-sync toggle interval (should be 26.1 ms) 16–35 ms 24–27 ms (98%) Guest's delay in seeing a toggle, under a 60 Hz wait uniform 0–16 ms 0–4 ms Guest's song position step, under a 60 Hz wait 16 ms / 33 ms bimodal centred on 25 ms (21–31) Ticks dropped in a paced 14 s attract 936 62 Frames over 25 ms (inside the song) — 0 Free-running with vsync off already observed within 0–7 ms and still does; what changed is that a run with vsync on now matches it.
Fixed
Swap pacing when the driver refuses vsync (Task 745) — WSLg's llvmpipe GL refuses
SDL_GL_SetSwapInterval(1)("That operation is not supported"). When refused, the engine waits aftergrBufferSwapuntil a deadline derived from the display's refresh rate. Deadlines accumulate, so the average rate is exact, and a deadline more than a period behind is resynchronised. The final report's twoGlide swap pacing …lines give the state, the period, the refusal and the jitter (late, resyncs, oversleep).Argument runs read
cfg/repiu.ini(Task 746) — a run that skips the launcher, such asrepiu pumpitea, reads and applies the same file first; an environment variable of the same meaning still wins. The log'sLauncher settings read from …line says where each value came from.Tick injection after an HLE reentry (Task 747) — Linux x64 handled the ISR's
iret,outandcliwithout attempting an injection afterwards, so after a long gate the owed ticks drained two a frame and the backlog (64) overflowed. The legacy chain's injection call is now there too, draining right afteriretwithout nesting.The MP3 playback clock free-runs (Task 749) — the previous clock restarted its line at the previous step every time the device took 768 frames, so a few ms of pull jitter became 12 ms jumps and stalls. It now advances from its own previous value at the PCM rate and slews, with a four second time constant (±2%), to the pulled count less half a device buffer. It still never passes the pulled count and never runs backwards.
Timer ticks while a swap is waited out (Task 750, Linux x64) — no guest code ran inside the
grBufferSwapgate, so the timer ISR stopped too. The gate now hands the present to the host thread and waits, injecting an owed tick as "an interrupt that arrived just before thecallthat reached the gate": the ISR'siretruns the call again and returns to the gate, which returns to its caller only when the present is done, as the real machine takes IRQ0 during its vblank wait. The code that arms ticks moved from the host poll loop into a call the guest's wait loop makes too.New switches and diagnostics (default behaviour when unset)
Variable / output Meaning Glide swap pacing active/refresh-hz/period-us/paced-swaps/slept-ms/refusal(final report)whether pacing is active, and the refusal Glide swap pacing late-swaps/late-max-us/resyncs/oversleep>1ms/oversleep-max-us(final report)the pacer's jitter Glide swap wait ticks swaps/injections(final report)swaps waited out with ticks delivered, and the injections REPIU_GLIDE_SWAP_WAIT_TICKS=0turns the ticks during the wait off (a Linux x64 feature, on by default) REPIU_GLIDE_SWAP_WAIT_LOG=1names the first few waits that could not deliver a tick REPIU_PIU10_MP3_POSITION_TRACE=1[repiu-mp3-pos] toggle|seen …per toggle and per guest read that first sees the new valueREPIU_GLIDE_LONG_FRAME_LOG=1(a larger value is a µs threshold)splits a frame over one and a half periods into sleep, guest and present REPIU_INPUT_SCRIPT=<file>a scripted keyboard pushing <ms> <key name> [hold ms]lines as SDL key events; seescripts/input_scripts/pumpitea_play.txtandpumpitea_play_steps.txtREPIU_DOS_ASSET_TRACE=alllifts the asset trace's caps (200 opens, 120 reads, 120 seeks) Known
- The wayland driver takes real vsync. WSLg keeps its socket in
/mnt/wslg/runtime-dir, so run withXDG_RUNTIME_DIR=/mnt/wslg/runtime-dir SDL_VIDEO_DRIVER=wayland. The earlier note that the wayland driver opens no window was this path. - Starting under wayland, 2 of 6 runs had the tick injection climb to 18,000 a second from 4 s and overflow the guest stack at 13 s. It has not appeared in recent runs and is not investigated.
- Ticks during the wait are Linux x64 only. Forced on under Win32 the run stalls after the first injection, without a fault, so it is not offered there; Win32's vsync waits in the same gate and is expected to have the same observation delay.
- Win32 pumpitea drops 380 ticks in 15 s, vsync on or off. Unrelated to this release and not investigated.
- A play without input empties its gauge about 14 s into the song and enters the fail sequence, whose
BGA loads (
HEYMAN.DATand others) make 80–340 ms frames. Use an input script when measuring, so they are not mistaken for in-song hitches. - A game whose call site is not
call rel32gets no ticks during the wait and waits as before.
- The wayland driver takes real vsync. WSLg keeps its socket in
-
2026-09-27
v0.0.192
Changes since v0.0.191 (Tasks 741–744). On Linux x64 one trap costs 41% less and pumpitea's breakpoint exceptions fell from about 38,000 to about 30,000 a second. A census of which guest sites trap is always on, and a latent defect where a
retinto an untranslated block died as an anonymousSIGTRAPis fixed. This release changes the Linux x64 engine internals only; game behaviour is unchanged (the Windows build either has no affected code or copies the original bytes as before).Full release notes
Changes since v0.0.191 (Tasks 741–744). On Linux x64 one trap costs 41% less and pumpitea's breakpoint exceptions fell from about 38,000 to about 30,000 a second. A census of which guest sites trap is always on, and a latent defect where a
retinto an untranslated block died as an anonymousSIGTRAPis fixed. This release changes the Linux x64 engine internals only; game behaviour is unchanged (the Windows build either has no affected code or copies the original bytes as before).In numbers (pumpitea attract, 27–30 s, WSL Linux x64 Release)
v0.0.191 v0.0.192 VEH cost of one breakpoint exception 15.2k cycles 8.9k cycles VEH share of wall 14.3% 6.7–7.3% Breakpoint exceptions 1.02–1.22 M 0.88–0.97 M memcpy helper push es,mov eax,ds53,914 × 2 0 jmp cs:[table](0x010F659E)27,594–36,659 0 pumpit2a 25 s breakpoints 149,911 108,557 Frame rate is unchanged: the remaining traps sit inside the game's own wait loops (Watcom
delay()'s INT 21h AH=2Ch calibration spin and polling,lseek), so a cheaper trap only spins more inside the same wait. The gain is CPU occupancy.Fixed
"The ISR input scan's trap load" was a misdiagnosis (Task 741): the new breakpoint site census (always on;
breakpoint site #N host/guest/cache/count/exits, the 32 busiest, in the final report) named the real top sites: three INT 21h AH=2Ch calls of Watcomdelay()at 61% (a 450,000-call calibration spin in the first two seconds plus 6,600 a second),lseekat 10%, a memcpy helper's segment-write pair at 10%, the ISR about 1%. The count is the game's own wait loop, so the cost of one trap was cut: every HLE reentry decoded the same guest address three ways with Zydis (segment write, span safety up to 64 instructions, long-mode identity); a per-address memo (1,024 slots, invalidated by a generation that moves on every guest code write, an eight-byte fingerprint and the code mode) replaces that.REPIU_AOT_REENTRY_MEMO=0turns it off. The per-callgetenvand thelocaltimeconversion inside the DOS service are cached too.Segment reads and pushes without a trap (Task 742):
mov r16/r32,sregand a 32-bitpush es/ds/fs/gsare emitted as long-mode slots. The value is the guest-visible selector the engine keeps in the shadow, so the slots read the shadow with no guard (new kindkGuardedSegmentPush; i386 images copy the bytes). The earlier code emitted the i386 read slot into long-mode images, pushing RFLAGS onto the host stack and comparing the host DS of 0 RIP-relatively, so it always fell back to INT3; it is no longer emitted in long mode.Returning to an untranslated target (Task 743): when a
retcame back to a block whose dynamic translation had been rejected, the return thunk's resolver allowed the legacy resume only for a byte-identical first instruction in long mode and died at the thunk'sint3for anything else (apop edx, commonly). It now uses the indirect call's condition (readable guest code). The dead ends that remain (no translation and no long-mode identity) end with[repiu-x64-untranslatable] stage= eip= bytes= …, and[repiu-fault] unhandled …carries the last VEH exit site (last_exit_site=,last_exit_eip=).Jump tables normalized by
xor edx,edx; mov dl,bl(Task 744): the planner's low-byte guard (cmp bl,imm; ja) knew onlyand r32,0xFFas its normalization, so this Watcomswitchform trapped every time through itsCS:prefix. The guard now survivesxor/sub r32,r32and completes onmov r8,r8'(ormovzx r32,r8).New diagnostics (inert when unset)
Variable / output What it does breakpoint site #N host/guest/cache/count/exits(final report, always)the 32 host/guest addresses raising the most breakpoint exceptions, with the VEH exit that ended them AOT reentry memo hit/miss segment/span/long-mode(final report, always)the reentry memo's hit rate REPIU_AOT_REENTRY_MEMO=0turns the reentry memo off, decoding every time (for A/B) REPIU_AOT_DYNAMIC_REJECT=<guest address>orreadmakes the worker reject those dynamic images, reproducing a translation failure [repiu-x64-untranslatable] …address and bytes where execution stops at an untranslated, non-identical instruction (up to eight) [repiu-fault] unhandled … last_exit_site= last_exit_eip=the refusing VEH exit site ( VehExitSitecode) and EIP[repiu-aot-unsafe] line=Nwhich check ended a dynamic image append as an unsafe failure Known
- An instruction with no translation and no long-mode identity (an absolute
cmp, say) still cannot run on Linux x64 (no interpreter); unreachable while dynamic translation works, and now named. - The remaining Linux x64 traps are all the game's own:
delay()AH=2Ch,lseek, the ISR's batchedin, EOI andiret. The tick rate wandering 234–244 a second during a song and the 40–160 ms tick gaps during scene loads were not investigated. REPIU_GUEST_POSITION_CENSUScaptures nothing on Linux.REPIU_DOS_INT_TRACE's stderr writes slow the wait loops and distort call counts; do not use it to count traps.- v0.0.191's known items (the Windows legacy backend's pumpitea crash, the VS 2026 v145
dos4gw_hellocrash, the CD-preview silence report) remain. - Linux figures were measured under WSLg.
Verified by the whole Linux x64 core probe (new cases: the reentry memo, three long-mode segment read/push slots, three jump-table guards), the whole Windows x86 core probe, an automated WSL reproduction of pumpitea from coin to song select to play (60 s, no faults), a pumpit2a 25 s regression, and 4 of 4 runs of the fixed build without a fault.
- An instruction with no translation and no long-mode identity (an absolute
-
2026-09-26
v0.0.191
Changes since v0.0.190 (Tasks 735–740). pumpitea now starts and plays on both Linux x64 and Windows. On Linux x64 the arrows no longer stall and jump during play and the music no longer carries noise (confirmed by the user). The window title shows platform, architecture and build configuration, and the Linux build scripts work as they are, with no
--target.Full release notes
Changes since v0.0.190 (Tasks 735–740). pumpitea now starts and plays on both Linux x64 and Windows. On Linux x64 the arrows no longer stall and jump during play and the music no longer carries noise (confirmed by the user). The window title shows platform, architecture and build configuration, and the Linux build scripts work as they are, with no
--target.Where pumpitea stands
v0.0.190 v0.0.191 Linux x64 start rejected by the AOT image's decode check starts → title → song select → play Linux x64 at 23 s segfault from nested timer ISRs runs past 90 s Windows start 0xC0000005at 8 sreaches the title at 90 s Arrows during play stall and jump smooth Music during play occasional noise none Fixed
pumpitea did not start on Linux x64 (Task 735): the AOT image's decode check rejected one entry, guest
82 68 10 01(sub byte [eax+0x10],1). Opcode82is the alias of the80group-1 operations and raises #UD in long mode; the long-mode path now judges and emits82as its80spelling. The build-failure message now names the stage, the first failing entry and the emitted bytes.pumpitea's nested timer ISRs (Task 736): with the PIT programmed at 51.9 kHz, ticks were injected again at the back-edge after the timer ISR's
cli(IF does not reflectcliin user mode), nesting the ISR until the stack overflowed. The PIC's IRQ0 in-service bit is modelled: no injection before the EOI (0x20/0x60to port0x20), and an owed tick is delivered at the guest'sstiafter it (never twostideliveries in a row, TF cleared from the injected frame).REPIU_PIC_TIMER_IN_SERVICE=0restores the earlier behaviour.Windows pumpitea start-up crash (Task 737): three layers. (1) The i386 indirect CALL fallback kept the push as the x64 rule says (x64 continues in the callee), pushing the return address twice; on i386 the legacy path re-executes the CALL, so the push is undone, and the miss tail's CALL also gets
+8. (2) i386 kCopy copied the three stub bytes of 16-bit code objects verbatim, rejecting every dynamic image; 16-bit records are now an INT3 HLE boundary. (3) The delay-loop batcher rejected the ISR input scan'scmp; jge exit; jmp backshape; the inverted form is accepted (19,001 batches in 90 s on Windows).Arrow stutter and noise during Linux x64 play (Task 740): both the MP3 frame-sync toggles and the decoder's refills followed the steps in which the SDL device pulls PCM (its buffer size). Several frame boundaries inside one step toggled in the same call (a guest polling at 240 Hz reads an even number as no change), and the decoder refilled a whole step at once, so the guest's demand and frame counter moved in the same steps. A playback clock interpolated at the PCM rate between device steps now drives both the toggles and the decode gate. With a forced 2048-frame device buffer, collapsed toggles went from four to six a second to none, and on the user's host both the stutter and the noise are gone.
Changed
- Window title (Task 738):
rePIU v0.0.191 (Win/x86 Debug) - Build … - Glide 2 OpenGL [dynamic] - FPS : …, with the platform/architecture and build configuration such asLinux/x64 Release.repiu::platform::BuildIdentityLabel(), CMakeREPIU_BUILD_CONFIG. - Linux build scripts (Task 739):
scripts/build_linux_x64.shandbuild_linux_i386.shbuild everything with no--target.repiu_exelinksGLPUBLICon Linux, which removes the link errors ofrepiu_instruction_censusand the i386 core probe. The i386 script gained--build-dirand the configuration-mismatch guard.--headlessonly suppresses the configure error when the X11/Wayland development packages are missing; the desktop drivers are built regardless. - The final report gained
PIU10 MP3 received/dropped/decoded/starvation/toggles/multi/pcm-empty/ pcm-low-water-bytes/device-buffer-framesandtimer IRQ0 in-service …lines.
New diagnostics (inert when unset)
Variable / tool What it does REPIU_PIU10_MP3_CENSUS_MS=<ms>one MP3 pipeline census line per interval (received, decoded, queue, clock lag, demand, toggles, collapsed toggles, starvation, empty queue, ticks) REPIU_PIC_TIMER_IN_SERVICE=0turns the IRQ0 in-service model off, restoring the earlier injection SDL_AUDIO_DEVICE_SAMPLE_FRAMES=<frames>(SDL hint) forces the device buffer, to reproduce a host with a larger one scripts/task740_mp3_census_capture.shone run with the census above and a per-second summary Known
- The Windows legacy backend's pumpitea crash (
0x040FD010) was not re-checked, nor was Windows pumpitea's progress to song select and play. - pumpitea's ISR input scan runs 200
ininstructions per tick through traps (4.05 million in 88 s on Linux). The results are right; the cost is high. - The tick rate wanders between 234 and 244 a second during a song, and there are 40–160 ms tick gaps during scene loads. Not investigated.
- From v0.0.190's list, the VS 2026 (v145)
dos4gw_hellocrash, the Linux x64 loading stalls and the CD-preview silence report remain as they were. - Linux figures were measured under WSLg.
Verified by Linux x64 core probe 33/33, Windows x86 core probe 31/31, Linux i386 core probe 31/31, an automated WSL reproduction of pumpitea from coin to song select to play (60–90 s, no faults), a 90-second Windows pumpitea run, pumpit2a regression runs, and the user's own play.
- Window title (Task 738):
-
2026-09-26
v0.0.190
Changes since v0.0.189 (Tasks 718–734). On Linux x64, pumpit2a now gets past the song-select screen, chooses a song and starts playing it. The render-phase game clock runs by default, and Glide calls dispatch directly instead of trapping. On Windows, an intermittent shutdown crash in pumpit1 legacy runs is fixed.
Full release notes
Changes since v0.0.189 (Tasks 718–734). On Linux x64, pumpit2a now gets past the song-select screen, chooses a song and starts playing it. The render-phase game clock runs by default, and Glide calls dispatch directly instead of trapping. On Windows, an intermittent shutdown crash in pumpit1 legacy runs is fixed.
Where Linux x64 stands
v0.0.189 v0.0.190 Game clock in the render phase runs only with the opt-in runs by default Glide calls all through a ud2trap (2.42 million in 180 s)direct dispatch (no traps) Swaps in a 180-second run 10,344 15,832 Song-select screen freezes passes → song choice → step data loaded → song plays Fixed
pumpit2a song-select freeze (Task 734): the Linux x64 return thunk did not restore EFLAGS after a guest
retor indirect call/jump, so everyretthrough it came back with CF=0. The Watcom C runtime'ssin/coshelper reports its result in CF, so the game retriedfsinforever. The thunk now restores the saved flags right before itsjmp. The defect may have affected any guest code that reads flags after aret.Linux x64 render-phase clock (Task 718): safe-point tick injection is on by default. Turn it off with
REPIU_LINUX_X64_SAFE_POINT_INJECTION=0; the variable used to be tested for presence, so=0meant on.Linux x64 Glide direct dispatch (Task 720): the direct-dispatch thunk existed only as i386 assembly, so every Glide call on Linux x64 went through a
ud2signal.RepiuLinuxX64GlideGateThunknow calls the export directly, saving and restoring the guest's x87 state. The 1–3.6 s stalls while loading are unchanged, which also established that Glide traps are not their cause.Shutdown recovery
- The x64 shutdown recovery decision reads the full RIP (Task 730). It used to read only the low 32 bits, so a host-library address whose low half fell in the guest range could be "recovered" from a host frame.
- Windows: pumpit1 legacy runs crashed with
0xC0000005at shutdown about half the time; fixed (Task 733). The cause was an exception the kernel was already delivering whenSetThreadContextran, now handled by a redirect guard installed only for the shutdown window. After the fix, 40 consecutive runs ended cleanly.
Changed — logs and source layout
- The shared loader moved from
src/host/win32/main.cpptosrc/host/loader/main.cpp(Task 731, byte-identical). - Loader log lines no longer start with
Win32(Task 732):[loader] Win32 minimal execution timed outis now[loader] minimal execution timed out. The 230 uses in the repository's scripts and guides were updated with it. External log parsers need their patterns updated. scripts/test_all.ps1's stale pumpit1 assertions were brought up to current behavior (Task 733).
Performance experiment (off by default)
The framebuffer readback each Glide LFB write lock did can be replaced by one copy per color buffer, exchanged at the swap like a 3dfx page flip (Tasks 728–729). In a 30-second run seeds fell from 304 to 2 and readback cost from about 6.5 billion cycles to 46.9 million, but that stretch's fps barely moved (27.7 → 28.0), so it stays off. Turn it on with
REPIU_GLIDE_LFB_STAGING_REUSE.New diagnostics (inert when unset)
Variable / tool What it does REPIU_GLIDE_PIXEL_DIAG_INTERVAL_MSsamples the back buffer by time (4×4 brightness grid), to line the two hosts up on the same scenes REPIU_LINUX_X64_NATIVE_SAMPLE,REPIU_LINUX_X64_NATIVE_SAMPLE_TRACELinux x64 guest-thread position samples, and a per-sample RIP / guest EIP log REPIU_GLIDE_LFB_TIME_PROFILE,REPIU_GLIDE_LFB_WRITE_CENSUSLFB lock cost by phase, and write extents REPIU_LINUX_X64_LFB_STORE_CENSUS,..._SOURCE_CENSUSthe guest instructions that store to the LFB, and where they come from REPIU_GLIDE_LFB_STAGING_SHADOW_CENSUS,REPIU_GLIDE_LFB_LOCK_INTERVAL_CENSUSlocks whose readback could be skipped, and what happens between locks REPIU_LINUX_X64_MEMORY_WRITE_TRACELinux x64 guest memory write tracing REPIU_SHUTDOWN_RECOVERY_TRACEWindows: logs the exceptions that arrive right after the shutdown redirect [repiu-shutdown]linehost_ip=,decision=,aliased=, and the Windows redirect guard's intervention countsscripts/task734_songselect_stall_capture.shrecords an interactive state such as the song-select freeze with frame rate, position samples and a cache map Known
build_win32_x86.ps1picks the newest installed Visual Studio, and a binary built with VS 2026 (toolset v145) crashes at the start ofdos4gw_hello(3 of 3); the VS 2022 (v143) binary is fine.test_all.ps1therefore could not be run end to end.- A user report that the CD preview music is inaudible did not reproduce on the development machine, where the WSLg output carries sound during CD play.
- The 1–3.6 s stalls while loading on Linux x64 remain (not caused by Glide).
- The
repiu_instruction_censustool does not link on Linux x64 (unresolved GL symbols). - Linux figures were measured under WSLg.
Verified by Linux x64 core probe 31/31 (including the new
guest_return_preserves_flags), Windows x86 core probe 29/29, an automated WSL reproduction of pumpit2a from song select to song playback, and 40 consecutive clean pumpit1 legacy shutdowns on Windows. -
2026-09-18
v0.0.189
Changes since v0.0.188. On Linux x64, pumpit2a now draws textured frames, and with an opt-in setting the game clock runs and the scenes advance. Windows behaviour is unchanged apart from two shared fixes listed separately below.
Full release notes
Changes since v0.0.188. On Linux x64, pumpit2a now draws textured frames, and with an opt-in setting the game clock runs and the scenes advance. Windows behaviour is unchanged apart from two shared fixes listed separately below.
Where Linux x64 stands
v0.0.188 v0.0.189 Glide init → render loop reached reached Texture upload skipped the same 96 gates as Win32 Floating point all NaN after a signal return correct Game clock in the render phase stops default: stops / opt-in: runs 90-second run, opt-in on — no faults, 3,913 frames, scenes advance The setting that keeps the clock running is
REPIU_LINUX_X64_SAFE_POINT_INJECTION. It is on whenever the variable exists, whatever its value (=0turns it on). The next release is expected to make it the default.Fixed
Re-entry and interrupts (Tasks 702–705)
- Execution re-enters AOT code after Glide gates and timer-chain boundaries. Before, a
PUSHorCALLafter the boundary ran on the host stack and vanished from the guest's. - INT 8 injection frames carry the guest selector, and the ISR's
IRETDis handled by HLE. - A thread interrupt that changed nothing no longer overwrites host
R15(guest ESP) with a 32-bit value.
CPU state and emission (Tasks 707, 710, 713, 716)
- x87 tag word: every signal return marked all FPU registers non-empty, so everything after it was NaN.
- High-byte lowering: the first byte of the lowering for
mov dh,[esp]and the like overwrote DL instead of saving it, corrupting EDX;glEnable(GL_TEXTURE_2D)then failed and textures were skipped altogether. - Timer safe points (1,067 sites) are planted on the long-mode path.
CS:override data accesses (mov al, cs:[edx+table]) are emitted as copies; before, they were HLE boundaries nothing on x64 handled.
Segment folds (Tasks 711, 712, 717)
- An override naming the loader's initial stack selector (
0x0034) or data selector (0x0024) now folds base 0 in any segment register, because the guest's implicit accesses are flat.- SS: Watcom
lseekwrote outside the stack and PIU.BIN was read 1.1 million times. - ES:
sprintf("%d")copied texture pixels as a number and overwrote the stack and heap.
- SS: Watcom
Glide LFB (Task 708): the LFB buffer address was a host pointer truncated to 32 bits; the buffer now sits in the shared below-4 GiB reservation (
ReserveLowAddressMemory) where the guest can address it.Shared fixes that also apply to Windows
- SS fold (Task 712): Windows now reads PIU.BIN correctly for the first time.
- Data selector fold (Task 717): clock, safe-point and segment figures of a 30-second run stayed within noise.
- The Windows x86 build no longer compiles the Linux-only telemetry source (Task 706).
New diagnostics (inert when unset)
Variable / output What it does REPIU_LIVE_GUEST_PEEK=<offset>[,n]logs up to 16 guest dwords each second REPIU_LIVE_GUEST_SCAN=<offset>,<bytes>,<dword>counts a dword in a range and lists its runs REPIU_LINUX_X64_DATA_WATCH=<address>Linux hardware write watchpoint, logged and resumed [repiu-fault-stack]64 guest stack dwords after an unhandled Linux fault Final execution report printed on the refused-recovery exit path as well (Task 709) Known
- With injection off (the default) the game clock stops once rendering starts, around 17 s.
- The two hosts return different values when the guest reads a segment register (Windows
0x2B, Linux0x0024). The folds treat them alike; the values still differ. - Linux figures were measured under WSLg, not on a native ext4 Ubuntu install.
Verified by Linux x64 core probe 30/30, Windows x86 core probe 28/28, 30-second runs compared on both hosts, and a 90-second Linux run.
- Execution re-enters AOT code after Glide gates and timer-chain boundaries. Before, a
-
2026-08-28
v0.0.171
Changes since v0.0.170. Source layout only; behaviour is unchanged.
Full release notes
Changes since v0.0.170. Source layout only; behaviour is unchanged.
The execution engine moved to
src/engine/AGENTS.mdputs platform-specific code undersrc/platform/{win32,linux,web}/. The platform-neutral execution engine was living undersrc/platform/win32/and Linux was compiling it. Counted, 102 of the 119 files there had no Win32 dependency at all.Before After src/platform/win32/119 6 (the backends with a Linux counterpart) src/engine/— 113 include/repiu/platform/win32/46 1 include/repiu/engine/— 46 Header paths and the namespace change — a compatibility break
include "repiu/platform/win32/foo.h" -> include "repiu/engine/foo.h" repiu::platform::win32::Bar -> repiu::engine::BarAnything including those headers needs updating. The backends (
repiu::platform) are unchanged.The dependency now runs one way
execution_trampoline.cppused to reach the backend with#include "win32_thread_api.h", reading a backend's private source tree. That header is public now, so the engine sees a backend only throughinclude/repiu/platform/win32/.Known
- No behaviour change. Confirmed by Linux and Windows builds, the probe, and the sample regression.
- The wasm configuration is unverified -- Emscripten is not installed in this environment.
- File and symbol names still carrying
Win32are untouched (aot_code_cache_win32.cpphas no Win32 code in it), as issrc/host/win32/main.cpp. Both are separate tasks.
-
2026-08-28
v0.0.170
Changes since v0.0.169. The Linux performance axis is narrowed to one trap per cache reentry. Nothing is fixed yet.
Full release notes
Changes since v0.0.169. The Linux performance axis is narrowed to one trap per cache reentry. Nothing is fixed yet.
The chain, narrowed
Windows Linux Reentries a frame 160.6 ~154 (0.90x) Breakpoints per reentry 0.0432 1.010 Linux reenters less often. What differs is whether leaving costs a trap, and that 23x matches the 24.2x breakpoint excess measured earlier. The path Windows uses to avoid it is Glide direct dispatch, at 95.1% of its reentries, and 87% of Linux's reentries take it too -- yet a success still costs a trap.
Four lines of live instrumentation
Enabled with
REPIU_EXECUTION_TIME_PROFILE=1andREPIU_LIVE_PROFILE_INTERVAL_MS.[repiu-live-profile] veh / glide / port-io / dos / unaccounted shares [repiu-live-veh] deliveries, cycles per delivery, kernel gap, three classes [repiu-live-aot] cache entry / boundary / reentry, five boundary reasons [repiu-live-gdd] direct dispatch patched / resolved / relinked / entry / successLinux never reaches the loader summary, so without these nothing here can be read.
Known
- No performance has been fixed.
- Next question: why that INT3 stops being hit on Windows.
- Four inferences were wrong while making this version. All took a counter's meaning from its name instead of from where it increments; the frontier carries the table.
-
2026-08-28
v0.0.169
Changes since v0.0.168. Stages 1 and 2 of the web (WebAssembly) port land, and Stages 3 through 5 are recorded as on hold.
Full release notes
Changes since v0.0.168. Stages 1 and 2 of the web (WebAssembly) port land, and Stages 3 through 5 are recorded as on hold.
The platform-neutral core builds as wasm32
Not one line of core source was changed.
Item Value repiu_exeobjects (wasm32)64 Core sources modified 0 Undefined symbols of the engine's own none repiu_core_probe(wasm32)9 of 9 passed, with the 6 excluded ones named Windows regression 15 of 15, unchanged The dependencies (SDL3, libchdr, miniz, Zydis) build under Emscripten as they are.
The interpreter's size is measured
pumpipx3'sPIU.EXE, by recursive descent from the entry point.Item Value Instructions 74,333 (17,367 blocks) Distinct mnemonics 120 Distinct (mnemonic, operand signature) 320 Top 17 mnemonics, cumulative 87.89% An interpreter is sized by 320, not 120 -- one
MOVis 19 forms. But with the top 17 at 87.89% it can be built incrementally.It does not run in a browser yet
Both present backends (
legacyanddynamic) stand on native x86, which has no wasm form. Stage 3, a platform-neutral interpreter, is where that goes, and it is on hold -- the Linux performance axis comes first.The resume conditions are in the web port frontier. In short, two things must be settled first: the x87 representation (80-bit or not) and assuming Worker execution. Changing either afterwards means writing the interpreter twice.
-
2026-08-27
v0.0.168
Changes since v0.0.167, which corrects one of that release's conclusions.
Full release notes
Changes since v0.0.167, which corrects one of that release's conclusions.
Signal delivery was not the culprit
v0.0.167 recorded 68% of Linux's gap as "signal delivery". Splitting that 68% into its two factors shows the name was wrong.
Windows Linux Factor Deliveries per frame 18.8 256.3 13.6x Cycles per delivery (handler body) 104,002 341,557 3.3x Kernel round-trip floor 21,756 9,632 0.44x Kernel round trip, single-step mean 28,185 16,466 0.58x Linux's signal delivery is more than twice as cheap as Windows' exception dispatch. What costs is not delivery but taking 13.6x more boundaries a frame and a handler body 3.3x more expensive each time.
One more report line
[repiu-live-veh] #8 veh_count=617579 per_frame=263 cycles_per_veh=344198 gap_min=9472 gap_ss_mean=16427 gap_ss_count=22082Enabled by
REPIU_EXECUTION_TIME_PROFILE=1withREPIU_LIVE_PROFILE_INTERVAL_MS. Nothing new is counted -- these values already accumulated and simply were not printed.Known
- No performance has been fixed yet. This narrows the cause.
- What the excess boundaries are is still unknown. Not single steps (3.6% of Linux's deliveries against Windows' 24.4%).
- The per-frame and per-second normalisations say different things. Per second Linux takes 40% fewer faults -- because frames are 24x rarer.
-
2026-08-27
v0.0.167
Changes since v0.0.166. We now know why Linux is slow.
Full release notes
Changes since v0.0.166. We now know why Linux is slow.
The answer: signal delivery
Both hosts are the same machine, so TSC cycles compare directly. Per frame, in millions of cycles:
Bucket Windows Linux Factor Share of the gap The fault handler 2.065 88.072 42.6x 68.3% Glide 1.618 26.480 16.4x 19.7% Unaccounted 2.159 17.214 8.0x 12.0% DOS services 0.006 2.438 433.9x 1.9% 68% of Linux's gap is the fault handler, and on Linux that is signal delivery. It reproduces over three runs, against a Windows control of 35.35%.
The Glide gate was ruled out first: putting the host round trips and the gate crossings back moved the frame rate not at all, while the same knob costs Windows 27.1%.
A run reports on itself while it is going
REPIU_LIVE_PROFILE_INTERVAL_MS=1000gives one line at the interval you name.[repiu-live-profile] #5 frames=1706 window_frames=275 cycles_per_frame=133327684 veh=59.82% glide=22.86% dos=2.90% unaccounted=17.30%Use it with
REPIU_EXECUTION_TIME_PROFILE=1. It was needed because a Linux run that reaches rendering never reaches the loader summary -- its guest thread does not stop, so the shutdown arm ends at_Exit.It immediately showed what a cumulative average cannot: Linux's cost per frame triples during a run (83M to 272M cycles). It is not uniformly slow; it gets worse as the scene proceeds.
Known
- No performance has been fixed yet. This goes as far as finding the axis.
- Whether signal delivery is a count problem or a price problem has not been separated. That is the next task.
- The probe suite has never been validated in Release (recorded in v0.0.166).
-
2026-08-27
v0.0.165
Changes since v0.0.164. Stage 3 of the Linux port: the game runs on Linux and puts a picture on the screen. v0.0.163 reached the launcher, which at the time answered a chosen ROM set by saying the execution engine was not on Linux yet.
Full release notes
Changes since v0.0.164. Stage 3 of the Linux port: the game runs on Linux and puts a picture on the screen. v0.0.163 reached the launcher, which at the time answered a chosen ROM set by saying the execution engine was not on Linux yet.
The game runs on Linux
- The default
dynamicAOT backend works on Linux. Its 62 Win32 memory calls moved onto the 3b virtual-memory layer, and the one thing that did not exist yet -- an instruction-cache flush -- was built. Cache placement, dynamic execution, inline patching and page retirement all run. - The
legacybackend single-steps every instruction and never finished the start-up asset decode. This was the item the screen was waiting on. - On WSLg
pumpit1: first buffer swap at about 45.1 seconds, 69,263 of 307,200 non-black pixels at about 51.7 seconds, and swaps continuing after that. A person has confirmed an actual game screen appears.
Shutdown works
- A run asked to stop stops by itself. Before this, SDL turned SIGTERM into a quit event and
pthread_joinwaited forever, so the run asked to stop hung instead. - The shutdown block's four Win32-only calls converged onto one
InterruptHostThread. A successful recovery ends the run normally; a refused one goes down without waiting. - And it ends without a core dump. The refused arm had been removing a fault handler the guest thread still needed. That arm now does no cleanup and ends immediately. Across six 60-second-budget runs: two SIGTRAPs became zero.
Known
- It is very slow. The Linux frame rate has never been measured, and that is the next task.
- What is drawn has never been compared frame by frame against Windows.
- The default
-
2026-08-24
v0.0.163
Changes since v0.0.162. Stage 2 of the Linux port: the launcher runs on Linux as a 32-bit binary. The execution engine is still Windows-only, so no game runs.
Full release notes
Changes since v0.0.162. Stage 2 of the Linux port: the launcher runs on Linux as a 32-bit binary. The execution engine is still Windows-only, so no game runs.
The Linux launcher
repiu_launcherbuilds as an i386 ELF and runs under WSLg, sharing the ROM set list and options screen with Windows as the same code. It reads the realroms/directory and separates the sets that can run from the ones that cannot. Choosing one reports that the execution engine does not exist on Linux yet and exits, rather than pretending to start something and failing later with a missing file.--headlessstill builds the core and its probes without the desktop packages.What this stage established
The 32-bit SDL3 desktop stack stands up: X11 video, OpenGL rendering, and ALSA audio. That covers not only the launcher but everything the game window and sound will need, and since the execution engine uses SDL for exactly those, the whole port depended on it.
Two obstacles were resolved along the way. SDL's configure stopped on the
XSCRNSAVERandXTESTextensions, which inhibit the screen saver and simulate input and which this project never uses; they were switched off rather than installed, because every extension left on is one more 32-bit package to find. And OpenGL had to be linked by plain name rather than throughfind_package(OpenGL)'s imported target, which can bind the 64-bitlibGL.soand fail an-m32link on architecture.Known limitations
No game runs on Linux; the execution engine port is the next stage. The Linux launcher does not copy the Windows return loop or child process, both of which exist because a GPU driver claims the guest's low address space there — whether the same applies on Linux can only be judged once an engine exists. The Wayland backend was not detected, which does not matter because WSLg also provides an X server.
-
2026-08-24
v0.0.162
Changes since v0.0.161. This is the first stage of the Linux port: the platform-neutral core and its probes build as i386 on Linux and pass there. Windows behavior is unchanged, and no game runs on Linux yet.
Full release notes
Changes since v0.0.161. This is the first stage of the Linux port: the platform-neutral core and its probes build as i386 on Linux and pass there. Windows behavior is unchanged, and no game runs on Linux yet.
Measurement decided the plan
Of 192 sources, 53 are the platform-neutral core and 76 are the Win32 layer. The core includes no Win32 header, uses no MSVC-specific construct, assumes no 32-bit pointer, and compiles under GCC 13 with C++20 without a single error — it moved to Linux unmodified. The Win32 layer is a different size of problem: 270
CONTEXTuses, 47VirtualProtectcalls, and 28fs:[...]assembly sites, which is the scale of the execution-engine port still ahead.Build structure
The Win32 sources and Windows-only executables moved behind
if(WIN32)inside the existing targets, so Windows output and linkage are untouched. A newrepiu_core_probegathers the nine platform-independent probes and builds on both systems; the two runs produce byte-identical 46-line output, which is the point — not that it passed, but that the same code behaves the same in both environments.scripts/build_linux_i386.shchecks the 32-bit toolchain before configuring and names the packages to install when it is missing. The architecture is i386 because the guest's 32-bit code runs natively in the host process, the same reason the Win32 host is a 32-bit process.Fixed
nvram_path_probeused MSVC's_putenv; POSIX spells thissetenvandunsetenvand removes a variable differently. Nothing of the kind appeared in the 53 core sources.Known limitations
The execution engine, loader, and launcher remain Windows-only, so no game runs on Linux. SDL3 refuses to configure without X11 or Wayland development packages, so this stage — which opens no window — takes SDL's documented headless option; the stage that brings the launcher over will need the real desktop packages.
repiu_chd_cd_probeandrepiu_glide_issue_probewere left unguarded because they reference no Win32 symbol, but their Linux builds have not been checked. -
2026-08-22
v0.0.161
Changes since v0.0.160. This release changes how rePIU is started: with no arguments it opens a launcher, and a finished game returns to the list.
Full release notes
Changes since v0.0.160. This release changes how rePIU is started: with no arguments it opens a launcher, and a finished game returns to the list.
The launcher
Running
repiu.exewith no arguments lists every ROM set in the built-in catalog and starts the one selected; when that game ends the launcher comes back, so another title can be chosen without restarting rePIU, and Quit ends the session. ROM sets that cannot run are not hidden: they are dimmed and carry their reason — a missingroms\<id>.zip, a zip without the required PIU10 entries, a missingroms\<id>\, no CHD, or more than one CHD — so it is obvious why a disc is unavailable. Availability uses the same rules as the mount but extracts nothing, because verifying twenty-two discs would outlast anyone's patience and the real judgement already happens at launch.Arrows move, Enter starts, Escape quits, and unavailable rows are skipped by navigation. The cursor opens on the ROM set played last. Vertical sync and sound gain are edited there and stored in
cfg\repiu.ini; an environment variable of the same meaning always wins, so measurement scripts and diagnostic procedures keep their precedence.Existing invocations are unchanged
repiu.exe <rom set>andrepiu.exe <path to PIU.EXE>behave exactly as before and end the process when the game ends — the return loop exists only for an argument-free standalone run.REPIU_LAUNCHER=0skips the launcher for automation that runs the binary bare, and a host that cannot create a window or GL context falls back to the previous default target rather than failing to start.Why not a native GUI
The launcher is drawn with Dear ImGui (MIT) inside the SDL3 window the host already creates. Every cross-platform native toolkit that would serve Linux — Qt, GTK, wxWidgets, FLTK — is LGPL-family and conflicts with the project's ban on copyleft dependencies, and avoiding them would mean building the same screen separately against Win32 and X11/Wayland. Two further reasons agree: cabinets have no keyboard or mouse, so the UI must eventually be operable inside the game window, and the same layer becomes the in-game OSD later. Availability and settings depend only on
std::filesystemand the existing INI parser, so they are probe-verified, and the screen touches only SDL3 and OpenGL.Verification
Debug and Release builds of
repiuandrepiu_aot_probesucceeded.repiu_aot_probe --launcherpassed five checks: the six availability states, catalog coverage, settings round trip with malformed values, environment precedence in every combination, and child command-line quoting with a path containing spaces. For regression,repiu.exe pumpit8exits cleanly with no launcher involvement. A user confirmed listing, launching, returning to the launcher, and keyboard operation.Known limitations
Window size and fullscreen are not available yet, because window creation follows the guest's logical resolution and no consumer exists for an override. Driving the launcher from the cabinet panels is not possible yet either: panel input arrives over PIUIO ports while the guest runs, which is a different path, so keyboard, mouse, and gamepad are the current means. Diagnostic toggles such as the execution backend or the direct-return table are deliberately not exposed and remain environment variables.
-
2026-08-22
v0.0.160
Changes since v0.0.159. This release is about execution performance: a translated
RETnow resolves its own destination in most cases instead of crossing to the host, which raised frames on the same scene by 59.4%.Full release notes
Changes since v0.0.159. This release is about execution performance: a translated
RETnow resolves its own destination in most cases instead of crossing to the host, which raised frames on the same scene by 59.4%.Measuring return cost before removing it
Task 482 added
REPIU_AOT_RETURN_STAGE_PROFILE, an opt-in instrument that splits the return handler into five mutually exclusive stages plus the residual of the same window. It immediately corrected the number the project had been steering by: the oldkAotReturnbucket measured only the inside ofHandleAotReturnTransferand missed the adapter entirely, so return handling was about 27% ofguest-run, not the 16.5-17.5% previously recorded — a factor of 1.39, reproduced across three runs. No stage dominated (resolve31.7%,patch27.5%,entry25.1%,read8.3%,continuation7.3%), and 99.4% of policy observations were megamorphic bypasses.Megamorphic direct-return table, on by default
Task 499 probes a shared memo table ahead of the return miss path; a hit returns with the original RET's own stack effect and no host transition, and a miss falls through into the existing path unchanged. A pumpit8 A/B of three 60-second pairs, with no instruments and only the toggle changing, measured 37,385 against 59,586 frames, +59.38% (623 against 993 fps) with non-overlapping groups, triangles per frame within 2.84%, and identical
grDrawPointtotals in all six runs. Host return round trips fell from about fifty million per run to about 2,900, taking return handling from 25.63% ofguest-runto 0.13%.REPIU_AOT_DIRECT_RETURN_TABLE=0restores the exact cache bytes of a build without the feature, andREPIU_AOT_DIRECT_RETURN_TABLE_BITSsizes the table from 8 to 18 bits, defaulting to 15.Correctness adds no new invariant: a table hit is semantically identical to a four-entry PIC hit, insertion takes only resolutions the host validated, and invalidation clears the table exactly where PIC guards are reset, at a point where the guest thread is already stopped.
What the Glide gate actually costs
The same measurement work decomposed the Glide gate (23.7-24.7% of
guest-run) across three runs. Its largest component is not rendering but crossings that never reach the host thread, 45-47% of the bucket and 10.7-11.6% ofguest-run: 70.8-71.1% of gate entries are calls that setter elision discards, and elision removed only the host round trip, leaving about 1,790 cycles of crossing per call — a cost that had never been measured.Verification
Debug and Release builds of all three binaries succeeded; the eight
--return-stage-profilechecks and five--direct-return-tablechecks passed, the latter including one that executes the emitted bytes and confirms a hit reproduces the original RET's EIP and ESP witheaxandecxpreserved, for bothC3andC2 iw; the complete pumpit8 probe exited zero on both configurations; and table invalidation behaved correctly across 36 live page retirements with zero return fallbacks and zero index scans in every run.Known limitations
The promotion rests on an unattended pumpit8 attract scene and deserves one gameplay-scene confirmation;
REPIU_AOT_DIRECT_RETURN_TABLE=0reverts instantly if a regression appears. A first pumpitea run crashed once on a null dereference after 4.2 seconds and did not reproduce; the mount silently skipping 89 external-extent files is the leading suspect and the investigation is recorded with its resumption steps in the pumpite CHD mount analysis. The next performance axis is Glide: moving the elision test ahead of the crossing, worth about 11% ofguest-run, and host wake latency, about 6%. -
2026-08-20
v0.0.159
Changes since v0.0.156. This release reworks how the host keyboard reaches the game: window key input is delivered to the BIOS
INT 16hpath, PIUIO panel and cabinet button mappings are configurable per ROM set, and the EEPROM is stored separately per ROM set.Full release notes
Changes since v0.0.156. This release reworks how the host keyboard reaches the game: window key input is delivered to the BIOS
INT 16hpath, PIUIO panel and cabinet button mappings are configurable per ROM set, and the EEPROM is stored separately per ROM set.Window keyboard BIOS input
- SDL key down/up events from the game window now reach the
INT 16hBIOS path the original game polls every frame. The existing JAMMA input path keeps working alongside it. - The platform-neutral
hle::BiosKeyboardowns the same 15-entry FIFO a real BIOS data area has, the legacy/enhanced AX pairs, and shift/lock state. The SDL host thread is the producer and the guest thread the consumer, so a short mutex critical section guards it. - The Win32 SDL adapter translates US 101-key scan codes, ASCII, Shift/Caps/Ctrl/Alt combinations, and the function, navigation, and numeric keypad groups. Key repeat is enqueued as a DOS typematic keystroke, and losing focus releases held modifiers while retaining lock toggles.
AH=00h/10hconsume the FIFO head,AH=01h/11hpeek without consuming and report presence through ZF, andAH=02h/12hreturn the real shift flags.
Per-ROM-set configuration files
- The
[Input]section ofcfg/<rom-set-id>.iniremaps the host keys for the PIUIO panels and cabinet buttons per ROM set. Names follow the physical panel positions:P1_UP_LEFT,P1_DOWN_RIGHT,TEST,SERVICE,CLEAR,COIN1, and the rest of the fourteen. - A missing file is created on first run as a commented-out template whose comments
carry the values currently in effect, so changing a binding means deleting the leading
;on that line. An existing file is never overwritten. - Several keys can drive one input (
P2_UP_LEFT = Keypad7, Home), and combinations such asCtrl+F1are supported. A binding with modifiers must match exactly, while one without ignores modifier state unless another binding contends over the same key. NumLock, CapsLock, and ScrollLock never affect any binding. - An empty value turns the input off: written as
TEST =, no key drives TEST at all. - A ROM set's file layers over its parent's per key, so shared settings placed in
cfg/pumpitup.iniapply to every title. - A typo warns rather than stopping the run, and the loader log names the file and line.
- Usage is documented in docs/guides/romset-config-files.md.
Input mapping consolidation
- The same key mapping previously lived in three places in three vocabularies —
ScanJammaPort8andCaptureCurrentJammaPressedMaskin Win32 virtual keys, the SDL event path in SDL keycodes — so changing one silently desynchronized the others. They now share one binding table. SDL_Keycodeis the canonical key type andSDL_Keymodthe modifier mask. The SDL event path compares directly with no translation; only the polling path uses a virtual key table.- Port, bit, logical input, and log name were merged into one table. That corrected a
mismatch where the log called bit
0x02P1-UpRightwhile the code enumerator waskP1Down; the enumerator order is unchanged because the timeline uses it as a bit position. - The host key query count is unchanged under the default configuration. With no combination in use, modifiers are never read, so the port I/O cost Task 403 measured stands.
Per-ROM-set NVRAM
- The 93C46 EEPROM image is stored at
nvram/<rom-set-id>/eeprom.datthe way MAME does. All 22 ROM sets previously shared oneeeprom.datin the working directory, so cabinet settings saved under one title were what another read back. - An existing
eeprom.datis copied into the ROM set's directory once, carrying accumulated cabinet settings forward. The original is not deleted. - The
REPIU_EEPROM_PATHfull-path override keeps its meaning, so the per-run EEPROM isolation used by the benchmark scripts and measurement guides still works. cfg/andnvram/were added to.gitignore.
Verification
- Win32 x86 Debug builds of
repiu_exe,repiu, andrepiu_aot_probesucceeded. repiu_aot_probe --romset-config: checks=94 failures=0, covering INI parsing, alias lists and turning an input off, name table health, combination parsing and its rejected shapes, modifier matching with the contention rule and lock-bit exclusion, layering, and generated file round trip and no-overwrite. The regression guard transcribes the pre-change hardcoded mapping directly and compares virtual key for virtual key.repiu_aot_probe --nvram-path: checks=14 failures=0, covering override precedence, directory creation, separation between ROM sets, and carry-forward with matching contents and a preserved original.repiu_aot_probe --bios-keyboard-inputpassed withtranslated=true,fifo=true,modifiers=true,overflow=1.- The complete
repiu_aot_probe build/runtime_mounts/pumpit1/PIU/PIU.EXEassertion set exited zero. - The user confirmed config file generation and key mapping on a real
pumpipx3run. - Only the existing C4819 code-page warnings remain.
Known limitations
COIN2and the cabinet input port0x02ABare unsupported; their I/O port bits are not identified.- Combinations are meant for cabinet buttons. Binding one to a stage panel is not recommended, because the window event path fixes the modifier test at the instant the base key goes down.
- No sections beyond
[Input]exist yet. Unrecognized sections are silently ignored so a file written for a later version does not stop an earlier build. - Per-ROM-set NVRAM has not yet been confirmed at runtime in a user environment: file creation after a run and carry-forward of existing settings remain unverified.
- SDL key down/up events from the game window now reach the
-
2026-08-17
v0.0.156
Changes since v0.0.155. This release removes the paletted-texture refresh bottleneck observed on the
pumpitp3title screen while preserving the Glide semantics that make a new palette affect existing P_8/AP_88 textures.Full release notes
Changes since v0.0.155. This release removes the paletted-texture refresh bottleneck observed on the
pumpitp3title screen while preserving the Glide semantics that make a new palette affect existing P_8/AP_88 textures.Lazy paletted-texture refresh
- Removed the eager path that CPU-decoded every retained P_8/AP_88 texture to RGBA
and re-uploaded the entire set through
glTexImage2Don every palette download. - The Win32 OpenGL backend advances a generation only when palette bytes actually change. A byte-identical download skips both generation advance and texture work.
- Each indexed texture records its applied palette generation. A textured draw
decodes retained indices and calls
glTexSubImage2Donly when the currently sampled texture is stale. - Palette changes remain visible before the next sample, retaining Task 483's independent texture/palette lifetime, opaque P_8 alpha, and AP_88 texel-alpha contracts.
Performance telemetry
- The ending texture census now reports total, changed, and identical palette downloads.
- It also reports lazy-refresh successes/failures, source/RGBA bytes, CPU decode time, and OpenGL upload-call time.
- The deferred Task 483 palette-performance TODO is complete in this implementation and its documentation.
Verification
- Win32 x86 Debug/Release builds of
repiu_aot_probeandrepiusucceeded. - The complete
pumpitp3/PIU/PIU.EXEAOT probe exited zero in both configurations. glide_texture_table_stack_probe=pass,glide_texture_census_palette_refresh_profiled=true, andglide_texture_census_all=truewere confirmed.- The user confirmed the performance improvement on the real
pumpitp3title screen. - Only the existing C4819 code-page and LNK4217 warnings remain.
- Removed the eager path that CPU-decoded every retained P_8/AP_88 texture to RGBA
and re-uploaded the entire set through
-
2026-08-15
v0.0.155
Changes since v0.0.154. This release restores DOS file-create dispatch on the dynamic traced execution path and scales Glide point size with the viewport when the 640×480 logical display is enlarged.
Full release notes
Changes since v0.0.154. This release restores DOS file-create dispatch on the dynamic traced execution path and scales Glide point size with the viewport when the 640×480 logical display is enlarged.
Dynamic DOS file creation
- Dynamic traced
INT 21h/AH=3Chrequests now delegate to the common DOS HLE handler. The existing VFS remains the single owner of file-create semantics, return registers, and EIP advancement. - Closing a file now releases the write-stream cache as well as the read stream, allowing scratch directories to be cleaned up after created files are closed.
- A probe using a real
CD 21instruction and guest path covers the integration boundary from traced dispatch through file creation and cleanup.
Viewport-scaled Glide point size
- Glide point size uses the smaller horizontal or vertical ratio between the original 640×480 logical display and the drawable viewport. At 1280×960, a former 1×1 dot occupies 2×2 physical pixels.
- Selecting the smaller ratio preserves square dots for non-uniform scaling, and downscaled viewports retain a minimum size of one pixel.
- The calculated size is clamped to OpenGL's reported aliased-point range and is applied consistently to direct and batched point rendering.
Verification
- Win32 x86 Release builds of
repiu_aot_probe,repiu_glide_render_probe, andrepiusucceeded. - The AOT probe passed with
dos_file_create_traced_dispatch=true,dos_file_create_cleanup=true, anddos_file_create_all=true. - The Glide render probe passed point-scale cases for 1×, 2×, 3×, non-uniform, downscaled, and invalid viewport dimensions.
- Only the existing C4819 code-page and optional LibUSB discovery warnings remain.
Remaining live checks
- Automated verification passed, but the next live-game execution frontier and
generated
ERRLOG.txtcontents have not yet been inspected. - The
pumpit8service screen still needs a visual check that dots occupy 2×2 pixels in the 2× viewport.
- Dynamic traced
-
2026-08-14
v0.0.154
Changes since v0.0.153. This release supports DOS date setting as per-run virtual state, expands the MAME Pump It Up target registry to the complete 22-entry catalog, and safely inherits clone CAT702 assets from parent ROM sets.
Full release notes
Changes since v0.0.153. This release supports DOS date setting as per-run virtual state, expands the MAME Pump It Up target registry to the complete 22-entry catalog, and safely inherits clone CAT702 assets from parent ROM sets.
Virtual DOS system date
- Implemented
INT 21h/AH=2BhSet System Date. A valid date is stored in the execution context as a day offset from the host's local date. AH=2AhGet System Date returns the virtual date and its weekday without changing the real Windows system date.- Ordinary DOS HLE and traced/AOT paths share the same contract, covered by date validation and set/get round-trip probes.
- The user confirmed normal progress after date setting in a real run.
Complete 22-entry Pump It Up target catalog
- Expanded the previous 14 MAME profiles to the 22-entry
GAMEcatalog order fromxtom3d.cpp, addingpumpit2a,pumpit3a,pumpitpru,pumpitea,pumpipx2p,pumpitp3a,pumpipx3a, andpumpipx3b. - Preserved version, year, and date details in display names and normalized the
brand spelling to
Pump It Up. - Removed the temporary
piu_1stprofile and changed runner, supervisor, and analyzer defaults topumpit1.
Parent ROM-set CAT702 fallback
- Every PIU profile now records its direct MAME parent ROM-set ID.
- CAT702 lookup checks the current name in the current ZIP, the parent name in that ZIP, then the parent name in the sibling parent ZIP. Only an absent member advances to fallback; archive, extraction, and CRC failures remain visible.
- A real
pumpitprurun selected the eight-bytepumpitpr.cat702member, initialized PIU10, handled all 33 observed PIU10 port-I/O operations, and reached Glide initialization.
Verification
- Full Win32 x86 Debug and Release builds passed.
- Full
pumpipx3AOT probes exited zero in both configurations withpiu10_target_profiles=true,dos_date_probe=pass, andcoherence_all=true. - Debug and Release
--piu10probes passed the profile, CAT702 vector, MP3 ring, and frame-audit checks. - Only the existing C4819 and LNK4217 warnings remain.
- Implemented
-
2026-08-13
v0.0.153
Changes since v0.0.152. This release corrects the Glide texture-table calling convention observed in
pumpipx3and restores corrupted colours by making existing P_8/AP_88 indexed textures consume palettes downloaded later.Full release notes
Changes since v0.0.152. This release corrects the Glide texture-table calling convention observed in
pumpipx3and restores corrupted colours by making existing P_8/AP_88 indexed textures consume palettes downloaded later.Glide texture-table ABI correction
_GRTEXDOWNLOADTABLE@12now decodes four guest-stack dwords: return address, TMU, table type, and data pointer. The old path read only three dwords including the return address, shifted type and data by one slot, and cleaned 12 rather than 16 bytes on return.- Every handled path, including success, unsupported table types, and read failures,
now applies the stdcall
ESP += 16contract. - The exact logged frame plus short and null frames are covered by
glide_texture_table_stack_probe.
Indexed-texture colour restoration
- The ignored high byte of a standard Glide palette entry is no longer treated as alpha. P_8 uses opaque palette RGB, while AP_88 retains alpha from the texel's high byte.
- The backend now preserves P_8/AP_88 indices and re-decodes existing indexed textures after a palette download, matching Glide's separate texture and palette lifetimes.
- The user confirmed that the previously corrupted palette colours are restored in the real scene.
Verification
- Win32 x86 Debug and Release builds of
repiu_aot_probeandrepiusucceeded. - Both full probes exited zero with
glide_texture_table_stack_probe=passandglide_texture_census_all=true. - Only the existing C4819 and LNK4217 warnings remain.
Known performance limitation
- The user's runtime check found a severe performance loss after colour restoration.
- Every palette download currently CPU-decodes all retained P_8/AP_88 textures to
RGBA and re-uploads them through
glTexImage2D. Unchanged-palette suppression, lazy refresh of used textures, and GPU indexed sampling are not implemented. - This release keeps the accuracy fix. Measurement and optimization are deferred in
the Task 483 follow-up recorded in
docs/TODO.md.
-
2026-08-12
v0.0.150
Everything since the previous release, v0.0.149. This release adds a guest memory-range dump diagnostic to the execution probe and, acting on the cause that diagnostic established, extends DOS low-memory read servicing to string instructions, removing the
pumpit8termination during background loading.Full release notes
Everything since the previous release, v0.0.149. This release adds a guest memory-range dump diagnostic to the execution probe and, acting on the cause that diagnostic established, extends DOS low-memory read servicing to string instructions, removing the
pumpit8termination during background loading.Execution probe memory-range dump
REPIU_EXECUTION_PROBE_DUMP_*preserves a guest memory range to a file at the probe's first hit. The base is a register name or an absolute address, with optional indirect dereference to follow a pointer inside a structure and up to 1 MiB copied.- The capture buffer is allocated once before execution starts, so the first-hit recorder never allocates during exception handling. The file is written after the guest thread stops, and no file I/O happens inside the VEH.
- The bytes never travel through the telemetry snapshot, which carries only capture and write status with the addresses.
- The copy happens only when the complete range is readable inside the guest arena; no partial range or arbitrary host address is read. Without the environment variables the existing execution path is unchanged.
- The repeatable procedure is in
docs/guides/execution-probe-memory-dump.md.
DOS low-memory string instruction servicing
HandleGuestLowMemoryReadFaultaccepted onlyMOV,MOVZX, andMOVSXand rejected every other mnemonic, leaving access violations from string instructions reading low memory unserviced.- Added a dedicated module servicing
SCAS,LODS, andCMPSreads of low memory.MOVSis excluded because a dedicated handler already owns it, andSTOSis excluded because it writes and falls outside this read facility. - Using the restartability of
REPforms between iterations,EIPadvances only when the repetition finished and is otherwise left in place, so the handler never tracks where low memory ends. - Width comes from the mnemonic rather than the operand width, since a
66hprefix changes the mnemonic itself and makes the mapping exact.ZYDIS_CATEGORY_STRINGOPgates first because the SSEcmpsdshares its mnemonic with the string form. SetCompareFlags8is generalized into a width-takingSetCompareFlagswith the original delegating to it; 8-bit results are unchanged.- Servicing declines, leaving the existing diagnostics to report the cause, when the address size is
not 32-bit, the access straddles the low-memory boundary, the
CMPScounterpart fails the guest-range check, or aREPhasECXalready zero. - No target-specific or executable-address behavior bypass was added.
pumpit8 background-loading termination resolved
- The
0xC0000005termination while readingBR01_01.tga— actually a PNG — fromBGA/083.DATis gone. - The cause is that the asset's
iCCPchunk carries a zlib datastream written without its four-byte adler32. The chunk CRC32 is0x8FC1BA08both stored and computed, confirming the chunk reached memory exactly as authored, so rePIU's file I/O, RES decryption, and PNG delivery are sound. - The original libpng
1.0.6does not check the null return frompng_decompress_chunkbefore adding the prefix0x17and dereferencing it withrepne scasb. Static disassembly confirms no conditional branch separates the call from the fault. - The analysis is accumulated in
docs/analysis/pumpit8-bga-iccp-crash.md.
Verification
- Win32 x86 Debug and Release builds passed.
- A Debug
pumpit8run recorded 2,522 string services over 2,522 iterations, last EIP0x040E5D0D, address0x00000017, mnemonicSCASB, and zero exceptions. - The same run proceeded past
BGA/083.DATtoAUDIO/004.AUD, rendering at roughly 6.9 FPS. It ended on an SDL exit request issued to stop the observation, not on an access violation. - All 819 OpenWatcom samples were built and the suite re-measured in the baseline's Release configuration, giving RunPassed 528 with zero regressions and zero new passes.
Known limitations
pumpit8only passes this site; completion is not verified, and later stages remain separate observation targets.- That linear
0x17is actually readable under DOS/4GW is inferred from standard protected-mode extender behavior and was not observed directly in the original environment. - Low-memory servicing covers reads only. Paths that write to low memory, such as
STOS, are not serviced yet. - No
67h-prefixed 16-bit address-size string instruction has been observed, so that form is not implemented. - The sample suite never reaches a low-memory string read, so zero regressions confirms the absence of impact rather than validating this path.
-
2026-08-11
v0.0.149
Everything since the previous release, v0.0.148. This release separates per-target CAT702 configuration, extends address- and binary-independent batching to wrapped port I/O and MP3 feeders, and adds seven later PIU10 target profiles.
Full release notes
Everything since the previous release, v0.0.148. This release separates per-target CAT702 configuration, extends address- and binary-independent batching to wrapped port I/O and MP3 feeders, and adds seven later PIU10 target profiles.
TargetProfile and CAT702
- Added
TargetProfile::enable_cat702independently from the PIU10 ISA-board capability. - With CAT702 disabled, PIU10 flash, MP3, and DAC remain active; CAT702 data-out is zero, serial
input is ignored, and
<target>.cat702is not required. - Disabling CAT702 for
pumpitpcreaches the real Lock Error key-wait path, while restoring the default enabled value continues beyond the same point. This verifies that the profile setting controls the original security-check result. - No title-specific adjustment was made to the CAT702 transform or challenge-response algorithm.
Wrapped port-I/O delay loops
- The batcher validates input-wrapper register preservation, port transfer, relative
call, guest stack return address, and caller loop before skipping only iterations whose input is discarded. - The original guest executes the final iteration and flag calculation, and matching uses no target name or fixed code address.
- In a same-Release-binary
pumpitpcA/B, total port I/O fell about 94.2%, from 244,970 to 14,110, and privileged-instruction exceptions fell about 96.7%, from 236,962 to 7,936.
Wrapped PIU10 MP3 feeders
- The existing direct
OUT DX,ALmatcher remains first, followed by a matcher that validates a register-preserving byte-output wrapper and feeder call/return relationship. - Source cursor, frame count,
ECX, backward loop, and service conditions come from instruction operands, without profile, executable-address, or hash dependencies. - Audit plans also stop at the compressed-inflight
0xE00DEMANDboundary, preserving the same return point as scalar status polling. - A live
pumpitpcaudit passed 3,700 consecutive segments. A normal run batched 136,595 of 140,795 bytes, about 97.0%, with no drops or starvation.
Additional target profiles
- Added
pumpitpr,pumpitpx,pumpit8,pumpitp2,pumpipx2,pumpitp3, andpumpipx3immediately after the existingpumpiteprofile. - They use the shared
piu_common, CHD mount, JAMMA/YMZ280B, PIU10 ISA, and CAT702 paths, with zero milliseconds of default MP3 startup latency. - Debug and Release profile probes validate registration order, uniqueness, paths, ROM-set ids, and capabilities.
Verification
- Win32 x86 Debug and Release builds passed.
- Debug and Release
repiu_aot_probe --piu10passed. - Local ROM ZIPs for all seven new targets passed required PIU10/CAT702 entry validation.
- Live
pumpitpcnormal execution, batching A/B, and MP3 audit runs completed.
Known limitations
- Copyrighted ROM ZIP and CHD assets are not distributed.
- The seven new targets have no local CHDs, so validation currently stops after profile and ZIP checks; ISO mounting and guest execution remain unverified.
- If a wrapper, loop, memory alias, or runtime range cannot be fully validated, the matchers retain the existing scalar path.
- Added
-
2026-08-11
v0.0.148
Everything since the previous release, v0.0.147. This release fixes the
Lock Error!!!seen during thepumpitpcCAT702 security check. It does not tune the CAT702 algorithm for one binary; it corrects the shared Port I/O policy so Watcom output wrappers reused across devices remain intact.Full release notes
Everything since the previous release, v0.0.147. This release fixes the
Lock Error!!!seen during thepumpitpcCAT702 security check. It does not tune the CAT702 algorithm for one binary; it corrects the shared Port I/O policy so Watcom output wrappers reused across devices remain intact.pumpitpc CAT702 security check
- A shared
OUT DX,AXwrapper handles both startup writes to0x2ACand0x2A6and later PIU10 CAT702 writes to0x2D4,0x2D6, and0x2DA. - The previous HLE permanently NOP-patched the wrapper's
OUTinstruction on the first ignored or observation-only output. Later CAT702 serial transactions could no longer execute, sending the game into its Lock Error path. - Ignored and deferred outputs now advance only the current EIP without modifying guest code. This discards one transaction while retaining the shared wrapper for later DX-port calls.
- The policy does not branch on target ID, executable address, or a specific port number, so it applies generally to equivalent dynamic DX-output wrappers.
CAT702 model verification
- The regression probe now covers real transform
F0 1C FE 03 81 40 38 F8, challengeD0 AA C9 F8 96 4C D0 DE B6 0B, and response7D 77 FE EA 8B 7A 55 7D 5F 1E. - With the guest's inverted LSB-first input, MSB-first response accumulation, extra clocks, adjacent-byte alignment, and final bit reversal included, the existing CAT702 HLE model matches the real response exactly.
- No CAT702 transform, bit-order, or per-binary adjustment was added.
Verification
- The Win32 x86 Debug build passed.
- The real PIU10 CAT702-vector probe and complete
pumpitpcAOT probe passed. - A default-environment
pumpitpcrun continued for more than 50 seconds beyond the previous failure point.
Known limitations
- Copyrighted ROM ZIP and CHD assets are not distributed.
- Additional CAT702 command forms used by other executables remain separate compatibility work.
- A shared
-
2026-08-10
v0.0.147
Everything since the previous release, v0.0.146. This release makes AOT execute the zlib DEFLATE state dispatch that previously fell through the exception path millions of times during pumpito loading.
Full release notes
Everything since the previous release, v0.0.146. This release makes AOT execute the zlib DEFLATE state dispatch that previously fell through the exception path millions of times during pumpito loading.
pumpito loading performance
- AOT now recognizes the byte-state jump table used by pumpito's statically linked zlib 1.1.3
inflate_codes(). The previous log recorded roughly 12.26 million HLE dispatcher round trips at this loading hotspot. - The matcher validates the register and mask relationship in
cmp low-r8, bound; jnbe; and parent-r32, 0xff; jmp cs:[parent-r32*4+table], then reuses the existing native jump-table emitter. - No pumpito-specific address, executable name, or zlib function name participates in matching. This is a general AOT optimization for binaries with the same machine-code contract.
- A different mask or destination register, a high-byte comparison, or omitted normalization safely falls back to the existing dispatcher path.
Verification
- Planning directly from pumpito
PIU.EXE'sinflate_codes()region produces one native jump table with ten targets and zero HLE boundaries. - Synthetic probes cover the supported byte-guard form, the existing 32-bit guard form, and four fail-closed variants.
- Win32 x86 Release builds of
repiuandrepiu_aot_probe, plus the full probe suite, pass.
Known limitations
- Loading-time and exception-count reductions still require final measurement from a new run with user-supplied game assets. Static planning and code-cache generation are verified, so no measured speedup multiplier is claimed yet.
- Copyrighted ROM ZIP and CHD assets are not distributed.
- AOT now recognizes the byte-state jump table used by pumpito's statically linked zlib 1.1.3
-
2026-08-09
v0.0.146
Everything since the previous release, v0.0.143. This stretch focuses on later PIU targets and PIU10 sound-board HLE.
pumpito,pumpitc,pumpitpc, andpumpitenow have a path that keeps the original game code running while MP3 music and rendering progress concurrently.Full release notes
Everything since the previous release, v0.0.143. This stretch focuses on later PIU targets and PIU10 sound-board HLE.
pumpito,pumpitc,pumpitpc, andpumpitenow have a path that keeps the original game code running while MP3 music and rendering progress concurrently.New targets and CHD support
- Added MAME CHD profiles for
pumpito,pumpitc,pumpitpc, andpumpite, bringing the selectablepumpit*profiles to seven together withpumpit1,pumpit2, andpumpit3. - Added cooked Mode1 CHD sectors plus data-track LBA and ISO-extent correction, allowing ISO9660
mounts for images such as
pumpite, whose data track does not begin at disc LBA zero. - Mount caches are reused while CHD identity is unchanged. Each profile launches
build/runtime_mounts/<target>/PIU/PIU.EXE. - JAMMA input is enabled for every
pumpit*profile. PIU10 ISA flash, CAT702, and MP3 capability is enabled only for the hardware-appropriatepumpito,pumpitc,pumpitpc, andpumpite.
PIU10 ISA-board HLE
- Added platform-neutral HLE for the PIU10 20-bit flash address, read-only
piu10.u8, destination selection, status source, and CAT702 PIU serial-security device. piu10.u8and<target>.cat702are extracted from the matching ROM ZIP with CRC validation. The board is not exposed to unsupported profiles, keeping thepumpit1throughpumpit3paths separate.- Only the ISA port-I/O boundary is replaced; original executable and gameplay logic remain authoritative.
MAS3507D MP3 streaming HLE
- PIU10 MPEG byte streams are played through minimp3 and SDL3. MAME's MAS3507D implementation informed the device contract only; no MAME MAS3507D code is included.
- Frames are decoded as they become available instead of waiting for a complete song. A persistent decoder and dedicated audio worker let original game execution and rendering continue during playback.
- Stream sync requires three compatible headers, and exactly one parser-validated frame is passed to the decoder, preventing an incomplete next header at a FIFO boundary from invalidating the current frame.
- MAS3507D
DEMANDis based on compressed inflight across both the 4 KiB physical ring and worker staging. This backpressure fixes mid-song interruption caused by the guest running seconds ahead of HLE output. - Frame-sync status follows actual SDL queue playback position rather than decode-ahead time.
DAC3350A output control
- DAC3350A AVOL transactions are reconstructed from destination-
0x010SDA/SCL traffic. - AVOL mute and dB steps control SDL audio-stream gain, including PCM already queued for output, without changing the compressed FIFO or MPEG decoder state.
- MP3 pause is not implemented.
MP3 input performance and generalization
- Batches the repeated
OUT DX,ALfeeder tail, removing a per-byte HLE dispatcher round trip. - The matcher uses no target name, fixed EIP, LE object number, or data offset. It validates the current instructions' load/increment/store aliases, register def-use, and branch boundaries, so equivalent compiler register allocations share one device contract.
- Live logs batch about 98.6% of MP3 input on
pumpitcand about 98.7% at the firstpumpitecheckpoint. Any validation failure preserves guest state and falls back to the scalar byte path.
MP3 latency and diagnostics
- Every PIU10 profile now defaults to 0 ms MP3 startup latency.
REPIU_PIU10_MP3_LATENCY_MS=0..500can request per-run silence compensation when needed.REPIU_PIU10_DAC_AUDIT=1records PCM queue, device buffer, compressed ring, decoder pending, and inflight state at each DAC transaction.REPIU_PIU10_MP3_STREAM_AUDIT=1compares producer and consumer streams with independent 4 KiB hashes. Both diagnostics are off by default.
Third-party code and licensing
- PIU10 and CAT702 contracts are informed by BSD-3-Clause MAME sources.
- MP3 frame decoding uses a pinned upstream minimp3 commit under CC0 1.0 Universal.
- No MAME emulator, CPU/machine framework, or MAS3507D wrapper code is included.
Known limitations
- Copyrighted ROM ZIP and CHD assets are not distributed; users must supply legally owned copies.
- CI has no game assets, so concurrent music/rendering behavior remains a local live-validation responsibility.
- rePIU remains research-stage software rather than a finished game launcher.
Versions included
v0.0.144,v0.0.145,v0.0.146. - Added MAME CHD profiles for
-
2026-08-07
v0.0.143
Everything since the previous release, v0.0.136. This stretch is about performance: pumpit2 gameplay went from 69.3 to 107.2 frames per second, +54.7%.
Full release notes
Everything since the previous release, v0.0.136. This stretch is about performance: pumpit2 gameplay went from 69.3 to 107.2 frames per second, +54.7%.
⚠️ The executable was renamed
repiu_loader_win32.exeis nowrepiu.exe. Update any script or shortcut that points at the old name.Performance
Removing the inline-cache patch round trip is where the frames came from: +54.7%. Every indirect call/jump cache miss writes fourteen bytes into the code cache, and the guest thread was waking a worker thread and waiting for it to do that — roughly 350 times per frame, holding 30.4% of the guest thread's sampled positions. The guest now patches its own caches.
Glide setter state elision skips the host round trip when a setter is called with the value already applied; 89.6% of setter calls are now elided, and
grDitherModecosts 95% less per call. Draw batching collects consecutive primitives into one submission, raising the mean batch from 2.0 to 32.1 primitives.All three are on by default and each has an off switch:
REPIU_AOT_INLINE_CACHE_PATCH_INLINE=0,REPIU_GLIDE_SETTER_ELIDE=0andREPIU_GLIDE_DRAW_BATCH=0.Diagnostics
A host crash reporter now records the exception code, address, access kind and a symbolised call stack with source lines. It earned itself immediately: a teardown crash that five rounds of guessing had failed to find was named on the first crashing run.
The asynchronous present axis was closed as a measurement error.
grBufferSwaplooked large with vsync on, but that was display wait; with vsync off it is 2.9%. The implementation stays opt-in, kept on accuracy grounds rather than speed.Build and CI
OpenWatcom is pinned to an immutable dated snapshot instead of upstream's rolling
Current-build, whose asset is replaced in place so that a pinned hash expires with nothing here changing. The toolchain moved with it and the sample baseline did not: 819 samples build, 528 pass, zero regressions. And the sample suite now builds without the original asset tree — it compiles its own DOS programs and never loads the game, but the environment check demanded the assets anyway.Versions included
v0.0.137,v0.0.138,v0.0.139,v0.0.140,v0.0.141,v0.0.142,v0.0.143. -
2026-08-07
v0.0.136
Full Changelog: https://github.com/nworkers/rePIU/compare/v0.0.81...v0.0.136
Full release notes
✨ New Features & Emulation (HLE)
- Emulation & Audio
- Emulate the PIU10 YMZ280B and route its output to SDL audio (
b3dcb5e) - Deliver CD playback position to the guest and start track 2 at its true beginning (
5f93b64)
- Emulate the PIU10 YMZ280B and route its output to SDL audio (
- Graphics & Glide
- HLE: Implement Glide Combine and TMU state for full alpha and texture accuracy (
3047b5c) - Implement Glide line drawing and cull modes (
2a9456a) - Implement safe
GlideGateIdO(1) switch dispatch and complete Glide API family (73a3122)
- HLE: Implement Glide Combine and TMU state for full alpha and texture accuracy (
- Target Bring-up
- Add
pumpit2multisession CHD profile and handle unresolved AOT-DBT direct edges (919ff28) - Bring up the
pumpit3target through five HLE gaps to its render loop (211cbfc)
- Add
- Execution & AOT
- Add AOT-DBT execution and improve SDL host lifecycle (
84797bf) - Implement Port I/O AOT Fast-Path and Opcode-directed HLE Dispatcher (
62c89f8) - Native region execution foundation: diagnose single-step wall, prove the interception mechanism (
0c7a5ba) - Translate AOT segment instructions natively, removing the dominant segmentation exception cost (
61d04ae)
- Add AOT-DBT execution and improve SDL host lifecycle (
⚡ Performance & Optimization
- AOT & Execution Speed
- Translate from the live guest arena and cut append cost 10.5x (
3552db7) - Unblock the post-HLE return path and raise the frame rate 2.19x (
e2f6563) - Move the performance baseline to Release and raise the frame rate 5.8x (
c1087b4) - Harden AOT-DBT dispatch and enable native linear spans (
d790ab7) - Reduce AOT-DBT single-step overhead safely (
fa73415) - Add guarded AOT segment-pop fast path (
18540db) - Improve AOT fallback performance and diagnostics (
e67a9e7) - Improve AOT caching and native execution coverage (
fc7ddba) - Attribute whole-run execution time and remove the AOT lookup bottleneck (
f687886) - Drop the redundant per-gate pump rendezvous (
a2982d9) - Re-baseline the cost profile and cut the Glide rendezvous latency (
6101dbe) - Optimize Music Select execution boundaries (
672e343)
- Translate from the live guest arena and cut append cost 10.5x (
- System & I/O
- Keep DOS file handles open across reads (Task 374) (
4760c19) - Replace Glide GL error polling and find the display limit (Tasks 369-371) (
8bf7d52)
- Keep DOS file handles open across reads (Task 374) (
🐛 Bug Fixes
- Graphics (Glide)
- Fix the corrupted 3D model by connecting Glide vertex depth (
6ab688a) - Fix Glide LFB rendering and stabilize Win32 AOT execution (
ea609ba) - Fix Glide background, fog, and LFB rendering (
bd5a5c4) - Draw Glide sprites at the right scale and color, and implement grHints (
9bcba7b) - Harden Glide gates and report implementation gaps (
7c758b0)
- Fix the corrupted 3D model by connecting Glide vertex depth (
- System & Inputs
- Fix the note and background animation (BGA) jumping by preserving owed timer ticks (
e341c8f) - Fix guest timer delivery and honor PIT cadence (
b18e54a) - Fix the
pumpit3startup stall and add the instruments that found it (280ed28) - Fix JAMMA system and P2 keypad mappings (
dc8852f) - Classify AOT-DBT RET fallbacks by exclusive cause and fix the next DBT target (
f815d2e)
- Fix the note and background animation (BGA) jumping by preserving owed timer ticks (
🔍 Profiling & Analysis
- Bottleneck Identification
- Find
pumpit3's real input cost and bound it with a snapshot (cc21627) - Locate
pumpit3's dominant cost in one port I/O instruction (8d9b365) - Reproduce the
pumpit3startup stall and rule out its two obvious causes (f78b895) - Locate the dynamic translation bottleneck in the full-arena snapshot (
79691a2) - Instrument AOT boundary exits and pin the root cause to segmentation instructions (
c4885c3)
- Find
- Cost Attribution & Profiling
- Attribute
pumpit3arena execution and record the next ten steps (458efdd) - Attribute every VEH exit and settle who consumes the arena-entry single step (
d789786) - Measure the kernel exception round trip and scope the next axis (Tasks 372-373) (
d1155a3) - Attribute and act on the Glide/exception axis (Tasks 364-368) (
3ab6a1b) - Attribute execution cost down to dynamic translation on the worker thread (
47e1b96) - Profile Release execution and Glide bottlenecks (
2e502e3) - Record aot-dynamic being 14.6x slower than legacy as the next task (
c15c362)
- Attribute
- Investigations & Diagnostics
- Instrument the CD audio path and trace the stall to our own watchdog (
7583fde) - Disprove the hidden single-step population and retract 6.5% (Task 376) (
99d7016) - Settle the texture suspicion with a census and pixel dump (Task 375) (
206d414)
- Instrument the CD audio path and trace the stall to our own watchdog (
🛠 Chore, Refactoring & CI/CD
- Refactoring
- Migrate window and CD audio services to SDL3 (
26f28fd) - Consolidate the execution backends to legacy and dynamic (
78366f8)
- Migrate window and CD audio services to SDL3 (
- CI/CD & Testing
- Add tag-triggered build verification and release artifacts on GitHub Actions (
c6e9117) - Require a version tag on every main merge (
35842b6) - Require completion in the OpenWatcom sample pass criterion (
3b5073c) - Refresh the OpenWatcom sample baseline to v0.0.133 (
2038829)
- Add tag-triggered build verification and release artifacts on GitHub Actions (
- Configuration & Documentation
- Default execution to dynamic with no time limit, and make source comments English-only (
d78e328) - Attribute plan build and move the performance baseline to Release (
dcd8937) - Document Glide call-volume performance plan (
609d77d) - Record the GLSL shader review and the Task 377 direction (
0e49182) - Record next optimization frontier: multi-slot indirect inline cache (
b3e7fa2) - Add the post on tracing note jumping to lost timer ticks (
a19360e)
- Default execution to dynamic with no time limit, and make source comments English-only (
Full Changelog: https://github.com/nworkers/rePIU/compare/v0.0.81...v0.0.136
- Emulation & Audio