Skip to content

Releases: beebono/DSperate

DSperate v1.15.1

Choose a tag to compare

@github-actions github-actions released this 11 Sep 01:55

DSperate v1.15.1

Full changelog: v1.15.0...v1.15.1

Games run without BIOS or firmware dumps (built-in FreeBIOS and a generated firmware); a set dumped from your own DS unlocks the firmware boot and cycle-exact SWI timing. None is included.

Tarballs: linux-aarch64 / linux-armhf are dynamically linked and stripped -- the ones to package. The -static variants carry libstdc++/libgcc inside and keep their debug symbols, for a device whose runtime is older than the build's, or for a backtrace.

DSperate v1.15.0

Choose a tag to compare

@github-actions github-actions released this 10 Sep 22:22

DSperate v1.15.0

Screen layout is now something you set from the pause menu rather than only from the ini, panel rotation reaches the scanline tiers for portrait handhelds, and the Pokemon games that hung on CONTINUE run.

Fixes:

  • Pokemon Platinum, HeartGold/SoulSilver and the romhacks built on them no longer hang on CONTINUE. Picking CONTINUE shuts the Wi-Fi radio down and then spins on W_POWERSTATE waiting for the power-off to show; with the register merely stored the ARM7 never left that loop and the screen stayed black. The transceiver power model is now ported from melonDS -- force, mode reset, power-state/TX/down-control, RF status and pins, and the 2048 us delayed power-on on a scheduler event. The five reference scenes are bit-identical, so nothing else moved.
  • On the KMS/DRM tier the pause menu no longer appears one press late. A menu redraw is a one-off present with no frame behind it, and that tier queues a flip behind the pending one and only issues it when the next frame starts -- so the picture waited for a keypress that had already happened. The present path now flushes.
  • Non-ASCII text draws instead of turning into blanks. Titles come from RetroAchievements as the game is actually named ("Okamiden" with a macron), and the menu font is ASCII. A Latin letter with a mark is now folded onto the letter it decorates and anything else non-ASCII draws as ?, with widths and trimming counting glyphs rather than bytes.
  • A dump RetroAchievements knows but has not published a set for is no longer announced as "achievements active". It loads successfully with zero achievements, which used to raise the same toast as a real set and send the player looking for a list that is not there. It is now quiet, and the account page says what happened. This is separate from an unknown hash, which still reports itself with the hash.
  • Truncated achievement descriptions scroll. A description too long for the row marquees like a name does, so it can be read to the end.
  • A pass over compiler warnings across the tree.

Additions:

  • The screen layout is on the pause menu. A LAYOUT row on the layout page picks between VERTICAL, HORIZONTAL, SINGLE, PIP, DOMINANT V and DOMINANT H directly, and the row reads the layout that is actually on screen -- the hotkeys and a loaded state move it without writing video.layout.
  • The layout hotkeys step through a set you choose (video.layout_cycle, one checkbox per layout on the same page). Ticking a layout puts it back at its own place in the ring rather than at the end, and the last ticked layout cannot be unticked, since the hotkeys need somewhere to go. The checkboxes take effect as you tick them; nothing on screen moves until you press a hotkey.
  • DS_ROTATE on the scanline tiers. Panel rotation of 0/90/180/270 -- the display-engine tier's convention -- now works on the fbdev, KMS, dmabuf and window-surface tiers, for a portrait fb0 like the RG28XX's. The frame is composed unrotated by the existing layout, scaling, inset and canvas code, so the pause menu and the OSD rotate with the game, and the present transposes it with NEON. Touch input un-rotates with it. On an RG35XX SP running SM64 this costs about 3.7 ms a frame against no rotation; the unrotated path is untouched.
  • DS_SHOT_AT=N takes the screenshot hotkey's picture after frame N, for offscreen and replay runs where no hotkey can fire.
  • The README is rewritten as a concise feature list.

