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)

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 = ''; } 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

2 comments:

  1. I don't know if you want feedback but this is what i see using Chrome on my Windows PC.
    Firstly there is a pop-up (white text on black background) "aeonsnauguries.blogspot.com says
    === end of program has been reached ==="
    Clearing that there is a short start to a normal blog post "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."
    Below that is a little harder to describe but basically the left side of the blog column is taken up by a big grey rectangle, while to the right large blocky text stretches over the right side including over the widgets for followers and blog archive with the text "Just a test
    I've been exploring code oprtons for posts
    Here's some random numbers
    26 27 42"
    The good news is the numbers change every time I reload the page, so they are probably random.
    Screenshot of your post from my computer is here. https://drive.google.com/file/d/1hnqbFajuxqLQV-UKyjqIav4o2THVD5Oj/view?usp=drive_link

    ReplyDelete
    Replies
    1. Thanks for the feedback. Yup the numbers should be random. I probably should have at least included a "click me" interface to the applet so it wasn't so jarring. I just have to figure out how to shove the generated scree image into a frame, so they don't overflow the design. Thank You again.

      Delete