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 () {