<!-- HAL -->
<!DOCTYPE html>
<html>
<!-- Hi I am a comment -->
<style >
body {background-color: gray;}
td { overflow='clip';}
textarea
{background-color: green; color: white; font-size: 25px; padding = 10px}
canvas {background-color=navy;}
th { color:black}
select { color:black;background-color : lime}
table {color: blue; text-align: center; border : 2px;}
button {background-color : red }
h1 {background-color : green; font-size : 24px; }
ul {background-color : blue; color : white; border : 4px ; text-align : center}
caption {background-color : yellow;font-size:20px;}
p {background-color : yellow; color: black;font-size: 15px;
text-align: center; vertical-align : top; width " 70"; hidden=true}
</style>
<h1 align='center'>HAL</h1>
<form id = 'dummy' >
<button onclick ='ok()'>OK</button>
<button onclick ='cancel()'>Cancel</button>
<textarea cols=50 rows = 3 font-size = 14px>Javascript libraries use this part of the screen for user input. The dialog docking area. Operators hide this window as desired.</textarea>
</form>
<div align = left>
<div>
<table >
<caption>Simple Menu System V1.77</caption>
<tbody >
<tr>
<td>
<textarea id = 'selectedItem' cols=10 rows=1>Top</textarea>
</td>
<td>
<button id ='menuNext' >Next</button>
<button id ='menuDown' >Down</button>
<button id ='menuUp' >Up</button>
</td>
<td width=200></td>
</tr>
</tbody>
</table>
</div>
</div>
<br>
<table id = 'Display1' align = left>
<caption>Incoming Connection Reports</caption>
<tbody class = 'Display' id = 'Displays' >
<tr>
<td ><textarea class = 'Display' cols=15 id = "D1"> Frequ 10</textarea></td>
<td><textarea class = 'Display' cols=15 id = "D2"> Power 10</textarea>
</td>
</tr>
<tr>
<td id = 'connectTop'>
<button name = 'B11' > B11</button><button name = 'B12' > B12</button><button name = 'B13'> B13</button> <button name = 'B14'> B14</button>
</td>
<td id = 'connectBottom'>
<button name = 'B21' > B21</button><button name = 'B22' > B22</button><button name = 'B23' > B23</button><button name = 'B24'>B24</button>
</td>
</tr>
</tbody>
</table>
<div >
<table >
<colgroup>
<col valign=top/>
<col valign=top/>
</colgroup>
<caption>Ham Assembly Language</caption>
<tbody >
<tr>
<th id= 'contexts'>
<select name= "textMethods" onchange = "actionText(this)">
<option onclick = 'saveText()'>Save</option>
<option onclick = 'closeText()'>Close</option>
<option onclick = 'openText()'>Open</option>
<option onclick = 'createText()'>Create</option>
</select>
</th>
<th>Log</th>
<th id = 'sources' contenteditable=true>Sources</th>
</tr>
<tr id = 'textareas' valign=top>
<td>
<ul class = 'visible' id = 'macroList'></ul>
</td>
<td>
<textarea class = 'visible' id = 'logger' cols="35" rows="25" border = 2 >Logger </textarea>
</td>
<td id = "textanchor"></td>
<td>
</td>
</tr>
</tbody>
</table>
</div>
<ul class = 'top' id = 'simpleMenu' hidden>
<ul class = 'Debugger'>
<li class = 'debug' onclick = 'DebugStep(INIT)'>Init</li>
<li class = 'debug' onclick = 'DebugStep(TOKEN)'>Token</li>
<li class = 'debug' onclick = 'DebugStep(LINE)'>Line</li>
</ul>
<ul class= "usb">
<li class = "method" onclick ="USBConnect(current)">Connect</li>
<li class = "method" onclick = "openport()">open</li>
<li class = "method" onclick ="getports()">Ports</li>
<li class = "method" onclick ="getport()">Port</li>
<li class = "method" onclick ="readport()">Read</li>
<li class = "method" onclick ="writeport()">Write</li>
<li class = "method" onclick ="closeport()">Close</li>
<li class = "method" onclick ="infoport()">Info</li>
<li class = "method" onclick ="freeport()">Free</li>
</ul>
<li class = 'Exec' onclick ="ExecSource(current);">RUN</li>
<ul class = 'Assembler'>
<li class = 'list' onclick ="listLibrary()">Library</li>
<li class = 'list' onclick ="listOpcodes()">Opcodes</li>
<li class = 'list' onclick ="listSysOps();">Sysops</li>
<li class = 'list' onclick ="listSymbols();">Symbols</li>
</ul>
</ul>
<div id = 'startcode'>
#log nice start;
add r0 hope;
#log end;
hope sub r0 r2;
#log done it;
</div>
<script>
//tokens
// tokens can have various types
// with precedances
// / scoping rules
const NEWLINE = 32;
const LITERAL = 20;
const VAL = 19; const NUM = 18; const REG = 17;
const MUL = 16; const DIV = 15;
const ADD = 14; const SUB = 13;
const LP = 12; const RP = 11;
const COMMA = 10;
const MACRO = 9;
const OP = 8; const SEMI = 7;
const ALPHA = 6;
const SYSOP = 5
const DISCARD = 4;
const QUOTE = 3;
// return status
const OK = 101; const DONE =102; // normal or source done
const WAIT = 103; const ERROR = -3; // forward reference or syntax error
// symbol classes
const USER = 40; const HAL = 41; const CONST = 42;
const EMBED = 43; const MODULE = 44; const LIB = 45;
// step indicators
const TEST = 56; const INIT = 55; const INDEX = 54;
const LINE = 53; const TOKEN = 52; const OFF = 51;
// symbol,states
const NULL = -1; const UNDEF = -2;
const DEF = 202; const PREDEF = 203;
const LOG = document.getElementById('logger');
</script>
<script id='halinone'>
//bigtable
// const USER,OP,SYSOP,EMBED,MODULE,LIB / scoping rules
// All symbols in one table, sysops, opodes, user symbols
const Symbol = {str : "",expr : 0,type : 0,value : 0,handle : null}
var symtable = [
{str : 'R0',expr : RegExp(/R0/,'i'),type : USER, value : 0,handle : 0},
{str : 'R1',expr : RegExp(/R1/,'i'),type : USER, value : 1,handle : null},
{str : 'R2',expr : RegExp(/R2/,'i'),type : USER, value : 2,handle : null},
{str : 'R3',expr : RegExp(/R3/,'i'),type : USER, value : 3,handle : null},
{str : 'define', expr : RegExp(/define/), type : SYSOP, value : 0,handle : function(){return define()}},
{str : 'assign', expr : RegExp(/assign/), type : SYSOP,value : 0,handle : function(){return assign() }},
{str : 'include', expr : RegExp(/include/),type : SYSOP,value : 0,handle : function(){return include()}},
{str : 'end', expr : RegExp(/end/), type : SYSOP,value : 0,handle : function(){return OK}},
{str : 'log', expr : RegExp(/log/), type : SYSOP,value : 0,handle : function(){return log()}},
{str : 'ifdef', expr : RegExp(/ifdef/), type : SYSOP,value : 0,handle : function(){return OK }},
{str : 'else', expr : RegExp(/else/) , type : SYSOP,value : 0,handle :
function(){return OK }},
{str : 'seg', expr : RegExp(/seg/) , type : SYSOP,value : 0,handle :
function(){return OK }},
{str : 'add', expr : RegExp(/add/), type : OP, value : 0,handle : null},
{str : 'sub', expr : RegExp(/sub/), type : OP, value : 0,handle : null},
{str : 'mul' ,expr : RegExp(/mul/), type : OP, value : 0,handle : null},
{str : 'div' ,expr : RegExp(/div/), type : OP, value : 0,handle : null},
{str : 'shft',expr : RegExp(/shft/),type : OP, value : 0,handle : null},
{str : 'and', expr : RegExp(/and/), type : OP, value : 0,handle : null},
{str : 'or' ,expr : RegExp(/or/), type : OP, value : 0,handle : null},
{str : 'xor' ,expr : RegExp(/xor/), type : OP, value : 0,handle : null},
{str : 'addi',expr : RegExp(/addi/),type : OP, value : 0,handle : null},
{str : 'subi',expr : RegExp(/subi/),type : OP, value : 0,handle : null},
{str : 'ori', expr : RegExp(/ori/), type : OP, value : 0,handle : null},
{str : 'xori',expr : RegExp(/xori/),type : OP, value : 0,handle : null},
{str : 'clr' ,expr : RegExp(/clr/), type : OP, value : 0,handle : null},
{str : 'comp',expr : RegExp(/comp/),type : OP, value : 0,handle : null},
{str : 'jmp' ,expr : RegExp(/jmp/), type : OP, value : 0,handle : null},
{str : 'call',expr : RegExp(/call/),type : OP, value : 0,handle : null},
{str : 'bre' ,expr : RegExp(/br/) , type : OP, value : 0,handle : null},
{str : 'brn' ,expr : RegExp(/brn/), type : OP, value : 0,handle : null},
{str : 'br' ,expr : RegExp(/brp/), type : OP, value : 0,handle : null},
{str : 'ret' ,expr : RegExp(/ret/), type : OP, value : 0,handle : null},
{str : 'btst',expr : RegExp(/btst/),type : OP, value : 0,handle : null},
{str : 'bset',expr : RegExp(/bset/),type : OP, value : 0,handle : null},
{str : 'ld' ,expr : RegExp(/ld/), type : OP, value : 0,handle : null},
{str : 'ldm' ,expr : RegExp(/stm/), type : OP, value : 0,handle : null},
{str : 'ldw' ,expr : RegExp(/ldw/), type : OP, value : 0,handle : null},
{str : 'st' ,expr : RegExp(/st/), type : OP, value : 0,handle : null},
{str : 'stm' ,expr : RegExp(/stm/), type : OP, value : 0,handle : null},
{str : 'stw' ,expr : RegExp(/stw/), type : OP, value : 0,handle : null}
]
var symLength = symtable.length;
function listSymbols(type) {
clearLog();
strLog('\n Symbols ');
for(let i=0; i < symtable.length;i++)
if(symtable[i] = type)
strLog('\n: ' + i +' ' + symtable[i].str + ' '+symtable[i].expr +' '+symtable[i].value );
}
function symValue(index) {
if(index == false) index =0;
if((index< 0) || (index > symtable.length)) return syntaxError('Bad index');
return symtable[index].value;
}
function addSym(name,type,value) {
var index = findSym(name)
if(index > -1) {
symtable[index].expr = RegExp(name);
symtable[inde].type = type
symtable[index].value = value;
}
else{
var sym = Object(Symbol);
sym.str =name;
sym.expr = RegExp(name)
sym.type = type;
sym.value = value;
symtable.push(sym);
}
return OK;
}
function findSym(name) {
len = name.length;
for(let i =0; i < symtable.length;i++) {
if(len == symtable[i].str.length)
if(symtable[i].expr.test(name)) {
return i;
}
}
return UNDEF;
}
// Drop entries from user symbols
function delSym() {
do {
if(nextToken() < 0) return syntaxError('No token')
if(index = findSym(cc.slice) < 0)
break;
var type = cc.type;
if(type == SEMI) break;
symtable.splice(index,1);
} while(type != SEMI);
return endOfLine();
}
// assembler found a symbol
// a finite string of alpha numerics
// that begins with an alpha
function symbol(opContext) {
var index;
if(arguments.length == 0) {// symbol defined
console.log('New symbol');
index=newSym(cc.slice(),cc.line())
symtable[index].state = DEF;
symtable[index].value = cc.line();
return waitService(i);
} else { // this maybe a wait
if( (index = findSym(cc.slice())) < 0){
opContext.index =newSym(cc.slice());
return waitRequest(opContext); // opcode context pushed onto wait stack
} else {
if(symtable[index].state == UNDEF) {
opContext.index = index;
return waitRequest(opContext); // opcode context pushed onto wait stace
} else {
cc.setIndex(symtable[index].value)
cc.setType(VAL); //resolved
return OK
}
}
}
}
// text
function clearLog() {
LOG.textContent = ' ';
}
// send string to log
function strLog(str){
LOG.textContent+= str;
}
// send string to log
function strPrependLog(str){
LOG.textContent = str + LOG.textContent;
}
function srcCharByIndex(node,index) {
var ch = node.textContent.charAt(index);
return ch
}
function tokenText(str) {
var s= String();
s = '|'+cc.slice()+'|';
//s.replace(/\n/,'X')
if(arguments > 1) s += str;
strPrependLog(
'\nTok ' + s + ' ' + ' t: ' + cc.type()+' s: '+cc.start()+ ' e: ' + cc.end()+'\n' );
}
// generic find a name among
// a list of html children
function findItemByName(items,name) {
for(i=0;i < items.length;i++)
if(items[i].name == name)
return items[i];
return null;
}
function findText(name) {
items = document.getElementById('textanchor').children;
return findItemByName(items,name);
}
function findListItem(name) {
items = document.getElementsByClassName('openmacro');
return findItemByName(items,name);
}
// when the user clicks on a macro list item
function setListEvent(node) {
node.addEventListener('click', () => {
showText(node.name);
blinkListItems(node.name);
//console.log('Set up ' +node.name)
});
}
function newListItem(name) {
var list = document.createElement('li')
list.name = name;
list.className = 'openmacro'
list.textContent = name
//console.log('NewItem '+ name);
setListEvent(list);
document.getElementById('macroList').appendChild(list);
showText(name);
}
function delListItem(name) {
parent = document.getElementById('macroList');
node = findName(name,parent.children);
parents.removeChild(node);
}
////////////// Methods to manage textareas////////////////
function listTexts() {
var items = document.getElementById('textanchor').children;
for(let i =0; i < items.length;i++) {
strLog(': '+ items[i].name);
}
}
function saveText() {
strPrependLog('Save\n');
var nameNode = document.getElementById('sources');
var textNode = findTextByName(nameNode.textContent);
if(textNode.hidden == false) {// The macro in focus
localStorage.setItem(nameNode.name,textNode.textContent);
nameSelector();
}
return;
}
// th element as editable input
function newText(name) {
strPrependLog('New\n');
if(name == null)
name = document.findElementByID('sources').textContent;
addText(name);
}
function openText(name) {
strPrependLog('Open\n');
}
function closeText() { strPrependLog('Close\n');
parent = document.getElementById('macroList');
name = document.getElementById('sources').textContent;
node = nodeFindByName(parent.chldren,name);
parent.removeChild(node);
parent = documemt.getElementById('textanchor');
node = nodeFindByName(parent.chldren,name);
parent.removeChild(node);
}
function showText(name) {
items = document.getElementById('textanchor').children;
for(i=0;i < items.length;i++) {
if(items[i].name == name)
items[i].hidden=false;
else
items[i].hidden=true;
document.getElementById('sources').textContent = name;
}
}
function createText(name) {
var td = document.getElementById('textanchor');
var text = document.createElement('textarea');
text.hidden = false;
text.cols =30
text.rows = 15
if(name == null)
text.name = document.getElementById('sources').textContent;
else
text.name = name;
text.className = 'invisible';
td.appendChild(text);
showText(text.name);
return text;
}
function context() {
this.hidden = true;
this.nextSibling().hidden = false;
//this.parent.onchange();
}
function actionText(node) {
var items = node.children;
for(i=0;i < items.length;i++)
if(items[i].selected) {
items[i].onclick(items[i].value);
return;}
}
function blinkListItems(name) {
items = document.getElementById('macroList').children;
radioButtons(name,items);
}
function source(name) {
getElementById('sources').textContent=name;
showTextArea(name);
console.log('arrived');
}
// Make a name list from local storage
function nameSelector() {
var len = localStorage.length;
parent = document.getElementById('sources');
var sel = document.createElement('select');
sel.hidden = true;
sel.onchange = 'source()'.
parent.append(sel);
for(let i = 0;i < len;i++) {
name = localStorage.key(i);
var opt = document.createElement('option');
opt.textContent = name;
sel.appendChild(opt)
}
opt = document.createElement('option');
opt.textContent='rename';
sel.appendChild(opt);
}
function listLibrary() {
clearLog();
var len = localStorage.length;
strLog('\nLocal storage ' + len)
for(let i =0;i < len;i++) {
var name = localStorage.key(i);
value = localStorage.getItem(name);
strLog('\n '+i+': ' +name + ' '+ value);
}
}
// sysops
//Install a module from local storage
// put up a selector for it, add its own
// text area, and install the name
// on the sysop table
function installModule(name,str) {
console.log('Install');
if(arguments.length == 2)
localStorage.setItem(name,str);
var index = findSym(name); // is it installed?
if((index > -1) && (index < builtinsLength))
return syntaxError('Opcode redefined i '+index+' '+builtinsLength+'|'+name+'|')
if(index >= 0) { // already in the opcode table
node= findListItem(name);
if(node==null) syntaxError('Library misorder')
node = findText(name)
if(node== 0) syntaxError('Set up wrong')
} else { // new set up
strPrependLog('\nNew set '+name);
node = createText(name);
console.log('Installed '+node.name + ' '+name);
addSym(name);
newListItem(name);
}
node.textContent=localStorage.getItem(name);
return OK;
}
// drop an entry in the sysop table
function undef() {
remove(delSys); }
// in user table
function remove() {
remove(delSym); }
function define() {
do {
if(nextToken() < 0) return syntaxError('No token')
newSysop(cc.slice());
}while(cc.type() != SEMI)
}
// manipulate the user symbol table
function assign(){
do {
if(nextToken() < 0) return syntaxError('No token')
type = cc.type();
if(type() == SEMI) break;
token1 = cc.slice();
if(nextToken() > 0) {
type = cc.type()
if(type ==ARG)
newSym(token1,args.pop());
else if(type = NUM)
newSym(token1,JSON.parse(cc.slice()));
} else
newSym(token1,UNDEF);
strPrependLog('Define: '+ token1 + ' ' + symValue(findSym(token1))+'\n')
} while(cc.type() != SEMI);
//nextToken(); //pass the semi
return endOfLine();
}
function log(ptr){
var start,end;
var eol = endOfLine(); // move the newline pointer
//console.log('log ' + newline.lastIndex + ' '+ SRC.textContent.length);
start = cc.end();
if(newline.lastIndex !== 0)
end = newline.lastIndex-1;
else
end = cc.node().textContent.length;
strPrependLog('\n '+ cc.slice(start,end)+'\n');
return eol;
}
function newSysop(name) {
var i=0;
var symbol = new Object(opCode);
i = findSym(name,optable)
console.log('New sys '+name + ' '+i)
if(i < 0) {// New entry
symbol.str = name;
symbol.expr = RegExp(name);
symbol.idx=optable.length+1;
optable.push(symbol);
opcodesLength = optable.length;
}
return OK;
}
function delsysop(name) {
i = findSym(name,optable);
if(i < 0) return OK;
optable.splice(i,1);
return OK;
}
function include(){
var node;
console.log('Include');
do {
r=nextToken()
var type = cc.type();
if(type == SEMI)
break; // move the newline pointer
if(r < 0) return syntaxError('No token');
if(type != ALPHA) return syntaxError('Expected Name');
var name = cc.slice();
installsysop(name);
strPrependLog('Add sysop '+ name+'\n')
} while(type != SEMI);
return OK;
}
// Bracket quoted HAL must be valid
// Should end with end; pseudo opcode
function macro() {
var index = nextToken();
if(index > -1) syntaxError('cannot redefine');
token = cc.slice();
nextToken();
if(cc.type() != LB) syntaxError('No start');
newOpcode(token,cc.end());
while(true) {
nextToken()
if(cc.type() == RB)
break;
endOfLine(); // skip the line
}
}
</script>
<script>
// sneak in a fake source on startup
function sneak() {
var str = document.getElementById('startcode').textContent
installModule('source',str);
}
sneak();
</script>
<script>
// exec
//Define a context of some type of token
// must include the node holding textContent,
// and a start, end, lastIndex
// scanned in a textArea of node, possibly
//having a symbol index pointing into the symbol table
// .
const Context = { start : 0, end : 0, type : 0,
node : 0,index : 0,line:1,lastIndex:0}
var CS = Array();
const cc = {
k : -1,
version : function(){ strPrependLog('V1.0\n')},
length : function(){ return CS[this.k].end - CS[this.k].start;},
get : function() {return CS[this.k]},
clear : function(){CS.length =0; this.k =-1;},
incLine : function(){ CS[this.k].line++;return CS[this.k].line;},
line : function(){ return CS[this.k].line;},
push : function(x){ this.k++; return CS.push(x)},
pop : function(){ this.k--; return CS.pop()},
startChar : function(){ return CS[this.k].node.textContent.charAt(CS[this.k].start)},
endChar : function(){ return CS[this.k].node.textContent.charAt(CS[this.k].end)},
indexChar : function(index) { return CS[this.k].node.textContent.charAt(index);},
setStart : function(start) {CS[this.k].start =start;},
incStart : function(start) {CS[this.k].start++;},
setLastIndex : function() {CS[this.k].lastIndex =CS[this.k].end;},
start : function(start) {return CS[this.k].start;},
setIndex : function(index) {CS[this.k].index =index;},
setEnd : function(end) { CS[this.k].end =end;},
end : function() {return CS[this.k].end;},
node : function() {return CS[this.k].node;},
setType : function(type) { CS[this.k].type =type;},
type : function() {return CS[this.k].type;},
text : function() { return CS[this.k.node.textContent];},
slice : function(start,end){
var s,e;
var i = this.k;
if(arguments.length > 0) s = start; else s = CS[this.k].start;
if(arguments.length > 1) e = end; else e =CS[this.k].end;
return CS[this.k].node.textContent.slice(s,e);
},
twoChars : function(){
var s = CS[this.k].end; // aftr the newline
return CS[this.k].node.textContent.slice(s,s+2);
}
}
// Initialize the assembler
function initHal() {
tokend.lastIndex =0;
newline.lastIndex =0;//
clearLog();
resetRpn();
//resetWaits();
return OK;
}
function endOfLine() {
newline.lastIndex = tokend.lastIndex;
newline.exec(cc.node().textContent)
if(newline.lastIndex !== 0) {
// Do not allow parser to chew past arguments
tokend.lastIndex = newline.lastIndex;
return OK;
}else return DONE;
}
// everything needed to process textContent
function newContext(node) {
var p = new Object(CONTEXT);
var x =0;
p.lastIndex = 0;
p.line = 1;
p.start = 0;
p.end = 0;
p.type = DISCARD;
p.node = node;
p.name = node.name;
cc.push(p);
// Newline required here
if(cc.indexChar(0) != '\n') return syntaxError('No starting newline');
strLog('New Context ' + node.name);
return endOfLine();
}
function execLines(){
var result=OK;
var count = 0;
while( (result == OK) || (result == WAIT)) {
count++;
result = firstColumn();
if(result==DONE)
return strPrependLog('\nSource done');
}
syntaxAlert('Assembly error ' + result)
}
function ExecSource() {
node = findText('source')
if(node == null) return syntaxError('Nosource');
initHal();
console.log('CC ' +cc.k)
newContext(node);
execLines();
return OK;
}
function listToken() {
var result = nextToken();
if(result == OK)
tokenText();
return result;
}
function listTokens() {
var result=OK;
var str;
clearLog()
strPrependLog('\n...All tokens....');
cc.setStart(0);
cc.setEnd(1);
tokstart.lastIndex =0;
tokend.lastIndex = 0;
newline.lastIndex =0;
//endOfLine(); // to starting line
do result = listToken(); while(result == OK);
strPrependLog('End of source');
return result;
}
function DebugStep(as) {
var result;
switch(as) {
case TEST:
e = findText('source');
if(e.hidden == 1) delete e.hidden;
else e.hidden = 1;
break;
case TOKEN :
if(listToken() == DONE) InitHal();
break;+tokend.lastIndex
case LINE :
if(nextToken() != DONE);
result = firstColumn();
//console.log('col end |'+cc.slice()+'|'+tokend.lastIndex);
break;
case INDEX:
break;
case INIT:
var node = findText('source');
if(node == null) console.log('Nosource');
initHal();
newContext(node);
break;
}
}
// forward reference array
var waits = new Array();
function waitService(index) {
var n = waits.length;
console.log('Wait service len ' + n)
for(let i = 0; i < n;i++)
if(waits[i].index == index) {
cc.push(waits[i]);
console.log('Service '+tokenText())
waits.splice(i,1);
argsProcess();
CS.pop();
}
return OK;
}
function waitRequest(context,index) {
console.log('Wait request');
context.index =index;
waits.push(context);
return WAIT;
}
function opProcess(token){}
function funProcess(token) {
var result = 0;
var textNodes = document.getElementsByClassName('invisible');
var el = findName(textNodes,optable[token.index].str);
initHal();
result = newContext(el);
while(result != DONE) {
result = firstColumn();
}
return OK;
}
//
// Global and local regx
const newline = RegExp(/\n/ ,'g'); // a regx to find end of line
const tokstart = RegExp(/\S|\n/, 'g');
const semi = RegExp(/;/,'g');
const tokend = RegExp(/\W/,'g'); // no alpha or numeric
const quote = RegExp(/\"/,'g'); // open literal
const bracket = RegExp(/\{\}/,'g'); // open literal
// local operators
const alum = RegExp(/\w/); // alpha or numeric
const space = RegExp(/\t| /); // valid separators
const nospace = RegExp(/\S/); // drk
const operator = RegExp(/[;\*\\\+\-#\(\)]/);
const white = RegExp(/\s/); //not words
const primes = [
{ str : 'register', expr : RegExp(/r\d/), type : REG},
{ str : 'alpha', expr : RegExp(/[a-zA-Z]/),type : ALPHA},
{ str : 'number', expr : RegExp(/[0-9]/),type : NUM},
{ str : 'sysop', expr : RegExp(/#/),type : SYSOP},
{ str : 'muliply', expr : RegExp(/[\*]/),type : MUL},
{ str : 'divide', expr: RegExp(/[\\]/),type : DIV},
{ str : 'add', expr : RegExp(/[\+]/),type : ADD},
{ str : 'sub', expr : RegExp(/[\-]/),type : SUB},
{ str : 'comma', expr : RegExp(/\,/),type : COMMA},
{ str : 'semi', expr : RegExp(/;/),type : SEMI},
{ str : 'left', expr : RegExp(/\(/),type : LP},
{ str : 'right', expr : RegExp(/\)/),type : RP},
{ str : 'newline', expr : RegExp(/\n/),type : NEWLINE}
]
const primeLength = primes.length;
function regtest(regx,str){
let x = regx.test(str);
return x;
}
// errors
function syntaxAlert(msg) {
var l;
alert(msg +' type '+cc.type()+' '+cc.start()+' '+cc.end()+' '+cc.slice()+'\n')
return OK;
}
// errors
function syntaxError(msg) {
var l;
strPrependLog(msg +' '+cc.start()+' '+cc.end()+' '+cc.slice()+'\n')
return ERROR;
}
function typeGet(index,str){
let x = primes[index].expr.test(str);
if(x) return primes[index].type;
else return ERROR;
}
function updateToken(index) {
var type,ch;
ch = cc.indexChar(index);
for(i=0;i < primeLength;i++)
if( (type = typeGet(i,ch)) > -1){
cc.setType(type);
return cc.type();
}
return syntaxError('No type |'+ch+'|' );
}
//args process found a quote character
function literal() {
cc.setStart(cc.end());
quote.lastIndex = cc.start();
quote.exec(cc.text());
if( (quote.lastIndex ==0) || (quote.lastIndex >= newlne.lastIndex) )
return syntaxError('missing quote');
cc.setEnd(quote.lastIndex-1);
rpn();
return OK;
}
// The function sets the start and end indices
// for a token of three classes:
// numeric, alphanumeric and single char operators
// New lin3 is a valid operator, right associated.
// It changes assmbler state based on the next two chars.
function nextToken() {
SRC= cc.node();
if(SRC ===null) syntaxError('No source');
var ch,index,result;
tokstart.lastIndex = tokend.lastIndex-1; // get end of last token
if(regtest(space,SRC.textContent.charAt(tokstart.lastIndex) )){
tokstart.exec(cc.node().textContent)
if(tokstart.lastIndex == 0) return DONE;
} else tokstart.lastIndex++;
updateToken(tokstart.lastIndex-1);
tokend.lastIndex = tokstart.lastIndex; // get start of current token
if(regtest(alum,SRC.textContent.charAt(tokend.lastIndex-1)) ){
tokend.exec(SRC.textContent);
if(tokend.lastIndex == 0) return syntaxError('No line terminator')
} else tokend.lastIndex++; //must be a single char
cc.setStart(tokstart.lastIndex-1);
cc.setEnd(tokend.lastIndex-1);
return OK;
}
//First argument always an opcode
// handle opcode and multiple arguments
// symbol argument, literals, end of line
function argsProcess() {
opContext = cc.get(); // restart here on forward reference
if(nextToken() == DONE) return DONE;
if(cc.type() == NEWLINE) { //blank line
return OK;
}
// opcode required here
index = findSym(cc.slice());
if( index< 0) return syntaxError('\nNo op ' + cc.slice())
rpn(); // process opcode
let type = cc.type();
while((type != SEMI) && (type != NEWLINE )) {
if(nextToken() == ERROR) return syntaxError('Or a bug'); // line done
type = cc.type();
if(type == QUOTE) { // literals shipped as is.
literal(); // one literal per line
break;
} else if(type == ALPHA)
if(symbol(opContext ) == WAIT ) {
return endOfLine(); // fludh to end of line
}
// ready to activate rpn)
if(type > OP )
rpn();
}
return OK;
//endOfLine();
}
//the currnt token is newline
// which triggers first column call
function firstColumn() {
var result;
if(cc.type() != NEWLINE) return syntaxError('Col bug');
var twochars = cc.twoChars();
// arrive here once per line and never more
cc.incLine(); // Every context keeps its own line counter
if(white.test(twochars)) // if no column one work
return argsProcess(); // pass it on
console.log('after args |'+cc.slice()+'|'+tokend.lastIndex);
nextToken(); // there is column one work
if(cc.type() == SYSOP) {
if(nextToken() < 0)return syntaxError('No sysop ');
let index = findMacro(cc.slice());
if(index < 0)
return syntaxError('Undefined sysop ');
else {
//console.log('Sysop '+cc.slice())
return mactable[index].handle();
}
}
// source defines symbol
else if(cc.type() == ALPHA) {
// we just set the symbol to the current lineZ
symbol();
return argsProcess();
}
else
return syntaxError('Illegal char ');
}
if ("serial" in navigator) {
// The Web Serial API is supported.
}
var port;
async function getport() {
console.log('get port')
port = await navigator.serial.requestPort();
if(port)
var info = port.getInfo();
else console.log('no port')
console.log('info '+ info)
}
// Get all serial ports the user has previously granted the website access to.
async function getports() {
ports = await navigator.serial.getPorts();
console.log('Get ports ' + ports.length)
console.log(ports[0])
}
async function closeport() {
await port.close();
}
// Filter on devices with te Arduino Uno USB Vendor/Product IDs.
const filters = [
{ usbVendorId: 0x2341, usbProductId: 0x0043 },
{ usbVendorId: 0x2341, usbProductId: 0x0001 }
];
// Prompt user to select an Arduino Uno device.
async function getPort() {
port = await navigator.serial.requestPort([filters]);
if(port)
var info = port.getInfo();
}
// Wait for the serial port to open.
async function openport() {
await port.open({ baudRate: 9600 });
}
//After the serial port connection is established, the readable and writable properties //from the SerialPort object return a ReadableStream and a WritableStream. Those will be //used to receive data from and send data to the serial device. Both use Uint8Array //instances for data transfer.
//When new data arrives from the serial device, port.readable.getReader().read() returns //two properties asynchronously: the value and a done boolean. If done is true, the serial //port has been closed or there is no more data coming in. Calling //port.readable.getReader() creates a reader and locks readable to it. While readable is //locked, the serial port can't be closed.
async function readport() {
reader = port.readable.getReader();
console.log('read '+ reader);
// Listen to data coming from the serial device.
while (true) {
var { value, done } = await reader.read();
if (done)
{
console.log('Value ' + value.length)
// Allow the serial port to be closed later.
reader.releaseLock();
break;
}
// value is a Uint8Array.
}
console.log('Done '+value.length);
}
function freeport(){
if(reader)
reader.releaseLock();
if(writer)
writr.relaseLock();
}
async function writeport() {
var writer = port.writable.getWriter();
var data = new Uint8Array([104, 101, 108, 108, 111,010,013]); // hello
await writer.write(data);
console.log('Written ');
// Allow the serial port to be closed later.
writer.releaseLock();
}
function getreader() {
const textDecoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
reader = textDecoder.readable.getReader();
}
// menu
var iCount=-1;
var current;
var selected = document.getElementById("selectedItem")
var menuItems = document.getElementById('simpleMenu').childNodes
current= menuItems[0];
parent=current.parentNode;
function radioButtons(name,items){
for(i=0;i < items.length;i++) {
if(items[i].name == name)
items[i].style.backgroundColor = "orange";
else
items[i].style.backgroundColor = "navy";
}
}
// set up the radio buttons
function setRadioEvent(element) {
element.addEventListener('click', () => {
element.style.backgroundColor = 'red'
let items=element.parentNode.children;
radioButtons(element.name,items);
//for(i=0;i<items.length;i++)
// if(items[i].name != element.name)
// items[i].style.backgroundColor = "blue"
});
}
function setConnects(str) {
var element = document.getElementById(str)
var items = element.children
for(i=0; i < items.length;i++) setRadioEvent(items[i]);
}
button = document.getElementById("menuNext")
button.addEventListener('click', () => {
do{
iCount++
if(menuItems.length == iCount) iCount = 0;
current = menuItems[iCount];
if(current.nodeName=="UL")
selected.textContent= current.className;
else if(current.nodeName=="LI")
selected.textContent= current.textContent;
} while(current.nodeType !=1)
});
button = document.getElementById("menuDown")
button.addEventListener('click', () => {
if(current.nodeName == "UL") {
parent=current;
menuItems = parent.childNodes;
current = menuItems[0];
iCount=-1
if(current.nodeName=="UL")
selected.textContent= current.className
}
else {
selected.textContent= current.textContent;
current.onclick()
}
});
button = document.getElementById("menuUp")
button.addEventListener('click', () => {
if(parent.nodeName =="UL") {
current = parent
parent = current.parentNode;
menuItems = parent.childNodes;
iCount=-1
selected.textContent= current.className
} else
console.log('To level')
});
setConnects('connectTop')
setConnects('connectBottom')
// resolve
objectCode = new Uint8Array(100);
arg = new Array();
oper = new Array();
function resetRpn() {
arg.length = 0;
oper.length = 0;}
function operLast() {
return oper[oper.length-1]}
function argLast() {
return arg[arg.length-1]}
function OpProcess(index) {
var result = 0;
switch(optable[index].type) {
case MODULE:
var str = optable[index].str;
var node = findTextarea(str);
newContext(node);
execLines();
cc.pop();
break
case MACRO:
var state = nextToken();
while(state != DONE)
executeLine();
break
case END:
return DONE;
break;
}
if(index < builtinsLength) processOpcode(index)
else { // must be a module
var str = optable[index].str;
var node = findText(str);
newContext(node);
execLines();
CS.pop();
}
return OK;
}
function flushLiteral(addr) {
start = cc.start();
end = start + cc.length;
node=cc.node();
while(start < end) {
objectCode[addr] = node.textContent[start];
start++;addr++;
}
}
function flushNum(n,addr) {
var list = String('0123456789abcdef')
var out = String(' ');
bit = 1;
while(bit < n) {
var r = 0;
var t = 1;
for(j=0;j < 4;j++) { // for each bit
if(n & bit)
r += t;
t *=2;
bit *=2;
}
out = list[r] +out;
}
for(j=0;j < out.length;j++){
objectCode[addr] = out[j];
addr++;
}
return addr;
}
// numbers or operators
function flushExpression(arg,addr) {
var len = expr.length;
var x;
var y;
var z;
console.log('flush');
while(arg.length>2) { // there should remain one resolved literal
x = arg.pop();
y = arg.pop();
z = 0;
switch(lastOper.type) {
case MUL:
z = (x*y);
break;
case DIV:
z =(x/y);
break;
case ADD:
z =(x+y);
break;
case SUB:
z =(x-y)
break;
}
arg.push(z);
arg.splice(0,1)
}
flushNum(z);
return OK;
}
// make the source line shunted
// evaluate arg expressions
// separat arguments
var addr=0;
function rpn()
{
strLog('\nrpn ' + cc.slice());
var type = cc.type();
switch(type) {
case LITERAL:
addr=flushLiteral(addr);
break;
case VAL:
arg.push(cc.index());
break;
case NUM:
x = JSON.parse(cc.slice());
arg.push(x);
break;
case RP:
while(oper.length > 0 )
if (type = lastOper.type != LP)
arg.push(oper.pop().type)
else
addr = flushExpression(addr);
if(oper.length == 0)return syntaxError('no left parenth')
oper.pop();
break;
case SEMI: // The last argument
if(oper.length==0) return(syntaxError('Semi-colon mismatch'));
addr = flushExpression(addr); // one last arg
type = oper.pop().type;
if((type != OP) && (type != MACRO))
return(sytaxError('Line not closed'));
// call the OP or MACRO process
processOpcode(cc.index());
break;
break;
case COMMA:
addr = flushExpression(addr);
break
case ALPHA:
case SYSOP:
return syntaxError('Not prime');
break;
default:
while( (oper.length != 0) && (operLast.type > type) )
arg.push(oper.pop().type);
}
return OK;
}
</script>
</html>