Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/controller.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,31 @@ export type ButtonKey = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;

export class Controller {
state: number[];
baseA: number;
baseB: number;
turboA: boolean;
turboB: boolean;
turboToggle: boolean;

buttonDown: (key: ButtonKey) => void;
buttonUp: (key: ButtonKey) => void;
clock: () => void;
toJSON(): {
state: number[];
baseA: number;
baseB: number;
turboA: boolean;
turboB: boolean;
turboToggle: boolean;
};
fromJSON(state: {
state: number[];
baseA: number;
baseB: number;
turboA: boolean;
turboB: boolean;
turboToggle: boolean;
}): void;

static readonly BUTTON_A = 0;
static readonly BUTTON_B = 1;
Expand All @@ -16,4 +38,12 @@ export class Controller {
static readonly BUTTON_RIGHT = 7;
static readonly BUTTON_TURBO_A = 8;
static readonly BUTTON_TURBO_B = 9;
static readonly JSON_PROPERTIES: readonly [
"state",
"baseA",
"baseB",
"turboA",
"turboB",
"turboToggle",
];
}
19 changes: 19 additions & 0 deletions src/controller.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { fromJSON, toJSON } from "./utils.js";

class Controller {
static BUTTON_A = 0;
static BUTTON_B = 1;
Expand Down Expand Up @@ -65,6 +67,23 @@ class Controller {
this.state[Controller.BUTTON_B] = this.turboToggle ? 0x41 : 0x40;
}
}

static JSON_PROPERTIES = [
"state",
"baseA",
"baseB",
"turboA",
"turboB",
"turboToggle",
];

toJSON() {
return toJSON(this);
}

fromJSON(s) {
fromJSON(this, s);
}
}

export default Controller;
1 change: 1 addition & 0 deletions src/cpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -2022,6 +2022,7 @@ class CPU {
"F_BRK",
"F_BRK_NEW",
"_cpuCycleBase",
"dataBus",
];

toJSON() {
Expand Down
9 changes: 9 additions & 0 deletions src/nes.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ class NES {
mmap: this.mmap.toJSON(),
ppu: this.ppu.toJSON(),
papu: this.papu.toJSON(),
controllers: {
1: this.controllers[1].toJSON(),
2: this.controllers[2].toJSON(),
},
};
}

Expand All @@ -192,6 +196,11 @@ class NES {
this.mmap.fromJSON(s.mmap);
this.ppu.fromJSON(s.ppu);
this.papu.fromJSON(s.papu);

if (s.controllers) {
this.controllers[1].fromJSON(s.controllers[1]);
this.controllers[2].fromJSON(s.controllers[2]);
}
}
}

Expand Down
28 changes: 28 additions & 0 deletions test/nes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,34 @@ describe("NES", function () {
});
});

describe("save state serialization", function () {
it("preserves cpu dataBus and controller state", function () {
let nes = new NES();
let data = fs.readFileSync("roms/croom/croom.nes");
nes.loadROM(data.toString("binary"));

nes.cpu.dataBus = 0xab;
nes.buttonDown(1, 0); // A
nes.buttonDown(1, 8); // turbo A
nes.controllers[1].clock();

const state = nes.toJSON();

let restored = new NES();
restored.loadROM(data.toString("binary"));
restored.fromJSON(state);

assert.strictEqual(restored.cpu.dataBus, 0xab);
assert.deepStrictEqual(restored.controllers[1].state, nes.controllers[1].state);
assert.strictEqual(restored.controllers[1].baseA, nes.controllers[1].baseA);
assert.strictEqual(restored.controllers[1].turboA, nes.controllers[1].turboA);
assert.strictEqual(
restored.controllers[1].turboToggle,
nes.controllers[1].turboToggle,
);
});
});

describe("#getFPS()", function () {
let nes = new NES();
before(function () {
Expand Down