mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-24 08:02:27 +00:00
nes: fixed for emuworker and new debug adapter
This commit is contained in:
1 parent
8bdd47cf57
commit
bb9bb9a2de
5 files changed
+82
-20
No files matched your search
@@ -233,6 +233,35 @@ describe('extension emuworker', function () {
|
||||
assert.equal(statuses[statuses.length - 1].state, 'paused');
|
||||
});
|
||||
|
||||
it('steps through C source on the NES, which has no Machine of its own', async function () {
|
||||
// jsnes stops between instructions through the trap its frame() takes
|
||||
var dir = path.join(ROOT, 'presets/nes');
|
||||
var mainText = fs.readFileSync(path.join(dir, 'hello.c'), 'utf-8');
|
||||
var read = async (rel: string) => {
|
||||
var p = path.join(dir, rel);
|
||||
return fs.existsSync(p) ? new Uint8Array(fs.readFileSync(p)) : null;
|
||||
};
|
||||
var nes = await new Builder(ROOT).build({
|
||||
platform: 'nes', mainPath: 'hello.c', mainText,
|
||||
files: new ProjectFileProvider(read, ROOT, 'nes'),
|
||||
});
|
||||
assert.deepEqual(nes.diagnostics, []);
|
||||
await rpc.call('start', 'nes', nes.output, nes.files, { paused: true });
|
||||
await debug('setBuild', { listings: nes.listings, symbols: nes.symbolmap, mainPath: 'hello.c', paths: nes.paths });
|
||||
var [bp] = await debug('setBreakpoints', [{ id: 1, type: 'source', file: 'hello.c', line: 19, enabled: true }]);
|
||||
assert.ok(bp.verified, bp.message);
|
||||
await debug('continue');
|
||||
assert.equal((await nextStop()).reason, 'breakpoint');
|
||||
assert.equal((await debug('location')).source.line, 19);
|
||||
await debug('step', 'over', 'line');
|
||||
assert.equal((await nextStop()).reason, 'step');
|
||||
assert.equal((await debug('location')).source.line, 20);
|
||||
await debug('stepBack', 'line');
|
||||
assert.equal((await nextStop()).reason, 'step');
|
||||
assert.equal((await debug('location')).source.line, 19);
|
||||
await debug('setBreakpoints', []);
|
||||
});
|
||||
|
||||
it('drives the debug adapter through the worker, as the extension does', async function () {
|
||||
var backend = new WorkerDebugBackend((method, ...args) => rpc.call('debug', method, ...args), async () => {
|
||||
await rpc.call('start', 'mw8080bw', built.output, built.files, { paused: true });
|
||||
|
||||
+1
-1
Submodule jsnes updated: a6a026b698...dd2fa697a0.
+42
-19
@@ -6,7 +6,7 @@ import { hex, byteArrayToString, replaceAll } from "../common/util";
|
||||
import { CodeAnalyzer_nes } from "../common/analysis";
|
||||
import { SampleAudio } from "../common/audio";
|
||||
import { ProbeRecorder } from "../common/probe";
|
||||
import { NullProbe, Probeable, ProbeAll } from "../common/devices";
|
||||
import { NullProbe, Probeable, ProbeAll, TrapCondition } from "../common/devices";
|
||||
import jsnes = require('../../jsnes');
|
||||
|
||||
// Lazy mousetrap require: mousetrap references `document` at module load time,
|
||||
@@ -77,11 +77,36 @@ const JSNES_KEYCODE_MAP = makeKeycodeMap([
|
||||
]);
|
||||
|
||||
/** Replace every typed array in `o` with a copy, breaking aliasing. */
|
||||
function copyTypedArrays(o: any) {
|
||||
if (!o) return;
|
||||
for (var k in o) {
|
||||
if (ArrayBuffer.isView(o[k])) o[k] = (o[k] as any).slice(0);
|
||||
/**
|
||||
* The JSNES core seen as a Machine, so the headless debugger (EmuCore,
|
||||
* MachineCore) can stop between instructions and step. JSNES runs whole
|
||||
* instructions, so every stop is stable.
|
||||
*/
|
||||
class JSNESMachine {
|
||||
cpuCyclesPerLine = 114; // width of the probe scope
|
||||
cpu = {
|
||||
getPC: () => this.p.getPC(),
|
||||
getSP: () => this.p.getSP(),
|
||||
isStable: () => true,
|
||||
};
|
||||
|
||||
constructor(readonly p: JSNESPlatform) { }
|
||||
|
||||
advanceFrame(trap: TrapCondition): number {
|
||||
var steps = 0;
|
||||
// frame() asks the trap before each instruction, so count those
|
||||
this.p.nes.frame(() => {
|
||||
if (trap && trap()) return true;
|
||||
steps++;
|
||||
return false;
|
||||
});
|
||||
return steps;
|
||||
}
|
||||
saveState() { return this.p.saveState(); }
|
||||
loadState(s) { this.p.loadState(s); }
|
||||
read(a: number) { return this.p.readAddress(a); }
|
||||
readConst(a: number) { return this.p.readAddress(a); }
|
||||
connectProbe(probe: ProbeAll) { this.p.connectProbe(probe); }
|
||||
}
|
||||
|
||||
class JSNESPlatform extends Base6502Platform implements Platform, Probeable {
|
||||
@@ -98,7 +123,7 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable {
|
||||
ntlastbuf: Uint32Array;
|
||||
showDebugView = false;
|
||||
|
||||
machine = { cpuCyclesPerLine: 114 }; // TODO: hack for width of probe scope
|
||||
machine = new JSNESMachine(this);
|
||||
|
||||
constructor(mainElement) {
|
||||
super();
|
||||
@@ -318,6 +343,11 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable {
|
||||
return this.nes.ppu.curX;
|
||||
}
|
||||
|
||||
// the debugger asks for these before every instruction; skip toJSON()
|
||||
getPC() { return (this.nes.cpu.REG_PC + this.debugPCDelta) & 0xffff; }
|
||||
getSP() { return this.nes.cpu.REG_SP & 0xff; }
|
||||
isStable() { return true; }
|
||||
|
||||
getCPUState() {
|
||||
var c = this.nes.cpu.toJSON();
|
||||
this.copy6502REGvars(c);
|
||||
@@ -335,13 +365,8 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable {
|
||||
}
|
||||
s.c = s.cpu;
|
||||
this.copy6502REGvars(s.c);
|
||||
// jsnes's utils.toJSON() stores *references* to the live typed arrays
|
||||
// (jsnes/src/utils.js), so a save state keeps changing as the emulator
|
||||
// runs on. Copy every one of them, or a checkpoint is worthless the moment
|
||||
// it is taken -- restoring it would be a no-op self-copy.
|
||||
copyTypedArrays(s.cpu);
|
||||
copyTypedArrays(s.ppu);
|
||||
copyTypedArrays(s.mmap);
|
||||
// our jsnes fork's toJSON() copies every array, so the state doesn't
|
||||
// change as the emulator runs on
|
||||
s.b = s.cpu.mem;
|
||||
s.ctrl = this.saveControlsState();
|
||||
return s;
|
||||
@@ -353,10 +378,7 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable {
|
||||
//this.nes.cpu.fromJSON(state.cpu);
|
||||
//this.nes.mmap.fromJSON(state.mmap);
|
||||
//this.nes.ppu.fromJSON(state.ppu);
|
||||
// fromJSON() copies typed arrays in with .set(), so nothing here aliases
|
||||
// the state -- but the plain `mem` reference does, and the debugger reads
|
||||
// through it
|
||||
this.nes.cpu.mem = state.cpu.mem.slice(0);
|
||||
// fromJSON() copies arrays in, so nothing here aliases the state
|
||||
this.loadControlsState(state.ctrl);
|
||||
//$.extend(this.nes, state);
|
||||
this.installIntercepts();
|
||||
@@ -368,8 +390,9 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable {
|
||||
};
|
||||
}
|
||||
loadControlsState(state) {
|
||||
this.nes.controllers[1].state = state.c1;
|
||||
this.nes.controllers[2].state = state.c2;
|
||||
// copy, or later button presses would change the saved state
|
||||
this.nes.controllers[1].state = state.c1.slice(0);
|
||||
this.nes.controllers[2].state = state.c2.slice(0);
|
||||
}
|
||||
readAddress(addr) {
|
||||
return this.nes.cpu.mem[addr];
|
||||
|
||||
@@ -244,6 +244,13 @@ describe('8bws CLI', function () {
|
||||
// 6502 registers and flags should be shown while stepping
|
||||
assert.ok(/A=\$[0-9A-F]{2} X=\$[0-9A-F]{2} Y=\$[0-9A-F]{2} SP=\$[0-9A-F]{2} [NnVvDdIiZzCc]{6}/.test(out), out);
|
||||
});
|
||||
it('should step and step back on nes (jsnes)', function () {
|
||||
var out = cli('run', '--platform', 'nes', '-e', 'run 5; step 3; back 2; pc 1', 'test/roms/nes/shoot2.c.rom');
|
||||
assert.ok(!/does not support|only checks the PC at frame boundaries/.test(out), out);
|
||||
// stopped 3 instructions into frame 5, then back to the 1st
|
||||
assert.ok(/^\[5:3\] PC=/m.test(out), out);
|
||||
assert.ok(/^\[5:1\] back 2: PC=\$[0-9A-F]{4}/m.test(out), out);
|
||||
});
|
||||
it('should record an instruction history', function () {
|
||||
var out = cli('run', '--platform', 'apple2', '-e', 'run 20; hist 5', 'test/roms/apple2/cosmic.c.rom');
|
||||
assert.ok(/instructions shown/.test(out), out);
|
||||
|
||||
@@ -56,4 +56,7 @@ describe('Deterministic replay', function () {
|
||||
it('should replay zx exactly (joymask must round-trip)', async function () {
|
||||
await checkDeterministic('zx', 20);
|
||||
});
|
||||
it('should replay nes exactly (jsnes save states must not alias live arrays)', async function () {
|
||||
assert.strictEqual(await checkDeterministic('nes', 20), 'insn');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user