Full changelog: v1.14.1...v1.15.0

Games run without BIOS or firmware dumps (built-in FreeBIOS and a generated firmware); a set dumped from your own DS unlocks the firmware boot and cycle-exact SWI timing. None is included.

Tarballs: linux-aarch64 / linux-armhf are dynamically linked and stripped -- the ones to package. The -static variants carry libstdc++/libgcc inside and keep their debug symbols, for a device whose runtime is older than the build's, or for a backtrace.

DSperate v1.14.1

Choose a tag to compare

@github-actions github-actions released this 10 Sep 04:17

DSperate v1.14.1

A small follow-up to v1.14.0: one save-state bug worth a point release, and a way to sign in to RetroAchievements from a launcher.

Fixes:

  • A save state made with different BIOS images is now refused instead of hanging the machine. The BIOS is not part of a state, but the CPUs' program counters are, and at a frame boundary the ARM7 is nearly always sitting inside a BIOS routine -- so a state made with your own dumps and loaded under the built-in FreeBIOS (or the other way round) resumed the ARM7 in unrelated code. On Mario & Luigi: Bowser's Inside Story it parked in Undefined mode with interrupts masked and never came back, which reads as a hang with dead sound and dead controls. States now carry the identity of the BIOS pair they were made with, and a mismatch is refused with a message saying which way round it is. A state made with a different firmware is not refused -- nothing resumes inside the firmware -- but it is noted on the log.
  • A refused state is now visible on the pause menu. The auto slot loads before there is a menu to say anything, so a rejection used to show only as the game starting from the beginning; the slot row now reads SLOT < n > REJECTED in warning colour until you move off it.

Additions:

  • --cheevos-token FILE and --cheevos-user NAME (config: cheevos.token_file, cheevos.username), for CFW front ends that already sign the player in and write the token out in PPSSPP's format -- the token alone, with the account name coming from the config or the flag. The file wins over a stored sign-in, is never written back to, and passing it implies --cheevos. Our own two-line username\ntoken file is accepted there too.
  • Character entry reads more plainly. Up now steps forward through the character table (A to B) and Down back, and the tables are named CAPITALS / LOWERCASE / NUM+SYM.
  • A refreshed PGO profile.

Full changelog: v1.14.0...v1.14.1

Games run without BIOS or firmware dumps (built-in FreeBIOS and a generated firmware); a set dumped from your own DS unlocks the firmware boot and cycle-exact SWI timing. None is included.

Tarballs: linux-aarch64 / linux-armhf are dynamically linked and stripped -- the ones to package. The -static variants carry libstdc++/libgcc inside and keep their debug symbols, for a device whose runtime is older than the build's, or for a backtrace.

DSperate v1.14.0

Choose a tag to compare

@github-actions github-actions released this 10 Sep 02:41

DSperate v1.14.0

What's new: RetroAchievements, in Casual mode. DSperate now talks to RetroAchievements through rcheevos, so a game that has a set unlocks achievements as you play it, credited to your account on the server. It is Casual-only by design -- hardcore credit is gated on RetroAchievements' registered-client list, and nothing in this build turns it on -- which means save states, rewind-shaped conveniences and the rest of the emulator stay exactly as they were.

