bring in web bios

This commit is contained in:
Lisa Milne 2023-12-11 15:14:19 +10:00
parent b1f3476fc2
commit b72764963d
2 changed files with 586 additions and 13 deletions

576
clite/vga.js Normal file
View file

@ -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<v.length; i++) {
if (x+i >= 80)
break;
bios.video.internal.write(x+i,y,v[i]);
}
},
writeStringVGA:function(x,y,v,fg) {
for (var i=0; i<v.length; i++) {
if (x+i >= 80)
break;
bios.data.vga.write(x+i,y,v[i]);
}
if (typeof fg === 'undefined')
return;
for (var i=0; i<v.length; i++) {
if (x+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<d.length; i++) {
bios.storage.io.test(d[i]);
}
bios.lib.writeStringVGA(3,8,d.toString());
bios.output.writer.deregister('biosvga');
bios.audio.bell();
bios.proc.init();
},
power:{
off:function() {
var pb = document.createElement('button');
pb.className = 'power';
pb.id = 'power';
pb.type = 'button';
pb.innerText = 'Power On';
pb.onclick = bios.power.on;
var v = document.getElementById('vga');
v.innerHTML = '';
v.appendChild(pb);
},
on:function() {
var pb = document.getElementById('power');
pb.parentNode.removeChild(pb);
setTimeout(bios.init,10);
}
}
};
bios.video = {
data:{
el:null,
isinit:false
},
internal:{
write:function(x,y,v) {
if (x < 0 || x > 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() {
}
};

View file

@ -1,20 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>CLIte</title>
<script type="text/javascript" src="clite/core.js"></script>
<link rel="stylesheet" type="text/css" href="clite/core.css" />
<style type="text/css"></style>
<title>BIOS</title>
<link rel="stylesheet" href="clite/core.css" type="text/css" />
<script type="text/javascript" src="clite/bios.js"></script>
</head>
<body>
<header>
<h1 id="title">CLIte</h1>
</header>
<section class="content">
<div id="terminal">
<noscript>This site requires Javascript.</noscript>
</div>
</section>
<script type="text/javascript">clite.init();</script>
<div class="vga" id="vga">VGA</div>
<div class="io">
<div class="ioblank"></div>
<div class="input" id="input"></div>
<div class="output" id="output"></div>
</div>
<script type="text/javascript">bios.power.off();</script>
</body>
</html>