Number.prototype.hex = function(len) { len = (len || 8); var str = (this >>> 0).toString(16).toUpperCase() while (str.length < len) { str = "0" + str } return str } const u8 = 'u8', u16 = 'u16', u32 = 'u32', s8 = 's8', s16 = 's16', s32 = 's32', float = 'float', double = 'double' const _typeSizes = { u8: 1, u16: 2, u32: 4, s8: 1, s16: 2, s32: 4, float: 4, double: 8 } const _regNums = { r0: 0, at: 1, v0: 2, v1: 3, a0: 4, a1: 5, a2: 6, a3: 7, t0: 8, t1: 9, t2: 10, t3: 11, t4: 12, t5: 13, t6: 14, t7: 15, s0: 16, s1: 17, s2: 18, s3: 19, s4: 20, s5: 21, s6: 22, s7: 23, t8: 24, t9: 25, k0: 26, k1: 27, gp: 28, sp: 29, fp: 30, ra: 31, f0: 0, f1: 1, f2: 2, f3: 3, f4: 4, f5: 5, f6: 6, f7: 7, f8: 8, f9: 9, f10: 10, f11: 11, f12: 12, f13: 13, f14: 14, f15: 15, f16: 16, f17: 17, f18: 18, f19: 19, f20: 20, f21: 21, f22: 22, f23: 23, f24: 24, f25: 25, f26: 26, f27: 27, f28: 28, f29: 29, f30: 30, f31: 31 } const _gprNames = [ 'r0', 'at', 'v0', 'v1', 'a0', 'a1', 'a2', 'a3', 't0', 't1', 't2', 't3', 't4', 't5', 't6', 't7', 's0', 's1', 's2', 's3', 's4', 's5', 's6', 's7', 't8', 't9', 'k0', 'k1', 'gp', 'sp', 'fp', 'ra' ] // When we give callbacks or objects to native code, we need to make sure we keep a strong backing reference // to them in JavaScript so they don't get garbage collected before native code uses them const _strongBackingReferences = (function() { const _references = []; const noop = function () { }; return { "add": function _strongBackingReferences_add(obj) { _references.push(obj); }, "remove": function _strongBackingReferences_remove(obj) { const index = _references.indexOf(obj); if (index !== -1) { _references.splice(index, 1); } }, "singleUseCallback": function _strongBackingReferences_singleUseCallback(callback) { if (callback === undefined) { return noop; } const singleUseCallback = function () { callback.apply(undefined, arguments); _strongBackingReferences.remove(callback); }; _strongBackingReferences.add(singleUseCallback); return singleUseCallback; } }; }) (); const GPR_R0 = (1 << 0) const GPR_AT = (1 << 1) const GPR_V0 = (1 << 2) const GPR_V1 = (1 << 3) const GPR_A0 = (1 << 4) const GPR_A1 = (1 << 5) const GPR_A2 = (1 << 6) const GPR_A3 = (1 << 7) const GPR_T0 = (1 << 8) const GPR_T1 = (1 << 9) const GPR_T2 = (1 << 10) const GPR_T3 = (1 << 11) const GPR_T4 = (1 << 12) const GPR_T5 = (1 << 13) const GPR_T6 = (1 << 14) const GPR_T7 = (1 << 15) const GPR_S0 = (1 << 16) const GPR_S1 = (1 << 17) const GPR_S2 = (1 << 18) const GPR_S3 = (1 << 19) const GPR_S4 = (1 << 20) const GPR_S5 = (1 << 21) const GPR_S6 = (1 << 22) const GPR_S7 = (1 << 23) const GPR_T8 = (1 << 24) const GPR_T9 = (1 << 25) const GPR_K0 = (1 << 26) const GPR_K1 = (1 << 27) const GPR_GP = (1 << 28) const GPR_SP = (1 << 29) const GPR_FP = (1 << 30) const GPR_RA = (1 << 31) >>> 0 const GPR_ANY_ARG = (GPR_A0 | GPR_A1 | GPR_A2 | GPR_A3) const GPR_ANY_TEMP = (GPR_T0 | GPR_T1 | GPR_T2 | GPR_T3 | GPR_T4 | GPR_T5 | GPR_T6 | GPR_T7 | GPR_T8 | GPR_T9) const GPR_ANY_SAVE = (GPR_S0 | GPR_S1 | GPR_S2 | GPR_S3 | GPR_S4 | GPR_S5 | GPR_S6 | GPR_S7) const GPR_ANY = (GPR_R0 | GPR_AT | GPR_V0 | GPR_V1 | GPR_ANY_ARG | GPR_ANY_TEMP | GPR_ANY_SAVE | GPR_K0 | GPR_K1 | GPR_GP | GPR_SP | GPR_FP | GPR_RA) >>> 0 function AddressRange(start, end) { this.start = start >>> 0 this.end = end >>> 0 Object.freeze(this) } const ADDR_ANY = new AddressRange(0x00000000, 0xFFFFFFFF) const ADDR_ANY_KUSEG = new AddressRange(0x00000000, 0x7FFFFFFF) const ADDR_ANY_KSEG0 = new AddressRange(0x80000000, 0x9FFFFFFF) const ADDR_ANY_KSEG1 = new AddressRange(0xA0000000, 0xBFFFFFFF) const ADDR_ANY_KSEG2 = new AddressRange(0xC0000000, 0xFFFFFFFF) const ADDR_ANY_RDRAM = new AddressRange(0x80000000, 0x807FFFFF) const ADDR_ANY_RDRAM_UNC = new AddressRange(0xA0000000, 0xA07FFFFF) const ADDR_ANY_CART_ROM = new AddressRange(0x90000000, 0x95FFFFFF) const ADDR_ANY_CART_ROM_UNC = new AddressRange(0xB0000000, 0xB5FFFFFF) const asm = { gprname: function(num) { if(num in _gprNames) { return _gprNames[num] } return "" } } const fs = { open: function(path, mode) { if (["r", "rb", "w", "wb", "a", "ab", "r+", "rb+", "r+b", "w+", "wb+", "w+b", "a+", "ab+", "a+b"].indexOf(mode) == -1) { return false } return _native.fsOpen(path, mode) }, close: function(fd) { return _native.fsClose(fd) }, write: function(fd, buffer, offset, length, position) { return _native.fsWrite(fd, buffer, offset, length, position) }, writeFile: function(path, buffer) { var fd = fs.open(path, 'wb') if (fd == false) { return false } fs.write(fd, buffer) fs.close(fd) return true }, read: function (fd, buffer, offset, length, position) { return _native.fsRead(fd, buffer, offset, length, position) }, readFile: function(path) { var fd = fs.open(path, 'rb') if(fd == false) { return false } var stats = fs.fstat(fd); var buf = new Buffer(stats.size) fs.read(fd, buf, 0, stats.size, 0) return buf }, fstat: function(fd) { var rawStats = _native.fsFStat(fd) if (rawStats == false) { return false } return new fs.Stats(rawStats) }, stat: function(path) { var rawStats = _native.fsStat(path) if (rawStats == false) { return false } return new fs.Stats(rawStats) }, mkdir: function(path) { return _native.fsMkDir(path) }, rmdir: function(path) { return _native.fsRmDir(path) }, readdir: function(path) { return _native.fsReadDir(path) }, unlink: function(path) { return _native.fsUnlink(path) }, Stats: function(rawstats) { this.dev = rawstats.dev this.ino = rawstats.ino this.mode = rawstats.mode this.nlink = rawstats.nlink this.uid = rawstats.uid this.gid = rawstats.gid this.rdev = rawstats.rdev this.size = rawstats.size this.atimeMs = rawstats.atimeMs this.mtimeMs = rawstats.mtimeMs this.ctimeMs = rawstats.ctimeMs this.atime = new Date(this.atimeMs) this.mtime = new Date(this.mtimeMs) this.ctime = new Date(this.ctimeMs) Object.freeze(this) } } fs.Stats.S_IFMT = 0xF000 fs.Stats.S_IFDIR = 0x4000 fs.Stats.S_IFCHR = 0x2000 fs.Stats.S_IFIFO = 0x1000 fs.Stats.S_IFREG = 0x8000 fs.Stats.S_IREAD = 0x0100 fs.Stats.S_IWRITE = 0x0080 fs.Stats.S_IEXEC = 0x0040 fs.Stats.prototype.isDirectory = function() { return ((this.mode & fs.Stats.S_IFMT) == fs.Stats.S_IFDIR) } fs.Stats.prototype.isFile = function() { return ((this.mode & fs.Stats.S_IFMT) == fs.Stats.S_IFREG) } Object.freeze(fs.Stats) Object.freeze(fs.Stats.prototype) const system = { pause: function() { _native.pause() }, resume: function() { }, reset: function() { }, hardreset: function() { }, savestate: function() { }, loadstate: function() { }, setsaveslot: function(slot) { }, getsaveslot: function() { }, generatebitmap: function() { } } const debug = { showmemory: function(address) { }, showcommands: function(address) { _native.showCommands(address) }, breakhere: function() { _native.breakHere() }, disasm: function() { } } const console = { print: function(data) { _native.consolePrint(data); }, log: function() { for(var i in arguments) { console.print(arguments[i].toString()) if(i < arguments.length - 1) { console.print(" ") } } console.print("\r\n") }, clear: function() { _native.consoleClear(); } } const screen = { print: function(x, y, text) { _native.screenPrint(x, y, text) } } const events = (function() { var callbacks = {}; var nextCallbackId = 0; return { on: function(hook, callback, param, param2, bOnce) { this._stashCallback(callback) return _native.addCallback(hook, callback, param, param2, 0, 0, bOnce) }, onexec: function(addr, callback) { var param = 0; var param2 = 0; if (typeof (addr) == "object") { param = addr.start; param2 = addr.end; } else if (typeof (addr) == "number") { param = addr; } return events.on('exec', callback, param, param2) }, onopcode: function (addr, value, arg3, arg4) { // onopcode(addr, value, callback) // onopcode(addr, value, mask, callback) var start = 0; var end = 0; var mask; var callback; if(typeof(addr) == "object") { start = addr.start; end = addr.end; } else if (typeof (addr) == "number") { start = addr; } if (typeof (arg3) == "number") { mask = arg3; callback = arg4; } else if (typeof (arg3) == "function") { mask = 0xFFFFFFFF; callback = arg3; } this._stashCallback(callback); return _native.addCallback('opcode', callback, start, end, value, mask) }, ongprvalue: function(addr, registers, value, callback) { var start = 0; var end = 0; if(typeof(addr) == "object") { start = addr.start; end = addr.end; } else if(typeof(addr) == "number") { start = addr; } this._stashCallback(callback); return _native.addCallback('gprvalue', callback, start, end, registers, value) }, onread: function(addr, callback) { var param = 0; var param2 = 0; if (typeof (addr) == "object") { param = addr.start; param2 = addr.end; } else if (typeof (addr) == "number") { param = addr; } return events.on('read', callback, param, param2) }, onwrite: function(addr, callback) { var param = 0; var param2 = 0; if (typeof (addr) == "object") { param = addr.start; param2 = addr.end; } else if (typeof (addr) == "number") { param = addr; } return events.on('write', callback, param, param2) }, ondraw: function(callback) { return events.on('draw', callback, 0) }, remove: function(callbackId) { _native.removeCallback(callbackId) }, clear: function(){}, _stashCallback: function(callback) { callbacks[nextCallbackId] = callback return nextCallbackId++; }, _unstashCallback: function() { }, } })(); const gpr = new Proxy({}, { get: function(obj, prop) { if (typeof prop == 'number' && prop < 32) { return _native.getGPRVal(prop) } if (prop in _regNums) { return _native.getGPRVal(_regNums[prop]) } switch(prop) { case 'pc': return _native.getPCVal(); break; case 'hi': return _native.getHIVal(false); break; case 'lo': return _native.getLOVal(false); break; } }, set: function(obj, prop, val) { if (typeof prop == 'number' && prop < 32) { _native.setGPRVal(prop, false, val) return } if (prop in _regNums) { _native.setGPRVal(_regNums[prop], false, val) return } switch(prop) { case 'pc': _native.setPCVal(val); break; case 'hi': _native.setHIVal(false, val); break; case 'lo': _native.setLOVal(false, val); break; } } }) const ugpr = new Proxy({}, { get: function (obj, prop) { if (typeof prop == 'number' && prop < 32) { return _native.getGPRVal(prop, true) } if (prop in _regNums) { return _native.getGPRVal(_regNums[prop], true) } switch(prop) { case 'hi': return _native.getHIVal(true); break; case 'lo': return _native.getLOVal(true); break; } }, set: function (obj, prop, val) { if (typeof prop == 'number' && prop < 32) { _native.setGPRVal(prop, true, val) return } if (prop in _regNums) { _native.setGPRVal(_regNums[prop], true, val) return } switch(prop) { case 'hi': _native.setHIVal(true, val); break; case 'lo': _native.setLOVal(true, val); break; } } }) const fpr = new Proxy({}, { get: function(obj, prop) { if (typeof prop == 'number') { return _native.getFPRVal(prop) } if (prop in _regNums) { return _native.getFPRVal(_regNums[prop]) } }, set: function(obj, prop, val) { if (typeof prop == 'number' && prop < 32) { _native.setFPRVal(prop, false, val) return } if (prop in _regNums) { _native.setFPRVal(_regNums[prop], false, val) } } }) const dfpr = new Proxy({}, { get: function (obj, prop) { if (typeof prop == 'number') { return _native.getFPRVal(prop, true) } if (prop in _regNums) { return _native.getFPRVal(_regNums[prop], true) } }, set: function (obj, prop, val) { if (typeof prop == 'number' && prop < 32) { _native.setFPRVal(prop, true, val) return } if (prop in _regNums) { _native.setFPRVal(_regNums[prop], true, val) } } }) const cop0 = new Proxy({}, { get: function (obj, prop) { if (prop == "cause") { return _native.getCauseVal(); } }, set: function (obj, prop, val) { if (prop == "cause") { _native.setCauseVal(val); } } }) const rom = { u8: new Proxy({}, { get: function (obj, prop) { return _native.getROMInt(prop, 8, false) } }), u16: new Proxy({}, { get: function (obj, prop) { return _native.getROMInt(prop, 16, false) } }), u32: new Proxy({}, { get: function (obj, prop) { return _native.getROMInt(prop, 32, false) } }), s8: new Proxy({}, { get: function (obj, prop) { return _native.getROMInt(prop, 8, true) } }), s16: new Proxy({}, { get: function (obj, prop) { return _native.getROMInt(prop, 16, true) } }), s32: new Proxy({}, { get: function (obj, prop) { return _native.getROMInt(prop, 32, true) } }), 'float': new Proxy({}, { get: function (obj, prop) { return _native.getROMFloat(prop) }, set: function (obj, prop, val) { _native.setROMFloat(prop, val) } }), 'double': new Proxy({}, { get: function (obj, prop) { return _native.getROMFloat(prop, true) }, set: function (obj, prop, val) { _native.setROMFloat(prop, val, true) } }), getblock: function (address, size) { return _native.getROMBlock(address, size) }, getstring: function(address, maxLen) { return _native.getROMString(address, maxLen) }, } const mem = { u8: new Proxy({}, { get: function(obj, prop) { return _native.getRDRAMInt(prop, 8, false) }, set: function(obj, prop, val) { _native.setRDRAMInt(prop, 8, val) } }), u16: new Proxy({}, { get: function(obj, prop) { return _native.getRDRAMInt(prop, 16, false) }, set: function(obj, prop, val) { _native.setRDRAMInt(prop, 16, val) } }), u32: new Proxy({}, { get: function(obj, prop) { return _native.getRDRAMInt(prop, 32, false) }, set: function(obj, prop, val) { _native.setRDRAMInt(prop, 32, val) } }), s8: new Proxy({}, { get: function(obj, prop) { return _native.getRDRAMInt(prop, 8, true) }, set: function(obj, prop, val) { _native.setRDRAMInt(prop, 8, val) } }), s16: new Proxy({}, { get: function(obj, prop) { return _native.getRDRAMInt(prop, 16, true) }, set: function(obj, prop, val) { _native.setRDRAMInt(prop, 16, val) } }), s32: new Proxy({}, { get: function(obj, prop) { return _native.getRDRAMInt(prop, 32, true) }, set: function(obj, prop, val) { _native.setRDRAMInt(prop, 32, val) } }), 'float': new Proxy({}, { get: function(obj, prop) { return _native.getRDRAMFloat(prop) }, set: function(obj, prop, val) { _native.setRDRAMFloat(prop, val) } }), 'double': new Proxy({}, { get: function(obj, prop) { return _native.getRDRAMFloat(prop, true) }, set: function(obj, prop, val) { _native.setRDRAMFloat(prop, val, true) } }), getblock: function(address, size) { return _native.getRDRAMBlock(address, size) }, getstring: function(address, maxLen) { return _native.getRDRAMString(address, maxLen) }, bindvar: function(obj, baseAddr, name, type) { Object.defineProperty(obj, name, { get: function() { return mem[type][baseAddr] }, set: function(val) { mem[type][baseAddr] = val } }) return obj }, bindvars: function(obj, list) { for(var i = 0; i < list.length; i++) { mem.bindvar(obj, list[i][0], list[i][1], list[i][2]) } return obj }, bindstruct: function(obj, baseAddr, props) { for (var name in props) { var type = props[name] var size = _typeSizes[type] mem.bindvar(obj, baseAddr, name, type) baseAddr += size } return obj }, typedef: function(props, proto) { var size = 0 for (var name in props) { size += _typeSizes[props[name]] } var StructClass = function(baseAddr) { mem.bindstruct(this, baseAddr, props) } StructClass.sizeof = function() { return size } /*if(proto) { StructClass.prototype = proto }*/ return StructClass } } function alert(text, caption){ caption = caption || "" _native.msgBox(text, caption) } function Socket(fd) { var connected = false; var _fd; if(fd) { // Assume this was constructed from server _fd = fd; connected = true; } else { _fd = _native.sockCreate(); } var _ondata = function(data){} var _onclose = function(){} var _onconnect = function(){} var _onconnect_base = function(){ connected = 1; if (_onconnect !== undefined) { _onconnect(); } } var _bufferSize = 2048 this.write = function(data, callback) { _native.write(_fd, data, _strongBackingReferences.singleUseCallback(callback)) } this.close = function() { _native.close(_fd) } this.connect = function(settings, callback) { if(!connected) { _onconnect = callback; _native.sockConnect(_fd, settings.host || '127.0.0.1', settings.port || 80, _strongBackingReferences.singleUseCallback(_onconnect_base)); } } var _read = function(data) { if(data == null) { connected = false; _onclose(); return; } _native.read(_fd, _bufferSize, _strongBackingReferences.singleUseCallback(_read)); _ondata(data); } this.on = function(eventType, callback) { switch(eventType) { case 'data': _ondata = callback _native.read(_fd, _bufferSize, _strongBackingReferences.singleUseCallback(_read)); break; case 'close': // Note: does nothing if ondata not set _onclose = callback break; } } } function Server(settings) { var _this = this; var _fd = _native.sockCreate() var _listening = false; var _queued_accept = false; var _onconnection = function(socket){} this.listen = function (port) { if (_native.sockListen(_fd, port || 80)) { _listening = true; } else { throw new Error("failed to listen"); } if (_queued_accept) { _native.sockAccept(_fd, _strongBackingReferences.singleUseCallback(_acceptClient)); } } if(settings.port) { this.listen(settings.port || 80); } // Intermediate callback // convert clientFd to Socket and accept next client var _acceptClient = function(clientFd) { _onconnection(new Socket(clientFd)) _native.sockAccept(_fd, _strongBackingReferences.singleUseCallback(_acceptClient)) } this.on = function(eventType, callback) { switch(eventType) { case 'connection': _onconnection = callback; if (_listening) { _native.sockAccept(_fd, _strongBackingReferences.singleUseCallback(_acceptClient)); } else { _queued_accept = true; } break; } } }