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 = '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
    
  
           
Subscribe to:
Comments (Atom)