jsbeeb 1.24.0 → 1.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/fake6502.js +5 -2
- package/src/models.js +3 -2
- package/src/test-machine.js +0 -26
- package/src/video.js +26 -0
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"name": "jsbeeb",
|
|
8
8
|
"description": "Emulate a BBC Micro",
|
|
9
9
|
"repository": "git@github.com:mattgodbolt/jsbeeb.git",
|
|
10
|
-
"version": "1.24.
|
|
10
|
+
"version": "1.24.1",
|
|
11
11
|
"//engines": "If you change the version of Node, it must also be updated at the top of the Dockerfile.",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=24.15.0"
|
package/src/fake6502.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Fakes out various 6502s for testing purposes.
|
|
2
2
|
|
|
3
|
-
import { FakeVideo } from "./video.js";
|
|
3
|
+
import { FakeAtomVideo, FakeVideo } from "./video.js";
|
|
4
4
|
import { FakeSoundChip } from "./soundchip.js";
|
|
5
5
|
import { TEST_6502, TEST_65C02, TEST_65C12, tubeModelFor } from "./models.js";
|
|
6
6
|
import { machineSpec, nullIo } from "./machine-spec.js";
|
|
@@ -11,7 +11,10 @@ const soundChip = new FakeSoundChip();
|
|
|
11
11
|
export function fake6502(model, opts = {}) {
|
|
12
12
|
model = model || TEST_6502;
|
|
13
13
|
return new model.Cpu(model, {
|
|
14
|
-
...nullIo({
|
|
14
|
+
...nullIo({
|
|
15
|
+
video: opts.video ?? (model.isAtom ? new FakeAtomVideo() : fakeVideo),
|
|
16
|
+
soundChip: opts.soundChip ?? soundChip,
|
|
17
|
+
}),
|
|
15
18
|
config: machineSpec({
|
|
16
19
|
tube: opts.tube ? tubeModelFor(model) : null,
|
|
17
20
|
tubeCpuMultiplier: opts.tubeCpuMultiplier,
|
package/src/models.js
CHANGED
|
@@ -50,8 +50,9 @@ class Model {
|
|
|
50
50
|
this.stringToKeys = this.isAtom ? stringToATOMKeys : stringToBBCKeys;
|
|
51
51
|
// The OS write-character vector, watched to capture what the machine prints.
|
|
52
52
|
this.wrchvAddress = this.isAtom ? 0x0208 : 0x020e;
|
|
53
|
-
// Where the machine sits waiting for a key: the Atom kernel's
|
|
54
|
-
|
|
53
|
+
// Where the machine sits waiting for a key: the Atom kernel's keyboard scan, which its
|
|
54
|
+
// line editor calls straight (the OSRDCH entry at $FE94 is only passed at boot), or BASIC's.
|
|
55
|
+
this.idleAddress = this.isAtom ? 0xfe71 : isMaster ? 0xe7e6 : 0xe581;
|
|
55
56
|
// The Atom ROM polls its keyboard once per VSync, so pasted keys need longer apart.
|
|
56
57
|
this.pasteKeyDelayMs = this.isAtom ? 80 : 50;
|
|
57
58
|
// Two of those 60 Hz scans, 33 ms, with margin: the ROM wants a key seen up
|
package/src/test-machine.js
CHANGED
|
@@ -32,32 +32,6 @@ export class TestMachine {
|
|
|
32
32
|
async initialise() {
|
|
33
33
|
setNodeBasePath(RepoRoot);
|
|
34
34
|
await this.processor.initialise();
|
|
35
|
-
if (this.model.isAtom) this._startAtomVSync();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* The Atom ROM's main loop waits for VSync (bit 7 of Port C) to
|
|
40
|
-
* toggle before scanning the keyboard. The MC6847 video chip drives
|
|
41
|
-
* this in the real emulator, but fake6502 doesn't create one, so we
|
|
42
|
-
* simulate it with a scheduler task at ~60 Hz (NTSC).
|
|
43
|
-
*/
|
|
44
|
-
_startAtomVSync() {
|
|
45
|
-
const ppia = this.processor.atomppia;
|
|
46
|
-
const VsyncPeriod = 16667; // 1 MHz / 60 Hz (NTSC 262-line frame)
|
|
47
|
-
const VsyncPulse = 800;
|
|
48
|
-
let inVsync = false;
|
|
49
|
-
const task = this.processor.scheduler.newTask(() => {
|
|
50
|
-
if (!inVsync) {
|
|
51
|
-
ppia.setVBlankInt(1);
|
|
52
|
-
inVsync = true;
|
|
53
|
-
task.reschedule(VsyncPulse);
|
|
54
|
-
} else {
|
|
55
|
-
ppia.setVBlankInt(0);
|
|
56
|
-
inVsync = false;
|
|
57
|
-
task.reschedule(VsyncPeriod - VsyncPulse);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
task.schedule(VsyncPeriod);
|
|
61
35
|
}
|
|
62
36
|
|
|
63
37
|
/**
|
package/src/video.js
CHANGED
|
@@ -1254,3 +1254,29 @@ export class FakeVideo {
|
|
|
1254
1254
|
|
|
1255
1255
|
restoreState() {}
|
|
1256
1256
|
}
|
|
1257
|
+
|
|
1258
|
+
// The 6847's field as the PPIA sees it: 262 lines at 60Hz, with field sync low
|
|
1259
|
+
// for the 32 lines of flyback (see 6847.js).
|
|
1260
|
+
const AtomFieldCycles = 16667;
|
|
1261
|
+
const AtomFlybackCycles = Math.round((AtomFieldCycles * 32) / 262);
|
|
1262
|
+
|
|
1263
|
+
/** No picture, but the field sync the Atom ROM waits on before each keyboard scan. */
|
|
1264
|
+
export class FakeAtomVideo extends FakeVideo {
|
|
1265
|
+
reset(cpu) {
|
|
1266
|
+
this.ppia = cpu.atomppia;
|
|
1267
|
+
this.cycles = 0;
|
|
1268
|
+
this.inFlyback = false;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
polltime(cycles) {
|
|
1272
|
+
this.cycles += cycles;
|
|
1273
|
+
if (!this.inFlyback && this.cycles >= AtomFieldCycles - AtomFlybackCycles) {
|
|
1274
|
+
this.inFlyback = true;
|
|
1275
|
+
this.ppia.setVBlankInt(1);
|
|
1276
|
+
} else if (this.inFlyback && this.cycles >= AtomFieldCycles) {
|
|
1277
|
+
this.inFlyback = false;
|
|
1278
|
+
this.cycles -= AtomFieldCycles;
|
|
1279
|
+
this.ppia.setVBlankInt(0);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|