What that looks like on a handheld:

  • Signing in without a keyboard. If the CFW has already signed you in, DSperate reads that token -- ROCKNIX's EmulationStation system.cfg and RetroArch's cheevos_* pair are both understood (cheevos.use_system_login, on by default). Only the token is read, never the password sitting next to it, and their file is never written. Failing that, an ACHIEVEMENTS row on the pause menu leads to an account page with an on-device sign-in; the token is kept at <config>/cheevos.token, mode 0600, and the password is never stored.
  • An achievement list and an account page, drawn at panel resolution, telling you whether you are signed in, whether this game has a set, and why not when it doesn't.
  • A toast when something unlocks, bottom-right so it covers least, with an optional screenshot of the frame that earned it.
  • Encore mode, which re-arms achievements your account already holds so a replay shows the set unlocking again. It is read when a game loads, so its row is labelled NEXT LAUNCH.
  • Progress survives a save state. A CHVO chunk carries the runtime's state, and a state loaded at startup -- from --load-state or the auto slot -- is held and applied once the set arrives, so the runtime is never evaluated against a machine state the game has left behind. A mismatched or rejected restore falls back to a clean reset rather than risking a false unlock.
  • Cheap enough to leave on. Measured on the device with a real 110-achievement set: 85 us mean, 282 us p99 -- under 2 % of a frame.
  • No new build or runtime dependency. The HTTP transport is a dlopen of the CFW's own libcurl.so.4; nothing is linked, no TLS stack is vendored and no certificates are shipped. DS_CHEEVOS_LIBCURL points at one that is hidden. Packagers: see docs/ for exactly which files each CFW already has and which it doesn't -- the H700 needs a copy, the A30 needs an armhf build.

One caveat worth knowing before you file a bug: RetroAchievements identifies a dump, not a game, so a ROM from your own cart can miss even when the game plainly has a set. The "no achievements" message now prints the hash, so you can check the game's supported files and ask for yours to be added.

Also in this release:

  • The pause menu and OSD draw on the display engine's overlay layer on the A30, at the panel's own pixels instead of a 256x192 scratch the scaler blew up with the picture. The zip notice screen took the same path.
  • Controls, made legible and repairable. Face buttons are named by position and compass rather than by whose letters SDL used, with the same names accepted in the ini; the modifier, pen tap, stick and d-pad all have rows; a menu fallback layer sits over controls nothing is bound to, with an UNREACHABLE warning when even that cannot reach DS A or B. Fixes: Escape during a pad capture quit the emulator, the keyboard column could not be left on a device with no keyboard, a cancelled capture never repainted, and acting past the end of a list bound nothing silently.
  • A refreshed PGO profile.

Full changelog: v1.13.1...v1.14.0

Games run without BIOS or firmware dumps (built-in FreeBIOS and a generated firmware); a set dumped from your own DS unlocks the firmware boot and cycle-exact SWI timing. None is included.

Tarballs: linux-aarch64 / linux-armhf are dynamically linked and stripped -- the ones to package. The -static variants carry libstdc++/libgcc inside and keep their debug symbols, for a device whose runtime is older than the build's, or for a backtrace.

DSperate v1.13.1

Choose a tag to compare

@github-actions github-actions released this 09 Sep 01:29

DSperate v1.13.1

What's new: autoload. [emu] autoload = true (AUTOLOAD ON START in the settings menu, --autoload on the command line) resumes a game from its auto slot -- the state [emu] autosave writes on quit -- instead of booting it, so a handheld that only knows how to launch a ROM comes back where it left off. It covers a game named on the command line and one launched from the loader cart's picker alike, keyed to the game's own code; --load-state still wins over it, and an unreadable auto state is a warning, not a failure. Also in this release: a refreshed PGO profile.

Full changelog: v1.13.0...v1.13.1

Games run without BIOS or firmware dumps (built-in FreeBIOS and a generated firmware); a set dumped from your own DS unlocks the firmware boot and cycle-exact SWI timing. None is included.

Tarballs: linux-aarch64 / linux-armhf are dynamically linked and stripped -- the ones to package. The -static variants carry libstdc++/libgcc inside and keep their debug symbols, for a device whose runtime is older than the build's, or for a backtrace.

DSperate v1.13.0

Choose a tag to compare

@github-actions github-actions released this 08 Sep 23:41

DSperate v1.13.0

Full changelog: v1.12.0...v1.13.0

A release about changing things without a keyboard. The pause menu grows Options, Controls and DS Options pages, so the settings that mattered on a handheld -- the picture, the layout, the button bindings, the console's own name and birthday -- can be reached from the device rather than by editing an ini over ssh. Nothing in the emulation model moved.

