diff --git a/extension/README.md b/extension/README.md index 7a0f0c90..7494c765 100644 --- a/extension/README.md +++ b/extension/README.md @@ -86,7 +86,7 @@ also appear in the editor title bar, the Explorer, or **Project Options**. | **Change Main File...** | Pick the main file from the workspace | | **Change Platform...** | Build the project for another platform | | **Detect Projects** | Scan the folder for projects and platforms | -| **Add Launch Configuration** | Write a run configuration to `.vscode/launch.json` | +| **Add Launch Configuration** | Write a run configuration to `.vscode/launch.json`; F5 debugs it, with step back | | **Project Options** | Open the project menu | | **Build** | Build without running | | **Run** | Build and run in the emulator | diff --git a/extension/docs/QUICKSTART.md b/extension/docs/QUICKSTART.md index 6b0dcbff..7cff79ab 100644 --- a/extension/docs/QUICKSTART.md +++ b/extension/docs/QUICKSTART.md @@ -88,7 +88,7 @@ back, click the status bar and choose **Run Main File**. To keep a list of programs to switch between, click the status bar and choose **Add Launch Configuration**. The program then appears in the -**Run and Debug** view's list, and F5 runs the one you chose. +**Run and Debug** view's list, and F5 debugs the one you chose. **A folder of small programs**, like a set of exercises, has no main file. **Run** runs the program in the editor you're looking at. The diff --git a/extension/docs/emulator.md b/extension/docs/emulator.md index 43080af4..ca3e8b14 100644 --- a/extension/docs/emulator.md +++ b/extension/docs/emulator.md @@ -43,6 +43,49 @@ A build that fails leaves the last good version running. Some programs end, such as a BASIC program. Then the panel says **Halted** and the reason. Click **Run** to start again. +## Go back in time + +The emulator records as it runs. While it's paused, a timeline appears +under the screen. Drag it to see any recorded frame. **Resume** runs on +from the frame you're looking at. Pressing a key there starts a new +future and drops the one that was recorded. + +The recording keeps about the last 50 seconds. The NES saves bigger +states, so it keeps about 10 seconds. + +## Debug + +Press F5 to build the program and debug it. Press +Ctrl+F5 (**Run Without Debugging**) to just run it. + +- **Breakpoints:** click left of a line number in C or assembly. You can + also add a breakpoint on a function by name (**+** in the Breakpoints + view), or on an address in the Disassembly view. A condition such as + `A == 3` or `[score] > 100` makes the breakpoint stop only when it's true. +- **Stepping:** Step Over, Step Into and Step Out go by source line. In the + Disassembly view they go by instruction. +- **Step Back** and **Reverse Continue** run backwards through the + recording, to the previous line or the previous breakpoint hit. +- **Variables** shows the CPU registers. **Watch** and hovers take the + same expressions as conditions: registers, symbols, `[addr]` for a byte + of memory, `#mem16[addr]` for a word. +- The **Debug Console** takes the commands of `8bws run -e`, such as + `mem $0200 32`, `hist 10`, `back 5` and `rbreak main`. Type `help` to + list them. + +A launch configuration can also set: + +| Setting | What it does | +|---|---| +| `stopOnEntry` | Stop before the first instruction instead of running | +| `script` | Debug Console commands to run once the program loads, such as `"break main"` | + +While you debug, a rebuild doesn't restart the program. Press +F5 again to debug the new build. + +Some platforms, such as the Atari 2600 and Verilog, can only stop +between frames. There, breakpoints and steps work a frame at a time. + ## Sound The emulator plays sound through the panel. Audio starts on your first click diff --git a/extension/docs/projects.md b/extension/docs/projects.md index 90e00c4f..07ef1304 100644 --- a/extension/docs/projects.md +++ b/extension/docs/projects.md @@ -78,7 +78,7 @@ To switch between programs with F5: 2. Click the status bar and choose **Add Launch Configuration**. The program appears in the **Run and Debug** view's list. Choose it there -and press F5 to build and run it. A launch configuration can +and press F5 to build and debug it. A launch configuration can also name another platform, so one program can run on `c64` and `vic20`: ```json @@ -91,7 +91,8 @@ also name another platform, so one program can run on `c64` and `vic20`: } ``` -F5 runs the program but doesn't debug it yet. +F5 builds and debugs the program; Ctrl+F5 +runs it without the debugger. See [Debug](emulator.md#debug). ## Where your choices are saved diff --git a/extension/notes/PLAN.md b/extension/notes/PLAN.md index d554629e..cf2dc490 100644 --- a/extension/notes/PLAN.md +++ b/extension/notes/PLAN.md @@ -1647,8 +1647,11 @@ debugger shouldn't become a third. `DebugService` and answers `debug(method, ...)`; its frame loop runs through the controller, and every stop is a `stopped` event. The adapter is `src/tools/dapsession.ts`, shared with `8bws dap`; - `extension/src/debugbackend.ts` is its RPC backend. Not yet wired into - `extension.ts`.)* The Debug Adapter (next + `extension/src/debugbackend.ts` is its RPC backend. F5 debugs; Ctrl+F5 + (`noDebug`) runs as before. A debug launch builds, starts the worker + paused, and sends the listings with `setBuild`. Rebuilds don't reload + under the debugger; a plain Run ends the session. The panel's timeline + slider (shown while stopped) calls `seekFrame`.)* The Debug Adapter (next section) runs in the extension host and translates DAP to RPC. DAP has reverse debugging built in: report `supportsStepBack`, and handle `stepBack` and `reverseContinue`. Scrubbing (`seekFrame`) is a timeline diff --git a/extension/package.json b/extension/package.json index 16d461b0..7c1ab897 100644 --- a/extension/package.json +++ b/extension/package.json @@ -434,6 +434,13 @@ "contents": "Make a program for a classic console or computer.\n[New 8bitworkshop Project](command:8bitworkshop.newProject)\n[Open 8bitworkshop Example](command:8bitworkshop.openExample)" } ], + "breakpoints": [ + { "language": "c" }, + { "language": "cpp" }, + { "language": "8bws-6502" }, + { "language": "8bws-z80" }, + { "language": "8bws-6809" } + ], "debuggers": [ { "type": "8bitworkshop", @@ -453,6 +460,15 @@ "tool": { "type": "string", "description": "Tool for the main file, if not the usual one." + }, + "stopOnEntry": { + "type": "boolean", + "description": "Stop before the program's first instruction instead of running.", + "default": false + }, + "script": { + "type": "string", + "description": "Commands to run once the program is loaded, as in the Debug Console (e.g. \"break main; key SPACE\")." } } } diff --git a/extension/src/emulatorpanel.ts b/extension/src/emulatorpanel.ts index d0262b65..02a64921 100644 --- a/extension/src/emulatorpanel.ts +++ b/extension/src/emulatorpanel.ts @@ -11,6 +11,8 @@ export interface PanelEvents { onKey(key: number, code: number, flags: number): void; /** the user showed or hid the controls bar */ onControlsVisible(visible: boolean): void; + /** the user dragged the timeline to a recorded frame */ + onSeek?(frame: number): void; onVisible(visible: boolean): void; onDispose(): void; } @@ -29,6 +31,7 @@ export class EmulatorPanel { if (msg.type === 'key') this.events.onKey(msg.key, msg.code, msg.flags); else if (msg.type === 'frameDone') this.frameInFlight = false; else if (msg.type === 'controlsVisible') this.events.onControlsVisible(!!msg.visible); + else if (msg.type === 'seek') this.events.onSeek?.(msg.frame); }); this.panel.onDidChangeViewState(e => this.events.onVisible(e.webviewPanel.visible)); this.panel.onDidDispose(() => this.events.onDispose()); @@ -102,11 +105,19 @@ function getHtml(controlsVisible: boolean) { #show { position: absolute; right: 6px; bottom: 4px; display: none; cursor: pointer; font: 11px var(--vscode-font-family); color: #ccc; background: rgba(0,0,0,0.5); border: 0; border-radius: 3px; padding: 2px 6px; } body.hascontrols:not(.controls) #show { display: block; } + /* the timeline: shown while stopped, to scrub through the recording */ + #timeline { position: absolute; left: 0; right: 0; display: none; align-items: center; gap: 8px; + padding: 2px 8px; box-sizing: border-box; background: rgba(0,0,0,0.7); + font: 11px var(--vscode-font-family); color: #ccc; } + body.stopped.haslog #timeline { display: flex; } + #scrub { flex: 1; accent-color: var(--vscode-focusBorder); } + #when { white-space: nowrap; font-variant-numeric: tabular-nums; }
+