2017-01-03 15:43:40 +00:00
|
|
|
"use strict";
|
2016-12-16 01:21:51 +00:00
|
|
|
|
2017-01-14 02:31:04 +00:00
|
|
|
// catch errors
|
2017-01-16 19:13:03 +00:00
|
|
|
function installErrorHandler() {
|
|
|
|
if (typeof window.onerror == "object") {
|
|
|
|
window.onerror = function (msgevent, url, line, col, error) {
|
|
|
|
console.log(msgevent, url, line, col);
|
|
|
|
console.log(error);
|
|
|
|
//$("#editor").hide();
|
|
|
|
if (window.location.host.endsWith('8bitworkshop.com')) {
|
|
|
|
ga('send', 'exception', {
|
|
|
|
'exDescription': msgevent + " " + url + " " + " " + line + ":" + col + ", " + error,
|
|
|
|
'exFatal': true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
alert(msgevent+"");
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function uninstallErrorHandler() {
|
|
|
|
window.onerror = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function gotoNewLocation() {
|
|
|
|
uninstallErrorHandler();
|
|
|
|
window.location = "?" + $.param(qs);
|
2017-01-14 02:31:04 +00:00
|
|
|
}
|
|
|
|
|
2017-01-14 05:47:26 +00:00
|
|
|
// make sure VCS doesn't start
|
2017-11-21 21:36:38 +00:00
|
|
|
if (window.Javatari) Javatari.AUTO_START = false;
|
2017-01-14 05:47:26 +00:00
|
|
|
|
2017-01-13 02:21:35 +00:00
|
|
|
// 8bitworkshop IDE user interface
|
|
|
|
|
|
|
|
var PRESETS; // presets array
|
|
|
|
var platform_id;
|
|
|
|
var platform; // platform object
|
2017-05-24 02:15:23 +00:00
|
|
|
var originalFileID;
|
|
|
|
var originalText;
|
2017-11-28 02:08:19 +00:00
|
|
|
var userPaused;
|
2017-01-13 02:21:35 +00:00
|
|
|
|
2017-04-12 16:23:24 +00:00
|
|
|
var toolbar = $("#controls_top");
|
|
|
|
|
2017-05-24 02:15:23 +00:00
|
|
|
function getBiggestItems(storage) {
|
|
|
|
var items = [];
|
|
|
|
for (var i = 0; i < storage.length; i++) {
|
|
|
|
var key = storage.key(i);
|
2017-11-21 21:36:38 +00:00
|
|
|
var len = storage.getItem(key).length;
|
|
|
|
if (len>=100)
|
|
|
|
items.push([lpad(len+"", 12), key]);
|
2017-05-24 02:15:23 +00:00
|
|
|
}
|
|
|
|
items.sort();
|
2017-11-21 21:36:38 +00:00
|
|
|
return items;
|
|
|
|
}
|
|
|
|
/*
|
2017-05-24 02:15:23 +00:00
|
|
|
var s = "";
|
|
|
|
for (var i=items.length-5; i<items.length; i++) {
|
|
|
|
s += items[i] + "\n";
|
|
|
|
}
|
|
|
|
return s;
|
2017-11-21 21:36:38 +00:00
|
|
|
}*/
|
2017-05-24 02:15:23 +00:00
|
|
|
|
2017-01-04 21:07:59 +00:00
|
|
|
var FileStore = function(storage, prefix) {
|
|
|
|
var self = this;
|
|
|
|
this.saveFile = function(name, text) {
|
2017-05-24 02:15:23 +00:00
|
|
|
try {
|
|
|
|
storage.setItem(prefix + name, text);
|
|
|
|
} catch (e) {
|
|
|
|
if (e.name == 'NS_ERROR_DOM_QUOTA_REACHED') {
|
|
|
|
console.log(e);
|
2017-11-21 21:36:38 +00:00
|
|
|
if (confirm("Sorry, you've reached your local storage quota for this browser.\n\nGo to local storage editor?")) {
|
|
|
|
window.location = 'editstorage.html';
|
|
|
|
return;
|
|
|
|
}
|
2017-05-24 02:15:23 +00:00
|
|
|
} else {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
2017-01-04 21:07:59 +00:00
|
|
|
}
|
|
|
|
this.loadFile = function(name) {
|
|
|
|
return storage.getItem(prefix + name) || storage.getItem(name);
|
|
|
|
}
|
|
|
|
this.getFiles = function(prefix2) {
|
2017-01-13 02:21:35 +00:00
|
|
|
// rename items for compatibility
|
|
|
|
for (var i = 0; i < storage.length; i++) {
|
|
|
|
var key = storage.key(i);
|
|
|
|
if (key.startsWith(prefix2) && platform_id == 'vcs') {
|
|
|
|
this.saveFile(key, storage.getItem(key));
|
|
|
|
storage.removeItem(key);
|
|
|
|
console.log("Renamed",key,'to',prefix+key);
|
|
|
|
i=-1; // reset loop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// iterate over files with <platform>/<dir> prefix
|
2017-01-04 21:07:59 +00:00
|
|
|
var files = [];
|
|
|
|
for (var i = 0; i < storage.length; i++) {
|
|
|
|
var key = storage.key(i);
|
|
|
|
if (key.startsWith(prefix + prefix2)) {
|
|
|
|
var name = key.substring(prefix.length + prefix2.length);
|
|
|
|
files.push(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return files;
|
|
|
|
}
|
|
|
|
this.deleteFile = function(name) {
|
2017-02-18 22:50:51 +00:00
|
|
|
storage.removeItem(prefix + name);
|
|
|
|
storage.removeItem(prefix + 'local/' + name);
|
2017-01-04 21:07:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-16 04:47:12 +00:00
|
|
|
var SourceFile = function(lines, text) {
|
|
|
|
lines = lines || [];
|
|
|
|
this.text = text;
|
|
|
|
this.offset2line = {};
|
|
|
|
this.line2offset = {};
|
|
|
|
for (var info of lines) {
|
|
|
|
if (info.offset >= 0) {
|
|
|
|
this.offset2line[info.offset] = info.line;
|
|
|
|
this.line2offset[info.line] = info.offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.findLineForOffset = function(PC) {
|
|
|
|
if (this.offset2line) {
|
2017-01-16 15:35:19 +00:00
|
|
|
for (var i=0; i<16; i++) {
|
2017-01-16 04:47:12 +00:00
|
|
|
var line = this.offset2line[PC];
|
|
|
|
if (line >= 0) {
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
PC--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2017-04-22 17:33:20 +00:00
|
|
|
this.lineCount = function() { return this.line2offset.length; }
|
2017-01-16 04:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var TOOL_TO_SOURCE_STYLE = {
|
|
|
|
'dasm': '6502',
|
|
|
|
'acme': '6502',
|
|
|
|
'cc65': 'text/x-csrc',
|
|
|
|
'ca65': '6502',
|
|
|
|
'z80asm': 'z80',
|
|
|
|
'sdasz80': 'z80',
|
|
|
|
'sdcc': 'text/x-csrc',
|
2017-11-11 19:45:32 +00:00
|
|
|
'verilator': 'verilog',
|
2017-01-16 04:47:12 +00:00
|
|
|
}
|
|
|
|
|
2016-12-16 01:21:51 +00:00
|
|
|
var worker = new Worker("./src/worker/workermain.js");
|
2017-04-29 15:31:11 +00:00
|
|
|
var main_editor;
|
2017-04-22 01:56:49 +00:00
|
|
|
var current_output;
|
|
|
|
var current_preset_index = -1;
|
|
|
|
var current_preset_id;
|
|
|
|
var assemblyfile;
|
|
|
|
var sourcefile;
|
|
|
|
var symbolmap;
|
|
|
|
var addr2symbol;
|
|
|
|
var compparams;
|
2016-12-16 01:21:51 +00:00
|
|
|
var trace_pending_at_pc;
|
2017-01-04 21:07:59 +00:00
|
|
|
var store;
|
2017-01-22 14:35:04 +00:00
|
|
|
var pendingWorkerMessages = 0;
|
2017-01-26 05:09:57 +00:00
|
|
|
var editor;
|
2017-01-08 15:51:19 +00:00
|
|
|
var disasmview = CodeMirror(document.getElementById('disassembly'), {
|
2017-01-16 04:47:12 +00:00
|
|
|
mode: 'z80',
|
2017-01-08 15:51:19 +00:00
|
|
|
theme: 'cobalt',
|
|
|
|
tabSize: 8,
|
|
|
|
readOnly: true,
|
2017-01-16 04:47:12 +00:00
|
|
|
styleActiveLine: true
|
2017-01-08 15:51:19 +00:00
|
|
|
});
|
2017-04-22 17:33:20 +00:00
|
|
|
scrollProfileView(disasmview);
|
2017-01-08 15:51:19 +00:00
|
|
|
|
2017-04-26 01:14:17 +00:00
|
|
|
var memorylist;
|
|
|
|
var profilelist;
|
2017-04-19 01:18:53 +00:00
|
|
|
|
2017-04-22 17:33:20 +00:00
|
|
|
function scrollProfileView(_ed) {
|
|
|
|
_ed.on('scroll', function(ed, changeobj) {
|
2017-04-26 01:14:17 +00:00
|
|
|
if (profilelist) {
|
|
|
|
profilelist.container.scrollTop = ed.getScrollInfo().top;
|
2017-04-22 17:33:20 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-01-26 05:09:57 +00:00
|
|
|
function newEditor(mode) {
|
2018-02-18 05:12:09 +00:00
|
|
|
var isAsm = mode=='6502' || mode =='z80' || mode=='verilog'; // TODO
|
2017-01-26 05:09:57 +00:00
|
|
|
editor = CodeMirror(document.getElementById('editor'), {
|
|
|
|
theme: 'mbo',
|
|
|
|
lineNumbers: true,
|
|
|
|
matchBrackets: true,
|
|
|
|
tabSize: 8,
|
2017-02-20 23:50:29 +00:00
|
|
|
indentAuto: true,
|
2017-01-26 05:09:57 +00:00
|
|
|
gutters: isAsm ? ["CodeMirror-linenumbers", "gutter-offset", "gutter-bytes", "gutter-clock", "gutter-info"]
|
|
|
|
: ["CodeMirror-linenumbers", "gutter-offset", "gutter-info"],
|
|
|
|
});
|
2017-02-20 23:50:29 +00:00
|
|
|
var timer;
|
2017-01-26 05:09:57 +00:00
|
|
|
editor.on('changes', function(ed, changeobj) {
|
2017-02-20 23:50:29 +00:00
|
|
|
clearTimeout(timer);
|
|
|
|
timer = setTimeout(function() {
|
2017-04-04 18:19:47 +00:00
|
|
|
setCode(editor.getValue());
|
2017-02-20 23:50:29 +00:00
|
|
|
}, 200);
|
2017-01-26 05:09:57 +00:00
|
|
|
});
|
2017-11-21 16:15:08 +00:00
|
|
|
editor.on('cursorActivity', function(ed) {
|
|
|
|
var start = editor.getCursor(true);
|
|
|
|
var end = editor.getCursor(false);
|
|
|
|
if (start.line == end.line && start.ch < end.ch) {
|
|
|
|
var name = editor.getSelection();
|
|
|
|
inspectVariable(editor, name);
|
|
|
|
} else {
|
|
|
|
inspectVariable(editor);
|
|
|
|
}
|
|
|
|
});
|
2017-04-22 17:33:20 +00:00
|
|
|
scrollProfileView(editor);
|
2017-01-26 05:09:57 +00:00
|
|
|
editor.setOption("mode", mode);
|
|
|
|
}
|
2016-12-16 01:21:51 +00:00
|
|
|
|
2017-11-21 16:15:08 +00:00
|
|
|
function inspectVariable(editor, name) {
|
|
|
|
var val;
|
|
|
|
if (platform.inspect) {
|
|
|
|
platform.inspect(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-16 01:21:51 +00:00
|
|
|
function getCurrentPresetTitle() {
|
2017-01-03 20:52:38 +00:00
|
|
|
if (current_preset_index < 0)
|
2016-12-16 01:21:51 +00:00
|
|
|
return "ROM";
|
|
|
|
else
|
2017-01-03 20:52:38 +00:00
|
|
|
return PRESETS[current_preset_index].title || PRESETS[current_preset_index].name || "ROM";
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function setLastPreset(id) {
|
2017-04-29 15:31:11 +00:00
|
|
|
if (platform_id != 'base_z80') { // TODO
|
|
|
|
localStorage.setItem("__lastplatform", platform_id);
|
|
|
|
localStorage.setItem("__lastid_"+platform_id, id);
|
|
|
|
}
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function updatePreset(current_preset_id, text) {
|
2017-11-15 00:12:52 +00:00
|
|
|
// TODO: do we have to save all Verilog thingies?
|
|
|
|
if (text.trim().length &&
|
|
|
|
(originalFileID != current_preset_id || text != originalText || platform_id=='verilog')) {
|
2017-01-04 21:07:59 +00:00
|
|
|
store.saveFile(current_preset_id, text);
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-16 04:47:12 +00:00
|
|
|
function loadCode(text, fileid) {
|
2017-01-23 21:21:45 +00:00
|
|
|
var tool = platform.getToolForFilename(fileid);
|
2017-04-29 15:31:11 +00:00
|
|
|
main_editor = newEditor(tool && TOOL_TO_SOURCE_STYLE[tool]);
|
2017-04-04 18:19:47 +00:00
|
|
|
editor.setValue(text); // calls setCode()
|
2017-01-23 21:21:45 +00:00
|
|
|
editor.clearHistory();
|
2016-12-16 01:21:51 +00:00
|
|
|
current_output = null;
|
2017-01-16 04:47:12 +00:00
|
|
|
setLastPreset(fileid);
|
2017-05-24 02:15:23 +00:00
|
|
|
originalFileID = fileid;
|
|
|
|
originalText = text;
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function loadFile(fileid, filename, index) {
|
|
|
|
current_preset_id = fileid;
|
2017-01-03 20:52:38 +00:00
|
|
|
current_preset_index = index;
|
2017-01-22 14:35:04 +00:00
|
|
|
var text = store.loadFile(fileid) || "";
|
2016-12-16 01:21:51 +00:00
|
|
|
if (text) {
|
2017-01-16 04:47:12 +00:00
|
|
|
loadCode(text, fileid);
|
2016-12-16 01:21:51 +00:00
|
|
|
} else if (!text && index >= 0) {
|
2017-02-14 23:15:06 +00:00
|
|
|
if (filename.indexOf('.') <= 0)
|
|
|
|
filename += ".a";
|
2016-12-16 01:21:51 +00:00
|
|
|
console.log("Loading preset", fileid, filename, index, PRESETS[index]);
|
|
|
|
if (text.length == 0) {
|
|
|
|
console.log("Fetching", filename);
|
|
|
|
$.get( filename, function( text ) {
|
|
|
|
console.log("GET",text.length,'bytes');
|
2017-01-16 04:47:12 +00:00
|
|
|
loadCode(text, fileid);
|
2017-03-12 22:47:44 +00:00
|
|
|
}, 'text')
|
|
|
|
.fail(function() {
|
|
|
|
alert("Could not load preset " + fileid);
|
|
|
|
loadCode("", fileid);
|
|
|
|
});
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
} else {
|
2017-01-26 05:09:57 +00:00
|
|
|
var ext = platform.getToolForFilename(fileid);
|
|
|
|
$.get( "presets/"+platform_id+"/skeleton."+ext, function( text ) {
|
2017-01-16 04:47:12 +00:00
|
|
|
loadCode(text, fileid);
|
2017-01-26 05:09:57 +00:00
|
|
|
}, 'text')
|
|
|
|
.fail(function() {
|
2017-03-12 22:47:44 +00:00
|
|
|
alert("Could not load skeleton for " + platform_id + "/" + ext);
|
2017-01-26 05:09:57 +00:00
|
|
|
loadCode("", fileid);
|
|
|
|
});
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function loadPreset(preset_id) {
|
|
|
|
// TODO
|
|
|
|
var index = parseInt(preset_id+"");
|
|
|
|
for (var i=0; i<PRESETS.length; i++)
|
|
|
|
if (PRESETS[i].id == preset_id)
|
|
|
|
index = i;
|
|
|
|
index = (index + PRESETS.length) % PRESETS.length;
|
|
|
|
if (index >= 0) {
|
|
|
|
// load the preset
|
2017-01-03 20:52:38 +00:00
|
|
|
loadFile(preset_id, "presets/" + platform_id + "/" + PRESETS[index].id, index);
|
2016-12-16 01:21:51 +00:00
|
|
|
} else {
|
|
|
|
// no preset found? load local
|
2017-01-03 20:52:38 +00:00
|
|
|
loadFile(preset_id, "local/" + platform_id + "/" + preset_id, -1);
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function gotoPresetAt(index) {
|
|
|
|
var index = (index + PRESETS.length) % PRESETS.length;
|
|
|
|
qs['file'] = PRESETS[index].id;
|
2017-01-16 19:13:03 +00:00
|
|
|
gotoNewLocation();
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function gotoPresetNamed(id) {
|
2017-01-16 15:35:19 +00:00
|
|
|
qs['platform'] = platform_id;
|
|
|
|
qs['file'] = id;
|
2017-01-16 19:13:03 +00:00
|
|
|
gotoNewLocation();
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function _createNewFile(e) {
|
2017-04-23 13:03:05 +00:00
|
|
|
var filename = prompt("Create New File", "newfile" + platform.getDefaultExtension());
|
2016-12-16 01:21:51 +00:00
|
|
|
if (filename && filename.length) {
|
|
|
|
if (filename.indexOf(".") < 0) {
|
2017-04-20 00:55:13 +00:00
|
|
|
filename += platform.getDefaultExtension();
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
2017-01-04 21:07:59 +00:00
|
|
|
qs['file'] = "local/" + filename;
|
2017-01-16 19:13:03 +00:00
|
|
|
gotoNewLocation();
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
2016-12-30 23:51:15 +00:00
|
|
|
return true;
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
2017-02-02 19:11:52 +00:00
|
|
|
function getCurrentFilename() {
|
|
|
|
var toks = current_preset_id.split("/");
|
|
|
|
return toks[toks.length-1];
|
|
|
|
}
|
|
|
|
|
2016-12-16 01:21:51 +00:00
|
|
|
function _shareFile(e) {
|
|
|
|
if (current_output == null) {
|
2016-12-30 23:51:15 +00:00
|
|
|
alert("Please fix errors before sharing.");
|
|
|
|
return true;
|
|
|
|
}
|
2017-01-25 17:30:05 +00:00
|
|
|
var github = new Octokat();
|
|
|
|
var files = {};
|
2016-12-16 01:21:51 +00:00
|
|
|
var text = editor.getValue();
|
2017-02-02 19:11:52 +00:00
|
|
|
files[getCurrentFilename()] = {"content": text};
|
2017-01-25 17:30:05 +00:00
|
|
|
var gistdata = {
|
|
|
|
"description": '8bitworkshop.com {"platform":"' + platform_id + '"}',
|
|
|
|
"public": true,
|
|
|
|
"files": files
|
|
|
|
};
|
|
|
|
var gist = github.gists.create(gistdata).done(function(val) {
|
|
|
|
var url = "http://8bitworkshop.com/?sharekey=" + val.id;
|
|
|
|
window.prompt("Copy link to clipboard (Ctrl+C, Enter)", url);
|
|
|
|
}).fail(function(err) {
|
|
|
|
alert("Error sharing file: " + err.message);
|
2016-12-16 01:21:51 +00:00
|
|
|
});
|
2016-12-30 23:51:15 +00:00
|
|
|
return true;
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function _resetPreset(e) {
|
2017-01-03 20:52:38 +00:00
|
|
|
if (current_preset_index < 0) {
|
2017-01-03 01:42:15 +00:00
|
|
|
alert("Can only reset built-in file examples.")
|
2017-01-03 20:52:38 +00:00
|
|
|
} else if (confirm("Reset '" + PRESETS[current_preset_index].name + "' to default?")) {
|
2016-12-16 01:21:51 +00:00
|
|
|
qs['reset'] = '1';
|
2017-01-16 19:13:03 +00:00
|
|
|
gotoNewLocation();
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
2016-12-30 23:51:15 +00:00
|
|
|
return true;
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
2017-02-02 19:11:52 +00:00
|
|
|
function _downloadROMImage(e) {
|
|
|
|
if (current_output == null) {
|
|
|
|
alert("Please fix errors before downloading ROM.");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
var blob = new Blob([current_output], {type: "application/octet-stream"});
|
|
|
|
saveAs(blob, getCurrentFilename()+".rom");
|
|
|
|
}
|
|
|
|
|
2016-12-16 01:21:51 +00:00
|
|
|
function populateExamples(sel) {
|
2017-04-29 15:31:11 +00:00
|
|
|
sel.append($("<option />").text("--------- Examples ---------").attr('disabled',true));
|
2016-12-16 01:21:51 +00:00
|
|
|
for (var i=0; i<PRESETS.length; i++) {
|
|
|
|
var preset = PRESETS[i];
|
2017-02-14 23:15:06 +00:00
|
|
|
var name = preset.chapter ? (preset.chapter + ". " + preset.name) : preset.name;
|
2016-12-16 01:21:51 +00:00
|
|
|
sel.append($("<option />").val(preset.id).text(name).attr('selected',preset.id==current_preset_id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-22 14:35:04 +00:00
|
|
|
function populateFiles(sel, name, prefix) {
|
|
|
|
sel.append($("<option />").text("------- " + name + " -------").attr('disabled',true));
|
|
|
|
var filenames = store.getFiles(prefix);
|
|
|
|
var foundSelected = false;
|
2017-01-04 21:07:59 +00:00
|
|
|
for (var i = 0; i < filenames.length; i++) {
|
|
|
|
var name = filenames[i];
|
2017-01-22 14:35:04 +00:00
|
|
|
var key = prefix + name;
|
2017-01-04 21:07:59 +00:00
|
|
|
sel.append($("<option />").val(key).text(name).attr('selected',key==current_preset_id));
|
2017-01-22 14:35:04 +00:00
|
|
|
if (key == current_preset_id) foundSelected = true;
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
2017-01-22 14:35:04 +00:00
|
|
|
if (!foundSelected && current_preset_id && current_preset_id.startsWith(prefix)) {
|
|
|
|
var name = current_preset_id.slice(prefix.length);
|
|
|
|
var key = prefix + name;
|
|
|
|
sel.append($("<option />").val(key).text(name).attr('selected',true));
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateSelector() {
|
|
|
|
var sel = $("#preset_select").empty();
|
2017-04-29 15:31:11 +00:00
|
|
|
if (platform_id != 'base_z80') { // TODO
|
|
|
|
populateFiles(sel, "Local Files", "local/");
|
|
|
|
populateFiles(sel, "Shared", "shared/");
|
|
|
|
}
|
2016-12-16 01:21:51 +00:00
|
|
|
populateExamples(sel);
|
|
|
|
// set click handlers
|
|
|
|
sel.off('change').change(function(e) {
|
|
|
|
gotoPresetNamed($(this).val());
|
|
|
|
});
|
|
|
|
$("#preset_prev").off('click').click(function() {
|
2017-01-03 20:52:38 +00:00
|
|
|
gotoPresetAt(current_preset_index - 1);
|
2016-12-16 01:21:51 +00:00
|
|
|
});
|
|
|
|
$("#preset_next").off('click').click(function() {
|
2017-01-03 20:52:38 +00:00
|
|
|
gotoPresetAt(current_preset_index + 1);
|
2016-12-16 01:21:51 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-11-15 00:12:52 +00:00
|
|
|
function loadFileDependencies(text) {
|
|
|
|
var arr = [];
|
|
|
|
if (platform_id == 'verilog') {
|
2017-11-29 01:48:27 +00:00
|
|
|
var re = /`include\s+"(.+?)"/g;
|
2017-11-15 00:12:52 +00:00
|
|
|
var m;
|
|
|
|
while (m = re.exec(text)) {
|
|
|
|
arr.push({
|
|
|
|
filename:m[1],
|
2018-02-14 20:58:38 +00:00
|
|
|
prefix:platform_id,
|
2017-11-29 01:48:27 +00:00
|
|
|
text:store.loadFile(m[1]) || store.loadFile('local/'+m[1]) // TODO??
|
2017-11-15 00:12:52 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
2016-12-16 01:21:51 +00:00
|
|
|
function setCode(text) {
|
2017-01-17 17:26:08 +00:00
|
|
|
if (pendingWorkerMessages++ > 0)
|
|
|
|
return;
|
2017-11-15 00:12:52 +00:00
|
|
|
worker.postMessage({
|
|
|
|
code:text,
|
|
|
|
dependencies:loadFileDependencies(text),
|
|
|
|
platform:platform_id,
|
|
|
|
tool:platform.getToolForFilename(current_preset_id)
|
|
|
|
});
|
2017-04-12 16:23:24 +00:00
|
|
|
toolbar.addClass("is-busy");
|
|
|
|
$('#compile_spinner').css('visibility', 'visible');
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function arrayCompare(a,b) {
|
|
|
|
if (a == null && b == null) return true;
|
|
|
|
if (a == null) return false;
|
|
|
|
if (b == null) return false;
|
|
|
|
if (a.length != b.length) return false;
|
|
|
|
for (var i=0; i<a.length; i++)
|
|
|
|
if (a[i] != b[i])
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-22 01:56:49 +00:00
|
|
|
function invertMap(m) {
|
|
|
|
var r = {};
|
|
|
|
if (m) {
|
|
|
|
for (var k in m) r[m[k]] = k;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2017-03-24 22:10:35 +00:00
|
|
|
function setCompileOutput(data) {
|
2017-05-04 15:54:56 +00:00
|
|
|
// TODO: kills current selection
|
2017-03-24 22:10:35 +00:00
|
|
|
sourcefile = new SourceFile(data.lines);
|
|
|
|
if (data.asmlines) {
|
|
|
|
assemblyfile = new SourceFile(data.asmlines, data.intermediate.listing);
|
2017-01-17 17:26:08 +00:00
|
|
|
}
|
2017-04-22 01:56:49 +00:00
|
|
|
symbolmap = data.symbolmap;
|
|
|
|
addr2symbol = invertMap(symbolmap);
|
|
|
|
addr2symbol[0x10000] = '__END__';
|
|
|
|
compparams = data.params;
|
2017-05-02 13:09:53 +00:00
|
|
|
updatePreset(current_preset_id, editor.getValue()); // update persisted entry
|
2016-12-16 01:21:51 +00:00
|
|
|
// errors?
|
2017-11-13 05:24:19 +00:00
|
|
|
var lines2errmsg = [];
|
2017-01-16 19:13:03 +00:00
|
|
|
function addErrorMarker(line, msg) {
|
|
|
|
var div = document.createElement("div");
|
|
|
|
div.setAttribute("class", "tooltipbox tooltiperror");
|
|
|
|
div.style.color = '#ff3333'; // TODO
|
|
|
|
div.appendChild(document.createTextNode("\u24cd"));
|
|
|
|
var tooltip = document.createElement("span");
|
|
|
|
tooltip.setAttribute("class", "tooltiptext");
|
2017-11-15 00:12:52 +00:00
|
|
|
if (lines2errmsg[line])
|
|
|
|
msg = lines2errmsg[line] + "\n" + msg;
|
2017-01-16 19:13:03 +00:00
|
|
|
tooltip.appendChild(document.createTextNode(msg));
|
2017-11-13 05:24:19 +00:00
|
|
|
lines2errmsg[line] = msg;
|
2017-01-16 19:13:03 +00:00
|
|
|
div.appendChild(tooltip);
|
|
|
|
editor.setGutterMarker(line, "gutter-info", div);
|
|
|
|
}
|
2017-03-24 22:10:35 +00:00
|
|
|
if (data.errors.length > 0) {
|
2017-04-04 18:19:47 +00:00
|
|
|
// TODO: move cursor to error line if offscreen?
|
2017-01-06 14:49:07 +00:00
|
|
|
toolbar.addClass("has-errors");
|
2016-12-16 01:21:51 +00:00
|
|
|
editor.clearGutter("gutter-info");
|
2017-02-20 23:50:29 +00:00
|
|
|
var numLines = editor.lineCount();
|
2017-03-24 22:10:35 +00:00
|
|
|
for (info of data.errors) {
|
2017-02-20 23:50:29 +00:00
|
|
|
var line = info.line-1;
|
|
|
|
if (line < 0 || line >= numLines) line = numLines-1;
|
|
|
|
addErrorMarker(line, info.msg);
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
current_output = null;
|
|
|
|
} else {
|
|
|
|
// load ROM
|
2017-03-24 22:10:35 +00:00
|
|
|
var rom = data.output;
|
2017-11-14 14:33:15 +00:00
|
|
|
var rom_changed = rom && !arrayCompare(rom.code||rom, current_output);
|
2016-12-16 01:21:51 +00:00
|
|
|
if (rom_changed) {
|
|
|
|
try {
|
|
|
|
//console.log("Loading ROM length", rom.length);
|
2016-12-31 16:05:22 +00:00
|
|
|
platform.loadROM(getCurrentPresetTitle(), rom);
|
2017-11-28 02:08:19 +00:00
|
|
|
if (!userPaused) resume();
|
2016-12-16 01:21:51 +00:00
|
|
|
current_output = rom;
|
2017-04-22 01:56:49 +00:00
|
|
|
resetProfiler();
|
2017-01-16 19:13:03 +00:00
|
|
|
toolbar.removeClass("has-errors");
|
2016-12-16 01:21:51 +00:00
|
|
|
} catch (e) {
|
2017-01-16 15:35:19 +00:00
|
|
|
console.log(e); // TODO: show error
|
2017-01-16 19:13:03 +00:00
|
|
|
toolbar.addClass("has-errors");
|
|
|
|
addErrorMarker(0, e+"");
|
2016-12-16 01:21:51 +00:00
|
|
|
current_output = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rom_changed || trace_pending_at_pc) {
|
|
|
|
// update editor annotations
|
2017-01-17 21:50:57 +00:00
|
|
|
// TODO: do incrementally for performance
|
2016-12-16 01:21:51 +00:00
|
|
|
editor.clearGutter("gutter-info");
|
|
|
|
editor.clearGutter("gutter-bytes");
|
|
|
|
editor.clearGutter("gutter-offset");
|
|
|
|
editor.clearGutter("gutter-clock");
|
2017-03-24 22:10:35 +00:00
|
|
|
for (var info of data.lines) {
|
2017-01-14 02:31:04 +00:00
|
|
|
if (info.offset >= 0) {
|
|
|
|
var textel = document.createTextNode(hex(info.offset,4));
|
2016-12-16 01:21:51 +00:00
|
|
|
editor.setGutterMarker(info.line-1, "gutter-offset", textel);
|
|
|
|
}
|
|
|
|
if (info.insns) {
|
2018-02-19 00:19:20 +00:00
|
|
|
var insnstr = info.insns.length > 9 ? ("...") : info.insns;
|
2016-12-16 01:21:51 +00:00
|
|
|
var textel = document.createTextNode(insnstr);
|
|
|
|
editor.setGutterMarker(info.line-1, "gutter-bytes", textel);
|
|
|
|
if (info.iscode) {
|
|
|
|
var opcode = parseInt(info.insns.split()[0], 16);
|
2017-01-16 15:35:19 +00:00
|
|
|
if (platform.getOpcodeMetadata) {
|
|
|
|
var meta = platform.getOpcodeMetadata(opcode, info.offset);
|
|
|
|
var clockstr = meta.minCycles+"";
|
|
|
|
var textel = document.createTextNode(clockstr);
|
|
|
|
editor.setGutterMarker(info.line-1, "gutter-clock", textel);
|
|
|
|
}
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-29 15:31:11 +00:00
|
|
|
updateDisassembly();
|
2016-12-16 01:21:51 +00:00
|
|
|
if (trace_pending_at_pc) {
|
|
|
|
showLoopTimingForPC(trace_pending_at_pc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
trace_pending_at_pc = null;
|
|
|
|
}
|
|
|
|
|
2017-03-24 22:10:35 +00:00
|
|
|
worker.onmessage = function(e) {
|
2017-04-12 16:23:24 +00:00
|
|
|
toolbar.removeClass("is-busy");
|
|
|
|
$('#compile_spinner').css('visibility', 'hidden');
|
2017-03-24 22:10:35 +00:00
|
|
|
if (pendingWorkerMessages > 1) {
|
|
|
|
pendingWorkerMessages = 0;
|
|
|
|
setCode(editor.getValue());
|
2017-04-13 19:48:37 +00:00
|
|
|
} else {
|
|
|
|
pendingWorkerMessages = 0;
|
2017-03-24 22:10:35 +00:00
|
|
|
}
|
|
|
|
setCompileOutput(e.data);
|
|
|
|
}
|
|
|
|
|
2016-12-16 01:21:51 +00:00
|
|
|
function setCurrentLine(line) {
|
|
|
|
editor.setSelection({line:line,ch:0}, {line:line-1,ch:0}, {scroll:true});
|
|
|
|
}
|
|
|
|
|
|
|
|
var lastDebugInfo;
|
2017-01-06 14:49:07 +00:00
|
|
|
var lastDebugState;
|
2016-12-16 01:21:51 +00:00
|
|
|
|
|
|
|
function showMemory(state) {
|
|
|
|
var s = "";
|
2017-11-24 19:14:22 +00:00
|
|
|
if (state && platform.cpuStateToLongString) {
|
2017-01-14 02:31:04 +00:00
|
|
|
s = platform.cpuStateToLongString(state.c);
|
2017-01-20 03:42:58 +00:00
|
|
|
if (platform.getRasterPosition) {
|
|
|
|
var pos = platform.getRasterPosition();
|
|
|
|
s += "H:" + pos.x + " V:" + pos.y + "\n"; // TODO: padding
|
|
|
|
}
|
2017-01-14 02:31:04 +00:00
|
|
|
if (platform.ramStateToLongString) {
|
|
|
|
s += platform.ramStateToLongString(state);
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
var hs = lastDebugInfo ? highlightDifferences(lastDebugInfo, s) : s;
|
|
|
|
$("#mem_info").show().html(hs);
|
|
|
|
lastDebugInfo = s;
|
|
|
|
} else {
|
|
|
|
$("#mem_info").hide();
|
|
|
|
lastDebugInfo = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function setupBreakpoint() {
|
|
|
|
// TODO
|
2016-12-31 16:05:22 +00:00
|
|
|
platform.setupDebug(function(state) {
|
2017-01-06 14:49:07 +00:00
|
|
|
lastDebugState = state;
|
2017-11-24 19:14:22 +00:00
|
|
|
if (state.c) {
|
|
|
|
var PC = state.c.PC;
|
|
|
|
var line = sourcefile.findLineForOffset(PC);
|
|
|
|
if (line >= 0) {
|
|
|
|
console.log("BREAKPOINT", hex(PC), line);
|
|
|
|
setCurrentLine(line);
|
|
|
|
} else {
|
|
|
|
console.log("BREAKPOINT", hex(PC));
|
|
|
|
// TODO: switch to disasm
|
|
|
|
}
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
showMemory(state);
|
2017-01-06 14:49:07 +00:00
|
|
|
updateDisassembly();
|
2016-12-31 16:05:22 +00:00
|
|
|
});
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
2017-11-16 17:08:06 +00:00
|
|
|
function _pause() {
|
2016-12-31 16:05:22 +00:00
|
|
|
if (platform.isRunning()) {
|
|
|
|
platform.pause();
|
2018-02-21 18:54:53 +00:00
|
|
|
console.log("Paused");
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
2017-11-16 17:08:06 +00:00
|
|
|
$("#dbg_pause").addClass("btn_stopped");
|
|
|
|
$("#dbg_go").removeClass("btn_active");
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
2017-11-16 17:08:06 +00:00
|
|
|
function pause() {
|
2016-12-16 01:21:51 +00:00
|
|
|
clearBreakpoint();
|
2017-11-16 17:08:06 +00:00
|
|
|
_pause();
|
2017-11-28 02:08:19 +00:00
|
|
|
userPaused = true;
|
2017-11-16 17:08:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function _resume() {
|
2016-12-31 16:05:22 +00:00
|
|
|
if (! platform.isRunning()) {
|
|
|
|
platform.resume();
|
2018-02-21 18:54:53 +00:00
|
|
|
console.log("Resumed");
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
2017-11-16 17:08:06 +00:00
|
|
|
$("#dbg_pause").removeClass("btn_stopped");
|
|
|
|
$("#dbg_go").addClass("btn_active");
|
|
|
|
}
|
|
|
|
|
|
|
|
function resume() {
|
|
|
|
clearBreakpoint();
|
2017-11-24 19:14:22 +00:00
|
|
|
if (! platform.isRunning() )
|
2017-11-16 17:08:06 +00:00
|
|
|
editor.setSelection(editor.getCursor()); // TODO??
|
|
|
|
_resume();
|
2017-11-28 02:08:19 +00:00
|
|
|
userPaused = false;
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function singleStep() {
|
|
|
|
setupBreakpoint();
|
2016-12-31 16:05:22 +00:00
|
|
|
platform.step();
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
2017-11-24 19:14:22 +00:00
|
|
|
function singleFrameStep() {
|
|
|
|
setupBreakpoint();
|
|
|
|
platform.runToVsync();
|
|
|
|
}
|
|
|
|
|
2016-12-16 01:21:51 +00:00
|
|
|
function getCurrentLine() {
|
|
|
|
return editor.getCursor().line+1;
|
|
|
|
}
|
|
|
|
|
2017-02-18 22:50:51 +00:00
|
|
|
function getDisasmViewPC() {
|
|
|
|
var line = disasmview.getCursor().line;
|
2017-02-25 15:57:39 +00:00
|
|
|
if (line >= 0) {
|
2017-02-18 22:50:51 +00:00
|
|
|
var toks = disasmview.getLine(line).split(/\s+/);
|
2017-02-25 15:57:39 +00:00
|
|
|
if (toks && toks[0].length == 4) {
|
2017-02-18 22:50:51 +00:00
|
|
|
return parseInt(toks[0], 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-05 04:19:54 +00:00
|
|
|
function getCurrentPC() {
|
2017-01-16 15:35:19 +00:00
|
|
|
var line = getCurrentLine();
|
2017-02-25 15:57:39 +00:00
|
|
|
while (line >= 0) {
|
|
|
|
var pc = sourcefile.line2offset[line];
|
|
|
|
if (pc >= 0) return pc;
|
|
|
|
line--;
|
2017-02-05 04:19:54 +00:00
|
|
|
}
|
2017-02-25 15:57:39 +00:00
|
|
|
return getDisasmViewPC();
|
2017-02-05 04:19:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function runToCursor() {
|
|
|
|
setupBreakpoint();
|
|
|
|
var pc = getCurrentPC();
|
2017-01-14 02:31:04 +00:00
|
|
|
if (pc >= 0) {
|
2017-02-05 04:19:54 +00:00
|
|
|
console.log("Run to", pc.toString(16));
|
2017-01-06 16:57:28 +00:00
|
|
|
platform.runEval(function(c) {
|
|
|
|
return c.PC == pc;
|
|
|
|
});
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-07 18:05:02 +00:00
|
|
|
function runUntilReturn() {
|
|
|
|
setupBreakpoint();
|
2017-01-14 02:31:04 +00:00
|
|
|
platform.runUntilReturn();
|
2017-01-07 18:05:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function runStepBackwards() {
|
|
|
|
setupBreakpoint();
|
|
|
|
platform.stepBack();
|
|
|
|
}
|
|
|
|
|
2016-12-16 01:21:51 +00:00
|
|
|
function clearBreakpoint() {
|
2017-01-06 14:49:07 +00:00
|
|
|
lastDebugState = null;
|
2017-11-11 19:45:32 +00:00
|
|
|
if (platform.clearDebug) platform.clearDebug();
|
2016-12-16 01:21:51 +00:00
|
|
|
showMemory();
|
|
|
|
}
|
|
|
|
|
|
|
|
function getClockCountsAtPC(pc) {
|
2016-12-31 16:05:22 +00:00
|
|
|
var opcode = platform.readAddress(pc);
|
|
|
|
var meta = platform.getOpcodeMetadata(opcode, pc);
|
2016-12-16 01:21:51 +00:00
|
|
|
return meta; // minCycles, maxCycles
|
|
|
|
}
|
|
|
|
|
|
|
|
function byte2signed(b) {
|
|
|
|
b &= 0xff;
|
|
|
|
return (b < 0x80) ? b : -(256-b);
|
|
|
|
}
|
|
|
|
|
|
|
|
// [taken, not taken]
|
|
|
|
var BRANCH_CONSTRAINTS = [
|
|
|
|
[{N:0},{N:1}],
|
|
|
|
[{N:1},{N:0}],
|
|
|
|
[{V:0},{V:1}],
|
|
|
|
[{V:1},{V:0}],
|
|
|
|
[{C:0},{C:1}],
|
|
|
|
[{C:1},{C:0}],
|
|
|
|
[{Z:0},{Z:1}],
|
|
|
|
[{Z:1},{Z:0}]
|
|
|
|
];
|
|
|
|
|
|
|
|
function constraintEquals(a,b) {
|
|
|
|
if (a == null || b == null)
|
|
|
|
return null;
|
|
|
|
for (var n in a) {
|
|
|
|
if (b[n] !== 'undefined')
|
|
|
|
return a[n] == b[n];
|
|
|
|
}
|
|
|
|
for (var n in b) {
|
|
|
|
if (a[n] !== 'undefined')
|
|
|
|
return a[n] == b[n];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
function showLoopTimingForCurrentLine() {
|
|
|
|
var line = getCurrentLine();
|
|
|
|
var pc = line2offset[line];
|
|
|
|
if (pc) {
|
|
|
|
showLoopTimingForPC(pc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2017-01-16 04:47:12 +00:00
|
|
|
function jumpToLine(ed, i) {
|
|
|
|
var t = ed.charCoords({line: i, ch: 0}, "local").top;
|
|
|
|
var middleHeight = ed.getScrollerElement().offsetHeight / 2;
|
|
|
|
ed.scrollTo(null, t - middleHeight - 5);
|
|
|
|
}
|
|
|
|
|
2017-04-22 17:33:20 +00:00
|
|
|
function getVisibleSourceFile() {
|
|
|
|
var div = $("#disassembly");
|
|
|
|
return div.is(':visible') ? assemblyfile : sourcefile;
|
|
|
|
}
|
|
|
|
|
2017-01-06 14:49:07 +00:00
|
|
|
function updateDisassembly() {
|
|
|
|
var div = $("#disassembly");
|
|
|
|
if (div.is(':visible')) {
|
|
|
|
var state = lastDebugState || platform.saveState();
|
2017-11-28 02:08:19 +00:00
|
|
|
var pc = state.c ? state.c.PC : 0;
|
2017-01-16 04:47:12 +00:00
|
|
|
if (assemblyfile && assemblyfile.text) {
|
2017-04-29 15:31:11 +00:00
|
|
|
var asmtext = assemblyfile.text;
|
|
|
|
if (platform_id == 'base_z80') { // TODO
|
|
|
|
asmtext = asmtext.replace(/[ ]+\d+\s+;.+\n/g, '');
|
|
|
|
asmtext = asmtext.replace(/[ ]+\d+\s+.area .+\n/g, '');
|
|
|
|
}
|
|
|
|
disasmview.setValue(asmtext);
|
2017-02-18 22:50:51 +00:00
|
|
|
var findPC = platform.getDebugCallback() ? pc : getCurrentPC();
|
|
|
|
if (findPC) {
|
|
|
|
var lineno = assemblyfile.findLineForOffset(findPC);
|
2017-01-16 04:47:12 +00:00
|
|
|
if (lineno) {
|
2017-02-18 22:50:51 +00:00
|
|
|
if (platform.getDebugCallback()) disasmview.setCursor(lineno-1, 0);
|
2017-01-16 04:47:12 +00:00
|
|
|
jumpToLine(disasmview, lineno-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-29 15:31:11 +00:00
|
|
|
else if (platform.disassemble) {
|
|
|
|
var curline = 0;
|
|
|
|
var selline = 0;
|
|
|
|
// TODO: not perfect disassembler
|
|
|
|
function disassemble(start, end) {
|
|
|
|
if (start < 0) start = 0;
|
|
|
|
if (end > 0xffff) end = 0xffff;
|
|
|
|
// TODO: use pc2visits
|
|
|
|
var a = start;
|
|
|
|
var s = "";
|
|
|
|
while (a < end) {
|
|
|
|
var disasm = platform.disassemble(a, platform.readAddress);
|
|
|
|
var srclinenum = sourcefile.offset2line[a];
|
|
|
|
if (srclinenum) {
|
|
|
|
var srcline = editor.getLine(srclinenum-1);
|
|
|
|
if (srcline && srcline.trim().length) {
|
|
|
|
s += "; " + srclinenum + ":\t" + srcline + "\n";
|
|
|
|
}
|
2017-01-08 15:51:19 +00:00
|
|
|
}
|
2017-04-29 15:31:11 +00:00
|
|
|
var bytes = "";
|
|
|
|
for (var i=0; i<disasm.nbytes; i++)
|
|
|
|
bytes += hex(platform.readAddress(a+i));
|
|
|
|
while (bytes.length < 14)
|
|
|
|
bytes += ' ';
|
|
|
|
var dline = hex(parseInt(a)) + "\t" + bytes + "\t" + disasm.line + "\n";
|
|
|
|
s += dline;
|
|
|
|
if (a == pc) selline = curline;
|
|
|
|
curline++;
|
|
|
|
a += disasm.nbytes || 1;
|
2017-01-08 15:51:19 +00:00
|
|
|
}
|
2017-04-29 15:31:11 +00:00
|
|
|
return s;
|
2017-01-08 15:51:19 +00:00
|
|
|
}
|
2017-04-29 15:31:11 +00:00
|
|
|
var text = disassemble(pc-96, pc) + disassemble(pc, pc+96);
|
|
|
|
disasmview.setValue(text);
|
|
|
|
disasmview.setCursor(selline, 0);
|
|
|
|
jumpToLine(disasmview, selline);
|
2018-02-15 00:26:40 +00:00
|
|
|
} else if (current_output && current_output.code) {
|
2017-11-28 02:08:19 +00:00
|
|
|
// show verilog javascript
|
|
|
|
disasmview.setValue(current_output.code);
|
2017-01-08 15:51:19 +00:00
|
|
|
}
|
2017-01-06 14:49:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function toggleDisassembly() {
|
|
|
|
$("#disassembly").toggle();
|
|
|
|
$("#editor").toggle();
|
|
|
|
updateDisassembly();
|
2017-04-26 01:14:17 +00:00
|
|
|
if (profilelist) createProfileWindow();
|
2017-01-06 14:49:07 +00:00
|
|
|
}
|
|
|
|
|
2016-12-16 01:21:51 +00:00
|
|
|
function resetAndDebug() {
|
2017-11-29 01:48:27 +00:00
|
|
|
if (platform.setupDebug && platform.readAddress) { // TODO??
|
2017-11-11 19:45:32 +00:00
|
|
|
clearBreakpoint();
|
2017-11-16 17:08:06 +00:00
|
|
|
_resume();
|
2017-11-11 19:45:32 +00:00
|
|
|
platform.reset();
|
|
|
|
setupBreakpoint();
|
|
|
|
platform.runEval(function(c) { return true; });
|
|
|
|
} else {
|
|
|
|
platform.reset();
|
|
|
|
}
|
2017-01-03 01:42:15 +00:00
|
|
|
}
|
2017-01-06 16:57:28 +00:00
|
|
|
|
2017-01-07 18:05:02 +00:00
|
|
|
function _breakExpression() {
|
2017-01-14 02:31:04 +00:00
|
|
|
var exprs = window.prompt("Enter break expression", "c.PC == 0x6000"); // TODO
|
2017-01-07 18:05:02 +00:00
|
|
|
if (exprs) {
|
|
|
|
var fn = new Function('c', 'return (' + exprs + ');');
|
|
|
|
setupBreakpoint();
|
|
|
|
platform.runEval(fn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-22 01:56:49 +00:00
|
|
|
function getSymbolAtAddress(a) {
|
|
|
|
if (addr2symbol[a]) return addr2symbol[a];
|
|
|
|
var i=0;
|
|
|
|
while (--a >= 0) {
|
|
|
|
i++;
|
|
|
|
if (addr2symbol[a]) return addr2symbol[a] + '+' + i;
|
|
|
|
}
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
2017-04-19 01:18:53 +00:00
|
|
|
function updateDebugWindows() {
|
|
|
|
if (platform.isRunning()) {
|
|
|
|
updateMemoryWindow();
|
|
|
|
updateProfileWindow();
|
|
|
|
}
|
|
|
|
setTimeout(updateDebugWindows, 200);
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateProfileWindow() {
|
2017-04-26 01:14:17 +00:00
|
|
|
if (profilelist && sourcefile) {
|
2017-04-19 01:18:53 +00:00
|
|
|
$("#profileview").find('[data-index]').each(function(i,e) {
|
|
|
|
var div = $(e);
|
|
|
|
var lineno = div.attr('data-index') | 0;
|
|
|
|
var newtext = getProfileLine(lineno+1);
|
|
|
|
if (newtext) {
|
|
|
|
var oldtext = div.text();
|
|
|
|
if (oldtext != newtext)
|
|
|
|
div.text(newtext);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateMemoryWindow() {
|
2017-04-26 01:14:17 +00:00
|
|
|
if (memorylist) {
|
2017-04-19 01:18:53 +00:00
|
|
|
$("#memoryview").find('[data-index]').each(function(i,e) {
|
|
|
|
var div = $(e);
|
2017-04-22 01:56:49 +00:00
|
|
|
var row = div.attr('data-index');
|
2017-04-19 01:18:53 +00:00
|
|
|
var oldtext = div.text();
|
2017-04-22 01:56:49 +00:00
|
|
|
var newtext = getMemoryLineAt(row);
|
2017-04-19 01:18:53 +00:00
|
|
|
if (oldtext != newtext)
|
|
|
|
div.text(newtext);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-22 01:56:49 +00:00
|
|
|
function getMemoryLineAt(row) {
|
|
|
|
var offset = row * 16;
|
|
|
|
var n1 = 0;
|
|
|
|
var n2 = 16;
|
|
|
|
var sym;
|
|
|
|
if (getDumpLines()) {
|
|
|
|
var dl = dumplines[row];
|
|
|
|
if (dl) {
|
|
|
|
offset = dl.a & 0xfff0;
|
|
|
|
n1 = dl.a - offset;
|
|
|
|
n2 = n1 + dl.l;
|
|
|
|
sym = dl.s;
|
|
|
|
} else {
|
|
|
|
return '.';
|
|
|
|
}
|
|
|
|
}
|
2017-04-19 01:18:53 +00:00
|
|
|
var s = hex(offset,4) + ' ';
|
2017-04-22 01:56:49 +00:00
|
|
|
for (var i=0; i<n1; i++) s += ' ';
|
|
|
|
if (n1 > 8) s += ' ';
|
|
|
|
for (var i=n1; i<n2; i++) {
|
|
|
|
var read = platform.readAddress(offset+i);
|
2017-04-19 01:18:53 +00:00
|
|
|
if (i==8) s += ' ';
|
|
|
|
s += ' ' + (read>=0?hex(read,2):'??');
|
|
|
|
}
|
2017-04-22 01:56:49 +00:00
|
|
|
for (var i=n2; i<16; i++) s += ' ';
|
|
|
|
if (sym) s += ' ' + sym;
|
2017-04-19 01:18:53 +00:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2017-04-22 17:33:20 +00:00
|
|
|
function getVisibleEditorLineHeight() {
|
|
|
|
return $(".CodeMirror-line:visible").first().height();
|
2017-04-19 01:18:53 +00:00
|
|
|
}
|
|
|
|
|
2017-04-22 01:56:49 +00:00
|
|
|
function getDumpLineAt(line) {
|
|
|
|
var d = dumplines[line];
|
|
|
|
if (d) {
|
|
|
|
return d.a + " " + d.s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var IGNORE_SYMS = {s__INITIALIZER:true, /* s__GSINIT:true, */ _color_prom:true};
|
|
|
|
|
|
|
|
function getDumpLines() {
|
|
|
|
if (!dumplines && addr2symbol) {
|
|
|
|
dumplines = [];
|
|
|
|
var ofs = 0;
|
|
|
|
var sym;
|
|
|
|
for (var nextofs in addr2symbol) {
|
|
|
|
nextofs |= 0;
|
|
|
|
var nextsym = addr2symbol[nextofs];
|
|
|
|
if (sym) {
|
|
|
|
if (IGNORE_SYMS[sym]) {
|
|
|
|
ofs = nextofs;
|
|
|
|
} else {
|
|
|
|
while (ofs < nextofs) {
|
|
|
|
var ofs2 = (ofs + 16) & 0xffff0;
|
|
|
|
if (ofs2 > nextofs) ofs2 = nextofs;
|
|
|
|
//if (ofs < 1000) console.log(ofs, ofs2, nextofs, sym);
|
|
|
|
dumplines.push({a:ofs, l:ofs2-ofs, s:sym});
|
|
|
|
ofs = ofs2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sym = nextsym;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dumplines;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getMemorySegment(a) {
|
|
|
|
if (!compparams) return 'unknown';
|
|
|
|
if (a >= compparams.data_start && a < compparams.data_start+compparams.data_size) {
|
|
|
|
if (platform.getSP && a >= platform.getSP() - 15)
|
|
|
|
return 'stack';
|
|
|
|
else
|
|
|
|
return 'data';
|
|
|
|
}
|
|
|
|
else if (a >= compparams.code_start && a < compparams.code_start+compparams.code_size)
|
|
|
|
return 'code';
|
|
|
|
else
|
|
|
|
return 'unknown';
|
|
|
|
}
|
|
|
|
|
2017-04-23 13:03:05 +00:00
|
|
|
function findMemoryWindowLine(a) {
|
|
|
|
for (var i=0; i<dumplines.length; i++)
|
|
|
|
if (dumplines[i].a >= a)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2017-04-19 01:18:53 +00:00
|
|
|
function showMemoryWindow() {
|
2017-04-26 01:14:17 +00:00
|
|
|
memorylist = new VirtualList({
|
2017-04-19 01:18:53 +00:00
|
|
|
w:$("#emulator").width(),
|
|
|
|
h:$("#emulator").height(),
|
2017-04-22 17:33:20 +00:00
|
|
|
itemHeight: getVisibleEditorLineHeight(),
|
2017-04-19 01:18:53 +00:00
|
|
|
totalRows: 0x1000,
|
|
|
|
generatorFn: function(row) {
|
2017-04-22 01:56:49 +00:00
|
|
|
var s = getMemoryLineAt(row);
|
2017-04-19 01:18:53 +00:00
|
|
|
var div = document.createElement("div");
|
2017-04-22 01:56:49 +00:00
|
|
|
if (dumplines) {
|
|
|
|
var dlr = dumplines[row];
|
|
|
|
if (dlr) div.classList.add('seg_' + getMemorySegment(dumplines[row].a));
|
|
|
|
}
|
2017-04-19 01:18:53 +00:00
|
|
|
div.appendChild(document.createTextNode(s));
|
|
|
|
return div;
|
|
|
|
}
|
|
|
|
});
|
2017-04-26 01:14:17 +00:00
|
|
|
$("#memoryview").empty().append(memorylist.container);
|
2017-04-19 01:18:53 +00:00
|
|
|
updateMemoryWindow();
|
2017-04-23 13:03:05 +00:00
|
|
|
if (compparams && dumplines)
|
2017-04-26 01:14:17 +00:00
|
|
|
memorylist.scrollToItem(findMemoryWindowLine(compparams.data_start));
|
2017-04-19 01:18:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function toggleMemoryWindow() {
|
|
|
|
if ($("#profileview").is(':visible')) toggleProfileWindow();
|
|
|
|
if ($("#memoryview").is(':visible')) {
|
2017-04-26 01:14:17 +00:00
|
|
|
memorylist = null;
|
2017-04-19 01:18:53 +00:00
|
|
|
$("#emulator").show();
|
|
|
|
$("#memoryview").hide();
|
|
|
|
} else {
|
|
|
|
showMemoryWindow();
|
|
|
|
$("#emulator").hide();
|
|
|
|
$("#memoryview").show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function createProfileWindow() {
|
2017-04-26 01:14:17 +00:00
|
|
|
profilelist = new VirtualList({
|
2017-04-19 01:18:53 +00:00
|
|
|
w:$("#emulator").width(),
|
|
|
|
h:$("#emulator").height(),
|
2017-04-22 17:33:20 +00:00
|
|
|
itemHeight: getVisibleEditorLineHeight(),
|
|
|
|
totalRows: getVisibleSourceFile().lineCount(),
|
2017-04-19 01:18:53 +00:00
|
|
|
generatorFn: function(row) {
|
|
|
|
var div = document.createElement("div");
|
|
|
|
div.appendChild(document.createTextNode("."));
|
|
|
|
return div;
|
|
|
|
}
|
|
|
|
});
|
2017-04-26 01:14:17 +00:00
|
|
|
$("#profileview").empty().append(profilelist.container);
|
2017-04-19 01:18:53 +00:00
|
|
|
updateProfileWindow();
|
|
|
|
}
|
|
|
|
|
2017-04-22 01:56:49 +00:00
|
|
|
var pcdata = {};
|
2017-04-19 01:18:53 +00:00
|
|
|
var prof_reads, prof_writes;
|
2017-04-22 01:56:49 +00:00
|
|
|
var dumplines;
|
|
|
|
|
|
|
|
function resetProfiler() {
|
|
|
|
prof_reads = [];
|
|
|
|
prof_writes = [];
|
|
|
|
pcdata = [];
|
|
|
|
dumplines = null;
|
|
|
|
}
|
2017-04-19 01:18:53 +00:00
|
|
|
|
|
|
|
function profileWindowCallback(a,v) {
|
2017-04-22 01:56:49 +00:00
|
|
|
if (platform.getPC) {
|
|
|
|
var pc = platform.getPC();
|
|
|
|
var pcd = pcdata[pc];
|
|
|
|
if (!pcd) {
|
|
|
|
pcd = pcdata[pc] = {nv:1};
|
|
|
|
}
|
|
|
|
if (a != pc) {
|
|
|
|
if (v >= 0) {
|
|
|
|
pcd.lastwa = a;
|
|
|
|
pcd.lastwv = v;
|
|
|
|
} else {
|
|
|
|
pcd.lastra = a;
|
|
|
|
pcd.lastrv = platform.readAddress(a);
|
|
|
|
}
|
|
|
|
} else {
|
2017-04-22 17:33:20 +00:00
|
|
|
pcd.nv++;
|
2017-04-22 01:56:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getProfileLine(line) {
|
2017-04-23 13:03:05 +00:00
|
|
|
var srcfile = getVisibleSourceFile();
|
|
|
|
var offset = srcfile.line2offset[line];
|
|
|
|
var offset2 = srcfile.line2offset[line+1];
|
|
|
|
if (!(offset2 > offset)) offset2 = offset+1;
|
|
|
|
var s = '';
|
|
|
|
var nv = 0;
|
|
|
|
while (offset < offset2) {
|
2017-04-22 01:56:49 +00:00
|
|
|
var pcd = pcdata[offset];
|
|
|
|
if (pcd) {
|
2017-04-23 13:03:05 +00:00
|
|
|
nv += pcd.nv;
|
2017-04-22 01:56:49 +00:00
|
|
|
if (pcd.lastra >= 0) {
|
2017-04-23 13:03:05 +00:00
|
|
|
s += " rd [" + hex(pcd.lastra,4) + "] == " + hex(pcd.lastrv,2);
|
2017-04-22 01:56:49 +00:00
|
|
|
}
|
|
|
|
if (pcd.lastwa >= 0) {
|
2017-04-23 13:03:05 +00:00
|
|
|
s += " wr " + hex(pcd.lastwv,2) + " -> [" + hex(pcd.lastwa,4) + "]";
|
2017-04-22 01:56:49 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-23 13:03:05 +00:00
|
|
|
offset++;
|
2017-04-19 01:18:53 +00:00
|
|
|
}
|
2017-04-23 13:03:05 +00:00
|
|
|
return nv ? (lpad(nv+"",8) + s) : '.';
|
2017-04-19 01:18:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function toggleProfileWindow() {
|
|
|
|
if ($("#memoryview").is(':visible')) toggleMemoryWindow();
|
|
|
|
if ($("#profileview").is(':visible')) {
|
2017-04-26 01:14:17 +00:00
|
|
|
profilelist = null;
|
2017-04-19 01:18:53 +00:00
|
|
|
platform.getProbe().deactivate();
|
|
|
|
$("#emulator").show();
|
|
|
|
$("#profileview").hide();
|
|
|
|
} else {
|
|
|
|
createProfileWindow();
|
|
|
|
platform.getProbe().activate(profileWindowCallback);
|
|
|
|
$("#emulator").hide();
|
|
|
|
$("#profileview").show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-04 15:54:56 +00:00
|
|
|
function handleWindowMessage(e) {
|
2017-11-21 21:36:38 +00:00
|
|
|
//console.log("window message", e.data);
|
2017-05-04 15:54:56 +00:00
|
|
|
if (e.data.bytes) {
|
|
|
|
editor.replaceSelection(e.data.bytestr);
|
|
|
|
}
|
|
|
|
if (e.data.close) {
|
2017-05-10 01:46:45 +00:00
|
|
|
$("#pixeditback").hide();
|
2017-05-04 15:54:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function openBitmapEditorWithParams(fmt, bytestr, palfmt, palstr) {
|
2017-05-10 01:46:45 +00:00
|
|
|
$("#pixeditback").show();
|
2017-05-04 15:54:56 +00:00
|
|
|
pixeditframe.contentWindow.postMessage({fmt:fmt, bytestr:bytestr, palfmt:palfmt, palstr:palstr}, '*');
|
|
|
|
}
|
|
|
|
|
2017-05-04 23:25:41 +00:00
|
|
|
function lookBackwardsForJSONComment(line, req) {
|
2017-05-05 13:51:47 +00:00
|
|
|
var re = /[/;][*;]([{].+[}])[*;][/;]/;
|
2017-05-04 15:54:56 +00:00
|
|
|
while (--line >= 0) {
|
|
|
|
var s = editor.getLine(line);
|
|
|
|
var m = re.exec(s);
|
|
|
|
if (m) {
|
|
|
|
var jsontxt = m[1].replace(/([A-Za-z]+):/g, '"$1":'); // fix lenient JSON
|
|
|
|
var obj = JSON.parse(jsontxt);
|
2017-05-04 23:25:41 +00:00
|
|
|
if (obj[req]) {
|
|
|
|
var start = {obj:obj, line:line, ch:s.indexOf(m[0])+m[0].length};
|
|
|
|
var line0 = line;
|
2017-05-05 13:51:47 +00:00
|
|
|
var pos0 = start.ch;
|
2017-05-04 23:25:41 +00:00
|
|
|
line--;
|
|
|
|
while (++line < editor.lineCount()) {
|
2017-11-21 16:15:08 +00:00
|
|
|
var l = editor.getLine(line);
|
|
|
|
var endsection;
|
|
|
|
if (platform_id == 'verilog')
|
|
|
|
endsection = l.indexOf('end') >= pos0;
|
|
|
|
else
|
|
|
|
endsection = l.indexOf(';') >= pos0;
|
|
|
|
if (endsection) {
|
2017-05-04 23:25:41 +00:00
|
|
|
var end = {line:line, ch:editor.getLine(line).length};
|
|
|
|
return {obj:obj, start:start, end:end};
|
|
|
|
}
|
2017-05-05 13:51:47 +00:00
|
|
|
pos0 = 0;
|
2017-05-04 15:54:56 +00:00
|
|
|
}
|
2017-05-04 23:25:41 +00:00
|
|
|
line = line0;
|
2017-05-04 15:54:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function openBitmapEditorAtCursor() {
|
|
|
|
if ($("#pixeditback").is(":visible")) {
|
|
|
|
$("#pixeditback").hide(250);
|
|
|
|
return;
|
|
|
|
}
|
2017-05-05 13:51:47 +00:00
|
|
|
var data = lookBackwardsForJSONComment(getCurrentLine(), 'w');
|
|
|
|
if (data && data.obj && data.obj.w>0 && data.obj.h>0) {
|
2017-05-04 23:25:41 +00:00
|
|
|
var paldata = lookBackwardsForJSONComment(data.start.line-1, 'pal');
|
2017-05-04 15:54:56 +00:00
|
|
|
var palbytestr;
|
|
|
|
if (paldata) {
|
|
|
|
palbytestr = editor.getRange(paldata.start, paldata.end);
|
|
|
|
paldata = paldata.obj;
|
|
|
|
}
|
|
|
|
editor.setSelection(data.end, data.start);
|
|
|
|
openBitmapEditorWithParams(data.obj, editor.getSelection(), paldata, palbytestr);
|
|
|
|
} else {
|
2017-05-11 12:31:44 +00:00
|
|
|
alert("To edit graphics, move cursor to a constant array preceded by a comment in the format:\n\n/*{w:,h:,bpp:,count:...}*/\n\n(See code examples)");
|
2017-05-04 15:54:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-20 19:13:23 +00:00
|
|
|
function _recordVideo() {
|
|
|
|
var canvas = $("#emulator").find("canvas")[0];
|
|
|
|
if (!canvas) {
|
|
|
|
alert("Could not find canvas element to record video!");
|
|
|
|
return;
|
|
|
|
}
|
2017-05-25 19:49:30 +00:00
|
|
|
var rotate = 0;
|
|
|
|
if (canvas.style && canvas.style.transform) {
|
|
|
|
if (canvas.style.transform.indexOf("rotate(-90deg)") >= 0)
|
|
|
|
rotate = -1;
|
|
|
|
else if (canvas.style.transform.indexOf("rotate(90deg)") >= 0)
|
|
|
|
rotate = 1;
|
|
|
|
}
|
|
|
|
var gif = new GIF({
|
|
|
|
workerScript: 'gif.js/dist/gif.worker.js',
|
|
|
|
workers: 4,
|
|
|
|
quality: 10,
|
|
|
|
rotate: rotate
|
|
|
|
});
|
2017-05-20 19:13:23 +00:00
|
|
|
var img = $('#videoPreviewImage');
|
|
|
|
//img.attr('src', 'https://articulate-heroes.s3.amazonaws.com/uploads/rte/kgrtehja_DancingBannana.gif');
|
|
|
|
gif.on('finished', function(blob) {
|
|
|
|
img.attr('src', URL.createObjectURL(blob));
|
2017-05-25 19:49:30 +00:00
|
|
|
$("#pleaseWaitModal").modal('hide');
|
2017-11-16 17:08:06 +00:00
|
|
|
_resume();
|
2017-05-20 19:13:23 +00:00
|
|
|
$("#videoPreviewModal").modal('show');
|
|
|
|
});
|
|
|
|
var intervalMsec = 17;
|
|
|
|
var maxFrames = 500;
|
|
|
|
var nframes = 0;
|
|
|
|
console.log("Recording video", canvas);
|
|
|
|
var f = function() {
|
|
|
|
if (nframes++ > maxFrames) {
|
|
|
|
console.log("Rendering video");
|
2017-05-25 19:49:30 +00:00
|
|
|
$("#pleaseWaitModal").modal('show');
|
2017-11-16 17:08:06 +00:00
|
|
|
_pause();
|
2017-05-20 19:13:23 +00:00
|
|
|
gif.render();
|
|
|
|
} else {
|
2017-05-25 19:49:30 +00:00
|
|
|
gif.addFrame(canvas, {delay: intervalMsec, copy: true});
|
2017-05-20 19:13:23 +00:00
|
|
|
setTimeout(f, intervalMsec);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
f();
|
|
|
|
}
|
|
|
|
|
2016-12-16 01:21:51 +00:00
|
|
|
function setupDebugControls(){
|
|
|
|
$("#dbg_reset").click(resetAndDebug);
|
|
|
|
$("#dbg_pause").click(pause);
|
|
|
|
$("#dbg_go").click(resume);
|
2017-11-24 19:14:22 +00:00
|
|
|
|
|
|
|
if (platform.step)
|
2017-11-11 19:45:32 +00:00
|
|
|
$("#dbg_step").click(singleStep).show();
|
2017-11-24 19:14:22 +00:00
|
|
|
else
|
2017-11-11 19:45:32 +00:00
|
|
|
$("#dbg_step").hide();
|
2017-11-24 19:14:22 +00:00
|
|
|
if (platform.runToVsync)
|
|
|
|
$("#dbg_tovsync").click(singleFrameStep).show();
|
|
|
|
else
|
|
|
|
$("#dbg_tovsync").hide();
|
|
|
|
if (platform.runEval && platform_id != 'verilog')
|
|
|
|
$("#dbg_toline").click(runToCursor).show();
|
|
|
|
else
|
2017-11-11 19:45:32 +00:00
|
|
|
$("#dbg_toline").hide();
|
2017-11-24 19:14:22 +00:00
|
|
|
if (platform.runUntilReturn)
|
|
|
|
$("#dbg_stepout").click(runUntilReturn).show();
|
|
|
|
else
|
2017-11-11 19:45:32 +00:00
|
|
|
$("#dbg_stepout").hide();
|
2017-11-24 19:14:22 +00:00
|
|
|
if (platform.stepBack)
|
|
|
|
$("#dbg_stepback").click(runStepBackwards).show();
|
|
|
|
else
|
2017-11-11 19:45:32 +00:00
|
|
|
$("#dbg_stepback").hide();
|
2017-11-24 19:14:22 +00:00
|
|
|
|
2017-11-20 01:32:58 +00:00
|
|
|
if (window.traceTiming) {
|
2017-01-16 15:35:19 +00:00
|
|
|
$("#dbg_timing").click(traceTiming).show();
|
2017-11-24 19:14:22 +00:00
|
|
|
} else if (platform.readAddress) {
|
2017-04-19 01:18:53 +00:00
|
|
|
$("#dbg_memory").click(toggleMemoryWindow).show();
|
|
|
|
}
|
|
|
|
if (platform.getProbe) {
|
|
|
|
$("#dbg_profile").click(toggleProfileWindow).show();
|
|
|
|
}
|
2017-11-28 02:08:19 +00:00
|
|
|
if (platform.saveState) { // TODO: only show if listing or disasm available
|
2017-01-16 15:35:19 +00:00
|
|
|
$("#dbg_disasm").click(toggleDisassembly).show();
|
|
|
|
}
|
2017-01-06 14:49:07 +00:00
|
|
|
$("#disassembly").hide();
|
2017-05-04 15:54:56 +00:00
|
|
|
$("#dbg_bitmap").click(openBitmapEditorAtCursor);
|
2016-12-30 23:51:15 +00:00
|
|
|
$(".dropdown-menu").collapse({toggle: false});
|
|
|
|
$("#item_new_file").click(_createNewFile);
|
|
|
|
$("#item_share_file").click(_shareFile);
|
|
|
|
$("#item_reset_file").click(_resetPreset);
|
2017-11-24 19:14:22 +00:00
|
|
|
if (platform.runEval)
|
|
|
|
$("#item_debug_expr").click(_breakExpression).show();
|
|
|
|
else
|
|
|
|
$("#item_debug_expr").hide();
|
2017-02-02 19:11:52 +00:00
|
|
|
$("#item_download_rom").click(_downloadROMImage);
|
2017-05-20 19:13:23 +00:00
|
|
|
$("#item_record_video").click(_recordVideo);
|
2017-04-19 01:18:53 +00:00
|
|
|
updateDebugWindows();
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
2016-12-18 20:59:31 +00:00
|
|
|
function showWelcomeMessage() {
|
2017-05-01 11:37:48 +00:00
|
|
|
if (!localStorage.getItem("8bitworkshop.hello")) {
|
2016-12-30 23:51:15 +00:00
|
|
|
// Instance the tour
|
2017-04-20 00:55:13 +00:00
|
|
|
var is_vcs = platform_id == 'vcs';
|
2016-12-30 23:51:15 +00:00
|
|
|
var tour = new Tour({
|
2017-01-03 15:43:40 +00:00
|
|
|
autoscroll:false,
|
2016-12-30 23:51:15 +00:00
|
|
|
//storage:false,
|
|
|
|
steps: [
|
|
|
|
{
|
|
|
|
element: "#editor",
|
|
|
|
title: "Welcome to 8bitworkshop!",
|
2017-04-20 00:55:13 +00:00
|
|
|
content: is_vcs ? "Type your 6502 assembly code into the editor, and it'll be assembled in real-time. All changes are saved to browser local storage."
|
|
|
|
: "Type your C source code into the editor, and it'll be compiled in real-time. All changes are saved to browser local storage."
|
2016-12-30 23:51:15 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
element: "#emulator",
|
|
|
|
placement: 'left',
|
2017-04-20 00:55:13 +00:00
|
|
|
title: "Emulator",
|
|
|
|
content: "This is an emulator for the \"" + platform_id + "\" platform. We'll load your compiled code into the emulator whenever you make changes."
|
2016-12-30 23:51:15 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
element: "#preset_select",
|
|
|
|
title: "File Selector",
|
|
|
|
content: "Pick a code example from the book, or access your own files and files shared by others."
|
|
|
|
},
|
|
|
|
{
|
|
|
|
element: "#debug_bar",
|
|
|
|
placement: 'bottom',
|
|
|
|
title: "Debug Tools",
|
2017-04-20 00:55:13 +00:00
|
|
|
content: "Use these buttons to set breakpoints, single step through code, pause/resume, and use debugging tools."
|
2016-12-30 23:51:15 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
element: "#dropdownMenuButton",
|
|
|
|
title: "Main Menu",
|
2017-04-20 00:55:13 +00:00
|
|
|
content: "Click the menu to switch between platforms, create new files, or share your work with others."
|
2016-12-30 23:51:15 +00:00
|
|
|
},
|
|
|
|
]});
|
|
|
|
tour.init();
|
2017-01-03 15:43:40 +00:00
|
|
|
setTimeout(function() { tour.start(); }, 2000);
|
2016-12-18 20:59:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-16 01:21:51 +00:00
|
|
|
///////////////////////////////////////////////////
|
|
|
|
|
|
|
|
var qs = (function (a) {
|
|
|
|
if (!a || a == "")
|
|
|
|
return {};
|
|
|
|
var b = {};
|
|
|
|
for (var i = 0; i < a.length; ++i) {
|
|
|
|
var p = a[i].split('=', 2);
|
|
|
|
if (p.length == 1)
|
|
|
|
b[p[0]] = "";
|
|
|
|
else
|
|
|
|
b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
|
|
|
|
}
|
|
|
|
return b;
|
|
|
|
})(window.location.search.substr(1).split('&'));
|
|
|
|
|
2017-01-23 21:21:45 +00:00
|
|
|
function preloadWorker(fileid) {
|
|
|
|
var tool = platform.getToolForFilename(fileid);
|
|
|
|
if (tool) worker.postMessage({preload:tool});
|
|
|
|
}
|
|
|
|
|
2017-01-25 17:30:05 +00:00
|
|
|
function initPlatform() {
|
|
|
|
store = new FileStore(localStorage, platform_id + '/');
|
|
|
|
}
|
|
|
|
|
2017-04-26 19:44:55 +00:00
|
|
|
function showBookLink() {
|
|
|
|
if (platform_id == 'vcs')
|
|
|
|
$("#booklink_vcs").show();
|
2017-11-15 00:12:52 +00:00
|
|
|
else if (platform_id == 'mw8080bw' || platform_id == 'vicdual' || platform_id == 'galaxian-scramble' || platform_id == 'vector-z80color' || platform_id == 'williams-z80')
|
2017-04-26 19:44:55 +00:00
|
|
|
$("#booklink_arcade").show();
|
|
|
|
}
|
|
|
|
|
2017-05-01 15:30:47 +00:00
|
|
|
function addPageFocusHandlers() {
|
2017-05-02 13:09:53 +00:00
|
|
|
var hidden = false;
|
2017-05-01 15:30:47 +00:00
|
|
|
document.addEventListener("visibilitychange", function() {
|
2017-05-02 13:09:53 +00:00
|
|
|
if (document.visibilityState == 'hidden' && platform.isRunning()) {
|
2017-11-16 17:08:06 +00:00
|
|
|
_pause();
|
2017-05-02 13:09:53 +00:00
|
|
|
hidden = true;
|
|
|
|
} else if (document.visibilityState == 'visible' && hidden) {
|
2017-11-16 17:08:06 +00:00
|
|
|
_resume();
|
2017-05-02 13:09:53 +00:00
|
|
|
hidden = false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
$(window).on("focus", function() {
|
|
|
|
if (hidden) {
|
2017-11-16 17:08:06 +00:00
|
|
|
_resume();
|
2017-05-02 13:09:53 +00:00
|
|
|
hidden = false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
$(window).on("blur", function() {
|
|
|
|
if (platform.isRunning()) {
|
2017-11-16 17:08:06 +00:00
|
|
|
_pause();
|
2017-05-02 13:09:53 +00:00
|
|
|
hidden = true;
|
|
|
|
}
|
2017-05-01 15:30:47 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-01-14 16:14:25 +00:00
|
|
|
function startPlatform() {
|
2017-01-25 17:30:05 +00:00
|
|
|
initPlatform();
|
2017-01-29 21:06:05 +00:00
|
|
|
if (!PLATFORMS[platform_id]) throw Error("Invalid platform '" + platform_id + "'.");
|
2017-01-14 16:14:25 +00:00
|
|
|
platform = new PLATFORMS[platform_id]($("#emulator")[0]);
|
|
|
|
PRESETS = platform.getPresets();
|
|
|
|
if (qs['file']) {
|
2017-01-16 15:35:19 +00:00
|
|
|
// start platform and load file
|
2017-01-23 21:21:45 +00:00
|
|
|
preloadWorker(qs['file']);
|
2017-01-16 15:35:19 +00:00
|
|
|
platform.start();
|
2017-04-22 01:56:49 +00:00
|
|
|
setupDebugControls();
|
2017-01-14 16:14:25 +00:00
|
|
|
loadPreset(qs['file']);
|
|
|
|
updateSelector();
|
2017-04-26 19:44:55 +00:00
|
|
|
showBookLink();
|
2017-05-01 15:30:47 +00:00
|
|
|
addPageFocusHandlers();
|
2017-01-16 15:35:19 +00:00
|
|
|
return true;
|
2017-01-14 16:14:25 +00:00
|
|
|
} else {
|
2017-01-16 15:35:19 +00:00
|
|
|
// try to load last file (redirect)
|
2017-01-14 16:14:25 +00:00
|
|
|
var lastid = localStorage.getItem("__lastid_"+platform_id) || localStorage.getItem("__lastid");
|
|
|
|
localStorage.removeItem("__lastid");
|
|
|
|
gotoPresetNamed(lastid || PRESETS[0].id);
|
2017-01-16 15:35:19 +00:00
|
|
|
return false;
|
2017-01-14 16:14:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-25 17:30:05 +00:00
|
|
|
function loadSharedFile(sharekey) {
|
|
|
|
var github = new Octokat();
|
|
|
|
var gist = github.gists(sharekey);
|
|
|
|
gist.fetch().done(function(val) {
|
|
|
|
var filename;
|
|
|
|
for (filename in val.files) { break; }
|
|
|
|
var newid = 'shared/' + filename;
|
|
|
|
var json = JSON.parse(val.description.slice(val.description.indexOf(' ')+1));
|
|
|
|
console.log("Fetched " + newid, json);
|
|
|
|
platform_id = json['platform'];
|
|
|
|
initPlatform();
|
|
|
|
updatePreset(newid, val.files[filename].content);
|
|
|
|
qs['file'] = newid;
|
|
|
|
qs['platform'] = platform_id;
|
|
|
|
delete qs['sharekey'];
|
|
|
|
gotoNewLocation();
|
|
|
|
}).fail(function(err) {
|
|
|
|
alert("Error loading share file: " + err.message);
|
|
|
|
});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-01-03 01:42:15 +00:00
|
|
|
// start
|
2017-01-14 16:14:25 +00:00
|
|
|
function startUI(loadplatform) {
|
2017-01-16 19:13:03 +00:00
|
|
|
installErrorHandler();
|
2017-05-04 15:54:56 +00:00
|
|
|
window.addEventListener("message", handleWindowMessage, false);
|
2017-01-25 17:30:05 +00:00
|
|
|
// add default platform?
|
|
|
|
platform_id = qs['platform'] || localStorage.getItem("__lastplatform");
|
|
|
|
if (!platform_id) {
|
|
|
|
platform_id = qs['platform'] = "vcs";
|
|
|
|
}
|
2017-01-14 16:14:25 +00:00
|
|
|
// parse query string
|
|
|
|
// is this a share URL?
|
|
|
|
if (qs['sharekey']) {
|
2017-01-25 17:30:05 +00:00
|
|
|
loadSharedFile(qs['sharekey']);
|
2017-01-14 02:31:04 +00:00
|
|
|
} else {
|
2017-01-14 16:14:25 +00:00
|
|
|
// reset file?
|
|
|
|
if (qs['file'] && qs['reset']) {
|
2017-01-27 02:59:34 +00:00
|
|
|
initPlatform();
|
2017-01-14 16:14:25 +00:00
|
|
|
store.deleteFile(qs['file']);
|
|
|
|
qs['reset'] = '';
|
2017-01-16 19:13:03 +00:00
|
|
|
gotoNewLocation();
|
2017-01-14 16:14:25 +00:00
|
|
|
} else {
|
|
|
|
// load and start platform object
|
|
|
|
if (loadplatform) {
|
2017-01-29 21:06:05 +00:00
|
|
|
var scriptfn = 'src/platform/' + platform_id.split('-')[0] + '.js';
|
2017-05-01 15:30:47 +00:00
|
|
|
var script = document.createElement('script');
|
|
|
|
script.onload = function() {
|
2017-01-14 16:14:25 +00:00
|
|
|
console.log("loaded platform", platform_id);
|
|
|
|
startPlatform();
|
2017-04-29 15:31:11 +00:00
|
|
|
showWelcomeMessage();
|
2017-05-01 15:30:47 +00:00
|
|
|
};
|
|
|
|
script.src = scriptfn;
|
|
|
|
document.getElementsByTagName('head')[0].appendChild(script);
|
2017-01-14 05:47:26 +00:00
|
|
|
} else {
|
2017-01-14 16:14:25 +00:00
|
|
|
startPlatform();
|
2017-04-29 15:31:11 +00:00
|
|
|
showWelcomeMessage();
|
2017-01-14 05:47:26 +00:00
|
|
|
}
|
2017-01-14 16:14:25 +00:00
|
|
|
}
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
}
|