Highlights

  • An Options menu for [emu] and [video]. OPTIONS leads to EMULATION, VISUAL FX and LAYOUT pages. The settings are a table, not code: each row carries its key, the label the player reads, its range or choices, what it takes to apply it, and a line saying what it does. The ini keeps its own vocabulary and the menu renames -- "mean" reads as DEFAULT, "linear" as BILINEAR, a 0..1 double as a percentage, a sentinel at the bottom of a range as AUTO or UNLIMITED -- and every row states the default the frontend actually uses rather than assuming the first choice. Rows switch off when they would do nothing, with a line saying why: FRAMESKIP MODE without frameskip, the grid and seams under BILINEAR, CHUNKY CELL without CHUNKY, the pip rows outside a pip layout. Changes apply live where there is a setter; anything that moves the picture about is noted when it is asked for and done on the way out, so the display is not reopened under a page the player is still reading. If the new settings will not open, the old ones go back. A change is written to the global ini, or to this game's file when the SAVE TO row says so. Toggling CPU OC flushes the translation cache, since the pricing is baked in when a block is compiled. Tier selection, window size, the JIT switch, the CPU interleave and real-time scheduling stay ini-only.
  • A Controls page that rebinds from the device itself. Two columns, keyboard and pad, with the shoulders switching between them and the page opening on the pad when one is plugged in. Rows are the twelve DS buttons and then the hotkey actions. A starts listening and the next thing pressed is bound -- taken before anything else looks at it, so rebinding the quit hotkey does not quit and rebinding A does not press A, and only from the device the column is for. Escape cancels, Y clears a binding, X puts a whole column back to the built-in layout. Bindings that shadow one another are shown on the page instead of only on stderr at startup. The page shows the config string itself, so it and the file cannot disagree.
  • A second control per hotkey. A handheld runs out of free buttons long before it runs out of actions. Every hotkey action now takes a second binding in its own column, spelled <action>.alt (hotkeys.pause.alt, padhotkeys.lid.alt). There is no default: unset means the action keeps the one control its column's layout gives it, so every existing config says what it always said. The two slots are one flat list, so the shadow and duplicate warnings, the Controls page's clash list and the "most specific pad binding wins" rule all see the second binding as they see the first. DS buttons keep their single binding.
  • A DS Options page, with a character editor. [user] -- what a game sees as the console's owner: nickname, message, favourite colour, birthday and language, with the colour, language and month named rather than numbered. With a real firmware dump the settings are read from the firmware image and written back to it, persisted to the sidecar beside the dump and never into the dump itself; the page says which of the two it is editing. Fields are written one at a time on purpose, so a dump's nickname holding characters these strings cannot represent is not turned into mojibake by an edit to something else; both settings copies go out with the same contents, the same incremented update counter and a recomputed CRC16. The two free-text fields get a character editor, since a handheld has no keyboard: up and down walk the current table, the shoulders change table, left and right move along the field, A commits and B abandons.
  • The menu is drawn on the output canvas. It used to be drawn into a 256x192 copy of one DS screen and upscaled with the picture, so on a single-screen device it was confined to that screen's slice of the panel, at DS resolution, with a hard ceiling of eight rows on the root page. It now draws in the output's own pixels over the whole window, at the same apparent size as before -- the gain is crispness, whole-panel centring, and room for pages to grow. The FPS counter, the state-slot toast and the screenshot flash followed it there, which deletes the PiP corner-dodging: drawn after the insets are down, nothing can bury them, so the counter stays in the corner it was asked for. The two tiers with no CPU-writable panel surface (the display engine and SDL_Renderer) keep the old route and put down the pixels they always did, verified by hashing 28 renders against the previous build.
  • Frameskip skips capture frames by default. frameskip_capture flips to true, with --no-frameskip-capture to turn it back off. A game that display-captures every frame -- Pokemon B/W, Golden Sun -- skipped nothing at all before, so asking for frameskip on those titles did nothing but print a line explaining why. Be clear about the trade: this makes an inexactness the default, and a capture-driven effect can visibly lag. The ini comment keeps its INEXACT marker.

