diff --git a/clite/vga.js b/clite/vga.js new file mode 100644 index 0000000..239ce86 --- /dev/null +++ b/clite/vga.js @@ -0,0 +1,576 @@ + +var bios = { + data:{ + version:0.1, + vga:null, + id:'BIOS' + }, + lib:{ + code:{ + base:null, + load:function(name,callback) { + var head = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + var file = ''; + if (bios.lib.code.base != null) + file += bios.lib.code.base+'/'; + file +=name+'.js'; + script.type = 'text/javascript'; + script.src = file; + script.onload = callback; + head.appendChild(script); + } + }, + getHex:function(s) { + if (typeof s === 'string') + s = parseInt(s); + if (typeof s !== 'number') + return '0'; + return s.toString(16).toUpperCase(); + }, + writeStringVC:function(x,y,v) { + for (var i=0; i= 80) + break; + bios.video.internal.write(x+i,y,v[i]); + } + }, + writeStringVGA:function(x,y,v,fg) { + for (var i=0; i= 80) + break; + bios.data.vga.write(x+i,y,v[i]); + } + if (typeof fg === 'undefined') + return; + for (var i=0; i= 80) + break; + bios.data.vga.writeSet(1,x+i,y,fg); + } + } + }, + input:{ + el:null, + devices:[], + readers:[], + device:{ + register:function(name,data) { + if (typeof bios.input.devices[name] !== 'undefined') + return false; + if (typeof data !== 'object') + return false; + if (typeof data.element === 'undefined') + return false; + bios.input.devices[name] = data; + bios.input.el = document.getElementById('input'); + bios.input.el.appendChild(data.element); + return true; + }, + deregister:function(name) { + } + }, + reader:{ + register:function(name,caller) { + if (typeof bios.input.devices[name] === 'undefined') + return null; + bios.input.readers[caller] = name; + return bios.input.devices[name]; + }, + deregister:function(name) { + if (typeof bios.input.readers[name] === 'undefined') + return; + delete bios.input.readers[name]; + } + } + }, + output:{ + el:null, + devices:{}, + writers:[], + device:{ + register:function(name,data) { + if (typeof bios.output.devices[name] !== 'undefined') + return false; + if (typeof data !== 'object') + return false; + if (typeof data.element === 'undefined') + return false; + bios.output.devices[name] = data; + if (name === 'vga') + return true; + if (data.element) { + bios.output.el = document.getElementById('output'); + bios.output.el.appendChild(data.element); + } + return true; + }, + deregister:function(name) { + if (typeof bios.output.devices[name] === 'undefined') + return; + if (name !== 'vga' && bios.output.devices[name].element) + bios.output.el.removeChild(bios.output.devices[name].element); + delete bios.output.devices[name]; + } + }, + writer:{ + register:function(name,caller) { + if (typeof bios.output.devices[name] === 'undefined') + return null; + bios.output.writers[caller] = name; + return bios.output.devices[name]; + }, + deregister:function(name) { + if (typeof bios.output.writers[name] === 'undefined') + return; + delete bios.output.writers[name]; + } + }, + }, + io:{ + data:{ + interrupts:{} + }, + read:function(c) { + switch (c) { + case 0: + return bios.data.id; + break; + default:; + } + return null; + }, + write:function(c,d) { + switch (c) { + case 0: + bios.data.id = d; + document.title = d; + break; + case 1: + bios.fault.data.handler = d; + break; + case 2: + bios.io.interrupt.setHandler('kbd',d); + break; + default:; + } + }, + wait:function(cb,ms) { + if (bios.fault.data.faulted) + return; + setTimeout(function() { + try{ + cb(); + }catch(e) { + bios.fault.process(e); + } + },ms); + }, + interrupt:{ + create:function(name,faults) { + if (typeof bios.io.data.interrupts[name] !== 'undefined') + return false; + bios.io.data.interrupts[name] = { + name:name, + crit:faults, + handler:null + }; + return true; + }, + setHandler:function(name,cb) { + if (typeof bios.io.data.interrupts[name] === 'undefined') + return false; + bios.io.data.interrupts[name].handler = cb; + return true; + }, + fire:function(name,data) { + if (typeof bios.io.data.interrupts[name] === 'undefined') + return; + if (bios.io.data.interrupts[name].handler == null) { + if (bios.io.data.interrupts[name].crit) { + bios.fault.process(new Error('Unhandled Interrupt')); + } + return; + } + try{ + bios.io.data.interrupts[name].handler(data); + }catch(e) { + bios.fault.process(new Error('Unhandled Interrupt')); + } + } + }, + setCPU:function(data) {}, + getCPU:function() {} + }, + fault:{ + data:{ + handler:null, + faulted:false + }, + process:function(e) { + if (!bios.fault.data.handler) + return bios.fault.triple(e); + try{ + bios.fault.data.handler(e.message); + }catch(err) { + bios.fault.triple(e); + } + }, + triple:function(e) { + bios.fault.data.faulted = true; + console.log('Triple Fault'); + throw e; + } + }, + init:function() { + bios.io.write(0,'WEB BIOS '+bios.data.version); + bios.video.init(); + bios.lib.writeStringVC(2,0,'JS Basic Input Output System'); + var v = bios.data.version.toString(); + bios.lib.writeStringVC(78-v.length,0,v); + bios.lib.writeStringVC(3,1,'Initialising...'); + bios.lib.writeStringVC(3,2,'Video Core:'); + bios.lib.writeStringVC(25,2,'Done'); + bios.lib.writeStringVC(3,3,'Video Graphics Array:'); + bios.video.initVGA(); + bios.data.vga = bios.output.writer.register('vga','biosvga'); + if (bios.data.vga == null) { + bios.lib.writeStringVC(25,3,'Failed'); + return; + } + for (var i=0; i<80; i++) { + bios.data.vga.writeSet(0,i,0,15); + bios.data.vga.writeSet(1,i,0,0); + } + for (var i=25; i<30; i++) { + bios.data.vga.writeSet(1,i,2,2); + } + bios.lib.writeStringVGA(25,3,'Done',2); + + bios.lib.writeStringVGA(3,4,'Keyboard:'); + bios.keyboard.init(); + bios.lib.writeStringVGA(25,4,'Done',2); + + bios.lib.writeStringVGA(3,5,'Audio:'); + bios.audio.init(); + bios.lib.writeStringVGA(25,5,'Done',2); + + bios.lib.writeStringVGA(3,7,'Seeking Bootable Device...'); + + var d = bios.storage.io.test(); + + for (var i=0; i 79 || y < 0 || y > 24) + return; + var c = bios.video.getPix(x,y); + c.textContent = v; + }, + setBG:function(x,y,id) { + if (id < 0 || id > 15) + return; + var c = bios.video.getPix(x,y); + c.data.bg = bios.lib.getHex(id); + c.className = bios.video.internal.dataToClass(c.data); + }, + setFG:function(x,y,id) { + if (id < 0 || id > 15) + return; + var c = bios.video.getPix(x,y); + c.data.fg = bios.lib.getHex(id); + c.className = bios.video.internal.dataToClass(c.data); + }, + setBlink:function(x,y,id) { + if (typeof id !== 'boolean') + return; + var c = bios.video.getPix(x,y); + c.data.bk = id; + c.className = bios.video.internal.dataToClass(c.data); + }, + writeSet:function(name,x,y,id) { + if (x < 0 || x > 79 || y < 0 || y > 24) + return; + switch(name) { + case 0: + bios.video.internal.setBG(x,y,id); + break; + case 1: + bios.video.internal.setFG(x,y,id); + break; + case 2: + bios.video.internal.setBlink(x,y,id); + break; + default:; + } + }, + dataToClass:function(d) { + var c = 'vga-b'+d.bg+' vga-f'+d.fg; + if (d.bk) + c += ' vga-blink'; + return c; + } + }, + io:{}, + getElement:function() { + bios.video.data.el = document.getElementById('vga'); + if (typeof bios.video.data.el !== 'undefined' && bios.video.data.el != null) + return bios.video.data.el; + bios.video.data.el = document.createNode('div'); + bios.video.data.el.id = 'vga'; + bios.video.data.el.className = 'vga'; + document.body.appendChild(bios.video.data.el); + return bios.video.data.el; + }, + getPix:function(x,y) { + return document.getElementById('vga-'+y+'-'+x); + }, + setMode:function(modedata) { + if (modedata === null || typeof modedata !== 'object') + return false; + if (typeof modedata.write !== 'function') + return false; + if (typeof modedata.el === 'undefined') + modedata.el = bios.video.getElement(); + if (bios.video.data.el == modedata.el) { + modedata.el.innerHTML = ''; + }else{ + if (modedata.el.parentNode != bios.video.data.el) + bios.video.data.el.appendChild(modedata.el); + } + bios.video.io = modedata; + return true; + }, + init:function() { + bios.video.internal.el = bios.video.getElement(); + if (!bios.video.setMode(bios.video.internal)) + return false; + bios.video.data.el.innerHTML = ''; + var ypx; + var xpx; + var c = document.createElement('span'); + c.textContent = 'W'; + bios.video.data.el.appendChild(c); + ypx = c.offsetHeight; + xpx = c.offsetWidth; + bios.video.data.el.innerHTML = ''; + bios.video.data.el.style.width = (xpx*80)+'px'; + bios.video.data.el.style.height = (ypx*25)+'px'; + bios.video.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.data = {bg:'0',fg:'F',bk:false}; + c.id = 'vga-'+y+'-'+x; + bios.video.data.el.appendChild(c); + } + } + bios.video.data.isinit = true; + return bios.video.data.isinit; + }, + initVGA:function() { + var e = bios.video.getElement(); + e.style.cursor = 'none'; + bios.output.device.register('vga',{element:e,write:bios.video.io.write,writeSet:bios.video.io.writeSet,reinit:bios.video.reinit}); + }, + reinit:function() { + } +}; + +bios.keyboard = { + data:{ + el:null + }, + 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.keyboard.internal.getKeyData(e.key,k); + } + //console.log('rawDown: "'+k.char+'":"'+k.lchr+'":"'+k.code+'"'); + if (k.code == 0) + return false; + bios.io.interrupt.fire('kbd',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.keyboard.internal.getKeyData(e.key,k); + } + if (k.code == 0) + return false; + bios.io.interrupt.fire('kbd',k); + return false; + }, + resetFocus:function(e) { + bios.io.wait(function() {document.getElementById('kbd').focus()},1); + return false; + }, + getKeyData:function(key,k) { + switch (key) { + case "Down": + case "ArrowDown": + k.code = 17; + break; + case "Up": + case "ArrowUp": + k.code = 18; + break; + case "Left": + case "ArrowLeft": + k.code = 19; + break; + case "Right": + case "ArrowRight": + k.code = 20; + break; + case "Enter": + k.char = String.fromCharCode(10); + k.lchr = k.char; + k.code = 10; + break; + case "Esc": + case "Escape": + k.code = 27; + break; + case "Shift": + k.code = 15; + 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; + break; + default: + k.code = key; + } + } + }, + getElement:function() { + bios.keyboard.data.el = document.getElementById('kbd'); + if (typeof bios.keyboard.data.el === 'undefined' || bios.keyboard.data.el == null) { + bios.keyboard.data.el = document.createElement('textarea'); + bios.keyboard.data.el.id = 'kbd'; + bios.keyboard.data.el.className = 'kbd'; + bios.keyboard.data.el.style.zIndex = '100'; + bios.keyboard.data.el.style.display = 'block'; + } + bios.keyboard.data.el.addEventListener('keydown',bios.keyboard.internal.rawDown); + bios.keyboard.data.el.addEventListener('keyup',bios.keyboard.internal.rawUp); + bios.keyboard.data.el.addEventListener('focusout',bios.keyboard.internal.resetFocus); + return bios.keyboard.data.el; + }, + init:function() { + var e = bios.keyboard.getElement(); + bios.io.interrupt.create('kbd',false); + // register the device without a read call, as this device fires interrupts + bios.input.device.register('kbd',{element:e,read:null,reinit:bios.video.reinit}); + e.focus(); + bios.keyboard.internal.resetFocus(); + }, + reinit:function() { + } +}; + +bios.audio = { + data:{ + isinit:false, + noped:false, + bell:null, + output:null, + input:null + }, + init:function() { + bios.audio.data.output = new Audio(); + bios.audio.data.bell = new Audio('beep.mp3'); + bios.audio.data.bell.addEventListener("canplaythrough", function(e) { + // play it as soon as possible to test if audio is possible + var promise = bios.audio.data.bell.play(); + if (promise !== undefined) { + promise.then(_ => { + bios.audio.data.isinit = true; + }).catch(error => { + bios.audio.data.noped = true; + }); + } + }); + bios.output.device.register('bell',{element:null,write:bios.audio.bell,writeSet:function() {},reinit:function() {}}); + }, + bell:function() { + if (!bios.audio.data.isinit) + return; + bios.audio.data.bell.play(); + }, + reinit:function() { + } +}; diff --git a/index.html b/index.html index 11a7610..8d26cba 100644 --- a/index.html +++ b/index.html @@ -1,20 +1,17 @@ - CLIte - - - + BIOS + + -
-

CLIte

-
-
-
- -
-
- +
VGA
+
+
+
+
+
+