jsbeeb 1.22.4 → 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 +16 -11
- package/src/6502.js +39 -42
- package/src/6502.opcodes.js +198 -123
- 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/jsbeeb.css +33 -0
- 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 +69 -184
- package/src/media-resolver.js +68 -0
- package/src/mmc.js +3 -2
- package/src/models.js +40 -4
- 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 +42 -72
- package/src/web/console-surface.js +25 -0
- package/src/web/debug.js +8 -49
- 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/icons.js +42 -0
- package/src/web/keyboard-setup.js +29 -40
- 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/src/fake6502.js
CHANGED
|
@@ -1,40 +1,24 @@
|
|
|
1
1
|
// Fakes out various 6502s for testing purposes.
|
|
2
|
-
"use strict";
|
|
3
2
|
|
|
4
3
|
import { FakeVideo } from "./video.js";
|
|
5
4
|
import { FakeSoundChip } from "./soundchip.js";
|
|
6
5
|
import { TEST_6502, TEST_65C02, TEST_65C12, tubeModelFor } from "./models.js";
|
|
7
|
-
import {
|
|
8
|
-
import { FakeRelayNoise } from "./relaynoise.js";
|
|
9
|
-
import { Cpu6502, AtomCpu6502 } from "./6502.js";
|
|
10
|
-
import { Cmos } from "./cmos.js";
|
|
11
|
-
import { FakeMusic5000 } from "./music5000.js";
|
|
6
|
+
import { machineSpec, nullIo } from "./machine-spec.js";
|
|
12
7
|
|
|
13
8
|
const fakeVideo = new FakeVideo();
|
|
14
9
|
const soundChip = new FakeSoundChip();
|
|
15
|
-
const dbgr = {
|
|
16
|
-
setCpu: () => {},
|
|
17
|
-
};
|
|
18
10
|
|
|
19
|
-
export function fake6502(model, opts) {
|
|
20
|
-
opts = opts || {};
|
|
11
|
+
export function fake6502(model, opts = {}) {
|
|
21
12
|
model = model || TEST_6502;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
video: opts.video || fakeVideo,
|
|
26
|
-
soundChip: opts.soundChip || soundChip,
|
|
27
|
-
ddNoise: new FakeDdNoise(),
|
|
28
|
-
relayNoise: new FakeRelayNoise(),
|
|
29
|
-
music5000: new FakeMusic5000(),
|
|
30
|
-
cmos: new Cmos(),
|
|
31
|
-
cycleAccurate: opts.cycleAccurate,
|
|
32
|
-
config: {
|
|
13
|
+
return new model.Cpu(model, {
|
|
14
|
+
...nullIo({ video: opts.video ?? fakeVideo, soundChip: opts.soundChip ?? soundChip }),
|
|
15
|
+
config: machineSpec({
|
|
33
16
|
tube: opts.tube ? tubeModelFor(model) : null,
|
|
34
17
|
tubeCpuMultiplier: opts.tubeCpuMultiplier,
|
|
35
18
|
cpuMultiplier: opts.cpuMultiplier,
|
|
36
19
|
hasTeletextAdaptor: opts.hasTeletextAdaptor,
|
|
37
|
-
},
|
|
20
|
+
}),
|
|
21
|
+
cycleAccurate: opts.cycleAccurate,
|
|
38
22
|
});
|
|
39
23
|
}
|
|
40
24
|
|
package/src/fdc.js
CHANGED
|
@@ -10,11 +10,12 @@ import {
|
|
|
10
10
|
toSsdOrDsd,
|
|
11
11
|
} from "./disc.js";
|
|
12
12
|
import { loadHfe, sniffHfeLayout, toHfe } from "./disc-hfe.js";
|
|
13
|
-
import
|
|
13
|
+
import { stringToUint8Array } from "./binary.js";
|
|
14
|
+
import { loadData } from "./loader.js";
|
|
14
15
|
|
|
15
16
|
export function load(name) {
|
|
16
|
-
console.log("Loading disc from " + name);
|
|
17
|
-
return
|
|
17
|
+
console.log("Loading disc from " + name);
|
|
18
|
+
return loadData(name);
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -185,7 +186,7 @@ const hfeDiscType = new DiscType({
|
|
|
185
186
|
const adlDiscType = new DiscType({
|
|
186
187
|
extension: ".adl",
|
|
187
188
|
loader: (disc, data, _onChange) => {
|
|
188
|
-
// TODO handle onChange
|
|
189
|
+
// TODO(#822) handle onChange
|
|
189
190
|
return loadAdf(disc, data, true);
|
|
190
191
|
},
|
|
191
192
|
saver: (_data) => {
|
|
@@ -200,7 +201,7 @@ const adlDiscType = new DiscType({
|
|
|
200
201
|
const adfDiscType = new DiscType({
|
|
201
202
|
extension: ".adf",
|
|
202
203
|
loader: (disc, data, _onChange) => {
|
|
203
|
-
// TODO handle onChange
|
|
204
|
+
// TODO(#822) handle onChange
|
|
204
205
|
return loadAdf(disc, data, false);
|
|
205
206
|
},
|
|
206
207
|
saver: (_data) => {
|
|
@@ -265,15 +266,14 @@ function is40TrackLayout(discType, data, name, layout) {
|
|
|
265
266
|
|
|
266
267
|
/**
|
|
267
268
|
* Create a disc object of the appropriate type based on the file name
|
|
268
|
-
* @param {Object} fdc - The FDC controller object
|
|
269
269
|
* @param {string} name - The file name with extension
|
|
270
270
|
* @param {string|Uint8Array} stringData - The disc image data as string or Uint8Array
|
|
271
|
-
* @param {function(Uint8Array): void} onChange -
|
|
271
|
+
* @param {function(Uint8Array): void} [onChange] - called when disc content changes
|
|
272
272
|
* @param {string} [layout] - one of DiscLayout; by default the image is asked what it is
|
|
273
273
|
* @returns {Disc} The loaded disc object
|
|
274
274
|
*/
|
|
275
|
-
export function discFor(
|
|
276
|
-
const data = typeof stringData !== "string" ? stringData :
|
|
275
|
+
export function discFor(name, stringData, onChange, layout = DiscLayout.auto) {
|
|
276
|
+
const data = typeof stringData !== "string" ? stringData : stringToUint8Array(stringData);
|
|
277
277
|
const discType = guessDiscTypeFromName(name);
|
|
278
278
|
const config = new DiscConfig();
|
|
279
279
|
config.expandTo80 = is40TrackLayout(discType, data, name, layout);
|
|
@@ -288,45 +288,3 @@ export function discFor(fdc, name, stringData, onChange, layout = DiscLayout.aut
|
|
|
288
288
|
disc.setOriginalImageCrc32(data instanceof Uint8Array ? data : new Uint8Array(data));
|
|
289
289
|
return disc;
|
|
290
290
|
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Create or open a disc held in the browser's local storage.
|
|
294
|
-
* @param {Object} fdc - The FDC controller object
|
|
295
|
-
* @param {string} name - The file name with extension
|
|
296
|
-
* @param {string} [layout] - one of DiscLayout; by default the image is asked what it is
|
|
297
|
-
* @param {function(*): void} [onSaveError] - called with whatever was thrown, the first time a write
|
|
298
|
-
* cannot be stored
|
|
299
|
-
* @returns {Disc} The loaded disc object
|
|
300
|
-
*/
|
|
301
|
-
export function localDisc(fdc, name, layout = DiscLayout.auto, onSaveError = () => {}) {
|
|
302
|
-
const discName = "disc_" + name;
|
|
303
|
-
let data;
|
|
304
|
-
const dataString = window.localStorage[discName];
|
|
305
|
-
if (!dataString) {
|
|
306
|
-
console.log("Creating browser-local disc " + name);
|
|
307
|
-
const discType = guessDiscTypeFromName(name);
|
|
308
|
-
if (!discType.byteSize) {
|
|
309
|
-
throw new Error(`Cannot create blank disc of type ${discType.extension} - unknown size`);
|
|
310
|
-
}
|
|
311
|
-
data = new Uint8Array(discType.byteSize);
|
|
312
|
-
if (discType.supportsCatalogue) {
|
|
313
|
-
discType.setDiscName(data, name);
|
|
314
|
-
}
|
|
315
|
-
} else {
|
|
316
|
-
console.log("Loading browser-local disc " + name);
|
|
317
|
-
data = utils.stringToUint8Array(dataString);
|
|
318
|
-
}
|
|
319
|
-
let reportedSaveError = false;
|
|
320
|
-
const onChange = (data) => {
|
|
321
|
-
try {
|
|
322
|
-
const str = utils.uint8ArrayToString(data);
|
|
323
|
-
window.localStorage.setItem(discName, str);
|
|
324
|
-
} catch (e) {
|
|
325
|
-
console.log(`Unable to save browser-local disc ${name}: ${e}`);
|
|
326
|
-
if (reportedSaveError) return;
|
|
327
|
-
reportedSaveError = true;
|
|
328
|
-
onSaveError(e);
|
|
329
|
-
}
|
|
330
|
-
};
|
|
331
|
-
return discFor(fdc, name, data, onChange, layout);
|
|
332
|
-
}
|
package/src/filestore.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
// Level-3 file server emulator ported from FSEM2
|
|
2
2
|
// https://github.com/mmbeeb/FSEM
|
|
3
3
|
|
|
4
|
-
"use strict";
|
|
5
|
-
|
|
6
4
|
import { ReceiveBlock, EconetPacket } from "./econet.js";
|
|
7
|
-
import
|
|
5
|
+
import { loadData } from "./loader.js";
|
|
8
6
|
|
|
9
7
|
export class Filestore {
|
|
10
8
|
constructor(cpu, econet) {
|
|
@@ -306,22 +304,17 @@ export class Filestore {
|
|
|
306
304
|
}
|
|
307
305
|
}
|
|
308
306
|
|
|
309
|
-
|
|
307
|
+
/** Resolves once the filestore code and its disc are loaded and in place. */
|
|
308
|
+
async reset() {
|
|
310
309
|
console.log("Filestore: initialisation");
|
|
311
310
|
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
filestoreRef.SP = 0xff;
|
|
320
|
-
filestoreRef.A = 1;
|
|
321
|
-
});
|
|
322
|
-
utils.loadData("econet/scsi.dat").then(function (data) {
|
|
323
|
-
filestoreRef.scsi = data;
|
|
324
|
-
});
|
|
311
|
+
const [code, disc] = await Promise.all([loadData("econet/L3FS.dat"), loadData("econet/scsi.dat")]);
|
|
312
|
+
this.l3fs = code;
|
|
313
|
+
this.ram.set(code, 0x400);
|
|
314
|
+
this.PC = 0x400;
|
|
315
|
+
this.SP = 0xff;
|
|
316
|
+
this.A = 1;
|
|
317
|
+
this.scsi = disc;
|
|
325
318
|
}
|
|
326
319
|
|
|
327
320
|
polltime(cycles) {
|
package/src/hex.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function parseAddr(s) {
|
|
2
|
+
if (s[0] === "$" || s[0] === "&") return parseInt(s.substring(1), 16);
|
|
3
|
+
if (s.indexOf("0x") === 0) return parseInt(s.substring(2), 16);
|
|
4
|
+
return parseInt(s, 16);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function hexbyte(value) {
|
|
8
|
+
return ((value >>> 4) & 0xf).toString(16) + (value & 0xf).toString(16);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function hexword(value) {
|
|
12
|
+
return hexbyte(value >>> 8) + hexbyte(value & 0xff);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function hd(reader, start, end, opts) {
|
|
16
|
+
opts = opts || {};
|
|
17
|
+
const width = opts.width || 16;
|
|
18
|
+
const gap = opts.gap === undefined ? 8 : opts.gap;
|
|
19
|
+
const res = [];
|
|
20
|
+
let str = "";
|
|
21
|
+
let j = 0;
|
|
22
|
+
for (let i = start; i < end; ++i) {
|
|
23
|
+
str += " ";
|
|
24
|
+
str += hexbyte(reader(i));
|
|
25
|
+
if (++j === gap) str += " ";
|
|
26
|
+
if (j === width) {
|
|
27
|
+
res.push(str);
|
|
28
|
+
str = "";
|
|
29
|
+
j = 0;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (str) res.push(str);
|
|
33
|
+
let joined = "";
|
|
34
|
+
for (let i = 0; i < res.length; ++i) {
|
|
35
|
+
joined += hexword(start + i * width) + " :" + res[i] + "\n";
|
|
36
|
+
}
|
|
37
|
+
return joined;
|
|
38
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What jsbeeb offers a program that is not the web page: a machine to drive
|
|
3
|
+
* headless, the session the MCP server wraps around it, the models, and the
|
|
4
|
+
* pieces that describe and feed a machine. Electron starts from src/app/app.js
|
|
5
|
+
* through package.json's main; the page starts from src/main.js.
|
|
6
|
+
*/
|
|
7
|
+
export { MachineSession } from "./machine-session.js";
|
|
8
|
+
export { TestMachine } from "./test-machine.js";
|
|
9
|
+
export { allModels, findModel } from "./models.js";
|
|
10
|
+
export { machineSpec, nullIo } from "./machine-spec.js";
|
|
11
|
+
export { MediaResolver } from "./media-resolver.js";
|
package/src/intel-fdc.js
CHANGED
|
@@ -9,7 +9,7 @@ import { Disc, IbmDiscFormat } from "./disc.js";
|
|
|
9
9
|
import { BaseDiscDrive, DiscDrive } from "./disc-drive.js";
|
|
10
10
|
// eslint-disable-next-line no-unused-vars
|
|
11
11
|
import { Scheduler } from "./scheduler.js";
|
|
12
|
-
import
|
|
12
|
+
import { hexbyte, hexword } from "./hex.js";
|
|
13
13
|
|
|
14
14
|
// TODOs remaining for intel-fdc and related functionality
|
|
15
15
|
// - support loading other disc formats
|
|
@@ -343,7 +343,7 @@ export class IntelFdc {
|
|
|
343
343
|
// aka. late DMA, which will abort the command while NMI is asserted. We
|
|
344
344
|
// therefore need to de-assert NMI so that the NMI for command completion
|
|
345
345
|
// isn't lost.
|
|
346
|
-
// TODO(
|
|
346
|
+
// TODO(#1058) we don't model NMIs properly here, each device should have its own nmi line
|
|
347
347
|
this._cpu.NMI(false);
|
|
348
348
|
}
|
|
349
349
|
|
|
@@ -400,7 +400,7 @@ export class IntelFdc {
|
|
|
400
400
|
case Address.unknown_read_3:
|
|
401
401
|
return this._regs[Registers.internalCountLsb];
|
|
402
402
|
default:
|
|
403
|
-
throw new Error(`"Unexpected read of addr ${
|
|
403
|
+
throw new Error(`"Unexpected read of addr ${hexword(addr)}"`);
|
|
404
404
|
}
|
|
405
405
|
}
|
|
406
406
|
|
|
@@ -443,7 +443,7 @@ export class IntelFdc {
|
|
|
443
443
|
break;
|
|
444
444
|
case 3:
|
|
445
445
|
default:
|
|
446
|
-
this._log(`Not supported: ${
|
|
446
|
+
this._log(`Not supported: ${hexword(addr)}=${hexbyte(val)}`);
|
|
447
447
|
}
|
|
448
448
|
}
|
|
449
449
|
|
|
@@ -516,7 +516,7 @@ export class IntelFdc {
|
|
|
516
516
|
const data = this._mmioData;
|
|
517
517
|
const pulses = IbmDiscFormat.fmTo2usPulses(clocks, data);
|
|
518
518
|
if (clocks !== 0xff && clocks !== IbmDiscFormat.markClockPattern)
|
|
519
|
-
this._log(`writing unusual clocks=${
|
|
519
|
+
this._log(`writing unusual clocks=${hexbyte(clocks)} data=${hexbyte(data)}`);
|
|
520
520
|
this._currentDrive.writePulses(pulses);
|
|
521
521
|
}
|
|
522
522
|
|
|
@@ -1058,7 +1058,7 @@ export class IntelFdc {
|
|
|
1058
1058
|
status |= StatusFlag.resultReady;
|
|
1059
1059
|
}
|
|
1060
1060
|
|
|
1061
|
-
// TODO
|
|
1061
|
+
// TODO(#1060) "command register full", bit value 0x40, isn't understood. In
|
|
1062
1062
|
// particular, the mode register (shared with the status register we
|
|
1063
1063
|
// believe) is set to 0xC1 in typical operation. This would seem to raise
|
|
1064
1064
|
// 0x40 after it has been lowered at command register acceptance. However,
|
|
@@ -1073,9 +1073,7 @@ export class IntelFdc {
|
|
|
1073
1073
|
_commandWritten(command) {
|
|
1074
1074
|
const status = this.internalStatus;
|
|
1075
1075
|
if (status & StatusFlag.busy) {
|
|
1076
|
-
this._log(
|
|
1077
|
-
`command ${utils.hexbyte(command)} while busy with ${utils.hexbyte(this._regs[Registers.internalCommand])}`,
|
|
1078
|
-
);
|
|
1076
|
+
this._log(`command ${hexbyte(command)} while busy with ${hexbyte(this._regs[Registers.internalCommand])}`);
|
|
1079
1077
|
}
|
|
1080
1078
|
|
|
1081
1079
|
// Set command.
|
|
@@ -1125,7 +1123,7 @@ export class IntelFdc {
|
|
|
1125
1123
|
break;
|
|
1126
1124
|
}
|
|
1127
1125
|
case ParamAccept.specify: {
|
|
1128
|
-
this._logCommand(`specify param ${
|
|
1126
|
+
this._logCommand(`specify param ${hexbyte(param)}`);
|
|
1129
1127
|
this._writeRegister(this._regs[Registers.internalPointer], param);
|
|
1130
1128
|
++this._regs[Registers.internalPointer];
|
|
1131
1129
|
if (--this._regs[Registers.internalParamCount] === 0) {
|
|
@@ -1155,7 +1153,7 @@ export class IntelFdc {
|
|
|
1155
1153
|
case Registers.mmioData & 0x07:
|
|
1156
1154
|
return this._mmioData;
|
|
1157
1155
|
default:
|
|
1158
|
-
this._log(`direct read from MMIO register ${
|
|
1156
|
+
this._log(`direct read from MMIO register ${hexbyte(reg)}`);
|
|
1159
1157
|
break;
|
|
1160
1158
|
}
|
|
1161
1159
|
return 0;
|
|
@@ -1185,7 +1183,7 @@ export class IntelFdc {
|
|
|
1185
1183
|
this._mmioData = val;
|
|
1186
1184
|
break;
|
|
1187
1185
|
default:
|
|
1188
|
-
this._log(`direct write to MMIO register ${
|
|
1186
|
+
this._log(`direct write to MMIO register ${hexbyte(reg)}`);
|
|
1189
1187
|
break;
|
|
1190
1188
|
}
|
|
1191
1189
|
}
|
|
@@ -1430,13 +1428,13 @@ export class IntelFdc {
|
|
|
1430
1428
|
commandReg &= ~(DriveOut.selectFlags | 0x03);
|
|
1431
1429
|
this._regs[Registers.internalCommand] = commandReg;
|
|
1432
1430
|
this._logCommand(
|
|
1433
|
-
`command ${
|
|
1434
|
-
`sel ${
|
|
1435
|
-
`params ${
|
|
1436
|
-
`${
|
|
1437
|
-
`${
|
|
1438
|
-
`${
|
|
1439
|
-
`${
|
|
1431
|
+
`command ${hexbyte(origCommand & 0x3f)} ` +
|
|
1432
|
+
`sel ${hexbyte(selectBits)} ` +
|
|
1433
|
+
`params ${hexbyte(this._regs[Registers.internalParam_1])} ` +
|
|
1434
|
+
`${hexbyte(this._regs[Registers.internalParam_2])} ` +
|
|
1435
|
+
`${hexbyte(this._regs[Registers.internalParam_3])} ` +
|
|
1436
|
+
`${hexbyte(this._regs[Registers.internalParam_4])} ` +
|
|
1437
|
+
`${hexbyte(this._regs[Registers.internalParam_5])} ` +
|
|
1440
1438
|
`ptrk ${this._currentDrive ? this._currentDrive.track : -1} ` +
|
|
1441
1439
|
`hpos ${this._currentDrive ? this._currentDrive.headPosition : -1}`,
|
|
1442
1440
|
);
|
|
@@ -1631,7 +1629,7 @@ export class IntelFdc {
|
|
|
1631
1629
|
|
|
1632
1630
|
_lowerBusyAndLog() {
|
|
1633
1631
|
this._statusLower(StatusFlag.busy);
|
|
1634
|
-
this._logCommand(`status ${
|
|
1632
|
+
this._logCommand(`status ${hexbyte(this._status)} result ${hexbyte(this._result)}`);
|
|
1635
1633
|
}
|
|
1636
1634
|
|
|
1637
1635
|
_spinDown() {
|
package/src/jsbeeb.css
CHANGED
|
@@ -560,6 +560,39 @@ small {
|
|
|
560
560
|
flex: none;
|
|
561
561
|
}
|
|
562
562
|
|
|
563
|
+
#header-bar .navbar-brand img {
|
|
564
|
+
display: block;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
#header-bar [data-icon] > svg {
|
|
568
|
+
width: 1.15em;
|
|
569
|
+
height: 1.15em;
|
|
570
|
+
vertical-align: -0.2em;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/* Shrunk, the menu list wraps onto a second row; the promo yields instead, by truncating. */
|
|
574
|
+
#header-bar .navbar-nav {
|
|
575
|
+
flex-shrink: 0;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
#header-bar .navbar-text {
|
|
579
|
+
flex: 0 1 auto;
|
|
580
|
+
min-width: 0;
|
|
581
|
+
white-space: nowrap;
|
|
582
|
+
overflow: hidden;
|
|
583
|
+
text-overflow: ellipsis;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
#paste-form {
|
|
587
|
+
flex: 1 0 6rem;
|
|
588
|
+
min-width: 0;
|
|
589
|
+
max-width: 14rem;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
#paste-text {
|
|
593
|
+
min-width: 0;
|
|
594
|
+
}
|
|
595
|
+
|
|
563
596
|
/* The only item in the header with no bound on its width, so it is the one that yields. */
|
|
564
597
|
#disc-name {
|
|
565
598
|
flex: 1;
|
|
@@ -1,21 +1,4 @@
|
|
|
1
|
-
import { keyCodes, userKeymap } from "./
|
|
2
|
-
|
|
3
|
-
// Detect keyboard layout locally — mirrors the logic in utils.js but avoids
|
|
4
|
-
// importing the non-exported isUKlayout. Should be replaced with an import
|
|
5
|
-
// if utils.js ever exports it.
|
|
6
|
-
function detectKeyboardLayout() {
|
|
7
|
-
if (typeof navigator === "undefined") return "UK";
|
|
8
|
-
if (typeof localStorage !== "undefined" && localStorage.keyboardLayout) {
|
|
9
|
-
return localStorage.keyboardLayout === "US" ? "US" : "UK";
|
|
10
|
-
}
|
|
11
|
-
if (navigator.language) {
|
|
12
|
-
if (navigator.language.toLowerCase() === "en-gb") return "UK";
|
|
13
|
-
if (navigator.language.toLowerCase() === "en-us") return "US";
|
|
14
|
-
}
|
|
15
|
-
return "UK";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const isUKlayout = detectKeyboardLayout() === "UK";
|
|
1
|
+
import { detectKeyboardLayout, keyCodes, userKeymap } from "./keymap.js";
|
|
19
2
|
|
|
20
3
|
// ATOM
|
|
21
4
|
|
|
@@ -228,6 +211,7 @@ export function stringToATOMKeys(str) {
|
|
|
228
211
|
}
|
|
229
212
|
|
|
230
213
|
export function getKeyMapAtom(keyLayout) {
|
|
214
|
+
const isUKlayout = detectKeyboardLayout() === "UK";
|
|
231
215
|
const keys2 = [];
|
|
232
216
|
|
|
233
217
|
// shift pressed
|