mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-24 08:02:27 +00:00
vscode: removed hex editor integration for now
This commit is contained in:
1 parent
2d09792b90
commit
36f2b9f63c
6 files changed
+23
-68
No files matched your search
@@ -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
|
||||
|
||||
+7
-7
@@ -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
|
||||
|
||||
|
||||
@@ -73,10 +73,6 @@ In a folder with a launch configuration, <kbd>F5</kbd> 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.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
+6
-33
@@ -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<string, number>();
|
||||
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<DebugProtocol.Variable[]> {
|
||||
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' });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,13 +22,12 @@ async function launch(args: any = {}): Promise<DapClient> {
|
||||
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 () {
|
||||
|
||||
Reference in new issue
Block a user