"use strict"; /// MAME SUPPORT Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseMAMEZ80Platform = exports.BaseMAME6502Platform = exports.BaseMAMEPlatform = void 0; const baseplatform_1 = require("./baseplatform"); const disasm6502_1 = require("./cpu/disasm6502"); const disasmz80_1 = require("./cpu/disasmz80"); const emu_1 = require("./emu"); class BaseMAMEPlatform { constructor(mainElement) { this.loaded = false; this.preinitted = false; this.started = false; this.romtype = 'cart'; this.running = false; this.initluavars = false; this.mainElement = mainElement; this.timer = new emu_1.AnimationTimer(20, this.poll.bind(this)); } // http://docs.mamedev.org/techspecs/luaengine.html luacall(s) { if (!this.js_lua_string) this.js_lua_string = Module.cwrap('_Z13js_lua_stringPKc', 'string', ['string']); return this.js_lua_string(s || ""); } _pause() { this.running = false; this.timer.stop(); } pause() { if (this.loaded && this.running) { this.luacall('emu.pause()'); this._pause(); } } _resume() { this.luacall('emu.unpause()'); this.running = true; this.timer.start(); } resume() { if (this.loaded && !this.running) { // TODO this._resume(); } } reset() { if (this.loaded) { this.luacall('manager:machine():soft_reset()'); this.running = true; this.initluavars = false; } } isRunning() { return this.running; } bufferConsoleOutput(s) { if (typeof s !== 'string') return; console.log(s); } startModule(mainElement, opts) { this.started = true; var romfn = this.romfn = this.romfn || opts.romfn; var romdata = this.romdata = this.romdata || opts.romdata || new emu_1.RAM(opts.romsize).mem; var romtype = this.romtype = this.romtype || opts.romtype; // create canvas var video = this.video = new emu_1.RasterVideo(this.mainElement, opts.width, opts.height); video.create(); $(video.canvas).attr('id', 'canvas'); // load asm.js module console.log("loading", opts.jsfile); var modargs = [opts.driver, '-debug', '-debugger', 'none', '-verbose', '-window', '-nokeepaspect', '-resolution', video.canvas.width + 'x' + video.canvas.height ]; if (romfn) { modargs.push('-' + romtype, romfn); } if (opts.extraargs) { modargs = modargs.concat(opts.extraargs); } console.log(modargs); window['JSMESS'] = {}; window['Module'] = { arguments: modargs, screenIsReadOnly: true, print: this.bufferConsoleOutput, canvas: video.canvas, doNotCaptureKeyboard: true, keyboardListeningElement: video.canvas, preInit: () => { console.log("loading FS"); ENV.SDL_EMSCRIPTEN_KEYBOARD_ELEMENT = 'canvas'; if (opts.cfgfile) { FS.mkdir('/cfg'); FS.writeFile('/cfg/' + opts.cfgfile, opts.cfgdata, { encoding: 'utf8' }); } if (opts.biosfile) { FS.mkdir('/roms'); FS.mkdir('/roms/' + opts.driver); FS.writeFile('/roms/' + opts.biosfile, opts.biosdata, { encoding: 'binary' }); } FS.mkdir('/emulator'); if (romfn) { FS.writeFile(romfn, romdata, { encoding: 'binary' }); } //FS.writeFile('/debug.ini', 'debugger none\n', {encoding:'utf8'}); if (opts.preInit) { opts.preInit(self); } this.preinitted = true; }, preRun: [ () => { $(video.canvas).click((e) => { video.canvas.focus(); }); this.loaded = true; console.log("about to run..."); } ] }; // preload files // TODO: ensure loaded var fetch_cfg, fetch_lua; var fetch_bios = $.Deferred(); var fetch_wasm = $.Deferred(); // fetch config file if (opts.cfgfile) { fetch_cfg = $.get('mame/cfg/' + opts.cfgfile, (data) => { opts.cfgdata = data; console.log("loaded " + opts.cfgfile); }, 'text'); } // fetch BIOS file if (opts.biosfile) { var oReq1 = new XMLHttpRequest(); oReq1.open("GET", 'mame/roms/' + opts.biosfile, true); oReq1.responseType = "arraybuffer"; oReq1.onload = (oEvent) => { opts.biosdata = new Uint8Array(oReq1.response); console.log("loaded " + opts.biosfile); // + " (" + oEvent.total + " bytes)"); fetch_bios.resolve(); }; oReq1.ontimeout = function (oEvent) { throw Error("Timeout loading " + opts.biosfile); }; oReq1.send(); } else { fetch_bios.resolve(); } // load debugger Lua script fetch_lua = $.get('mame/debugger.lua', (data) => { this.luadebugscript = data; console.log("loaded debugger.lua"); }, 'text'); // load WASM { var oReq2 = new XMLHttpRequest(); oReq2.open("GET", 'mame/' + opts.jsfile.replace('.js', '.wasm'), true); oReq2.responseType = "arraybuffer"; oReq2.onload = (oEvent) => { console.log("loaded WASM file"); window['Module'].wasmBinary = new Uint8Array(oReq2.response); fetch_wasm.resolve(); }; oReq2.ontimeout = function (oEvent) { throw Error("Timeout loading " + opts.jsfile); }; oReq2.send(); } // start loading script $.when(fetch_lua, fetch_cfg, fetch_bios, fetch_wasm).done(() => { var script = document.createElement('script'); script.src = 'mame/' + opts.jsfile; document.getElementsByTagName('head')[0].appendChild(script); console.log("created script element"); }); // for debugging via browser console window['mamelua'] = (s) => { this.initlua(); return [s, this.luacall(s)]; }; } loadROMFile(data) { this.romdata = data; if (this.preinitted && this.romfn) { FS.writeFile(this.romfn, data, { encoding: 'binary' }); } } loadRegion(region, data) { if (this.loaded && data.length > 0) { //this.luacall('cart=manager:machine().images["cart"]\nprint(cart:filename())\ncart:load("' + region + '")\n'); var s = 'rgn = manager:machine():memory().regions["' + region + '"]\n'; //s += 'print(rgn.size)\n'; for (var i = 0; i < data.length; i += 4) { var v = data[i] + (data[i + 1] << 8) + (data[i + 2] << 16) + (data[i + 3] << 24); s += 'rgn:write_u32(' + i + ',' + v + ')\n'; // TODO: endian? } this.luacall(s); this.reset(); } } // DEBUGGING SUPPORT initlua() { if (!this.initluavars) { this.luacall(this.luadebugscript); this.luacall('mamedbg.init()'); this.initluavars = true; } } readAddress(a) { this.initlua(); return parseInt(this.luacall('return mem:read_u8(' + a + ')')); } getCPUReg(reg) { if (!this.loaded) return 0; // TODO this.initlua(); return parseInt(this.luacall('return cpu.state.' + reg + '.value')); } grabState(expr) { this.initlua(); return { c: this.getCPUState(), buf: this.luacall("return string.tohex(" + expr + ")") }; } saveState() { return this.grabState("manager:machine():buffer_save()"); } loadState(state) { this.initlua(); return this.luacall("manager:machine():buffer_load(string.fromhex('" + state.buf + "'))"); } poll() { if (this.onBreakpointHit && this.luacall("return tostring(mamedbg.is_stopped())") == 'true') { this._pause(); //this.luacall("manager:machine():buffer_load(lastBreakState)"); var state = this.grabState("lastBreakState"); this.onBreakpointHit(state); } } clearDebug() { this.onBreakpointHit = null; if (this.loaded) { this.initlua(); this.luacall('mamedbg.reset()'); } } getDebugCallback() { return this.onBreakpointHit; // TODO? } setupDebug(callback) { this.onBreakpointHit = callback; } debugcmd(s) { this.initlua(); this.luacall(s); this._resume(); } runToPC(pc) { this.debugcmd('mamedbg.runTo(' + pc + ')'); } runToVsync() { this.debugcmd('mamedbg.runToVsync()'); } runUntilReturn() { this.debugcmd('mamedbg.runUntilReturn()'); } // TODO runEval() { this.reset(); this.step(); } step() { this.debugcmd('mamedbg.step()'); } getDebugCategories() { return ['CPU']; } getDebugInfo(category, state) { switch (category) { case 'CPU': return this.cpuStateToLongString(state.c); } } getDebugTree() { this.initlua(); var devices = JSON.parse(this.luacall(`return table.tojson(manager:machine().devices)`)); var images = JSON.parse(this.luacall(`return table.tojson(manager:machine().images)`)); var regions = JSON.parse(this.luacall(`return table.tojson(manager:machine():memory().regions)`)); return { devices: devices, images: images, regions: regions, }; } } exports.BaseMAMEPlatform = BaseMAMEPlatform; class BaseMAME6502Platform extends BaseMAMEPlatform { getPC() { return this.getCPUReg('PC'); } getSP() { return this.getCPUReg('SP'); } isStable() { return true; } getCPUState() { return { PC: this.getPC(), SP: this.getSP(), A: this.getCPUReg('A'), X: this.getCPUReg('X'), Y: this.getCPUReg('Y'), flags: this.getCPUReg('P'), }; } disassemble(pc, read) { return (0, disasm6502_1.disassemble6502)(pc, read(pc), read(pc + 1), read(pc + 2)); } cpuStateToLongString(c) { return (0, baseplatform_1.cpuStateToLongString_6502)(c); } } exports.BaseMAME6502Platform = BaseMAME6502Platform; class BaseMAMEZ80Platform extends BaseMAMEPlatform { getPC() { return this.getCPUReg('PC'); } getSP() { return this.getCPUReg('SP'); } isStable() { return true; } getCPUState() { return { PC: this.getPC(), SP: this.getSP(), AF: this.getCPUReg('AF'), BC: this.getCPUReg('BC'), DE: this.getCPUReg('DE'), HL: this.getCPUReg('HL'), IX: this.getCPUReg('IX'), IY: this.getCPUReg('IY'), IR: this.getCPUReg('R') + (this.getCPUReg('I') << 8), }; } disassemble(pc, read) { return (0, disasmz80_1.disassembleZ80)(pc, read(pc), read(pc + 1), read(pc + 2), read(pc + 3)); } cpuStateToLongString(c) { return (0, baseplatform_1.cpuStateToLongString_Z80)(c); } } exports.BaseMAMEZ80Platform = BaseMAMEZ80Platform; //# sourceMappingURL=mameplatform.js.map