rk86 2.0.17 → 2.0.19
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 +3 -3
- package/package.json +1 -1
- package/rk86.js +88 -38
package/README.md
CHANGED
|
@@ -62,13 +62,13 @@ bunx rk86 -l # список файлов встроенн
|
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
64
|
bunx rk86 --headless \
|
|
65
|
-
--input "KeyD,Digit0,Comma,KeyF,KeyF,Enter" \
|
|
65
|
+
--input "KeyD,KeyF,Digit8,Digit0,Digit0,Comma,KeyF,Digit8,KeyF,KeyF,Enter" \
|
|
66
66
|
--timeout 10 \
|
|
67
67
|
--screen out.txt
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
После выхода `out.txt` содержит приглашение `-->
|
|
71
|
-
дампа `
|
|
70
|
+
После выхода `out.txt` содержит приглашение `-->DF800,F8FF` и 16 строк шестнадцатеричного
|
|
71
|
+
дампа `F800..F8FF`.
|
|
72
72
|
|
|
73
73
|
### Пример 2. Запись HLT и запуск через команды `M` / `G`
|
|
74
74
|
|
package/package.json
CHANGED
package/rk86.js
CHANGED
|
@@ -2277,7 +2277,7 @@ import { readFile, writeFile } from "fs/promises";
|
|
|
2277
2277
|
// packages/rk86/package.json
|
|
2278
2278
|
var package_default = {
|
|
2279
2279
|
name: "rk86",
|
|
2280
|
-
version: "2.0.
|
|
2280
|
+
version: "2.0.18",
|
|
2281
2281
|
description: "\u042D\u043C\u0443\u043B\u044F\u0442\u043E\u0440 \u0420\u0430\u0434\u0438\u043E-86\u0420\u041A (Intel 8080) \u0434\u043B\u044F \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0430",
|
|
2282
2282
|
bin: {
|
|
2283
2283
|
rk86: "rk86.js"
|
|
@@ -3707,16 +3707,19 @@ class Runner {
|
|
|
3707
3707
|
init_sound(enabled) {
|
|
3708
3708
|
if (enabled && this.sound == null && this.sound_factory) {
|
|
3709
3709
|
this.sound = this.sound_factory();
|
|
3710
|
-
|
|
3710
|
+
this.machine.log("\u0437\u0432\u0443\u043A \u0432\u043A\u043B\u044E\u0447\u0435\u043D");
|
|
3711
3711
|
} else if (!enabled) {
|
|
3712
3712
|
this.sound = null;
|
|
3713
|
-
|
|
3713
|
+
this.machine.log("\u0437\u0432\u0443\u043A \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D");
|
|
3714
3714
|
}
|
|
3715
3715
|
}
|
|
3716
3716
|
execute(options = {}) {
|
|
3717
|
-
const { terminate_address, on_terminate, exit_on_halt,
|
|
3717
|
+
const { terminate_address, on_terminate, exit_on_halt, on_batch_complete, turbo } = options;
|
|
3718
3718
|
clearTimeout(this.execute_timer);
|
|
3719
|
-
|
|
3719
|
+
const bursts = turbo ? 100 : 1;
|
|
3720
|
+
for (let burst = 0;burst < bursts; burst++) {
|
|
3721
|
+
if (this.paused)
|
|
3722
|
+
break;
|
|
3720
3723
|
let batch_ticks = 0;
|
|
3721
3724
|
let batch_instructions = 0;
|
|
3722
3725
|
while (batch_ticks < this.TICK_PER_MS) {
|
|
@@ -3742,8 +3745,6 @@ class Runner {
|
|
|
3742
3745
|
this.machine.ui.on_visualizer_hit(this.machine.memory.read_raw(this.machine.cpu.pc));
|
|
3743
3746
|
}
|
|
3744
3747
|
batch_instructions += 1;
|
|
3745
|
-
if (armed?.value === false)
|
|
3746
|
-
continue;
|
|
3747
3748
|
if (terminate_address !== undefined && this.machine.cpu.pc === terminate_address) {
|
|
3748
3749
|
on_terminate?.();
|
|
3749
3750
|
return;
|
|
@@ -3758,8 +3759,10 @@ class Runner {
|
|
|
3758
3759
|
this.previous_batch_time = now;
|
|
3759
3760
|
this.instructions_per_millisecond = batch_instructions / elapsed;
|
|
3760
3761
|
this.ticks_per_millisecond = batch_ticks / elapsed;
|
|
3762
|
+
this.machine.screen.tick_cursor(this.total_ticks, this.FREQ * (this.machine.screen.cursor_rate / 1000));
|
|
3763
|
+
on_batch_complete?.();
|
|
3761
3764
|
}
|
|
3762
|
-
this.execute_timer = setTimeout(() => this.execute(options), 10);
|
|
3765
|
+
this.execute_timer = setTimeout(() => this.execute(options), turbo ? 0 : 10);
|
|
3763
3766
|
}
|
|
3764
3767
|
pause() {
|
|
3765
3768
|
this.paused = true;
|
|
@@ -3845,9 +3848,15 @@ class Screen {
|
|
|
3845
3848
|
start(renderer) {
|
|
3846
3849
|
this.renderer = renderer;
|
|
3847
3850
|
this.renderer.connect(this.machine);
|
|
3848
|
-
this.flip_cursor();
|
|
3849
3851
|
this.render_loop();
|
|
3850
3852
|
}
|
|
3853
|
+
last_flip_ticks = 0;
|
|
3854
|
+
tick_cursor(total_ticks, ticks_per_flip) {
|
|
3855
|
+
while (total_ticks - this.last_flip_ticks >= ticks_per_flip) {
|
|
3856
|
+
this.cursor_state = !this.cursor_state;
|
|
3857
|
+
this.last_flip_ticks += ticks_per_flip;
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3851
3860
|
render_loop() {
|
|
3852
3861
|
if (this.ready)
|
|
3853
3862
|
this.renderer.update();
|
|
@@ -3862,7 +3871,7 @@ class Screen {
|
|
|
3862
3871
|
this.machine.ui.update_screen_geometry(this.width, this.height);
|
|
3863
3872
|
if (this.last_width === this.width && this.last_height === this.height)
|
|
3864
3873
|
return;
|
|
3865
|
-
|
|
3874
|
+
this.machine.log(`\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D \u0440\u0430\u0437\u043C\u0435\u0440 \u044D\u043A\u0440\u0430\u043D\u0430: ${width} x ${height}`);
|
|
3866
3875
|
this.last_width = this.width;
|
|
3867
3876
|
this.last_height = this.height;
|
|
3868
3877
|
if (this.last_video_memory_base !== -1)
|
|
@@ -3874,7 +3883,7 @@ class Screen {
|
|
|
3874
3883
|
this.machine.ui.update_video_memory_address(this.video_memory_base);
|
|
3875
3884
|
if (this.last_video_memory_base === this.video_memory_base)
|
|
3876
3885
|
return;
|
|
3877
|
-
|
|
3886
|
+
this.machine.log(`\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u0432\u0438\u0434\u0435\u043E\u043F\u0430\u043C\u044F\u0442\u044C \u0441 \u0430\u0434\u0440\u0435\u0441\u0430`, `${hex16(this.video_memory_base)}`, `\u0440\u0430\u0437\u043C\u0435\u0440\u043E\u043C ${hex16(this.video_memory_size)}`);
|
|
3878
3887
|
this.last_video_memory_base = this.video_memory_base;
|
|
3879
3888
|
if (this.last_width !== -1)
|
|
3880
3889
|
this.ready = true;
|
|
@@ -3883,13 +3892,28 @@ class Screen {
|
|
|
3883
3892
|
this.cursor_x = x;
|
|
3884
3893
|
this.cursor_y = y;
|
|
3885
3894
|
}
|
|
3886
|
-
flip_cursor() {
|
|
3887
|
-
this.cursor_state = !this.cursor_state;
|
|
3888
|
-
setTimeout(() => this.flip_cursor(), this.cursor_rate);
|
|
3889
|
-
}
|
|
3890
3895
|
}
|
|
3891
3896
|
|
|
3892
3897
|
// src/lib/core/rk86_snapshot.ts
|
|
3898
|
+
function rk86_snapshot(machine, version) {
|
|
3899
|
+
const { screen, cpu, keyboard, memory } = machine;
|
|
3900
|
+
const h16 = (n) => "0x" + hex16(n);
|
|
3901
|
+
const snapshot = {
|
|
3902
|
+
id: "rk86",
|
|
3903
|
+
created: new Date().toISOString(),
|
|
3904
|
+
format: "1",
|
|
3905
|
+
emulator: "rk86.ru",
|
|
3906
|
+
version,
|
|
3907
|
+
start: h16(0),
|
|
3908
|
+
end: h16(65535),
|
|
3909
|
+
boot: { keyboard: [] },
|
|
3910
|
+
cpu: cpu.export(),
|
|
3911
|
+
keyboard: keyboard.export(),
|
|
3912
|
+
screen: screen.export(),
|
|
3913
|
+
memory: memory.export()
|
|
3914
|
+
};
|
|
3915
|
+
return JSON.stringify(snapshot, null, 4);
|
|
3916
|
+
}
|
|
3893
3917
|
function rk86_snapshot_restore(snapshot, machine, keys_injector) {
|
|
3894
3918
|
try {
|
|
3895
3919
|
const json = typeof snapshot === "string" ? JSON.parse(snapshot) : snapshot;
|
|
@@ -4344,15 +4368,19 @@ function printHelp() {
|
|
|
4344
4368
|
-m <\u0444\u0430\u0439\u043B> \u043C\u043E\u043D\u0438\u0442\u043E\u0440 (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E: \u0432\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u044B\u0439 mon32.bin)
|
|
4345
4369
|
-p \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0444\u0430\u0439\u043B \u0431\u0435\u0437 \u0437\u0430\u043F\u0443\u0441\u043A\u0430
|
|
4346
4370
|
-g <\u0430\u0434\u0440\u0435\u0441> \u0430\u0434\u0440\u0435\u0441 \u0437\u0430\u043F\u0443\u0441\u043A\u0430 (\u043D\u0435\u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C \u0441 -p)
|
|
4371
|
+
-G <\u0430\u0434\u0440\u0435\u0441> \u0437\u0430\u043F\u0443\u0441\u043A \u0447\u0435\u0440\u0435\u0437 \u043A\u043E\u043C\u0430\u043D\u0434\u0443 G \u043C\u043E\u043D\u0438\u0442\u043E\u0440\u0430 (\u0438\u043D\u044A\u0435\u043A\u0446\u0438\u044F \u043A\u043B\u0430\u0432\u0438\u0448)
|
|
4347
4372
|
--exit-halt \u0432\u044B\u0445\u043E\u0434 \u043F\u0440\u0438 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0438 HLT
|
|
4348
4373
|
--exit-address [\u0430\u0434\u0440\u0435\u0441] \u0432\u044B\u0445\u043E\u0434 \u043F\u0440\u0438 \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u0435 \u043D\u0430 \u0430\u0434\u0440\u0435\u0441 (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E: 0xFFFE)
|
|
4349
4374
|
--headless \u0431\u0435\u0437 \u043E\u0442\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F \u044D\u043A\u0440\u0430\u043D\u0430 (\u0434\u043B\u044F \u0430\u0432\u0442\u043E\u0442\u0435\u0441\u0442\u043E\u0432)
|
|
4375
|
+
--turbo \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435 \u0431\u0435\u0437 \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0438\u044F \u0441\u043A\u043E\u0440\u043E\u0441\u0442\u0438 (\u0434\u043B\u044F \u0430\u0432\u0442\u043E\u0442\u0435\u0441\u0442\u043E\u0432)
|
|
4350
4376
|
--timeout <\u0441\u0435\u043A> \u0432\u044B\u0445\u043E\u0434 \u043F\u043E \u0442\u0430\u0439\u043C\u0430\u0443\u0442\u0443
|
|
4351
4377
|
--memory <\u0444\u0430\u0439\u043B> \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u043F\u0430\u043C\u044F\u0442\u044C \u0432 \u0444\u0430\u0439\u043B \u043F\u0440\u0438 \u0432\u044B\u0445\u043E\u0434\u0435
|
|
4352
4378
|
--memory-from <\u0430\u0434\u0440\u0435\u0441> \u043D\u0430\u0447\u0430\u043B\u043E \u043E\u0431\u043B\u0430\u0441\u0442\u0438 \u0434\u0430\u043C\u043F\u0430 \u043F\u0430\u043C\u044F\u0442\u0438 (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E: 0x0000)
|
|
4353
4379
|
--memory-to <\u0430\u0434\u0440\u0435\u0441> \u043A\u043E\u043D\u0435\u0446 \u043E\u0431\u043B\u0430\u0441\u0442\u0438 \u0434\u0430\u043C\u043F\u0430 \u043F\u0430\u043C\u044F\u0442\u0438 \u0432\u043A\u043B\u044E\u0447\u0438\u0442\u0435\u043B\u044C\u043D\u043E (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E: 0xFFFF)
|
|
4354
4380
|
--screen <\u0444\u0430\u0439\u043B> \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u044D\u043A\u0440\u0430\u043D 78x30 \u043A\u0430\u043A \u0442\u0435\u043A\u0441\u0442 \u043F\u0440\u0438 \u0432\u044B\u0445\u043E\u0434\u0435
|
|
4381
|
+
--snapshot <\u0444\u0430\u0439\u043B> \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u0441\u043D\u0438\u043C\u043E\u043A \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F (JSON) \u043F\u0440\u0438 \u0432\u044B\u0445\u043E\u0434\u0435
|
|
4355
4382
|
--input <seq> \u0438\u043D\u044A\u0435\u043A\u0446\u0438\u044F \u043A\u043B\u0430\u0432\u0438\u0448 (\u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043F\u044F\u0442\u0443\u044E): KeyA,Digit1,Enter,...
|
|
4383
|
+
\u0442\u043E\u043A\u0435\u043D *N \u0437\u0430\u0434\u0430\u0451\u0442 \u043F\u0430\u0443\u0437\u0443 N \u043C\u0441 (\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 *200)
|
|
4356
4384
|
|
|
4357
4385
|
\u041F\u0440\u0438\u043C\u0435\u0440\u044B:
|
|
4358
4386
|
bunx rk86 \u0437\u0430\u043F\u0443\u0441\u043A \u043C\u043E\u043D\u0438\u0442\u043E\u0440\u0430
|
|
@@ -4424,6 +4452,7 @@ async function main() {
|
|
|
4424
4452
|
const exitAddr = exitAddrValue !== undefined;
|
|
4425
4453
|
const monitorFile_ = arg(args, "-m");
|
|
4426
4454
|
const headless = flag(args, "--headless");
|
|
4455
|
+
const turbo = flag(args, "--turbo");
|
|
4427
4456
|
const timeoutSec = arg(args, "--timeout", undefined, /^\d+(\.\d+)?$/, parseFloat);
|
|
4428
4457
|
const memoryFile = arg(args, "--memory");
|
|
4429
4458
|
const addrRe = /^(0x)?[0-9a-fA-F]+$/i;
|
|
@@ -4431,14 +4460,23 @@ async function main() {
|
|
|
4431
4460
|
const memoryFrom = arg(args, "--memory-from", undefined, addrRe, parseAddr) ?? 0;
|
|
4432
4461
|
const memoryTo = arg(args, "--memory-to", undefined, addrRe, parseAddr) ?? 65535;
|
|
4433
4462
|
const screenFile = arg(args, "--screen");
|
|
4434
|
-
const
|
|
4463
|
+
const snapshotFile = arg(args, "--snapshot");
|
|
4464
|
+
const goViaMonitor = arg(args, "-G", undefined, addrRe, parseAddr);
|
|
4465
|
+
let inputSeq = arg(args, "--input");
|
|
4466
|
+
if (goViaMonitor !== undefined) {
|
|
4467
|
+
const hex2 = goViaMonitor.toString(16).toUpperCase();
|
|
4468
|
+
const keys = [...hex2].map((c) => c >= "0" && c <= "9" ? `Digit${c}` : `Key${c}`);
|
|
4469
|
+
const gSeq = ["KeyG", ...keys, "Enter"].join(",");
|
|
4470
|
+
inputSeq = inputSeq ? `${inputSeq},${gSeq}` : gSeq;
|
|
4471
|
+
}
|
|
4435
4472
|
const programFile = args[0];
|
|
4436
4473
|
const keyboard = new Keyboard;
|
|
4437
4474
|
const io = new IO;
|
|
4438
4475
|
const machineBuilder = {
|
|
4439
4476
|
font: rk86_font_image(),
|
|
4440
4477
|
keyboard,
|
|
4441
|
-
io
|
|
4478
|
+
io,
|
|
4479
|
+
log: (...args2) => console.log(...args2)
|
|
4442
4480
|
};
|
|
4443
4481
|
const machine = machineBuilder;
|
|
4444
4482
|
machine.ui = new TerminalUI;
|
|
@@ -4511,6 +4549,8 @@ async function main() {
|
|
|
4511
4549
|
await writeFile(screenFile, dumpScreen(machine));
|
|
4512
4550
|
if (memoryFile)
|
|
4513
4551
|
await writeFile(memoryFile, new Uint8Array(machine.memory.buf.slice(memoryFrom, memoryTo + 1)));
|
|
4552
|
+
if (snapshotFile)
|
|
4553
|
+
await writeFile(snapshotFile, rk86_snapshot(machine, package_default.version));
|
|
4514
4554
|
if (!headless)
|
|
4515
4555
|
process.stdout.write("\x1B[?25h");
|
|
4516
4556
|
if (message !== null && !headless) {
|
|
@@ -4524,39 +4564,49 @@ async function main() {
|
|
|
4524
4564
|
renderer.update();
|
|
4525
4565
|
setTimeout(() => doExit(`\u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0430 \u0440\u0430\u0431\u043E\u0442\u0443 \u043D\u0430 ${hex16(machine.cpu.pc)}`), headless ? 0 : 1000);
|
|
4526
4566
|
} : undefined;
|
|
4527
|
-
const armed = { value: entryPoint === undefined };
|
|
4528
|
-
machine.runner.execute({
|
|
4529
|
-
terminate_address: exitAddr ? exitAddrValue : undefined,
|
|
4530
|
-
exit_on_halt: exitOnHalt,
|
|
4531
|
-
on_terminate: onTerminate,
|
|
4532
|
-
armed
|
|
4533
|
-
});
|
|
4534
4567
|
const armDelayMs = 500;
|
|
4535
4568
|
if (entryPoint !== undefined && !loadOnly) {
|
|
4536
4569
|
setTimeout(() => {
|
|
4537
4570
|
machine.cpu.jump(entryPoint);
|
|
4538
|
-
armed.value = true;
|
|
4539
4571
|
}, armDelayMs);
|
|
4540
4572
|
}
|
|
4573
|
+
const tickEvents = [];
|
|
4541
4574
|
if (inputSeq) {
|
|
4542
4575
|
const keys = inputSeq.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
4576
|
+
const TICKS_PER_MS = machine.runner.FREQ / 1000;
|
|
4543
4577
|
const settleMs = armDelayMs + 1000;
|
|
4544
4578
|
const keyDownMs = 50;
|
|
4545
4579
|
const keyGapMs = 50;
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
}
|
|
4557
|
-
|
|
4558
|
-
|
|
4580
|
+
let t = settleMs * TICKS_PER_MS;
|
|
4581
|
+
for (const token of keys) {
|
|
4582
|
+
if (token.startsWith("*")) {
|
|
4583
|
+
const delayMs = parseInt(token.slice(1), 10);
|
|
4584
|
+
if (!Number.isFinite(delayMs) || delayMs < 0) {
|
|
4585
|
+
console.error(`\u043D\u0435\u0432\u0435\u0440\u043D\u0430\u044F \u0437\u0430\u0434\u0435\u0440\u0436\u043A\u0430 \u0432 --input: ${token}`);
|
|
4586
|
+
process.exit(1);
|
|
4587
|
+
}
|
|
4588
|
+
t += delayMs * TICKS_PER_MS;
|
|
4589
|
+
continue;
|
|
4590
|
+
}
|
|
4591
|
+
const code = token;
|
|
4592
|
+
tickEvents.push({ at_ticks: t, action: () => keyboard.onkeydown(code) });
|
|
4593
|
+
t += keyDownMs * TICKS_PER_MS;
|
|
4594
|
+
tickEvents.push({ at_ticks: t, action: () => keyboard.onkeyup(code) });
|
|
4595
|
+
t += keyGapMs * TICKS_PER_MS;
|
|
4596
|
+
}
|
|
4559
4597
|
}
|
|
4598
|
+
machine.runner.execute({
|
|
4599
|
+
terminate_address: exitAddr ? exitAddrValue : undefined,
|
|
4600
|
+
exit_on_halt: exitOnHalt,
|
|
4601
|
+
on_terminate: onTerminate,
|
|
4602
|
+
turbo,
|
|
4603
|
+
on_batch_complete: () => {
|
|
4604
|
+
const now = machine.runner.total_ticks;
|
|
4605
|
+
while (tickEvents.length > 0 && tickEvents[0].at_ticks <= now) {
|
|
4606
|
+
tickEvents.shift().action();
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
});
|
|
4560
4610
|
if (timeoutSec !== undefined) {
|
|
4561
4611
|
setTimeout(() => doExit(`\u0432\u044B\u0445\u043E\u0434 \u043F\u043E \u0442\u0430\u0439\u043C\u0430\u0443\u0442\u0443 ${timeoutSec}\u0441`), timeoutSec * 1000);
|
|
4562
4612
|
}
|