jsbeeb 1.23.0 → 1.24.0
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/README.md +2 -2
- package/package.json +14 -10
- package/src/6502.js +39 -42
- package/src/6502.opcodes.js +7 -13
- package/src/6847.js +4 -5
- package/src/6847_fontdata.js +0 -2
- package/src/acia.js +2 -3
- package/src/app/app.js +0 -1
- package/src/app/args.js +0 -2
- package/src/app/electron.js +4 -6
- package/src/app-bench.js +1 -3
- package/src/archive.js +243 -0
- package/src/basic-loader.js +0 -7
- package/src/basic-tokenise.js +19 -6
- package/src/bbc-palette.js +0 -2
- package/src/bbcdiscs.js +0 -2
- package/src/bem-snapshot.js +1 -3
- package/src/binary.js +175 -0
- package/src/cmos.js +0 -2
- package/src/ddnoise.js +0 -1
- package/src/debounce.js +7 -0
- package/src/disc-surface.js +0 -2
- package/src/disc.js +10 -9
- package/src/fake6502.js +7 -23
- package/src/fdc.js +9 -51
- package/src/filestore.js +10 -17
- package/src/hex.js +38 -0
- package/src/index.js +11 -0
- package/src/intel-fdc.js +18 -20
- package/src/{utils_atom.js → keymap-atom.js} +2 -18
- package/src/{utils.js → keymap.js} +47 -610
- package/src/loader.js +63 -0
- package/src/machine-session.js +37 -177
- package/src/machine-spec.js +59 -0
- package/src/main.js +66 -180
- package/src/media-resolver.js +68 -0
- package/src/mmc.js +3 -2
- package/src/models.js +17 -3
- package/src/music5000.js +0 -2
- package/src/ppia.js +3 -4
- package/src/printer.js +7 -14
- package/src/relaynoise.js +0 -1
- package/src/rewind.js +0 -2
- package/src/sample-player.js +2 -3
- package/src/serial.js +0 -2
- package/src/snapshot-helpers.js +0 -2
- package/src/snapshot.js +0 -2
- package/src/soundchip.js +1 -1
- package/src/state-utils.js +0 -2
- package/src/sth.js +2 -4
- package/src/tapes.js +10 -4
- package/src/teletext.js +2 -3
- package/src/teletext_adaptor.js +2 -4
- package/src/teletext_data.js +0 -2
- package/src/test-machine.js +307 -0
- package/src/touchscreen.js +2 -4
- package/src/tube.js +5 -6
- package/src/typist.js +103 -0
- package/src/uef-snapshot.js +0 -2
- package/src/url-params.js +4 -5
- package/src/vdu-capture.js +113 -0
- package/src/via.js +3 -4
- package/src/video-filters/pal-composite.js +0 -2
- package/src/video-filters/passthrough-filter.js +0 -2
- package/src/video-filters/pixel-grid.js +0 -2
- package/src/video-filters/shader-program.js +0 -2
- package/src/video-filters/xbr-filter.js +0 -2
- package/src/video.js +12 -11
- package/src/wd-fdc.js +20 -20
- package/src/web/analogue-inputs.js +6 -8
- package/src/web/analytics.js +13 -0
- package/src/web/audio-handler.js +2 -2
- package/src/{audio-utils.js → web/audio-utils.js} +0 -2
- package/src/web/autoboot.js +10 -13
- package/src/{canvas.js → web/canvas.js} +4 -5
- package/src/web/config.js +41 -74
- package/src/web/console-surface.js +25 -0
- package/src/web/debug.js +6 -5
- package/src/web/disc-visualiser.js +0 -2
- package/src/web/display.js +21 -4
- package/src/{dom-utils.js → web/dom-utils.js} +1 -11
- package/src/web/drives.js +1 -1
- package/src/web/emulation-loop.js +54 -33
- package/src/web/front-panel.js +13 -12
- package/src/{gamepads.js → web/gamepads.js} +2 -6
- package/src/web/google-drive-picker.js +14 -9
- package/src/{google-drive.js → web/google-drive.js} +9 -10
- package/src/web/hfe-picker.js +5 -4
- package/src/web/keyboard-setup.js +18 -38
- package/src/{keyboard.js → web/keyboard.js} +18 -119
- package/src/web/layout.js +4 -0
- package/src/web/local-disc.js +44 -0
- package/src/web/machine.js +27 -20
- package/src/web/media-loader.js +38 -147
- package/src/{microphone-input.js → web/microphone-input.js} +1 -1
- package/src/web/modals.js +6 -9
- package/src/web/page-actions.js +88 -0
- package/src/web/quick-settings.js +16 -15
- package/src/web/reporting.js +0 -7
- package/src/{rewind-thumbnail.js → web/rewind-thumbnail.js} +0 -2
- package/src/web/rewind-ui.js +29 -29
- package/src/web/run-controls.js +39 -0
- package/src/web/settings.js +64 -135
- package/src/web/snapshot-ui.js +10 -16
- package/src/{speech-output.js → web/speech-output.js} +0 -2
- package/src/web/sth-picker.js +9 -7
- package/src/web/url-state.js +20 -6
- package/src/econet/L3FS.dat +0 -0
- package/src/econet/scsi.dat +0 -0
- package/tests/test-machine.js +0 -609
package/README.md
CHANGED
|
@@ -96,8 +96,8 @@ Some things to know:
|
|
|
96
96
|
was at fault.
|
|
97
97
|
- On the Atom, use the Atom's own key names (`LOCK`, `UP_DOWN`, `LEFT_RIGHT` and so on) rather than the BBC's.
|
|
98
98
|
|
|
99
|
-
The definitive lists are `keyCodes` (host) and `BBC` (BBC micro) in [`src/
|
|
100
|
-
[`src/
|
|
99
|
+
The definitive lists are `keyCodes` (host) and `BBC` (BBC micro) in [`src/keymap.js`](src/keymap.js), and `ATOM` in
|
|
100
|
+
[`src/keymap-atom.js`](src/keymap-atom.js).
|
|
101
101
|
|
|
102
102
|
### Emulator Shortcuts
|
|
103
103
|
|
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.
|
|
10
|
+
"version": "1.24.0",
|
|
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"
|
|
@@ -15,14 +15,12 @@
|
|
|
15
15
|
"type": "module",
|
|
16
16
|
"main": "./src/app/app.js",
|
|
17
17
|
"exports": {
|
|
18
|
-
".": "./src/
|
|
18
|
+
".": "./src/index.js",
|
|
19
19
|
"./machine-session": "./src/machine-session.js",
|
|
20
|
-
"./src/*": "./src/*"
|
|
21
|
-
"./tests/test-machine.js": "./tests/test-machine.js"
|
|
20
|
+
"./src/*": "./src/*"
|
|
22
21
|
},
|
|
23
22
|
"files": [
|
|
24
23
|
"src/",
|
|
25
|
-
"tests/test-machine.js",
|
|
26
24
|
"public/roms/"
|
|
27
25
|
],
|
|
28
26
|
"dependencies": {
|
|
@@ -32,11 +30,11 @@
|
|
|
32
30
|
"bootstrap-icons": "^1.13.1",
|
|
33
31
|
"bootswatch": "^5.3.8",
|
|
34
32
|
"pako": "^3.0.1",
|
|
35
|
-
"sharp": "^0.35.3",
|
|
36
33
|
"smoothie": "^1.36.1"
|
|
37
34
|
},
|
|
38
35
|
"devDependencies": {
|
|
39
36
|
"@eslint/js": "^10.0.1",
|
|
37
|
+
"@playwright/test": "^1.62.1",
|
|
40
38
|
"@vitest/coverage-v8": "^4.0.18",
|
|
41
39
|
"eslint": "^10.7.0",
|
|
42
40
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -54,6 +52,7 @@
|
|
|
54
52
|
},
|
|
55
53
|
"optionalDependencies": {
|
|
56
54
|
"electron": "^44.0.0",
|
|
55
|
+
"sharp": "^0.35.3",
|
|
57
56
|
"electron-builder": "^26.15.3",
|
|
58
57
|
"electron-store": "^11.0.2"
|
|
59
58
|
},
|
|
@@ -90,9 +89,12 @@
|
|
|
90
89
|
"start": "vite",
|
|
91
90
|
"build": "vite build",
|
|
92
91
|
"preview": "vite preview",
|
|
93
|
-
"ci-checks": "
|
|
94
|
-
"
|
|
92
|
+
"ci-checks": "npm-run-all lint format:check check-workflow-suites",
|
|
93
|
+
"check-workflow-suites": "node tools/check-workflow-suites.js",
|
|
94
|
+
"lint": "eslint .",
|
|
95
|
+
"lint-fix": "eslint . --fix",
|
|
95
96
|
"format": "prettier --write .",
|
|
97
|
+
"format:check": "prettier --check .",
|
|
96
98
|
"pre-commit": "lint-staged",
|
|
97
99
|
"test": "npm-run-all test:*",
|
|
98
100
|
"test:unit": "vitest run --silent=true tests/unit",
|
|
@@ -100,6 +102,7 @@
|
|
|
100
102
|
"test:cpu": "node tests/test-suite.js",
|
|
101
103
|
"test:shader": "vitest run --silent=true tests/shader",
|
|
102
104
|
"coverage:unit": "vitest run tests/unit --coverage",
|
|
105
|
+
"dom-unit-tests": "vitest run --silent=true --project unit $(grep -rl domFromIndexHtml tests/unit)",
|
|
103
106
|
"coverage:all-tests": "vitest run --coverage",
|
|
104
107
|
"benchmark": "node src/app-bench.js",
|
|
105
108
|
"mirror-sth": "node tools/mirror-sth.js --out .sth-mirror",
|
|
@@ -116,14 +119,15 @@
|
|
|
116
119
|
"mirror-bbcdiscs:upload:index": "aws s3 cp .bbcdiscs-mirror/hfe/manifest.json s3://bbc.xania.org/archive/bbcdiscs/hfe/manifest.json --cache-control 'public, max-age=300' && aws s3 cp .bbcdiscs-mirror/manifest.json s3://bbc.xania.org/archive/bbcdiscs/manifest.json --cache-control 'public, max-age=300'",
|
|
117
120
|
"electron": "npm run build && ELECTRON_DISABLE_SANDBOX=1 electron .",
|
|
118
121
|
"electron:build": "electron-builder",
|
|
119
|
-
"test:
|
|
122
|
+
"test:e2e": "npm run build && playwright test --project chrome"
|
|
120
123
|
},
|
|
121
124
|
"lint-staged": {
|
|
122
125
|
"*.js": [
|
|
123
|
-
"vitest related --run --
|
|
126
|
+
"vitest related --run --project unit --no-file-parallelism",
|
|
124
127
|
"eslint --cache --fix",
|
|
125
128
|
"prettier --write"
|
|
126
129
|
],
|
|
130
|
+
"index.html": "npm run dom-unit-tests --",
|
|
127
131
|
"*.{md,json,yml,yaml,css,html}": "prettier --write"
|
|
128
132
|
},
|
|
129
133
|
"allowScripts": {
|
package/src/6502.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import * as utils from "./utils.js";
|
|
3
1
|
import * as via from "./via.js";
|
|
4
2
|
import { Acia } from "./acia.js";
|
|
5
3
|
import { Serial } from "./serial.js";
|
|
@@ -12,8 +10,10 @@ import { Filestore } from "./filestore.js";
|
|
|
12
10
|
import { FakeRelayNoise } from "./relaynoise.js";
|
|
13
11
|
import { AtomPPIA } from "./ppia.js";
|
|
14
12
|
import { AtomMMC2 } from "./mmc.js";
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
import { unzipRomImage } from "./archive.js";
|
|
14
|
+
import { signExtend } from "./binary.js";
|
|
15
|
+
import { hexbyte, hexword } from "./hex.js";
|
|
16
|
+
import { loadData } from "./loader.js";
|
|
17
17
|
|
|
18
18
|
function _set(byte, mask, set) {
|
|
19
19
|
return (byte & ~mask) | (set ? mask : 0);
|
|
@@ -255,7 +255,7 @@ class Base6502 {
|
|
|
255
255
|
this.polltime(3);
|
|
256
256
|
} else {
|
|
257
257
|
this.polltime(2);
|
|
258
|
-
// TODO
|
|
258
|
+
// TODO(#1063) check 65c12 BRK interrupt poll timing.
|
|
259
259
|
this.checkInt();
|
|
260
260
|
this.polltime(1);
|
|
261
261
|
}
|
|
@@ -540,7 +540,7 @@ class Tube6502 extends Base6502 {
|
|
|
540
540
|
async loadOs() {
|
|
541
541
|
console.log("Loading tube rom from roms/" + this.model.os);
|
|
542
542
|
const tubeRom = this.rom;
|
|
543
|
-
const data = await
|
|
543
|
+
const data = await loadData("roms/" + this.model.os);
|
|
544
544
|
const len = data.length;
|
|
545
545
|
if (len !== 2048) throw new Error("Broken ROM file (length=" + len + ")");
|
|
546
546
|
for (let i = 0; i < len; ++i) {
|
|
@@ -561,25 +561,6 @@ class FakeTube {
|
|
|
561
561
|
reset() {}
|
|
562
562
|
}
|
|
563
563
|
|
|
564
|
-
class FakeUserPort {
|
|
565
|
-
write() {}
|
|
566
|
-
|
|
567
|
-
read() {
|
|
568
|
-
return 0xff;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function fixUpConfig(config) {
|
|
573
|
-
if (config === undefined) config = {};
|
|
574
|
-
if (!config.keyLayout) config.keyLayout = "physical";
|
|
575
|
-
if (!config.cpuMultiplier) config.cpuMultiplier = 1;
|
|
576
|
-
if (!config.userPort) config.userPort = new FakeUserPort();
|
|
577
|
-
if (config.printerPort === undefined) config.printerPort = null;
|
|
578
|
-
config.extraRoms = config.extraRoms || [];
|
|
579
|
-
config.debugFlags = config.debugFlags || {};
|
|
580
|
-
return config;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
564
|
class DebugHook {
|
|
584
565
|
constructor(cpu, functionName) {
|
|
585
566
|
this.cpu = cpu;
|
|
@@ -645,7 +626,8 @@ export class Cpu6502 extends Base6502 {
|
|
|
645
626
|
} = {},
|
|
646
627
|
) {
|
|
647
628
|
super(model, { cycleAccurate });
|
|
648
|
-
this.config =
|
|
629
|
+
this.config = config;
|
|
630
|
+
this.keyLayout = config.keyLayout;
|
|
649
631
|
this.debugFlags = this.config.debugFlags;
|
|
650
632
|
this.cmos = cmos;
|
|
651
633
|
this.debugger = dbgr;
|
|
@@ -702,12 +684,14 @@ export class Cpu6502 extends Base6502 {
|
|
|
702
684
|
soundChip: this.soundChip,
|
|
703
685
|
cmos: this.cmos,
|
|
704
686
|
isMaster: this.model.isMaster,
|
|
705
|
-
initialLayout: this.
|
|
687
|
+
initialLayout: this.keyLayout,
|
|
706
688
|
getGamepads: this.config.getGamepads,
|
|
707
689
|
});
|
|
708
690
|
this.uservia = new via.UserVia(this, this.scheduler, this.model.isMaster, this.config.userPort);
|
|
709
691
|
this.acia = new Acia(this, this.soundChip.toneGenerator, this.scheduler, this.relayNoise);
|
|
710
692
|
this.serial = new Serial(this.acia);
|
|
693
|
+
this.keyboardInterface = this.sysvia;
|
|
694
|
+
this.tapeInterface = this.acia;
|
|
711
695
|
this.adconverter = new Adc(this.sysvia, this.scheduler);
|
|
712
696
|
this.touchScreen = new TouchScreen(this.scheduler, this.model.cyclesPerSecond);
|
|
713
697
|
this.soundChip.setScheduler(this.scheduler);
|
|
@@ -826,7 +810,7 @@ export class Cpu6502 extends Base6502 {
|
|
|
826
810
|
readArea(addr, len) {
|
|
827
811
|
let str = "";
|
|
828
812
|
for (let i = 0; i < len; ++i) {
|
|
829
|
-
str +=
|
|
813
|
+
str += hexbyte(this.readmem(addr + i));
|
|
830
814
|
}
|
|
831
815
|
return str;
|
|
832
816
|
}
|
|
@@ -1003,7 +987,7 @@ export class Cpu6502 extends Base6502 {
|
|
|
1003
987
|
const offset = this.memLook[statOffset];
|
|
1004
988
|
return this.ramRomOs[offset + addr];
|
|
1005
989
|
} else {
|
|
1006
|
-
return 0xff; // TODO
|
|
990
|
+
return 0xff; // TODO(#1062) peekDevice: this.peekDevice(addr);
|
|
1007
991
|
}
|
|
1008
992
|
}
|
|
1009
993
|
|
|
@@ -1165,9 +1149,9 @@ export class Cpu6502 extends Base6502 {
|
|
|
1165
1149
|
if (name.indexOf("http") !== 0) name = "roms/" + name;
|
|
1166
1150
|
console.log("Loading ROM from " + name);
|
|
1167
1151
|
const ramRomOs = this.ramRomOs;
|
|
1168
|
-
let data = await
|
|
1152
|
+
let data = await loadData(name);
|
|
1169
1153
|
if (/\.zip/i.test(name)) {
|
|
1170
|
-
data = (await
|
|
1154
|
+
data = (await unzipRomImage(data)).data;
|
|
1171
1155
|
}
|
|
1172
1156
|
ramRomOs.set(data, offset);
|
|
1173
1157
|
}
|
|
@@ -1177,7 +1161,7 @@ export class Cpu6502 extends Base6502 {
|
|
|
1177
1161
|
os = "roms/" + os;
|
|
1178
1162
|
console.log(`Loading OS from ${os}`);
|
|
1179
1163
|
const ramRomOs = this.ramRomOs;
|
|
1180
|
-
const data = await
|
|
1164
|
+
const data = await loadData(os);
|
|
1181
1165
|
const len = data.length;
|
|
1182
1166
|
if (len < 16384 || len & 16383) throw new Error(`Broken OS ROM file (length=${len})`);
|
|
1183
1167
|
ramRomOs.set(data, this.osOffset);
|
|
@@ -1410,7 +1394,7 @@ export class Cpu6502 extends Base6502 {
|
|
|
1410
1394
|
}
|
|
1411
1395
|
|
|
1412
1396
|
updateKeyLayout() {
|
|
1413
|
-
this.sysvia.setKeyLayout(this.
|
|
1397
|
+
this.sysvia.setKeyLayout(this.keyLayout);
|
|
1414
1398
|
}
|
|
1415
1399
|
|
|
1416
1400
|
polltimeAddr(cycles, addr, isWrite) {
|
|
@@ -1555,6 +1539,12 @@ export class Cpu6502 extends Base6502 {
|
|
|
1555
1539
|
this.halted = true;
|
|
1556
1540
|
}
|
|
1557
1541
|
|
|
1542
|
+
/** Changes the host key layout for the keyboard, and for every reset after it. */
|
|
1543
|
+
setKeyLayout(layout) {
|
|
1544
|
+
this.keyLayout = layout;
|
|
1545
|
+
this.sysvia.setKeyLayout(layout);
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1558
1548
|
dumpTrace(maxToShow, func) {
|
|
1559
1549
|
if (!maxToShow) maxToShow = 256;
|
|
1560
1550
|
if (maxToShow > 256) maxToShow = 256;
|
|
@@ -1564,11 +1554,11 @@ export class Cpu6502 extends Base6502 {
|
|
|
1564
1554
|
function (pc, a, x, y) {
|
|
1565
1555
|
const dis = disassembler.disassemble(pc, true)[0];
|
|
1566
1556
|
console.log(
|
|
1567
|
-
|
|
1557
|
+
hexword(pc),
|
|
1568
1558
|
(dis + " ").substring(0, 15),
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1559
|
+
hexbyte(a),
|
|
1560
|
+
hexbyte(x),
|
|
1561
|
+
hexbyte(y),
|
|
1572
1562
|
);
|
|
1573
1563
|
};
|
|
1574
1564
|
for (let i = maxToShow - 2; i >= 0; --i) {
|
|
@@ -1613,12 +1603,19 @@ const NumBranquartBanks = 16;
|
|
|
1613
1603
|
const AtomRomBlockSize = 0x1000; // 4KB
|
|
1614
1604
|
|
|
1615
1605
|
export class AtomCpu6502 extends Cpu6502 {
|
|
1606
|
+
setKeyLayout(layout) {
|
|
1607
|
+
this.keyLayout = layout;
|
|
1608
|
+
this.atomppia.setKeyLayout(layout);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1616
1611
|
constructor(model, options) {
|
|
1617
1612
|
super(model, options);
|
|
1618
1613
|
|
|
1619
1614
|
// Atom peripherals
|
|
1620
|
-
this.atomppia = new AtomPPIA(this, this.
|
|
1615
|
+
this.atomppia = new AtomPPIA(this, this.keyLayout, this.scheduler);
|
|
1621
1616
|
this.atommc = new AtomMMC2(this);
|
|
1617
|
+
this.keyboardInterface = this.atomppia;
|
|
1618
|
+
this.tapeInterface = this.atomppia;
|
|
1622
1619
|
|
|
1623
1620
|
// Branquart bank selection
|
|
1624
1621
|
this.branquartLatch = 0;
|
|
@@ -1785,7 +1782,7 @@ export class AtomCpu6502 extends Cpu6502 {
|
|
|
1785
1782
|
const bankRoms = this.model.banks || [];
|
|
1786
1783
|
os = "roms/" + os;
|
|
1787
1784
|
console.log(`Loading Atom OS from ${os}`);
|
|
1788
|
-
const data = await
|
|
1785
|
+
const data = await loadData(os);
|
|
1789
1786
|
const len = data.length;
|
|
1790
1787
|
|
|
1791
1788
|
if (len < AtomRomBlockSize || len % AtomRomBlockSize !== 0) {
|
|
@@ -1825,9 +1822,9 @@ export class AtomCpu6502 extends Cpu6502 {
|
|
|
1825
1822
|
// Override loadRom to accept 4KB ROMs
|
|
1826
1823
|
async loadRom(name, offset) {
|
|
1827
1824
|
if (name.indexOf("http") !== 0) name = "roms/" + name;
|
|
1828
|
-
let data = await
|
|
1825
|
+
let data = await loadData(name);
|
|
1829
1826
|
if (/\.zip/i.test(name)) {
|
|
1830
|
-
data = (await
|
|
1827
|
+
data = (await unzipRomImage(data)).data;
|
|
1831
1828
|
}
|
|
1832
1829
|
const len = data.length;
|
|
1833
1830
|
if (len !== 16384 && len !== 8192 && len !== 4096) {
|
|
@@ -1838,7 +1835,7 @@ export class AtomCpu6502 extends Cpu6502 {
|
|
|
1838
1835
|
|
|
1839
1836
|
// Atom key layout goes through PPIA, not SysVia
|
|
1840
1837
|
updateKeyLayout() {
|
|
1841
|
-
this.atomppia.setKeyLayout(this.
|
|
1838
|
+
this.atomppia.setKeyLayout(this.keyLayout);
|
|
1842
1839
|
}
|
|
1843
1840
|
|
|
1844
1841
|
snapshotState(options) {
|
package/src/6502.opcodes.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
const hexword = utils.hexword;
|
|
5
|
-
const hexbyte = utils.hexbyte;
|
|
6
|
-
const signExtend = utils.signExtend;
|
|
1
|
+
import { signExtend } from "./binary.js";
|
|
2
|
+
import { hexbyte, hexword } from "./hex.js";
|
|
7
3
|
|
|
8
4
|
function rotate(left, logical) {
|
|
9
5
|
const lines = [];
|
|
@@ -1072,13 +1068,13 @@ class Disassemble6502 {
|
|
|
1072
1068
|
case "()": {
|
|
1073
1069
|
const zp = this.cpu.peekmem(addr + 1);
|
|
1074
1070
|
const destAddr = this.cpu.peekmem(zp) | (this.cpu.peekmem(zp + 1) << 8);
|
|
1075
|
-
return [`${split[0]} ($${hexbyte(zp)})${suffix} ; $${
|
|
1071
|
+
return [`${split[0]} ($${hexbyte(zp)})${suffix} ; $${hexword(destAddr)}${suffix2}`, addr + 2];
|
|
1076
1072
|
}
|
|
1077
1073
|
case "(abs)": {
|
|
1078
1074
|
const destAddr = this.cpu.peekmem(addr + 1) | (this.cpu.peekmem(addr + 2) << 8);
|
|
1079
1075
|
const indDest = this.cpu.peekmem(destAddr) | (this.cpu.peekmem(destAddr + 1) << 8);
|
|
1080
1076
|
return [
|
|
1081
|
-
`${split[0]} ($${formatJumpAddr(destAddr)})${suffix} ; $${
|
|
1077
|
+
`${split[0]} ($${formatJumpAddr(destAddr)})${suffix} ; $${hexword(indDest)}${suffix2}`,
|
|
1082
1078
|
addr + 3,
|
|
1083
1079
|
indDest,
|
|
1084
1080
|
];
|
|
@@ -1390,12 +1386,10 @@ function makeCpuFunctions(cpu, opcodes, undocumented, is65c12, cycleAccurate = t
|
|
|
1390
1386
|
indent +
|
|
1391
1387
|
[
|
|
1392
1388
|
'"use strict";',
|
|
1393
|
-
`// ${
|
|
1389
|
+
`// ${hexbyte(opcodeNum)} - ${opcode}
|
|
1394
1390
|
`,
|
|
1395
1391
|
].concat(
|
|
1396
|
-
opcode
|
|
1397
|
-
? getInstruction(opcode, !!needsReg)
|
|
1398
|
-
: [`this.invalidOpcode(cpu, 0x${utils.hexbyte(opcodeNum)});`],
|
|
1392
|
+
opcode ? getInstruction(opcode, !!needsReg) : [`this.invalidOpcode(cpu, 0x${hexbyte(opcodeNum)});`],
|
|
1399
1393
|
).join(`
|
|
1400
1394
|
${indent}`)
|
|
1401
1395
|
);
|
|
@@ -1408,7 +1402,7 @@ ${indent}`)
|
|
|
1408
1402
|
const funcs = [];
|
|
1409
1403
|
for (let opcodeNum = 0; opcodeNum < 256; ++opcodeNum) {
|
|
1410
1404
|
const opcodeFunc = new Function("cpu", getIndentedSource(" ", opcodeNum, true));
|
|
1411
|
-
let funcName = `exec_${
|
|
1405
|
+
let funcName = `exec_${hexbyte(opcodeNum)}_`;
|
|
1412
1406
|
if (opcodes[opcodeNum]) {
|
|
1413
1407
|
const instrName = opcodes[opcodeNum]
|
|
1414
1408
|
.replace("()", "ind")
|
package/src/6847.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import * as utils from "./utils.js";
|
|
3
1
|
import * as fontData from "./6847_fontdata.js";
|
|
4
2
|
import { encodeLineGrid } from "./video-filters/pixel-grid.js";
|
|
3
|
+
import { makeFast32 } from "./binary.js";
|
|
5
4
|
|
|
6
5
|
const VDISPENABLE = 1 << 0,
|
|
7
6
|
HDISPENABLE = 1 << 1,
|
|
@@ -114,7 +113,7 @@ const MODE_AG = 0x10; // graphics mode
|
|
|
114
113
|
|
|
115
114
|
// The MC6847 datasheet specifies 3.579545 MHz, but 3.638004 is used here as an
|
|
116
115
|
// empirical correction to align VSync/interrupt timing with the CPU clock.
|
|
117
|
-
// TODO
|
|
116
|
+
// TODO(#1067) revisit once full integration is testable.
|
|
118
117
|
const VdgClockMhz = 3.638004;
|
|
119
118
|
|
|
120
119
|
export class Video6847 {
|
|
@@ -127,7 +126,7 @@ export class Video6847 {
|
|
|
127
126
|
|
|
128
127
|
// 8 colours (alpha on MSB)
|
|
129
128
|
//
|
|
130
|
-
this.collook =
|
|
129
|
+
this.collook = makeFast32(
|
|
131
130
|
new Uint32Array([
|
|
132
131
|
0xff000000, // #00000000, // black
|
|
133
132
|
0xff03b91e, // #00ff00, // green
|
|
@@ -496,7 +495,7 @@ export class Video6847 {
|
|
|
496
495
|
// Render data depending on display enable state.
|
|
497
496
|
if (this.bitmapX >= 0 && this.bitmapX < 1024 && this.bitmapY < 625) {
|
|
498
497
|
{
|
|
499
|
-
// TODO
|
|
498
|
+
// TODO(#1067) Add in the INTEXT modifiers to mode (if necessary)
|
|
500
499
|
// blit into the fb32 buffer which is painted by VIDEO
|
|
501
500
|
const textMode = (mode & MODE_AG) === 0; // 0x10 is the AG bit
|
|
502
501
|
this.recordLineGrid(textMode);
|
package/src/6847_fontdata.js
CHANGED
package/src/acia.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Some info:
|
|
3
2
|
// http://www.playvectrex.com/designit/lecture/UP12.HTM
|
|
4
3
|
// https://books.google.com/books?id=wUecAQAAQBAJ&pg=PA431&lpg=PA431&dq=acia+tdre&source=bl&ots=mp-yF-mK-P&sig=e6aXkFRfiIOb57WZmrvdIGsCooI&hl=en&sa=X&ei=0g2fVdDyFIXT-QG8-JD4BA&ved=0CCwQ6AEwAw#v=onepage&q=acia%20tdre&f=false
|
|
@@ -52,7 +51,7 @@ export class Acia extends EventTarget {
|
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
reset() {
|
|
55
|
-
// TODO
|
|
54
|
+
// TODO(#1064) testing on a real beeb seems to suggest that reset also
|
|
56
55
|
// clears CR bits (i.e. things stop working until CR is rewritten
|
|
57
56
|
// with sane value). This disagrees with the datasheet.
|
|
58
57
|
// CTS and DTD are based on external inputs so leave them alone.
|
|
@@ -199,7 +198,7 @@ export class Acia extends EventTarget {
|
|
|
199
198
|
byte |= 0;
|
|
200
199
|
if (this.sr & 0x01) {
|
|
201
200
|
// Overrun.
|
|
202
|
-
// TODO
|
|
201
|
+
// TODO(#1064) this doesn't match the datasheet:
|
|
203
202
|
// "The Overrun does not occur in the Status Register until the
|
|
204
203
|
// valid character prior to Overrun has been read."
|
|
205
204
|
this.sr |= 0xa0;
|
package/src/app/app.js
CHANGED
package/src/app/args.js
CHANGED
package/src/app/electron.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
1
|
// Electron integration for jsbeeb desktop application.
|
|
4
2
|
// Handles IPC communication for loading disc/tape images and showing modals from Electron's main process.
|
|
5
3
|
|
|
6
4
|
function init(args) {
|
|
7
|
-
const { loadDiscImage, loadTapeImage, loadStateFile, processor, modals, actions,
|
|
5
|
+
const { loadDiscImage, loadTapeImage, loadStateFile, processor, modals, actions, settings, media } = args;
|
|
8
6
|
const api = window.electronAPI;
|
|
9
7
|
|
|
10
8
|
api.onLoadDisc(async (message) => {
|
|
@@ -16,7 +14,7 @@ function init(args) {
|
|
|
16
14
|
api.onLoadTape(async (message) => {
|
|
17
15
|
const { path } = message;
|
|
18
16
|
const tape = await loadTapeImage(path);
|
|
19
|
-
processor.
|
|
17
|
+
processor.tapeInterface.setTape(tape);
|
|
20
18
|
});
|
|
21
19
|
|
|
22
20
|
api.onShowModal((message) => {
|
|
@@ -54,8 +52,8 @@ function init(args) {
|
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
// Save settings when they change
|
|
57
|
-
if (
|
|
58
|
-
|
|
55
|
+
if (settings) {
|
|
56
|
+
settings.addEventListener("change", (e) => {
|
|
59
57
|
api.saveSettings(e.detail);
|
|
60
58
|
});
|
|
61
59
|
}
|
package/src/app-bench.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
1
|
import { fake6502 } from "./fake6502.js";
|
|
4
2
|
import * as models from "./models.js";
|
|
5
3
|
import * as disc from "./fdc.js";
|
|
@@ -23,7 +21,7 @@ async function main() {
|
|
|
23
21
|
const cpu = fake6502(models.findModel("B"));
|
|
24
22
|
await cpu.initialise();
|
|
25
23
|
const data = await disc.load("discs/" + discName + ".ssd");
|
|
26
|
-
cpu.fdc.loadDisc(0, disc.discFor(
|
|
24
|
+
cpu.fdc.loadDisc(0, disc.discFor(discName, data));
|
|
27
25
|
cpu.sysvia.keyDown(16);
|
|
28
26
|
cpu.execute(10 * 1000 * 1000);
|
|
29
27
|
cpu.sysvia.keyUp(16);
|