(function() { var bios = { data:{ version:'0.1.4', id:'BIOS', bootable:'clite/core.js', entry:null }, core:{ getHex:function(s) { if (typeof s === 'string') s = parseInt(s); if (typeof s !== 'number') return '0'; return s.toString(16).toUpperCase(); }, writeStringVGA:function(x,y,v,fg) { for (var i=0; i= 80) break; bios.video.text.io.write(x+i,y,v[i]); } if (typeof fg === 'undefined') return; for (var i=0; i= 80) break; bios.video.text.io.writeSet(1,x+i,y,fg); } }, expose:function(name,fn) { window['BIOS'+name] = function() { try{ fn.apply(null,arguments); } catch(err) {} setTimeout(function() {window['BIOS'+name] = null;},10); } }, load:{ script:function(name,callback) { var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); script.type = 'text/javascript'; script.src = name+'?d='+(new Date).getTime(); script.onload = callback; head.appendChild(script); }, localfile:function(name,callback) { bios.io.write(3,{file:name,fn:callback}); }, file:function(name,callback) { if (window.location.protocol == 'file:') { bios.core.load.localfile(name,callback); return; } fetch(name+'?d='+(new Date).getTime()) .then(function(response) { return response.text(); }) .then(function(data) { callback(data); }); } }, }, io:{ read:function(c) { switch (c) { case 0: return bios.data.id; break; case 1: return bios.video.io; break; case 2: return bios.storage.io; break; default:; } return null; }, write:function(c,d) { switch (c) { case 0: bios.data.id = d; document.title = d; break; case 1: bios.video.setMode(d); break; case 2: // writing to the keyboard handler without a callback flushes the buffer if (typeof d === 'undefined') { bios.input.keybuffer.buff = []; return; } bios.input.keybuffer.rcvr = d; break; case 3: bios.input.upload.data.callback = d.fn; bios.input.upload.data.file = d.file; bios.input.setState(2); break; case 4: bios.boot.shutdown(!d); if (d == true) setTimeout(bios.boot.reboot,10); break; default:; } }, }, video:{ mode:{ current:0, modes:[null], }, viewport:{ width:0, height:0 }, viewportResize:function() { bios.video.viewport.width = window.innerWidth; bios.video.viewport.height = window.innerHeight; if (bios.video.mode.current == 0) return; try{ bios.video.mode.modes[bios.video.mode.current].resize(); }catch(err) {} }, init:function() { window.addEventListener('resize',bios.video.viewportResize); bios.video.viewportResize(); bios.video.mode.modes[1] = bios.video.text; bios.video.mode.modes[2] = bios.video.graphic; bios.video.setMode(1); }, io:{}, setMode:function(m) { if (m < 0 || m >= bios.video.mode.modes.length) return false; if (m == bios.video.mode.current) return true; try{ bios.video.mode.modes[bios.video.mode.current].exit(); } catch(err) {} bios.video.mode.current = m; try{ bios.video.mode.modes[bios.video.mode.current].init(); } catch(err) { bios.video.io = null; return false; } return true; } }, storage:{ data:{ files:[] }, internal:{ flush:function() { var list = bios.storage.data.files.join(':'); window.localStorage.setItem('files',list); } }, io:{ addFile:function(fn,data) { if (bios.storage.data.files.indexOf(fn) < 0) bios.storage.data.files.push(fn); window.localStorage.setItem(fn,data); bios.storage.internal.flush(); }, delFile:function(fn) { var ind = bios.storage.data.files.indexOf(fn) if (ind > -1) bios.storage.data.files.splice(ind,1); window.localStorage.removeItem(fn); bios.storage.internal.flush(); }, getList:function() { return bios.storage.data.files; }, getFile:function(fn) { var ind = bios.storage.data.files.indexOf(fn) if (ind < 0) return null; return window.localStorage.getItem(fn).toString(); }, clear:function() { window.localStorage.clear(); } }, init:function() { let f = window.localStorage.getItem('files'); if (f) bios.storage.data.files = f.split(':'); } }, input:{ el:null, state:0, setState:function(v) { // 1 for normal keyboard input, 2 for file access if (bios.input.state == v) return; switch (bios.input.state) { case 1: bios.input.keyboard.disable(); break; case 2: bios.input.upload.disable(); break; default:; } switch (v) { case 2: bios.input.upload.enable(); break; default: bios.input.keyboard.enable(); v = 1; } bios.input.state = v; }, init:function() { bios.input.el = document.getElementById('input'); bios.input.keyboard.init(); bios.input.upload.init(); bios.input.setState(1); }, keybuffer:{ rcvr:null, buff:[], input:function(key) { if (bios.input.state == 1) { try{ bios.input.keybuffer.rcvr(key); return; } catch(err) {} } bios.input.keybuffer.buff.push(key); } } }, boot:{ setEntry:function(fn) { bios.data.entry = fn; setTimeout(bios.boot.doBoot,10); }, preBoot:function() { bios.core.expose('entry',bios.boot.setEntry); bios.core.load.script(bios.data.bootable,null); }, doBoot:function() { try{ bios.data.entry(bios); }catch(err) { bios.core.writeStringVGA(1,7,'Error in bootable entry point',4); bios.core.writeStringVGA(1,8,err.message,6); } }, shutdown:function(complete) { var s = Array.from(document.getElementsByTagName('src')); s.forEach(function(el,i) { if (i>0) el.parentNode.removeChild(el); }); if (!complete) return; document.body.innerHTML = 'The system has shutdown'; bios = null; }, reboot:function() { setTimeout(bios.boot.preBoot,1000); } }, init:function() { bios.io.write(0,bios.data.id); bios.video.init(); bios.core.writeStringVGA(2,0,'JS Basic Input Output System'); var v = bios.data.version.toString(); bios.core.writeStringVGA(78-v.length,0,v); for (var i=0; i<80; i++) { bios.video.io.writeSet(0,i,0,15); bios.video.io.writeSet(1,i,0,0); } bios.core.writeStringVGA(3,1,'Initialising...'); bios.core.writeStringVGA(3,3,'Video Graphics Array:'); bios.core.writeStringVGA(25,3,'Done',2); bios.core.writeStringVGA(3,4,'Input Devices:'); bios.input.init(); bios.core.writeStringVGA(25,4,'Done',2); bios.core.writeStringVGA(3,5,'Storage Devices:'); bios.storage.init(); bios.core.writeStringVGA(25,5,'Done',2); bios.core.writeStringVGA(0,7,'Booting...'); bios.boot.preBoot(); } }; bios.video.text = { data:{ el:null, isinit:false, screen:{ width:0, height:0, nw:0, nh:0, f:0 } }, internal:{ getElement:function() { bios.video.text.data.el = document.getElementById('vga'); if (typeof bios.video.text.data.el !== 'undefined' && bios.video.text.data.el != null) return bios.video.text.data.el; bios.video.text.data.el = document.createNode('div'); bios.video.text.data.el.id = 'vga'; bios.video.text.data.el.className = 'vga'; document.body.appendChild(bios.video.text.data.el); return bios.video.data.text.el; }, getPix:function(x,y) { return document.getElementById('vga-'+y+'-'+x); }, dataToClass:function(d) { var c = 'vga-b'+d.bg+' vga-f'+d.fg; if (d.bk) c += ' vga-blink'; return c; } }, io:{ write:function(x,y,v) { if (x < 0 || x > 79 || y < 0 || y > 24) return; var c = bios.video.text.internal.getPix(x,y); c.textContent = v; }, setBG:function(x,y,id) { if (id < 0 || id > 15) return; var c = bios.video.text.internal.getPix(x,y); c.data.bg = bios.core.getHex(id); c.className = bios.video.text.internal.dataToClass(c.data); }, setFG:function(x,y,id) { if (id < 0 || id > 15) return; var c = bios.video.text.internal.getPix(x,y); c.data.fg = bios.core.getHex(id); c.className = bios.video.text.internal.dataToClass(c.data); }, setBlink:function(x,y,id) { if (typeof id !== 'boolean') return; var c = bios.video.text.internal.getPix(x,y); c.data.bk = id; c.className = bios.video.text.internal.dataToClass(c.data); }, setCursor:function(x,y,enabled) { if (typeof enabled !== 'boolean') return; var c = document.getElementById('vga-cursor'); if (!c) return; c.data.x = x; c.data.y = y; c.data.enabled = enabled; if (!enabled) { c.style.display = 'none'; return; } c.style.display = 'block'; c.style.top = (y*bios.video.text.data.screen.nh)+'px'; c.style.left = (x*bios.video.text.data.screen.nw)+'px'; }, writeSet:function(name,x,y,id) { if (x < 0 || x > 79 || y < 0 || y > 24) return; switch(name) { case 0: bios.video.text.io.setBG(x,y,id); break; case 1: bios.video.text.io.setFG(x,y,id); break; case 2: bios.video.text.io.setBlink(x,y,id); break; case 3: bios.video.text.io.setCursor(x,y,id); break; default:; } } }, init:function() { if (!bios.video.text.internal.getElement()) return false; bios.video.io = bios.video.text.io; bios.video.text.data.el.innerHTML = ''; var ypx; var xpx; var c = document.createElement('span'); c.textContent = 'W'; bios.video.text.data.el.appendChild(c); ypx = c.offsetHeight; xpx = c.offsetWidth; bios.video.text.data.el.innerHTML = ''; bios.video.text.data.el.style.width = (xpx*80)+'px'; bios.video.text.data.el.style.height = (ypx*25)+'px'; bios.video.text.data.el.style.position = 'relative'; for (var y=0; y<25; y++) { for (var x=0; x<80; x++) { var c = document.createElement('div'); c.className = 'vga-b0 vga-fF'; c.style.position = 'absolute'; c.style.top = (y*ypx)+'px'; c.style.left = (x*xpx)+'px'; c.style.width = xpx+'px'; c.style.height = ypx+'px'; c.style.zIndex = '5'; c.data = {bg:'0',fg:'F',bk:false}; c.id = 'vga-'+y+'-'+x; bios.video.text.data.el.appendChild(c); } } // the cursor var c = document.createElement('div'); c.className = 'vga-cursor vga-blink vga-bt vga-fF'; c.style.position = 'absolute'; c.style.top = '0px'; c.style.left = '0px'; c.style.width = xpx+'px'; c.style.height = ypx+'px'; c.style.zIndex = '10'; c.id = 'vga-cursor'; c.textContent = '_'; c.data = {}; c.data.x = x; c.data.y = y; c.data.enabled = false; bios.video.text.data.el.appendChild(c); bios.video.text.resize(); bios.video.text.data.isinit = true; return bios.video.text.data.isinit; }, exit:function() { if (!bios.video.text.internal.getElement()) return; bios.video.text.data.el.innerHTML = ''; }, resize:function() { var w = bios.video.viewport.width; var h = bios.video.viewport.height; if (h/1.42 > w) { h = w/1.42; }else{ w = h*1.42; } bios.video.text.data.screen.height = parseInt(h); bios.video.text.data.screen.width = parseInt(w); bios.video.text.data.screen.nh = parseInt(h/25); bios.video.text.data.screen.nw = parseInt(w/80); bios.video.text.data.screen.f = parseInt(bios.video.text.data.screen.nh/1.2); var e = bios.video.text.internal.getElement(); e.style.width = bios.video.text.data.screen.width+'px'; e.style.height = bios.video.text.data.screen.height+'px'; for (var y=0; y<25; y++) { for (var x=0; x<80; x++) { var c = bios.video.text.internal.getPix(x,y); c.style.top = (y*bios.video.text.data.screen.nh)+'px'; c.style.left = (x*bios.video.text.data.screen.nw)+'px'; c.style.width = bios.video.text.data.screen.nw+'px'; c.style.height = bios.video.text.data.screen.nh+'px'; c.style.fontSize = bios.video.text.data.screen.f+'px'; } } var c = document.getElementById('vga-cursor'); if (c) { c.style.top = (y*bios.video.text.data.screen.nh)+'px'; c.style.left = (x*bios.video.text.data.screen.nw)+'px'; c.style.width = bios.video.text.data.screen.nw+'px'; c.style.height = bios.video.text.data.screen.nh+'px'; c.style.fontSize = bios.video.text.data.screen.f+'px'; } } }; bios.video.graphic = { init:function() { }, exit:function() { } }; bios.input.keyboard = { data:{ el:null, enabled:false }, internal:{ rawDown:function(e) { if (e.isComposing) return; var k = { down:true, code:0, char:0, lchr:0 }; if (e.key.length == 1) { k.char = e.key; k.lchr = e.key.toLowerCase(); k.code = k.lchr.charCodeAt(0); }else{ bios.input.keyboard.internal.getKeyData(e.key,k); } if (k.code == 0) return false; e.preventDefault(); bios.input.keyboard.internal.resetFocus(); bios.input.keybuffer.input(k); return false; }, rawUp:function(e) { if (e.isComposing) return; var k = { down:false, code:0, char:0, lchr:0 }; if (e.key.length == 1) { k.char = e.key; k.lchr = e.key.toLowerCase(); k.code = k.lchr.charCodeAt(0); }else{ bios.input.keyboard.internal.getKeyData(e.key,k); } if (k.code == 0) return false; e.target.value = ''; bios.input.keyboard.internal.resetFocus(); bios.input.keybuffer.input(k); return false; }, resetFocus:function(e) { setTimeout(function() { try{ document.getElementById('kbd').focus(); } catch(err) {} },10); return false; }, getKeyData:function(key,k) { // arrow keys are assigned the ascii device control 1/2/3/4 code // this will be changed by the tty switch (key) { case "Down": case "ArrowDown": k.char = -3; k.lchr = k.char; k.code = -3; break; case "Up": case "ArrowUp": k.char = -4; k.lchr = k.char; k.code = -4; break; case "Left": case "ArrowLeft": k.char = -5; k.lchr = k.char; k.code = -5; break; case "Right": case "ArrowRight": k.char = -6; k.lchr = k.char; k.code = -6; break; case "Enter": k.char = String.fromCharCode(10); k.lchr = k.char; k.code = 10; break; case "Esc": case "Escape": k.char = String.fromCharCode(27); k.lchr = k.char; k.code = 27; break; case "Shift": k.char = -2; k.lchr = k.char; k.code = -2; break; case "Tab": k.char = String.fromCharCode(9); k.lchr = k.char; k.code = 9; break; case "Delete": k.char = String.fromCharCode(127); k.lchr = k.char; k.code = 127; case "Backspace": k.char = String.fromCharCode(8); k.lchr = k.char; k.code = 8; break; case 'Control': k.char = -1; k.lchar = k.char; k.code = -1; break; case 'PageUp': k.char = -7; k.lchr = k.char; k.code = -7; break; case 'PageDown': k.char = -8; k.lchr = k.char; k.code = -8; break; case 'Home': k.char = -9; k.lchr = k.char; k.code = -9; break; case 'End': k.char = -10; k.lchr = k.char; k.code = -10; break; default: k.code = key; } } }, getElement:function() { bios.input.keyboard.data.el = document.getElementById('kbd'); if (typeof bios.input.keyboard.data.el === 'undefined' || bios.input.keyboard.data.el == null) { bios.input.keyboard.data.el = document.createElement('textarea'); bios.input.keyboard.data.el.id = 'kbd'; bios.input.keyboard.data.el.className = 'kbd'; //bios.input.keyboard.data.el.style.zIndex = '100'; bios.input.keyboard.data.el.style.display = 'block'; } bios.input.keyboard.data.el.addEventListener('keydown',bios.input.keyboard.internal.rawDown); bios.input.keyboard.data.el.addEventListener('keyup',bios.input.keyboard.internal.rawUp); if (bios.input.keyboard.data.enabled) { bios.input.keyboard.data.el.addEventListener('focusout',bios.input.keyboard.internal.resetFocus); bios.input.keyboard.internal.resetFocus(); } return bios.input.keyboard.data.el; }, init:function() { var el = bios.input.keyboard.getElement(); bios.input.el.appendChild(el); }, enable:function() { bios.input.keyboard.data.enabled = true; bios.input.keyboard.getElement(); }, disable:function() { bios.input.keyboard.data.enabled = false; bios.input.keyboard.getElement(); bios.input.keyboard.data.el.removeEventListener('focusout',bios.input.keyboard.internal.resetFocus); } }; bios.input.upload = { data:{ el:null, enabled:false, reader:null, callback:null, file:null, panel:null }, internal:{ createUploadPanel:function() { if (bios.video.mode.current != 1 || bios.input.upload.data.file == null) return; var l = 28; if (bios.input.upload.data.file.length > l) l = bios.input.upload.data.file.length; var w = l+4; var h = 6; var x = 40-(w/2); var y = 9; bios.input.upload.data.panel = {w:w,h:h,x:x,y:y,d:[]}; for (var i=0; i