Skip to content

Instantly share code, notes, and snippets.

@Pixelsuft
Last active August 3, 2022 12:36
Show Gist options
  • Save Pixelsuft/62f847c9a846e345a9a9804878ae6616 to your computer and use it in GitHub Desktop.
Save Pixelsuft/62f847c9a846e345a9a9804878ae6616 to your computer and use it in GitHub Desktop.
v86 nodejs text screen example
#!/usr/bin/env node
"use strict";
var fs = require("fs");
var V86Starter = require("../build/libv86.js").V86Starter;
function readfile(path)
{
return new Uint8Array(fs.readFileSync(path)).buffer;
}
function resize_terminal(rows, cols)
{
// set size
process.stdout.write("\x1b[8;" + cols + ";" + rows + "t");
// move cursor back
process.stdout.write("\x1b[2J" + cursor_pos(cursor_row, cursor_col + 1));
}
function number_as_rgb(n)
{
return [
n >> 16 & 0xFF,
n >> 8 & 0xFF,
n & 0xFF
];
}
function rgb_to_bin(rgb)
{
// example: [200, 30, 255] -> [255, 0, 255] -> 0b101
return rgb[0] >> 7 << 2 |
rgb[1] >> 7 << 1 |
rgb[2] >> 7;
}
function cursor_pos(y, x)
{
return cursor_pos_chr + (y + 1) + ";" + x + "H";
}
function should_be_bright(rgb)
{
return rgb[0] > 198 ||
rgb[1] > 198 ||
rgb[2] > 198;
}
function text_update_row(row)
{
var offset = 3 * row * width,
text = cursor_pos(row, 0);
var bg_color,
fg_color,
fg_rgb;
for(var i = 0; i < width; )
{
bg_color = text_mode_data[offset + 1];
fg_color = text_mode_data[offset + 2];
fg_rgb = number_as_rgb(fg_color);
text += should_be_bright(fg_rgb) ? fg_bright : reset_all;
text += bg_chars[rgb_to_bin(number_as_rgb(bg_color))];
text += fg_chars[rgb_to_bin(fg_rgb)];
// put characters of the same color in one element
while(i < width &&
text_mode_data[offset + 1] === bg_color &&
text_mode_data[offset + 2] === fg_color)
{
var ascii = text_mode_data[offset];
text += charmap[ascii];
i++;
offset += 3;
}
}
return text;
}
var bios = readfile(__dirname + "/../bios/seabios.bin");
var vgabios = readfile(__dirname + "/../bios/vgabios.bin");
var msdos = readfile(__dirname + "/../images/msdos.img");
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.setEncoding("utf8");
var emulator = new V86Starter({
bios: { buffer: bios },
vga_bios: { buffer: vgabios },
hda: { buffer: msdos },
memory_size: 16 * 1024 * 1024,
autostart: true,
});
var width,
height,
cursor_row,
cursor_col,
changed_rows,
text_mode_data,
running;
// escape codes
var cursor_pos_chr = String.fromCharCode(27, 91),
reset_all = "\x1b[0m",
fg_bright = "\x1b[1m",
fg_chars = {
0b000: "\x1b[30m",
0b100: "\x1b[31m",
0b010: "\x1b[32m",
0b110: "\x1b[33m",
0b001: "\x1b[34m",
0b101: "\x1b[35m",
0b011: "\x1b[36m",
0b111: "\x1b[37m"
},
bg_chars = {
0b000: "\x1b[40m",
0b100: "\x1b[41m",
0b010: "\x1b[42m",
0b110: "\x1b[43m",
0b001: "\x1b[44m",
0b101: "\x1b[45m",
0b011: "\x1b[46m",
0b111: "\x1b[47m"
},
// f1 - f12
f_keys = [
"\x1B[[A",
"\x1B[[B",
"\x1B[[C",
"\x1B[[D",
"\x1B[[E",
"\x1B[17~",
"\x1B[18~",
"\x1B[19~",
"\x1B[10~",
"\x1B[21~",
"\x1B[23~",
"\x1B[24~"
];
// from screen.js
var charmap = String.fromCharCode(
0x20, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
0x25BA, 0x25C4, 0x2195, 0x203C, 0xB6, 0xA7, 0x25AC, 0x21A8,
0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0xC7, 0xFC, 0xE9, 0xE2, 0xE4, 0xE0, 0xE5, 0xE7,
0xEA, 0xEB, 0xE8, 0xEF, 0xEE, 0xEC, 0xC4, 0xC5,
0xC9, 0xE6, 0xC6, 0xF4, 0xF6, 0xF2, 0xFB, 0xF9,
0xFF, 0xD6, 0xDC, 0xA2, 0xA3, 0xA5, 0x20A7, 0x192,
0xE1, 0xED, 0xF3, 0xFA, 0xF1, 0xD1, 0xAA, 0xBA,
0xBF, 0x2310, 0xAC, 0xBD, 0xBC, 0xA1, 0xAB, 0xBB,
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
0x3B1, 0xDF, 0x393, 0x3C0, 0x3A3, 0x3C3, 0xB5, 0x3C4,
0x3A6, 0x398, 0x3A9, 0x3B4, 0x221E, 0x3C6, 0x3B5, 0x2229,
0x2261, 0xB1, 0x2265, 0x2264, 0x2320, 0x2321, 0xF7, 0x2248,
0xB0, 0x2219, 0xB7, 0x221A, 0x207F, 0xB2, 0x25A0, 0xA0
);
process.stdin.on("data", function(c)
{
switch(c)
{
case "\u0003":
// ctrl c
emulator.stop();
process.stdin.pause();
running = false;
process.stdout.write(reset_all);
break;
case "\r":
// enter
emulator.keyboard_adapter.simulate_press(0xD);
break;
case "\x1B[A":
// up arrow
emulator.keyboard_adapter.simulate_press(0x26);
break;
case "\x1B[B":
// down arrow
emulator.keyboard_adapter.simulate_press(0x28);
break;
case "\x1B[C":
// right arrow
emulator.keyboard_adapter.simulate_press(0x27);
break;
case "\x1B[D":
// left arrow
emulator.keyboard_adapter.simulate_press(0x25);
break;
case "\x1B":
// escape
emulator.keyboard_adapter.simulate_press(0x1B);
break;
default:
if (f_keys.includes(c))
{
// f1 - f12
emulator.keyboard_adapter.simulate_press(0x70 + f_keys.indexOf(c));
break;
}
// any other
emulator.keyboard_adapter.simulate_char(c);
break;
}
});
emulator.bus.register("screen-set-size-text", function(data)
{
if (data[0] === width && data[1] === height)
return;
width = data[0];
height = data[1];
changed_rows = new Int8Array(data[1]);
changed_rows.fill(1); // update all
text_mode_data = new Int32Array(data[0] * data[1] * 3);
resize_terminal(width, height);
if (!running) {
running = true;
setImmediate(update_text);
}
});
emulator.bus.register("screen-update-cursor", function(data)
{
if (data[0] === cursor_row && data[1] === cursor_col)
return;
changed_rows[data[0]] = 1;
changed_rows[cursor_row] = 1;
cursor_row = data[0];
cursor_col = data[1];
});
emulator.bus.register("screen-put-char", function(data)
{
if(data[0] < height && data[1] < width)
{
var p = 3 * (data[0] * width + data[1]);
text_mode_data[p] = data[2];
text_mode_data[p + 1] = data[3];
text_mode_data[p + 2] = data[4];
changed_rows[data[0]] = 1;
}
});
function update_text()
{
if (!running)
return;
var text = "";
for(var i = 0; i < height; i++)
{
if(changed_rows[i])
{
text += text_update_row(i);
changed_rows[i] = 0;
}
}
text += cursor_pos(cursor_row, cursor_col + 1);
// call process.stdout.write one time per update
process.stdout.write(text);
setImmediate(update_text);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment