From 36f2b9f63cc74bab4f8a46cff2c57283c8ca08d5 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 1 Oct 2026 10:29:37 +0200 Subject: [PATCH] vscode: removed hex editor integration for now --- extension/CHANGELOG.md | 2 +- extension/README.md | 14 ++++++------- extension/docs/emulator.md | 4 ---- extension/notes/PLAN.md | 13 ++++++------- src/tools/dapsession.ts | 39 ++++++------------------------------- test/unit/testdapsession.ts | 19 +++--------------- 6 files changed, 23 insertions(+), 68 deletions(-) diff --git a/extension/CHANGELOG.md b/extension/CHANGELOG.md index b4a5918b..c02a270f 100644 --- a/extension/CHANGELOG.md +++ b/extension/CHANGELOG.md @@ -18,7 +18,7 @@ First preview. a timeline to scrub through the recording while paused. - Debug views: registers, the machine's internal state (the IDE's Debug Tree), symbols, a call stack walked from return addresses, and - disassembly. Memory opens in VS Code's Hex Editor. + disassembly. - `.s`, `.asm`, `.inc` and `.a` files in a project get the highlighting for the platform's CPU. `.acme` files get 6502 highlighting. - An `8bws` command in the integrated terminal builds and runs programs diff --git a/extension/README.md b/extension/README.md index 05330cf8..166b6430 100644 --- a/extension/README.md +++ b/extension/README.md @@ -4,11 +4,12 @@ The original online 8-bit IDE, now in VS Code! -Write programs for classic consoles and computers, then compile and play -them without leaving VS Code. - -This extension bundles the assemblers, -compilers, and emulators from [8bitworkshop.com](https://8bitworkshop.com). +Features: +- Built-in emulators, compilers, and libraries from [8bitworkshop.com](https://8bitworkshop.com). +- Compile as you type +- Single-step debugging with breakpoints, disassembly, call stack, symbols +- Sample code for each platform +- Syntax highlighting for C and assembly Supported platforms include 6502, Z80, and 6809 CPUs: @@ -71,9 +72,8 @@ Your local user settings: | `8bitworkshop.autoBuild` | `onType` (default), `onSave`, or `off` | | `8bitworkshop.reloadOnBuild` | `always` (default), `onSave`, or `never` | | `8bitworkshop.exportRom` | Write each successful build's ROM to the export folder | -| `8bitworkshop.toolchainPath` | Use a local 8bitworkshop checkout instead of the included toolchains | -| `8bitworkshop.assetUrl` | Server or local directory to download toolchains from, tried before the default servers | | `8bitworkshop.terminalCommand` | Add the `8bws` command to the integrated terminal (off by default) | +| `8bitworkshop.toolchainPath` | Use a local 8bitworkshop checkout instead of the included toolchains | ## Commands diff --git a/extension/docs/emulator.md b/extension/docs/emulator.md index 53f5f6af..a17b1631 100644 --- a/extension/docs/emulator.md +++ b/extension/docs/emulator.md @@ -73,10 +73,6 @@ In a folder with a launch configuration, F5 does the same, and symbol's address and the byte there). **Watch** and hovers take the same expressions as conditions: registers, symbols, `[addr]` for a byte of memory, `#mem16[addr]` for a word. -- **Memory:** the binary-data icon next to a register or symbol opens - memory at that address in the Hex Editor (VS Code offers to install - it). On most platforms you can edit it there; VCS and MAME-based - platforms are read-only. - **Call Stack** shows the routines that called the current one, found from return addresses on the stack. A caller marked `?` (dimmed) is a guess: its call went through a pointer or a jump table. diff --git a/extension/notes/PLAN.md b/extension/notes/PLAN.md index 303051b1..38d80b91 100644 --- a/extension/notes/PLAN.md +++ b/extension/notes/PLAN.md @@ -1845,7 +1845,7 @@ runs has to be a webview too. | CPU registers | Variables → "Registers" scope (done) | | Debug Tree (`getDebugTree()`) | Variables → "Machine" scope (done) | | Symbols | Variables → "Symbols" scope (done) | -| Memory (hex) | "View Binary Data" on a variable → Hex Editor, via `readMemory`/`writeMemory` (done) | +| Memory (hex) | own webview editor (not started; the DAP Hex Editor integration was removed: it opened at the variable, not `$0`) | | Debug info panel (`getDebugInfo()` text) | webview ("Machine" view) | | Memory with symbols, memory map | webviews | | Probe log, scanline I/O, heatmaps, stack map, frame calls | webviews | @@ -1873,17 +1873,16 @@ runs has to be a webview too. expensive (collapsed), and is shown only when the platform has `getDebugTree` (`DebugCapabilities.tree`). - **Symbols scope.** Every build symbol by name, with its address and the - byte there, and a `memoryReference` so "View Binary Data" opens it. - Collapsed by default. + byte there. Collapsed by default. - **Memory writes.** `EmuCore.write()` goes through the machine's bus, so writing an I/O address does what a store there does. It restarts the timeline (the past can't be replayed into the edited state). It needs a `Machine` with `write()`, so VCS and the MAME platforms can't write (NES's `JSNESMachine` writes RAM or the mapper directly, skipping - `cpu.write()`'s bus cycle); `DebugCapabilities.write` says so and the adapter turns on - `supportsWriteMemoryRequest` only when it can. After a write the adapter - sends `invalidated` (variables); after every stop it sends a `memory` - event for the whole space so the Hex Editor reads again. + `cpu.write()`'s bus cycle); `DebugCapabilities.write` says so. The DAP `readMemory`/`writeMemory` + requests, `memoryReference`s and `memory` events were removed (the Hex + Editor opened at the variable, not `$0`); `DebugService` still has + `readMemory`/`writeMemory` for a future editor of our own. - Tests: `test/unit/testdebugtree.ts`, and a case in `test/unit/testdapsession.ts`. diff --git a/src/tools/dapsession.ts b/src/tools/dapsession.ts index e0ebf5bf..98d5e756 100644 --- a/src/tools/dapsession.ts +++ b/src/tools/dapsession.ts @@ -12,8 +12,8 @@ // level at a time, by path), and the build's symbols with the byte at each. import { - Breakpoint as DapBreakpoint, CapabilitiesEvent, InitializedEvent, InvalidatedEvent, LoggingDebugSession, - MemoryEvent, OutputEvent, Scope, Source, StackFrame, StoppedEvent, TerminatedEvent, Thread, + Breakpoint as DapBreakpoint, CapabilitiesEvent, InitializedEvent, LoggingDebugSession, + OutputEvent, Scope, Source, StackFrame, StoppedEvent, TerminatedEvent, Thread, } from '@vscode/debugadapter'; import { DebugProtocol } from '@vscode/debugprotocol'; import * as path from 'path'; @@ -91,8 +91,6 @@ export class EmuDebugSession extends LoggingDebugSession { /** disassembly listings by sourceReference, and each one's reference by its text */ private listings: Listing[] = []; private listingRefs = new Map(); - private clientMemoryEvents = false; - private clientInvalidatedEvents = false; constructor(private backend: DebugBackend) { super(); @@ -104,21 +102,17 @@ export class EmuDebugSession extends LoggingDebugSession { } protected initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void { - this.clientMemoryEvents = !!args.supportsMemoryEvent; - this.clientInvalidatedEvents = !!args.supportsInvalidatedEvent; response.body = { supportsConfigurationDoneRequest: true, supportsConditionalBreakpoints: true, supportsFunctionBreakpoints: true, supportsInstructionBreakpoints: true, supportsSteppingGranularity: true, - supportsReadMemoryRequest: true, supportsDisassembleRequest: true, supportsEvaluateForHovers: true, supportsTerminateRequest: true, // until launch says whether this platform can supportsStepBack: false, - supportsWriteMemoryRequest: false, }; this.sendResponse(response); } @@ -129,8 +123,8 @@ export class EmuDebugSession extends LoggingDebugSession { this.root = r.root; this.stopOnEntry = !!args.stopOnEntry; this.hasTree = r.capabilities.tree; - if (r.capabilities.rewind || r.capabilities.write) { - this.sendEvent(new CapabilitiesEvent({ supportsStepBack: r.capabilities.rewind, supportsWriteMemoryRequest: r.capabilities.write })); + if (r.capabilities.rewind) { + this.sendEvent(new CapabilitiesEvent({ supportsStepBack: r.capabilities.rewind })); } if (!r.capabilities.step) { this.sendEvent(new OutputEvent(`This platform stops only between frames: stepping and breakpoints are frame by frame.\n`, 'console')); @@ -255,8 +249,6 @@ export class EmuDebugSession extends LoggingDebugSession { else if (e.message) ev.body.description = e.message; if (e.breakpoints) ev.body.hitBreakpointIds = e.breakpoints; this.sendEvent(ev); - // anything showing memory reads it again - if (this.clientMemoryEvents) this.sendEvent(new MemoryEvent(addressRef(0), 0, 0x10000)); } //// where it is @@ -370,12 +362,12 @@ export class EmuDebugSession extends LoggingDebugSession { private async variables(ref: number): Promise { if (ref === REGISTERS_REF) { const regs = await this.backend.registers(); - return regs.map(r => ({ name: r.name, value: r.text, variablesReference: 0, memoryReference: addressRef(r.value) })); + return regs.map(r => ({ name: r.name, value: r.text, variablesReference: 0 })); } if (ref === SYMBOLS_REF) { const syms = await this.backend.symbols(); return syms.map(s => ({ - name: s.name, value: `$${hex(s.addr, 4)}: $${hex(s.value, 2)}`, variablesReference: 0, memoryReference: addressRef(s.addr), + name: s.name, value: `$${hex(s.addr, 4)}: $${hex(s.value, 2)}`, variablesReference: 0, })); } const path = this.treeRefs[ref - FIRST_TREE_REF]; @@ -397,24 +389,6 @@ export class EmuDebugSession extends LoggingDebugSession { return ref; } - protected async readMemoryRequest(response: DebugProtocol.ReadMemoryResponse, args: DebugProtocol.ReadMemoryArguments) { - await this.run(response, async () => { - const addr = parseAddress(args.memoryReference) + (args.offset || 0); - const bytes = await this.backend.readMemory(addr, Math.min(args.count, 0x10000)); - response.body = { address: addressRef(addr), data: Buffer.from(bytes).toString('base64') }; - }); - } - - protected async writeMemoryRequest(response: DebugProtocol.WriteMemoryResponse, args: DebugProtocol.WriteMemoryArguments) { - await this.run(response, async () => { - const addr = parseAddress(args.memoryReference) + (args.offset || 0); - const bytes = [...Buffer.from(args.data, 'base64')]; - response.body = { bytesWritten: await this.backend.writeMemory(addr, bytes) }; - // the symbols and the machine may show what changed - if (this.clientInvalidatedEvents) this.sendEvent(new InvalidatedEvent(['variables'])); - }); - } - protected async disassembleRequest(response: DebugProtocol.DisassembleResponse, args: DebugProtocol.DisassembleArguments) { await this.run(response, async () => { const addr = parseAddress(args.memoryReference) + (args.offset || 0); @@ -437,7 +411,6 @@ export class EmuDebugSession extends LoggingDebugSession { await this.run(response, async () => { const r = await this.backend.evaluate(args.expression, args.context || 'repl'); response.body = { result: r.result, variablesReference: 0 }; - if (r.value != null) response.body.memoryReference = addressRef(r.value); if (r.moved) this.stopped({ ...(await this.backend.location()), reason: 'goto' }); }); } diff --git a/test/unit/testdapsession.ts b/test/unit/testdapsession.ts index 570473d9..7e966c4a 100644 --- a/test/unit/testdapsession.ts +++ b/test/unit/testdapsession.ts @@ -22,13 +22,12 @@ async function launch(args: any = {}): Promise { return { target, root: ROOT, debugInfo: { listings: build.listings, symbols: build.symbolmap, mainPath: 'game2.c' } }; }); const c = new DapClient(new EmuDebugSession(backend)); - const caps = await c.request('initialize', { adapterID: '8bitworkshop', linesStartAt1: true, columnsStartAt1: true, pathFormat: 'path', supportsMemoryEvent: true, supportsInvalidatedEvent: true }); + const caps = await c.request('initialize', { adapterID: '8bitworkshop', linesStartAt1: true, columnsStartAt1: true, pathFormat: 'path' }); assert.ok(caps.supportsDisassembleRequest); await c.request('launch', { program: 'game2.c', ...args }); await c.event('initialized'); const { capabilities } = await c.event('capabilities'); assert.strictEqual(capabilities.supportsStepBack, true); - assert.strictEqual(capabilities.supportsWriteMemoryRequest, true); return c; } @@ -88,21 +87,17 @@ describe('Debug adapter', function () { const reg = variables.find((v: any) => v.name === 'PC'); assert.strictEqual(parseInt(reg.value.slice(1), 16), pc); - const mem = await c.request('readMemory', { memoryReference: frame.instructionPointerReference, count: 4 }); - assert.strictEqual(Buffer.from(mem.data, 'base64').length, 4); - const { instructions } = await c.request('disassemble', { memoryReference: frame.instructionPointerReference, instructionOffset: -3, instructionCount: 6 }); assert.strictEqual(instructions.length, 6); assert.strictEqual(parseInt(instructions[3].address, 16), pc); assert.strictEqual(instructions[3].symbol, '_draw_char'); }); - it('shows the machine tree and symbols, and writes memory', async function () { + it('shows the machine tree and symbols', async function () { const c = await launch(); await c.request('setFunctionBreakpoints', { breakpoints: [{ name: 'draw_char' }] }); await c.request('configurationDone'); await c.event('stopped'); - await c.event('memory'); const frame = await c.where(); const { scopes } = await c.request('scopes', { frameId: frame.id }); assert.deepStrictEqual(scopes.map((s: any) => s.name), ['Registers', 'Machine', 'Symbols']); @@ -113,17 +108,9 @@ describe('Debug adapter', function () { const stateVars = await c.request('variables', { variablesReference: state.variablesReference }); assert.ok(stateVars.variables.find((v: any) => v.name === 'c'), 'CPU state under state'); - const addr = build.symbolmap['_draw_char']; const syms = await c.request('variables', { variablesReference: scopes[2].variablesReference }); const sym = syms.variables.find((v: any) => v.name === '_draw_char'); - assert.strictEqual(parseInt(sym.memoryReference, 16), addr); - - // video RAM - const { bytesWritten } = await c.request('writeMemory', { memoryReference: '0x2400', data: Buffer.from([0x12, 0x34]).toString('base64') }); - assert.strictEqual(bytesWritten, 2); - await c.event('invalidated'); - const mem = await c.request('readMemory', { memoryReference: '0x2400', count: 2 }); - assert.deepStrictEqual([...Buffer.from(mem.data, 'base64')], [0x12, 0x34]); + assert.ok(sym.value.includes('$' + build.symbolmap['_draw_char'].toString(16).padStart(4, '0').toUpperCase()), 'symbol shows its address'); }); it('runs run-script commands in the Debug Console, and expressions in watches', async function () {