Also

  • Two ordering bugs that made a config key silently do nothing: the per-game ini was merged after the display, audio and input were already set up, so a per-game [keys], [pad] or video.scale was ignored -- even though the layout hotkey writes video.layout into that very file; and emu.idle_skip reached the core through an environment variable set after the scheduler had read it.
  • CPU writes to a scanout dmabuf are bracketed with DMA_BUF_IOCTL_SYNC. The mainline CMA heap does its cache maintenance in that ioctl, and drawing the overlays on the canvas made them the last writes before commit -- on the RG DS the menu, the counter and the notices flickered while updating, and not in a screenshot. Costs nothing measurable: median frame 4.555/4.584 ms with against 4.558/4.559 without, both orders, p99 and over-budget count unchanged.
  • The letterbox outside the screens is cleared once per scanout buffer, so with three or four buffers in rotation each kept whatever overlay was last drawn into it and a counter that changed width left fragments behind. Each frame now says what it drew, and that area is cleared when its buffer next comes round.
  • Cheats look a zipped game up by the game's own header. load_for_rom() read the first 512 bytes of the path it was given, which for a .zip is the archive's local file header, so the game code was "PK\3\4" and every lookup missed -- silently, since a game that is not in the database is the common case.
  • Wrapped text and legends get the rows they need: the firmware's 26-character message is laid out over two lines as the console's own settings screen does it, and the legends that were cut off at handheld scales -- losing exactly the half that says how to leave the page -- split onto a second row.
  • Tests: the page stack, value stepping and sentinels, disabled-row walking, binding capture, the character editor and DS Options behind a firmware dump; every page drawn at ten canvas sizes from 160x128 to 1920x1080 against guard rows on all four sides, which caught a real hang in the pages that shrink to fit. Config had no tests at all despite the menu and the layout hotkeys rewriting single keys in it, and now covers parsing, the comment forms, single-key rewrites keeping the rest of the file, and the later-load-wins layering the per-game files rely on.
  • configs/default.ini and the example configs are regenerated, and the PGO profile is refreshed for this build.

Games run without BIOS or firmware dumps (built-in FreeBIOS and a generated firmware); a set dumped from your own DS unlocks the firmware boot and cycle-exact SWI timing. None is included.

Tarballs: linux-aarch64 / linux-armhf are dynamically linked and stripped -- the ones to package. The -static variants carry libstdc++/libgcc inside and keep their debug symbols, for a device whose runtime is older than the build's, or for a backtrace.

DSperate v1.12.0

Choose a tag to compare

@github-actions github-actions released this 08 Sep 14:00

DSperate v1.12.0

Full changelog: v1.11.1...v1.12.0

A performance release. The exact model is unchanged frame for frame on every recorded scene; the two speed-hack tiers gain a geometry worker thread and the whole build gets faster to translate, boot and present.

