| // Copyright 2019-2020 Linus Åkesson | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright | |
| // notice, this list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright | |
| // notice, this list of conditions and the following disclaimer in the | |
| // documentation and/or other materials provided with the distribution. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS | |
| // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
| // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | |
| // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| (function() {"use strict"; | |
| var HEAPFULL = 0x4001; | |
| var AUXFULL = 0x4002; | |
| var EXPECTOBJ = 0x4003; | |
| var EXPECTBOUND = 0x4004; | |
| var LTSFULL = 0x4006; | |
| var IOSTATE = 0x4007; | |
| if(!Uint8Array.prototype.slice) { | |
| Object.defineProperty(Uint8Array.prototype, "slice", { | |
| value: function(begin, end) { | |
| return new Uint8Array(Array.prototype.slice.call(this, begin, end)); | |
| } | |
| }); | |
| } | |
| if(!String.prototype.includes) { | |
| Object.defineProperty(String.prototype, "includes", { | |
| value: function(search, start) { | |
| if(typeof start !== 'number') start = 0; | |
| if(start + search.length > this.length) return false; | |
| return this.indexOf(search, start) !== -1; | |
| } | |
| }); | |
| } | |
| if(!Uint8Array.prototype.includes) { | |
| Object.defineProperty(Uint8Array.prototype, "includes", { | |
| value: function(elem, start) { | |
| var i; | |
| if(typeof start !== 'number') start = 0; | |
| for(i = start; i < this.length; i++) { | |
| if(this[i] === elem) return true; | |
| } | |
| return false; | |
| } | |
| }); | |
| } | |
| // parameter to vm_proceed_with_key: | |
| var keys = { | |
| KEY_BACKSPACE: 8, | |
| KEY_RETURN: 13, | |
| KEY_UP: 16, | |
| KEY_DOWN: 17, | |
| KEY_LEFT: 18, | |
| KEY_RIGHT: 19 | |
| }; | |
| // return code from vm_run: | |
| var status = { | |
| quit: 0, | |
| get_input: 1, | |
| get_key: 2, | |
| restore: 3, | |
| }; | |
| function getfour(array, offset) { | |
| var out = ""; | |
| var i; | |
| for(i = 0; i < 4; i++) { | |
| out += String.fromCharCode(array[offset + i]); | |
| } | |
| return out; | |
| } | |
| function get32(array, offset) { | |
| return (array[offset + 0] << 24) | (array[offset + 1] << 16) | (array[offset + 2] << 8) | array[offset + 3]; | |
| } | |
| function get16(array, offset) { | |
| return (array[offset + 0] << 8) | array[offset + 1]; | |
| } | |
| function putfour(array, offset, str) { | |
| var i; | |
| for(i = 0; i < 4; i++) { | |
| array[offset++] = str.charCodeAt(i); | |
| } | |
| } | |
| function put32(array, offset, value) { | |
| array[offset++] = (value >> 24) & 0xff; | |
| array[offset++] = (value >> 16) & 0xff; | |
| array[offset++] = (value >> 8) & 0xff; | |
| array[offset++] = (value >> 0) & 0xff; | |
| return offset; | |
| } | |
| function put16(array, offset, value) { | |
| array[offset++] = value >> 8; | |
| array[offset++] = value & 0xff; | |
| return offset; | |
| } | |
| function findchunk(filedata, name) { | |
| var size = get32(filedata, 4) + 8; | |
| var pos = 12, chname, chsize; | |
| while(pos < size) { | |
| chname = getfour(filedata, pos); | |
| chsize = get32(filedata, pos + 4); | |
| if(chname == name) { | |
| return filedata.slice(pos + 8, pos + 8 + chsize); | |
| } | |
| pos += 8 + ((chsize + 1) & ~1); | |
| } | |
| return null; | |
| } | |
| function tohex(v, len) { | |
| var str = v.toString(16); | |
| while(str.length < len) str = '0' + str; | |
| return str; | |
| } | |
| function decodechar(e, aach) { | |
| var entry, uchar; | |
| if(e.upper) { | |
| if(aach >= 0x61 && aach <= 0x7a) { | |
| aach ^= 0x20; | |
| } else if(aach >= 0x80) { | |
| aach = e.lang[e.extchars + 1 + (aach & 0x7f) * 5 + 1]; | |
| } | |
| e.upper = false; | |
| } | |
| if(aach < 0x80) { | |
| return String.fromCharCode(aach); | |
| } else { | |
| aach &= 0x7f; | |
| if(aach >= e.lang[e.extchars]) { | |
| e.upper = false; | |
| return "??"; | |
| } else { | |
| entry = e.extchars + 1 + aach * 5; | |
| uchar = (e.lang[entry + 2] << 16) | (e.lang[entry + 3] << 8) | e.lang[entry + 4]; | |
| return String.fromCharCode(uchar); | |
| } | |
| } | |
| } | |
| function decodestr(e, addr) { | |
| var decoder = get16(e.lang, 0); | |
| var state = 0, code, bits = 0, nbit = 0, str = ""; | |
| var i, len, charaddr, entry; | |
| while(true) { | |
| if(!nbit) { | |
| bits = e.writ[addr++]; | |
| nbit = 8; | |
| } | |
| code = e.lang[decoder + (state << 1) + ((bits & 0x80) ? 1 : 0)]; | |
| bits <<= 1; | |
| nbit--; | |
| if(code >= 0x81) { | |
| state = code & 0x7f; | |
| } else if(code == 0x80) { | |
| break; | |
| } else if(code == 0x5f) { | |
| code = 0; | |
| for(i = 0; i < e.esc_bits; i++) { | |
| if(!nbit) { | |
| bits = e.writ[addr++]; | |
| nbit = 8; | |
| } | |
| code <<= 1; | |
| if(bits & 0x80) code |= 1; | |
| bits <<= 1; | |
| nbit--; | |
| } | |
| if(e.head[1] < 4) { | |
| str += decodechar(e, 0x80 + code); | |
| } else if(code < e.esc_boundary) { | |
| str += decodechar(e, 0xa0 + code); | |
| } else { | |
| str += " "; | |
| entry = 2 + (code - e.esc_boundary) * 3; | |
| len = e.dict[entry]; | |
| charaddr = (e.dict[entry + 1] << 8) | e.dict[entry + 2]; | |
| for(i = 0; i < len; i++) { | |
| str += decodechar(e, e.dict[charaddr + i]); | |
| } | |
| } | |
| state = 0; | |
| } else { | |
| str += decodechar(e, code + 0x20); | |
| state = 0; | |
| } | |
| } | |
| return str; | |
| } | |
| function prepare_story(file_array, io, seed, quit, toparea, inlinearea) { | |
| var e, i, stopptr, stopend; | |
| function findch(name, mandatory) { | |
| var data = findchunk(file_array, name); | |
| if(!data && mandatory) { | |
| throw 'Missing ' + name + ' chunk.'; | |
| } | |
| return data; | |
| } | |
| function findfiles() { | |
| var size = get32(file_array, 4) + 8; | |
| var i; | |
| var pos = 12, chname, chsize, fname; | |
| var list = {}; | |
| while(pos < size) { | |
| chname = getfour(file_array, pos); | |
| chsize = get32(file_array, pos + 4); | |
| if(chname == 'FILE') { | |
| fname = ""; | |
| for(i = 0; file_array[pos + 8 + i]; i++) { | |
| fname += String.fromCharCode(file_array[pos + 8 + i]); | |
| } | |
| list[fname] = file_array.slice(pos + 8 + i + 1, pos + 8 + chsize); | |
| } | |
| pos += 8 + ((chsize + 1) & ~1); | |
| } | |
| return list; | |
| } | |
| if(getfour(file_array, 0) != "FORM" || getfour(file_array, 8) != "AAVM") { | |
| throw "Not an aastory file"; | |
| } | |
| e = { | |
| SP_AUTO: 0, | |
| SP_NOSPACE: 1, | |
| SP_PENDING: 2, | |
| SP_SPACE: 3, | |
| SP_LINE: 4, | |
| SP_PAR: 5, | |
| head: findch("HEAD", true), | |
| code: findch("CODE", true), | |
| dict: findch("DICT", true), | |
| init: findch("INIT", true), | |
| lang: findch("LANG", true), | |
| maps: findch("MAPS", true), | |
| tags: findch("TAGS", false), | |
| writ: findch("WRIT", true), | |
| look: findch("LOOK", true), | |
| meta: findch("META", false), | |
| urls: findch("URLS", false), | |
| files: findfiles(), | |
| randomseed: seed, | |
| strshift: 0, | |
| extchars: 0, | |
| esc_bits: 7, | |
| esc_boundary: 0, | |
| io: io, | |
| stopchars: [], | |
| nospcbefore: [], | |
| nospcafter: [], | |
| reg: new Uint16Array(64), | |
| inst: 0, | |
| cont: 0, | |
| top: 0, | |
| env: 0, | |
| cho: 0, | |
| sim: 0xffff, | |
| aux: 0, | |
| trl: 0, | |
| sta: 0, | |
| stc: 0, | |
| cwl: 0, | |
| spc: 0, | |
| nob: 0, | |
| ltb: 0, | |
| ltt: 0, | |
| upper: false, | |
| trace: false, | |
| divs: [], | |
| in_status: false, | |
| n_statusdiv: 0, | |
| n_span: 0, | |
| n_link: 0, | |
| undodata: [], | |
| pruned_undo: false, | |
| havequit: quit, | |
| havetop: toparea, | |
| haveinline: inlinearea, | |
| create_pair: function(head, tail, e) { | |
| var addr = this.top; | |
| this.top += 2; | |
| if(this.top > this.env || this.top > this.cho) { | |
| throw HEAPFULL; | |
| } | |
| this.heapdata[addr + 0] = head; | |
| this.heapdata[addr + 1] = tail; | |
| return addr | 0xc000; | |
| } | |
| }; | |
| if(e.head[0] != 0 || e.head[1] > 5) { | |
| throw "Unsupported aastory file format version (" + e.head[0] + "." + e.head[1] + ")"; | |
| } | |
| if(e.head[2] != 2) { | |
| throw "Unsupported word size (" + e.head[2] + ")"; | |
| } | |
| e.heapdata = new Uint16Array(get16(e.head, 16)); | |
| e.auxdata = new Uint16Array(get16(e.head, 18)); | |
| e.ramdata = new Uint16Array(get16(e.head, 20)); | |
| e.strshift = e.head[3]; | |
| e.extchars = get16(e.lang, 2); | |
| if(e.head[1] >= 4) { | |
| e.esc_boundary = e.lang[e.extchars] - 32; | |
| if(e.esc_boundary < 0) e.esc_boundary = 0; | |
| i = e.esc_boundary + get16(e.dict, 0) - 1; | |
| e.esc_bits = 0; | |
| while(i > 0) { | |
| i >>= 1; | |
| e.esc_bits++; | |
| } | |
| } | |
| stopptr = get16(e.lang, 6); | |
| stopend = stopptr; | |
| while(e.lang[stopend]) stopend++; | |
| e.stopchars = e.lang.slice(stopptr, stopend); | |
| if(e.head[1] >= 4) { | |
| stopptr = stopend + 1; | |
| stopend = stopptr; | |
| while(e.lang[stopend]) stopend++; | |
| e.nospcbefore = e.lang.slice(stopptr, stopend); | |
| stopptr = stopend + 1; | |
| stopend = stopptr; | |
| while(e.lang[stopend]) stopend++; | |
| e.nospcafter = e.lang.slice(stopptr, stopend); | |
| } | |
| vm_reinit(e); | |
| vm_reset(e, 0, true); | |
| e.initstate = vm_capture_state(e, 1); | |
| io.reset(); | |
| return e; | |
| } | |
| function get_styles(e) { | |
| var styles = []; | |
| var n = get16(e.look, 0); | |
| var i, offs, map, str, c, colon, key; | |
| for(i = 0; i < n; i++) { | |
| offs = get16(e.look, 2 + i * 2); | |
| map = {}; | |
| while(e.look[offs]) { | |
| str = ""; | |
| while((c = e.look[offs++])) { | |
| str += String.fromCharCode(c); | |
| } | |
| colon = str.indexOf(":"); | |
| if(colon > 0) { | |
| key = str.slice(0, colon++); | |
| while(str[colon] == ' ') colon++; | |
| map[key] = str.slice(colon); | |
| } | |
| } | |
| styles.push(map); | |
| } | |
| return styles; | |
| } | |
| function get_metadata(e) { | |
| var i, offs, key, ch, value; | |
| var result = {title: "Untitled story", release: get16(e.head, 4)}; | |
| var keynames = ["title", "author", "noun", "blurb", "date", "compiler"]; | |
| if(e.meta) { | |
| offs = 1; | |
| for(i = 0; i < e.meta[0]; i++) { | |
| key = e.meta[offs++]; | |
| value = ""; | |
| while((ch = e.meta[offs++])) { | |
| value += decodechar(e, ch); | |
| } | |
| if(key >= 1 && key <= keynames.length) { | |
| result[keynames[key - 1]] = value; | |
| } | |
| } | |
| } | |
| return result; | |
| } | |
| function vm_reinit(e) { | |
| var i; | |
| e.nob = get16(e.init, 0); | |
| e.ltb = get16(e.init, 2); | |
| e.ltt = get16(e.init, 4); | |
| for(i = 0; i < e.heapdata.length; i++) { | |
| e.heapdata[i] = 0x3f3f; | |
| } | |
| for(i = 0; i < e.auxdata.length; i++) { | |
| e.auxdata[i] = 0x3f3f; | |
| } | |
| for(i = (e.init.length - 6) >> 1; i < e.ramdata.length; i++) { | |
| e.ramdata[i] = 0x3f3f; | |
| } | |
| for(i = 6; i < e.init.length; i += 2) { | |
| e.ramdata[(i - 6) >> 1] = get16(e.init, i); | |
| } | |
| } | |
| function vm_reset(e, arg0, clear_undo) { | |
| var i; | |
| e.reg[0] = arg0; | |
| for(i = 1; i < 64; i++) { | |
| e.reg[i] = 0; | |
| } | |
| e.inst = 1; | |
| e.cont = 0; | |
| e.top = 0; | |
| e.env = e.heapdata.length; | |
| e.cho = e.heapdata.length; | |
| e.sim = 0xffff; | |
| e.aux = 0; | |
| e.trl = e.auxdata.length; | |
| e.sta = 0; | |
| e.stc = 0; | |
| e.cwl = 0; | |
| e.spc = e.SP_LINE; | |
| e.divs = []; | |
| e.upper = false; | |
| e.in_status = false; | |
| if(clear_undo) { | |
| e.undodata = []; | |
| e.pruned_undo = false; | |
| } | |
| e.randomstate = e.randomseed? e.randomseed : Date.now(); | |
| } | |
| function vm_capture_state(e, new_inst) { | |
| var nword = 3 + e.ramdata.length + e.auxdata.length + e.heapdata.length; | |
| var data = new Uint8Array(nword * 2); | |
| var regs = new Uint8Array(128 + 26 + 2 + e.divs.length * 2); | |
| var i, j = 0; | |
| j = put16(data, j, e.nob); | |
| j = put16(data, j, e.ltb); | |
| j = put16(data, j, e.ltt); | |
| for(i = 0; i < e.ramdata.length; i++) { | |
| j = put16(data, j, i < e.ltt ? e.ramdata[i] : 0x3f3f); | |
| } | |
| for(i = 0; i < e.auxdata.length; i++) { | |
| j = put16(data, j, (i < e.aux || i >= e.trl) ? e.auxdata[i] : 0x3f3f); | |
| } | |
| for(i = 0; i < e.heapdata.length; i++) { | |
| j = put16(data, j, (i < e.top || i >= e.env || i >= e.cho) ? e.heapdata[i] : 0x3f3f); | |
| } | |
| j = 0; | |
| for(i = 0; i < 64; i++) { | |
| j = put16(regs, j, e.reg[i]); | |
| } | |
| j = put32(regs, j, new_inst); | |
| j = put32(regs, j, e.cont); | |
| j = put16(regs, j, e.top); | |
| j = put16(regs, j, e.env); | |
| j = put16(regs, j, e.cho); | |
| j = put16(regs, j, e.sim); | |
| j = put16(regs, j, e.aux); | |
| j = put16(regs, j, e.trl); | |
| j = put16(regs, j, e.sta); | |
| j = put16(regs, j, e.stc); | |
| regs[j++] = e.cwl; | |
| regs[j++] = e.spc; | |
| j = put16(regs, j, e.divs.length); | |
| for(i = 0; i < e.divs.length; i++) { | |
| j = put16(regs, j, e.divs[i]); | |
| } | |
| return {data: data, regs: regs}; | |
| } | |
| function vm_clear_divs(e) { | |
| e.io.leave_all(); | |
| e.in_status = false; | |
| e.n_span = 0; | |
| e.n_link = 0; | |
| e.divs = []; | |
| } | |
| function vm_restore_state(e, state) { | |
| var data = state.data, regs = state.regs; | |
| var i, j = 0, ndiv; | |
| e.nob = get16(data, j); j += 2; | |
| e.ltb = get16(data, j); j += 2; | |
| e.ltt = get16(data, j); j += 2; | |
| for(i = 0; i < e.ramdata.length; i++) { | |
| e.ramdata[i] = get16(data, j); j += 2; | |
| } | |
| for(i = 0; i < e.auxdata.length; i++) { | |
| e.auxdata[i] = get16(data, j); j += 2; | |
| } | |
| for(i = 0; i < e.heapdata.length; i++) { | |
| e.heapdata[i] = get16(data, j); j += 2; | |
| } | |
| j = 0; | |
| for(i = 0; i < 64; i++) { | |
| e.reg[i] = get16(regs, j); j += 2; | |
| } | |
| e.inst = get32(regs, j); j += 4; | |
| e.cont = get32(regs, j); j += 4; | |
| e.top = get16(regs, j); j += 2; | |
| e.env = get16(regs, j); j += 2; | |
| e.cho = get16(regs, j); j += 2; | |
| e.sim = get16(regs, j); j += 2; | |
| e.aux = get16(regs, j); j += 2; | |
| e.trl = get16(regs, j); j += 2; | |
| e.sta = get16(regs, j); j += 2; | |
| e.stc = get16(regs, j); j += 2; | |
| e.cwl = regs[j++]; | |
| e.spc = regs[j++]; | |
| ndiv = get16(regs, j); j += 2; | |
| for(i = 0; i < ndiv; i++) { | |
| e.divs[i] = get16(regs, j); j += 2; | |
| e.io.enter_div(e.divs[i]); | |
| } | |
| } | |
| function vm_rlenc_state(reference, state) { | |
| var i, j = 0, bytes = 0, nz = 0, encoded, diff; | |
| for(i = 0; i < reference.data.length; i++) { | |
| if(reference.data[i] ^ state.data[i]) { | |
| bytes++; | |
| nz = 0; | |
| } else { | |
| if(nz && nz < 0x100) { | |
| nz++; | |
| } else { | |
| bytes += 2; | |
| nz = 1; | |
| } | |
| } | |
| } | |
| encoded = new Uint8Array(bytes); | |
| for(i = 0; i < reference.data.length; i++) { | |
| diff = reference.data[i] ^ state.data[i]; | |
| if(diff) { | |
| encoded[j++] = diff; | |
| } else { | |
| encoded[j++] = 0; | |
| nz = 1; | |
| while(nz < 0x100 && !(reference.data[i + nz] ^ state.data[i + nz])) { | |
| nz++; | |
| } | |
| encoded[j++] = nz - 1; | |
| i += nz - 1; | |
| } | |
| } | |
| return {rledata: encoded, regs: state.regs}; | |
| } | |
| function vm_rldec_state(reference, encoded) { | |
| var array = new Uint8Array(reference.data.length); | |
| var i, j = 0, diff, nz; | |
| for(i = 0; i < encoded.rledata.length; i++) { | |
| diff = encoded.rledata[i]; | |
| if(diff) { | |
| array[j] = reference.data[j] ^ diff; | |
| j++; | |
| } else { | |
| nz = encoded.rledata[++i] + 1; | |
| while(nz--) { | |
| array[j] = reference.data[j]; | |
| j++; | |
| } | |
| } | |
| } | |
| return {data: array, regs: encoded.regs}; | |
| } | |
| function vm_wrap_savefile(e, encoded) { | |
| function makechunk(tag, array) { | |
| var size = (array.length + 1) & ~1; | |
| var result = new Uint8Array(8 + size); | |
| putfour(result, 0, tag); | |
| put32(result, 4, array.length); | |
| result.set(array, 8); | |
| return result; | |
| } | |
| var head = makechunk("HEAD", e.head); | |
| var data = makechunk("DATA", encoded.rledata); | |
| var regs = makechunk("REGS", encoded.regs); | |
| var size = 4 + head.length + data.length + regs.length; | |
| var result = new Uint8Array(8 + size); | |
| putfour(result, 0, "FORM"); | |
| put32(result, 4, size); | |
| putfour(result, 8, "AASV"); | |
| result.set(head, 12); | |
| result.set(data, 12 + head.length); | |
| result.set(regs, 12 + head.length + data.length); | |
| return result; | |
| } | |
| function vm_unwrap_savefile(e, filedata) { | |
| var head, data, regs, i; | |
| if(getfour(filedata, 0) != "FORM" || getfour(filedata, 8) != "AASV") { | |
| e.io.print("Not an aasave file!"); | |
| e.io.line(); | |
| return null; | |
| } | |
| head = findchunk(filedata, "HEAD"); | |
| data = findchunk(filedata, "DATA"); | |
| regs = findchunk(filedata, "REGS"); | |
| if(!head || !data || !regs) { | |
| e.io.print("Incomplete aasave file!"); | |
| e.io.line(); | |
| return null; | |
| } | |
| for(i = 0; i < head.length && i < e.head.length; i++) { | |
| if(head[i] != e.head[i]) break; | |
| } | |
| if(i != head.length || i != e.head.length) { | |
| e.io.print("This savefile is from another story (or another version of the present story)."); | |
| e.io.line(); | |
| return null; | |
| } | |
| return {rledata: data, regs: regs}; | |
| } | |
| function vm_run(e, param) { | |
| var io = e.io; | |
| var op, a1, a2, a3, a4, addr, tmp, v, i, j, flag, iter, match, curr, str; | |
| function fvalue() { | |
| var v = e.code[e.inst++]; | |
| if(v >= 0xc0) { | |
| return e.heapdata[e.env + 4 + (v & 0x3f)]; | |
| } else if(v >= 0x80) { | |
| return e.reg[v & 0x3f]; | |
| } else { | |
| return (v << 8) | e.code[e.inst++]; | |
| } | |
| } | |
| function findex() { | |
| var v = e.code[e.inst++]; | |
| if(v >= 0xc0) { | |
| return ((v & 0x3f) << 8) | e.code[e.inst++]; | |
| } else { | |
| return v; | |
| } | |
| } | |
| function fcode() { | |
| var v = e.code[e.inst++]; | |
| if(v == 0) { | |
| return 0; | |
| } else if(v < 0x40) { | |
| return e.inst + v; | |
| } else if(v < 0x80) { | |
| v = ((v & 0x3f) << 8) | e.code[e.inst++]; | |
| if(v & 0x2000) { | |
| return e.inst + v - 0x4000; | |
| } else { | |
| return e.inst + v; | |
| } | |
| } else { | |
| v = ((v & 0x7f) << 16) | (e.code[e.inst++] << 8); | |
| return v | e.code[e.inst++]; | |
| } | |
| } | |
| function fstring() { | |
| var v = e.code[e.inst++]; | |
| if(v >= 0xc0) { | |
| v = ((v & 0x3f) << 16) | (e.code[e.inst++] << 8); | |
| v |= e.code[e.inst++]; | |
| return v << e.strshift; | |
| } else if(v >= 0x80) { | |
| v = ((v & 0x3f) << 8) | e.code[e.inst++]; | |
| return v << e.strshift; | |
| } else { | |
| return v << 1; | |
| } | |
| } | |
| function fword() { | |
| var v = e.code[e.inst++]; | |
| return (v << 8) | e.code[e.inst++]; | |
| } | |
| function deref(v) { | |
| var t; | |
| while((v & 0xe000) == 0x8000) { | |
| t = e.heapdata[v & 0x1fff]; | |
| if(!t) return v; | |
| v = t; | |
| } | |
| return v; | |
| } | |
| function fail() { | |
| e.inst = (e.heapdata[e.cho + 4] << 16) | e.heapdata[e.cho + 5]; | |
| } | |
| function unify(a, b) { | |
| while(true) { | |
| a = deref(a); | |
| b = deref(b); | |
| if((a & 0xe000) == 0x8000 && (b & 0xe000) == 0x8000) { | |
| if(e.trl <= e.aux) throw AUXFULL; | |
| if(a < b) { | |
| e.auxdata[--e.trl] = b & 0x1fff; | |
| e.heapdata[b & 0x1fff] = a; | |
| } else if(a > b) { | |
| e.auxdata[--e.trl] = a & 0x1fff; | |
| e.heapdata[a & 0x1fff] = b; | |
| } | |
| return true; | |
| } else if((a & 0xe000) == 0x8000) { | |
| if(e.trl <= e.aux) throw AUXFULL; | |
| e.auxdata[--e.trl] = a & 0x1fff; | |
| e.heapdata[a & 0x1fff] = b; | |
| return true; | |
| } else if((b & 0xe000) == 0x8000) { | |
| if(e.trl <= e.aux) throw AUXFULL; | |
| e.auxdata[--e.trl] = b & 0x1fff; | |
| e.heapdata[b & 0x1fff] = a; | |
| return true; | |
| } else if(a >= 0xe000 && b >= 0xe000) { | |
| a = e.heapdata[a & 0x1fff]; | |
| b = e.heapdata[b & 0x1fff]; | |
| } else if(a >= 0xe000) { | |
| a = e.heapdata[a & 0x1fff]; | |
| } else if(b >= 0xe000) { | |
| b = e.heapdata[b & 0x1fff]; | |
| } else if(a == b) { | |
| return true; | |
| } else if(a >= 0xc000 && b >= 0xc000) { | |
| if(!unify(a - 0x4000, b - 0x4000)) return false; | |
| a = a - 0x3fff; | |
| b = b - 0x3fff; | |
| } else { | |
| return false; | |
| } | |
| } | |
| } | |
| function would_unify(a, b) { | |
| while(true) { | |
| a = deref(a); | |
| b = deref(b); | |
| if((a & 0xe000) == 0x8000 || (b & 0xe000) == 0x8000) { | |
| return true; | |
| } else if(a >= 0xe000 && b >= 0xe000) { | |
| a = e.heapdata[a & 0x1fff]; | |
| b = e.heapdata[b & 0x1fff]; | |
| } else if(a >= 0xe000) { | |
| a = e.heapdata[a & 0x1fff]; | |
| } else if(b >= 0xe000) { | |
| b = e.heapdata[b & 0x1fff]; | |
| } else if(a == b) { | |
| return true; | |
| } else if(a >= 0xc000 && b >= 0xc000) { | |
| if(!would_unify(a - 0x4000, b - 0x4000)) { | |
| return false; | |
| } | |
| a = a - 0x3fff; | |
| b = b - 0x3fff; | |
| } else { | |
| return false; | |
| } | |
| } | |
| } | |
| function destvalue(dest) { | |
| if(dest & 0x40) { | |
| return e.heapdata[e.env + 4 + (dest & 0x3f)]; | |
| } else { | |
| return e.reg[dest & 0x3f]; | |
| } | |
| } | |
| function store(dest, src) { | |
| //console.log(tohex(src, 4)); | |
| if(dest >= 0xc0) { | |
| if(!unify(e.heapdata[e.env + 4 + (dest & 0x3f)], src)) fail(); | |
| } else if(dest >= 0x80) { | |
| if(!unify(e.reg[dest & 0x3f], src)) fail(); | |
| } else if(dest >= 0x40) { | |
| e.heapdata[e.env + 4 + (dest & 0x3f)] = src; | |
| } else { | |
| e.reg[dest] = src; | |
| } | |
| } | |
| function push_cho(narg, next) { | |
| var i; | |
| var addr = ((e.env < e.cho) ? e.env : e.cho) - 9 - narg; | |
| if(addr < e.top) throw HEAPFULL; | |
| e.heapdata[addr + 0] = e.env; | |
| e.heapdata[addr + 1] = e.sim; | |
| e.heapdata[addr + 2] = e.cont >> 16; | |
| e.heapdata[addr + 3] = e.cont & 0xffff; | |
| e.heapdata[addr + 4] = next >> 16; | |
| e.heapdata[addr + 5] = next & 0xffff; | |
| e.heapdata[addr + 6] = e.cho; | |
| e.heapdata[addr + 7] = e.top; | |
| e.heapdata[addr + 8] = e.trl; | |
| for(i = 0; i < narg; i++) { | |
| e.heapdata[addr + 9 + i] = e.reg[i]; | |
| } | |
| e.cho = addr; | |
| } | |
| function push_aux(v) { | |
| var count; | |
| v = deref(v); | |
| if(v >= 0xe000) { | |
| push_aux(e.heapdata[(v & 0x1fff) + 1]); | |
| push_aux(e.heapdata[(v & 0x1fff) + 0]); | |
| v = 0x8100; | |
| } else if(v >= 0xc000) { | |
| count = 0; | |
| while(true) { | |
| push_aux(v - 0x4000); | |
| count++; | |
| v = deref(v - 0x3fff); | |
| if(v == 0x3f00) { | |
| v = 0xc000 | count; | |
| break; | |
| } else if((v & 0xe000) != 0xc000) { | |
| push_aux(v); | |
| v = 0xe000 | count; | |
| break; | |
| } | |
| } | |
| } else if(v >= 0x8000) { | |
| v = 0x8000; | |
| } | |
| if(e.aux >= e.trl) throw AUXFULL; | |
| e.auxdata[e.aux++] = v; | |
| } | |
| function pop_aux() { | |
| var v = e.auxdata[--e.aux]; | |
| var addr, count; | |
| if(v == 0x8000) { | |
| addr = e.top++; | |
| if(e.top > e.env || e.top > e.cho) throw HEAPFULL; | |
| e.heapdata[addr] = 0; | |
| v = 0x8000 | addr; | |
| } else if(v == 0x8100) { | |
| addr = e.top; | |
| e.top += 2; | |
| if(e.top > e.env || e.top > e.cho) throw HEAPFULL; | |
| e.heapdata[addr + 0] = pop_aux(); | |
| e.heapdata[addr + 1] = pop_aux(); | |
| v = 0xe000 | addr; | |
| } else if(v >= 0xc000) { | |
| count = v & 0x1fff; | |
| if(v & 0x2000) { | |
| v = pop_aux(); | |
| } else { | |
| v = 0x3f00; | |
| } | |
| while(count--) { | |
| addr = e.top; | |
| e.top += 2; | |
| if(e.top > e.env || e.top > e.cho) throw HEAPFULL; | |
| e.heapdata[addr + 0] = pop_aux(); | |
| e.heapdata[addr + 1] = v; | |
| v = 0xc000 | addr; | |
| } | |
| } | |
| return v; | |
| } | |
| function pop_aux_list() { | |
| var list = 0x3f00; | |
| var v; | |
| while((v = pop_aux())) { | |
| addr = e.top; | |
| e.top += 2; | |
| if(e.top > e.env || e.top > e.cho) throw HEAPFULL; | |
| e.heapdata[addr + 0] = v; | |
| e.heapdata[addr + 1] = list; | |
| list = 0xc000 | addr; | |
| } | |
| return list; | |
| } | |
| function fieldaddr(field, obj) { | |
| obj = deref(obj); | |
| if(obj > e.nob) { | |
| throw EXPECTOBJ; | |
| } else { | |
| return e.ramdata[obj] + field; | |
| } | |
| } | |
| function readfield(field, obj) { | |
| obj = deref(obj); | |
| if(obj > e.nob) { | |
| return 0; | |
| } else { | |
| return e.ramdata[e.ramdata[obj] + field]; | |
| } | |
| } | |
| function unlink(root_addr, next, key) { | |
| var tail, addr; | |
| if(!key || key >= 0x2000) return; | |
| tail = e.ramdata[fieldaddr(next, key)]; | |
| addr = root_addr; | |
| while(e.ramdata[addr]) { | |
| if(e.ramdata[addr] == key) { | |
| e.ramdata[addr] = tail; | |
| return; | |
| } | |
| addr = fieldaddr(next, e.ramdata[addr]); | |
| } | |
| } | |
| function pop_lts() { | |
| var v = e.ramdata[--tmp]; | |
| var addr, count; | |
| if(v == 0x8100) { | |
| addr = e.top; | |
| e.top += 2; | |
| if(e.top > e.env || e.top > e.cho) throw HEAPFULL; | |
| e.heapdata[addr + 0] = pop_lts(); | |
| e.heapdata[addr + 1] = pop_lts(); | |
| v = 0xe000 | addr; | |
| } else if(v >= 0xc000) { | |
| count = v & 0x1fff; | |
| if(v & 0x2000) { | |
| v = pop_lts(); | |
| } else { | |
| v = 0x3f00; | |
| } | |
| while(count--) { | |
| addr = e.top; | |
| e.top += 2; | |
| if(e.top > e.env || e.top > e.cho) throw HEAPFULL; | |
| e.heapdata[addr + 0] = pop_lts(); | |
| e.heapdata[addr + 1] = v; | |
| v = 0xc000 | addr; | |
| } | |
| } | |
| return v; | |
| } | |
| function push_lts(v) { | |
| var count; | |
| v = deref(v); | |
| if(v >= 0xe000) { | |
| push_lts(e.heapdata[(v & 0x1fff) + 1]); | |
| push_lts(e.heapdata[(v & 0x1fff) + 0]); | |
| v = 0x8100; | |
| } else if(v >= 0xc000) { | |
| count = 0; | |
| while(true) { | |
| push_lts(v - 0x4000); | |
| count++; | |
| v = deref(v - 0x3fff); | |
| if(v == 0x3f00) { | |
| v = 0xc000 | count; | |
| break; | |
| } else if((v & 0xe000) != 0xc000) { | |
| push_lts(v); | |
| v = 0xe000 | count; | |
| break; | |
| } | |
| } | |
| } else if(v >= 0x8000) { | |
| throw EXPECTBOUND; | |
| } | |
| if(tmp > e.ramdata.length) throw LTSFULL; | |
| e.ramdata[tmp++] = v; | |
| } | |
| function clear_lts(addr) { | |
| var v = e.ramdata[addr]; | |
| var i, size; | |
| if(v & 0x8000) { | |
| e.ramdata[addr] = 0; | |
| v &= 0x7fff; | |
| size = e.ramdata[v]; | |
| for(i = v; i < e.ltt - size; i++) { | |
| e.ramdata[i] = e.ramdata[i + size]; | |
| } | |
| e.ltt -= size; | |
| while(v < e.ltt) { | |
| e.ramdata[e.ramdata[v + 1]] -= size; | |
| v += e.ramdata[v]; | |
| } | |
| } | |
| } | |
| function get_lts(v) { | |
| if(v & 0x8000) { | |
| tmp = v & 0x7fff; | |
| tmp += e.ramdata[tmp]; | |
| return pop_lts(); | |
| } else { | |
| return v; | |
| } | |
| } | |
| function put_lts(addr, v) { | |
| clear_lts(addr); | |
| v = deref(v); | |
| if(v < 0x8000) { | |
| e.ramdata[addr] = v; | |
| } else { | |
| tmp = e.ltt + 2; | |
| if(tmp > e.ramdata.length) throw LTSFULL; | |
| push_lts(v); | |
| e.ramdata[addr] = 0x8000 | e.ltt; | |
| e.ramdata[e.ltt + 0] = tmp - e.ltt; | |
| e.ramdata[e.ltt + 1] = addr; | |
| e.ltt = tmp; | |
| } | |
| } | |
| function val2str(v) { | |
| var i, str, entry, len, addr, x, needsp; | |
| v = deref(v); | |
| if(v >= 0xe000) { | |
| str = ""; | |
| for(i = 0; i < 2; i++) { | |
| x = e.heapdata[(v & 0x1fff) + i]; | |
| if(x >= 0x3f00) { | |
| while(x >= 0xc000) { | |
| str += val2str(e.heapdata[x & 0x1fff]); | |
| x = e.heapdata[(x & 0x1fff) + 1]; | |
| } | |
| } else { | |
| str += val2str(x); | |
| } | |
| } | |
| } else if(v >= 0xc000) { | |
| needsp = false; | |
| e.upper = false; | |
| str = "["; | |
| while((v & 0xe000) == 0xc000) { | |
| if(needsp) str += " "; | |
| str += val2str(v - 0x4000); | |
| needsp = true; | |
| v = deref(v - 0x3fff); | |
| } | |
| if(v == 0x3f00) { | |
| str += "]"; | |
| } else { | |
| str += " | " + val2str(v) + "]"; | |
| } | |
| } else if(v >= 0x8000) { | |
| e.upper = false; | |
| str = "$"; | |
| } else if(v >= 0x4000) { | |
| e.upper = false; | |
| str = (v & 0x3fff).toString(); | |
| } else if(v >= 0x3f00) { | |
| e.upper = false; | |
| str = "[]"; | |
| } else if(v >= 0x3e00) { | |
| str = decodechar(e, v & 0xff); | |
| } else if(v >= 0x2000) { | |
| entry = 2 + (v & 0x1fff) * 3; | |
| len = e.dict[entry]; | |
| addr = (e.dict[entry + 1] << 8) | e.dict[entry + 2]; | |
| str = ""; | |
| for(i = 0; i < len; i++) { | |
| str += decodechar(e, e.dict[addr + i]); | |
| } | |
| } else if(v) { | |
| e.upper = false; | |
| str = "#"; | |
| if(e.tags) { | |
| addr = get16(e.tags, v * 2); | |
| while((v = e.tags[addr++])) { | |
| str += decodechar(e, v); | |
| } | |
| } | |
| } | |
| return str; | |
| } | |
| function wordmap(mapnum, v) { | |
| var map = get16(e.maps, 2 + mapnum * 2); | |
| var start = 0; | |
| var end = get16(e.maps, map); | |
| var o, mid, midval, ptr; | |
| while(start < end) { | |
| mid = (start + end) >> 1; | |
| midval = get16(e.maps, map + 2 + mid * 4); | |
| if(midval == v) { | |
| ptr = get16(e.maps, map + 4 + mid * 4); | |
| if(!ptr) { | |
| return false; | |
| } else if(ptr & 0xe000) { | |
| if(e.aux >= e.trl) throw AUXFULL; | |
| e.auxdata[e.aux++] = ptr & 0x1fff; | |
| return true; | |
| } else { | |
| while((o = e.maps[ptr++])) { | |
| if(e.aux >= e.trl) throw AUXFULL; | |
| if(o >= 0xe0) { | |
| o = ((o & 0x1f) << 8) | e.maps[ptr++]; | |
| } | |
| e.auxdata[e.aux++] = o; | |
| } | |
| return true; | |
| } | |
| } else if(midval > v) { | |
| end = mid; | |
| } else { | |
| start = mid + 1; | |
| } | |
| } | |
| return true; | |
| } | |
| function compat_rand() { | |
| var high = (e.randomstate >> 16) & 0xffff; | |
| var low = e.randomstate & 0xffff; | |
| var newhigh = ((0x15a * low) + (0x4e35 * high)) & 0xffff; | |
| e.randomstate = (((newhigh << 16)>>>0) + (0x4e35 * low) + 1) & 0xffffffff; | |
| return (e.randomstate >> 16) & 0x7fff; | |
| } | |
| function get_res(id) { | |
| var obj = {url: "", alt: "", options: ""}; | |
| var n, i, offs; | |
| if(e.urls) { | |
| n = get16(e.urls, 0); | |
| if(id < n) { | |
| offs = get16(e.urls, 2 + id * 2); | |
| obj.alt = decodestr(e, ( | |
| (e.urls[offs] << 16) | | |
| (e.urls[offs + 1] << 8) | | |
| e.urls[offs + 2]) << e.strshift); | |
| for(i = 3; e.urls[offs + i]; i++) { | |
| obj.url += String.fromCharCode(e.urls[offs + i]); | |
| } | |
| for(i++; e.urls[offs + i]; i++) { | |
| obj.options += String.fromCharCode(e.urls[offs + i]); | |
| } | |
| } | |
| } | |
| return obj; | |
| } | |
| function makepairsub(literal, arg, addr) { | |
| if(literal) { | |
| e.heapdata[addr] = arg; | |
| } else if(arg & 0x80) { | |
| e.heapdata[addr] = destvalue(arg); | |
| } else { | |
| e.heapdata[addr] = 0; | |
| store(arg, 0x8000 | addr); | |
| } | |
| } | |
| function makepair(a1val, a1, a2, a3) { | |
| var addr; | |
| if(a3 & 0x80) { | |
| // unify | |
| a3 = deref(destvalue(a3)); | |
| if((a3 & 0xe000) == 0xc000) { | |
| if(a1val) { | |
| if(!unify(a1, a3 - 0x4000)) fail(); | |
| } else { | |
| store(a1, a3 - 0x4000); | |
| } | |
| store(a2, a3 - 0x3fff); | |
| } else if((a3 & 0xe000) == 0x8000) { | |
| addr = e.top; | |
| e.top += 2; | |
| if(e.top > e.env || e.top > e.cho) throw HEAPFULL; | |
| makepairsub(a1val, a1, addr); | |
| makepairsub(false, a2, addr + 1); | |
| unify(a3, 0xc000 | addr); | |
| } else { | |
| fail(); | |
| } | |
| } else { | |
| // create | |
| addr = e.top; | |
| e.top += 2; | |
| if(e.top > e.env || e.top > e.cho) throw HEAPFULL; | |
| makepairsub(a1val, a1, addr); | |
| makepairsub(false, a2, addr + 1); | |
| store(a3, 0xc000 | addr); | |
| } | |
| } | |
| function prepend_chars(v, list) { | |
| var entry, len, addr, i, ch; | |
| entry = 2 + (v & 0x1fff) * 3; | |
| len = e.dict[entry]; | |
| addr = (e.dict[entry + 1] << 8) | e.dict[entry + 2]; | |
| for(i = len - 1; i >= 0; i--) { | |
| ch = e.dict[addr + i]; | |
| if(ch >= '0' && ch <= '9') { | |
| ch += 0x4000 - '0'; | |
| } else { | |
| ch |= 0x3e00; | |
| } | |
| list = e.create_pair(ch, list); | |
| } | |
| return list; | |
| } | |
| function words_to_charlist(list) { | |
| var buf = [], v, str, ch, entry, len, addr, part1; | |
| do { | |
| v = deref(e.heapdata[(list & 0x1fff) + 0]); | |
| if(v >= 0xe000) { | |
| part1 = e.heapdata[(v & 0x1fff) + 0]; | |
| if(part1 >= 0x8000) { | |
| buf = buf.concat(words_to_charlist(part1)); | |
| } else { | |
| buf = buf.concat(words_to_charlist(v)); | |
| } | |
| } else if(v >= 0x4000 && v < 0x8000) { | |
| str = (v & 0x3fff).toString(); | |
| for(i = 0; i < str.length; i++) { | |
| buf.push(str.charCodeAt(i)); | |
| } | |
| } else if(v >= 0x3e00 && v < 0x3f00) { | |
| ch = v & 0xff; | |
| if(ch <= 0x20) return 0; | |
| if(e.stopchars.includes(ch)) return 0; | |
| buf.push(ch); | |
| } else if(v >= 0x2000 && v < 0x3e00) { | |
| entry = 2 + (v & 0x1fff) * 3; | |
| len = e.dict[entry]; | |
| addr = (e.dict[entry + 1] << 8) | e.dict[entry + 2]; | |
| for(i = 0; i < len; i++) { | |
| buf.push(e.dict[addr + i]); | |
| } | |
| } else { | |
| return 0; | |
| } | |
| list = deref(e.heapdata[(list & 0x1fff) + 1]); | |
| } while((list & 0xe000) == 0xc000); | |
| if(list != 0x3f00) return 0; | |
| return buf; | |
| } | |
| if(param) { | |
| store(e.code[e.inst++], param); | |
| } | |
| while(true) { | |
| try { | |
| while(true) { | |
| op = e.code[e.inst++]; | |
| //console.log(tohex(e.inst - 1, 6) + ' ' + tohex(op, 2)); | |
| switch(op) { | |
| case 0x00: // nop | |
| break; | |
| case 0x01: // fail | |
| fail(); | |
| break; | |
| case 0x02: // set_cont code | |
| e.cont = fcode(); | |
| break; | |
| case 0x03: // proceed | |
| if(e.sim < 0x8000) e.cho = e.sim; | |
| e.inst = e.cont; | |
| break; | |
| case 0x04: // jmp code | |
| e.inst = fcode(); | |
| break; | |
| case 0x05: // jmp_multi code | |
| e.sim = 0xffff; | |
| e.inst = fcode(); | |
| break; | |
| case 0x85: // jmpl_multi code | |
| a1 = fcode(); | |
| e.cont = e.inst; | |
| e.sim = 0xffff; | |
| e.inst = a1; | |
| break; | |
| case 0x06: // jmp_simple code | |
| e.sim = e.cho; | |
| e.inst = fcode(); | |
| break; | |
| case 0x86: // jmpl_simple code | |
| a1 = fcode(); | |
| e.cont = e.inst; | |
| e.sim = e.cho; | |
| e.inst = a1; | |
| break; | |
| case 0x07: // jmp_tail code | |
| if(e.sim >= 0x8000) e.sim = e.cho; | |
| e.inst = fcode(); | |
| break; | |
| case 0x87: // tail | |
| if(e.sim >= 0x8000) e.sim = e.cho; | |
| break; | |
| case 0x08: case 0x88: // push_env byte/0 | |
| a1 = (op & 0x80)? 0 : e.code[e.inst++]; | |
| addr = ((e.env < e.cho) ? e.env : e.cho) - 4 - a1; | |
| if(addr < e.top) throw HEAPFULL; | |
| e.heapdata[addr + 0] = e.env; | |
| e.heapdata[addr + 1] = e.sim; | |
| e.heapdata[addr + 2] = e.cont >> 16; | |
| e.heapdata[addr + 3] = e.cont & 0xffff; | |
| e.env = addr; | |
| break; | |
| case 0x09: // pop_env | |
| e.cont = (e.heapdata[e.env + 2] << 16) | e.heapdata[e.env + 3]; | |
| e.sim = e.heapdata[e.env + 1]; | |
| e.env = e.heapdata[e.env + 0]; | |
| break; | |
| case 0x89: // pop_env_proceed | |
| e.inst = (e.heapdata[e.env + 2] << 16) | e.heapdata[e.env + 3]; | |
| if(e.heapdata[e.env + 1] < 0x8000) e.cho = e.heapdata[e.env + 1]; | |
| e.env = e.heapdata[e.env + 0]; | |
| break; | |
| case 0x0a: case 0x8a: // push_choice byte/0 next | |
| a1 = (op & 0x80)? 0 : e.code[e.inst++]; | |
| push_cho(a1, fcode()); | |
| break; | |
| case 0x0b: case 0x8b: // pop_choice byte/0 | |
| a1 = (op & 0x80)? 0 : e.code[e.inst++]; | |
| for(i = 0; i < a1; i++) { | |
| e.reg[i] = e.heapdata[e.cho + 9 + i]; | |
| } | |
| while(e.trl < e.heapdata[e.cho + 8]) { | |
| e.heapdata[e.auxdata[e.trl++]] = 0; | |
| } | |
| e.top = e.heapdata[e.cho + 7]; | |
| e.cont = (e.heapdata[e.cho + 2] << 16) | e.heapdata[e.cho + 3]; | |
| e.sim = e.heapdata[e.cho + 1]; | |
| e.env = e.heapdata[e.cho + 0]; | |
| e.cho = e.heapdata[e.cho + 6]; | |
| break; | |
| case 0x0c: case 0x8c: // pop_push_choice byte/0 code | |
| a1 = (op & 0x80)? 0 : e.code[e.inst++]; | |
| a2 = fcode(); | |
| e.heapdata[e.cho + 4] = a2 >> 16; | |
| e.heapdata[e.cho + 5] = a2 & 0xffff; | |
| for(i = 0; i < a1; i++) { | |
| e.reg[i] = e.heapdata[e.cho + 9 + i]; | |
| } | |
| while(e.trl < e.heapdata[e.cho + 8]) { | |
| e.heapdata[e.auxdata[e.trl++]] = 0; | |
| } | |
| e.top = e.heapdata[e.cho + 7]; | |
| e.cont = (e.heapdata[e.cho + 2] << 16) | e.heapdata[e.cho + 3]; | |
| e.sim = e.heapdata[e.cho + 1]; | |
| e.env = e.heapdata[e.cho + 0]; | |
| break; | |
| case 0x0d: // cut_choice | |
| e.cho = e.heapdata[e.cho + 6]; | |
| break; | |
| case 0x0e: // get_cho dest | |
| store(e.code[e.inst++], e.cho); | |
| break; | |
| case 0x0f: // set_cho value | |
| e.cho = fvalue(); | |
| break; | |
| case 0x10: case 0x90: // assign value/vbyte dest | |
| a1 = (op & 0x80)? e.code[e.inst++] : fvalue(); | |
| a2 = e.code[e.inst++]; | |
| store(a2, a1); | |
| break; | |
| case 0x11: // make_var dest | |
| addr = e.top++; | |
| if(e.top > e.env || e.top > e.cho) throw HEAPFULL; | |
| e.heapdata[addr] = 0; | |
| store(e.code[e.inst++], 0x8000 | addr); | |
| break; | |
| case 0x12: // make_pair DEST DEST DEST | |
| a1 = e.code[e.inst++]; | |
| a2 = e.code[e.inst++]; | |
| a3 = e.code[e.inst++]; | |
| makepair(false, a1, a2, a3); | |
| break; | |
| case 0x13: case 0x93: // make_pair WORD/VBYTE DEST DEST | |
| a1 = (op & 0x80)? e.code[e.inst++] : fword(); | |
| a2 = e.code[e.inst++]; | |
| a3 = e.code[e.inst++]; | |
| makepair(true, a1, a2, a3); | |
| break; | |
| case 0x14: // aux_push_val value | |
| push_aux(fvalue()); | |
| break; | |
| case 0x94: // aux_push_raw 0 | |
| if(e.aux >= e.trl) throw AUXFULL; | |
| e.auxdata[e.aux++] = 0; | |
| break; | |
| case 0x15: // aux_push_raw word | |
| if(e.aux >= e.trl) throw AUXFULL; | |
| e.auxdata[e.aux++] = fword(); | |
| break; | |
| case 0x95: // aux_push_raw vbyte | |
| if(e.aux >= e.trl) throw AUXFULL; | |
| e.auxdata[e.aux++] = e.code[e.inst++]; | |
| break; | |
| case 0x16: // aux_pop_val dest | |
| store(e.code[e.inst++], pop_aux()); | |
| break; | |
| case 0x17: // aux_pop_list dest | |
| store(e.code[e.inst++], pop_aux_list()); | |
| break; | |
| case 0x18: // aux_pop_list_chk value | |
| a1 = deref(fvalue()); | |
| flag = false; | |
| while((v = e.auxdata[--e.aux])) { | |
| if(v == a1) flag = true; | |
| } | |
| if(!flag) fail(); | |
| break; | |
| case 0x19: // aux_pop_list_match value | |
| tmp = e.top; | |
| a1 = deref(fvalue()); | |
| v = pop_aux_list(); | |
| while((a1 & 0xe000) == 0xc000) { | |
| iter = v; | |
| match = false; | |
| while((iter & 0xe000) == 0xc000 && !match) { | |
| if(would_unify(iter - 0x4000, a1 - 0x4000)) { | |
| match = true; | |
| } | |
| iter = deref(iter - 0x3fff); | |
| } | |
| if(!match) { | |
| fail(); | |
| break; | |
| } | |
| a1 = deref(a1 - 0x3fff); | |
| } | |
| e.top = tmp; | |
| break; | |
| case 0x1b: // split_list value value dest | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| v = 0x3f00; | |
| if(a1 != a2 && (a1 & 0xe000) == 0xc000) { | |
| curr = e.top; | |
| v = 0xc000 | curr; | |
| while(true) { | |
| e.top += 2; | |
| if(e.top > e.env || e.top > e.cho) throw HEAPFULL; | |
| e.heapdata[curr + 0] = e.heapdata[a1 & 0x1fff]; | |
| a1 = deref(a1 - 0x3fff); | |
| if(a1 == a2 || (a1 & 0xe000) != 0xc000) { | |
| break; | |
| } | |
| e.heapdata[curr + 1] = 0xc000 | e.top; | |
| curr = e.top; | |
| } | |
| e.heapdata[curr + 1] = 0x3f00; | |
| } | |
| store(e.code[e.inst++], v); | |
| break; | |
| case 0x1c: // stop | |
| e.cho = e.stc; | |
| e.inst = (e.heapdata[e.cho + 4] << 16) | e.heapdata[e.cho + 5]; | |
| break; | |
| case 0x1d: // push_stop code | |
| if(e.aux + 2 > e.trl) throw AUXFULL; | |
| e.auxdata[e.aux++] = e.stc; | |
| e.auxdata[e.aux++] = e.sta; | |
| e.sta = e.aux; | |
| push_cho(0, fcode()); | |
| e.stc = e.cho; | |
| break; | |
| case 0x1e: // pop_stop | |
| e.aux = e.sta; | |
| e.sta = e.auxdata[--e.aux]; | |
| e.stc = e.auxdata[--e.aux]; | |
| break; | |
| case 0x1f: // split_word value dest | |
| a1 = deref(fvalue()); | |
| if(a1 >= 0x2000 && a1 < 0x3e00) { | |
| v = prepend_chars(a1, 0x3f00); | |
| } else if(a1 >= 0x3e00 && a1 < 0x3f00) { | |
| v = e.create_pair(a1, 0x3f00); | |
| } else if(a1 >= 0x4000 && a1 < 0x8000) { | |
| i = a1 & 0x3fff; | |
| v = 0x3f00; | |
| do { | |
| v = e.create_pair(0x4000 | (i % 10), v); | |
| i = Math.floor(i / 10); | |
| } while(i); | |
| } else if(a1 >= 0xe000) { | |
| a2 = e.heapdata[(a1 & 0x1fff) + 0]; | |
| if(a2 >= 0x8000) { | |
| v = a2; | |
| } else { | |
| a3 = e.heapdata[(a1 & 0x1fff) + 1]; | |
| v = prepend_chars(a2, a3); | |
| } | |
| } else { | |
| fail(); | |
| break; | |
| } | |
| store(e.code[e.inst++], v); | |
| break; | |
| case 0x9f: // join_words value dest | |
| a1 = deref(fvalue()); | |
| if((a1 & 0xe000) != 0xc000) { | |
| fail(); | |
| break; | |
| } | |
| a2 = deref(e.heapdata[(a1 & 0x1fff) + 0]); | |
| if((a2 & 0xff00) == 0x3e00) { | |
| a3 = deref(e.heapdata[(a1 & 0x1fff) + 1]); | |
| if(a3 == 0x3f00) { | |
| store(e.code[e.inst++], a2); | |
| break; | |
| } | |
| } | |
| tmp = words_to_charlist(a1); | |
| if(tmp) { | |
| store(e.code[e.inst++], parse_word(tmp, e)); | |
| } else { | |
| fail(); | |
| } | |
| break; | |
| case 0x20: case 0xa0: // load_word value/0 index dest | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| store(e.code[e.inst++], readfield(a2, a1)); | |
| break; | |
| case 0x21: case 0xa1: // load_byte value/0 index dest | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| v = readfield(a2 >> 1, a1); | |
| store(e.code[e.inst++], (a2 & 1)? (v & 0xff) : (v >> 8)); | |
| break; | |
| case 0x22: case 0xa2: // load_val value/0 index dest | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| v = get_lts(readfield(a2, a1)); | |
| if(v) { | |
| store(e.code[e.inst++], v); | |
| } else { | |
| fail(); | |
| } | |
| break; | |
| case 0x24: case 0xa4: // store_word value/0 index value | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| e.ramdata[fieldaddr(a2, a1)] = fvalue(); | |
| break; | |
| case 0x25: case 0xa5: // store_byte value/0 index value | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| a3 = fvalue(); | |
| addr = fieldaddr(a2 >> 1, a1); | |
| if(a2 & 1) { | |
| e.ramdata[addr] = (e.ramdata[addr] & 0xff00) | (a3 & 0xff); | |
| } else { | |
| e.ramdata[addr] = (e.ramdata[addr] & 0x00ff) | ((a3 & 0xff) << 8); | |
| } | |
| break; | |
| case 0x26: case 0xa6: // store_val value/0 index value | |
| a1 = (op & 0x80)? 0 : deref(fvalue()); | |
| a2 = findex(); | |
| a3 = fvalue(); | |
| if(a1 <= e.nob || a3) { | |
| put_lts(fieldaddr(a2, a1), a3); | |
| } | |
| break; | |
| case 0x28: case 0xa8: // set_flag value/0 index | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| e.ramdata[fieldaddr(a2 >> 4, a1)] |= 0x8000 >> (a2 & 15); | |
| break; | |
| case 0x29: case 0xa9: // reset_flag value/0 index | |
| a1 = (op & 0x80)? 0 : deref(fvalue()); | |
| a2 = findex(); | |
| if(a1 <= e.nob) { | |
| e.ramdata[fieldaddr(a2 >> 4, a1)] &= ~(0x8000 >> (a2 & 15)); | |
| } | |
| break; | |
| case 0x2d: case 0xad: // unlink value/0 index index value | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| a3 = findex(); | |
| unlink(fieldaddr(a2, a1), a3, deref(fvalue())); | |
| break; | |
| case 0x2e: case 0x2f: case 0xae: case 0xaf: // set_parent value/vbyte value/vbyte | |
| a1 = (op & 0x80)? e.code[e.inst++] : deref(fvalue()); | |
| a2 = (op & 0x01)? e.code[e.inst++] : deref(fvalue()); | |
| if(a1 < e.nob || a2) { | |
| if(a1 >= 0x2000 || a2 >= 0x2000) throw EXPECTOBJ; | |
| if((v = e.ramdata[fieldaddr(0, a1)])) { | |
| unlink(fieldaddr(1, v), 2, a1); | |
| } | |
| e.ramdata[fieldaddr(0, a1)] = a2; | |
| if(a2) { | |
| e.ramdata[fieldaddr(2, a1)] = e.ramdata[fieldaddr(1, a2)]; | |
| e.ramdata[fieldaddr(1, a2)] = a1; | |
| } | |
| } | |
| break; | |
| case 0x30: case 0xb0: // if_raw_eq word/0 value code | |
| a1 = (op & 0x80)? 0 : fword(); | |
| a2 = fvalue(); | |
| a3 = fcode(); | |
| if(a1 == a2) { | |
| e.inst = a3; | |
| } | |
| break; | |
| case 0x31: // if_bound value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if((a1 & 0xe000) != 0x8000) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x32: // if_empty value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if(a1 == 0x3f00) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x33: // if_num value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if(a1 >= 0x4000 && a1 < 0x8000) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x34: // if_pair value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if((a1 & 0xe000) == 0xc000) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x35: // if_obj value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if(a1 < 0x2000) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x36: // if_word value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if(a1 >= 0xe000 || (a1 >= 0x2000 && a1 < 0x3f00)) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0xb6: // if_listword value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if(a1 >= 0xe000 && ((e.heapdata[a1 & 0x1fff] & 0xe000) == 0xc000)) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x37: // if_unify value value code | |
| a1 = fvalue(); | |
| a2 = fvalue(); | |
| a3 = fcode(); | |
| if(would_unify(a1, a2)) { | |
| e.inst = a3; | |
| } | |
| break; | |
| case 0x38: // if_gt value value code | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| a3 = fcode(); | |
| if(a1 >= 0x4000 && a1 < 0x8000 && a2 >= 0x4000 && a2 < 0x8000 && a1 > a2) { | |
| e.inst = a3; | |
| } | |
| break; | |
| case 0x39: case 0xb9: // if_eq word/vbyte value code | |
| a1 = (op & 0x80)? e.code[e.inst++] : fword(); | |
| a2 = fvalue(); | |
| a3 = fcode(); | |
| if(a1 == deref(a2)) { | |
| e.inst = a3; | |
| } | |
| break; | |
| case 0x3a: case 0xba: // if_mem_eq value/0 index value code | |
| case 0x3d: case 0xbd: // if_mem_eq value/0 index vbyte code | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| a3 = (op & 1)? e.code[e.inst++] : fvalue(); | |
| a4 = fcode(); | |
| if(readfield(a2, a1) == a3) { | |
| e.inst = a4; | |
| } | |
| break; | |
| case 0x3b: case 0xbb: // if_flag value/0 index code | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| a3 = fcode(); | |
| if(readfield(a2 >> 4, a1) & (0x8000 >> (a2 & 15))) { | |
| e.inst = a3; | |
| } | |
| break; | |
| case 0x3c: // if_cwl code | |
| a1 = fcode(); | |
| if(e.cwl) e.inst = a1; | |
| break; | |
| case 0x3d: case 0xbd: // if_mem_eq value/0 index vbyte code | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| a3 = | |
| a4 = fcode(); | |
| if(readfield(a2, a1) == a3) { | |
| e.inst = a4; | |
| } | |
| break; | |
| case 0x40: case 0xc0: // ifn_raw_eq word/0 value code | |
| a1 = (op & 0x80)? 0 : fword(); | |
| a2 = fvalue(); | |
| a3 = fcode(); | |
| if(a1 != a2) { | |
| e.inst = a3; | |
| } | |
| break; | |
| case 0x41: // ifn_bound value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if((a1 & 0xe000) == 0x8000) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x42: // ifn_empty value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if(a1 != 0x3f00) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x43: // ifn_num value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if(a1 < 0x4000 || a1 >= 0x8000) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x44: // ifn_pair value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if((a1 & 0xe000) != 0xc000) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x45: // ifn_obj value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if(a1 >= 0x2000) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x46: // ifn_word value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if(a1 < 0xe000 && (a1 < 0x2000 || a1 >= 0x3f00)) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0xc6: // ifn_listword value code | |
| a1 = deref(fvalue()); | |
| a2 = fcode(); | |
| if(a1 < 0xe000 || ((e.heapdata[a1 & 0x1fff] & 0xe000) != 0xc000)) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x47: // ifn_unify value value code | |
| a1 = fvalue(); | |
| a2 = fvalue(); | |
| a3 = fcode(); | |
| if(!would_unify(a1, a2)) { | |
| e.inst = a3; | |
| } | |
| break; | |
| case 0x48: // ifn_gt value value code | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| a3 = fcode(); | |
| if(!(a1 >= 0x4000 && a1 < 0x8000 && a2 >= 0x4000 && a2 < 0x8000 && a1 > a2)) { | |
| e.inst = a3; | |
| } | |
| break; | |
| case 0x49: case 0xc9: // ifn_eq word/vbyte value code | |
| a1 = (op & 0x80)? e.code[e.inst++] : fword(); | |
| a2 = fvalue(); | |
| a3 = fcode(); | |
| if(a1 != deref(a2)) { | |
| e.inst = a3; | |
| } | |
| break; | |
| case 0x4a: case 0xca: // ifn_mem_eq value/0 index value code | |
| case 0x4d: case 0xcd: // ifn_mem_eq value/0 index vbyte code | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| a3 = (op & 1)? e.code[e.inst++] : fvalue(); | |
| a4 = fcode(); | |
| if(readfield(a2, a1) != a3) { | |
| e.inst = a4; | |
| } | |
| break; | |
| case 0x4b: case 0xcb: // ifn_flag value/0 index code | |
| a1 = (op & 0x80)? 0 : fvalue(); | |
| a2 = findex(); | |
| a3 = fcode(); | |
| if(!(readfield(a2 >> 4, a1) & (0x8000 >> (a2 & 15)))) { | |
| e.inst = a3; | |
| } | |
| break; | |
| case 0x4c: // ifn_cwl code | |
| a1 = fcode(); | |
| if(!e.cwl) e.inst = a1; | |
| break; | |
| case 0x50: // add_raw value value dest | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| store(e.code[e.inst++], (a1 + a2) & 0xffff); | |
| break; | |
| case 0xd0: // inc_raw value dest | |
| a1 = deref(fvalue()); | |
| store(e.code[e.inst++], (a1 + 1) & 0xffff); | |
| break; | |
| case 0x51: // sub_raw value value dest | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| store(e.code[e.inst++], (a1 - a2) & 0xffff); | |
| break; | |
| case 0xd1: // dec_raw value dest | |
| a1 = deref(fvalue()); | |
| store(e.code[e.inst++], (a1 - 1) & 0xffff); | |
| break; | |
| case 0x52: // rand_raw byte dest | |
| a1 = e.code[e.inst++]; | |
| store(e.code[e.inst++], compat_rand() % (a1 + 1)); | |
| break; | |
| case 0x58: // add_num value value dest | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| if(a1 >= 0x4000 && a1 < 0x8000 && a2 >= 0x4000 && a2 < 0x8000) { | |
| v = (a1 & 0x3fff) + (a2 & 0x3fff); | |
| if(v < 0x4000) { | |
| store(e.code[e.inst++], v | 0x4000); | |
| } else fail(); | |
| } else fail(); | |
| break; | |
| case 0xd8: // inc_num value dest | |
| a1 = deref(fvalue()); | |
| if(a1 >= 0x4000 && a1 < 0x7fff) { | |
| store(e.code[e.inst++], a1 + 1); | |
| } else fail(); | |
| break; | |
| case 0x59: // sub_num value value dest | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| if(a1 >= 0x4000 && a1 < 0x8000 && a2 >= 0x4000 && a2 < 0x8000) { | |
| v = (a1 & 0x3fff) - (a2 & 0x3fff); | |
| if(v >= 0) { | |
| store(e.code[e.inst++], v | 0x4000); | |
| } else fail(); | |
| } else fail(); | |
| break; | |
| case 0xd9: // dec_num value dest | |
| a1 = deref(fvalue()); | |
| if(a1 > 0x4000 && a1 < 0x8000) { | |
| store(e.code[e.inst++], a1 - 1); | |
| } else fail(); | |
| break; | |
| case 0x5a: // rand_num value value dest | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| if(a1 >= 0x4000 && a1 < 0x8000 && a2 >= 0x4000 && a2 < 0x8000 && a2 >= a1) { | |
| v = a1 + (compat_rand() % (a2 - a1 + 1)); | |
| store(e.code[e.inst++], v); | |
| } else fail(); | |
| break; | |
| case 0x5b: // mul_num value value dest | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| if(a1 >= 0x4000 && a1 < 0x8000 && a2 >= 0x4000 && a2 < 0x8000) { | |
| v = ((a1 & 0x3fff) * (a2 & 0x3fff)) & 0x3fff; | |
| store(e.code[e.inst++], v | 0x4000); | |
| } else fail(); | |
| break; | |
| case 0x5c: // div_num value value dest | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| if(a1 >= 0x4000 && a1 < 0x8000 && a2 > 0x4000 && a2 < 0x8000) { | |
| v = (a1 & 0x3fff) / (a2 & 0x3fff); | |
| store(e.code[e.inst++], v | 0x4000); // bitwise-or truncates the float | |
| } else fail(); | |
| break; | |
| case 0x5d: // mod_num value value dest | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| if(a1 >= 0x4000 && a1 < 0x8000 && a2 > 0x4000 && a2 < 0x8000) { | |
| v = (a1 & 0x3fff) % (a2 & 0x3fff); | |
| store(e.code[e.inst++], v | 0x4000); | |
| } else fail(); | |
| break; | |
| case 0x60: // print_a_str_a string | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) io.space(); | |
| io.print(decodestr(e, fstring())); | |
| e.spc = e.SP_AUTO; | |
| break; | |
| case 0xe0: // print_n_str_a string | |
| if(e.spc == e.SP_PENDING) io.space(); | |
| io.print(decodestr(e, fstring())); | |
| e.spc = e.SP_AUTO; | |
| break; | |
| case 0x61: // print_a_str_n string | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) io.space(); | |
| io.print(decodestr(e, fstring())); | |
| e.spc = e.SP_NOSPACE; | |
| break; | |
| case 0xe1: // print_n_str_n string | |
| if(e.spc == e.SP_PENDING) io.space(); | |
| io.print(decodestr(e, fstring())); | |
| e.spc = e.SP_NOSPACE; | |
| break; | |
| case 0x62: // nospace | |
| if(!e.cwl) { | |
| if(e.spc < e.SP_NOSPACE) { | |
| e.spc = e.SP_NOSPACE; | |
| } | |
| } | |
| break; | |
| case 0xe2: // space | |
| if(!e.cwl) { | |
| if(e.spc < e.SP_PENDING) { | |
| e.spc = e.SP_PENDING; | |
| } | |
| } | |
| break; | |
| case 0x63: // line | |
| if(!e.cwl) { | |
| if(e.spc < e.SP_LINE) { | |
| io.line(); | |
| e.spc = e.SP_LINE; | |
| } | |
| } | |
| break; | |
| case 0xe3: // par | |
| if(!e.cwl) { | |
| if(e.spc < e.SP_PAR) { | |
| if(e.n_span) { | |
| io.line(); | |
| io.line(); | |
| } else { | |
| io.par(); | |
| } | |
| e.spc = e.SP_PAR; | |
| } | |
| } | |
| break; | |
| case 0x64: // space_n value | |
| a1 = deref(fvalue()); | |
| if(!e.cwl && a1 > 0x4000 && a1 < 0x8000) { | |
| io.space_n(a1 & 0x3fff); | |
| e.spc = e.SP_SPACE; | |
| } | |
| break; | |
| case 0x65: // print_val value | |
| a1 = deref(fvalue()); | |
| if(e.cwl) { | |
| push_aux(a1); | |
| } else { | |
| if((a1 & 0xff00) == 0x3e00) { | |
| tmp = a1 & 0xff; | |
| if(e.spc == e.SP_PENDING || (e.spc == e.SP_AUTO && !e.nospcbefore.includes(tmp))) { | |
| io.space(); | |
| } | |
| io.print(decodechar(e, tmp)); | |
| if(e.nospcafter.includes(tmp)) { | |
| e.spc = e.SP_NOSPACE; | |
| } else { | |
| e.spc = e.SP_AUTO; | |
| } | |
| } else { | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) io.space(); | |
| io.print(val2str(a1)); | |
| e.spc = e.SP_AUTO; | |
| } | |
| } | |
| break; | |
| case 0x66: // enter_div index | |
| a1 = findex(); | |
| if(!e.cwl) { | |
| if(e.n_span) throw IOSTATE; | |
| io.enter_div(a1); | |
| e.divs.push(a1); | |
| e.spc = e.SP_PAR; | |
| } | |
| break; | |
| case 0xe6: // leave_div | |
| if(!e.cwl) { | |
| io.leave_div(e.divs.pop()); | |
| e.spc = e.SP_PAR; | |
| } | |
| break; | |
| case 0x67: // enter_status 0 index | |
| a1 = findex(); | |
| if(!e.cwl) { | |
| if(e.in_status || e.n_span) { | |
| throw IOSTATE; | |
| } | |
| io.enter_status(0, a1); | |
| e.in_status = a1; | |
| e.spc = e.SP_PAR; | |
| } | |
| break; | |
| case 0xe7: // leave_status | |
| if(!e.cwl) { | |
| io.leave_status(); | |
| e.in_status = null; | |
| e.spc = e.SP_PAR; | |
| } | |
| break; | |
| case 0x68: // enter_link_res value | |
| a1 = deref(fvalue()); | |
| if(!e.cwl) { | |
| if(!e.n_link) { | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) { | |
| io.space(); | |
| } | |
| io.enter_link_res(get_res(a1 & 0x1fff)); | |
| e.spc = e.SP_NOSPACE; | |
| } | |
| e.n_link++; | |
| e.n_span++; | |
| } | |
| break; | |
| case 0xe8: // leave_link_res | |
| if(!e.cwl) { | |
| e.n_link--; | |
| e.n_span--; | |
| if(!e.n_link) io.leave_link_res(); | |
| } | |
| break; | |
| case 0x69: // enter_link value | |
| a1 = deref(fvalue()); | |
| if(!e.cwl) { | |
| if(!e.n_link) { | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) { | |
| io.space(); | |
| } | |
| i = e.upper; | |
| e.upper = false; | |
| str = ""; | |
| while((a1 & 0xe000) == 0xc000) { | |
| v = deref(a1 - 0x4000); | |
| if((v >= 0x2000 && v < 0x8000) || v >= 0xe000) { | |
| if(str) str += " "; | |
| str += val2str(v); | |
| } | |
| a1 = deref(a1 - 0x3fff); | |
| } | |
| io.enter_link(str); | |
| e.upper = i; | |
| e.spc = e.SP_NOSPACE; | |
| } | |
| e.n_link++; | |
| e.n_span++; | |
| } | |
| break; | |
| case 0xe9: // leave_link | |
| if(!e.cwl) { | |
| e.n_link--; | |
| e.n_span--; | |
| if(!e.n_link) io.leave_link(); | |
| } | |
| break; | |
| case 0x6a: // enter_self_link | |
| if(!e.cwl) { | |
| if(!e.n_link) { | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) { | |
| io.space(); | |
| } | |
| io.enter_self_link(str); | |
| e.spc = e.SP_NOSPACE; | |
| } | |
| e.n_link++; | |
| e.n_span++; | |
| } | |
| break; | |
| case 0xea: // leave_self_link | |
| if(!e.cwl) { | |
| e.n_link--; | |
| e.n_span--; | |
| if(!e.n_link) io.leave_self_link(); | |
| } | |
| break; | |
| case 0x6b: // set_style byte | |
| a1 = e.code[e.inst++]; | |
| if(!e.cwl) { | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) io.space(); | |
| io.setstyle(a1); | |
| e.spc = e.SP_SPACE; | |
| } | |
| break; | |
| case 0xeb: // reset_style byte | |
| a1 = e.code[e.inst++]; | |
| if(!e.cwl) { | |
| io.resetstyle(a1); | |
| } | |
| break; | |
| case 0x6c: // embed_res value | |
| a1 = deref(fvalue()); | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) io.space(); | |
| io.embed_res(get_res(a1 & 0x1fff)); | |
| e.spc = e.SP_AUTO; | |
| break; | |
| case 0xec: // can_embed_res value dest | |
| a1 = deref(fvalue()); | |
| store(e.code[e.inst++], io.can_embed_res(get_res(a1 & 0x1fff))? 1 : 0); | |
| break; | |
| case 0x6d: // progress value value | |
| a1 = deref(fvalue()); | |
| a2 = deref(fvalue()); | |
| if(!e.cwl) { | |
| if(a1 >= 0x4000 && a1 < 0x8000 && a2 >= 0x4000 && a2 < 0x8000) { | |
| io.progressbar(a1 & 0x3fff, a2 & 0x3fff); | |
| } | |
| } | |
| break; | |
| case 0x6e: // enter_span index | |
| a1 = findex(); | |
| if(!e.cwl) { | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) io.space(); | |
| io.enter_span(a1); | |
| e.n_span++; | |
| e.spc = e.SP_NOSPACE; | |
| } | |
| break; | |
| case 0xee: // leave_span | |
| if(!e.cwl) { | |
| io.leave_span(); | |
| e.n_span--; | |
| e.spc = e.SP_AUTO; | |
| } | |
| break; | |
| case 0x6f: // enter_status byte index | |
| a1 = e.code[e.inst++]; | |
| a2 = findex(); | |
| if(!e.cwl) { | |
| if(e.in_status || e.n_span) { | |
| throw IOSTATE; | |
| } | |
| io.enter_status(a1, a2); | |
| e.in_status = a2; | |
| e.spc = e.SP_PAR; | |
| } | |
| break; | |
| case 0x70: // ext0 byte | |
| a1 = e.code[e.inst++]; | |
| switch(a1) { | |
| case 0x00: // quit | |
| io.flush(); | |
| return status.quit; | |
| case 0x01: // restart | |
| vm_clear_divs(e); | |
| vm_reset(e, 0, true); | |
| vm_restore_state(e, e.initstate); | |
| io.reset(); | |
| break; | |
| case 0x02: // restore | |
| io.flush(); | |
| io.restore(); | |
| return status.restore; | |
| case 0x03: // undo | |
| if(e.undodata.length) { | |
| vm_clear_divs(e); | |
| vm_restore_state(e, vm_rldec_state(e.initstate, e.undodata.pop())); | |
| } else if(!e.pruned_undo) { | |
| fail(); | |
| } | |
| break; | |
| case 0x04: // unstyle | |
| if(!e.cwl) { | |
| io.unstyle(); | |
| } | |
| break; | |
| case 0x05: // print_serial | |
| if(!e.cwl) { | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) io.space(); | |
| for(i = 0; i < 6; i++) { | |
| io.print(String.fromCharCode(e.head[6 + i])); | |
| } | |
| e.spc = e.SP_AUTO; | |
| } | |
| break; | |
| case 0x06: // clear | |
| case 0x07: // clear_all | |
| if(e.in_status || e.n_span) throw IOSTATE; | |
| tmp = e.divs; | |
| vm_clear_divs(e); | |
| if(a1 == 0x06) { | |
| io.clear(); | |
| } else { | |
| io.clear_all(); | |
| } | |
| for(i = 0; i < tmp.length; i++) { | |
| io.enter_div(tmp[i]); | |
| } | |
| e.divs = tmp; | |
| break; | |
| case 0x08: // script_on | |
| if(!io.script_on()) { | |
| fail(); | |
| } | |
| break; | |
| case 0x09: // script_off | |
| io.script_off(); | |
| break; | |
| case 0x0a: // trace_on | |
| e.trace = true; | |
| break; | |
| case 0x0b: // trace_off | |
| e.trace = false; | |
| break; | |
| case 0x0c: // inc_cwl | |
| e.cwl++; | |
| break; | |
| case 0x0d: // dec_cwl | |
| e.cwl--; | |
| break; | |
| case 0x0e: // uppercase | |
| if(!e.cwl) { | |
| e.upper = true; | |
| } | |
| break; | |
| case 0x0f: // clear_links | |
| io.clear_links(); | |
| break; | |
| case 0x10: // clear_old | |
| if(e.n_span) { | |
| throw IOSTATE; | |
| } | |
| io.clear_old(); | |
| break; | |
| case 0x11: // clear_div | |
| io.clear_div(); | |
| break; | |
| default: | |
| throw 'Unimplemented ext0 ' + a1.toString(16) + ' at ' + (e.inst - 2).toString(16); | |
| } | |
| break; | |
| case 0x72: // save code | |
| a1 = fcode(); | |
| if(e.in_status || e.n_span) { | |
| throw IOSTATE; | |
| } | |
| if(!io.save(vm_wrap_savefile(e, vm_rlenc_state(e.initstate, vm_capture_state(e, a1))))) { | |
| fail(); | |
| } | |
| break; | |
| case 0xf2: // save_undo code | |
| a1 = fcode(); | |
| if(e.in_status || e.n_span) { | |
| throw IOSTATE; | |
| } | |
| if(e.undodata.length > 50) { | |
| e.undodata = e.undodata.slice(1); | |
| e.pruned_undo = true; | |
| } | |
| e.undodata.push(vm_rlenc_state(e.initstate, vm_capture_state(e, a1))); | |
| break; | |
| case 0x73: // get_input dest | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) io.space(); | |
| io.flush(); | |
| return status.get_input; | |
| case 0xf3: // get_key dest | |
| if(e.spc == e.SP_AUTO || e.spc == e.SP_PENDING) io.space(); | |
| io.flush(); | |
| return status.get_key; | |
| case 0x74: // vm_info byte dest | |
| a1 = e.code[e.inst++]; | |
| v = 0; | |
| switch(a1) { | |
| case 0x00: // peak heap | |
| for(i = 0, v = 0x4000; i < e.heapdata.length; i++) { | |
| if(e.heapdata[i] != 0x3f3f) v++; | |
| } | |
| break; | |
| case 0x01: // peak aux | |
| for(i = 0, v = 0x4000; i < e.auxdata.length; i++) { | |
| if(e.auxdata[i] != 0x3f3f) v++; | |
| } | |
| break; | |
| case 0x02: // peak lts | |
| for(i = e.ltb, v = 0x4000; i < e.ramdata.length; i++) { | |
| if(e.ramdata[i] != 0x3f3f) v++; | |
| } | |
| break; | |
| case 0x40: // interpreter supports undo | |
| v = 1; | |
| break; | |
| case 0x41: // interpreter supports save/restore | |
| v = 1; | |
| break; | |
| case 0x42: // interpreter supports links | |
| v = io.have_links()? 1 : 0; | |
| break; | |
| case 0x43: // interpreter supports quit | |
| v = e.havequit? 1 : 0; | |
| break; | |
| case 0x60: // interpreter supports top status area | |
| v = e.havetop? 1 : 0; | |
| break; | |
| case 0x61: // interpreter supports inline status area | |
| v = e.haveinline? 1 : 0; | |
| break; | |
| default: | |
| if(a1 < 0x40) { | |
| throw 'Unimplemented vminfo ' + a1.toString(16) + ' at ' + (e.inst - 2).toString(16); | |
| } | |
| } | |
| store(e.code[e.inst++], v); | |
| break; | |
| case 0x78: // set_idx value | |
| v = deref(fvalue()); | |
| if(v >= 0xe000) v = e.heapdata[v & 0x1fff]; | |
| e.reg[0x3f] = v; | |
| break; | |
| case 0x79: case 0xf9: // check_eq word/vbyte code | |
| a1 = (op & 0x80)? e.code[e.inst++] : fword(); | |
| a2 = fcode(); | |
| if(e.reg[0x3f] == a1) e.inst = a2; | |
| break; | |
| case 0x7a: case 0xfa: // check_gt_eq word/vbyte code code | |
| a1 = (op & 0x80)? e.code[e.inst++] : fword(); | |
| a2 = fcode(); | |
| a3 = fcode(); | |
| if(e.reg[0x3f] > a1) { | |
| e.inst = a2; | |
| } else if(e.reg[0x3f] == a1) { | |
| e.inst = a3; | |
| } | |
| break; | |
| case 0x7b: case 0xfb: // check_gt value/byte code | |
| a1 = (op & 0x80)? e.code[e.inst++] : fvalue(); | |
| a2 = fcode(); | |
| if(e.reg[0x3f] > a1) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x7c: // check_wordmap index code | |
| a1 = findex(); | |
| a2 = fcode(); | |
| if(wordmap(a1, e.reg[0x3f])) { | |
| e.inst = a2; | |
| } | |
| break; | |
| case 0x7d: case 0xfd: // check_eq word/vbyte word/vbyte code | |
| a1 = (op & 0x80)? e.code[e.inst++] : fword(); | |
| a2 = (op & 0x80)? e.code[e.inst++] : fword(); | |
| a3 = fcode(); | |
| if(e.reg[0x3f] == a1 || e.reg[0x3f] == a2) e.inst = a3; | |
| break; | |
| case 0x7f: // tracepoint string string string word | |
| a1 = fstring(); | |
| a2 = fstring(); | |
| a3 = fstring(); | |
| a4 = fword(); | |
| if(e.trace) { | |
| str = decodestr(e, a1) + "("; | |
| a2 = decodestr(e, a2); | |
| j = 0; | |
| for(i = 0; i < a2.length; i++) { | |
| if(a2[i] == '$') { | |
| str += val2str(e.reg[j++]); | |
| } else { | |
| str += a2[i]; | |
| } | |
| } | |
| str += ") " + decodestr(e, a3) + ":" + a4; | |
| io.trace(str); | |
| } | |
| break; | |
| default: | |
| throw 'Unimplemented op ' + op.toString(16) + ' at ' + (e.inst - 1).toString(16); | |
| } | |
| } | |
| } catch(x) { | |
| if(x > 0x4000 && x < 0x8000) { | |
| if(e.spc < e.SP_LINE) { | |
| io.line(); | |
| } | |
| vm_clear_divs(e); | |
| vm_reset(e, x, false); | |
| } else { | |
| throw x; | |
| } | |
| } | |
| } | |
| } | |
| function parse_word(chars, e) { | |
| var state = 0; | |
| var rev_ending = []; | |
| var v, i, instr, next; | |
| var len = chars.length; | |
| var enddecoder = get16(e.lang, 4); | |
| function buildlist(list) { | |
| var i, v = 0x3f00, ch; | |
| for(i = 0; i < list.length; i++) { | |
| ch = list[i]; | |
| if(ch >= 0x30 && ch <= 0x39) { | |
| v = e.create_pair(ch + 0x4000 - 0x30, v); | |
| } else { | |
| v = e.create_pair(0x3e00 | ch, v); | |
| } | |
| } | |
| return v; | |
| } | |
| function finddict() { | |
| var start = 0; | |
| var end = get16(e.dict, 0); | |
| var diff, i, mid, dictlen, dictoffs; | |
| while(start < end) { | |
| mid = (start + end) >> 1; | |
| dictlen = e.dict[2 + 3 * mid]; | |
| dictoffs = get16(e.dict, 2 + 3 * mid + 1); | |
| for(i = 0; i < len && i < dictlen; i++) { | |
| diff = chars[i] - e.dict[dictoffs + i]; | |
| if(diff) break; | |
| } | |
| if(i == dictlen && i == len) { | |
| if(!diff) { | |
| return 0x2000 | mid; | |
| } | |
| } else if(i == dictlen) { | |
| diff = 1; | |
| } else if(i == len) { | |
| diff = -1; | |
| } | |
| if(diff < 0) { | |
| end = mid; | |
| } else { | |
| start = mid + 1; | |
| } | |
| } | |
| return 0; | |
| } | |
| if(len > 1 && (v = finddict())) { | |
| return v; | |
| } | |
| v = 0; | |
| for(i = 0; i < chars.length; i++) { | |
| if(chars[i] < 0x30 || chars[i] > 0x39) break; | |
| v = v * 10 + chars[i] - 0x30; | |
| if(v >= 16384) break; | |
| } | |
| if(i == chars.length) { | |
| return 0x4000 | v; | |
| } | |
| if(len == 1) { | |
| return 0x3e00 | chars[0]; | |
| } | |
| while(true) { | |
| instr = e.lang[enddecoder + state++]; | |
| if(!instr) { | |
| while(len) rev_ending.push(chars[--len]); | |
| return e.create_pair(buildlist(rev_ending), 0x3f00) | 0xe000; | |
| } else if(instr == 1) { | |
| if((v = finddict())) { | |
| return e.create_pair(v, buildlist(rev_ending)) | 0xe000; | |
| } | |
| } else { | |
| next = e.lang[enddecoder + state++]; | |
| if(len > 2 && instr == chars[len - 1]) { | |
| rev_ending.push(instr); | |
| len--; | |
| state = next; | |
| } | |
| } | |
| } | |
| } | |
| function vm_proceed_with_input(e, str) { | |
| var words = []; | |
| var i, j, start, v, uchar, entry; | |
| var chars = new Uint8Array(str.length); | |
| for(i = 0; i < str.length; i++) { | |
| uchar = str.charCodeAt(i); | |
| if(uchar >= 0x41 && uchar <= 0x5a) { | |
| chars[i] = uchar ^ 0x20; | |
| } else if(uchar < 0x80) { | |
| chars[i] = uchar; | |
| } else { | |
| chars[i] = 0x3f; | |
| for(j = e.lang[e.extchars] - 1; j >= 0; j--) { | |
| entry = e.extchars + 1 + j * 5; | |
| if(e.lang[entry + 2] == ((uchar >> 16) & 0xff) && | |
| e.lang[entry + 3] == ((uchar >> 8) & 0xff) && | |
| e.lang[entry + 4] == (uchar & 0xff)) { | |
| chars[i] = e.lang[entry]; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| start = 0; | |
| for(i = 0; i < str.length; i++) { | |
| if(chars[i] == 32) { | |
| if(i != start) words.push(chars.slice(start, i)); | |
| start = i + 1; | |
| } else { | |
| if(e.stopchars.includes(chars[i])) { | |
| if(i != start) words.push(chars.slice(start, i)); | |
| words.push(chars.slice(i, i + 1)); | |
| start = i + 1; | |
| } | |
| } | |
| } | |
| if(i != start) words.push(chars.slice(start, i)); | |
| v = 0x3f00; | |
| try { | |
| for(i = words.length - 1; i >= 0; i--) { | |
| v = e.create_pair(parse_word(words[i], e), v); | |
| } | |
| } catch(x) { | |
| if(x == HEAPFULL) { | |
| if(e.spc < e.SP_LINE) { | |
| e.io.line(); | |
| } | |
| vm_clear_divs(e); | |
| vm_reset(e, x, false); | |
| v = null; | |
| } else { | |
| throw x; | |
| } | |
| } | |
| e.spc = e.SP_LINE; | |
| return vm_run(e, v); | |
| } | |
| function vm_proceed_with_key(e, code) { | |
| var v, i, entry; | |
| if(code >= 0x20 && code < 0x7f) { | |
| if(code >= 0x41 && code <= 0x5a) code ^= 0x20; | |
| v = code; | |
| } | |
| if(!v) { | |
| for(i in keys) { | |
| if(keys.hasOwnProperty(i) && code == keys[i]) { | |
| v = code; | |
| break; | |
| } | |
| } | |
| } | |
| if(!v) { | |
| for(i = 0; i < e.lang[e.extchars]; i++) { | |
| entry = 1 + i * 5; | |
| if(code == (e.lang[entry + 2] << 16) | | |
| (e.lang[entry + 3] << 8) | | |
| e.lang[entry + 4]) | |
| { | |
| break; | |
| } | |
| } | |
| if(i < e.lang[e.extchars]) { | |
| v = 0x80 | e.lang[1 + i * 5]; | |
| } | |
| } | |
| if(!v) { | |
| return status.get_key; | |
| } else { | |
| e.spc = e.SP_SPACE; | |
| if(v >= 0x30 && v <= 0x39) { | |
| v += 0x4000 - 0x30; | |
| } else { | |
| v |= 0x3e00; | |
| } | |
| return vm_run(e, v); | |
| } | |
| } | |
| var instance_e; | |
| var aaengine = { | |
| prepare_story: function(file_array, io, seed, quit, toparea, inlinearea) { | |
| instance_e = prepare_story(file_array, io, seed, quit, toparea, inlinearea); | |
| this.keys = keys; | |
| this.status = status; | |
| }, | |
| get_styles: function() { | |
| return get_styles(instance_e); | |
| }, | |
| get_metadata: function() { | |
| return get_metadata(instance_e); | |
| }, | |
| get_file: function(name) { | |
| return instance_e.files[name]; | |
| }, | |
| get_story_key: function() { | |
| var i, str, hex; | |
| str = this.get_metadata().title.replace(/[^a-zA-Z0-9]+/g, "-") + "-"; | |
| for(i = 0; i < 6; i++) str += decodechar(instance_e, instance_e.head[6 + i]); | |
| str += "-"; | |
| for(i = 0; i < 4; i++) { | |
| hex = instance_e.head[12 + i].toString(16); | |
| if(hex.length == 1) hex = "0" + hex; | |
| str += hex; | |
| } | |
| return str; | |
| }, | |
| vm_start: function() { | |
| return vm_run(instance_e, null); | |
| }, | |
| vm_proceed_with_input: function(str) { | |
| return vm_proceed_with_input(instance_e, str); | |
| }, | |
| vm_proceed_with_key: function(charcode) { | |
| return vm_proceed_with_key(instance_e, charcode); | |
| }, | |
| vm_restore: function(filedata) { | |
| var v; | |
| if(filedata && (v = vm_unwrap_savefile(instance_e, filedata))) { | |
| vm_clear_divs(instance_e); | |
| vm_reset(instance_e, 0, true); | |
| vm_restore_state(instance_e, vm_rldec_state(instance_e.initstate, v)); | |
| } | |
| instance_e.spc = instance_e.SP_LINE; | |
| return vm_run(instance_e, null); | |
| }, | |
| async_restart: function() { | |
| vm_clear_divs(instance_e); | |
| vm_reset(instance_e, 0, true); | |
| vm_restore_state(instance_e, instance_e.initstate); | |
| instance_e.io.reset(); | |
| return vm_run(instance_e, null); | |
| }, | |
| async_save: function(st) { | |
| var state = vm_capture_state(instance_e, instance_e.inst - ((st == status.quit)? 2 : 1)); | |
| return vm_wrap_savefile(instance_e, vm_rlenc_state(instance_e.initstate, state)); | |
| }, | |
| async_restore: function(filedata) { | |
| var v; | |
| v = vm_unwrap_savefile(instance_e, filedata); | |
| vm_reset(instance_e, 0, true); | |
| vm_restore_state(instance_e, vm_rldec_state(instance_e.initstate, v)); | |
| instance_e.spc = instance_e.SP_LINE; | |
| }, | |
| async_resume: function() { | |
| return vm_run(instance_e, null); | |
| }, | |
| get_undo_array: function() { | |
| return instance_e.undodata.map(function(state) {return vm_wrap_savefile(instance_e, state)}); | |
| }, | |
| set_undo_array: function(arr) { | |
| instance_e.undodata = arr.map(function(wrapped) {return vm_unwrap_savefile(instance_e, wrapped)}); | |
| }, | |
| mem_info: function() { | |
| var h = 0, a = 0, lts = 0, i; | |
| var e = instance_e; | |
| for(i = 0; i < e.heapdata.length; i++) { | |
| if(e.heapdata[i] != 0x3f3f) h++; | |
| } | |
| for(i = 0; i < e.auxdata.length; i++) { | |
| if(e.auxdata[i] != 0x3f3f) a++; | |
| } | |
| for(i = e.ltb; i < e.ramdata.length; i++) { | |
| if(e.ramdata[i] != 0x3f3f) lts++; | |
| } | |
| return { | |
| heap: h, | |
| aux: a, | |
| lts: lts, | |
| heapsize: e.heapdata.length, | |
| auxsize: e.auxdata.length, | |
| ltssize: e.ramdata.length - e.ltb}; | |
| }, | |
| }; | |
| if(typeof module === 'undefined') { | |
| window.aaengine = aaengine; | |
| } else { | |
| module.exports = aaengine; | |
| } | |
| })(); | |
Xet Storage Details
- Size:
- 67.5 kB
- Xet hash:
- 0d1c7cdec4ef6e366b683aebf58e16e6986fb158f672dbc871950fe37f11d7e8
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.