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.
Files changed (3) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. 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` содержит приглашение `-->D0,FF` и 16 строк шестнадцатеричного
71
- дампа `0000..00F0`.
70
+ После выхода `out.txt` содержит приглашение `-->DF800,F8FF` и 16 строк шестнадцатеричного
71
+ дампа `F800..F8FF`.
72
72
 
73
73
  ### Пример 2. Запись HLT и запуск через команды `M` / `G`
74
74
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rk86",
3
- "version": "2.0.17",
3
+ "version": "2.0.19",
4
4
  "description": "Эмулятор Радио-86РК (Intel 8080) для терминала",
5
5
  "bin": {
6
6
  "rk86": "rk86.js"
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.16",
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
- console.log("\u0437\u0432\u0443\u043A \u0432\u043A\u043B\u044E\u0447\u0435\u043D");
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
- console.log("\u0437\u0432\u0443\u043A \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D");
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, armed } = options;
3717
+ const { terminate_address, on_terminate, exit_on_halt, on_batch_complete, turbo } = options;
3718
3718
  clearTimeout(this.execute_timer);
3719
- if (!this.paused) {
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
- console.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}`);
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
- console.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)}`);
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 inputSeq = arg(args, "--input");
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
- setTimeout(() => {
4547
- const pressNext = (i) => {
4548
- if (i >= keys.length)
4549
- return;
4550
- const code = keys[i];
4551
- keyboard.onkeydown(code);
4552
- setTimeout(() => {
4553
- keyboard.onkeyup(code);
4554
- setTimeout(() => pressNext(i + 1), keyGapMs);
4555
- }, keyDownMs);
4556
- };
4557
- pressNext(0);
4558
- }, settleMs);
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
  }