Highlights

  • A geometry worker thread under cpu_oc / timing_oc. The DS's 3D geometry engine (matrices, transform, clip, polygon writes) now runs on a thread of its own when either speed hack is on, a frame at most behind the emulation thread. Under timing_oc (no FIFO, DraStic's model) the command stream is queued as it arrives; under cpu_oc the emulation thread keeps the FIFO, its level and the stall, prices each command with the exact cycle model, and hands the work over, with the cull priced from the previous frame's kept ratio. A per-frame controller decides for each heavy/light frame pair whether the worker or a third raster band is the better use of the core, measuring rather than modelling (frontends report their present and pacing time so it sees emulation time only); DS_GX_THREAD=0/1/2 forces never / controller / always. Measured today on the RG DS (RK3566), SDL frontend, dual window, --cpu-oc --timing-oc, two rounds: Golden Sun Dark Dawn median 14.3 → 11.9 ms; Spirit Tracks p90 17.6 → 15.6 ms and frames over budget 40 % → 2-4 % of 900. With neither flag the build is flat against v1.11.1 on both, so nothing is paid when the hacks are off. A fidelity fix came with it: a SWAP_BUFFERS issued after the VBlank interrupt now takes effect at the next VBlank under timing_oc, as on hardware, which brings Etrian Odyssey from 368 to 599 of 600 frames identical to the exact model. Save states carry the worker's state; DS_PROFILE gains a join stage.
  • Recompiler translation is 25 % faster. The logical-immediate encoder tried every rotation and ran the whole search to say no; it is a popcount and a count-trailing-zeros now (checked identical over all 2^32 inputs), and the per-block instruction and fixup lists no longer allocate. Translation bursts are what the worst frames are made of: on the RG DS Dragon Ball Origins' first 40 frames translate in 29 ms instead of 38.7, its p99 12.2 → 11.3 ms and max 22.8 → 20.2 ms; Super Mario 64 DS max 12.8 → 11.1 ms. Bit-identical.
  • Boot in a third of the time. The timing tables at reset evaluated the ARM7 cost model 32 times for each of 131 K pages and filled a million ARM9 pages a byte at a time; both are done once per distinct input now. RG DS boot to the first frame 395 → 234 ms.
  • Triple buffering on the KMS scanout tier. The direct-to-panel path had two buffers and waited for the outstanding flip at every frame, so nothing overlapped. A third buffer lets a frame be drawn while a flip is pending. Golden Sun at default timing on the RG DS with sway stopped: 40 fps (10.5 ms of vsync wait a frame) → 57-58 fps. Presentation only; the Wayland dmabuf tier already had this shape.

Also

Every item below is bit-exact on the five recorded scenes and the Golden Sun state, and measured on the RG DS in both run orders.

  • 3D: polygon edges are set up on first use instead of for the whole list per raster instance (Golden Sun median 16.3 → 15.8 ms).
  • Recompiler: code-page lists keep each block's byte span beside its pointer, so self-modifying data on a hot ITCM page invalidates only the victims (Golden Sun −1.7 %); a per-page ARM9 refill table for indirect branches (Super Mario 64 DS −1.5 %); no-op GBA-slot retimes are skipped.
  • 2D: text rows two tiles at a time with vectorised map-row control, a scalar uniformity test instead of vector readbacks, whole-record pass-through in the compositor, and sprite planes cleared only where they are read (Mario & Luigi 2D −2.4 %).
  • VRAM: a 16/32-bit VRAMCNT store remaps once instead of per byte, and the write-trap toggle visits only mapped pages (Etrian Odyssey p99 13.2 → 12.7 ms).
  • ARM7: the SPI busy poll that Spirit Tracks spins on ~9 k times a frame is slept through to the transfer's ready time at the same guest instant (polls 9 k → 1.8 k; median 17.8 → 17.5 ms).
  • The trivial exact batch from the audit (2D, 3D and glue trims E2, D4, C4, E8, and one bug).
  • Instruments: DS_FRAME_SERIES in the SDL frontend, DS_JIT_DUMP writes the arena at exit so perf samples decode to instructions, DS_GX_SHAPE_LOG=1 prints each controller decision, and the frame breakdown shows the fastest fifth beside the typical and p99 columns.
  • configs/default.ini documents what cpu_oc and timing_oc now do; the PGO profile is refreshed for this build.

Games run without BIOS or firmware dumps (built-in FreeBIOS and a generated firmware); a set dumped from your own DS unlocks the firmware boot and cycle-exact SWI timing. None is included.

