ilo-vm/source/ilo-js.html

701 lines
16 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<title>ilo.js :: Konilo in the Browser</title>
<meta charset="utf-8">
<script>
// LocalData, (c) 2016 Pawel; MIT Licensed
(function() {
var win = typeof window !== 'undefined' ? window : {}
var indexedDB = win.indexedDB || win.mozIndexedDB || win.webkitIndexedDB || win.msIndexedDB;
if (typeof window !== 'undefined' && !indexedDB) {
console.error('indexDB not supported');
return;
}
var db,
request = indexedDB.open('ldb', 1);
request.onsuccess = function(evt) {
db = this.result;
};
request.onerror = function(event) {
console.error('indexedDB request error');
console.log(event);
};
request.onupgradeneeded = function(event) {
db = null;
var store = event.target.result.createObjectStore('s', {
keyPath: 'k'
});
store.transaction.oncomplete = function (e){
db = e.target.db;
};
};
// if using proxy mode comment this
var ldb = {
ready: false,
get: function(key, callback) {
if (!db) {
setTimeout(function () { ldb.get(key, callback); }, 50);
return;
}
db.transaction('s').objectStore('s').get(key).onsuccess = function(event) {
var result = (event.target.result && event.target.result['v']) || null;
callback(result);
};
},
set: function(key, value, callback) {
if (!db) {
setTimeout(function () { ldb.set(key, value, callback); }, 50);
return;
}
let txn = db.transaction('s', 'readwrite');
txn.oncomplete = function(event) {
var toString$ = {}.toString;
if (toString$.call(callback).slice(8, -1) === 'Function') {
callback();
}
}
txn.objectStore('s').put({
'k': key,
'v': value,
});
txn.commit();
},
delete: function(key, callback) {
if (!db) {
setTimeout(function () { ldb.delete(key, callback); }, 50);
return;
}
db.transaction('s', 'readwrite').objectStore('s').delete(key).onsuccess = function(event) {
if (callback) callback();
};
},
list: function(callback) {
if (!db) {
setTimeout(function () { ldb.list(callback); }, 50);
return;
}
db.transaction('s').objectStore('s').getAllKeys().onsuccess = function(event) {
var result = (event.target.result) || null;
callback(result);
};
},
getAll: function(callback) {
if (!db) {
setTimeout(function () { ldb.getAll(callback); }, 50);
return;
}
db.transaction('s').objectStore('s').getAll().onsuccess = function(event) {
var result = (event.target.result) || null;
callback(result);
};
},
clear: function(callback) {
if (!db) {
setTimeout(function () { ldb.clear(callback); }, 50);
return;
}
db.transaction('s', 'readwrite').objectStore('s').clear().onsuccess = function(event) {
if (callback) callback();
};
},
}
const exported = {
'get': ldb.get,
'set': ldb.set,
'delete': ldb.delete,
'list': ldb.list,
'getAll': ldb.getAll,
'clear': ldb.clear,
};
win['ldb'] = exported
if (typeof module !== 'undefined') {
module.exports = exported;
}
})();
</script>
<script>
let m = new Int32Array(65536);
let blocks = new Int32Array(1024*1024);
var ds = new Int32Array(33);
var as = new Int32Array(257);
var ip = 0;
var sp = 0;
var rp = 0;
var input = "";
function push(v) {
ds[sp + 1] = v;
sp += 1;
}
function pop() {
sp -= 1;
return ds[sp + 1];
}
function download_blocks(int32Array, filename) {
var uint8Array = new Uint8Array(int32Array.buffer);
var blob = new Blob([uint8Array], { type: 'application/octet-stream' });
var link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
URL.revokeObjectURL(link.href);
}
function get_file(f, target) {
fetch(f)
.then(response => response.arrayBuffer())
.then(buffer => {
const dataView = new DataView(buffer);
for (let i = 0; i < target.length; i++) {
target[i] = dataView.getInt32(i * 4, true);
}
})
.catch(error => {
console.error('Error loading the file:', error);
});
}
function upload(file, target) {
var reader = new FileReader();
reader.onload = function(event) {
var arrayBuffer = event.target.result;
var dataView = new Int32Array(arrayBuffer);
target.set(dataView);
};
reader.readAsArrayBuffer(file);
}
function enforce32BitSignedInteger(value) {
const int32Max = Math.pow(2, 31) - 1;
const int32Min = -Math.pow(2, 31);
value = value | 0;
value = ((value - int32Min) % (int32Max - int32Min + 1)) + int32Min;
return value;
}
function guard() {
ds[sp] = enforce32BitSignedInteger(ds[sp]);
if (sp >= 2) {
ds[sp - 1] = enforce32BitSignedInteger(ds[sp - 1]);
}
}
function save_ip() {
rp += 1;
as[rp] = ip;
}
var max_lines = 256;
var cycles = 25000;
function limit_output_lines() {
const display = document.getElementById('display');
const lines = display.value.split('\n').slice(-(1+max_lines));
let k = '';
let i = '';
for (i in lines) {
k = k.concat(lines[i], '\n');
}
display.value = k.substring(0, k.length - 1);
}
async function sleep(ms) {
const display = document.getElementById("display")
display.scrollTop = display.scrollHeight;
console.log('sleep()');
limit_output_lines();
return new Promise((resolve) => setTimeout(resolve, ms));
}
function no() { }
function li() {
ip += 1;
push(m[ip]);
}
function du() {
push(ds[sp]);
}
function dr() {
ds[sp] = 0;
sp -= 1;
}
function sw() {
const a = ds[sp];
ds[sp] = ds[sp - 1];
ds[sp - 1] = a;
}
function pu() {
rp += 1;
as[rp] = pop();
}
function po() {
push(as[rp]);
rp -= 1;
}
function ju() {
ip = pop() - 1;
}
function ca() {
save_ip();
ip = pop() - 1;
}
function cc() {
var a = pop();
if (pop()) {
save_ip();
ip = a - 1;
}
}
function cj() {
var a = pop();
if (pop()) {
ip = a - 1;
}
}
function re() {
ip = as[rp];
rp -= 1;
}
function eq() {
ds[sp - 1] = (ds[sp - 1] == ds[sp]) ? -1 : 0;
sp -= 1;
}
function ne() {
ds[sp - 1] = (ds[sp - 1] != ds[sp]) ? -1 : 0;
sp -= 1;
}
function lt() {
ds[sp - 1] = (ds[sp - 1] < ds[sp]) ? -1 : 0;
sp -= 1;
}
function gt() {
ds[sp - 1] = (ds[sp - 1] > ds[sp]) ? -1 : 0;
sp -= 1;
}
function fe() {
ds[sp] = m[ds[sp]];
}
function st() {
m[ds[sp]] = ds[sp - 1];
sp -= 2;
}
function ad() {
ds[sp - 1] += ds[sp];
sp -= 1;
}
function su() {
ds[sp - 1] -= ds[sp];
sp -= 1;
}
function mu() {
ds[sp - 1] *= ds[sp];
sp -= 1;
}
function di() {
const a = ds[sp];
const b = ds[sp - 1];
ds[sp] = Math.floor(b / a);
ds[sp - 1] = b % a;
if (b >= 0 && ds[sp - 1] < 0) {
ds[sp] += 1;
ds[sp - 1] -= b;
}
}
function an() {
ds[sp - 1] = ds[sp] & ds[sp - 1];
sp -= 1;
}
function or() {
ds[sp - 1] = ds[sp] | ds[sp - 1];
sp -= 1;
}
function xo() {
ds[sp - 1] = ds[sp] ^ ds[sp - 1];
sp -= 1;
}
function sl() {
ds[sp - 1] = ds[sp - 1] << ds[sp];
sp -= 1;
}
function sr() {
ds[sp - 1] = ds[sp - 1] >> ds[sp];
sp -= 1;
}
function cp() {
let l = pop();
let d = pop();
let s = ds[sp];
ds[sp] = -1;
while (l) {
if (m[d] !== m[s]) {
ds[sp] = 0;
}
l -= 1;
s += 1;
d += 1;
}
}
function cy() {
let l = pop();
let d = pop();
let s = pop();
while (l) {
m[d] = m[s];
l -= 1;
s += 1;
d += 1;
}
}
function ioa() {
const a = String.fromCharCode(pop());
document.getElementById("display").value += a;
}
function iob() {
const a = input.charCodeAt(0);
input = input.slice(1);
push(a);
}
function ioc() {
const buf = pop();
const blk = pop();
let i = 0;
while (i < 1024) {
m[buf + i] = blocks[(blk * 1024) + i];
i += 1;
}
}
function iod() {
const buf = pop();
const blk = pop();
let i = 0;
while (i < 1024) {
blocks[(blk * 1024) + i] = m[buf + i];
i += 1;
}
}
function ioe() { }
function iof() {
ip = -1; sp = 0; rp = 0;
get_file('ilo.rom', m);
}
function iog() {
ip = 65536;
input = "";
}
function ioh() {
push(sp); push(rp);
}
function io() {
var dev = pop();
switch (dev) {
case 0: ioa(); break;
case 1: iob(); break;
case 2: ioc(); break;
case 3: iod(); break;
case 4: ioe(); break;
case 5: iof(); break;
case 6: iog(); break;
case 7: ioh(); break;
default: console.log("wtf? " + dev); break;
}
}
const instructions = [
no, li, du, dr, sw,
pu, po, ju, ca, cc,
cj, re, eq, ne, lt,
gt, fe, st, ad, su,
mu, di, an, or, xo,
sl, sr, cp, cy, io
];
function process(o) {
if (o >= 0 && o < instructions.length) {
instructions[o]();
}
guard();
}
async function execute() {
input += " " + document.getElementById("input").value + " \n";
document.getElementById("input").value = "";
let ticks = 0;
while ((ip < 65536) && input.length >= 1 && !isNaN(ip)) {
var o = m[ip];
process(o & 0xff);
process((o >> 8) & 0xff);
process((o >> 16) & 0xff);
process((o >> 24) & 0xff);
ip = ip + 1;
ticks += 1;
if (ticks % cycles === 0) {
ticks = 0;
await sleep(0);
}
}
await sleep(0);
console.log("done");
document.getElementById("display").scrollTop =
document.getElementById("display").scrollHeight;
}
function use_default() {
ip = -1; sp = 0; rp = 0;
execute();
}
async function startup() {
load_prefs();
var load_rom = document.getElementById("rom-file");
load_rom.addEventListener("change", function(event) {
var file = event.target.files[0];
upload(file, m);
});
var load_blocks = document.getElementById("block-file");
load_blocks.addEventListener("change", function(event) {
var file = event.target.files[0];
upload(file, blocks);
});
var enter = document.getElementById("input").addEventListener('keypress', function(event) {
if (event.key === 'Enter') {
execute();
}
});
get_file('ilo.rom', m);
get_file('ilo.blocks', blocks);
await sleep(5000);
use_default();
}
</script>
<style>
:root {
--bg:#050505;
--fg:#ffd;
}
table {
border: 0;
width: 100%;
}
td {
width: 50%;
vertical-align: top;
}
* {
font-family: "Anonymous Pro", "Go Mono", monospace;
padding: 3px;
background: #111;
color: #ffd;
outline-width: 1px;
outline-color: #000;
outline-style: outset;
margin: 2px;
font-size: min(2vh, 2vw);
}
body {
background: #111;
color: #fec;
outline-style: none;
}
textarea {
background: var(--bg);
color: var(--fg);
display: block;
margin-left: auto;
margin-right: auto;
width: min(90vh,90vw);
height: min(55vh,55vw);
font-size: min(2vh, 2vw);
}
input, button {
background: #3b3b3b;
color: #fafada;
}
hr,br {
background: #fff;
margin-bottom: .3vh;
padding-bottom:.005vh;
}
#settings {
display: none;
}
td {
vertical-align: top;
}
</style>
<script>
function localLoad(key, defaultValue) {
const storedValue = localStorage.getItem(key);
return storedValue !== null ? storedValue : defaultValue;
}
function load_prefs() {
let a = localLoad('fg', '#ffb000');
let b = localLoad('bg', '#050505');
document.querySelector(':root').style.setProperty('--fg', a);
document.querySelector(':root').style.setProperty('--bg', b);
cycles = parseInt(localLoad('cycles', 25000));
max_lines = parseInt(localLoad('max_lines', 256));
document.getElementById('input_cycles').value = cycles;
document.getElementById('input_max_lines').value = max_lines;
}
function save_prefs() {
let a = document.querySelector(':root').style.getPropertyValue('--fg');
let b = document.querySelector(':root').style.getPropertyValue('--bg');
localStorage.setItem('fg', a);
localStorage.setItem('bg', b);
localStorage.setItem('cycles', cycles);
localStorage.setItem('max_lines', max_lines);
}
function toggle(x) {
const e = document.getElementById(x);
e.style.display = e.style.display == "block" ? "none" : "block";
}
function default_colors() {
document.querySelector(':root').style.setProperty('--fg', '#ffb000');
document.querySelector(':root').style.setProperty('--bg', '#050505');
}
function green_colors() {
document.querySelector(':root').style.setProperty('--fg', '#33ff33');
document.querySelector(':root').style.setProperty('--bg', '#050505');
}
function cyan_colors() {
document.querySelector(':root').style.setProperty('--fg', '#80ffd5');
document.querySelector(':root').style.setProperty('--bg', '#050505');
}
function plasma_colors() {
document.querySelector(':root').style.setProperty('--fg', '#fe3d14');
document.querySelector(':root').style.setProperty('--bg', '#400f15');
}
function dark_colors() {
document.querySelector(':root').style.setProperty('--fg', '#ffd');
document.querySelector(':root').style.setProperty('--bg', '#050505');
}
function light_colors() {
document.querySelector(':root').style.setProperty('--fg', '#050505');
document.querySelector(':root').style.setProperty('--bg', '#ffd');
}
</script>
</head>
<body onload="startup()"> <!-- startup -->
<button type="button" id="b_settings" onclick="toggle('settings')">Settings</button>
<button type="button" id="boot" onclick="use_default()">
Start Konilo</button> (using default image &amp; blocks)
<hr>
<div id="settings">
<table>
<tr><td>
Use your own image &amp; blocks.<br> <hr> <br>
ROM :::: <input type="file" id="rom-file" style="width:30vw"> <br>
Blocks : <input type="file" id="block-file" style="width:30vw"> <br>
<button type="button" id="b_boot_user" onclick="execute()">Boot</button> <hr>
<button type="button" id="getblocks" onclick="download_blocks(blocks,'ilo.blocks')">Save Blocks to Device</button>
</td><td>
--CONFIG-- <br> <hr> <br>
Max lines :::::::::::::: <input type="number" id="input_max_lines" min="1"
value="256" onchange="max_lines=parseInt(this.value)" style="width:15vw"> <br>
Cycles Between Updates : <input type="number" id="input_cycles" min="1"
value="25000" onchange="cycles=parseInt(this.value)" style="width:15vw"> <br>
<hr> COLORS
<button type="button" id="b_default" onclick="default_colors()">default</button>
<button type="button" id="b_green" onclick="green_colors()">green</button>
<button type="button" id="b_cyan" onclick="cyan_colors()">cyan</button>
<button type="button" id="b_plasma" onclick="plasma_colors()">plasma</button>
<button type="button" id="b_light" onclick="light_colors()">light</button>
<button type="button" id="b_dark" onclick="dark_colors()">dark</button>
<button type="button" id="b_save_prefs" onclick="save_prefs()">Save Prefrrences</button>
</td></tr>
</table>
<hr>
</div>
<textarea rows="24" cols="80" id="display" readonly></textarea>
<hr>
<input type="text" style="width: 80vw" id="input">
<button type="button" id="enter" onclick="execute()">Enter</button>
</body>
<!-- TODO
add custom themes
add indicator for reading/writing blocks
add indicator for processing + animation
-->
<!-- CHANGELOG
Change default cycles value.
Added ability to save config + auto loading on page load.
Amber is now default, previous default under 'dark'.
Modified cyan.
-->
</html>