rk86 2.0.30 → 2.0.31

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 (2) hide show
  1. package/package.json +2 -2
  2. package/rk86.js +127 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rk86",
3
- "version": "2.0.30",
3
+ "version": "2.0.31",
4
4
  "description": "Эмулятор Радио-86РК (Intel 8080) для терминала",
5
5
  "bin": {
6
6
  "rk86": "rk86.js"
@@ -18,6 +18,6 @@
18
18
  "license": "MIT",
19
19
  "repository": {
20
20
  "type": "git",
21
- "url": "https://github.com/begoon/rk86-js-web"
21
+ "url": "https://github.com/begoon/rk86-js"
22
22
  }
23
23
  }
package/rk86.js CHANGED
@@ -165,6 +165,7 @@ function preprocess(source) {
165
165
  const out = [];
166
166
  const stack = [];
167
167
  let counter = 0;
168
+ let procCounter = 0;
168
169
  let proc = null;
169
170
  for (let i = 0;i < lines.length; i++) {
170
171
  const line = lines[i];
@@ -223,7 +224,14 @@ function preprocess(source) {
223
224
  regs.push(up);
224
225
  }
225
226
  }
226
- proc = { regs, line: orig, source: line };
227
+ const id = procCounter++;
228
+ proc = {
229
+ regs,
230
+ line: orig,
231
+ source: line,
232
+ exitLabel: `__proc_${id}_exit`,
233
+ returnUsed: false
234
+ };
227
235
  out.push({ text: `${name}:`, orig });
228
236
  for (const r of regs) {
229
237
  out.push({ text: ` PUSH ${r}`, orig });
@@ -237,6 +245,9 @@ function preprocess(source) {
237
245
  if (!proc) {
238
246
  throw new AsmError(".endp without .proc", orig, line, firstNonSpaceCol(line));
239
247
  }
248
+ if (proc.returnUsed) {
249
+ out.push({ text: `${proc.exitLabel}:`, orig });
250
+ }
240
251
  out.push(...popsAndRet(proc.regs, orig));
241
252
  proc = null;
242
253
  continue;
@@ -245,7 +256,12 @@ function preprocess(source) {
245
256
  if (!proc) {
246
257
  throw new AsmError(".return outside .proc", orig, line, firstNonSpaceCol(line));
247
258
  }
248
- out.push(...popsAndRet(proc.regs, orig));
259
+ if (proc.regs.length === 0) {
260
+ out.push({ text: ` RET`, orig });
261
+ } else {
262
+ proc.returnUsed = true;
263
+ out.push({ text: ` JMP ${proc.exitLabel}`, orig });
264
+ }
249
265
  continue;
250
266
  }
251
267
  out.push({ text: line, orig });
@@ -742,6 +758,7 @@ function countDb(operands) {
742
758
  function asm(source) {
743
759
  const pp = preprocess(source);
744
760
  const symbols = new Map;
761
+ const pending = [];
745
762
  let pc = 0;
746
763
  let lastLabel = "";
747
764
  let ended = false;
@@ -760,7 +777,7 @@ function asm(source) {
760
777
  lastLabel = parts.label;
761
778
  }
762
779
  if (parts.isEqu) {
763
- symbols.set(labelName.toUpperCase(), evalExpr(parts.operands[0], symbols, pc, lastLabel));
780
+ tryDefineEqu(symbols, pending, labelName, parts.operands[0], pc, lastLabel, orig, line);
764
781
  continue;
765
782
  }
766
783
  symbols.set(labelName.toUpperCase(), pc);
@@ -800,6 +817,7 @@ function asm(source) {
800
817
  throw new AsmError(e.message, orig, line, firstNonSpaceCol(line));
801
818
  }
802
819
  }
820
+ resolvePendingEqus(symbols, pending);
803
821
  const sections = [];
804
822
  let current = null;
805
823
  const sectionNames = new Set;
@@ -861,6 +879,50 @@ function asm(source) {
861
879
  }
862
880
  return sections;
863
881
  }
882
+ function isUnknownSymbolErr(e) {
883
+ return e instanceof Error && /^unknown symbol:/.test(e.message);
884
+ }
885
+ function tryDefineEqu(symbols, pending, name, expr, pc, lastLabel, orig, line) {
886
+ try {
887
+ symbols.set(name.toUpperCase(), evalExpr(expr, symbols, pc, lastLabel));
888
+ } catch (e) {
889
+ if (isUnknownSymbolErr(e)) {
890
+ pending.push({ name, expr, pc, lastLabel, orig, line });
891
+ } else {
892
+ throw e;
893
+ }
894
+ }
895
+ }
896
+ function resolvePendingEqus(symbols, pending) {
897
+ while (pending.length > 0) {
898
+ let progress = false;
899
+ const next = [];
900
+ for (const p of pending) {
901
+ try {
902
+ symbols.set(p.name.toUpperCase(), evalExpr(p.expr, symbols, p.pc, p.lastLabel));
903
+ progress = true;
904
+ } catch (e) {
905
+ if (isUnknownSymbolErr(e)) {
906
+ next.push(p);
907
+ } else {
908
+ throw new AsmError(e.message, p.orig, p.line, firstNonSpaceCol(p.line));
909
+ }
910
+ }
911
+ }
912
+ if (!progress) {
913
+ const p = next[0];
914
+ try {
915
+ evalExpr(p.expr, symbols, p.pc, p.lastLabel);
916
+ } catch (e) {
917
+ throw new AsmError(e.message, p.orig, p.line, firstNonSpaceCol(p.line));
918
+ }
919
+ return;
920
+ }
921
+ pending.length = 0;
922
+ pending.push(...next);
923
+ }
924
+ }
925
+ var DATA_DIRECTIVES = new Set(["DB", "DW", "DS"]);
864
926
  if (false) {}
865
927
 
866
928
  // src/lib/terminal/rk86_terminal.ts
@@ -871,7 +933,7 @@ import { basename } from "path";
871
933
  // packages/rk86/package.json
872
934
  var package_default = {
873
935
  name: "rk86",
874
- version: "2.0.29",
936
+ version: "2.0.30",
875
937
  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",
876
938
  bin: {
877
939
  rk86: "rk86.js"
@@ -889,7 +951,7 @@ var package_default = {
889
951
  license: "MIT",
890
952
  repository: {
891
953
  type: "git",
892
- url: "https://github.com/begoon/rk86-js-web"
954
+ url: "https://github.com/begoon/rk86-js"
893
955
  }
894
956
  };
895
957
 
@@ -2261,6 +2323,8 @@ class Memory {
2261
2323
  return;
2262
2324
  }
2263
2325
  if (vg75_reg === 49152 && this.vg75_c001_00_cmd === 3) {
2326
+ this.machine.screen.set_char_height((byte & 15) + 1);
2327
+ this.machine.screen.underline_scanline = byte >> 4 & 15;
2264
2328
  this.vg75_c001_00_cmd += 1;
2265
2329
  return;
2266
2330
  }
@@ -2354,6 +2418,9 @@ class Memory {
2354
2418
  // src/lib/core/rk86_runner.ts
2355
2419
  class Runner {
2356
2420
  paused = false;
2421
+ turbo = false;
2422
+ hardware_id_enabled = false;
2423
+ stc_streak = 0;
2357
2424
  tracer = null;
2358
2425
  last_instructions = [];
2359
2426
  previous_batch_time = 0;
@@ -2375,7 +2442,7 @@ class Runner {
2375
2442
  this.machine.cpu.jump(63488);
2376
2443
  }
2377
2444
  interrupt(iff) {
2378
- if (!this.sound)
2445
+ if (!this.sound || this.turbo)
2379
2446
  return;
2380
2447
  if (this.last_iff == iff)
2381
2448
  return;
@@ -2400,8 +2467,11 @@ class Runner {
2400
2467
  }
2401
2468
  }
2402
2469
  execute(options = {}) {
2403
- const { terminate_address, on_terminate, exit_on_halt, on_halt, on_batch_complete, turbo } = options;
2470
+ const { terminate_address, on_terminate, exit_on_halt, on_halt, on_batch_complete } = options;
2471
+ if (options.turbo !== undefined)
2472
+ this.turbo = options.turbo;
2404
2473
  clearTimeout(this.execute_timer);
2474
+ const turbo = this.turbo;
2405
2475
  const bursts = turbo ? 100 : 1;
2406
2476
  for (let burst = 0;burst < bursts; burst++) {
2407
2477
  if (this.paused)
@@ -2418,10 +2488,21 @@ class Runner {
2418
2488
  if (this.last_instructions.length > 5) {
2419
2489
  this.last_instructions.shift();
2420
2490
  }
2491
+ const opcode_pc = this.machine.cpu.pc;
2421
2492
  this.machine.memory.invalidate_access_variables();
2422
2493
  const instruction_ticks = this.machine.cpu.instruction();
2423
2494
  batch_ticks += instruction_ticks;
2424
2495
  this.total_ticks += instruction_ticks;
2496
+ if (this.hardware_id_enabled) {
2497
+ if (this.machine.memory.read_raw(opcode_pc) === 55) {
2498
+ if (++this.stc_streak >= 4) {
2499
+ this.stc_streak = 0;
2500
+ this.fire_hardware_id();
2501
+ }
2502
+ } else {
2503
+ this.stc_streak = 0;
2504
+ }
2505
+ }
2425
2506
  if (this.tracer) {
2426
2507
  this.tracer("after");
2427
2508
  if (this.paused)
@@ -2464,6 +2545,13 @@ class Runner {
2464
2545
  this.machine.cpu.jump(63488);
2465
2546
  this.machine.keyboard.reset();
2466
2547
  }
2548
+ fire_hardware_id() {
2549
+ const colorIdx = COLOR_MODES.indexOf(this.machine.screen.color_mode);
2550
+ this.machine.cpu.set_a(1);
2551
+ this.machine.cpu.set_b(colorIdx < 0 ? 0 : colorIdx);
2552
+ this.machine.cpu.set_c(this.turbo ? 1 : 0);
2553
+ this.machine.cpu.cf = 0;
2554
+ }
2467
2555
  }
2468
2556
 
2469
2557
  // src/lib/core/rk86_screen.ts
@@ -2484,6 +2572,8 @@ class Screen {
2484
2572
  video_memory_base = 0;
2485
2573
  video_memory_size = 0;
2486
2574
  transparent_attr = false;
2575
+ char_height = 10;
2576
+ underline_scanline = 7;
2487
2577
  color_mode = DEFAULT_COLOR_MODE;
2488
2578
  ready = false;
2489
2579
  renderer;
@@ -2544,6 +2634,11 @@ class Screen {
2544
2634
  }
2545
2635
  last_flip_ticks = 0;
2546
2636
  tick_cursor(total_ticks, ticks_per_flip) {
2637
+ if (this.machine.runner.turbo) {
2638
+ this.cursor_state = true;
2639
+ this.last_flip_ticks = total_ticks;
2640
+ return;
2641
+ }
2547
2642
  while (total_ticks - this.last_flip_ticks >= ticks_per_flip) {
2548
2643
  this.cursor_state = !this.cursor_state;
2549
2644
  this.last_flip_ticks += ticks_per_flip;
@@ -2560,7 +2655,7 @@ class Screen {
2560
2655
  this.width = width;
2561
2656
  this.height = height;
2562
2657
  this.video_memory_size = width * height;
2563
- this.machine.ui.update_screen_geometry(this.width, this.height);
2658
+ this.machine.ui.update_screen_geometry(this.width, this.height, this.char_height);
2564
2659
  if (this.last_width === this.width && this.last_height === this.height)
2565
2660
  return;
2566
2661
  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}`);
@@ -2569,6 +2664,12 @@ class Screen {
2569
2664
  if (this.last_video_memory_base !== -1)
2570
2665
  this.ready = true;
2571
2666
  }
2667
+ set_char_height(char_height) {
2668
+ if (this.char_height === char_height)
2669
+ return;
2670
+ this.char_height = char_height;
2671
+ this.machine.ui.update_screen_geometry(this.width, this.height, this.char_height);
2672
+ }
2572
2673
  last_video_memory_base = -1;
2573
2674
  set_video_memory(base) {
2574
2675
  this.video_memory_base = base;
@@ -2992,21 +3093,34 @@ class TerminalRenderer {
2992
3093
  addr += w;
2993
3094
  }
2994
3095
  let prevAnsi = -1;
3096
+ let prevReverse = false;
3097
+ let prevUnderline = false;
2995
3098
  for (let x = 0;x < w; x++) {
2996
3099
  const cell = cells[x];
2997
3100
  const ch = cell.blink && blinkOff ? 0 : cell.ch;
2998
3101
  const glyph = rk86char(ch);
2999
- let attrs = cell.attrs;
3102
+ let colorAttrs = cell.attrs;
3000
3103
  if (offset && x + 1 < w && cells[x + 1].isFA) {
3001
- attrs = cells[x + 1].attrs;
3104
+ colorAttrs = cells[x + 1].attrs;
3002
3105
  }
3003
- const ansi = rgbToAnsiBaseFg(attrToRgb(mode, attrs));
3106
+ const ansi = rgbToAnsiBaseFg(attrToRgb(mode, colorAttrs));
3107
+ const reverse = !cell.isFA && (cell.attrs & 16) !== 0;
3108
+ const underline = !cell.isFA && (cell.attrs & 32) !== 0;
3004
3109
  if (ansi !== prevAnsi) {
3005
3110
  line += `\x1B[${ansi}m`;
3006
3111
  prevAnsi = ansi;
3007
3112
  }
3113
+ if (reverse !== prevReverse) {
3114
+ line += reverse ? `\x1B[7m` : `\x1B[27m`;
3115
+ prevReverse = reverse;
3116
+ }
3117
+ if (underline !== prevUnderline) {
3118
+ line += underline ? `\x1B[4m` : `\x1B[24m`;
3119
+ prevUnderline = underline;
3120
+ }
3008
3121
  if (x === screen.cursor_x && y === screen.cursor_y) {
3009
3122
  line += `\x1B[4m${glyph}\x1B[24m`;
3123
+ prevUnderline = false;
3010
3124
  } else {
3011
3125
  line += glyph;
3012
3126
  }
@@ -3299,6 +3413,8 @@ async function main() {
3299
3413
  const machine = machineBuilder;
3300
3414
  machine.ui = new TerminalUI;
3301
3415
  machine.memory = new Memory(machine);
3416
+ io.input = (port) => machine.memory.read(port | port << 8);
3417
+ io.output = (port, w8) => machine.memory.write(port | port << 8, w8);
3302
3418
  machine.cpu = new I8080(machine);
3303
3419
  machine.screen = new Screen(machine);
3304
3420
  machine.screen.color_mode = colorMode;