Skip to content

Common: move the step refresh into the bridge - #194

Merged
JumpLink merged 1 commit into
mainfrom
fix/step-refreshes-memory
Sep 22, 2026
Merged

JumpLink merged 1 commit into
mainfrom
fix/step-refreshes-memory

Conversation

@JumpLink

@JumpLink JumpLink commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner

The claim, and what it is worth

game-console-event-bridge.ts updates only the register panel on "step", never the memory
monitor — and since it is shared common-ui, that hits GNOME, Web and Android alike.

Half of that holds. The bridge really did answer "step" with updateDebugInfo() alone, and
that path ends in DebuggerController.updateDebugInfo(), which touches the debug-info widget
and nothing else. But the reach was wrong: two of the three ports had already re-implemented
the missing half in their own callback.

Port its updateDebugInfo callback memory on a single step
GNOME if (simulator.stepperEnabled) this.updateDebugger() redrawn
Web the same two lines redrawn
Android debuggerView.updateDebugInfo(simulator) not redrawn

Measured, not read: the web app built with gjsify build --app browser, driven in headless
Chrome — region Display Memory ($0200-$05FF), Stepping mode on, 24 × Step through the starter
program. On unmodified main the dump already changed on 6 of those 24 steps, which is
exactly the six STA $0200,X writes among them. So the claim is false for Web, false for GNOME
by the same two lines, and true only for Android.

Hex Monitor before and after, web app

Top row main at 7319f80d, bottom row this PR; left after Assemble, right after 24 x Step.
Method, and why the bytes differ between the rows: pr-194/README.md on the assets branch.

Why "step" could not simply refresh everything

Simulator.execute() dispatches "step" for every executed instruction, not just for the
Step button, and a free run calls execute() 97 times per multiExecute() tick. An
unconditional debugger refresh there would redraw the hex monitor ~97 times a tick. That is
what the stepperEnabled guard buys: with the stepper on, multiExecute() returns early, so
one event is one press of Step. A free run stays on the throttled "multistep" path.

The change

The guard moves into the shared bridge, so the rule is stated once instead of twice plus a gap:

  • GameConsoleEventBridge decides on "step": stepper on → updateDebugger().
  • updateDebugInfo leaves GameConsoleEventBridgeCallbacks and all three ports — nothing
    called it any more.
  • Android gains the memory refresh and loses an unthrottled per-instruction register redraw
    during a free run. GNOME and Web keep the behaviour they had.

No new cost class: updateDebugger() runs through DebuggerController.update, throttled to
349 ms, and every port already drives that same path from assemble-success, start, stop,
reset, goto and multistep.

What is not verified

  • Not seen on a device: GNOME and Android. Both type-check, and the edit is the same
    deletion in all three, but no GUI was driven for them.
  • Android could not be run at all today: a clean build of main (e855872e) in this worktree
    crashes at launch on a Medium_Phone_API_36 emulator — TypeError: Cannot read properties of undefined (reading 'split') in clampChildClassName ← AdwClamp._allocate ← set_child,
    from buildEditorScreen, where new ScrollView() reaches Adw.Clamp with no className.
    It reproduces on unmodified main, so it is not this change; an APK built elsewhere from the
    pre-Android: drop Material-era leftovers #192 tree launches on the same emulator, and I did not isolate the difference further
    (@gjsify/adwaita-nativescript is 0.51.1 and adw-clamp.js byte-identical in both trees).

gjsify format --check, gjsify lint, and check for core, common-ui, app-web, app-gnome and
app-android all pass; @learn6502/core and the web app build.

🤖 Generated with Claude Code

The shared GameConsoleEventBridge answered "step" with updateDebugInfo()
alone, which ends at DebuggerController.updateDebugInfo() and touches the
debug-info widget only — the memory monitor was never redrawn.

Two of the three ports had quietly compensated for that in their own
callback: app-gnome and app-web both ran
`if (simulator.stepperEnabled) this.updateDebugger()`, so on both the
monitor does follow a single step (measured on the web app: 6 of 24 steps
change the dump, exactly the six STA writes). app-android had no such
guard, so there a single step refreshed the registers and nothing else.

The bridge could not simply refresh everything, and that is why the guard
existed: Simulator.execute() dispatches "step" for every executed
instruction, and a free run calls execute() 97 times per multiExecute()
tick. So the guard moves into the bridge instead of being copied a third
time — with the stepper on, multiExecute() returns early and one event is
one press of Step; a free run stays on the throttled "multistep" path.

updateDebugInfo is therefore gone from GameConsoleEventBridgeCallbacks and
from all three ports, because nothing calls it any more. Android gains the
memory refresh and loses an unthrottled per-instruction register redraw
during a free run; GNOME and web behave exactly as before.
@JumpLink
JumpLink merged commit e7981b9 into main Sep 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant