mirror of
https://gitlab.com/jusax23/raspberry-pi-pico-machine-code-emulator.git
synced 2024-11-22 22:56:36 +01:00
652 lines
15 KiB
JavaScript
652 lines
15 KiB
JavaScript
import * as fs from "fs";
|
|
var path = process.argv[2];
|
|
var pathout = process.argv[3];
|
|
if(!path||!pathout){
|
|
console.log("PLease this Schema: node ToAs.js [path] [pathout]");
|
|
process.exit();
|
|
}
|
|
var file = fs.readFileSync(path).toString();
|
|
|
|
function error(msg,l=null,c=null){
|
|
var out = "Error"
|
|
if(l!=null&&c!=null)out+=` in line ${l} and character ${c}`;
|
|
out+=": "+msg;
|
|
console.log("\x1b[31m",out,"\x1b[0m");
|
|
process.exit(1);
|
|
}
|
|
|
|
function cutCmd(code,line,chars){
|
|
if(!code.startsWith("("))return ["",error("Compiler Error",line,chars)];
|
|
|
|
if(!code.endsWith(")"))return ["",error("Compiler Error",line,chars)];
|
|
|
|
code = code.substring(1,code.length-1);
|
|
|
|
var countLinesBegin = line;
|
|
var countCharsBegin = chars;
|
|
|
|
var countLines = line;
|
|
var countChars = chars;
|
|
var inC = 0;
|
|
var wasinC = false;
|
|
var inStr = 0;
|
|
var buffer = "";
|
|
var i = 0;
|
|
var out = [];
|
|
|
|
function finishBuff(){
|
|
out.push(
|
|
wasinC?
|
|
new LISPcmd(buffer.trim(),countLinesBegin,countCharsBegin)
|
|
:
|
|
new LISPstring(buffer.trim(),countLinesBegin,countCharsBegin)
|
|
);
|
|
countLinesBegin = countLines;
|
|
countCharsBegin = countChars;
|
|
buffer = "";
|
|
wasinC = false;
|
|
}
|
|
|
|
for (var i = 0; i < code.length; i++) {
|
|
|
|
countChars++;
|
|
//console.log(code,countLines,countChars);
|
|
let c = code[i];
|
|
if(!inC&&!inStr){
|
|
countLinesBegin = countLines;
|
|
countCharsBegin = countChars;
|
|
}
|
|
if(c=="\\"){
|
|
buffer+=code[++i];
|
|
continue;
|
|
}
|
|
if(inStr){
|
|
if(c=="\n"){
|
|
countLines++;
|
|
countChars = 0;
|
|
}else if(c=='"'){
|
|
if(!inC)finishBuff();
|
|
inStr = false;
|
|
}else{
|
|
buffer+=c;
|
|
}
|
|
continue;
|
|
}
|
|
if(c=='"'){
|
|
inStr = true;
|
|
continue;
|
|
}
|
|
if(c==";"&&!inC){
|
|
while(code[++i]!="\n"){}
|
|
countLines++;
|
|
countChars = 0;
|
|
continue;
|
|
}
|
|
if(c=="\n"){
|
|
countLines++;
|
|
countChars = 0;
|
|
}
|
|
if((c == " "||c=="\n")&&!inC){
|
|
if(buffer.trim()!=""){
|
|
finishBuff();
|
|
}
|
|
continue;
|
|
}
|
|
if(c == "("){
|
|
if(!inC&&buffer.trim()!=""){
|
|
finishBuff();
|
|
}
|
|
inC++;
|
|
wasinC = true;
|
|
}
|
|
if(c == ")"){
|
|
inC--;
|
|
if(inC<0)error("Closing braket to much!",countLines,countChars);
|
|
if(!inC){
|
|
buffer+=c;
|
|
finishBuff();
|
|
continue;
|
|
}
|
|
}
|
|
buffer+=c;
|
|
|
|
}
|
|
if(inStr)error("Missing closing quotation mark!",countLines,countChars);
|
|
if(inC)error("Missing closing braket!",countLines,countChars);
|
|
|
|
if(buffer.trim()!="")finishBuff();
|
|
|
|
return out;
|
|
}
|
|
|
|
class LISPstring extends String {
|
|
#l;
|
|
#c;
|
|
constructor(string,line,char) {
|
|
super(string);
|
|
this.#l = line;
|
|
this.#c = char;
|
|
}
|
|
get lineCount(){
|
|
return this.#l;
|
|
}
|
|
get charCount(){
|
|
return this.#l;
|
|
}
|
|
get pos(){
|
|
return [this.#l,this.#c];
|
|
}
|
|
}
|
|
|
|
class LISPcmd extends Array {
|
|
#l;
|
|
#c;
|
|
|
|
constructor(code,line,char) {
|
|
var childs = cutCmd(code,line,char);
|
|
|
|
super(...childs);
|
|
this.#l = line;
|
|
this.#c = char;
|
|
}
|
|
get lineCount(){
|
|
return this.#l;
|
|
}
|
|
get charCount(){
|
|
return this.#l;
|
|
}
|
|
get pos(){
|
|
return [this.#l,this.#c];
|
|
}
|
|
}
|
|
var data = new LISPcmd("(\n"+file+"\n)",0,0);
|
|
|
|
//console.log(JSON.stringify(data));
|
|
|
|
|
|
|
|
function getType(data){
|
|
/*if(data=="empty"){
|
|
return ["emp", ""];
|
|
}*/
|
|
if(data=="true"){
|
|
return ["bool", 1];
|
|
}
|
|
if(data=="false"){
|
|
return ["bool", 0];
|
|
}
|
|
if(data=="NaN"){
|
|
return ["num", NaN];
|
|
}
|
|
/*if(typeof data == "string"&&data.startsWith('"') && data.endsWith('"')){
|
|
return ["str", data.slice(1,-1)];
|
|
}*/
|
|
if((data instanceof LISPstring)&&!isNaN(data)/*!data.match(new RegExp("[^0-9.-e+]","g"))*/){
|
|
return ["num", Number(data)];
|
|
}
|
|
if(data instanceof LISPcmd/*Array.isArray(data)*/){
|
|
return ["code", data];
|
|
}
|
|
return ["var",data];
|
|
}
|
|
|
|
var nid = 0;
|
|
|
|
var dataTypes = {
|
|
uint32: {ptype:0, length:4, mask:null },
|
|
uint16: {ptype:0, length:2, mask:0xffff },
|
|
uint8: {ptype:0, length:1, mask:0xff },
|
|
int32: {ptype:1, length:4, mask:null },
|
|
int16: {ptype:1, length:2, mask:0xffff },
|
|
int8: {ptype:1, length:1, mask:0xff },
|
|
float: {ptype:2, length:4, mask:null },
|
|
bool: {ptype:0, length:1, mask:1 },
|
|
};
|
|
var dataTypesReversed = {
|
|
0:"uint/bool",
|
|
1:"int",
|
|
2:"float"
|
|
}
|
|
|
|
/*var numTypes = ["uint32", "uint16","uint8","int32","int16","int8","float","bool"];*/
|
|
|
|
|
|
function last(d){
|
|
return d[d.length-1];
|
|
}
|
|
function find(d,n){
|
|
for (var i = d.length-1; i >= 0 ; i--) {
|
|
if(typeof d[i][n] == "object")return d[i][n];
|
|
}
|
|
return null;
|
|
}
|
|
function createVars(c){
|
|
var l = 0;
|
|
return [Object.entries(c).map(d=>{
|
|
var out = `${d[1].id} = ${l};${dataTypes[d[1].type].length}`;
|
|
if(d[1].used==0) return ";"+out;
|
|
l+=dataTypes[d[1].type].length;
|
|
return out;
|
|
}).join("\n"),l];
|
|
}
|
|
|
|
var extrafuns = {
|
|
|
|
};
|
|
|
|
function execute(data,expect,context,local){
|
|
var code = "";
|
|
var ptype = 0;
|
|
var doconv = true;
|
|
let [type,d] = getType(data);
|
|
|
|
if(type == "code"){
|
|
if(data[0]=="defvar"){
|
|
var ctx = last(context);
|
|
var [vname,vtype] = data[1].split(":");
|
|
if(ctx[vname])error(`Can not redefine: ${cname}! It is already defined!`,...data.pos);
|
|
if(!dataTypes[vtype])error(`Unknown Datatype: ${vtype}`,...data.pos);
|
|
|
|
let mvar = {type:vtype,used:0,id:"v"+nid++,local:local};
|
|
ctx["v"+vname] = mvar;
|
|
let [c,etype] = execute(data[2],mvar.type,context,local);
|
|
code+= `
|
|
;defvar: executing value:
|
|
${c}
|
|
;defvar: Store Value
|
|
STA ${dataTypes[mvar.type].length|(mvar.local?0x10:0)} ${mvar.id}
|
|
`;
|
|
ptype = dataTypes[mvar.type].ptype;
|
|
}else if(data[0]=="defun"){
|
|
var [fname,ftype] = (data[1]??"").split(":");
|
|
if(local)error(`Nested functions are currently not supported: ${fname}`,...data.pos);
|
|
if(extrafuns[fname])error(`You can not declare functions double: ${fname}`,...data.pos);
|
|
if(!dataTypes[ftype])error(`Unknown Datatype: ${ftype}`,...data.pos);
|
|
var ctx = last(context);
|
|
var funsCtx = {};
|
|
var args = [];
|
|
var lcode = "";
|
|
data[2].forEach(v=>{
|
|
var [vname,vtype] = v.split(":");
|
|
if(funsCtx[vname])error(`You declared the Argument ${vname} in function",fname,"twice!`,...data.pos);
|
|
if(!dataTypes[vtype])error(`Unknown Datatype: ${vtype}`,...data.pos);
|
|
var mvarg = {type:vtype,used:1,id:"arg"+nid++,local:false};
|
|
var mvarl = {type:vtype,used:1,id:"arg"+nid++,local:true};
|
|
|
|
funsCtx["v"+vname] = mvarl;
|
|
args.push(mvarg);
|
|
ctx["arg_"+vname] = mvarg;
|
|
lcode+=`
|
|
;function copy args
|
|
LDA ${dataTypes[mvarg.type].length} ${mvarg.id}
|
|
STA ${dataTypes[mvarl.type].length|0x10} ${mvarl.id}
|
|
`
|
|
});
|
|
var mvar = {type:ftype,used:1,id:"return"+nid++,local:false};
|
|
var fun = {
|
|
type:ftype,
|
|
code:"",
|
|
args,
|
|
return:mvar
|
|
};
|
|
extrafuns[fname] = fun;
|
|
//execute lcode
|
|
lcode+="\n;function code:\n";
|
|
for (var i = 3; i < data.length; i++) {
|
|
let [c,t] = execute(data[i],data.length-1==i?ftype:"any",[...context,funsCtx],true);
|
|
lcode+=c;
|
|
}
|
|
|
|
ctx["return_"+fname] = mvar;
|
|
lcode+= `
|
|
;return from subrutine
|
|
STA ${dataTypes[mvar.type].length} ${mvar.id}
|
|
RSR
|
|
`;
|
|
let [localvars,localL] = createVars(funsCtx);
|
|
lcode = `
|
|
;function reserve Stackspace
|
|
${(new Array(Math.floor(localL/255))).fill("PSH 255").join("\n ")}
|
|
${(localL%255>0?`PSH ${localL%255}`:'')}
|
|
;function place variable pointers
|
|
${localvars}
|
|
|
|
${lcode}
|
|
`;
|
|
|
|
fun.code = lcode;
|
|
|
|
code=`
|
|
LIA .${fname}
|
|
`;
|
|
ptype=0;
|
|
|
|
}else if(data[0]=="let"){
|
|
var mvar = find(context,"v"+data[1]);
|
|
if(mvar == null)error(`Unknown Variable: ${data[1]}`,...data.pos);
|
|
mvar.used++;
|
|
let [c,etype] = execute(data[2],mvar.type,context,local);
|
|
code+= `
|
|
;let: executing value:
|
|
${c}
|
|
;let: Store Value
|
|
STA ${dataTypes[mvar.type].length|(mvar.local?0x10:0)} ${mvar.id}
|
|
`;
|
|
ptype = dataTypes[mvar.type].ptype;
|
|
}else if(data[0]=="+"){
|
|
var etypes = [];
|
|
var etypeMax = 0;
|
|
for (var i = 1; i < data.length; i++) {
|
|
let [c,etype] = execute(data[i],"num",context,local);
|
|
code+=`
|
|
;+: next value
|
|
${c}
|
|
`;
|
|
if(i<data.length-1){
|
|
code+=`
|
|
PSH 4
|
|
`;
|
|
}
|
|
etypes.push(etype);
|
|
if(etype>etypeMax)etypeMax=etype;
|
|
}
|
|
if(etypes[0]!=etypeMax){
|
|
code+=`
|
|
;+: converting not matching types
|
|
CVA ${etypes[0]} ${etypeMax}
|
|
`;
|
|
}
|
|
for (var i = 1; i < etypes.length; i++) {
|
|
code+=`
|
|
;+: Pull/Add next
|
|
CAB
|
|
PUL 4
|
|
ADD ${etypeMax} ${etypes[i]} ${etypeMax}
|
|
`;
|
|
}
|
|
ptype = etypeMax;
|
|
}else if(data[0]=="-"){
|
|
var etypes = [];
|
|
var etypeMax = 0;
|
|
for (var i = data.length-1; i >=1 ; i--) {
|
|
let [c,etype] = execute(data[i],"num",context,local);
|
|
code+=`
|
|
;-: next value
|
|
${c}
|
|
`;
|
|
if(i>1){
|
|
code+=`
|
|
PSH 4
|
|
`;
|
|
}
|
|
etypes.push(etype);
|
|
if(etype>etypeMax)etypeMax=etype;
|
|
}
|
|
if(etypes[0]!=etypeMax){
|
|
code+=`
|
|
;-: converting not matching types
|
|
CVA ${etypes[0]} ${etypeMax}
|
|
`;
|
|
}
|
|
for (var i = 1; i < etypes.length; i++) {
|
|
code+=`
|
|
;-: Pull/Add next
|
|
CAC
|
|
PUL 4
|
|
CAB
|
|
CCA
|
|
SUB ${etypeMax} ${etypes[i]} ${etypeMax}
|
|
`;
|
|
}
|
|
ptype = etypeMax;
|
|
}else if(data[0]=="*"){
|
|
var etypes = [];
|
|
var etypeMax = 0;
|
|
for (var i = 1; i < data.length; i++) {
|
|
let [c,etype] = execute(data[i],"num",context,local);
|
|
code+=`
|
|
;*: next value
|
|
${c}
|
|
`;
|
|
if(i<data.length-1){
|
|
code+=`
|
|
PSH 4
|
|
`;
|
|
}
|
|
etypes.push(etype);
|
|
if(etype>etypeMax)etypeMax=etype;
|
|
}
|
|
if(etypes[0]!=etypeMax){
|
|
code+=`
|
|
;*: converting not matching types
|
|
CVA ${etypes[0]} ${etypeMax}
|
|
`;
|
|
}
|
|
for (var i = 1; i < etypes.length; i++) {
|
|
code+=`
|
|
;*: Pull/Add next
|
|
CAB
|
|
PUL 4
|
|
MUL ${etypeMax} ${etypes[i]} ${etypeMax}
|
|
`;
|
|
}
|
|
ptype = etypeMax;
|
|
}else if(data[0]=="/"){
|
|
var etypes = [];
|
|
var etypeMax = 0;
|
|
for (var i = data.length-1; i >=1 ; i--) {
|
|
let [c,etype] = execute(data[i],"num",context,local);
|
|
code+=`
|
|
;/: next value
|
|
${c}
|
|
`;
|
|
if(i>1){
|
|
code+=`
|
|
PSH 4
|
|
`;
|
|
}
|
|
etypes.push(etype);
|
|
if(etype>etypeMax)etypeMax=etype;
|
|
}
|
|
if(etypes[0]!=etypeMax){
|
|
code+=`
|
|
;/: converting not matching types
|
|
CVA ${etypes[0]} ${etypeMax}
|
|
`;
|
|
}
|
|
for (var i = 1; i < etypes.length; i++) {
|
|
code+=`
|
|
;/: Pull/Add next
|
|
CAC
|
|
PUL 4
|
|
CAB
|
|
CCA
|
|
DIV ${etypeMax} ${etypes[i]} ${etypeMax}
|
|
`;
|
|
}
|
|
ptype = etypeMax;
|
|
}else if(data[0]=="print"){
|
|
let [c,etype] = execute(data[1],"any",context,local);
|
|
code+=`
|
|
;print: executing value:
|
|
${c}
|
|
;print Value
|
|
OUT ${etype}
|
|
`;
|
|
ptype = etype;
|
|
}else if(data[0]=="if"){
|
|
let [c,etype] = execute(data[1],"bool",context,local);
|
|
let [c1,etype1] = execute(data[2],expect,context,local);
|
|
let c2,etype2;
|
|
if(typeof data[3]!="undefined"){
|
|
[c2,etype2] = execute(data[3],expect,context,local);
|
|
}else{
|
|
[c2,etype2] = ["LIA 0",0];
|
|
}
|
|
|
|
var id = nid++;
|
|
code+=`
|
|
;if: executing value:
|
|
${c}
|
|
;if:
|
|
LIB 1
|
|
CMP 0 0
|
|
JNE .else${id}
|
|
;then code
|
|
${c1}
|
|
JMP .afterif${id}
|
|
else${id}:
|
|
;else code
|
|
${c2}
|
|
afterif${id}:
|
|
`;
|
|
ptype = etype1;
|
|
}else if(data[0]==">"){
|
|
let [c1,etype1] = execute(data[1],"num",context,local);
|
|
let [c2,etype2] = execute(data[2],"num",context,local);
|
|
doconv = false;
|
|
var id = nid++;
|
|
code+=`
|
|
;>: execute first
|
|
${c1}
|
|
PSH 4
|
|
;>: execute secound
|
|
${c2}
|
|
CAB
|
|
PUL 4
|
|
CMP ${etype1} ${etype2}
|
|
LIA 1
|
|
JB .endcompare${id}
|
|
LIA 0
|
|
endcompare${id}:
|
|
`;
|
|
}else if(data[0]=="<"){
|
|
let [c1,etype1] = execute(data[1],"num",context,local);
|
|
let [c2,etype2] = execute(data[2],"num",context,local);
|
|
doconv = false;
|
|
var id = nid++;
|
|
code+=`
|
|
;<: execute first
|
|
${c1}
|
|
PSH 4
|
|
;<: execute secound
|
|
${c2}
|
|
CAB
|
|
PUL 4
|
|
CMP ${etype1} ${etype2}
|
|
LIA 1
|
|
JS .endcompare${id}
|
|
LIA 0
|
|
endcompare${id}:
|
|
`;
|
|
}else if(data[0]=="="){
|
|
let [c1,etype1] = execute(data[1],"num",context,local);
|
|
let [c2,etype2] = execute(data[2],"num",context,local);
|
|
doconv = false;
|
|
var id = nid++;
|
|
code+=`
|
|
;>: execute first
|
|
${c1}
|
|
PSH 4
|
|
;>: execute secound
|
|
${c2}
|
|
CAB
|
|
PUL 4
|
|
CMP ${etype1} ${etype2}
|
|
LIA 1
|
|
JE .endcompare${id}
|
|
LIA 0
|
|
endcompare${id}:
|
|
`;
|
|
}else{
|
|
if(extrafuns[data[0]]){
|
|
var fun = extrafuns[data[0]];
|
|
for (var i = 0; i < fun.args.length; i++) {
|
|
if(typeof data[i+1] == "undefined")error(`Argument missing for function ${data[0]}`,...data.pos);
|
|
var [ecode,etype] = execute(data[i+1],fun.args[i].type,context,local);
|
|
code+=`
|
|
;${i+1} Argument
|
|
${ecode}
|
|
STA ${dataTypes[fun.args[i].type].length} ${fun.args[i].id}
|
|
`;
|
|
}
|
|
code+=`
|
|
;excute function
|
|
JSR .${data[0]}
|
|
;loading return value
|
|
LDA ${dataTypes[fun.return.type].length} ${fun.return.id}
|
|
`;
|
|
ptype = dataTypes[fun.return.type].ptype;
|
|
}else{
|
|
error(`Unknown command: ${data[0]}`,...data.pos);
|
|
}
|
|
}
|
|
}else if(type == "var"){
|
|
let mvar = find(context,"v"+d);
|
|
if(mvar == null)error(`Unknown Variable/Expression: ${d}`,...data.pos);
|
|
mvar.used++;
|
|
code += `
|
|
;var: load Variable
|
|
LDA ${dataTypes[mvar.type].length|(mvar.local?0x10:0)} ${mvar.id}
|
|
`;
|
|
ptype = dataTypes[mvar.type].ptype;
|
|
}else if(type == "num"||type=="bool"){
|
|
ptype = Number.isInteger(d)?(d>=0?0:1):2;
|
|
if(dataTypes[expect]?.ptype == ptype){
|
|
code += `
|
|
;num: Loading num
|
|
LIA ${dataTypes[expect].mask==null?d:dataTypes[expect].ptype&d}
|
|
`;
|
|
doconv = false;
|
|
}else{
|
|
code += `
|
|
;num: Loading num
|
|
LIA ${d}
|
|
`;
|
|
}
|
|
|
|
}else{
|
|
error(`Not Supported execution type: ${type} of ${d}`,...data.pos);
|
|
}
|
|
|
|
if(expect=="any")return [code,ptype];
|
|
if(expect=="num"){
|
|
if(ptype==0||ptype==1||ptype==2){
|
|
return [code,ptype];
|
|
}else{
|
|
error(`Can not convert ${dataTypesReversed[ptype]} to Number`,...data.pos);
|
|
}
|
|
}
|
|
if(ptype!=dataTypes[expect].ptype){
|
|
code+=`
|
|
CVA ${ptype} ${dataTypes[expect].ptype}
|
|
`;
|
|
}
|
|
if(doconv&&dataTypes[expect].mask != null){
|
|
code+=`
|
|
LIB ${dataTypes[expect].mask}
|
|
BWA
|
|
`;
|
|
}
|
|
return [code,dataTypes[expect].ptype];
|
|
|
|
}
|
|
var code = "";
|
|
var context = [{}];
|
|
for (var i = 0; i < data.length; i++) {
|
|
let [c,t] = execute(data[i],"any",context);
|
|
code += c;
|
|
//console.log(c);
|
|
}
|
|
var [globvars,globL] = createVars(context[0]);
|
|
var finish = `
|
|
${globvars}
|
|
SHS ${globL}
|
|
;code
|
|
${code}
|
|
HLT
|
|
;functions
|
|
${Object.entries(extrafuns).map(d=>d[0]+":\n"+d[1].code).join("\n\n")}
|
|
`;
|
|
fs.writeFileSync(pathout,finish);
|
|
console.log(`Finished compiling in ${Math.round(performance.now())/1000}sec. Assembly saved to: ${pathout}`);
|