Tarballs: linux-aarch64 / linux-armhf are dynamically linked and stripped -- the ones to package. The -static variants carry libstdc++/libgcc inside and keep their debug symbols, for a device whose runtime is older than the build's, or for a backtrace.

DSperate v1.11.1

Choose a tag to compare

@github-actions github-actions released this 08 Sep 02:47

DSperate v1.11.1

Full changelog: v1.11.0...v1.11.1

A release-structure update only: the emulator is the same as v1.11.0, no new features and no fixes. See the v1.11.0 notes for what is in this build.

What changed

The release now ships two tarballs per architecture from the same build:

  • linux-aarch64 / linux-armhf (standard): dynamically linked and stripped. This is the one to package on a distro with a current libstdc++, such as ROCKNIX.
  • linux-aarch64-static / linux-armhf-static: libstdc++ and libgcc linked in and the debug symbols kept, for a device whose runtime is older than the build's, or when a backtrace is wanted. This is what earlier releases shipped as their only tarball.

Both are profile-guided builds from the committed profile, as in v1.11.0.

Games run without BIOS or firmware dumps (built-in FreeBIOS and a generated firmware); a set dumped from your own DS unlocks the firmware boot and cycle-exact SWI timing. None is included.

DSperate v1.11.0

Choose a tag to compare

@github-actions github-actions released this 08 Sep 02:08

DSperate v1.11.0

Full changelog: v1.10.0...v1.11.0

A BIOS/firmware set dumped from your own DS is no longer required to play; see the first highlight for what it still unlocks. None is included.

