diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..53e4d91
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+all:
+ @echo "this is Fabrice Bellard's unbelievable linux kernel in a browser"
+
+wget-javascript:
+ rm -f *.js
+ wget -c https://bellard.org/jslinux/term.js
+ wget -c https://bellard.org/jslinux/jslinux.js
diff --git a/jslinux.js b/jslinux.js
new file mode 100644
index 0000000..a706e00
--- /dev/null
+++ b/jslinux.js
@@ -0,0 +1,126 @@
+/*
+ Linux launcher
+
+ Copyright (c) 2011-2012 Fabrice Bellard
+
+ Redistribution or commercial use is prohibited without the author's
+ permission.
+*/
+"use strict";
+
+var term, pc, boot_start_time, init_state;
+
+function term_start()
+{
+ term = new Term(80, 30, term_handler);
+
+ term.open();
+}
+
+/* send chars to the serial port */
+function term_handler(str)
+{
+ pc.serial.send_chars(str);
+}
+
+function clipboard_set(val)
+{
+ var el;
+ el = document.getElementById("text_clipboard");
+ el.value = val;
+}
+
+function clipboard_get()
+{
+ var el;
+ el = document.getElementById("text_clipboard");
+ return el.value;
+}
+
+function clear_clipboard()
+{
+ var el;
+ el = document.getElementById("text_clipboard");
+ el.value = "";
+}
+
+/* just used to display the boot time in the VM */
+function get_boot_time()
+{
+ return (+new Date()) - boot_start_time;
+}
+
+function start()
+{
+ var params;
+
+ init_state = new Object();
+
+ params = new Object();
+
+ /* serial output chars */
+ params.serial_write = term.write.bind(term);
+
+ /* memory size (in bytes) */
+ params.mem_size = 31 * 1024 * 1024;
+
+ /* clipboard I/O */
+ params.clipboard_get = clipboard_get;
+ params.clipboard_set = clipboard_set;
+
+ params.get_boot_time = get_boot_time;
+
+ /* IDE drive. The raw disk image is split into files of
+ * 'block_size' KB.
+ */
+ //params.hda = { url: "hda%d.bin", block_size: 64, nb_blocks: 912 };
+ params.hda = { url: "bin/hda%d.bin", block_size: 64, nb_blocks: 912 };
+ //modify by hao
+ pc = new PCEmulator(params);
+
+ init_state.params = params;
+
+ pc.load_binary("vmlinux-2.6.20.bin", 0x00100000, start2);
+}
+
+function start2(ret)
+{
+ if (ret < 0)
+ return;
+ init_state.start_addr = 0x10000;
+ pc.load_binary("linuxstart.bin", init_state.start_addr, start3);
+}
+
+function start3(ret)
+{
+ var block_list;
+ if (ret < 0)
+ return;
+ /* Preload blocks so that the boot time does not depend on the
+ * time to load the required disk data (optional) */
+ block_list = [ 0, 7, 3, 643, 720, 256, 336, 644, 781, 387, 464, 475, 131, 589, 468, 472, 474, 776, 777, 778, 779, 465, 466, 473, 467, 469, 470, 512, 592, 471, 691, 697, 708, 792, 775, 769 ];
+ pc.ide0.drives[0].bs.preload(block_list, start4);
+}
+
+function start4(ret)
+{
+ var cmdline_addr;
+
+ if (ret < 0)
+ return;
+
+ /* set the Linux kernel command line */
+ cmdline_addr = 0xf800;
+ pc.cpu.write_string(cmdline_addr, "console=ttyS0 root=/dev/hda ro init=/sbin/init notsc=1 hdb=none");
+
+ pc.cpu.set_reg(8, init_state.start_addr); /* eip */
+ pc.cpu.set_reg(0, init_state.params.mem_size); /* eax */
+ pc.cpu.set_reg(3, 0); /* ebx = initrd_size (no longer used) */
+ pc.cpu.set_reg(1, cmdline_addr); /* ecx */
+
+ boot_start_time = (+new Date());
+
+ pc.start();
+}
+
+term_start();
diff --git a/term.js b/term.js
new file mode 100644
index 0000000..2dfe106
--- /dev/null
+++ b/term.js
@@ -0,0 +1,476 @@
+/*
+ Javascript Terminal
+
+ Copyright (c) 2011 Fabrice Bellard
+
+ Redistribution or commercial use is prohibited without the author's
+ permission.
+*/
+"use strict";
+function Term(aa, ba, term_handler) {
+ this.w = aa;
+ this.h = ba;
+ this.cur_h = ba;
+ this.tot_h = 1000;
+ this.y_base = 0;
+ this.y_disp = 0;
+ this.x = 0;
+ this.y = 0;
+ this.cursorstate = 0;
+ this.handler = term_handler;
+ this.convert_lf_to_crlf = false;
+ this.state = 0;
+ this.output_queue = "";
+ this.bg_colors = ["#000000", "#ff0000", "#00ff00", "#ffff00", "#0000ff", "#ff00ff", "#00ffff", "#ffffff"];
+ this.fg_colors = ["#000000", "#ff0000", "#00ff00", "#ffff00", "#0000ff", "#ff00ff", "#00ffff", "#ffffff"];
+ this.def_attr = (7 << 3) | 0;
+ this.cur_attr = this.def_attr;
+ this.is_mac = (navigator.userAgent.indexOf("Mac") >= 0) ? true: false;
+ this.key_rep_state = 0;
+ this.key_rep_str = "";
+}
+Term.prototype.open = function() {
+ var y, da, i, ea, c;
+ this.lines = new Array();
+ c = 32 | (this.def_attr << 16);
+ for (y = 0; y < this.cur_h; y++) {
+ da = new Array();
+ for (i = 0; i < this.w; i++) da[i] = c;
+ this.lines[y] = da;
+ }
+ document.writeln('
');
+ for (y = 0; y < this.h; y++) {
+ document.writeln(' |
');
+ }
+ document.writeln('
');
+ this.refresh(0, this.h - 1);
+ document.addEventListener("keydown", this.keyDownHandler.bind(this), true);
+ document.addEventListener("keypress", this.keyPressHandler.bind(this), true);
+ ea = this;
+ setInterval(function() {
+ ea.cursor_timer_cb();
+ },
+ 1000);
+};
+Term.prototype.refresh = function(fa, ga) {
+ var ha, y, da, ia, c, w, i, ja, ka, la, ma, na, oa;
+ for (y = fa; y <= ga; y++) {
+ oa = y + this.y_disp;
+ if (oa >= this.cur_h) oa -= this.cur_h;
+ da = this.lines[oa];
+ ia = "";
+ w = this.w;
+ if (y == this.y && this.cursor_state && this.y_disp == this.y_base) {
+ ja = this.x;
+ } else {
+ ja = -1;
+ }
+ la = this.def_attr;
+ for (i = 0; i < w; i++) {
+ c = da[i];
+ ka = c >> 16;
+ c &= 65535;
+ if (i == ja) {
+ ka = -1;
+ }
+ if (ka != la) {
+ if (la != this.def_attr) ia += '';
+ if (ka != this.def_attr) {
+ if (ka == -1) {
+ ia += '';
+ } else {
+ ia += '';
+ }
+ }
+ }
+ switch (c) {
+ case 32:
+ ia += " ";
+ break;
+ case 38:
+ ia += "&";
+ break;
+ case 60:
+ ia += "<";
+ break;
+ case 62:
+ ia += ">";
+ break;
+ default:
+ if (c < 32) {
+ ia += " ";
+ } else {
+ ia += String.fromCharCode(c);
+ }
+ break;
+ }
+ la = ka;
+ }
+ if (la != this.def_attr) {
+ ia += '';
+ }
+ ha = document.getElementById("tline" + y);
+ ha.innerHTML = ia;
+ }
+};
+Term.prototype.cursor_timer_cb = function() {
+ this.cursor_state ^= 1;
+ this.refresh(this.y, this.y);
+};
+Term.prototype.show_cursor = function() {
+ if (!this.cursor_state) {
+ this.cursor_state = 1;
+ this.refresh(this.y, this.y);
+ }
+};
+Term.prototype.scroll = function() {
+ var y, da, x, c, oa;
+ if (this.cur_h < this.tot_h) {
+ this.cur_h++;
+ }
+ if (++this.y_base == this.cur_h) this.y_base = 0;
+ this.y_disp = this.y_base;
+ c = 32 | (this.def_attr << 16);
+ da = new Array();
+ for (x = 0; x < this.w; x++) da[x] = c;
+ oa = this.y_base + this.h - 1;
+ if (oa >= this.cur_h) oa -= this.cur_h;
+ this.lines[oa] = da;
+};
+Term.prototype.scroll_disp = function(n) {
+ var i, oa;
+ if (n >= 0) {
+ for (i = 0; i < n; i++) {
+ if (this.y_disp == this.y_base) break;
+ if (++this.y_disp == this.cur_h) this.y_disp = 0;
+ }
+ } else {
+ n = -n;
+ oa = this.y_base + this.h;
+ if (oa >= this.cur_h) oa -= this.cur_h;
+ for (i = 0; i < n; i++) {
+ if (this.y_disp == oa) break;
+ if (--this.y_disp < 0) this.y_disp = this.cur_h - 1;
+ }
+ }
+ this.refresh(0, this.h - 1);
+};
+Term.prototype.write = function(pa) {
+ function qa(y) {
+ fa = Math.min(fa, y);
+ ga = Math.max(ga, y);
+ }
+ function ra(s, x, y) {
+ var l, i, c, oa;
+ oa = s.y_base + y;
+ if (oa >= s.cur_h) oa -= s.cur_h;
+ l = s.lines[oa];
+ c = 32 | (s.def_attr << 16);
+ for (i = x; i < s.w; i++) l[i] = c;
+ qa(y);
+ }
+ function sa(s, ta) {
+ var j, n;
+ if (ta.length == 0) {
+ s.cur_attr = s.def_attr;
+ } else {
+ for (j = 0; j < ta.length; j++) {
+ n = ta[j];
+ if (n >= 30 && n <= 37) {
+ s.cur_attr = (s.cur_attr & ~ (7 << 3)) | ((n - 30) << 3);
+ } else if (n >= 40 && n <= 47) {
+ s.cur_attr = (s.cur_attr & ~7) | (n - 40);
+ } else if (n == 0) {
+ s.cur_attr = s.def_attr;
+ }
+ }
+ }
+ }
+ var ua = 0;
+ var va = 1;
+ var wa = 2;
+ var i, c, fa, ga, l, n, j, oa;
+ fa = this.h;
+ ga = -1;
+ qa(this.y);
+ if (this.y_base != this.y_disp) {
+ this.y_disp = this.y_base;
+ fa = 0;
+ ga = this.h - 1;
+ }
+ for (i = 0; i < pa.length; i++) {
+ c = pa.charCodeAt(i);
+ switch (this.state) {
+ case ua:
+ switch (c) {
+ case 10:
+ if (this.convert_lf_to_crlf) {
+ this.x = 0;
+ }
+ this.y++;
+ if (this.y >= this.h) {
+ this.y--;
+ this.scroll();
+ fa = 0;
+ ga = this.h - 1;
+ }
+ break;
+ case 13:
+ this.x = 0;
+ break;
+ case 8:
+ if (this.x > 0) {
+ this.x--;
+ }
+ break;
+ case 9:
+ n = (this.x + 8) & ~7;
+ if (n <= this.w) {
+ this.x = n;
+ }
+ break;
+ case 27:
+ this.state = va;
+ break;
+ default:
+ if (c >= 32) {
+ if (this.x >= this.w) {
+ this.x = 0;
+ this.y++;
+ if (this.y >= this.h) {
+ this.y--;
+ this.scroll();
+ fa = 0;
+ ga = this.h - 1;
+ }
+ }
+ oa = this.y + this.y_base;
+ if (oa >= this.cur_h) oa -= this.cur_h;
+ this.lines[oa][this.x] = (c & 65535) | (this.cur_attr << 16);
+ this.x++;
+ qa(this.y);
+ }
+ break;
+ }
+ break;
+ case va:
+ if (c == 91) {
+ this.esc_params = new Array();
+ this.cur_param = 0;
+ this.state = wa;
+ } else {
+ this.state = ua;
+ }
+ break;
+ case wa:
+ if (c >= 48 && c <= 57) {
+ this.cur_param = this.cur_param * 10 + c - 48;
+ } else {
+ this.esc_params[this.esc_params.length] = this.cur_param;
+ this.cur_param = 0;
+ if (c == 59) break;
+ this.state = ua;
+ switch (c) {
+ case 65:
+ n = this.esc_params[0];
+ if (n < 1) n = 1;
+ this.y -= n;
+ if (this.y < 0) this.y = 0;
+ break;
+ case 66:
+ n = this.esc_params[0];
+ if (n < 1) n = 1;
+ this.y += n;
+ if (this.y >= this.h) this.y = this.h - 1;
+ break;
+ case 67:
+ n = this.esc_params[0];
+ if (n < 1) n = 1;
+ this.x += n;
+ if (this.x >= this.w - 1) this.x = this.w - 1;
+ break;
+ case 68:
+ n = this.esc_params[0];
+ if (n < 1) n = 1;
+ this.x -= n;
+ if (this.x < 0) this.x = 0;
+ break;
+ case 72:
+ {
+ var xa, oa;
+ oa = this.esc_params[0] - 1;
+ if (this.esc_params.length >= 2) xa = this.esc_params[1] - 1;
+ else xa = 0;
+ if (oa < 0) oa = 0;
+ else if (oa >= this.h) oa = this.h - 1;
+ if (xa < 0) xa = 0;
+ else if (xa >= this.w) xa = this.w - 1;
+ this.x = xa;
+ this.y = oa;
+ }
+ break;
+ case 74:
+ ra(this, this.x, this.y);
+ for (j = this.y + 1; j < this.h; j++) ra(this, 0, j);
+ break;
+ case 75:
+ ra(this, this.x, this.y);
+ break;
+ case 109:
+ sa(this, this.esc_params);
+ break;
+ case 110:
+ this.queue_chars("\x1b[" + (this.y + 1) + ";" + (this.x + 1) + "R");
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ }
+ qa(this.y);
+ if (ga >= fa) this.refresh(fa, ga);
+};
+Term.prototype.writeln = function(pa) {
+ this.write(pa + '\r\n');
+};
+Term.prototype.keyDownHandler = function(ya) {
+ var pa;
+ pa = "";
+ switch (ya.keyCode) {
+ case 8:
+ pa = "";
+ break;
+ case 9:
+ pa = "\t";
+ break;
+ case 13:
+ pa = "\r";
+ break;
+ case 27:
+ pa = "\x1b";
+ break;
+ case 37:
+ pa = "\x1b[D";
+ break;
+ case 39:
+ pa = "\x1b[C";
+ break;
+ case 38:
+ if (ya.ctrlKey) {
+ this.scroll_disp( - 1);
+ } else {
+ pa = "\x1b[A";
+ }
+ break;
+ case 40:
+ if (ya.ctrlKey) {
+ this.scroll_disp(1);
+ } else {
+ pa = "\x1b[B";
+ }
+ break;
+ case 46:
+ pa = "\x1b[3~";
+ break;
+ case 45:
+ pa = "\x1b[2~";
+ break;
+ case 36:
+ pa = "\x1bOH";
+ break;
+ case 35:
+ pa = "\x1bOF";
+ break;
+ case 33:
+ if (ya.ctrlKey) {
+ this.scroll_disp( - (this.h - 1));
+ } else {
+ pa = "\x1b[5~";
+ }
+ break;
+ case 34:
+ if (ya.ctrlKey) {
+ this.scroll_disp(this.h - 1);
+ } else {
+ pa = "\x1b[6~";
+ }
+ break;
+ default:
+ if (ya.ctrlKey) {
+ if (ya.keyCode >= 65 && ya.keyCode <= 90) {
+ pa = String.fromCharCode(ya.keyCode - 64);
+ } else if (ya.keyCode == 32) {
+ pa = String.fromCharCode(0);
+ }
+ } else if ((!this.is_mac && ya.altKey) || (this.is_mac && ya.metaKey)) {
+ if (ya.keyCode >= 65 && ya.keyCode <= 90) {
+ pa = "\x1b" + String.fromCharCode(ya.keyCode + 32);
+ }
+ }
+ break;
+ }
+ if (pa) {
+ if (ya.stopPropagation) ya.stopPropagation();
+ if (ya.preventDefault) ya.preventDefault();
+ this.show_cursor();
+ this.key_rep_state = 1;
+ this.key_rep_str = pa;
+ this.handler(pa);
+ return false;
+ } else {
+ this.key_rep_state = 0;
+ return true;
+ }
+};
+Term.prototype.keyPressHandler = function(ya) {
+ var pa, za;
+ if (ya.stopPropagation) ya.stopPropagation();
+ if (ya.preventDefault) ya.preventDefault();
+ pa = "";
+ if (! ("charCode" in ya)) {
+ za = ya.keyCode;
+ if (this.key_rep_state == 1) {
+ this.key_rep_state = 2;
+ return false;
+ } else if (this.key_rep_state == 2) {
+ this.show_cursor();
+ this.handler(this.key_rep_str);
+ return false;
+ }
+ } else {
+ za = ya.charCode;
+ }
+ if (za != 0) {
+ if (!ya.ctrlKey && ((!this.is_mac && !ya.altKey) || (this.is_mac && !ya.metaKey))) {
+ pa = String.fromCharCode(za);
+ }
+ }
+ if (pa) {
+ this.show_cursor();
+ this.handler(pa);
+ return false;
+ } else {
+ return true;
+ }
+};
+Term.prototype.queue_chars = function(pa) {
+ this.output_queue += pa;
+ if (this.output_queue) setTimeout(this.outputHandler.bind(this), 0);
+};
+Term.prototype.outputHandler = function() {
+ if (this.output_queue) {
+ this.handler(this.output_queue);
+ this.output_queue = "";
+ }
+};
\ No newline at end of file