retroforth/interfaces/retro12.html
crc 05a224fe80 update js implementation
FossilOrigin-Name: 8efe2c73849f02bc753619d3c1f30155dd4634d78a5f5d091a2ddfb0f5d374b6
2018-11-22 03:44:19 +00:00

760 lines
15 KiB
HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>RETRO FORTH</title>
<style type="text/css">
/*<![CDATA[*/
*::-webkit-scrollbar {
width: 0.5em;
}
*::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
}
*::-webkit-scrollbar-thumb {
background-color: #333333;
outline: 1px solid slategrey;
}
body {
padding: 0px;
margin: 0px;
background: #101010;
}
#wrapper {
display: table;
table-layout: fixed;
width:100%;
height:80vh;
background: #101010;
}
#wrapper div {
display: table-cell;
height:100%;
}
#left, #right {
background: #181818;
border: 1px solid #333333;
}
#listenerbar {
background: rgba(255,255,255,0);
height: 5vp;
}
#listener {
margin-top: 8pt;
width: 85%;
margin-left: 1%;
background: #181818;
color: #D6D6D6;
border: 0px;
}
textarea {
height: 100%;
width: 99%;
margin-left: 1%;
background: rgba(255,255,255,0);
color: #D6D6D6;
border: 0px;
}
button {
border: 0px;
height: 6vh;
margin-top: 1vh;
margin-bottom: 1vh;
margin-right: 8px;
margin-left: 8px;
color: #D66A00;
background: rgba(255,255,255,0);
}
button:hover {
color: #FF8C00;
}
* {
font-size: 20pt;
}
/*]]>*/
</style>
<script>
/* Nga ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (c) 2008 - 2018, Charles Childers
Copyright (c) 2009 - 2010, Luke Parrish
Copyright (c) 2010, Marc Simpson
Copyright (c) 2010, Jay Skeer
Copyright (c) 2011, Kenneth Keating
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
var IMAGE_SIZE = 524288 * 4; /* Amount of simulated RAM */
var DATA_DEPTH = 8192; /* Depth of data stack */
var ADDRESS_DEPTH = 32768; /* Depth of the stacks */
function Stack(size) {
this.sp = 0;
this.data = new Array(size);
this.push = function(n) {
this.sp++;
this.data[this.sp] = n;
}
this.pop = function() {
return this.data[this.sp--];
}
this.depth = function() {
return this.sp;
}
this.tos = function() {
return this.data[this.sp];
}
this.nos = function() {
return this.data[this.sp - 1];
}
this.dup = function() {
this.push(this.tos());
}
this.drop = function() {
this.sp--;
}
this.swap = function() {
var a = this.nos();
this.data[this.sp - 1] = this.tos();
this.data[this.sp] = a;
}
this.inc = function() {
this.data[this.sp]++;
}
this.dec = function() {
this.data[this.sp]--;
}
this.reset = function() {
this.sp = 0;
}
}
function Opcodes() {
this.NOP = 0;
this.LIT = 1;
this.DUP = 2;
this.DROP = 3;
this.SWAP = 4;
this.PUSH = 5;
this.POP = 6;
this.JUMP = 7;
this.CALL = 8;
this.CCALL = 9;
this.RETURN = 10;
this.EQ = 11;
this.NEQ = 12;
this.LT = 13;
this.GT = 14;
this.FETCH = 15;
this.STORE = 16;
this.ADD = 17;
this.SUB = 18;
this.MUL = 19;
this.DIVMOD = 20;
this.AND = 21;
this.OR = 22;
this.XOR = 23;
this.SHIFT = 24;
this.ZERO_EXIT = 25;
this.END = 26;
this.IE = 27;
this.IQ = 28;
this.II = 29;
}
var ip = 0;
var data = new Stack(DATA_DEPTH);
var address = new Stack(ADDRESS_DEPTH);
var image = new Array(IMAGE_SIZE);
var vm = new Opcodes();
var instructions = new Array(vm.II + 1);
function rxPrepareVM() {
ip = 0;
data.reset();
address.reset();
}
instructions[vm.NOP] = function() {}
instructions[vm.LIT] = function() {
ip++;
data.push(image[ip]);
}
instructions[vm.DUP] = function() {
data.dup();
}
instructions[vm.DROP] = function() {
data.drop();
}
instructions[vm.SWAP] = function() {
data.swap();
}
instructions[vm.PUSH] = function() {
address.push(data.pop());
}
instructions[vm.POP] = function() {
data.push(address.pop())
}
instructions[vm.JUMP] = function() {
ip = data.pop() - 1;
}
instructions[vm.CALL] = function() {
address.push(ip);
ip = data.pop() - 1;
}
instructions[vm.CCALL] = function() {
var a, b;
a = data.pop();
b = data.pop();
if (b != 0) {
address.push(ip);
ip = a - 1;
}
}
instructions[vm.RETURN] = function() {
ip = address.pop();
}
instructions[vm.EQ] = function() {
var a, b;
a = data.pop();
b = data.pop();
if (b == a)
data.push(-1);
else
data.push(0);
}
instructions[vm.NEQ] = function() {
var a, b;
a = data.pop();
b = data.pop();
if (b != a)
data.push(-1);
else
data.push(0);
}
instructions[vm.LT] = function() {
var a, b;
a = data.pop();
b = data.pop();
if (b < a)
data.push(-1);
else
data.push(0);
}
instructions[vm.GT] = function() {
var a, b;
a = data.pop();
b = data.pop();
if (b > a)
data.push(-1);
else
data.push(0);
}
instructions[vm.FETCH] = function() {
x = data.pop();
if (x == -1)
data.push(data.sp);
else if (x == -2)
data.push(address.sp);
else if (x == -3)
data.push(IMAGE_SIZE);
else
data.push(image[x]);
}
instructions[vm.STORE] = function() {
image[data.tos()] = data.nos();
data.drop();
data.drop();
}
instructions[vm.ADD] = function() {
var x = data.pop();
var y = data.pop();
data.push(x + y);
}
instructions[vm.SUB] = function() {
var x = data.pop();
var y = data.pop();
data.push(y - x);
}
instructions[vm.MUL] = function() {
var x = data.pop();
var y = data.pop();
data.push(y * x);
}
instructions[vm.DIVMOD] = function() {
var b = data.pop();
var a = data.pop();
if (b == 0) {
ip = 0;
data.sp = 0;
address.sp = 0;
} else {
var x = Math.abs(b);
var y = Math.abs(a);
var q = Math.floor(y / x);
var r = y % x;
if (a < 0 && b < 0)
r = r * -1;
if (a > 0 && b < 0)
q = q * -1;
if (a < 0 && b > 0) {
r = r * -1;
q = q * -1;
}
data.push(r);
data.push(q);
}
}
instructions[vm.AND] = function() {
var x = data.pop();
var y = data.pop();
data.push(x & y);
}
instructions[vm.OR] = function() {
var x = data.pop();
var y = data.pop();
data.push(x | y);
}
instructions[vm.XOR] = function() {
var x = data.pop();
var y = data.pop();
data.push(x ^ y);
}
instructions[vm.SHIFT] = function() {
var x = data.pop();
var y = data.pop();
if (x < 0)
data.push(y << (x * -1));
else
data.push(y >>= x);
}
instructions[vm.ZERO_EXIT] = function() {
if (data.tos() == 0) {
data.drop();
ip = address.pop();
}
}
instructions[vm.END] = function() {
ip = IMAGE_SIZE;
}
instructions[vm.IE] = function() {
data.push(1);
}
instructions[vm.IQ] = function() {
data.drop();
data.push(0);
data.push(0);
}
instructions[vm.II] = function() {
data.pop();
var s = String.fromCharCode(data.pop());
document.getElementById('console').value += s;
}
window.addEventListener('load', function(e) {
rxPrepareVM();
}, false);
function processOpcode(opcode) {
if (opcode != 0) {
instructions[opcode]();
checkStack();
}
}
function validatePackedOpcodes(opcode) {
var raw = opcode;
var current;
var valid = -1;
var i = 0;
while (i < 4) {
current = raw & 0xFF;
if (!current >= 0 && current <= 29)
valid = 0;
raw = raw >> 8;
i++;
}
}
function ngaProcessPackedOpcodes(opcode) {
var raw = opcode;
ops = new Array(3);
ops[0] = raw & (255);
raw = raw >> 8;
ops[1] = raw & (255);
raw = raw >> 8;
ops[2] = raw & (255);
raw = raw >> 8;
ops[3] = raw & (255);
processOpcode(ops[0]);
processOpcode(ops[1]);
processOpcode(ops[2]);
processOpcode(ops[3]);
}
var ngaImage = [
<<<IMAGE>>>
];
/* Nga - Retro - JavaScript Interface ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (c) 2008 - 2018, Charles Childers
Copyright (c) 2009 - 2010, Luke Parrish
Copyright (c) 2010, Marc Simpson
Copyright (c) 2010, Jay Skeer
Copyright (c) 2011, Kenneth Keating
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
function loadInitialImage() {
image = ngaImage.slice();
}
function execute(offset) {
var opcode;
address.sp = 1;
ip = offset;
var notfound = d_xt_for("err:notfound");
while (ip < IMAGE_SIZE) {
opcode = image[ip];
if (ip == notfound) {
document.getElementById('console').value +=
"err:notfound : " + string_extract(1471) + "\n";
}
if (validatePackedOpcodes(opcode) != 0) {
ngaProcessPackedOpcodes(opcode);
} else {
alert("Invalid instruction!");
alert("At " + ip + ", opcode " + opcode);
}
if (address.sp == 0)
ip = IMAGE_SIZE;
ip++;
}
}
function checkStack() {
var depth = data.depth();
var adepth = address.depth();
var flag = 0;
if (depth < 0 || adepth < 0) {
flag = -1;
}
if (depth > DATA_DEPTH || adepth > DATA_DEPTH) {
flag = -1;
}
if (flag == -1) {
ip = 0;
data.sp = 0;
address.sp = 0;
}
}
function string_inject(str, buffer) {
var m = str.length;
var i = 0;
while (m > 0) {
image[buffer + i] = str[i].charCodeAt(0);
image[buffer + i + 1] = 0;
m--;
i++;
}
}
function string_extract(at) {
string_data = "";
var starting = at;
var i = 0;
while (image[starting] != 0)
string_data += String.fromCharCode(image[starting++]);
return string_data;
}
function d_link(dt) {
return dt + 0;
}
function d_xt(dt) {
return dt + 1;
}
function d_class(dt) {
return dt + 2;
}
function d_name(dt) {
return dt + 3;
}
function d_count_entries() {
var c = 0;
var i = image[2];
while (image[i] != 0) {
c++;
i = image[i];
}
return c;
}
function d_lookup(name) {
var dt = 0;
var i = image[2];
var dname;
while (image[i] != 0 && i != 0) {
dname = string_extract(d_name(i));
if (dname == name) {
dt = i;
i = 0;
} else {
i = image[i];
}
}
return dt;
}
function d_xt_for(name) {
return image[d_xt(d_lookup(name))];
}
function d_class_for(name) {
return image[d_class(d_lookup(name))];
}
function evaluate(s) {
if (s.length == 0)
return;
var i = d_xt_for("interpret");
string_inject(s, 1471);
data.push(1471);
execute(i);
}
function cls() {
document.getElementById('console').value = "";
}
function unu(src) {
raw = src.split("\n");
var i = raw.length;
var lines = new Array();
var j = 0;
var code = 0;
while (j < i) {
if (code == 1 && raw[j] == "~~~") {
code = 0;
} else if (code == 0 && raw[j] == "~~~") {
code = 1;
} else if (code == 1) {
lines.push(raw[j]);
}
j++;
}
return lines.join(" ");
}
function main() {
rxPrepareVM();
loadInitialImage();
}
function go() {
rxPrepareVM();
loadInitialImage();
document.getElementById("console").value = "";
src = document.getElementById("input").value;
tokens = unu(src).match(/\S+/g);
var i = tokens.length;
var j = 0;
while (j < i) {
evaluate(tokens[j]);
j++;
}
s = "";
i = data.depth();
j = 1;
while (j <= i) {
s = s + data.data[j] + " ";
j++;
}
document.getElementById("console").value += "\n";
}
function listen() {
src = "~~~\n" + document.getElementById("listener").value + "\n~~~\n";
document.getElementById("listener").value = "";
tokens = unu(src).match(/\S+/g);
var i = tokens.length;
var j = 0;
while (j < i) {
evaluate(tokens[j]);
j++;
}
s = "";
i = data.depth();
j = 1;
while (j <= i) {
s = s + data.data[j] + " ";
j++;
}
document.getElementById("console").value += "\n";
}
function saveSnapshot() {
src = document.getElementById("input").value;
console.log('saving ' + src);
localStorage.setItem("Snapshot", src);
}
function loadSnapshot() {
src = localStorage.getItem("Snapshot");
document.getElementById("input").value = src;
}
function newProject() {
document.getElementById("input").value = "";
}
</script>
</head>
<body onLoad='main()'>
<div id="wrapper">
<div id='left'>
<textarea id='input'>
# Syntax
RETRO code consists of a series of whitespace delimited tokens. Each of these can have an optional
prefix telling RETRO how to treat the token. If the token lacks a valid prefix, it will be treated as a
word name.
So:
[prefix][token]
Prefixes are single character modifiers. They are similar to colors in ColorForth, but are handled via
words in the `prefix:` namespace.
The major prefixes are:
@ Fetch from variable
! Store into variable
& Pointer to named item
# Numbers
$ ASCII characters
' Strings
( Comments
: Define a word
Example:
~~~
(This_is_a_comment)
~~~
Define some words:
~~~
:hello (-)
'Hello_World! s:put nl ;
:n:square (n-m)
dup * ;
~~~
And then use them:
~~~
hello
#33 n:square
~~~
Numbers must be prefixed with a # for RETRO to recognize them:
~~~
#2 n:put sp
#3 n:put sp
#4 n:put sp
nl
~~~
A few more things...
~~~
$a (this_is_the_ASCII_'a')
~~~
Strings:
~~~
'Use_underscores_in_place_of_spaces_in_strings
s:put nl
~~~
Strings also allow for formatted output:
~~~
#3 #1 #2 '%n_+_%n_=_%n\n s:format s:put
~~~
Variables:
~~~
'Foo var
#100 !Foo
@Foo n:put
~~~
Note that RETRO will only run code in a fenced block (starting and ending with ~~~). This allows for
code and documentation to be intermixed easily.
The full glossary can be browsed:
http://forthworks.com:9999 or gopher://forthworks.com:9999
</textarea>
</div>
<div id='right'>
<textarea id='console'></textarea>
</div>
</div>
<div id='listenerbar'>
<input id='listener'>
<button onclick="listen()">Eval</button>
</div>
<div id="toolbar">
<button onclick="newProject()">New</button>
<button onclick="saveSnapshot()">Save</button>
<button onclick="loadSnapshot()">Load</button>
<span style="float: right">
<button onclick="cls()">Clear</button>
<button onclick="go()">Go</button>
</span>
</div>
</body>
</html>
<!--
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
-->