rk86 2.0.5 → 2.0.7
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 +32 -164
package/package.json
CHANGED
package/rk86.js
CHANGED
|
@@ -1588,6 +1588,10 @@ var init_catalog_data = __esm(() => {
|
|
|
1588
1588
|
];
|
|
1589
1589
|
});
|
|
1590
1590
|
|
|
1591
|
+
// src/lib/rk86_terminal.ts
|
|
1592
|
+
import { existsSync } from "fs";
|
|
1593
|
+
import { readFile } from "fs/promises";
|
|
1594
|
+
|
|
1591
1595
|
// src/lib/hex.ts
|
|
1592
1596
|
function hex(v, prefix) {
|
|
1593
1597
|
return v.toString(16).toUpperCase();
|
|
@@ -3099,11 +3103,6 @@ class Screen {
|
|
|
3099
3103
|
static #update_rate = 25;
|
|
3100
3104
|
machine;
|
|
3101
3105
|
cursor_rate;
|
|
3102
|
-
char_width;
|
|
3103
|
-
char_height;
|
|
3104
|
-
char_height_gap;
|
|
3105
|
-
cursor_width;
|
|
3106
|
-
cursor_height;
|
|
3107
3106
|
scale_x;
|
|
3108
3107
|
scale_y;
|
|
3109
3108
|
width;
|
|
@@ -3111,22 +3110,15 @@ class Screen {
|
|
|
3111
3110
|
cursor_state;
|
|
3112
3111
|
cursor_x;
|
|
3113
3112
|
cursor_y;
|
|
3114
|
-
last_cursor_state;
|
|
3115
|
-
last_cursor_x;
|
|
3116
|
-
last_cursor_y;
|
|
3117
|
-
font;
|
|
3118
3113
|
light_pen_x;
|
|
3119
3114
|
light_pen_y;
|
|
3120
3115
|
light_pen_active;
|
|
3121
|
-
|
|
3116
|
+
video_memory_base = 0;
|
|
3117
|
+
video_memory_size = 0;
|
|
3118
|
+
renderer;
|
|
3122
3119
|
constructor(machine) {
|
|
3123
3120
|
this.machine = machine;
|
|
3124
3121
|
this.cursor_rate = 500;
|
|
3125
|
-
this.char_width = 6;
|
|
3126
|
-
this.char_height = 8;
|
|
3127
|
-
this.char_height_gap = 2;
|
|
3128
|
-
this.cursor_width = this.char_width;
|
|
3129
|
-
this.cursor_height = 1;
|
|
3130
3122
|
this.scale_x = 1;
|
|
3131
3123
|
this.scale_y = 1;
|
|
3132
3124
|
this.width = 78;
|
|
@@ -3134,11 +3126,6 @@ class Screen {
|
|
|
3134
3126
|
this.cursor_state = false;
|
|
3135
3127
|
this.cursor_x = 0;
|
|
3136
3128
|
this.cursor_y = 0;
|
|
3137
|
-
this.last_cursor_state = false;
|
|
3138
|
-
this.last_cursor_x = 0;
|
|
3139
|
-
this.last_cursor_y = 0;
|
|
3140
|
-
this.font = new Image;
|
|
3141
|
-
this.font.src = this.machine.font;
|
|
3142
3129
|
this.light_pen_x = 0;
|
|
3143
3130
|
this.light_pen_y = 0;
|
|
3144
3131
|
this.light_pen_active = 0;
|
|
@@ -3179,73 +3166,32 @@ class Screen {
|
|
|
3179
3166
|
this.set_geometry(this.width, this.height);
|
|
3180
3167
|
this.set_video_memory(this.video_memory_base);
|
|
3181
3168
|
}
|
|
3182
|
-
start() {
|
|
3183
|
-
this.
|
|
3184
|
-
this.
|
|
3169
|
+
start(renderer) {
|
|
3170
|
+
this.renderer = renderer;
|
|
3171
|
+
this.renderer.connect(this.machine);
|
|
3185
3172
|
this.flip_cursor();
|
|
3186
|
-
this.
|
|
3187
|
-
this.machine.ui.canvas.onmouseup = () => this.light_pen_active = 0;
|
|
3188
|
-
this.machine.ui.canvas.onmousedown = () => this.light_pen_active = 1;
|
|
3189
|
-
}
|
|
3190
|
-
cache = [];
|
|
3191
|
-
init_cache(sz) {
|
|
3192
|
-
for (let i = 0;i < sz; ++i)
|
|
3193
|
-
this.cache[i] = -1;
|
|
3194
|
-
}
|
|
3195
|
-
draw_char(x, y, ch) {
|
|
3196
|
-
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);
|
|
3197
|
-
}
|
|
3198
|
-
draw_cursor(x, y, visible) {
|
|
3199
|
-
const cy = (y2) => (y2 * (this.char_height + this.char_height_gap) + this.char_height) * this.scale_y;
|
|
3200
|
-
if (this.last_cursor_x !== x || this.last_cursor_y !== y) {
|
|
3201
|
-
if (this.last_cursor_state) {
|
|
3202
|
-
this.ctx.fillStyle = "#000000";
|
|
3203
|
-
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);
|
|
3204
|
-
}
|
|
3205
|
-
this.last_cursor_state = this.cursor_state;
|
|
3206
|
-
this.last_cursor_x = x;
|
|
3207
|
-
this.last_cursor_y = y;
|
|
3208
|
-
}
|
|
3209
|
-
const cx = x * this.char_width * this.scale_x;
|
|
3210
|
-
this.ctx.fillStyle = visible ? "#ffffff" : "#000000";
|
|
3211
|
-
this.ctx.fillRect(cx, cy(y), this.cursor_width * this.scale_x, this.cursor_height * this.scale_y);
|
|
3173
|
+
this.render_loop();
|
|
3212
3174
|
}
|
|
3213
|
-
|
|
3214
|
-
this.
|
|
3215
|
-
|
|
3216
|
-
setTimeout(() => this.flip_cursor(), this.cursor_rate);
|
|
3217
|
-
}
|
|
3218
|
-
init() {
|
|
3219
|
-
this.ctx = this.machine.ui.canvas.getContext("2d");
|
|
3220
|
-
}
|
|
3221
|
-
disable_smoothing() {
|
|
3222
|
-
this.ctx.imageSmoothingEnabled = false;
|
|
3175
|
+
render_loop() {
|
|
3176
|
+
this.renderer.update();
|
|
3177
|
+
setTimeout(() => this.render_loop(), Screen.#update_rate);
|
|
3223
3178
|
}
|
|
3224
3179
|
last_width = 0;
|
|
3225
3180
|
last_height = 0;
|
|
3226
|
-
video_memory_size = 0;
|
|
3227
3181
|
set_geometry(width, height) {
|
|
3228
3182
|
this.width = width;
|
|
3229
3183
|
this.height = height;
|
|
3230
3184
|
this.video_memory_size = width * height;
|
|
3231
3185
|
this.machine.ui.update_screen_geometry(this.width, this.height);
|
|
3232
|
-
const canvas_width = this.width * this.char_width * this.scale_x;
|
|
3233
|
-
const canvas_height = this.height * (this.char_height + this.char_height_gap) * this.scale_y;
|
|
3234
|
-
this.machine.ui.resize_canvas(canvas_width, canvas_height);
|
|
3235
|
-
this.disable_smoothing();
|
|
3236
|
-
this.ctx.fillStyle = "#000000";
|
|
3237
|
-
this.ctx.fillRect(0, 0, canvas_width, canvas_height);
|
|
3238
3186
|
if (this.last_width === this.width && this.last_height === this.height)
|
|
3239
3187
|
return;
|
|
3240
3188
|
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}`);
|
|
3241
3189
|
this.last_width = this.width;
|
|
3242
3190
|
this.last_height = this.height;
|
|
3243
3191
|
}
|
|
3244
|
-
video_memory_base = 0;
|
|
3245
3192
|
last_video_memory_base = 0;
|
|
3246
3193
|
set_video_memory(base) {
|
|
3247
3194
|
this.video_memory_base = base;
|
|
3248
|
-
this.init_cache(this.video_memory_size);
|
|
3249
3195
|
this.machine.ui.update_video_memory_address(this.video_memory_base);
|
|
3250
3196
|
if (this.last_video_memory_base === this.video_memory_base)
|
|
3251
3197
|
return;
|
|
@@ -3253,37 +3199,12 @@ class Screen {
|
|
|
3253
3199
|
this.last_video_memory_base = this.video_memory_base;
|
|
3254
3200
|
}
|
|
3255
3201
|
set_cursor(x, y) {
|
|
3256
|
-
this.draw_cursor(this.cursor_x, this.cursor_y, false);
|
|
3257
3202
|
this.cursor_x = x;
|
|
3258
3203
|
this.cursor_y = y;
|
|
3259
3204
|
}
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
for (let y = 0;y < this.height; ++y) {
|
|
3264
|
-
for (let x = 0;x < this.width; ++x) {
|
|
3265
|
-
const cache_i = i - this.video_memory_base;
|
|
3266
|
-
const ch = memory.read(i);
|
|
3267
|
-
if (this.cache[cache_i] !== ch) {
|
|
3268
|
-
this.draw_char(x, y, ch);
|
|
3269
|
-
this.cache[cache_i] = ch;
|
|
3270
|
-
}
|
|
3271
|
-
i += 1;
|
|
3272
|
-
}
|
|
3273
|
-
}
|
|
3274
|
-
setTimeout(() => this.draw_screen(), Screen.#update_rate);
|
|
3275
|
-
}
|
|
3276
|
-
handle_mousemove(event) {
|
|
3277
|
-
const canvas = this.machine.ui.canvas;
|
|
3278
|
-
const box = canvas.getBoundingClientRect();
|
|
3279
|
-
const scaleX = canvas.width / box.width;
|
|
3280
|
-
const scaleY = canvas.height / box.height;
|
|
3281
|
-
const mouseX = (event.clientX - box.left) * scaleX;
|
|
3282
|
-
const mouseY = (event.clientY - box.top) * scaleY;
|
|
3283
|
-
const x = Math.floor(mouseX / (this.char_width * this.scale_x));
|
|
3284
|
-
const y = Math.floor(mouseY / ((this.char_height + this.char_height_gap) * this.scale_y));
|
|
3285
|
-
this.light_pen_x = x;
|
|
3286
|
-
this.light_pen_y = y;
|
|
3205
|
+
flip_cursor() {
|
|
3206
|
+
this.cursor_state = !this.cursor_state;
|
|
3207
|
+
setTimeout(() => this.flip_cursor(), this.cursor_rate);
|
|
3287
3208
|
}
|
|
3288
3209
|
}
|
|
3289
3210
|
|
|
@@ -3389,8 +3310,6 @@ class Tape {
|
|
|
3389
3310
|
}
|
|
3390
3311
|
|
|
3391
3312
|
// src/lib/rk86_terminal.ts
|
|
3392
|
-
globalThis.Image = class {
|
|
3393
|
-
};
|
|
3394
3313
|
var charMap = {
|
|
3395
3314
|
0: " ",
|
|
3396
3315
|
1: "\u2598",
|
|
@@ -3556,35 +3475,25 @@ class IO {
|
|
|
3556
3475
|
interrupt = (_iff) => {};
|
|
3557
3476
|
}
|
|
3558
3477
|
|
|
3559
|
-
class
|
|
3478
|
+
class TerminalRenderer {
|
|
3560
3479
|
machine;
|
|
3561
|
-
|
|
3562
|
-
height = 30;
|
|
3563
|
-
video_memory_base = 0;
|
|
3564
|
-
timer;
|
|
3565
|
-
constructor(machine) {
|
|
3480
|
+
connect(machine) {
|
|
3566
3481
|
this.machine = machine;
|
|
3567
3482
|
}
|
|
3568
|
-
|
|
3569
|
-
this.render();
|
|
3570
|
-
}
|
|
3571
|
-
render() {
|
|
3483
|
+
update() {
|
|
3572
3484
|
const { memory, screen } = this.machine;
|
|
3573
|
-
const cursorX = screen.cursor_x;
|
|
3574
|
-
const cursorY = screen.cursor_y;
|
|
3575
|
-
const cursorVisible = screen.cursor_state;
|
|
3576
3485
|
const dim = "\x1B[2m";
|
|
3577
3486
|
const reset = "\x1B[0m";
|
|
3578
|
-
const w =
|
|
3487
|
+
const w = screen.width;
|
|
3579
3488
|
let output = "\x1B[H";
|
|
3580
3489
|
output += `${dim}\u250C${"\u2500".repeat(w)}\u2510${reset}
|
|
3581
3490
|
`;
|
|
3582
|
-
let addr =
|
|
3583
|
-
for (let y = 0;y <
|
|
3491
|
+
let addr = screen.video_memory_base;
|
|
3492
|
+
for (let y = 0;y < screen.height; y++) {
|
|
3584
3493
|
let line = `${dim}\u2502${reset}`;
|
|
3585
3494
|
for (let x = 0;x < w; x++) {
|
|
3586
3495
|
const ch = rk86char(memory.read(addr));
|
|
3587
|
-
if (x ===
|
|
3496
|
+
if (x === screen.cursor_x && y === screen.cursor_y) {
|
|
3588
3497
|
line += `\x1B[4m${ch}${reset}`;
|
|
3589
3498
|
} else {
|
|
3590
3499
|
line += ch;
|
|
@@ -3598,7 +3507,6 @@ class TerminalScreen {
|
|
|
3598
3507
|
output += `${dim}\u2514${"\u2500".repeat(w)}\u2518${reset}
|
|
3599
3508
|
`;
|
|
3600
3509
|
process.stdout.write(output);
|
|
3601
|
-
this.timer = setTimeout(() => this.render(), 40);
|
|
3602
3510
|
}
|
|
3603
3511
|
}
|
|
3604
3512
|
var KEY_MAP = {
|
|
@@ -3701,12 +3609,12 @@ function decodeMon32() {
|
|
|
3701
3609
|
return Array.from(new Uint8Array(Uint8Array.from(atob(MON32_B64), (c) => c.charCodeAt(0))));
|
|
3702
3610
|
}
|
|
3703
3611
|
async function fetchFile(name) {
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
} catch {
|
|
3708
|
-
console.error(`\u043E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0444\u0430\u0439\u043B\u0430: ${name}`);
|
|
3612
|
+
if (!existsSync(name)) {
|
|
3613
|
+
console.error(`\u0444\u0430\u0439\u043B \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${name}`);
|
|
3614
|
+
process.exit(1);
|
|
3709
3615
|
}
|
|
3616
|
+
const data = await readFile(name);
|
|
3617
|
+
return Array.from(data);
|
|
3710
3618
|
}
|
|
3711
3619
|
function printHelp() {
|
|
3712
3620
|
console.log(`\u042D\u043C\u0443\u043B\u044F\u0442\u043E\u0440 \u0420\u0430\u0434\u0438\u043E-86\u0420\u041A (\u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B)
|
|
@@ -3753,7 +3661,7 @@ async function main() {
|
|
|
3753
3661
|
const loadOnly = args.includes("-p");
|
|
3754
3662
|
const monitorIdx = args.indexOf("-m");
|
|
3755
3663
|
const monitorFile_ = monitorIdx >= 0 ? args[monitorIdx + 1] : undefined;
|
|
3756
|
-
const positional = args.filter((a, i) => !a.startsWith("-") && i !== monitorIdx + 1);
|
|
3664
|
+
const positional = args.filter((a, i) => !a.startsWith("-") && (monitorIdx < 0 || i !== monitorIdx + 1));
|
|
3757
3665
|
const programFile = positional[0];
|
|
3758
3666
|
const keyboard = new Keyboard;
|
|
3759
3667
|
const io = new IO;
|
|
@@ -3770,26 +3678,12 @@ async function main() {
|
|
|
3770
3678
|
machine.tape = new Tape(machine);
|
|
3771
3679
|
machine.runner = new Runner(machine);
|
|
3772
3680
|
machine.memory.update_ruslat = machine.ui.update_ruslat;
|
|
3773
|
-
|
|
3774
|
-
if (monitorFile_) {
|
|
3775
|
-
const content = await fetchFile(monitorFile_);
|
|
3776
|
-
if (!content) {
|
|
3777
|
-
console.error(`\u043C\u043E\u043D\u0438\u0442\u043E\u0440 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${monitorFile_}`);
|
|
3778
|
-
process.exit(1);
|
|
3779
|
-
}
|
|
3780
|
-
monitorContent = content;
|
|
3781
|
-
} else {
|
|
3782
|
-
monitorContent = decodeMon32();
|
|
3783
|
-
}
|
|
3681
|
+
const monitorContent = monitorFile_ ? await fetchFile(monitorFile_) : decodeMon32();
|
|
3784
3682
|
const monitorFile = parse_rk86_binary(monitorFile_ || "mon32.bin", monitorContent);
|
|
3785
3683
|
machine.memory.load_file(monitorFile);
|
|
3786
3684
|
let entryPoint;
|
|
3787
3685
|
if (programFile) {
|
|
3788
3686
|
const content = await fetchFile(programFile);
|
|
3789
|
-
if (!content) {
|
|
3790
|
-
console.error(`\u0444\u0430\u0439\u043B \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${programFile}`);
|
|
3791
|
-
process.exit(1);
|
|
3792
|
-
}
|
|
3793
3687
|
const file = parse_rk86_binary(programFile, content);
|
|
3794
3688
|
machine.memory.load_file(file);
|
|
3795
3689
|
entryPoint = file.entry;
|
|
@@ -3798,34 +3692,8 @@ async function main() {
|
|
|
3798
3692
|
process.stdout.write("\x1B[?25l");
|
|
3799
3693
|
process.stdout.write("\x1B[2J");
|
|
3800
3694
|
setupKeyboard(keyboard);
|
|
3801
|
-
|
|
3802
|
-
const origSetGeometry = machine.screen.set_geometry.bind(machine.screen);
|
|
3803
|
-
machine.screen.set_geometry = (width, height) => {
|
|
3804
|
-
origSetGeometry(width, height);
|
|
3805
|
-
termScreen.width = width;
|
|
3806
|
-
termScreen.height = height;
|
|
3807
|
-
};
|
|
3808
|
-
const origSetVideoMemory = machine.screen.set_video_memory.bind(machine.screen);
|
|
3809
|
-
machine.screen.set_video_memory = (base) => {
|
|
3810
|
-
origSetVideoMemory(base);
|
|
3811
|
-
termScreen.video_memory_base = base;
|
|
3812
|
-
};
|
|
3813
|
-
const noopCtx = {
|
|
3814
|
-
imageSmoothingEnabled: false,
|
|
3815
|
-
fillStyle: "",
|
|
3816
|
-
fillRect() {},
|
|
3817
|
-
drawImage() {},
|
|
3818
|
-
clearRect() {}
|
|
3819
|
-
};
|
|
3820
|
-
machine.screen.ctx = noopCtx;
|
|
3821
|
-
machine.screen.init = () => {
|
|
3822
|
-
machine.screen.ctx = noopCtx;
|
|
3823
|
-
};
|
|
3824
|
-
machine.screen.draw_screen = () => {};
|
|
3825
|
-
machine.screen.draw_cursor = () => {};
|
|
3826
|
-
machine.screen.start();
|
|
3695
|
+
machine.screen.start(new TerminalRenderer);
|
|
3827
3696
|
machine.runner.execute();
|
|
3828
|
-
termScreen.start();
|
|
3829
3697
|
if (entryPoint !== undefined && !loadOnly) {
|
|
3830
3698
|
setTimeout(() => machine.cpu.jump(entryPoint), 500);
|
|
3831
3699
|
}
|