Highlights

  • Games run without any dumps. With no bios9/bios7 the built-in FreeBIOS (Gilead Kutnick's clean-room SWI table, BSD-3-Clause, the same NTR images melonDS and DraStic ship) fills the BIOS regions, and with no firmware a generated 256 KB image carries your [user] settings (nickname, message, birthday, colour, language). Both substitutes are announced at start. A configured path that names no file now falls back the same way, so the stock handheld ini works out of the box; a file that exists but is wrong, or one BIOS half without the other, is still an error naming the file. Direct boot copies the ROM's Nintendo logo into the ARM9 BIOS region, as games check it. What the real dumps still unlock: the firmware boot (no ROM / BootMenu.nds) needs the real pair, since FreeBIOS has no boot code and the generated firmware no DS menu; the generated firmware refuses the .ovr settings sidecar; and FreeBIOS's SWI routines are not cycle-matched to Nintendo's, so replays and frame hashes recorded against a real BIOS drift after a few dozen frames under it. Carts with a still-encrypted secure area need the real ARM7 BIOS for its KEY1 table; the usual decrypted dumps do not.
  • Profile-guided builds, now also from CI. The release binaries are built with a GCC profile trained on recorded scenes from seven games and committed under pgo/ with a manifest, so the public build needs no ROMs and refuses to configure if the compiler or flags no longer match the profile. On the RG DS (RK3566) it is the largest exact win of the campaign: Spirit Tracks median 16.8 → 15.6 ms and over-budget frames 285 → 158 of 570; Golden Sun 17.9 → 16.5 ms and 454 → 245; Super Mario 64 DS −9 %, Dragon Ball Origins −11 %. Frames are bit-identical.
  • Heavy/light frame pairs are absorbed. Spirit Tracks' intro and Golden Sun's title submit their 3D at 30 Hz, so an 18 ms frame follows a 12 ms one: on time over the pair, late on every other frame. The dmabuf scanout now keeps four buffers (two frames of run-ahead) and the wall-clock pacer keeps up to one frame of debt instead of resetting on a late frame. Delivered speed on the RG DS: Spirit Tracks intro 93-95 % → 97-98 %, Golden Sun title 93 → 97 %.
  • dominant_ratio = auto (the default) with dominant_threshold. The dominant screen takes the largest whole number of panel pixels per DS pixel that leaves the other screen at least a quarter of its size, and the other grows into the room left. On a 640x480 panel that is a 2x top screen over a 128x96 bottom, with the LCD grid and chunky cells exact on the screen that matters. A number still gives a fixed ratio.
  • LCD grid and chunky cells on small views. At exactly 2x the grid seams land on every other DS pixel boundary with a lit 3x3 cell between, instead of dimming three pixels in four into a flat darkening. A dimmed grid now applies from 1x on the PiP inset and the dominant layouts' secondary (the black grid keeps its 2x rule), and the chunky cell is chosen once, on the largest view, and matched in DS pixels on the others rather than coming out coarsest on the smallest screen.

Fixes

  • A crash in the 3D rasteriser under load: the finalised polygon list was rewritten while a band worker was still starting on the previous frame's list. The sorted list is now kept per polygon bank and the renderer latches it at dispatch. The same window made exact-mode output nondeterministic from run to run on Etrian Odyssey; it is bit-stable now.
  • Loading a state saved by a session with a running RTC into one without refused (an armed clock event with no handler); the clock event is now cancelled on load.

Also

  • Build hardening (_FORTIFY_SOURCE, -fstack-protector) is off by default: it put __memcpy_chk inside the 2D fetch loops, and turning it off measured Etrian Odyssey median 3.65 → 3.02 ms with no change elsewhere. DSPERATE_HARDEN=ON restores it; DSPERATE_TUNE takes -mcpu/-mtune flags (all measured flat on the RK3566, so it stays empty).
  • Main-thread trims from the performance audit, each exact: jump-table I/O dispatch, VRAM remaps applied as chunked diffs with the raster joined only when texture views move, the recompiler's GXFIFO stores on the geometry fast path, idle SPU channels skipped before the mix.
  • A thread that would block waiting for a raster band draws an unclaimed band itself instead; neutral on the RG DS, kept for hosts where the raster is the wait.
  • DS_FPS prints the scanout wait, the pacing time and the unaccounted remainder per window; DS_FRAME_SERIES writes per-frame timings for the headless build; tools/ab_flags_device.sh and docs/perf-audit-2026-09-07.md document the device measurements behind the above.

DSperate v1.10.0

Choose a tag to compare

@github-actions github-actions released this 06 Sep 22:43

DSperate v1.10.0

Full changelog: v1.9.1...v1.10.0

Requires a BIOS/firmware set dumped from your own DS; none is included.

Highlights

  • Integer scaling: --integer-scale under|over ([video] integer_scale). A whole number of panel pixels per DS pixel on the full-size screens -- under takes the largest scale that fits and letterboxes, over the smallest that covers and crops. The crop is centred, so a stacked or side-by-side pair keeps the edge between its screens, and in dual window the top screen keeps its bottom row and the bottom screen its top. The PiP inset and the dominant layouts' secondary keep their ratio to the screen they belong to. It works on every tier, the A30 display engine included, where over is a source-window crop. At a whole scale the LCD grid and the chunky cells come out exact: one seam or one cell per DS pixel, evenly.
  • A screenshot now flashes white. Blended over both screens the frame after the picture is written and fading over 12 frames, so you can see the hotkey landed. It is drawn into the presented buffers only, never into the frame the PNG comes from.
  • The overlays say what happened. The slot field shows STATE N SAVED / STATE N LOADED after a state hotkey, not just the slot digit, and the FPS field shows FF while fast-forwarding (FF 120 when the counter is on too). The 3x5 OSD font gained capitals and spaces for it.

Fixes

  • --chunky game.nds booted the firmware. A flag whose value is optional took the next word unless it was another option, so the ROM was read as the chunky mode. The command line is now pre-scanned for the game (a .nds or .zip, or a path to an existing file) and an optional-value flag never takes that word.
  • [cart] cheats accepts a directly named usrcheat.dat, not only a directory holding one.

Also

  • --pip-alpha X sets the inset's resting opacity from the command line, like the other layout settings.