From 7fb39ccc64ea00e2876cbb634eb1c2fc8b0376cc Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Tue, 22 Sep 2026 09:47:53 +0200 Subject: [PATCH] Common: move the step refresh into the bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- packages/app-android/app/views/main.ts | 3 --- packages/app-gnome/src/views/main.window.ts | 6 ------ packages/app-web/src/app/main-window.ts | 3 --- .../src/controller/game-console-event-bridge.ts | 16 ++++++++++------ 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/packages/app-android/app/views/main.ts b/packages/app-android/app/views/main.ts index 179c036b..3301bd5f 100644 --- a/packages/app-android/app/views/main.ts +++ b/packages/app-android/app/views/main.ts @@ -128,9 +128,6 @@ export class MainController implements MainView { showNotification: (key) => { notificationService.showNotification({ title: _(NOTIFICATION_TITLES[key] || key), timeout: 2 }); }, - updateDebugInfo: (simulator) => { - debuggerView.updateDebugInfo(simulator); - }, }); this.mainBridge = new MainEventBridge({ diff --git a/packages/app-gnome/src/views/main.window.ts b/packages/app-gnome/src/views/main.window.ts index a1bd83f5..1eafff68 100644 --- a/packages/app-gnome/src/views/main.window.ts +++ b/packages/app-gnome/src/views/main.window.ts @@ -712,12 +712,6 @@ export class MainWindow extends Adw.ApplicationWindow implements MainView { timeout: 2, }); }, - updateDebugInfo: (simulator) => { - // If stepper is enabled, update full debugger; otherwise just debug info - if (this._gameConsole.simulator.stepperEnabled) { - this.updateDebugger(); - } - }, }); this.gameConsoleBridge.connect(); } diff --git a/packages/app-web/src/app/main-window.ts b/packages/app-web/src/app/main-window.ts index 9a5efd9a..df56208b 100644 --- a/packages/app-web/src/app/main-window.ts +++ b/packages/app-web/src/app/main-window.ts @@ -400,9 +400,6 @@ export class MainWindow implements MainView { }, updateUiState: () => this.updateRunActions(this.simulator.state), showNotification: (key) => this.showToast(NOTIFICATION_TITLES[key] ?? key), - updateDebugInfo: () => { - if (this.simulator.stepperEnabled) this.updateDebugger(); - }, }); this.bridge.connect(); diff --git a/packages/common-ui/src/controller/game-console-event-bridge.ts b/packages/common-ui/src/controller/game-console-event-bridge.ts index 4a69fdc0..e3ea8a01 100644 --- a/packages/common-ui/src/controller/game-console-event-bridge.ts +++ b/packages/common-ui/src/controller/game-console-event-bridge.ts @@ -1,5 +1,5 @@ import { SimulatorState, _ } from "@learn6502/core"; -import type { Assembler, Simulator } from "@learn6502/core"; +import type { Assembler } from "@learn6502/core"; import { gameConsoleController } from "./game-console-controller.ts"; import { debuggerController } from "./debugger-controller.ts"; @@ -27,9 +27,6 @@ export interface GameConsoleEventBridgeCallbacks { /** Show a notification with a translatable key. */ showNotification(key: string): void; - - /** Called when a step/multistep/goto occurs and debugger may need update. */ - updateDebugInfo(simulator: Simulator): void; } type EventUnsubscriber = () => void; @@ -125,8 +122,15 @@ export class GameConsoleEventBridge { if (signal.message) { this.callbacks.formatAndLog(signal.message, signal.params); } - if (signal.simulator) { - this.callbacks.updateDebugInfo(signal.simulator); + // "step" is not the Step button: Simulator.execute() emits it for every + // executed instruction, and a free run runs execute() 97 times per + // multiExecute() tick. Refreshing the debugger here is therefore only + // affordable while the stepper is on — multiExecute() then returns + // early, so one event is one press of Step, and the memory monitor has + // to be redrawn or the step leaves stale bytes on screen. A free run is + // covered by the "multistep" handler below, whose update is throttled. + if (signal.simulator.stepperEnabled) { + this.callbacks.updateDebugger(); } });