rk86 2.0.6 → 2.0.8
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/package.json +1 -1
- package/rk86.js +69 -149
package/package.json
CHANGED
package/rk86.js
CHANGED
|
@@ -2468,6 +2468,18 @@ var extract_rk86_word = function(v, i) {
|
|
|
2468
2468
|
};
|
|
2469
2469
|
var to_text = (binary) => binary.reduce((a, x) => a + String.fromCharCode(x), "");
|
|
2470
2470
|
var is_hex_file = (image) => to_text(image.slice(0, 6)) === "#!rk86";
|
|
2471
|
+
var parse = (binary) => {
|
|
2472
|
+
try {
|
|
2473
|
+
if (!binary)
|
|
2474
|
+
return { ok: false };
|
|
2475
|
+
if (binary instanceof Uint8Array)
|
|
2476
|
+
binary = Array.from(binary);
|
|
2477
|
+
const text = to_text(binary);
|
|
2478
|
+
return { ok: true, json: JSON.parse(text) };
|
|
2479
|
+
} catch {
|
|
2480
|
+
return { ok: false };
|
|
2481
|
+
}
|
|
2482
|
+
};
|
|
2471
2483
|
var convert_hex_to_binary = function(text) {
|
|
2472
2484
|
const lines = text.split(`
|
|
2473
2485
|
`).filter((line) => line.trim().length).filter((line) => !line.startsWith(";") && !line.startsWith("#"));
|
|
@@ -2659,7 +2671,7 @@ function create(array, width = 16) {
|
|
|
2659
2671
|
}
|
|
2660
2672
|
return v;
|
|
2661
2673
|
}
|
|
2662
|
-
function
|
|
2674
|
+
function parse2(hex2) {
|
|
2663
2675
|
const array = [];
|
|
2664
2676
|
for (let [label, line] of Object.entries(hex2)) {
|
|
2665
2677
|
const address = parseInt(label.slice(1), 16);
|
|
@@ -2773,7 +2785,7 @@ class Memory {
|
|
|
2773
2785
|
this.video_screen_cursor_y = snapshot.video_screen_cursor_y;
|
|
2774
2786
|
this.last_access_address = h(snapshot.last_access_address);
|
|
2775
2787
|
this.last_access_operation = snapshot.last_access_operation;
|
|
2776
|
-
this.buf =
|
|
2788
|
+
this.buf = parse2(snapshot.memory);
|
|
2777
2789
|
};
|
|
2778
2790
|
invalidate_access_variables() {
|
|
2779
2791
|
this.last_access_address = 0;
|
|
@@ -3103,11 +3115,6 @@ class Screen {
|
|
|
3103
3115
|
static #update_rate = 25;
|
|
3104
3116
|
machine;
|
|
3105
3117
|
cursor_rate;
|
|
3106
|
-
char_width;
|
|
3107
|
-
char_height;
|
|
3108
|
-
char_height_gap;
|
|
3109
|
-
cursor_width;
|
|
3110
|
-
cursor_height;
|
|
3111
3118
|
scale_x;
|
|
3112
3119
|
scale_y;
|
|
3113
3120
|
width;
|
|
@@ -3115,22 +3122,15 @@ class Screen {
|
|
|
3115
3122
|
cursor_state;
|
|
3116
3123
|
cursor_x;
|
|
3117
3124
|
cursor_y;
|
|
3118
|
-
last_cursor_state;
|
|
3119
|
-
last_cursor_x;
|
|
3120
|
-
last_cursor_y;
|
|
3121
|
-
font;
|
|
3122
3125
|
light_pen_x;
|
|
3123
3126
|
light_pen_y;
|
|
3124
3127
|
light_pen_active;
|
|
3125
|
-
|
|
3128
|
+
video_memory_base = 0;
|
|
3129
|
+
video_memory_size = 0;
|
|
3130
|
+
renderer;
|
|
3126
3131
|
constructor(machine) {
|
|
3127
3132
|
this.machine = machine;
|
|
3128
3133
|
this.cursor_rate = 500;
|
|
3129
|
-
this.char_width = 6;
|
|
3130
|
-
this.char_height = 8;
|
|
3131
|
-
this.char_height_gap = 2;
|
|
3132
|
-
this.cursor_width = this.char_width;
|
|
3133
|
-
this.cursor_height = 1;
|
|
3134
3134
|
this.scale_x = 1;
|
|
3135
3135
|
this.scale_y = 1;
|
|
3136
3136
|
this.width = 78;
|
|
@@ -3138,11 +3138,6 @@ class Screen {
|
|
|
3138
3138
|
this.cursor_state = false;
|
|
3139
3139
|
this.cursor_x = 0;
|
|
3140
3140
|
this.cursor_y = 0;
|
|
3141
|
-
this.last_cursor_state = false;
|
|
3142
|
-
this.last_cursor_x = 0;
|
|
3143
|
-
this.last_cursor_y = 0;
|
|
3144
|
-
this.font = new Image;
|
|
3145
|
-
this.font.src = this.machine.font;
|
|
3146
3141
|
this.light_pen_x = 0;
|
|
3147
3142
|
this.light_pen_y = 0;
|
|
3148
3143
|
this.light_pen_active = 0;
|
|
@@ -3183,73 +3178,32 @@ class Screen {
|
|
|
3183
3178
|
this.set_geometry(this.width, this.height);
|
|
3184
3179
|
this.set_video_memory(this.video_memory_base);
|
|
3185
3180
|
}
|
|
3186
|
-
start() {
|
|
3187
|
-
this.
|
|
3188
|
-
this.
|
|
3181
|
+
start(renderer) {
|
|
3182
|
+
this.renderer = renderer;
|
|
3183
|
+
this.renderer.connect(this.machine);
|
|
3189
3184
|
this.flip_cursor();
|
|
3190
|
-
this.
|
|
3191
|
-
this.machine.ui.canvas.onmouseup = () => this.light_pen_active = 0;
|
|
3192
|
-
this.machine.ui.canvas.onmousedown = () => this.light_pen_active = 1;
|
|
3193
|
-
}
|
|
3194
|
-
cache = [];
|
|
3195
|
-
init_cache(sz) {
|
|
3196
|
-
for (let i = 0;i < sz; ++i)
|
|
3197
|
-
this.cache[i] = -1;
|
|
3198
|
-
}
|
|
3199
|
-
draw_char(x, y, ch) {
|
|
3200
|
-
this.ctx.drawImage(this.font, 2, this.char_height * ch, this.char_width, this.char_height, x * this.char_width * this.scale_x, y * (this.char_height + this.char_height_gap) * this.scale_y, this.char_width * this.scale_x, this.char_height * this.scale_y);
|
|
3201
|
-
}
|
|
3202
|
-
draw_cursor(x, y, visible) {
|
|
3203
|
-
const cy = (y2) => (y2 * (this.char_height + this.char_height_gap) + this.char_height) * this.scale_y;
|
|
3204
|
-
if (this.last_cursor_x !== x || this.last_cursor_y !== y) {
|
|
3205
|
-
if (this.last_cursor_state) {
|
|
3206
|
-
this.ctx.fillStyle = "#000000";
|
|
3207
|
-
this.ctx.fillRect(this.last_cursor_x * this.char_width * this.scale_x, cy(this.last_cursor_y), this.cursor_width * this.scale_x, this.cursor_height * this.scale_y);
|
|
3208
|
-
}
|
|
3209
|
-
this.last_cursor_state = this.cursor_state;
|
|
3210
|
-
this.last_cursor_x = x;
|
|
3211
|
-
this.last_cursor_y = y;
|
|
3212
|
-
}
|
|
3213
|
-
const cx = x * this.char_width * this.scale_x;
|
|
3214
|
-
this.ctx.fillStyle = visible ? "#ffffff" : "#000000";
|
|
3215
|
-
this.ctx.fillRect(cx, cy(y), this.cursor_width * this.scale_x, this.cursor_height * this.scale_y);
|
|
3185
|
+
this.render_loop();
|
|
3216
3186
|
}
|
|
3217
|
-
|
|
3218
|
-
this.
|
|
3219
|
-
|
|
3220
|
-
setTimeout(() => this.flip_cursor(), this.cursor_rate);
|
|
3221
|
-
}
|
|
3222
|
-
init() {
|
|
3223
|
-
this.ctx = this.machine.ui.canvas.getContext("2d");
|
|
3224
|
-
}
|
|
3225
|
-
disable_smoothing() {
|
|
3226
|
-
this.ctx.imageSmoothingEnabled = false;
|
|
3187
|
+
render_loop() {
|
|
3188
|
+
this.renderer.update();
|
|
3189
|
+
setTimeout(() => this.render_loop(), Screen.#update_rate);
|
|
3227
3190
|
}
|
|
3228
3191
|
last_width = 0;
|
|
3229
3192
|
last_height = 0;
|
|
3230
|
-
video_memory_size = 0;
|
|
3231
3193
|
set_geometry(width, height) {
|
|
3232
3194
|
this.width = width;
|
|
3233
3195
|
this.height = height;
|
|
3234
3196
|
this.video_memory_size = width * height;
|
|
3235
3197
|
this.machine.ui.update_screen_geometry(this.width, this.height);
|
|
3236
|
-
const canvas_width = this.width * this.char_width * this.scale_x;
|
|
3237
|
-
const canvas_height = this.height * (this.char_height + this.char_height_gap) * this.scale_y;
|
|
3238
|
-
this.machine.ui.resize_canvas(canvas_width, canvas_height);
|
|
3239
|
-
this.disable_smoothing();
|
|
3240
|
-
this.ctx.fillStyle = "#000000";
|
|
3241
|
-
this.ctx.fillRect(0, 0, canvas_width, canvas_height);
|
|
3242
3198
|
if (this.last_width === this.width && this.last_height === this.height)
|
|
3243
3199
|
return;
|
|
3244
3200
|
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}`);
|
|
3245
3201
|
this.last_width = this.width;
|
|
3246
3202
|
this.last_height = this.height;
|
|
3247
3203
|
}
|
|
3248
|
-
video_memory_base = 0;
|
|
3249
3204
|
last_video_memory_base = 0;
|
|
3250
3205
|
set_video_memory(base) {
|
|
3251
3206
|
this.video_memory_base = base;
|
|
3252
|
-
this.init_cache(this.video_memory_size);
|
|
3253
3207
|
this.machine.ui.update_video_memory_address(this.video_memory_base);
|
|
3254
3208
|
if (this.last_video_memory_base === this.video_memory_base)
|
|
3255
3209
|
return;
|
|
@@ -3257,37 +3211,35 @@ class Screen {
|
|
|
3257
3211
|
this.last_video_memory_base = this.video_memory_base;
|
|
3258
3212
|
}
|
|
3259
3213
|
set_cursor(x, y) {
|
|
3260
|
-
this.draw_cursor(this.cursor_x, this.cursor_y, false);
|
|
3261
3214
|
this.cursor_x = x;
|
|
3262
3215
|
this.cursor_y = y;
|
|
3263
3216
|
}
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
this.light_pen_y = y;
|
|
3217
|
+
flip_cursor() {
|
|
3218
|
+
this.cursor_state = !this.cursor_state;
|
|
3219
|
+
setTimeout(() => this.flip_cursor(), this.cursor_rate);
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
|
|
3223
|
+
// src/lib/rk86_snapshot.ts
|
|
3224
|
+
function rk86_snapshot_restore(snapshot, machine, keys_injector) {
|
|
3225
|
+
try {
|
|
3226
|
+
const json = typeof snapshot === "string" ? JSON.parse(snapshot) : snapshot;
|
|
3227
|
+
if (json.id != "rk86")
|
|
3228
|
+
return false;
|
|
3229
|
+
if (!machine)
|
|
3230
|
+
return false;
|
|
3231
|
+
const { screen, cpu, memory, keyboard } = machine;
|
|
3232
|
+
cpu.import(json.cpu);
|
|
3233
|
+
keyboard.import(json.keyboard);
|
|
3234
|
+
screen.import(json.screen);
|
|
3235
|
+
memory.import(json.memory);
|
|
3236
|
+
screen.apply_import();
|
|
3237
|
+
if (keys_injector && json.boot?.keyboard)
|
|
3238
|
+
keys_injector(json.boot?.keyboard);
|
|
3239
|
+
return true;
|
|
3240
|
+
} catch (e) {
|
|
3241
|
+
console.error("failed restoring snapshot", e);
|
|
3242
|
+
return false;
|
|
3291
3243
|
}
|
|
3292
3244
|
}
|
|
3293
3245
|
|
|
@@ -3393,8 +3345,6 @@ class Tape {
|
|
|
3393
3345
|
}
|
|
3394
3346
|
|
|
3395
3347
|
// src/lib/rk86_terminal.ts
|
|
3396
|
-
globalThis.Image = class {
|
|
3397
|
-
};
|
|
3398
3348
|
var charMap = {
|
|
3399
3349
|
0: " ",
|
|
3400
3350
|
1: "\u2598",
|
|
@@ -3560,35 +3510,25 @@ class IO {
|
|
|
3560
3510
|
interrupt = (_iff) => {};
|
|
3561
3511
|
}
|
|
3562
3512
|
|
|
3563
|
-
class
|
|
3513
|
+
class TerminalRenderer {
|
|
3564
3514
|
machine;
|
|
3565
|
-
|
|
3566
|
-
height = 30;
|
|
3567
|
-
video_memory_base = 0;
|
|
3568
|
-
timer;
|
|
3569
|
-
constructor(machine) {
|
|
3515
|
+
connect(machine) {
|
|
3570
3516
|
this.machine = machine;
|
|
3571
3517
|
}
|
|
3572
|
-
|
|
3573
|
-
this.render();
|
|
3574
|
-
}
|
|
3575
|
-
render() {
|
|
3518
|
+
update() {
|
|
3576
3519
|
const { memory, screen } = this.machine;
|
|
3577
|
-
const cursorX = screen.cursor_x;
|
|
3578
|
-
const cursorY = screen.cursor_y;
|
|
3579
|
-
const cursorVisible = screen.cursor_state;
|
|
3580
3520
|
const dim = "\x1B[2m";
|
|
3581
3521
|
const reset = "\x1B[0m";
|
|
3582
|
-
const w =
|
|
3522
|
+
const w = screen.width;
|
|
3583
3523
|
let output = "\x1B[H";
|
|
3584
3524
|
output += `${dim}\u250C${"\u2500".repeat(w)}\u2510${reset}
|
|
3585
3525
|
`;
|
|
3586
|
-
let addr =
|
|
3587
|
-
for (let y = 0;y <
|
|
3526
|
+
let addr = screen.video_memory_base;
|
|
3527
|
+
for (let y = 0;y < screen.height; y++) {
|
|
3588
3528
|
let line = `${dim}\u2502${reset}`;
|
|
3589
3529
|
for (let x = 0;x < w; x++) {
|
|
3590
3530
|
const ch = rk86char(memory.read(addr));
|
|
3591
|
-
if (x ===
|
|
3531
|
+
if (x === screen.cursor_x && y === screen.cursor_y) {
|
|
3592
3532
|
line += `\x1B[4m${ch}${reset}`;
|
|
3593
3533
|
} else {
|
|
3594
3534
|
line += ch;
|
|
@@ -3602,7 +3542,6 @@ class TerminalScreen {
|
|
|
3602
3542
|
output += `${dim}\u2514${"\u2500".repeat(w)}\u2518${reset}
|
|
3603
3543
|
`;
|
|
3604
3544
|
process.stdout.write(output);
|
|
3605
|
-
this.timer = setTimeout(() => this.render(), 40);
|
|
3606
3545
|
}
|
|
3607
3546
|
}
|
|
3608
3547
|
var KEY_MAP = {
|
|
@@ -3780,42 +3719,23 @@ async function main() {
|
|
|
3780
3719
|
let entryPoint;
|
|
3781
3720
|
if (programFile) {
|
|
3782
3721
|
const content = await fetchFile(programFile);
|
|
3783
|
-
const
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3722
|
+
const { ok, json } = parse(content);
|
|
3723
|
+
if (ok) {
|
|
3724
|
+
rk86_snapshot_restore(json, machine);
|
|
3725
|
+
entryPoint = parseInt(json.cpu.pc);
|
|
3726
|
+
console.error(`\u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D \u043E\u0431\u0440\u0430\u0437: ${programFile} (PC=${entryPoint.toString(16)})`);
|
|
3727
|
+
} else {
|
|
3728
|
+
const file = parse_rk86_binary(programFile, content);
|
|
3729
|
+
machine.memory.load_file(file);
|
|
3730
|
+
entryPoint = file.entry;
|
|
3731
|
+
console.error(`\u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D: ${programFile} (${file.start.toString(16)}-${file.end.toString(16)}, G${file.entry.toString(16)})`);
|
|
3732
|
+
}
|
|
3787
3733
|
}
|
|
3788
3734
|
process.stdout.write("\x1B[?25l");
|
|
3789
3735
|
process.stdout.write("\x1B[2J");
|
|
3790
3736
|
setupKeyboard(keyboard);
|
|
3791
|
-
|
|
3792
|
-
const origSetGeometry = machine.screen.set_geometry.bind(machine.screen);
|
|
3793
|
-
machine.screen.set_geometry = (width, height) => {
|
|
3794
|
-
origSetGeometry(width, height);
|
|
3795
|
-
termScreen.width = width;
|
|
3796
|
-
termScreen.height = height;
|
|
3797
|
-
};
|
|
3798
|
-
const origSetVideoMemory = machine.screen.set_video_memory.bind(machine.screen);
|
|
3799
|
-
machine.screen.set_video_memory = (base) => {
|
|
3800
|
-
origSetVideoMemory(base);
|
|
3801
|
-
termScreen.video_memory_base = base;
|
|
3802
|
-
};
|
|
3803
|
-
const noopCtx = {
|
|
3804
|
-
imageSmoothingEnabled: false,
|
|
3805
|
-
fillStyle: "",
|
|
3806
|
-
fillRect() {},
|
|
3807
|
-
drawImage() {},
|
|
3808
|
-
clearRect() {}
|
|
3809
|
-
};
|
|
3810
|
-
machine.screen.ctx = noopCtx;
|
|
3811
|
-
machine.screen.init = () => {
|
|
3812
|
-
machine.screen.ctx = noopCtx;
|
|
3813
|
-
};
|
|
3814
|
-
machine.screen.draw_screen = () => {};
|
|
3815
|
-
machine.screen.draw_cursor = () => {};
|
|
3816
|
-
machine.screen.start();
|
|
3737
|
+
machine.screen.start(new TerminalRenderer);
|
|
3817
3738
|
machine.runner.execute();
|
|
3818
|
-
termScreen.start();
|
|
3819
3739
|
if (entryPoint !== undefined && !loadOnly) {
|
|
3820
3740
|
setTimeout(() => machine.cpu.jump(entryPoint), 500);
|
|
3821
3741
|
}
|