Friday, May 9, 2025
The Rule of 2
In my games I've been using the rule of 2.
The rule of 2 is easy. If you are better at 2 things over your oppoent or 2 steps better you get +2 to your rolls. If you are worese at 2 things compared to your opponent you are -2 to yuor rolls.
I keep track of quality of manufacture and materials used in equipment. So if your weapon is 2 steps better than an opponent's armor you are +2 to hit.
Two or more friends helping you out in a task? +2 to yuor roll!
Ability contest between two characters? I use a 1d20 roll vs opponents ability score. As we alreayd track differences in ability scores in the score itself and ability modifiers to rolls that diffwerence isn't factore into the rule of 2 but levels are. So if you are 2 levels or higher than an challenger yuo are +2 to your rolls and they are -2 to their roll.
Oh is this saving throw 10th level but you are only 5th level. That's -2 to your saving throw roll! (oh no)
Oh and only ever apply the rule of 2 in two factors at most keeping all modifiers the rule of 2 could apply to form -4 to +4.
I like fiddly details but I also recognize not all players can track a lot of details and apply the math quickly. So I keep the math out of the descriptions a lot of the time.
why 2 instead of one? Becasue a shift of only 1 isn't really noticable on a turn per turn (or round by round) basis beyond what the rules are already keepign track of in most situations using a d20 roll.
the rule
Rule of 2 baby!
Monday, May 5, 2025
It was safe trust me.
That last post was an experiment. A fun one and despite it beign a little frightenign completely safe. It's a javasctipt scrput that embeds a basic transpiler. I've been a nohhyst and work-tools promammer for years and always expermineting to bringign more of that to my gaming. I have a simple dungeon geenrator I've been fiddling with on and off for sometime now that I'm explorign ways to share.
Monday, April 21, 2025
A Little expriment. If this works well enough I might be able to get some fun little applets on the blog.
New.BAS 15 (prod 2025-04-21 19:03 utc)
';
} else if (tok == '&' && code.substr(0, 1).toLowerCase() == 'h') {
code = code.substr(1);
var n = code.match(/^([0-9a-fA-F]+)/);
if (n === null) {
Throw('Bad hex number');
}
tok = '0x' + n[1];
code = code.substr(n[1].length);
} else if (tok == '&' && code.substr(0, 1).toLowerCase() == 'o') {
code = code.substr(1);
var n = code.match(/^([0-7]+)/);
if (n === null) {
Throw('Bad octal number');
}
tok = (+('0o' + n[1])).toString();
code = code.substr(n[1].length);
} else if (tok == '&' && code.substr(0, 1).toLowerCase() == 'b') {
code = code.substr(1);
var n = code.match(/^([0-1]+)/);
if (n === null) {
Throw('Bad binary number');
}
tok = '0b' + n[1];
code = code.substr(n[1].length);
}
return;
}
}
tok += code.substr(0, 1).toLowerCase();
code = code.substr(1);
if (code == '') {
return;
}
}
}
Next();
function ConsumeData() {
var quote = false;
var had_quote = false;
var item = '';
for (;;) {
var ch = code.substr(0, 1);
if (ch == '\n' || ch == '') {
if (!had_quote) {
if (!quote) {
item = item.trim();
}
data.push(item);
}
break;
}
else if (quote==false && item.trim()=='' && ch=='&') {item += '0';}
else if (quote==false && item.trim()=='0' && /^([hH])$/.test(ch) ) {item += 'x';}
else if (ch == '"') {
if (quote) {
data.push(item);
item = '';
quote = false;
had_quote = true;
} else {
quote = true;
if (item.search(/[^ \t]/) != -1) {
Throw('Data statement extra text: "' + item + '"');
}
item = '';
}
} else if (ch == ',') {
if (!quote) {
if (!had_quote) {
data.push(item.trim());
} else {
had_quote = false;
if (item.search(/[^ \t]/) != -1) {
Throw('Data statement extra text: "' + item + '"');
}
}
item = '';
} else {
item += ',';
}
} else {
item += ch;
}
code = code.substr(1);
}
Next();
}
function Throw(msg,dotl=true) {
if (dotl==true) {var tl=line-5;} else {var tl = 'unknown'};
if (tok==""||tok==""||msg.includes("OUT OF DATA")) {tl-=1;}
alert('🛑 ERROR: line '+tl+': '+msg);
throw 'line '+tl+': '+msg;
}
function Skip(t,eek) {
if (tok!=t && NoEekOnSkip==0) {
Throw(eek+': Expected "'+t+'" found "'+tok+'"');
}
Next();
}
function EndOfStatement() {
return tok==':'||tok=='';
}
function SkipEndOfStatement() {
if (!EndOfStatement()) {
Throw('Expected ":" (between statements) or "," (between parameters) or EOL');
}
Next();
}
function IsKeyword(a) {
var d = ''; if (inside_type == true) {d='(TYPE definition error)'}
if (':::keywords:::'.includes('.'+a.toLowerCase()+'.')){Throw(a+' is a reserved keyword '+d);}
}
function NewOp() {
ops.push(curop);
curop = '';
}
function If(e, n) {
if (n === undefined) {
n = [];
}
NewOp();
ops[ops.length - 1] += 'if (!(' + e + ')) { ip = ';
flow.push(['if', ops.length - 1, []]);
}
function Else() {
var f = flow.pop();
if (f[0] != 'if') {
Throw('ELSE unmatched to IF');
}
NewOp();
var pos = ops.length - 1;
ops[pos] += 'ip = ';
NewOp();
ops[f[1]] += ops.length + '; }\n';
flow.push(['else', null, f[2].concat(pos)]);
}
function ElseIf(e) {
var f = flow.pop();
if (f[0] != 'if') {
Throw('ELSEIF unmatched to IF');
}
NewOp();
var pos = ops.length - 1;
ops[pos] += 'ip = ';
NewOp();
ops[f[1]] += ops.length + '; }\n';
NewOp();
ops[ops.length - 1] += 'if (!(' + e + ')) { ip = ';
flow.push(['if', ops.length - 1, f[2].concat([pos])]);
}
function EndIf() {
NewOp();
var f = flow.pop();
if (f[0] == 'else') {
// nothing needed
} else if (f[0] == 'if') {
ops[f[1]] += ops.length + '; }\n';
} else {
Throw('Unmatch end if');
}
for (var i = 0; i < f[2].length; ++i) {
ops[f[2][i]] += ops.length + ';\n';
}
}
function VarPtr(vname) {
var vinfo;
if (vars[vname] !== undefined) {vinfo = vars[vname];}
else if (global_vars[vname] !== undefined) {vinfo = global_vars[vname];}
if (vinfo === undefined) {Throw('Undefined variable name');}
if (vinfo.global) {return vinfo.offset;}
else {return '(bp + ' + vinfo.offset + ')';}
}
function AddLabel(name) {
var n2=name.replace(/^0+/, "");
if (labels[n2]!==undefined) {Throw('Label '+n2+' defined twice');}
NewOp();
curop+='// LABEL '+n2+':\n';
labels[n2]=ops.length;
data_labels[n2]=data.length;
}
function Factor3() {
var ts=''; var te='';
if (tok=='('||tok=='['||tok=='{') {
if (tok=='[') {ts='[';te=']';}
else if (tok=='{') {ts='{';te='}';}
else {ts='(';te=')';}
Skip(ts);
var ret = Expression();
Skip(te);
return ret;
} else {
var name = tok;
Next();
if (name.substr(0, 1) == '"' ||
/^[0-9]*([.][0-9]*)?([eE][+-]?[0-9]+)?$/.test(name) ||
/^0x[0-9a-fA-F]+$/.test(name) ||
/^0b[0-9a-fA-F]+$/.test(name)) {
return name;
}
if (name == 'rnd') {
if (tok == '(') {
Skip('(');
if (tok != ')') {
var e = Expression();
}
Skip(')');
}
return 'Math.random()';
}
if (name == '_pi') {
var e = 1
if (tok == '(') {
Skip('(');
if (tok != ')') {
e = Expression();
}
Skip(')');
}
return 'Math.PI * ' + e;
}
if (name == 'varptr') {
Skip('(');
var vname = tok;
Next();
Skip(')');
return VarPtr(vname);
}
if (name == 'stackdepth') {
Skip('(');
Skip(')');
return 'sp';
}
if (name == 'basedepth') {
Skip('(');
Skip(')');
return 'bp';
}
if (name == 'true') {return '-1';}
if (name == 'false') {return '0';}
if (name=='_fontheight') {return 'font_height';}
if (name=='screen_aspect') {return 'screen_aspect';}
if (name=='_width') {return 'curr_width';}
if (name=='_height') {return 'curr_height';}
if (name=='xmax') {return 'curr_width - 1';}
if (name=='ymax') {return 'curr_height - 1';}
if (name=='_windowwidth') {return 'GetWinWidth()';}
if (name=='_windowheight') {return 'GetWinHeight()';}
if (name=='touchdevice') {return -+(('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));}
if (name=='csrlin') {return 'text_y + 1';}
if (name=='_mousex') {return 'mouse_x';}
if (name=='_mousey') {return 'mouse_y';}
if (name=='_mousebutton') {
if (tok=='(') {Skip('('); var e=Expression(); Skip(')');}
return 'mouse_buttons';}
if (name=='_mousewheel') {return 'MouseWheel()';}
if (name=='_audiodone') {return 'AudioDone()';}
if (name=='urlquerystring$') {return 'decodeURIComponent(window.location.search.substring(1))';}
if (name=='now$') {return 'Date().toString()';}
if (name=='time$') {return 'Date().toString().split(" ")[4]';}
if (name=='day$') {var t=new Date(); var r=['SUN','MON','TUE','WED','THU','FRI','SAT'][t.getDay()]; return '"' + r + '"';}
if (name=='date$') {
var today = new Date();
var dd = "0".concat(today.getDate().toString());
dd = dd.substr(dd.length-2, 2);
var mm = today.getMonth()+1;
mm = "0".concat(mm.toString());
mm = mm.substr(mm.length-2, 2);
var yyyy = today.getFullYear();
return '"".concat("' + mm + '","-","' + dd + '","-","' + yyyy + '")';
}
if (name=='log'||name=='ucase$'||name=='lcase$'||
name=='chr$'||name=='getchr$'||name=='sqr'||name=='hex$'||
name=='oct$'||name=='_bin$'||name=='bin$'||
name=='int'||name=='cint'||name=='cdbl'||name=='csng'||name=='fix'||name=='frac'||
name=='asc'||name=='fre'||name=='sgn'||
name=='abs'||name=='len'||name=='val'||
name=='cos'||name=='sin'||name=='tan'||name=='atn'||
name=='exp'||name=='_d2r'||name=='_r2d'||
name=='str$'||name=='peek'||name=='_red'||name=='_green'||name=='_blue'||
name=='ltrim$'||name=='rtrim$'||name=='spc'||
name=='space$'||name=='tab'||name=='pos'||name=='mapget'||
name=='confirm'||name=='getlocalstorageitem'||name=='getsessionstorageitem') {
Skip('(');
var e=Expression();
Skip(')');
if (name=='confirm') {curop+='Sleep(0.005);\n'; NewOp();}
switch (name) {
case 'log': return 'Math.log('+e+')';
case 'ucase$': return '('+e+').toUpperCase()'; case 'lcase$': return '('+e+').toLowerCase()';
case 'chr$': return 'String.fromCharCode('+e+')';
case 'getchr$': return 'GetChr('+e+')';
case 'asc': return '('+e+').charCodeAt(0)';
case 'fre': return '("60300")';
case 'sgn': return 'Math.sign('+e+')';
case 'sqr': return 'Math.sqrt('+e+')';
case 'hex$': return '(Math.round('+e+') >>> 0).toString(16)';
case 'oct$': return '(Math.round('+e+') >>> 0).toString(8)';
case 'bin$':
case '_bin$': return '(Math.round('+e+') >>> 0).toString(2)';
case 'int': return 'Math.floor('+e+')'; case 'cint': return 'Math.sign('+e+') * Math.round(Math.abs('+e+'))';
case 'cdbl': return 'Math.fround('+e+')'; case 'csng': return 'Math.fround('+e+')';
case 'fix': return 'Math.trunc('+e+')'; case 'abs': return 'Math.abs('+e+')';
case 'frac': return e + '- Math.trunc('+e+')';
case 'cos': return 'Math.cos('+e+')'; case 'sin': return 'Math.sin('+e+')';
case 'tan': return 'Math.tan('+e+')'; case 'atn': return 'Math.atan('+e+')';
case 'exp': return 'Math.exp('+e+')';
case '_d2r': return 'Number('+e+')*(Math.PI/180)'; case '_r2d': return 'Number('+e+')*(180/Math.PI)';
case 'str$': return 'ToString('+e+')';
case 'val': return '+('+e+')||0';
case 'peek': return 'Peek('+e+').toString()';
case '_red': return '('+e+'>>16)';
case '_green': return '('+e+'>>8)&0xFF';
case '_blue': return '('+e+'&0xFF)';
case 'len': return '(('+e+').length)';
case 'ltrim$': return '(('+e+').trimStart())';
case 'rtrim$': return '(('+e+').trimEnd())';
case 'spc': return 'StringRep(('+e+'), " ")';
case 'space$': return 'StringRep(('+e+'), " ")';
case 'tab': return 'StringRep((text_x < '+e+' ? '+e+'-text_x-1 : Math.floor(curr_width/8)-text_x-('+e+'<0?0:1)+'+e+'), " ")';
// case 'tab': return 'StringRep(('+e+'), "\t")';
case 'pos': return '(text_x+1)';
case 'confirm': return '-+confirm(('+e+'))';
case 'mapget': return 'MapGet('+e+')';
case 'getlocalstorageitem': return 'GetLocalStorageItem('+e+')';
case 'getsessionstorageitem': return 'GetSessionStorageItem('+e+')';
case 'stackdepth': return 'sp';
}
Throw('This cannot happen');
}
if (name=='instr') {
var i = '0';
Skip('(');
var a = Expression();
Skip(',');
var b = Expression();
if (tok==',') {
i=a; a=b;
Skip(',');
b = Expression();}
Skip(')');
return '(('+a+').indexOf('+b+','+i+'-1)+1)';
}
if (name=='atan2'||name=='_atan2'||name=='string$'||
name=='left$'||name=='right$'||name=='nvl$') {
Skip('(');
var a = Expression();
Skip(',');
var b = Expression();
Skip(')');
if (name=='atan2' || name=='_atan2') {return 'Math.atan2('+a+','+b+')';
} else if (name=='string$') {return 'StringRep('+a+','+b+')';
} else if (name=='left$') {return '(('+a+').substr(0,('+b+')))';
} else if (name=='right$') {return 'Right(('+a+'),('+b+'))';
} else if (name=='nvl$') {return a+'||'+b;
} else {throw 'impossible';
}
}
if (name=='prompt' || name=='_inputbox$') {
curop+='Sleep(0.005);\n'; NewOp();
Skip('(');
var a=Expression();
if (name=='_prompt' && tok==')') {
Skip(')');
var b='""';
}
else {
Skip(',');
if (name=='_inputbox$') {
a=Expression();
if (tok==')') {Skip(')'); var b='""'; }
else {Skip(','); var b=Expression(); Skip(')');}
}
else {
var b=Expression();
Skip(')');}
}
return 'prompt(('+a+'),('+b+'))';
}
if (name=='labelexists') {
Skip('('); var a=Expression(); Skip(')');
return 'LabelExists('+a+')';
}
if (name=='point') {
Skip('(');
var a=Expression();
if (tok==',') {Skip(',');var b=Expression();Skip(')');return 'Point(('+a+'), ('+b+'))';}
else {Skip(')');return 'PointPos('+a+')';}
}
if (name=='rgb2bgr') {
Skip('(');var a=Expression();Skip(')');return 'RGB2BGR('+a+')';}
if (name=='_rgb'||name=='_rgb32'||name=='bgr') {
Skip('('); var a=Expression();
Skip(','); var b=Expression();
Skip(','); var c=Expression(); Skip(')');
if (name=='bgr') {[a,c]=[c,a];return 'RGB2BGR(RGB2(' + a + ',' + b + ',' + c + '))';}
else {return 'RGB2(' + a + ',' + b + ',' + c + ')';}
}
if (name=='keystate') {
Skip('('); var a;
if (tok!=')') {a = Expression();} Skip(')');
return 'Keystate('+a+')';
}
if (name=='_mousezone') {
Skip('(');
var a = Expression();
Skip(',');
var b = Expression();
var c = '1'; var d = '1';
if (tok==',') {
Skip(','); c = Expression();
Skip(','); d = Expression();}
Skip(')');
return 'MouseZone ('+a+','+b+','+c+','+d+')';
}
if (name=='iff') {
Skip('(');
var a = Expression();
Skip(',');
var b = Expression();
Skip(',');
var c = Expression();
Skip(')');
return '('+ a + ' ? ' + b + ' : ' + c + ')';
}
if (name=='between') {
Skip('(');
var a = Expression();
Skip(',');
var b = Expression();
Skip(',');
var c = Expression();
var d = '0';
if (tok==',') {Skip(','); d = Expression();}
Skip(')');
return 'BETWEEN ('+a+','+b+','+c+','+d+')';
}
if (name=='min'||name=='max') {
// return 'Math.' + name + '(('+a+'),('+b+'))
var z = 'Math.' + name + '((';
Skip('(');
z = z + Expression();
while (tok==',') {
Skip(',');
z = z + '),(' + Expression();
}
Skip(')');
z = z + '))';
return '(' + z + ')';
}
if (name=='choose') {
Skip('(');
var a = Expression();
var z = '[';
while (tok==',') {
Skip(',');
z = z + Expression() + ',';
}
Skip(')');
z = z.slice(0,-1) + ']'
return '('+z+'['+a+'-1])';
}
if (name=='mid$' || name=='replace$') {
Skip('(');
var a = Expression();
Skip(',');
var b = Expression();
var c = "";
if (name == 'mid$') {
c = a + '.length + 1 -' + b;
};
if (tok == ',') {
Skip(',');
c = Expression();
}
Skip(')');
if (name == 'mid$') {
return '((' + a + ').substr((' + b + ') - 1, (' + c + ')))';
} else if (name == 'replace$') {
return 'StrReplace(' + a + ', ' + b + ', ' + c + ')';
}
}
if (name=='inkey$') {
return 'Inkey()';
}
if (name=='timer') {
return 'GetTimer()';
}
if (functions[name] !== undefined && !functions[name].is_subroutine) {
return FunctionCall(name, {is_subroutine: false});
}
return IndexVariable(name);
}
}
function Factor2() {
var a = Factor3();
while (tok == '^') {
Next();
var n = '';
if (tok=='-') {n=tok;Next();}
var b = Factor3();
a = 'Math.pow(' + a + ', ' + n + b + ')';
}
return a;
}
function Factor1() {
var ret = '';
while (tok == '+' || tok == '-') {
ret += tok;
Next();
}
return ret + '(' + Factor2() + ')';
}
function Factor() {
var a = Factor1();
while (tok == '*' || tok == '/'||tok == '\\'||tok == 'mod'||tok == 'div') {
var op = tok;
Next();
var b = Factor1();
if (op=='\\'||op=='div') {a = 'Math.floor((' + a + ')/(' + b + '))';}
else if (op=='mod') {a = '((' + a + ')%(' + b + '))';}
else {a = '(' + a + ')' + op + '(' + b + ')';}
}
return a;
}
function Term() {
var a = Factor();
while (tok == '+' || tok == '-') {
var op = tok;
Next();
var b = Factor();
a = '(' + a + ')' + op + '(' + b + ')';
}
return a;
}
function Relational() {
var a = Term();
while (tok == '=' || tok == '<' || tok == '>' ||
tok == '<>' || tok == '<=' || tok == '>=' || tok == '=>') {
var op = tok;
Next();
var op2 = op + tok;
if (op2 == '<>' || op2 == '<=' || op2 == '>=' || op2 == '=>') {
op = op2;
Next();
}
if (op == '=>') {
op = '>=';
}
var b = Term();
if (op == '=') {
a = '(' + a + ') == (' + b + ') ? -1 : 0';
} else if (op == '<>') {
a = '(' + a + ') != (' + b + ') ? -1 : 0';
} else {
a = '(' + a + ') ' + op + ' (' + b + ') ? -1 : 0';
}
}
return a;
}
function Logical1() {
var ret = '';
while (tok == 'not') {
Next();
ret += '~';
}
return ret + '(' + Relational() + ')';
}
function Logical() {
var a = Logical1();
while (tok == 'and') {
Next();
var b = Logical1();
a = '(' + a + ') & (' + b + ')';
}
return a;
}
function Logical03() {
var a = Logical();
while (tok == 'imp') {
Next();
var b = Logical();
a = '-Math.abs(+(!(' + a + '|' + b + ') | ' + b + '))';
}
return a;
}
function Logical02() {
var a = Logical03();
while (tok == 'eqv') {
Next();
var b = Logical03();
a = '-Math.abs(+(' + a + '==' + b + '))';
}
return a;
}
function Logical01() {
var a = Logical02();
while (tok == 'xor') {
Next();
var b = Logical02();
// a = '-Math.abs(+('+a+'? !'+b+':'+b+'))';
a = '('+a+')^('+b+')';
}
return a;
}
function Expression() {
var a = Logical01();
while (tok == 'or') {
Next();
var b = Logical01();
a = '(' + a + ') | (' + b + ')';
}
return a;
}
function TypeName() {
if (SIMPLE_TYPE_INFO[tok]) {
var type = tok;
Next();
return type;
} else if (tok == 'integer') {
Skip('integer');
return 'short';
} else if (tok == 'any') {
Skip('any');
// TODO: Handle this properly.
return 'string';
} else if (types[tok] !== undefined) {
var type_name = tok;
if (types[type_name] === undefined) {
Throw('Undefined type');
}
Next();
return type_name;
}
Throw('Undefined type "' + tok + '"');
}
function ImplicitType(name) {
return IMPLICIT_TYPE_MAP[name.slice(-3)] || IMPLICIT_TYPE_MAP[name.slice(-2)] || IMPLICIT_TYPE_MAP[name.slice(-1)] ||
letter_default[name[0]] || 'single';
}
function Align(alignment) {
allocated = Math.floor((allocated + alignment - 1) /
alignment) * alignment;
}
function Allocate(size) {
Align(size > 8 ? 8 : size);
var ret = allocated;
allocated += size;
return ret;
}
function DimScalarVariable(name, type_name, defaults) {
var info = types[type_name] || SIMPLE_TYPE_INFO[type_name];
if (info === undefined) {
Throw('Unknown type');
}
var size = info.size;
var offset = Allocate(size);
vars[name] = {
offset: offset,
dimensions: 0,
type_name: type_name,
global: vars === global_vars,
};
if (inside_type) {
var_decls += '// field ' + name + ' is at ' + offset + '\n';
} else if (vars[name].global) {
var_decls += '// ' + name + ' is at ' + offset + '\n';
} else {
if (inside_function) {
curop += '// ' + name + ' is at (bp + ' + offset + ')\n';
} else {
var_decls += '// ' + name + ' is at (bp + ' + offset + ')\n';
}
}
if (defaults.length > 0) {
curop += IndexVariable(name, true) + ' = ' + defaults[0] + ';\n';
}
}
function MaybeImplicitDimVariable(name, argument_to_function) {
// TODO: Handle array variables.
if (argument_to_function &&
argument_to_function.vars[name] !== undefined) {
return argument_to_function.vars[name];
}
if (vars[name] !== undefined) {
return vars[name];
}
if (global_vars[name] !== undefined) {
return global_vars[name];
}
if (option_explicit) {
Throw('Undeclared variable ' + name);
}
var type_name = ImplicitType(name);
DimScalarVariable(name, type_name, []);
return vars[name];
}
function ArrayPart(offset, i) {
return SIMPLE_TYPE_INFO['long'].view +
'[((' + offset + '>>2)+' + i + ')]';
}
function ReserveArrayCell(name) {
if (vars[name] === undefined &&
global_vars[name] == undefined) {
var offset = Allocate(4 + MAX_DIMENSIONS * 4 * 2);
vars[name] = {
offset: offset,
dimensions: null,
type_name: null,
global: vars === global_vars,
};
var boffset = offset;
if (!vars[name].global) {
boffset = '(bp+' + boffset + ')';
}
var_decls += '// ' + name + ' is at ' + ArrayPart(boffset, 0) +
' (cell-addr: ' + offset + ')\n';
}
if (vars[name] !== undefined) {
return vars[name];
}
return global_vars[name];
}
function DimVariable(default_tname,redim,is_declare) {
var name=tok;
IsKeyword(name);
Next();
// Pick default.
if (default_tname===null) {default_tname=ImplicitType(name);}
var type_name=default_tname;
var dimensions=[];
var defaults=[];
var is_scalar=true;
if (tok == '(') {
Skip('(');
if (tok==')') {Throw(name+' : missing dimension(s) for array?');}
is_scalar=false;
while (tok!=')') {
var e=Expression();
var d='dim'+const_count++;
//cjv var_decls+='const '+d+'=('+e+');\n';
curop+='const '+d+'=('+e+');\n'; //cjv
if (tok=='to') {
Skip('to');
var e1 = Expression();
var d1='dim'+const_count++;
//cjv var_decls += 'const ' + d1 + ' = (' + e1 + ');\n';
curop += 'const ' + d1 + ' = (' + e1 + ');\n'; //cjv
dimensions.push([d, d1]);
} else {
dimensions.push([option_base, d]);
}
if (tok != ',') {
break;
}
Skip(',');
}
Skip(')');
if (tok == '=') {
Skip('=');
Skip('{');
var e = Expression();
defaults.push(e);
while (tok == ',') {
Skip(',');
var e = Expression();
defaults.push(e);
}
Skip('}');
}
} else if (tok == '=') {
Skip('=');
var e = Expression();
defaults.push(e);
}
if (tok == 'as') {
Skip('as');
type_name = TypeName();
}
if (vars[name] !== undefined && vars[name].dimensions != null) {
if (redim) {
return;
}
Throw('Variable ' + name + ' defined twice');
}
if (is_scalar) {
DimScalarVariable(name, type_name, defaults);
} else {
if (dimensions.length > MAX_DIMENSIONS) {
Throw('Too many dimensions');
}
var offset = ReserveArrayCell(name).offset;
var info = types[type_name] || SIMPLE_TYPE_INFO[type_name];
var parts = [];
for (var i = 0; i < dimensions.length; i++) {
parts.push('((' + dimensions[i][1] + ')-(' +
dimensions[i][0] + ')+1)');
}
if (!is_declare) {
if (!vars[name].global) {
offset = '(bp+' + offset + ')';
}
curop += '// Allocate ' + name + '\n';
curop += 'if (' + ArrayPart(offset, 0) + ' === 0) {\n';
curop += ' ' + ArrayPart(offset, 0) + ' = Allocate(' +
[info.size].concat(parts).join('*') + ');\n';
for (var i = 0; i < dimensions.length; i++) {
curop += ' ' + ArrayPart(offset, i * 2 + 1) + ' = ' +
dimensions[i][0] + ';\n';
curop += ' ' + ArrayPart(offset, i * 2 + 2) + ' = ' +
[info.size].concat(parts).slice(0, i + 1).join('*') + ';\n';
}
if (defaults.length > 0) {
if (dimensions.length > 1) {
Throw('Only 1-d array defaults supported');
}
if (!SIMPLE_TYPE_INFO[type_name]) {
Throw('Only simple type array defaults supported');
}
for (var i = 0; i < defaults.length; i++) {
curop += ' ' + info.view + '[' +
' + (' + ArrayPart(offset, 0) + ' >> ' + info.shift + ') + '
+ i + '] = (' + defaults[i] + ');\n';
}
}
curop += '}\n';
}
vars[name] = {
offset: offset,
dimensions: dimensions.length > 0 ? dimensions.length : -1,
type_name: type_name,
global: vars === global_vars,
};
}
}
function IndexVariable(name, assignable, argument_to_function) {
IsKeyword(name);
var v = MaybeImplicitDimVariable(name, argument_to_function);
var offset = v.offset;
if (!v.global) {
if (argument_to_function) {
offset = '(sp+' + offset + ')';
} else {
if ((offset.toString()).indexOf('bp') == -1) {offset = '(bp+' + offset + ')';}
//cjv OFFSET BUG FIX ???
}
}
var type_name = v.type_name;
while (!argument_to_function && (tok == '(' || tok == '.')) {
if (tok=='(') {
Skip('(');
var dims=[];
var e='';
while (tok!=')') {
e=Expression();
dims.push(e);
if (tok!=',') {break;}
Skip(',');
}
if (e=='') {Throw(name+': undeclared function');}
Skip(')');
var info=types[type_name]||SIMPLE_TYPE_INFO[type_name];
// Extra indirection for array parameter access.
if (v.dimensions === -1) {
offset = 'i[' + offset + ']';
}
var noffset = '(';
noffset += ArrayPart(offset, 0) + ' + (';
if (v.dimensions !== -1 && dims.length != v.dimensions) {
if (v.dimensions==0) {
// Throw(name+': undeclared array/function');
Throw(name+': missing dimension or parameter');
}
else {
Throw('Array dimension expected ' + v.dimensions +
' but found ' + dims.length + ', array named: ' + name);
}
}
for (var i = 0; i < dims.length; ++i) {
noffset += '(((' + dims[i] + ')|0)-' +
ArrayPart(offset, i * 2 + 1) + ')';
noffset += '*' + ArrayPart(offset, i * 2 + 2);
if (i != dims.length - 1) {
noffset += '+';
}
}
noffset += '))';
offset = noffset;
} else if (tok == '.') {
Skip('.');
v = types[type_name];
if (v === undefined) {
Throw('Not a struct type');
}
var field = v.vars[tok];
if (field === undefined) {
Throw('Invalid field name');
}
Next();
offset = '(' + offset + ' + ' + field.offset + ')';
type_name = field.type_name;
}
}
var info = SIMPLE_TYPE_INFO[type_name];
if (!info) {
Throw('Expected simple type');
}
var vname = info.view + '[' + offset + '>>' + info.shift + ']';
if (info.view == 'str' && assignable === undefined) {
vname = '((' + vname + ')||"")';
}
return vname;
}
function FunctionDefine(options) {
var name = tok;
Next();
if (vars !== global_vars) {
Throw('Nested SUB/FUNCTION not allowed');
}
IsKeyword(name);
if (functions[name] !== undefined && !functions[name].is_declaration) {
Throw('SUB/FUNCTION/CONST/EQN already defined: ' + name);
}
NewOp();
var pos = ops.length - 1;
var parameters = [];
var old_allocated = allocated;
allocated = 0;
vars = {};
var nfunc = {
vars: vars,
parameters: parameters,
ip: ops.length,
allocation: -1,
is_subroutine: options.is_subroutine || false,
is_declaration: options.is_declaration || false,
};
if (nfunc.is_declaration) {
if (nfunc.is_subroutine) {
var_decls += '// SUB ' + name + '\n';
} else {
var_decls += '// FUNCTION ' + name + '\n';
}
} else {
if (nfunc.is_subroutine) {
curop += '// SUB ' + name + '\n';
} else {
curop += '// FUNCTION ' + name + '\n';
}
}
if (!nfunc.is_declaration) {
inside_function = true;
}
DimScalarVariable(name, ImplicitType(name), []);
// In case return value gets redefined.
Align(8);
if (tok == '(') {
Skip('(');
if (tok != ')') {
if (tok=='byref') {Skip(tok);}
if (tok=='byval') {Skip(tok);}
parameters.push(tok);
DimVariable(null, undefined, true);
while (tok == ',') {
Skip(',');
if (tok=='byref') {Skip(tok);}
if (tok=='byval') {Skip(tok);}
parameters.push(tok);
DimVariable(null, undefined, true);
}
}
Skip(')');
Align(8);
}
nfunc.allocation = allocated;
if (options.is_declaration) {
vars = global_vars;
allocated = old_allocated;
if (functions[name] == undefined) {
functions[name] = nfunc;
} else {
// TODO: Check for declaration mismatch in type.
if (functions[name].parameters.length != nfunc.parameter.length) {
Throw('DECLARE and definition parameters do not match');
}
}
} else {
function_old_allocated = old_allocated;
function_define_pos = pos;
function_name = name;
functions[name] = nfunc;
}
if (tok == 'as') {
Skip('as');
var type_name = TypeName();
if (!SIMPLE_TYPE_INFO[type_name]) {
Throw('Expected basic type');
}
functions[name].type_name = type_name;
}
if (tok == 'static') {
Skip('static');
// TODO: Implement.
}
}
function FunctionExit() {
if (vars === global_vars) {
Throw('SUB/FUNCTION EXIT only allowed inside SUB/FUNCTION.');
}
curop += 'sp -= 8; ip = i[sp>>2];\n';
NewOp();
}
function FunctionEnd() {
if (vars === global_vars) {
Throw('SUB/FUNCTION END only allowed at end of SUB/FUNCTION.');
}
inside_function = false;
FunctionExit();
ops[function_define_pos] += 'ip = ' + ops.length + ';\n';
vars = global_vars;
Align(8);
functions[function_name].allocation = allocated;
function_name = null;
allocated = function_old_allocated;
}
function FunctionCall(name,options) {
var func=functions[name];
if (func==undefined) {Throw(name+' is undeclared');}
if (options.is_subroutine!==func.is_subroutine) {
if (options.is_subroutine) {
Throw('Expected valid subroutine name, found: ' + name);
} else {
Throw('Expected valid function name, found: ' + name);
}
}
curop += 'i[sp>>2] = bp; sp += 8;\n';
var has_parens = tok == '(' || func.parameters.length != 0;
if ((options.is_call && has_parens) || (!options.is_subroutine && has_parens) || (!options.is_call && options.is_subroutine && has_parens)) {
Skip('(',name);
}
var args = [];
for (var i = 0; i < func.parameters.length; ++i) {
if (func.vars[func.parameters[i]].dimensions == -1) {
var vname = tok;
Next();
Skip('(');
Skip(')');
args.push(null);
curop += 'i[sp + ' + func.vars[func.parameters[i]].offset + '] = ' +
VarPtr(vname) + ';\n';
} else {
var old_tok_count = tok_count;
var old_tok = tok;
var e = Expression();
curop += IndexVariable(func.parameters[i], true, func) +
' = ' + e + ';\n';
if (vars[old_tok] && tok_count - old_tok_count == 1) {
args.push(old_tok);
} else {
args.push(null);
}
}
if (i != func.parameters.length - 1) {
Skip(',');
}
}
if ((options.is_call && has_parens) || (!options.is_subroutine && has_parens) || (!options.is_call && options.is_subroutine && has_parens)) {
Skip(')');
}
// Blank return value.
if (!options.is_subroutine) {
curop += IndexVariable(name, true, func) + ' = 123;\n';
}
curop += 'bp = sp;\n';
curop += 'sp += functions["' + name + '"].allocation;\n';
curop += 'i[sp>>2] = ip; sp += 8;\n';
curop += 'ip = functions["' + name + '"].ip;\n';
NewOp();
// TODO: Types?
curop += 'sp -= functions["' + name + '"].allocation;\n';
curop += 'bp = i[(sp-8)>>2];\n';
var temp = '#temp' + temp_count;
if (!options.is_subroutine) {
++temp_count;
DimScalarVariable(temp, func.vars[name].type_name, []);
curop += IndexVariable(temp, true) +
' = ' + IndexVariable(name, false, func) + ';\n';
}
for (var i = 0; i < args.length; ++i) {
if (args[i]) {
if (constants_list.indexOf('C'+args[i]+'C')>-1) {args[i] = '(' + args[i] + ')';}
curop += IndexVariable(args[i], true) + ' = ' +
IndexVariable(func.parameters[i], false, func) + ';\n';
}
}
curop += 'sp -= 8;\n';
if (!options.is_subroutine) {
return IndexVariable(temp, false);
}
}
function End() {
Sleep(0.5); yielding=1; autodisplay=1; quitting=1;
if (canvas) {
console.log('=== BASIC END ===');
setTimeout(function(){alert('=== END OF PROGRAM HAS BEEN REACHED ===')},10);
} else {
if (output_buffer!='') {
PutCh(null);
}
}
throw '';
}
function SetClipboardText(t) {
navigator.clipboard.writeText(t);
}
function fOpen(fname,fmode,fnum) {
localStorage.setItem('file'+fnum,'');
localStorage.setItem('file'+fnum+'_name',fname);
}
function fPrint(fnum, t, sttmnt) {
if (t.length==0) {
t = '\n';
}
else if (t.slice(-1)!==';' && t.slice(-1)!==',') {
t = t + '\n';
}
sessionStorage.setItem('file' + fnum, (sessionStorage.getItem('file' + fnum)) + t);
}
function fClose(fnum) {
hiddenElement.href = 'data:attachment/text,' + encodeURIComponent(localStorage.getItem('file' + fnum));
hiddenElement.target = '_blank';
hiddenElement.download = localStorage.getItem('file' + fnum + '_name') || 'BAM_' + fnum + '.txt';
hiddenElement.click();
}
function Lprint(t) {
spool_text=spool_text+t+'\n';
if (!spool_name) {
EndSpool();
}
}
function EndSpool() {
hiddenElement.href = 'data:attachment/text,' + encodeURIComponent(spool_text);
hiddenElement.target = '_blank';
hiddenElement.download = spool_name || 'BAM_LPRINT_Output.txt';
hiddenElement.click();
CancelSpool();
}
function CancelSpool() {
spool_name='';
spool_text='';
}
function OpenWindow(c) {
if ( window.location == window.parent.location ) {
var html = '';
openwindow = window.open('', 'ThisBamOutputWindow', '');
openwindow.document.write(html);
openwindow.document.body.innerHTML = c.replace('script',' script');
}}
function GetWinWidth() {return innerWidth;}
function GetWinHeight() {return innerHeight;}
function MapSet(k,v) {
gmap.set(k,v);
}
function MapGet(k) {
var v = gmap.get(k);
if (v===undefined) {v=""}
return v;
}
function ClearLocalStorage() {
localStorage.clear();
}
function RemoveLocalStorageItem(k) {
localStorage.removeItem(k);
}
function SetLocalStorageItem(k,v) {
localStorage.setItem(k,v);
}
function GetLocalStorageItem(k) {
var v = localStorage.getItem(k);
if (v === null) { v = ""}
return v;
}
function SetSessionStorageItem(k,v) {
sessionStorage.setItem(k,v);
}
function GetSessionStorageItem(k) {
var v = sessionStorage.getItem(k);
if (v === null) { v = ""}
return v;
}
function Sleep(t) {
yielding = 1;
delay = t * 1000;
}
function GetChr(k) {
var v = "";
var n=0;
var f = font_height;
for (let r=0;r<8;r++) {
for (let c=0;c<8;c++) {
n = font_data[k*64*f/8+r*8*f/8+c];
if (n==0) {v=v+'.';} else {v=v+'X';}
}
}
return v;
}
function LetChr(k,v) {
var f = font_height;
var n=0;
for (let r=0;r<8;r++) {
for (let c=0;c<8;c++) {
if (v[r*8+c]=='X') {n=255;} else {n=0;}
font_data[r*f + c +k*64*f/8]=n;
if (f==16) {font_data[(r)*f+8 + c +k*64*f/8]=n;}
}
}
}
function MouseWheel() {
var mwnow = mouse_wheel;
mouse_wheel = 0;
return mwnow;
}
function InitAudio() {globalAudioContext = new (window.AudioContext || window.webkitAudioContext)();}
function EndAudio() {yielding = 0; for (var i = 0; i < audio_queue.length; i++) { clearTimeout(audio_queue.pop());} audio_queue_timer = 0;}
function AudioDone() {return -(audio_queue_timer 0) {
return keys.shift();
} else {
return '';
}
}
function KeyClear() {
while (keys.length > 0) {Inkey();}
}
function Yield() {yielding=1;}
function Right(s,n) {return s.substr(s.length-n);}
function LabelExists(l) {
if (labels[((l).replace(/^0+/, "").toLowerCase())]!==undefined) {return -1;}
else {return 0;}
}
function BadLabel(l) {Throw("Invalid line identifier: "+l,false);}
function PointPos(f) {
if (f==0||f==2) {return pen_x;}
if (f==1||f==3) {return pen_y;}
}
function Point(x,y) {
x=Math.floor(x);
y=Math.floor(y);
var c=display_data[x+y*display.width];
if (color_map==undefined) {
return RGB2((c&0x000000ff),((c&0x0000ff00)>>8),((c&0x00ff0000)>>16));
}
if (color_map.length==16||color_map.length==256) {
return color_map.indexOf(c-0xff000000-0x1000000);
}
if (color_map.length==2) {
return color_map.indexOf(c);
}
if (color_map.length==4) {
if (c==0xff000000) {
return 0;
}
return color_map.indexOf(c-0xff000000-0x1000000);
}
return color_map;
}
function StringRep(n, ch) {
var ret = '';
var cch;
if (typeof ch == 'string') {
cch = ch;
} else {
cch = String.fromCharCode(ch);
}
for (var i = 0; i < n; ++i) {
ret += cch;
}
return ret;
}
function ToString(s) {
if (s < 0) {
return s.toString();
} else {
return ' ' + s.toString();
}
}
function StrReplace(str,replaceWhat,replaceTo) {
replaceWhat = replaceWhat.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
var re = new RegExp(replaceWhat, 'g');
return str.replace(re,replaceTo);
}
function Peek(addr) {
return 0;
}
function RGB(r, g, b) {
return BLACK | r | (g << 8) | (b << 16);
}
function RGB2(r,g,b) {
return +("0x" + ("0" + Math.floor(r).toString(16)).slice(-2) + ("0" + Math.floor(g).toString(16)).slice(-2) + ("0" + Math.floor(b).toString(16)).slice(-2));
}
function RGB2BGR(v) {
var cr = Math.floor(v/256/256);
var cg = Math.floor((v-cr*256*256)/256);
var cb = v-cr*256*256-cg*256;
return RGB(cr,cg,cb);
}
function MouseZone(x,y,w,h) {
if (mouse_x>=x && mouse_x<=(x+w-1) && mouse_y>=y && mouse_y<=(y+h-1))
{return -1;}
return 0;
}
function BETWEEN(a,b,c,d) {
if (d==0) {return ((ac)?0:-1));}
else {return ((a<=b)?0:((a>=c)?0:-1));}
}
function DefModes() {
// TODO: Handle color right in CGA, EGA, VGA modes.
var L=0x55,M=0xAA,H=0xFF;
var p2=[BLACK,WHITE];
var p4=[BLACK,RGB(0,M,M),RGB(M,0,M),RGB(M,M,M)];
var p16=[
RGB(0,0,0),RGB(0,0,M),RGB(0,M,0),RGB(0,M,M),RGB(M,0,0),RGB(M,0,M),RGB(M,L,0),RGB(M,M,M),
RGB(L,L,L),RGB(L,L,H),RGB(L,H,L),RGB(L,H,H),RGB(H,L,L),RGB(H,L,H),RGB(H,H,L),RGB(H,H,H)];
var p256=[
RGB(0,0,0),RGB(0,0,M),RGB(0,M,0),RGB(0,M,M),RGB(M,0,0),RGB(M,0,M),RGB(M,M,0),RGB(M,M,M),
RGB(0,0,L),RGB(0,0,H),RGB(0,M,L),RGB(0,M,H),RGB(M,0,L),RGB(M,0,H),RGB(M,M,L),RGB(M,M,H),
RGB(0,L,0),RGB(0,L,M),RGB(0,H,0),RGB(0,H,M),RGB(M,L,0),RGB(M,L,M),RGB(M,H,0),RGB(M,H,M),
RGB(0,L,L),RGB(0,L,H),RGB(0,H,L),RGB(0,H,H),RGB(M,L,L),RGB(M,L,H),RGB(M,H,L),RGB(M,H,H),
RGB(L,0,0),RGB(L,0,M),RGB(L,M,0),RGB(L,M,M),RGB(H,0,0),RGB(H,0,M),RGB(H,M,0),RGB(H,M,M),
RGB(L,0,L),RGB(L,0,H),RGB(L,M,L),RGB(L,M,H),RGB(H,0,L),RGB(H,0,H),RGB(H,M,L),RGB(H,M,H),
RGB(L,L,0),RGB(L,L,M),RGB(L,H,0),RGB(L,H,M),RGB(H,L,0),RGB(H,L,M),RGB(H,H,0),RGB(H,H,M),
RGB(L,L,L),RGB(L,L,H),RGB(L,H,L),RGB(L,H,H),RGB(H,L,L),RGB(H,L,H),RGB(H,H,L),RGB(H,H,H),
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,RGB(H,H,H)];
return {
0: [640,200,2.4,8,p16,4], 1: [320,200,1.2,8,p4,2],
2: [640,200,2.4,8,p2,1], 7: [320,200,1.2,8,p16,4],
8: [640,200,2.4,8,p16,4], 9: [640,350,480/350,14,p16,4],
10: [640,350,480/350,14,p2,1], 11: [640,480,1,16,p2,1],
12: [640,480,1,16,p16,4], 13: [320,200,1.2,8,p256,8],
14: [320,200,1,8,p256,8], 15: [320,200,2.4,8,p256,8],
16: [320,200,1.2,16,p256,8], 17: [320,200,1,16,p256,8],
23: [640,400,1.2,8,undefined,24],
24: [640,400,1,8,undefined,24], 25: [640,400,2.4,8,undefined,24],
26: [640,400,1.2,16,undefined,24], 27: [640,400,1,16,undefined,24]
}
}
function InitPalette() {
var modes=DefModes();
var m=modes[screen_mode];
color_map=m[4];
// reverse_color_map = {};
if (color_map !== undefined) {
for (var i = 0; i < color_map.length; ++i) {
reverse_color_map[color_map[i]] = i;
}
fg_color = color_map[color_map.length - 1];
bg_color = color_map[0];
} else {
fg_color = WHITE;
bg_color = BLACK;
}
}
function Screen(mode, m0, m1) {
if (mode==32) {mode=27;}
else if ( [0,1,2,7,8,9,10,11,12,13,14,15,16,17,23,24,25,26,27].indexOf(mode) == -1) {
mode = 0;
curr_mode= 0;
}
else {curr_mode = mode;}
if (!canvas) {return;}
// TODO: Handle color right in CGA, EGA, VGA modes.
// var monochrome=DefPal(2);
// cjv: adding 16 colors to rgba for gw-basic compatibility was a bad idea; reverted
// var p16=DefPal(16);
// var p256=DefPal(256);
// var screen1=DefPal(4);
var modes=DefModes();
var m=modes[mode];
if (m===undefined) {
Throw('Invalid mode '+mode);
}
if (m0!=undefined) {
m[0]=m0;
}
if (m1!=undefined) {
m[1]=m1;
}
curr_width=m[0];
curr_height=m[1];
SetupDisplay(m[0],m[1],m[2],m[3]);
window.dispatchEvent(new Event('resize'));
color_map=m[4];
screen_bpp=m[5];
reverse_color_map = {};
if (color_map !== undefined) {
for (var i = 0; i < color_map.length; ++i) {
reverse_color_map[color_map[i]] = i;
}
fg_color = color_map[color_map.length - 1];
bg_color = color_map[0];
} else {
fg_color = WHITE;
bg_color = BLACK;
}
screen_mode = mode;
pen_x = display.width / 2;
pen_y = display.height / 2;
Cls(0);
}
function Width(w) {
//if (screen_mode == 0 && (w == 80 || w == 40)) {
// SetupDisplay(w * 8, display.height, w == 80 ? 2.4 : 1.2, font_height);
//}
var modes=DefModes();
var m=modes[screen_mode];
if (m[0] > 0) {
SetupDisplay(w * 8, display.height, ( w * m[2] / (m[0]/8) ), font_height);
Cls(0);}
}
function Height(w) {
var modes=DefModes();
var m=modes[screen_mode];
if (m[0] > 0) {
var ny = w * m[3];
var nx = display.width * (ny/display.height);
SetupDisplay(display.width, ny, ( nx * m[2] / (m[0]/font_height) ), font_height);
Cls(0);}
}
var output_buffer = '';
function PutCh(ch) {
if (!canvas) {
if (ch == null) {
console.log(output_buffer);
output_buffer = '';
} else {
output_buffer += ch;
}
return;
}
if (ch == null) {
text_x = 0;
text_y++;
}
else {
var fg = fg_color;
var bg = bg_color;
var chcode = (ch.charCodeAt(0) & 0xff) >>> 0;
var chpos = chcode * font_height * 8;
for (var y = 0; y < font_height; ++y) {
var pos = text_x * 8 + (y + text_y * font_height) * display.width;
for (var x = 0; x < 8; ++x) {
display_data[pos++] = font_data[chpos++] ? fg : bg;
}
}
text_x++;
if (text_x >= text_width) {
text_y++;
text_x = 0;
}
}
if (text_y >= text_height) {
text_y = text_height - 1;
for (var i = (font_height*display.width - 1); i < (display.width * display.height - 1); i++) {
display_data[i-(font_height*display.width)] = display_data[i];
}
for (var i = ((display.width*display.height)-(font_height*display.width)); i < (display.width * display.height - 1); i++) {
display_data[i] = bg_color;
}
}
}
function Pcopy(x1,y1,x2,y2,s,d) {
var srcdata;
if (x1==undefined || x1<0) {x1=0;}
if (y1==undefined || y1<0) {y1=0;}
if (x2==undefined || Math.floor(x2)>display.width-1) {x2=display.width-1;}
if (y2==undefined || Math.floor(y2)>display.height-1) {y2=display.height-1;}
x1=Math.floor(x1);y1=Math.floor(y1);
x2=Math.floor(x2);y2=Math.floor(y2);
switch(s) {
case -1:
srcdata = page0; break;
case 0:
srcdata = display_data; break;
case 1:
if (page1==undefined) {alert("Page 1 is undefined");}
srcdata = page1; break;
case 2:
if (page2==undefined) {alert("Page 2 is undefined");}
srcdata = page2; break;
default:
alert(s + " is an invalid source page for PCOPY."); return;
}
switch(d) {
case -1:
page0=[...srcdata];
break;
case 0:
if (x1==0 && y1==0 && x2==display.width-1 && y2==display.height - 1) {
for (var i = 0; i < display.width * display.height; i++) {
display_data[i] = srcdata[i];}}
else {for (var y = y1; y < y2+1; y++) {
for (var x = x1; x < x2+1; x++) {
display_data[x+y*display.width] = srcdata[x+y*display.width];
}}}
break;
case 1:
if (page1==undefined) {page1=[...srcdata];}
else if (x1==0 && y1==0 && x2==display.width-1 && y2==display.height - 1) {page1=[...srcdata];}
else {for (var y = y1; y < y2+1; y++) {
for (var x = x1; x < x2+1; x++) {
page1[x+y*display.width] = srcdata[x+y*display.width];
}}}
break;
case 2:
if (page2==undefined) {page2=[...srcdata];}
else if (x1==0 && y1==0 && x2==display.width-1 && y2==display.height - 1) {page2=[...srcdata];}
else {for (var y = y1; y < y2+1; y++) {
for (var x = x1; x < x2+1; x++) {
page2[x+y*display.width] = srcdata[x+y*display.width];
}}}
break;
default:
alert(d + " is an invalid destination page for PCOPY."); return;
}
}
function Scroll(x1,y1,x2,y2,h,v,w) {
var tempdata;tempdata = [...display_data];
var wx=0; var wy=0;
if (x1==undefined || x1<0) {x1=0;}
if (y1==undefined || y1<0) {y1=0;}
if (x2==undefined || x2>display.width-1) {x2=display.width-1;}
if (y2==undefined || y2>display.height-1) {y2=display.height-1;}
x1=Math.floor(x1); y1=Math.floor(y1); x2=Math.floor(x2); y2=Math.floor(y2); h=Math.floor(h);v=Math.floor(v);
h=h%(x2-x1);v=v%(y2-y1);
var xMin = x1; var xMax = x2; var yMin = y1; var yMax = y2;
if (h<0) {xMax += h;} else {xMin += h;}
if (v<0) {yMax += v; } else {yMin += v;}
// alert('xMin: ' + xMin + 'xMax: ' + xMax + 'yMin: ' + yMin + 'yMax: ' + yMax + '\ndisplay.width:' + display.width + 'display.height:' + display.height);
for (var y = y1; y < y2+1; y++) {
for (var x = x1; x < x2+1; x++) {
if (y>=yMin && y<=yMax && x>=xMin && x<=xMax) {
display_data[x + y * display.width] = tempdata[x + y * display.width - h - v*display.width];}
else {
if (w==0) {display_data[x + y * display.width] = bg_color;}
else {
if (xxMax) {wx=xMin+(x-xMax)-1;} else {wx=x-h;}
if (yyMax) {wy=yMin+(y-yMax)-1;} else {wy=y-v;}
display_data[x + y * display.width] = tempdata[wx + wy * display.width];}
}
}
}
}
function LineInput(crlf=1) {
while (keys.length > 0) {
const key = keys.shift();
if (key == String.fromCharCode(13)) {
--text_x;
if (crlf==1) {PutCh(' ');PutCh(null);}
return;
}
if (key == String.fromCharCode(8) && input_string.length > 0) {
input_string = input_string.substr(0, input_string.length - 1);
--text_x;
PutCh(' ');
text_x -= 2;
PutCh(String.fromCharCode(219));
}
if (key.charCodeAt(0) >= 32 && key.charCodeAt(0) <= 126) {
--text_x;
PutCh(key);
PutCh(String.fromCharCode(219));
input_string += key;
}
}
yielding = 1;
--ip;
}
function Print(items, sttmnt='p', fnum=null) {
if (items.length==0) {
PutCh(null);
return;
}
for (var i=0;i 0 ? 1 : 0);
value = Math.abs(value);
var before = 0;
var after = 0;
var found_point = false;
for (var i = 0; i < format.length; ++i) {
if (format[i] == '.') {
found_point = true;
} else if (format[i] == '#') {
if (found_point) {
++after;
} else {
++before;
}
}
}
var t = value;
var fail = Math.floor(t * Math.pow(10, -before)) > 0;
value = value * Math.pow(10, after);
var ret = '';
var done = false;
for (var i = format.length - 1; i >= 0; --i) {
if (format[i] == '#') {
if (fail) {
ret = '*' + ret;
} else if (done) {
ret = ' ' + ret;
} else {
ret = Math.floor(value % 10) + ret;
value = Math.floor(value / 10);
if (value == 0) {
done = true;
}
}
} else if (format[i] == '+' || format[i] == '-') {
if (fail) {
ret = '*' + ret;
} else if (sgn < 0) {
ret = '-' + ret;
} else if (sgn > 0) {
if (format[i] == '+') {
ret = '+' + ret;
}
} else {
ret = ' ' + ret;
}
} else if (format[i] == ',') {
if (fail) {
ret = '*' + ret;
} else if (done) {
ret = ' ' + ret;
} else {
ret = ',' + ret;
}
} else {
ret = format[i] + ret;
}
}
return ret;
}
function PrintUsing(format, items) {
var parts = [];
var p = '';
var has_num = false;
for (var i = 0; i < format.length; ++i) {
if (format[i] == '#' || format[i] == ',' || format[i] == '.') {
has_num = true;
} else {
if (has_num) {
parts.push(p)
p = '';
has_num = false;
}
}
p += format[i];
}
if (p != '') {
if (has_num) {
parts.push(p);
} else {
parts[parts.length - 1] += p;
}
}
var values = [];
for (var i = 0; i < items.length; i += 2) {
if (parts.length * 2 > i) {
items[i] = Using(parts[(i / 2) | 0], items[i]);
}
}
Print(items);
}
function ColorFlip(c) {
return BLACK |
((c & 0xff0000) >> 16) | ((c & 0xff) << 16) | (c & 0x00ff00);
}
function FixupColor(c) {
if (c === undefined) {c=fg_id;}
if (c === undefined) {
if (color_map ) {
return color_map[color_map.length - 1];
} else {
return WHITE;
}
}
if (color_map !== undefined) {
// cjv: gw-basic compatibility hack
var cml = color_map.length;
c = ((c % cml ) + cml) % cml;
return color_map[(c%color_map.length)] || color_map[color_map.length - 1]; // cjv || BLACK;
} else {
c = c | 0;return ColorFlip(c);
}
}
function Palette(c, v) {if (c < color_map.length) {color_map[c] = v;reverse_color_map[color_map[c]] = c;}}
function Color(fg, bg) {
if (fg != undefined) {fg_color = FixupColor(fg);fg_id=fg;};
if (bg != undefined) {bg_color = FixupColor(bg);bg_id=bg;};
}
// function Color_old(fg, bg) {
// if (screen_mode == 0 || screen_mode > 2) {
// if (fg != undefined) fg_color = FixupColor(fg);
// } else {
// fg_color = FixupColor(undefined);
// }
// if (screen_mode == 0 || screen_mode > 2) {
// if (bg != undefined) bg_color = FixupColor(bg);
// } else {
// bg_color = FixupColor(undefined);
// }
// }
function Locate(x, y) {
if (x != 0) { text_x = x - 1;}
if (y != 0) {text_y = y - 1;}
// Hack to yield more often (for NIBBLES.BAS)
if (x == 1 && y == 1) {
yielding = 1;
}
}
function Box(x1, y1, x2, y2, c) {
x1 = x1 | 0;
y1 = y1 | 0;
x2 = x2 | 0;
y2 = y2 | 0;
c = c | 0;
if (x1 > x2) {
var t = x2;
x2 = x1;
x1 = t;
}
if (y1 > y2) {
var t = y2;
y2 = y1;
y1 = t;
}
if (x1 >= display.width ||
y1 >= display.height ||
x2 < 0 || y2 < 0) {
return;
}
if (x1 < 0) x1 = 0;
if (x2 > display.width - 1) x2 = display.width - 1;
if (y1 < 0) y1 = 0;
if (y2 > display.height - 1) y2 = display.height - 1;
for (var y = y1; y <= y2; ++y) {
var pos = x1 + y * display.width;
for (var x = x1; x <= x2; ++x) {
display_data[pos++] = c;
}
}
}
function RawLine(x1, y1, x2, y2, c) {
x1 = x1 | 0;
y1 = y1 | 0;
x2 = x2 | 0;
y2 = y2 | 0;
if (x1 == x2 || y1 == y2) {
Box(x1, y1, x2, y2, c);
return;
}
if (Math.abs(x1 - x2) > Math.abs(y1 - y2)) {
if (x1 > x2) {
var tmp;
tmp = x1; x1 = x2; x2 = tmp;
tmp = y1; y1 = y2; y2 = tmp;
}
for (var x = x1; x <= x2; ++x) {
var t = (x - x1) / (x2 - x1);
// cjv: changed from Math.floor to Math.round; keep an eye on this
var y = y1 + Math.round(t * (y2 - y1));
Box(x, y, x, y, c);
}
} else {
if (y1 > y2) {
var tmp;
tmp = x1; x1 = x2; x2 = tmp;
tmp = y1; y1 = y2; y2 = tmp;
}
for (var y = y1; y <= y2; ++y) {
var t = (y - y1) / (y2 - y1);
// cjv: changed from Math.floor to Math.round; keep an eye on this
var x = x1 + Math.round(t * (x2 - x1));
Box(x, y, x, y, c);
}
}
}
function Line(x1, y1, x2, y2, c, fill) {
var pen_color = FixupColor(c);
if (fill == 0) {
// Should be line.
RawLine(x1, y1, x2, y2, pen_color);
} else if (fill == 1) {
Box(x1, y1, x2, y1, pen_color);
Box(x1, y2, x2, y2, pen_color);
Box(x1, y1, x1, y2, pen_color);
Box(x2, y1, x2, y2, pen_color);
} else {
Box(x1, y1, x2, y2, pen_color);
}
}
var last = 0;
function Line2(x1, y1, x2, y2, c, fill) {
var pen_color = FixupColor(c);
if (fill == 0) {
// Should be line.
RawLine(x1, y1, x2, y2, pen_color);
} else if (fill == 1) {
Box(x1, y1, x2, y1, pen_color);
Box(x1, y2, x2, y2, pen_color);
Box(x1, y1, x1, y2, pen_color);
Box(x2, y1, x2, y2, pen_color);
} else {
Box(x1, y1, x2, y2, pen_color);
}
pen_x = x2;
pen_y = y2;
}
var last = 0;
function Cls(mode) {
// TODO: Handle mode.
Box(0, 0, display.width, display.height, bg_color);
text_x = 0;
text_y = 0;
}
function Pset(x, y, c) {
x = Math.floor(x);
y = Math.floor(y);
if (x<0 || y<0 || x>=display.width || y>=display.height) {}
else {
var pen_color = FixupColor(c);
if (c==-1) {pen_color=bg_color;}
else if (c==-2) {pen_color=fg_color;}
display_data[x + y * display.width] = pen_color;
pen_x = x;
pen_y = y;
}
}
function Pset2(x, y, c) {
x = Math.floor(x);
y = Math.floor(y);
if (x<0 || y<0 || x>=display.width || y>=display.height) {}
else {
display_data[x + y * display.width] = c;
}}
function Circle(x, y, r, c, start, end, aspect, fill) {
x=Math.floor(x);
y=Math.floor(y);
r=Math.floor(r);
var pen_color = FixupColor(c);
if (c==-1) {pen_color=bg_color;}
else if (c==-2) {pen_color=fg_color;}
var complete = false;
if (start < 0) { start = -start; complete = true; }
if (end < 0) { end = -end; complete = true; }
if (end < start) {
end += Math.PI * 3;
}
var rx, ry;
if (aspect == null) {
rx = r;
ry = r / screen_aspect;
} else if (aspect > 1) {
rx = r / aspect;
ry = r;
} else {
rx = r;
ry = r * aspect;
}
if (start != 0 || (end != Math.PI * 3) || aspect != null) {
x += 0.5;
y += 0.5;
var oxx = x + Math.cos(start) * rx;
var oyy = y - Math.sin(start) * ry;
if (complete) {
RawLine(x, y, oxx, oyy, pen_color);
}
for (var ang = start; ang <= end; ang += 0.03) {
var xx = x + Math.cos(ang) * rx;
var yy = y - Math.sin(ang) * ry;
if (ang == start) { oxx = xx; oyy = yy; }
RawLine(oxx, oyy, xx, yy, pen_color);
oxx = xx;
oyy = yy;
}
if (complete) {
RawLine(x, y, xx, yy, pen_color);
}
}
else {
var a;
function DoPset2(x,xy,a,y,asa,xysa) {
Pset2(x+xy,y-asa, pen_color);
Pset2(x-xy,y-asa, pen_color);
Pset2(x+xy,y+asa, pen_color);
Pset2(x-xy,y+asa, pen_color);
Pset2(x-a,y+xysa, pen_color);
Pset2(x-a,y-xysa, pen_color);
Pset2(x+a,y+xysa, pen_color);
Pset2(x+a,y-xysa, pen_color);
if (fill==2) {
Pset2(x+xy-1,y-asa, pen_color);
Pset2(x-xy+1,y-asa, pen_color);
Pset2(x+xy-1,y+asa, pen_color);
Pset2(x-xy+1,y+asa, pen_color);
Pset2(x-a+1,y+xysa, pen_color);
Pset2(x-a+1,y-xysa, pen_color);
Pset2(x+a-1,y+xysa, pen_color);
Pset2(x+a-1,y-xysa, pen_color);
Pset2(x+xy,y-asa+1, pen_color);
Pset2(x-xy,y-asa+1, pen_color);
Pset2(x+xy,y+asa-1, pen_color);
Pset2(x-xy,y+asa-1, pen_color);
Pset2(x-a,y+xysa-1, pen_color);
Pset2(x-a,y-xysa+1, pen_color);
Pset2(x+a,y+xysa-1, pen_color);
Pset2(x+a,y-xysa+1, pen_color);}
}
for (var xy = 0; xy < r*0.8; xy += 1) {
a = Math.round(Math.sqrt(r * r - xy * xy));
// DoPset2(x,xy,a,y,(a),xy);
DoPset2(x,xy,a,y,(a/screen_aspect),xy/screen_aspect);
}
}
if (fill==1 && start == 0 && end > Math.PI * 2) {
Paint(x, y, c, c);
}
pen_x = x;
pen_y = y;
}
function Circle_OLD(x, y, r, c, start, end, aspect, fill) {
x=Math.floor(x);
y=Math.floor(y);
r=Math.floor(r);
var pen_color = FixupColor(c);
var complete = false;
if (start < 0) { start = -start; complete = true; }
if (end < 0) { end = -end; complete = true; }
if (end < start) {
end += Math.PI * 3;
}
var rx, ry;
if (aspect == null) {
rx = r;
ry = r / screen_aspect;
} else if (aspect > 1) {
rx = r / aspect;
ry = r;
} else {
rx = r;
ry = r * aspect;
}
if (start != 0 || (end != Math.PI * 3) || aspect != null) {
x += 0.5;
y += 0.5;
var oxx = x + Math.cos(start) * rx;
var oyy = y - Math.sin(start) * ry;
if (complete) {
RawLine(x, y, oxx, oyy, pen_color);
}
for (var ang = start; ang <= end; ang += 0.03) {
var xx = x + Math.cos(ang) * rx;
var yy = y - Math.sin(ang) * ry;
if (ang == start) { oxx = xx; oyy = yy; }
RawLine(oxx, oyy, xx, yy, pen_color);
oxx = xx;
oyy = yy;
}
if (complete) {
RawLine(x, y, xx, yy, pen_color);
}
}
else {
var a; var a_ar; var xy_ar;
for (var xy = 0; xy < r*0.8; xy += 1) {
a = Math.round(Math.sqrt(r * r - xy * xy));
Pset(x+xy,y-(a/screen_aspect), c);
Pset(x-xy,y-(a/screen_aspect), c);
Pset(x+xy,y+(a/screen_aspect), c);
Pset(x-xy,y+(a/screen_aspect), c);
Pset(x-a,y+(xy/screen_aspect), c);
Pset(x-a,y-(xy/screen_aspect), c);
Pset(x+a,y+(xy/screen_aspect), c);
Pset(x+a,y-(xy/screen_aspect), c);
Pset(x+xy-1,y-(a/screen_aspect), c);
Pset(x-xy+1,y-(a/screen_aspect), c);
Pset(x+xy-1,y+(a/screen_aspect), c);
Pset(x-xy+1,y+(a/screen_aspect), c);
Pset(x-a+1,y+(xy/screen_aspect), c);
Pset(x-a+1,y-(xy/screen_aspect), c);
Pset(x+a-1,y+(xy/screen_aspect), c);
Pset(x+a-1,y-(xy/screen_aspect), c);
Pset(x+xy,y-(a/screen_aspect)+1, c);
Pset(x-xy,y-(a/screen_aspect)+1, c);
Pset(x+xy,y+(a/screen_aspect)-1, c);
Pset(x-xy,y+(a/screen_aspect)-1, c);
Pset(x-a,y+(xy/screen_aspect)-1, c);
Pset(x-a,y-(xy/screen_aspect)+1, c);
Pset(x+a,y+(xy/screen_aspect)-1, c);
Pset(x+a,y-(xy/screen_aspect)+1, c);
}
}
if (fill && start == 0 && end > Math.PI * 2) {
Paint(x, y, c, c);
}
pen_x = x;
pen_y = y;
}
function GetImage(x1, y1, x2, y2, buffer, offset) {
x1 = x1 | 0;
y1 = y1 | 0;
x2 = x2 | 0;
y2 = y2 | 0;
if (x1>x2) {[x1,x2]=[x2,x1];}
if (y1>y2) {[y1,y2]=[y2,y1];}
var d16 = new Uint16Array(buffer);
if (screen_bpp <= 2) {
d16[(offset >> 1) + 0] = (x2 - x1 + 1) * screen_bpp;
} else {
d16[(offset >> 1) + 0] = (x2 - x1 + 1);
}
d16[(offset >> 1) + 1] = y2 - y1 + 1;
var d = new Uint8Array(buffer);
var src = display_data;
if (screen_bpp > 8) {
var dstpos = offset + 4;
for (var y = y1; y <= y2; ++y) {
var srcpos = x1 + y * display.width;
for (var x = x1; x <= x2; ++x) {
var v = src[srcpos++];
d[dstpos++] = v;
d[dstpos++] = (v >> 8);
d[dstpos++] = (v >> 16);
}
}
} else {
var dstpos = offset + 4;
var shift = 8;
var v = 0;
for (var y = y1; y <= y2; ++y) {
var srcpos = x1 + y * display.width;
for (var x = x1; x <= x2; ++x) {
shift -= screen_bpp;
var cc = reverse_color_map[src[srcpos++] | BLACK] | 0;
v |= (cc << shift);
if (shift == 0) {
d[dstpos++] = v;
v = 0;
shift = 8;
}
}
if (shift != 8) {
d[dstpos++] = v;
v = 0;
shift = 8;
}
}
}
}
function PutImage(x1, y1, buffer, offset, mode) {
x1 = x1 | 0;
y1 = y1 | 0;
var s16 = new Uint16Array(buffer);
var x2;
if (screen_bpp <= 2) {
x2 = x1 + (s16[(offset >> 1) + 0] / screen_bpp) - 1;
} else {
x2 = x1 + s16[(offset >> 1)] - 1;
}
var y2 = y1 + s16[(offset >> 1) + 1] - 1;
var s = new Uint8Array(buffer);
var dst = display_data;
if (screen_bpp > 8) {
var srcpos = offset + 4;
for (var y = y1; y <= y2; ++y) {
var dstpos = x1 + y * display.width;
for (var x = x1; x <= x2; ++x) {
var v = s[srcpos] | (s[srcpos + 1] << 8) | (s[srcpos + 2] << 16);
srcpos += 3;
// TODO: Optimize
if (x < 0 || x >= display.width || y < 0 || y >= display.height) {
dstpos++;
continue;
}
if (mode == 'xor') {
dst[dstpos++] = (dst[dstpos] ^ v) | BLACK;
} else if (mode == 'preset') {
dst[dstpos++] = (~v) | BLACK;
} else if (mode == 'and') {
dst[dstpos++] = (dst[dstpos] & v) | BLACK;
} else if (mode == 'or') {
dst[dstpos++] = (dst[dstpos] | v) | BLACK;
} else {
dst[dstpos++] = v | BLACK;
}
}
}
} else {
var srcpos = offset + 4;
var mask = (1 << screen_bpp) - 1;
for (var y = y1; y <= y2; ++y) {
var dstpos = x1 + y * display.width;
var v = 0;
var shift = 8;
for (var x = x1; x <= x2; ++x) {
if (shift == 8) {
v = s[srcpos++];
}
shift -= screen_bpp;
var cc = (v >> shift) & mask;
var old = reverse_color_map[dst[dstpos] | BLACK] | 0;
if (mode == 'xor') {
cc ^= old;
} else if (mode == 'preset') {
cc = cc ^ mask;
} else if (mode == 'and') {
cc &= old;
} else if (mode == 'or') {
cc |= old;
}
// alert('color_map[cc]: ' + color_map[cc]);
var px = color_map[cc] | 0;
// TODO: Optimize
if (y >= 0 && y < display.height && x >= 0 && x < display.width) {
dst[dstpos++] = px;
} else {
dstpos++;
}
if (shift == 0) {
shift = 8;
}
}
}
}
}
var draw_state = {
noplot: false,
nomove: false,
angle: 0,
turn_angle: 0,
color: undefined,
scale: 1,
};
function StepUnscaled(dx, dy) {
if (!draw_state.noplot) {
Line(pen_x, pen_y, pen_x + dx, pen_y + dy, draw_state.color, 0);
}
if (!draw_state.nomove) {
pen_x += dx;
pen_y += dy;
}
draw_state.noplot = false;
draw_state.nomove = false;
}
function Step(dx, dy) {
StepUnscaled(dx * draw_state.scale, dy * draw_state.scale);
}
function Draw(cmds) {
function AngleStep(a_inc) {
var a_tot = draw_angle*Math.PI/180+a_inc*Math.PI/180;
Step(Math.round(Math.cos(a_tot)*n),Math.round(-Math.sin(a_tot)*n));
}
cmds = cmds.toLowerCase();
cmds = cmds.replace(/\s+/g, '');
cmds = cmds.replace(/;/g, '');
cmds = cmds.replace(/=/g, '');
var m;
while (cmds.length) {
if (m = cmds.match(/^(u|d|l|r|e|f|g|h|w|x|y|z|a|ta|c|s)([0-9.+-]+)?/)) {
var op = m[1];
var n = m[2] == '' ? 1 : m[2];
if (op == 'c') {
draw_state.color = n;
} else if (op=='a') {
draw_angle=n*90;
} else if (op=='ta') {
draw_angle=n;
} else if (op=='s') {
draw_state.scale = n / 4;
} else if (op=='u') {AngleStep(90);}
else if (op=='d') {AngleStep(270);}
else if (op=='l') {AngleStep(180);}
else if (op=='r') {AngleStep(0);}
else if (op=='e') {n=Math.sqrt(n*n+n*n);AngleStep(45);}
else if (op=='f') {n=Math.sqrt(n*n+n*n);AngleStep(315);}
else if (op=='g') {n=Math.sqrt(n*n+n*n);AngleStep(225);}
else if (op=='h') {n=Math.sqrt(n*n+n*n);AngleStep(135);}
else if (op=='w') {AngleStep(45);}
else if (op=='x') {AngleStep(315);}
else if (op=='y') {AngleStep(225);}
else if (op=='z') {AngleStep(135);}
} else if (m = cmds.match(/^(m)([+-]?)([0-9]+)[,]([+-]?[0-9]+)/)) {
var op = m[1];
var sx = m[2];
var x = parseInt(m[3]);
var y = parseInt(m[4]);
if (sx) {
x = parseInt(sx + '1') * x;
Step(x, y);
} else {
StepUnscaled(x - pen_x, y - pen_y);
}
} else if (m = cmds.match(/^(p)([0-9]+),([0-9]+1)/)) {
var op = m[1];
var x = parseInt(m[2]);
var y = parseInt(m[3]);
// TODO: Implement.
} else if (m = cmds.match(/^(b|n)/)) {
var op = m[1];
if (op == 'b') {
draw_state.noplot = true;
} else if (op == 'n') {
draw_state.nomove = true;
}
} else {
Throw('Bad drop op: ' + cmds);
}
cmds = cmds.substr(m[0].length);
}
}
function Paint(x, y, paint, border) {
paint = FixupColor(paint);
if (border === undefined) {
border = paint;
} else {
border = FixupColor(border) & 0xffffff;
}
var fpaint = paint & 0xffffff;
var data = display_data;
var pending = [];
pending.push([Math.floor(x), Math.floor(y)]);
while (pending.length) {
var p = pending.pop();
if (p[0] < 0 || p[0] >= display.width ||
p[1] < 0 || p[1] >= display.height) {
continue;
}
var pos = p[0] + p[1] * display.width;
if ((data[pos] & 0xffffff) == border ||
(data[pos] & 0xffffff) == fpaint) {
continue;
}
data[pos] = paint;
pending.push([p[0] - 1, p[1]]);
pending.push([p[0] + 1, p[1]]);
pending.push([p[0], p[1] - 1]);
pending.push([p[0], p[1] + 1]);
}
}
function GetVar() {
var name = tok;
Next();
return IndexVariable(name, true);
}
var DEFAULT_TYPES = {
'defdbl': 'double',
'defsng': 'single',
'defint': 'short',
'deflng': 'long',
'defstr': 'string',
};
function Statement() {
if (EndOfStatement()) {
// Ignore empty lines.
} else if (tok == 'rem') {
do {
Next();
} while (tok != '');
} else if (tok == 'if') {
Skip('if');
var e = Expression();
var kw=tok;
if (kw=='goto') {Skip(kw);}
else {Skip('then');}
if (EndOfStatement()) {
If(e);
while (tok == ':') {
Skip(':');
Statement();
}
if (tok == 'else') {
Skip('else');
Else();
while (tok == ':') {
Skip(':');
Statement();
}
}
if (tok == 'end') {
Skip('end');
Skip('if');
EndIf();
}
} else {
// Classic if then
If(e);
if ((kw=='goto')||(tok.match(/^[0-9]+$/))) {
var name = tok;
Next();
curop += 'if (LabelExists("'+name+'")==-1) {ip = labels[("' + name + '").replace(/^0+/, "")];} else {BadLabel("'+name+'");}\n';
NewOp();
} else {
Statement();
while (tok==':') {
Skip(':');
Statement();
}
}
var f = flow.pop();
if (f[0]!='if') {
Throw('If in mixed style');
}
flow.push(f);
NewOp();
if (tok=='else') {
Skip('else');
Else();
if ((kw=='goto')||tok.match(/^[0-9]+$/)) {
var name = tok;
Next();
curop += 'if (LabelExists("'+name+'")==-1) {ip = labels[("' + name + '").replace(/^0+/, "")];} else {BadLabel("'+name+'");}\n';
NewOp();
} else {
Statement();
while (tok==':') {
Skip(':');
Statement();
} }
}
EndIf();
}
} else if (tok=='elseif') {
Skip(tok);
var e=Expression();
Skip('then');
ElseIf(e);
} else if (tok=='else') {
Skip(tok);
Else();
if (!EndOfStatement()) {Statement();}
} else if (tok=='do') {
Skip(tok);
if (tok=='while') {
// Support DO WHILE
Statement();
return;
}
NewOp();
flow.push(['do', ops.length]);
} else if (tok=='loop') {
Skip(tok);
var is_while;
if (tok=='while') {
Skip(tok);
is_while=true;
} else if (tok=='until') {
Skip(tok);
is_while=false;
} else if (EndOfStatement()) {
var f=flow.pop();
if (f[0] != 'while' && f[0] != 'do') {Throw('LOOP does not match DO / WHILE');}
curop+='ip = '+f[1]+';\n';
NewOp();
if (f[0]=='while') {ops[f[1]] += ops.length + '; }\n';}
return;
} else {Throw('Expected while/until');}
var e = Expression();
var f = flow.pop();
if (f[0]!='do') {Throw('LOOP does not match DO');}
if (is_while) {curop += 'if ('+e+') {ip='+f[1]+'; }\n';}
else {curop += 'if (!('+e+')) {ip='+f[1]+';}\n';}
NewOp();
} else if (tok=='while') {
Skip(tok);
var e=Expression();
NewOp();
curop+='if (!('+e+')) {ip=';
NewOp();
flow.push(['while',ops.length-1]);
} else if (tok=='wend') {
Skip(tok);
var f = flow.pop();
if (f[0]!='while') {Throw('Wend does not match while');}
curop += 'ip = ' + f[1] + ';\n';
NewOp();
ops[f[1]] += ops.length + '; }\n';
} else if (tok == 'exit') {
Skip('exit');
if (tok == 'sub') {
Skip('sub');
FunctionExit();
} else if (tok == 'function') {
Skip('function');
FunctionExit();
} else {
Throw('Exit only works with SUB and FUNCTION');
}
} else if (tok == 'end') {
Skip('end');
if (tok == 'if') {
Skip('if');
EndIf();
} else if (tok == 'select') {
Skip('select');
NewOp();
var f = flow.pop();
if (f[0] != 'select') {
Throw('end select outside select');
}
var disp = 'var t = (' + f[1] + ');\n';
disp += 'if (false) {}\n';
for (var i = 0; i < f[3].length; i++) {
var ii = f[3][i];
if (ii[0] == ii[1]) {
disp += 'else if (t == (' + ii[0] +
')) { ip = ' + ii[2] + '; }\n';
} else {
disp += 'else if (t >= (' + ii[0] + ') && t <= (' + ii[1] +
')) { ip = ' + ii[2] + '; }\n';
}
}
if (f[5] !== null) {
disp += 'else { ip = ' + f[5] + '; }\n';
} else {
disp += 'else { ip = ' + ops.length + '; }\n';
}
ops[f[2]] += disp;
for (var i = 0; i < f[4].length; i++) {
ops[f[4][i]] += 'ip = ' + ops.length + ';\n';
}
} else if (tok == 'sub') {
Skip('sub');
FunctionEnd();
sub_check = sub_check - 1;
} else if (tok == 'function') {
Skip('function');
FunctionEnd();
fn_check = fn_check - 1;
} else {
curop += 'End();\n';
}
} else if (tok=='stop') {
Skip(tok);
Throw('BREAK');
} else if (tok=='goto') {
Skip(tok);
var name = tok;
if (tok=='eval') {Skip(tok);Skip('(');name=Expression();Skip(')');curop += 'if (LabelExists(('+name+').toString())==-1) {ip = labels[((' + name + '.toString()).toLowerCase()).replace(/^0+/, "")];} else {BadLabel('+name+');}\n';}
else {Next();curop += 'if (LabelExists("'+name+'")==-1) {ip = labels[("' + name + '").replace(/^0+/, "")];} else {BadLabel("'+name+'");}\n';}
NewOp();
} else if (tok=='gosub') {
Skip(tok);
var name = tok;
curop += 'i[sp>>2] = ip; sp += 8;\n';
if (tok=='eval') {Skip(tok);Skip('(');name=Expression();Skip(')');curop += 'if (LabelExists(('+name+').toString())==-1) {ip = labels[((' + name + '.toString()).toLowerCase()).replace(/^0+/, "")];} else {BadLabel('+name+');}\n';}
else {Next();curop += 'if (LabelExists("'+name+'")==-1) {ip = labels[("' + name + '").replace(/^0+/, "")];} else {BadLabel("'+name+'");}\n';}
NewOp();
} else if (tok=='return') {
Skip(tok);
curop += 'sp-=8;ip=i[sp>>2];\n';
NewOp();
} else if (tok=='declare') {
Skip(tok);
if (tok=='sub') {Skip(tok);FunctionDefine({is_subroutine: true, is_declaration: true});}
else if (tok=='function') {Skip(tok);FunctionDefine({is_subroutine: false, is_declaration: true});}
else {Throw('Unexpected declaration');}
} else if (tok=='type'||tok=='struct'||tok=='structure'||tok=='record') {
var lbl=tok;
var old_allocated = allocated;
vars = {};
Skip(tok);
var type_name = tok;
Next();
if (types[type_name]!==undefined) {Throw('Duplicate type definition');}
types[type_name]={vars: vars,size: 0,};
inside_type=true;
var_decls+='// TYPE '+type_name+'\n';
SkipEndOfStatement();
while (tok!='end') {
while (!EndOfStatement()) {
DimVariable(null);
while (tok == ',') {Skip(',');DimVariable(null);}
}
SkipEndOfStatement();
}
Skip('end');
Skip(lbl);
inside_type=false;
types[type_name].size=allocated;
vars=global_vars;
allocated=old_allocated;
} else if (tok=='defdv') {
Skip(tok);
for (;;) {
var fname = tok;
var pos = FunctionDefine({is_subroutine: false});
Skip('=');
var e = Expression();
curop += IndexVariable(fname, true) + ' = ' + e + ';\n';
FunctionEnd(pos);
if (tok == ',') {
Skip(',');
continue;
}
break;
}
} else if (tok=='let') {
var kw=tok;
Skip(tok);
while (!EndOfStatement()) {
if (tok==',') {Skip(',');}
var name = tok;
Next();
var vname = IndexVariable(name, true);
if (constants_list.indexOf('C'+name+'C')>-1) {
Throw('A constant named "'+name+'" already exists.');}
if (tok=='=' || tok=='+=' || tok=='-=' ||
tok=='*=' || tok=='/=' || tok=='\\=' ||
tok=='^=' || tok=='&=') {
var op = tok;
Skip(tok);
var e = Expression();
if (op == '&=') {
op = '+=';
} else if (op == '\\=') {
op = '//=';
}
if (op == '^=') {curop += vname + ' = Math.pow(' + vname + ', ' + e + ');\n';}
else {curop += vname + ' ' + op + ' (' + e + ');\n';}
} else {
Throw('Expected "=" or "x=" found "' + tok + '"\n\n(assuming assignment to a variable; could be a case of a misspelled keyword)');
}}
} else if (tok=='dim'||tok=='redim'||tok=='var'||tok=='const') {
var op=tok;
Next();
if (tok=='shared') {
Skip('shared');
}
var tname = null;
if (tok=='as') {
Skip('as');
tname=TypeName();
}
IsKeyword(tok);
if (op=='const' && constants_list.indexOf('C'+tok+'C')==-1)
{constants_list=constants_list+'C'+tok+'C';}
else if (constants_list.indexOf('C'+tok+'C')>-1) {Throw('A constant named "'+tok+'" already exists.');}
DimVariable(tname,op=='redim');
while (tok==',') {
Skip(',');
DimVariable(tname,op=='redim');
}
} else if (tok == 'on') {
Skip(tok);
var name=Expression();
var kw=tok;
var i=0;
if (kw=='error') {Skip('error');}
else {
Next();if (EndOfStatement()) {Throw('Expected labels.');}
var rlbls = ' ';
while (!(EndOfStatement())) {
rlbls+=String(tok);
i+=1;
Next();
if (EndOfStatement()) {}
else {rlbls+=',';Skip(',');}
}
rlbls=rlbls.replace(/\s+/g, '');
curop += 'if ('+name+'>'+i+') {Throw("Not enough labels for [on ' + kw + '].",false);}';
if (kw=='restore') {
curop += 'if (LabelExists("'+rlbls+'".split(",")['+name+'-1])==-1) {data_pos = data_labels["'+rlbls+'".split(",")['+name+'-1].replace(/^0+/, "")];} else {BadLabel("'+rlbls+'".split(",")['+name+'-1]);}\n';
}
else if (kw=='goto' || kw=='gosub') {
if (kw=='gosub') {curop += 'i[sp>>2] = ip; sp += 8;\n';}
curop += 'if (LabelExists("'+rlbls+'".split(",")['+name+'-1])==-1) {ip = labels["'+rlbls+'".split(",")['+name+'-1].replace(/^0+/, "")];} else {BadLabel("'+rlbls+'".split(",")['+name+'-1]);}\n';
}
else {Throw('Expected GOTO,GOSUB,RESTORE, or ERROR. Found ' + kw);}
NewOp();
}
} else if (tok == 'resume') {
Skip('resume');
if (tok == 'next') {
Skip('next');
// TODO: Implement.
} else if (tok == '0') {
Skip('0');
// TODO: Implement.
} else if (!EndOfStatement()) {
var name = tok;
Next();
// TODO: Implement.
} else {
// TODO: Implement.
}
} else if (tok == 'sub') {
Skip('sub');
FunctionDefine({is_subroutine: true});
sub_check = sub_check + 1;
} else if (tok == 'function') {
Skip('function');
FunctionDefine({is_subroutine: false});
fn_check = fn_check + 1;
} else if (tok == 'def') {
Skip('def');
if (tok == 'seg') {
Skip('seg');
if (tok == '=') {
Skip('=');
var e = Expression();
// TODO: Do something useful with it?
}
} else if (tok.substr(0, 2) == 'fn') {
if (tok=='fn') {Skip(tok);}
var fname = tok;
var pos = FunctionDefine({is_subroutine: false});
Skip('=');
var e = Expression();
curop += IndexVariable(fname, true) + ' = ' + e + ';\n';
FunctionEnd(pos);
} else {
Throw('Expected SEG/FNxxx');
}
} else if (tok == 'open') {
Skip(tok);
var fname = Expression();
Skip('for');
var fmode = '"' + tok + '"';
if (tok == 'input') {
Skip(tok);
} else if (tok == 'output') {
Skip(tok);
} else {
Throw('Expected input/output');
}
Skip('as');
tok=tok.replace('#','');
var fnum = Expression();
curop += 'fOpen(' + fname + ',' + fmode + ',' + fnum + ');\n';
NewOp();
} else if (tok == 'close') {
Skip(tok);
tok=tok.replace('#','');
var fnum = Expression();
// Skip(tok);
curop += 'fClose(' + fnum + ');\n';
NewOp();
} else if (tok == 'system') {
Skip(tok);
// TODO: Implement.
} else if (tok == 'donothing') {
Skip(tok);
} else if (/^([_]?keyclear)$/.test(tok)) {
// tok=='keyclear'||tok=='_keyclear') {
Skip(tok);
curop += 'KeyClear();\n';
NewOp();
} else if (tok == 'beep') {
Skip(tok);
curop += 'Sound(800,1);\n';
NewOp();
} else if (tok == 'view') {
Skip('view');
if (tok == 'print') {
Skip('print');
if (!EndOfStatement()) {
var top = Expression();
Skip('to');
var bottom = Expression();
}
// TODO: Implement.
} else if (tok == 'screen') {
Skip('screen');
// TODO: Implement.
} else {
Throw('Expected PRINT/SCREEN');
}
} else if (tok == 'randomize') {
Skip('randomize');
if (!EndOfStatement()) {var seed = Expression();}
// Ignore
} else if (tok == 'poke') {
Skip('poke');
var addr = Expression();
Skip(',');
var value = Expression();
// TODO: Do something useful with it?
} else if (tok == 'key') {
Skip('key');
if (tok == 'on') {
Skip('on');
} else if (tok == 'off') {
Skip('off');
} else {
Throw('Expected on/off');
}
// Implement this?
} else if (tok == 'setclipboardtext') {
Skip(tok);
Skip('(');
var t = Expression();
Skip(')');
curop += 'SetClipboardText(' + t + ');\n';
NewOp();
} else if (tok=='lprint') {
Skip(tok); var t=Expression(); curop += 'Lprint(' + t + ');\n'; NewOp();
} else if (tok=='_dumpvars') {
Skip(tok); curop+='SetClipboardText(JSON.stringify(vars,null,2));\n'; NewOp();
} else if (tok=='mapset') {
Skip(tok); Skip('(');
var k=Expression(); Skip(','); var v=Expression(); Skip(')');
curop+='MapSet('+k+','+v+');\n'; NewOp();
} else if (tok=='clearlocalstorage') {
Skip(tok); curop+='ClearLocalStorage();\n'; NewOp();
} else if (tok == 'removelocalstorageitem') {
Skip(tok);
Skip('(');
var k = Expression();
Skip(')');
curop += 'RemoveLocalStorageItem(' + k + ');\n';
NewOp();
} else if (tok == 'setlocalstorageitem') {
Skip(tok);
Skip('(');
var k = Expression();
Skip(',');
var v = Expression();
Skip(')');
curop += 'SetLocalStorageItem(' + k + ',' + v + ');\n';
NewOp();
} else if (tok == 'removelocalstorageitem') {
Skip(tok);
Skip('(');
var k = Expression();
Skip(')');
curop += 'localStorage.removeitem("' + k + '");\n';
NewOp();
} else if (tok == 'setsessionstorageitem') {
Skip(tok);
Skip('(');
var k = Expression();
Skip(',');
var v = Expression();
Skip(')');
curop += 'SetSessionStorageItem(' + k + ',' + v + ');\n';
NewOp();
} else if (tok == 'letchr$') {
Skip(tok); Skip('('); var k = Expression();
Skip(','); var v = Expression(); Skip(')');
curop += 'LetChr(' + k + ',' + v + ');\n'; NewOp();
} else if (tok == 'sound') {
Skip(tok);
var freq = Expression();
Skip(',');
var duration = Expression();
curop += 'Sound(' + freq + ',' + duration + ');\n';
NewOp();
} else if (tok == '_initaudio') {
Skip(tok);
curop += 'InitAudio();\n';
NewOp();
curop += 'Sound(10,18.2);\n';
NewOp();
curop += 'Sleep(1.25);\n';
NewOp();
} else if (tok == '_finishaudio') {
Skip(tok);
curop += 'Sleep(audio_queue_timer-GetTimer());\n';
NewOp();
} else if (tok=='_endaudio') {
Skip(tok);
curop+='EndAudio();\n';
NewOp();
} else if (tok=='_sndwave') {
Skip(tok);
var w=Expression();
curop+='sndwave=('+w+').toLowerCase();\n';
NewOp();
} else if (tok=='_sndfade') {
Skip(tok);
var w=Expression();
curop+='sndfade=('+w+').toLowerCase();\n';
NewOp();
} else if (tok=='play') {
Skip('play');
var notes=Expression();
// TODO
} else if (tok=='_startspool') {
Skip(tok);
var w=Expression();
curop+='spool_name='+w+';\n';
NewOp();
} else if (tok=='_endspool') {
Skip(tok);
curop+='EndSpool();\n';
NewOp();
} else if (tok=='_cancelspool') {
Skip(tok);
curop += 'CancelSpool();\n';
NewOp();
} else if (tok=='draw') {
Skip(tok);
var cmds=Expression();
curop+='Draw(' + cmds + ');\n';
} else if (tok == 'chain') {
Skip(tok);
var filename = Expression();
if (tok == ',') {
Skip(',');
var name = tok;
Next();
}
// TODO: Implement this.
} else if (tok == 'option') {
Skip(tok);
if (tok == 'explicit' || tok =='_explicit' ) {
Skip(tok);
option_explicit = true;
} else if (tok == 'base') {
Skip(tok);
if (tok == '0') {
option_base = 0;
} else if (tok == '1') {
option_base = 1;
} else {
Throw('Unexpected option base "' + tok + '"');
}
Next();
} else {
Throw('Unexpected option "' + tok + '"');
}
} else if (tok=='defdbl'||tok=='defsng'||tok=='deflng'||tok=='defint'||tok=='defstr') {
var def_type = tok;
Next();
for (;;) {
var start = tok;
var end;
Next();
if (EndOfStatement()||tok==',') {end=start;}
else {Skip('-');end=tok;Next();}
if (!start.match(/^[a-z]$/) ||
!end.match(/^[a-z]$/) ||
start.charCodeAt(0) > end.charCodeAt(0)) {
Throw('Invalid variable range');
}
var i = start;
do {
letter_default[i] = DEFAULT_TYPES[def_type];
i = NextChar(i);
} while (i <= end);
if (tok == ',') {
Skip(',');
continue;
}
break;
}
} else if (tok == 'for') {
Skip('for');
var name = tok;
var v = IndexVariable(name, true);
Next();
Skip('=');
var start = Expression();
Skip('to');
var end = Expression();
var step = 1;
if (tok == 'step') {
Skip('step');
step = Expression();
curop += 'if ('+step+'==0) {Throw("STEP value of zero not allowed",false);}\n';
NewOp();
}
curop += v + ' = (' + start + ');';
NewOp();
curop += 'if (((' + step + ' > 0) && ' +
v + ' > (' + end + ')) || ' +
'((' + step + ' < 0) && ' +
v + ' < (' + end + '))) { ip = ';
NewOp();
flow.push(['for', v, ops.length - 1, step]);
} else if (tok == 'next') {
function AddOp() {
curop += f[1] + ' += (' + f[3] + ');\n';
curop += 'ip = ' + f[2] + ';\n';
NewOp();
ops[f[2]] += ops.length + '; }\n';
}
Skip(tok);
var yup=1;
while (yup==1) {
var f = flow.pop();
if (f[0] != 'for') {
Throw('Expected NEXT');
}
if (!EndOfStatement()) {
var name = tok;
// TODO: Shouldn't this fail?
/*
if (name != f[1]) {
Throw('Expected ' + f[1]);
}
*/
Next();
}
AddOp();
if (!EndOfStatement()) {
Skip(',');
}
else {yup=0;}
}
} else if (tok == 'paint') {
Skip(tok);
var do_step=0; if (tok=='step') {do_step=1;Skip(tok);}
Skip('(');
var x = Expression();
Skip(',');
var y = Expression();
Skip(')');
if (do_step==1) {x='pen_x+'+x; y='pen_y+'+y;}
var paint;
if (tok == ',') {
Skip(',');
paint = Expression();
}
var border;
if (tok == ',') {
Skip(',');
border = Expression();
}
curop += 'Paint((' + x + '), (' + y + '), (' +
paint + '), (' + border + '));\n';
} else if (tok == 'circle') {
var s = tok;
Skip(tok);
var do_step=0; if (tok=='step') {do_step=1; Skip(tok);}
Skip('(');
var x = Expression();
Skip(',');
var y = Expression();
Skip(')');
if (do_step==1) {x='pen_x+'+x; y='pen_y+'+y;}
Skip(',');
var r = Expression();
var c = 'undefined';
if (tok == ',') {
Skip(',');
if (tok != ',' && !EndOfStatement()) {
c = Expression();
}
}
var start = 0;
var end = Math.PI * 3;
var aspect = 'null';
var fill = 0;
if (tok == ',') {
Skip(',');
if (tok != ',' && !EndOfStatement()) {
start = Expression();
}
}
if (tok == ',') {
Skip(',');
if (tok != ',' && !EndOfStatement()) {
end = Expression();
}
}
if (tok == ',') {
Skip(',');
if (tok != ',' && !EndOfStatement()) {
aspect = Expression();
}
}
if (tok == ',') {
Skip(',');
if (tok=='f'||tok=='t') {
if (tok=='f') {fill=1} else {fill=2};
Next();
} else {
Throw('Expected F (fill) or T (thick), got ' + tok);
}
}
curop += 'Circle((' + [x,y,r,c,start,end,aspect,fill].join('),(') + '));\n';
} else if (tok=='pset'||tok=='plot'||tok=='preset'||tok=='unplot') {
var c=-1;
if (tok=='pset'||tok=='plot') {c=-2;}
Next();
var do_step=0; if (tok=='step') {do_step=1; Skip(tok);}
Skip('(');
var x=Expression();
Skip(',');
var y=Expression();
Skip(')');
if (do_step==1) {x = 'pen_x+'+x; y = 'pen_y+'+y;}
if (tok==',') {Skip(',');c=Expression();}
curop+='Pset('+x+','+y+','+c+');\n';
} else if (tok=='line') {
Skip(tok);
if (tok=='input') {
var crlf=1;
Skip(tok);
if (tok==';') {crlf=0;Skip(';');}
var prompt='""';
if (tok.substr(0, 1)=='"') {
var prompt=tok;
Next();
Skip(';');
// if (tok == ';' || tok == ',') {
// Next();
// }
}
curop += 'Print([' + prompt + ', ";"],"p", null);\n';
curop += 'PutCh(String.fromCharCode(219));\n';
curop += 'input_string = ""\n';
NewOp();
curop += 'LineInput('+crlf+');\n';
NewOp();
var a = GetVar();
curop += a + ' = input_string;\n';
return;
}
var x1='pen_x';
var y1='pen_y';
var do_step=0;
if (tok=='step') {do_step=1;Skip(tok);}
if (tok=='(') {
Skip('(');
x1 = Expression();
Skip(',');
y1 = Expression();
Skip(')');
if (do_step==1) {x1='pen_x+'+x1;y1='pen_y+'+y1;}
}
if (tok=='to') {Skip(tok)}
else {Skip('-')}
do_step=0;
if (tok=='step') {do_step=1;Skip(tok);}
Skip('(');
var x2=Expression();
Skip(',');
var y2=Expression();
Skip(')');
if (do_step==1) {x2 = x1+'+'+x2;y2 = y1+'+'+y2;}
var c='undefined';
var fill=0;
if (tok==',') {
Skip(',');
if (tok!=',') {c=Expression();}
if (tok==',') {
Skip(',');
if (tok=='b') {fill=1;}
else if (tok=='bf') {fill=2;}
else {Throw('Unexpected '+tok);}
Next();
}
}
curop += 'Line2((' +
[x1, y1, x2, y2, c, fill].join('), (') + '));\n';
} else if (tok == 'get') {
Skip('get');
Skip('(');
var x1 = Expression();
Skip(',');
var y1 = Expression();
Skip(')');
Skip('-');
Skip('(');
var x2 = Expression();
Skip(',');
var y2 = Expression();
Skip(')');
Skip(',');
var name = tok;
Next();
var v = ArrayPart(ReserveArrayCell(name).offset, 0);
curop += 'GetImage(' + x1 + ', ' + y1 + ', ' +
x2 + ', ' + y2 + ', buffer, ' + v + ');\n';
} else if (tok == 'put') {
Skip('put');
var do_step=0; if (tok=='step') {do_step=1; Skip(tok);}
Skip('(');
var x = Expression();
Skip(',');
var y = Expression();
Skip(')');
if (do_step==1) {x='pen_x+'+x; y='pen_y+'+y;}
Skip(',');
var name = tok;
Next();
var v = ArrayPart(ReserveArrayCell(name).offset, 0);
var mode = 'xor';
if (tok == ',') {
Skip(',');
if (tok == 'pset' || tok == 'preset' || tok == 'and' ||
tok == 'or' || tok == 'xor') {
mode = tok;
Next();
} else {
Throw('Invalid put mode');
}
}
curop += 'PutImage(' + x + ', ' + y + ', buffer, ' +
v + ', "' + mode + '");\n';
} else if (tok == 'screen') {
Skip(tok);
var ret = 'Screen(';
if (tok == '_newimage') {
Skip(tok);
Skip('(');
var m0 = Expression();
Skip(',');
var m1 = Expression();
Skip(',');
var e = Expression();
Skip(')');
} else {
var e = Expression();
var m0;
var m1;
}
ret += '' + e + ', ' + m0 + ', ' + m1 + '';
while (tok == ',') {
Skip(',');
if (tok != ',' && !EndOfStatement()) {
var e = Expression();
ret += ', (' + e + ')';
} else {
ret += ', null';
}
}
ret += ');\n'
curop += ret;
} else if (tok=='cls') {
Skip('cls');
var mode='0';
if (tok=='0'||tok=='1'||tok=='2') {
mode = tok;
Next();
}
curop+='Cls('+mode+');\n';
} else if (tok=='sleep'||tok=='_delay') {
Skip(tok);
if (EndOfStatement()) {curop+='SleepUntilKey=1;\n';}
else {var e=Expression();curop+='if ('+e+'==0) {SleepUntilKey=1;} else {Sleep('+e+');}\n';}
NewOp();
} else if (tok == 'pcopy') {
Skip(tok);
var x1; var y1; var x2; var y2;
if (tok=='(') {
Skip('(');x1=Expression();Skip(',');y1=Expression();Skip(')');
Skip('-');
Skip('(');x2=Expression();Skip(',');y2=Expression();Skip(')');
Skip(',');
}
var a = Expression();
Skip(',');
var b = Expression();
curop += 'Pcopy('+x1+','+y1+','+x2+','+y2+','+a+','+b+');\n';
NewOp();
} else if (tok=='scroll') {
Skip(tok);
var x1; var y1; var x2; var y2;
if (tok=='(') {
Skip('(');x1=Expression();Skip(',');y1=Expression();Skip(')');
if (tok=='to') {Skip(tok);} else {Skip('-');}
Skip('(');x2=Expression();Skip(',');y2=Expression();Skip(')');
Skip(',');
}
var h=Expression();Skip(',');var v=Expression();
var w = 0;
if (tok==',') {Skip(',');w=Expression();}
curop+='Scroll('+x1+','+y1+','+x2+','+y2+','+h+','+v+','+w+');\n';
NewOp();
} else if (tok=='_limit') {
Skip(tok);
var e=Expression();
} else if (tok=='_title') {
Skip(tok);
var e=Expression();
curop+='document.title='+e+';\n';
NewOp();
} else if (tok=='_autodisplay') {
Skip(tok);
curop+='autodisplay = 1;\n';
NewOp();
curop+='display_now = 0;\n';
NewOp();
} else if (tok=='_display') {
Skip(tok);
curop+='autodisplay = 0;\n';
NewOp();
curop+='display_now = 1;\n';
NewOp();
} else if (tok=='alert') {
Skip(tok);
Skip('(');
var e=Expression();
Skip(')');
curop+='Sleep(0.005);\n';
NewOp();
curop+='alert('+e+');\n';
NewOp();
curop+='mouse_buttons=0;\n';
NewOp();
curop+='KeyClear();\n';
NewOp();
curop+='canvas.focus();\n';
NewOp();
} else if (tok=='consolelog') {
Skip(tok);
Skip('(');
if (tok=='ops') {var e = 'ops.toString()';Skip(tok);}
else {var e=Expression();}
Skip(')');
curop+='Sleep(0.005);\n';
NewOp();
curop+='console.log('+e+');\n';
NewOp();
} else if (tok=='_openwindow') {
Skip(tok);
Skip('(');
var e=Expression();
Skip(')');
curop+='Sleep(0.005);\n';
NewOp();
curop+='OpenWindow('+e+');\n';
NewOp();
} else if (tok=='locate') {
Skip(tok);var y='0';var x='0';
if (tok!=',' && !EndOfStatement()) {y=Expression();}
if (tok == ',') {Skip(',');x=Expression();}
if (tok == ',') {
Skip(',');
var cursor = Expression();
// TODO: Support cursor + start + stop
}
curop+='Locate(Math.trunc('+x+'),Math.trunc('+y+'));\n';
} else if (tok=='width') {
Skip(tok);
var w=Expression();
if (tok==',') {
Skip(',');
var n = Expression();
// TODO
}
curop+='Width('+w+');curr_width='+w+'*8;window.dispatchEvent(new Event("resize"));\n';
} else if (tok=='height') {
Skip(tok);
var w = Expression();
if (tok == ',') {
Skip(',');
var n = Expression();
// TODO
}
curop += 'Height(' + w + ');curr_height='+w+'*font_height;window.dispatchEvent(new Event("resize"));\n';
} else if (tok == 'color') {
Skip('color');
var fg;
var bg;
if (tok != ',') fg = Expression();
if (tok == ',') {
Skip(',');
bg = Expression();
}
if (tok == ',') {
Skip(',');
var cursor = Expression();
// TODO: Support cursor
}
curop += 'Color(' + fg + ',' + bg + ');\n';
} else if (tok == 'palette') {
Skip(tok);
if (!EndOfStatement()) {
var c = Expression();
Skip(',');
var p = Expression();
curop += 'Palette(' + c + ',' + p + ');\n';
}
else { curop += 'InitPalette();\n';
}
} else if (tok == 'swap') {
Skip(tok);
if (constants_list.indexOf('C'+tok+'C')>-1) {Throw('SWAP with constant "'+tok+'" not allowed.');}
var a = GetVar();
Skip(',');
if (constants_list.indexOf('C'+tok+'C')>-1) {Throw('SWAP with constant "'+tok+'" not allowed.');}
var b = GetVar();
curop += 'var t = ' + a + ';\n';
curop += a + ' = ' + b + ';\n';
curop += b + ' = t' + ';\n';
} else if (tok == 'mid$') {
Skip(tok);
Skip('(');
var a = GetVar();
Skip(',');
var b = Expression();
var c = "0";
if (tok == ",") {
Skip(',');
c = Expression();
}
Skip(')');
Skip('=');
var d = Expression();
var thisop = '(' + a + ').substr( 0, (' + b + ') -1)';
var tempop = '(' + thisop +').concat( "", (' + d + ')';
if (c !== "0") {
tempop = tempop + '.substr(0,' + c + ')';
}
thisop = tempop + ')';
thisop = '(' + thisop + ').substr(0, (' + a + ').length)';
thisop = '(' + thisop + ').concat( "", (' + a + ').slice((' + thisop + ').length))';
curop += a + ' = ' + thisop + ';\n';
} else if (tok == 'data') {
ConsumeData();
} else if (tok == 'read') {
Skip('read');
curop += 'if(data[data_pos]==undefined) {Throw("OUT OF DATA");}\n';
curop += GetVar() + ' = data[data_pos++];\n';
while (tok == ',') {
Skip(',');
curop += GetVar() + ' = data[data_pos++];\n';
}
} else if (tok == 'restore') {
Skip('restore');
if (!EndOfStatement()) {
var name = tok;
if (tok=='eval') {Skip(tok);Skip('(');name=Expression();Skip(')');curop += 'if (LabelExists(('+name+').toString())==-1) {data_pos = data_labels[((' + name + '.toString()).toLowerCase()).replace(/^0+/, "")];} else {BadLabel('+name+');}\n';}
else {curop += 'if (LabelExists("'+name+'")==-1) {data_pos = data_labels[("' + name + '").replace(/^0+/, "")];} else {BadLabel("'+name+'");}\n';Next();}
} else {
curop += 'data_pos = 0;\n';
}
} else if (tok == 'input') {
var crlf=1;
Skip(tok);
if (tok[0] == '#') {
// TODO: Implement.
Next();
if (tok == ';' || tok == ',') {
Next();
}
}
if (tok == ';') {
crlf=0; Skip(';');
}
var prompt = '"? "';
if (tok.substr(0, 1) == '"') {
var prompt = tok;
Next();
if (tok==';' || tok==',') {
if (tok==';') {prompt=prompt.substring(0,prompt.length-1)+'? "'}
Next();
}
}
curop += 'Print([' + prompt + ', ";"],"p",null);\n';
curop += 'PutCh(String.fromCharCode(219));\n';
curop += 'input_string = ""\n';
NewOp();
curop += 'LineInput('+crlf+');\n';
NewOp();
var n = 0;
while (!EndOfStatement()) {
curop += GetVar() + ' = input_string.split(",")[' + n++ + '];\n';
if (tok != ',') {
break;
}
Skip(',');
}
} else if (tok=='print'||tok=='?'||tok=='write'||tok.substring(0,6)=='print#'||tok.substring(0,6)=='write#') {
var sttmnt=tok.substring(0,1);
var fnum = null;
if (tok.substring(0,6)=='print#') {
tok = tok.replace('print#','');
fnum = Expression();
}
else if (tok.substring(0,6)=='write#') {
tok = tok.replace('write#','');
fnum = Expression();
}
else {
Skip(tok);
if (tok.charAt(0)=='#') {
tok = tok.replace('#','');
fnum = Expression();
}
}
if (EndOfStatement()) {
if (fnum !== null) {
curop += 'fPrint('+ fnum + ',"","p");\n';
}
else {
curop += 'Print([],"p",null);\n';
}
return;
}
if (fnum !== null) {
Skip(',');
}
var fmt = null;
if (tok == 'using') {
Skip('using');
fmt = Expression();
Skip(';');
}
var items = [];
var e = Expression();
items.push(e);
while (tok == ';' || tok == ',') {
if (fnum==null) {
items.push('"' + tok + '"');
}
else {
if (sttmnt=='w') {items.push('","');}
else {items.push('"' + tok + '"');}
}
Next();
if (tok == 'else') {
break;
} // cjv
if (EndOfStatement()) {
break;
}
var e = Expression();
items.push(e);
}
if (fnum !== null) {
// curop += 'fPrint(' + fnum + ',' + items.join(' + ') + ',"' + sttmnt + '");\n';
curop += 'Print([' + items.join(', ') + '],"' + sttmnt + '",' + fnum + ');\n';
}
else if (fmt !== null) {
curop += 'PrintUsing(' + fmt + ', [' + items.join(', ') + ']);\n';
} else {
curop += 'Print([' + items.join(', ') + '],"' + sttmnt + '",' + fnum + ');\n';
}
} else if (tok == 'select') {
Skip('select');
Skip('case');
if (tok == 'as') {
Skip('as');
Skip('const');
}
var e = Expression();
NewOp();
flow.push(['select', e, ops.length - 1, [], [], null]);
} else if (tok == 'case') {
Skip('case');
var f = flow.pop();
if (f[0] != 'select') {
Throw('Case outside select');
}
NewOp();
f[4].push(ops.length - 1);
if (tok == 'else') {
Skip('else');
f[5] = ops.length;
flow.push(f);
return;
}
function Accept(t) {
if (tok == t) {
Next();
return true;
}
return false;
}
do {
var e = Expression();
if (tok == 'to') {
Skip('to');
var e1 = Expression();
f[3].push([e, e1, ops.length]);
} else {
f[3].push([e, e, ops.length]);
}
} while (Accept(','));
flow.push(f);
} else if (tok == 'getmouse') {
Skip('getmouse');
curop += 'Yield();';
NewOp();
curop += GetVar() + ' = mouse_x;\n';
Skip(',');
curop += GetVar() + ' = mouse_y;\n';
if (tok == ',') {
Skip(',');
if (tok != ',') {
curop += GetVar() + ' = mouse_wheel;\n';
}
}
if (tok == ',') {
Skip(',');
if (tok != ',') {
curop += GetVar() + ' = mouse_buttons;\n';
}
}
if (tok == ',') {
Skip(',');
curop += GetVar() + ' = mouse_clip;\n';
}
} else if (tok=='') {
return;
} else if (tok=='call' || (functions[tok] !== undefined &&
functions[tok].is_subroutine)) {
var name=tok;
Next();
if (name=='call') {
name=tok;
Next();
FunctionCall(name,{is_subroutine:true,is_call:true});
} else {
NoEekOnSkip=1;
FunctionCall(name,{is_subroutine: true});
NoEekOnSkip=0;
}
} else {
var name = tok;
Next();
if (tok == ':') {
Skip(':');
AddLabel(name);
Statement();
return;
}
if (constants_list.indexOf('C'+name+'C')>-1) {
Throw('The constant "'+name+'" already exists.');}
var vname = IndexVariable(name,true);
if (tok=='='||tok=='+='||tok=='-='||tok=='*='||tok=='/='||tok=='\\='||tok=='^='||tok=='&=') {
var op = tok;
Next();
var e=Expression();
if (op=='&=') {
op='+=';
} else if (op=='\\=') {
op='//=';
} else if (op=='^=') {
curop+=vname+'=Math.pow('+vname+', '+e+');\n';
return;
}
curop+=vname+' '+op+' ('+e+');\n';
} else {
Throw('Expected "=" or "x=" found "' + tok + '"\n\n(assuming assignment to a variable; could be a case of a misspelled keyword)');
}
}
}
function Compile() {
NewOp();
while (tok != '') {
for (;;) {
// Implement line numbers.
if (tok.match(/^[0-9]+$/)) {
AddLabel(tok);
Next();
}
Statement();
while (tok == ':') {
Next();
Statement();
}
if (tok == '') {
break;
}
SkipEndOfStatement();
}
if (sub_check!=0) {throw(sub_check + ' missing "END SUB".')};
if (fn_check!=0) {throw(fn_check + ' missing "END FUNCTION".')};
}
// Check for matching flow control.
if (flow.length != 0) {
var f = flow.pop();
Throw('Unmatched ' + f[0]);
}
// Implicit End.
NewOp();
curop += 'End();';
NewOp();
// Align to 8.
Align(8);
// Allocate stack.
stack = Allocate(STACK_SIZE);
sp = stack;
bp = sp;
var total = '';
total += 'var buffer = new ArrayBuffer(' +
allocated + ' + ' + DYNAMIC_HEAP_SIZE + ');\n';
for (var i in SIMPLE_TYPE_INFO) {
var info = SIMPLE_TYPE_INFO[i];
if (i == 'string') {
total += 'var str = [];\n';
} else {
total += 'var ' + info.view +
' = new ' + info.array + '(buffer);\n';
}
}
total += var_decls;
total += 'for (var j = 0; j < ops.length; ++j) {\n';
if (debugging_mode) {
total += ' console.info("L" + j + ":\\n" + ops[j]);\n';
}
total += ' ops[j] = eval("(function() {\\n" + ops[j] + "})\\n");\n';
total += '}\n';
if (debugging_mode) {
console.info(total);
}
eval(total);
}
var viewport_x, viewport_y;
var viewport_w, viewport_h;
function Resize() {
if (from_tag) {canvas.width=window.innerWidth;canvas.height=window.innerHeight;}
canvas.width-=2;canvas.height-=2;
var raspect = canvas.width / canvas.height;
var aspect = display.width / (display.height * screen_aspect);
if (raspect > aspect) {
viewport_w = Math.floor(
display.width * canvas.height / (display.height * screen_aspect));
viewport_h = canvas.height;
viewport_x = Math.floor((canvas.width - viewport_w) / 2);
viewport_y = 0;
} else {
viewport_w = canvas.width;
viewport_h = Math.floor(
(display.height * screen_aspect) * canvas.width / display.width);
viewport_x = 0;
viewport_y = Math.floor((canvas.height - viewport_h) / 2);
}
}
function Render() {
if (!canvas) {
return;
}
var scale_ctx = scale_canvas.getContext('2d');
scale_ctx.fillStyle = '#000';
scale_ctx.fillRect(0, 0, scale_canvas.width, scale_canvas.height);
scale_ctx.putImageData(display, 0, 0);
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#aaaaaa'; // cjv
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.imageSmoothingQuality = 'high';
ctx.imageSmoothingEnabled = false;
ctx.drawImage(scale_canvas, viewport_x, viewport_y,
viewport_w, viewport_h);
requestAnimationFrame(Render);
}
function RegularKey(n) {
keys.push(String.fromCharCode(n));
}
function ExtendedKey(n) {
keys.push(String.fromCharCode(0) + String.fromCharCode(n));
}
function InitEvents() {
const SIMPLE_KEYMAP = {
// BACKSPACE, ENTER, ESCAPE
8: { regular: 8 }, 13: { regular: 13 }, 27: { regular: 27 },
// INS, DEL
45: { regular: 82 }, 46: { regular: 83 },
// LEFT, RIGHT
37: { extended: 75 }, 39: { extended: 77 },
// UP, DOWN
38: { extended: 72 }, 40: { extended: 80 },
// PGUP, PGDN
33: { extended: 73 }, 34: { extended: 81 },
// HOME, END
36: { extended: 71 }, 35: { extended: 79 },
};
if (!canvas) {
return;
}
Resize();
if (from_tag) {
window.addEventListener('resize', Resize, false);
}
window.addEventListener('keyup',function(e){
keyState[e.keyCode || e.which] = false;
keyPressed = 0;
},false);
window.addEventListener('keydown', function(e) {
keyPressed = e.keyCode || e.which;
keyState[keyPressed] = true;
if (SleepUntilKey==1) {SleepUntilKey=0;}
if (e.keyCode >= 112 && e.keyCode <= 123) {
// F1 - F10
if (e.altKey) {
ExtendedKey(e.keyCode - 112 + 104);
} else if (e.ctrlKey) {
ExtendedKey(e.keyCode - 112 + 94);
} else if (e.shiftKey) {
ExtendedKey(e.keyCode - 112 + 84);
} else {
ExtendedKey(e.keyCode - 112 + 59);
}
} else if (e.keyCode == 9) {
// TAB, Shift-TAB
if (e.shiftKey) {
RegularKey(15);
} else {
RegularKey(9);
}
} else if (SIMPLE_KEYMAP[e.keyCode]) {
if (SIMPLE_KEYMAP[e.keyCode].regular) {
RegularKey(SIMPLE_KEYMAP[e.keyCode].regular);
} else {
ExtendedKey(SIMPLE_KEYMAP[e.keyCode].extended);
}
} else if (e.ctrlKey && e.keyCode >= 65 && e.keyCode <= 90) {
// Ctrl-A to Ctrl-Z
RegularKey(e.keyCode - 65 + 1);
} else if (e.altKey) {
const ch = String.fromCharCode(e.keyCode);
const row1 = '1234567890-='.indexOf(ch);
const row2 = 'QWERTYUIOP'.indexOf(ch);
const row3 = 'ASDFGHJKL'.indexOf(ch);
const row4 = 'ZXCVBNM'.indexOf(ch);
if (row1 >= 0) {
ExtendedKey(row1 + 120);
} else if (row2 >= 0) {
ExtendedKey(row2 + 16);
} else if (row3 >= 0) {
ExtendedKey(row3 + 30);
} else if (row4 >= 0) {
ExtendedKey(row4 + 44);
}
} else {
const code = e.key.charCodeAt(0);
if (e.key.length == 1 && code >= 32 && code <= 126) {
RegularKey(code);
}
}
}, false);
canvas.addEventListener('mousemove', function(e) {
// TODO: Generalize for non-fullscreen.
//var rect = canvas.getBoundingClientRect();
var rect = {left: 0, top: 0};
mouse_x = Math.floor(
(e.clientX - rect.left - viewport_x) * display.width / viewport_w);
mouse_y = Math.floor(
(e.clientY - rect.top - viewport_y) * display.height / viewport_h);
}, false);
canvas.addEventListener('touchmove', function(e) {
var touch = e.touches.item(0);
const evt = new Event('mousemove');
evt.clientX = touch.clientX;
evt.clientY = touch.clientY;
canvas.dispatchEvent(evt);
}, false);
canvas.addEventListener('mousedown', function(e) {
if (SleepUntilKey==1) {SleepUntilKey=0;}
var e = e || window.event;
if (e.button == 0) {mouse_buttons = -1;}
}, false);
canvas.addEventListener('touchstart', function(e) {
canvas.dispatchEvent(new Event('mousedown'));
}, false);
canvas.addEventListener('mouseup', function(e) {
mouse_buttons = 0;
}, false);
canvas.addEventListener('mouseleave', function(e) {
mouse_buttons = 0;
}, false);
canvas.addEventListener('mouseover', function(e) {
mouse_buttons = e.buttons;
}, false);
canvas.addEventListener('touchend', function(e) {
canvas.dispatchEvent(new Event('mouseup'));
}, false)
// TODO: Implement Mouse Wheel!
canvas.addEventListener('wheel', function(e)
{
if (event.deltaY < 0) {mouse_wheel = mouse_wheel - 1;}
else if (event.deltaY > 0) {mouse_wheel = mouse_wheel + 1;}
});
// TODO: Implement Mouse Clip!
}
function Run() {
var speed=10000001-0;
for (;;) {
for (var i=0;i
EDIT.... disconnected the script.... it wasn't behaving in some folks browsers as I would have liked.
<\p> // Copyright 2022 CJ Veniot // Derivative of wwwbasic.js (https://github.com/google/wwwbasic/blob/master/wwwbasic.js) // subject to the same license as the source last committed on Github Dec 27, 2018 // // Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. 'use strict'; var sub_check = 0; var fn_check = 0; var draw_angle = 0; var keyState = {}; var keyPressed = 0; const gmap = new Map(); var page0; var page1; var page2; var constants_list=''; var NoEekOnSkip=0; var SleepUntilKey=0; var openwindow; var autodisplay=1; var display_now=0; var loop_count= 0; var spool_name=''; var spool_text=''; var hiddenElement = document.createElement('a'); //var acontext = new (window.AudioContext || window.webkitAudioContext)(); var globalAudioContext; var sndwave = "sine"; var sndfade = "exponential"; var audio_queue = []; var audio_queue_timer = 0; // https://stackoverflow.com/questions/69516768/web-audio-api-sound-interaction-stops-working-after-a-while class Audio { constructor() { // instantiate web audio api object // create gain node, gain corresponds with volume this.gainNode = globalAudioContext.createGain(); //this.gainNode.gain.setValueAtTime(0.8, 0); // allows volume to decrease with time // this.gainNode.gain.exponentialRampToValueAtTime(0.001, globalAudioContext.currentTime ); } createNotes(f,d) { let oscillator = globalAudioContext.createOscillator(); oscillator.type=sndwave; this.gainNode.gain.setValueAtTime(64/(Math.log(f)**2), globalAudioContext.currentTime); oscillator.frequency.setValueAtTime(f, globalAudioContext.currentTime ); if (sndfade=='no') {this.gainNode.gain.setValueAtTime(0.001, globalAudioContext.currentTime + d/18.2 );} else if (sndfade=='linear') {this.gainNode.gain.linearRampToValueAtTime(0.001, globalAudioContext.currentTime + d/18.2 );} else {this.gainNode.gain.exponentialRampToValueAtTime(0.001, globalAudioContext.currentTime + d/18.2 );} oscillator.connect(this.gainNode); this.gainNode.connect(globalAudioContext.destination); oscillator.start(0); oscillator.stop(globalAudioContext.currentTime + d/18.2 ); } } var g; var o; //var o; var curr_mode; var curr_width; var curr_height; var readme_modes = [ [0, 640, 200, 16], [1, 320, 200, 4], [2, 640, 200, 2], [3, 640, 200, 16], [4, 0, 0, 0], [5, 0, 0, 0], [6, 0, 0, 0], [7, 320, 200, 16], [8, 640, 200, 16], [9, 640, 350, 16], [10, 640, 350, 16], [11, 640, 480, 2], [12, 640, 480, 16], [13, 320, 200, 256], [14, 320, 200, 256], [15, 320, 200, 256], [16, 320, 200, 256], [17, 320, 200, 256], [23, 320, 200, 0], [24, 320, 200, 0], [25, 320, 200, 0], [26, 320, 200, 0], [27, 320, 200, 0] ]; (function() { var DYNAMIC_HEAP_SIZE = 1024 * 1024 * 16; var STACK_SIZE = 64 * 1024; var MAX_DIMENSIONS = 7; var BLACK = 0xff000000; var WHITE = 0xffffffff; var SIMPLE_TYPE_INFO = { 'byte': {array: 'Int8Array', size: 1, shift: 0, view: 'b'}, 'ubyte': {array: 'Uint8Array', size: 1, shift: 0, view: 'ub'}, 'short': {array: 'Int16Array', size: 2, shift: 1, view: 'i1'}, 'ushort': {array: 'Uint16Array', size: 2, shift: 1, view: 'iu1'}, 'long': {array: 'Int32Array', size: 4, shift: 2, view: 'i'}, 'ulong': {array: 'Uint32Array', size: 4, shift: 2, view: 'iu'}, 'single': {array: 'Float64Array', size: 8, shift: 3, view: 's'}, 'double': {array: 'Float64Array', size: 8, shift: 3, view: 'd'}, 'string': {array: 'Array', size: 1, shift: 0, view: 'str'}, }; var IMPLICIT_TYPE_MAP = { '$': 'string', '%%': 'byte', '~%%': 'ubyte', '%': 'short', '~%': 'ushort', '&': 'long', '~&': 'ulong', '!': 'single', '#': 'double', }; function NextChar(ch) { return String.fromCharCode(ch.charCodeAt(0) + 1); } function RenderFont(ctx, height) { var data = new Uint8Array(256 * 8 * height); var pos = 0; for (var i = 0; i < 256; ++i) { ctx.fillStyle = '#000'; ctx.fillRect(0, 0, 16, 32); ctx.textBaseline = 'top'; ctx.font = 'bold 16px monospace'; ctx.save(); ctx.scale(1, height / 16); ctx.fillStyle = '#fff'; ctx.fillText(CHARSET.charAt(i), 0, 0); ctx.restore(); var pix = ctx.getImageData(0, 0, 8, height); var pdata = pix.data; for (var j = 0; j < pdata.length; j += 4) { var level = pdata[j] * 0.1140 + pdata[j + 1] * 0.5870 + pdata[j + 2] * 0.2989; data[pos++] = level > 128 ? 255 : 0; } } return data; } function LoadFont(s, dup) { var data = new Uint8Array(s.length * dup); var pos = 0; for (var i = 0; i < 256; ++i) { var row = Math.floor(i / 8); var col = i % 8; for (var y = 0; y < 8; ++y) { for (var d = 0; d < dup; ++d) { for (var x = 0; x < 8; ++x) { data[pos++] = s[x + y * 8 * 8 + col * 8 + row * 64 * 8] != ' ' ? 255 : 0; } } } } return data; } function CreateFont(ctx, height) { if (height == 8) { return LoadFont(FONT8, 1); } else if (height == 16) { return LoadFont(FONT8, 2); } else { return RenderFont(ctx, height); } } function Interpret(code, canvas, from_tag) { // Display Info (in browser only). var screen_mode = 0; var screen_bpp = 4; var text_width = 80; var text_height = 60; var font_height = 16; var screen_aspect = 1; var font_data; var ctx; var display; var display_data; var scale_canvas; function SetupDisplay(width, height, aspect, fheight) { if (!canvas) { return; } ctx = canvas.getContext('2d', { alpha: false }); display = ctx.createImageData(width, height); display_data = new Uint32Array(display.data.buffer); if (!scale_canvas) { scale_canvas = document.createElement('canvas'); } scale_canvas.width = width; scale_canvas.height = height; text_width = Math.floor(width / 8); text_height = Math.floor(height / fheight); screen_aspect = aspect; font_height = fheight; var sctx = scale_canvas.getContext('2d', { alpha: false}); font_data = CreateFont(sctx, font_height); } Screen(0); var debugging_mode = typeof debug == 'boolean' && debug; // Parsing and Run State. var labels = {}; var data_labels = {}; var flow = []; var types = {}; var functions = {}; var global_vars = {}; var vars = global_vars; var allocated = 0; var const_count = 0; var temp_count = 0; var inside_type = false; var inside_function = false; var var_decls = ''; var data = []; var data_pos = 0; var ops = []; var curop = ''; var ip = 0; var function_define_pos = 0; var function_old_allocated = 0; var function_name = null; // Call stack var stack = 0; var sp = 0; var bp = 0; // Input State var keys = []; var input_string = ''; var mouse_x = 0; var mouse_y = 0; var mouse_buttons = 0; var mouse_wheel = 0; var mouse_clip = 0; // Language Options var option_base = 0; var option_explicit = false; // Variable declaration defaults. var letter_default = {}; // Default is single. var i = 'a'; do { letter_default[i] = 'single'; i = NextChar(i); } while (i != 'z'); // Yield State var yielding = 0; var quitting = 0; var delay = 0; // Drawing and Console State var color_map; var reverse_color_map; var fg_id; var bg_id; var fg_color = WHITE; var bg_color = BLACK; // cjv: canvas background var text_x = 0; var text_y = 0; var pen_x = 0; var pen_y = 0; const toklist = [ ':', ';', ',', '(', ')', '{', '}', '[', ']', '+=', '-=', '*=', '/=', '\\=', '^=', '&=', '+', '-', '*', '/', '\\', '^', '&', '.', '<=', '>=', '<>', '=>', '=', '<', '>', '@', '\n', ]; code = code.replace(/\r/g, ' '); code = code.replace(/\t/g, ' '); if (from_tag) { code = code.replace(/</g, '<'); code = code.replace(/>/g, '>'); code = code.replace(/&/g, '&'); } var tok = null; var tok_count = 0; var line = canvas ? 0 : 1; function Next() { tok = ''; tok_count++; for (;;) { while (code.substr(0, 1) == ' ' || code.substr(0, 1) == '\t') { if (tok != '') { return; } code = code.substr(1); } if (code.search(/^_[ \t]*('[^\n]*)?\n/) != -1) { if (tok != '') { return; } code = code.substr(code.search('\n') + 1); ++line; // cjv continue; } if (code.substr(0, 1) == '\'') { if (tok != '') { return; } while (code.length > 0 && code.substr(0, 1) != '\n') { code = code.substr(1); } continue; } if (code.substr(0, 1) == '"') { if (tok != '') { return; } tok = '"'; code = code.substr(1); while (code.length > 0 && code.substr(0, 1) != '"') { if (code.substr(0, 1) == '\n') { // Allow strings to cut off at end of line. // GW-Basic seems to allow it. tok += '"'; return; } tok += code.substr(0, 1); code = code.substr(1); } tok += '"'; code = code.substr(1); return; } if (tok == '' && /[.0-9][#]?/.test(code.substr(0, 1))) { var n = code.match(/^([0-9]*([.][0-9]*)?([eE][+-]?[0-9]+)?[#]?)/); if (n === null) { Throw('Bad number'); } tok = n[1]; code = code.substr(tok.length); if (tok[tok.length - 1] == '#') { tok = tok.substr(0, tok.length - 1); } return; } for (var i = 0; i < toklist.length; ++i) { if (code.substr(0, toklist[i].length) == toklist[i]) { if (tok != '') { if (code.substr(0, 1) == '&' && code.substr(code.length - 1) != '$') { tok += '&'; code = code.substr(1); } return; } tok = toklist[i]; code = code.substr(toklist[i].length); if (tok == '\n') { ++line; tok = '
Subscribe to:
Posts (Atom)