Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update for spec 1.5 #19

Merged
merged 13 commits into from
Apr 27, 2012
168 changes: 69 additions & 99 deletions dcpu.htm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<head>
<title>DCPU-16 emulator</title>
<script type="text/javascript" src="js/assembler.js?v=1"></script>
<script type="text/javascript" src="js/disassembler.js?v=1"></script>
<script type="text/javascript" src="js/dcpu.js?v=1"></script>
<script type="text/javascript" src="js/screen.js?v=1"></script>
<style>
Expand Down Expand Up @@ -379,14 +380,10 @@ <h4>Memory dump:</h4>
var memToLine = {};
var lineToMem = {};
var breaks = {};
// Not final yet! Will most probably change after clarifications from Notch
var keymap = {0x0d: 0x0a, 0x25: 0x01, 0x27: 0x02, 0x26: 0x03, 0x28: 0x04};
var keypointer = 0;

document.onkeydown = function(event) {
var e = event || window.event;
var key = e.keyCode;
if (keymap[key]) key = keymap[key];
switch (key) {
var code = window.event ? event.keyCode : event.which;
switch (code) {
case 116: { // F5
run(ge("button_run"));
return false;
Expand All @@ -396,19 +393,22 @@ <h4>Memory dump:</h4>
return false;
}
default: { // pass it to program
if (!runningTimer) {
return true;
}
if (!memory[keypointer + 0x9000]) {
memory[keypointer + 0x9000] = key;
keypointer = (keypointer + 1) % 0x10;
updateMemoryView();
}
return false;
if (!runningTimer) return true;
return Keyboard.onkeydown(event);
}
}
return true;
}
};
document.onkeypress = function(event) {
if (!runningTimer) return true;
if (event.which == 8) { return false; }

Keyboard.onkeypress(event);
};
document.onkeyup = function(event) {
if (!runningTimer) return true;
Keyboard.onkeyup(event);
};

function toggleTab(index) {
for (var i = 0; i < 2; i++) {
ge("tab" + i + "_wrapper").style.display = (index == i) ? "block" : "none";
Expand Down Expand Up @@ -505,7 +505,7 @@ <h4>Memory dump:</h4>

function step() {
ge('loading_overlay').style.display = 'none';
var rv = DCPU.step(memory, registers, [Screen]);
var rv = DCPU.step(memory, registers, [ Screen, Keyboard ]);
if (rv > 0) {
cycles += rv;
}
Expand All @@ -524,7 +524,7 @@ <h4>Memory dump:</h4>
runningTimer = setInterval(function() {
var was_cycles = cycles;
for (var i = 0; i < 10000; i++) {
var rv = DCPU.step(memory, registers, [Screen]);
var rv = DCPU.step(memory, registers, [ Screen, Keyboard ]);
if (rv < 0) { // break
if (runningTimer) run(button);
return;
Expand Down Expand Up @@ -616,102 +616,70 @@ <h4>Memory dump:</h4>
}

function disassemble() {
lastInput = ge("da_input").value;
var input = lastInput + " ";
var linenum = lastInput.split("\n").length;
var data = [];
var s = "";
for (var i = 0; i < input.length; i++) {
if ("0123456789abcdefABCDEF".indexOf(input.charAt(i)) > -1) {
s += input.charAt(i);
if (s.length == 4) {
data.push(parseInt(s, 16));
s = "";
}
var lines = ge("code").value.split("\n");
var logger = function(line, address, pos, message, fatal) { };
var buffer = [ ];
var rv = Assembler.compile(lines, buffer, logger);
if (!rv) return;

// figure out where code is being compiled
var code_blocks = [ ];
var pc_start = -1;
var pc_current = -1;
for (var i = 0; i < rv.infos.length; i++) {
var info = rv.infos[i];
if (info === undefined) continue;
if (info.pc != pc_current) {
if (pc_current != -1 && pc_current > pc_start) code_blocks.push({ start: pc_start, end: pc_current });
pc_start = info.pc;
pc_current = info.pc;
}
pc_current += (info.size || 0);
}
if (pc_current != -1 && pc_current > pc_start) code_blocks.push({ start: pc_start, end: pc_current });

var log = [];
var aborted = false;
var logger = function(offset, msg, fatal) {
log.push("<span class='line'>" + pad(offset, 4) + ":</span> " + (fatal ? "(<span class='fatal'>Fatal</span>) " : "") + msg);
if (fatal) aborted = true;
};
var targets = [ ];
for (var i = 0; i < code_blocks.length; i++) {
var t = Disassembler.findTargets(buffer, code_blocks[i].start, code_blocks[i].end);
targets = targets.concat(t);
}
targets.sort();
var labels = { };
for (var i = 0; i < targets.length; i++) {
labels[targets[i]] = "label" + i;
}

var used = {};
var code = {};
var stack = [];
var lines = [];
var output = [];
var conditional = false;
if (data.length > 0) {
stack.push(0);
}
var labels = {last: 0};
while (stack.length > 0) {
var pc = stack.pop();
if (used[pc]) {
continue;
}
for (var i = 0; i < code_blocks.length; i++) {
var pc = code_blocks[i].start;
var end = code_blocks[i].end;
do {
var info = DCPU.disassemble(data, pc, labels, logger);
if (info.branch !== undefined) {
stack.push(info.branch);
}
for (var i = pc; i < pc + info.size; i++) {
used[i] = true;
var info = Disassembler.disassemble(buffer, pc, labels, function(type, str) {
return "<span class='" + type + "'>" + str + "</span>";
});
if (labels[pc]) {
lines.push("");
output.push(":" + wrapAs(labels[pc], "lbl"));
}
if (info.code !== undefined) {
code[pc] = (conditional ? "&nbsp;&nbsp;" : "") + info.code;
lines.push(pad(pc.toString(16), 4) + ":");
output.push("&nbsp;&nbsp;" + (conditional ? "&nbsp;&nbsp;" : "") + info.code);
}
pc += info.size;
if (conditional) {
info.terminal = false;
}
conditional = info.conditional;
} while (pc < data.length && !info.terminal);
}

var lines = [];
var output = [];
for (var i = 0; i < data.length; i++) {
if (labels[i]) {
lines.push("");
output.push("");
lines.push("");
output.push(":" + wrapAs(labels[i], "lbl"));
}
if (code[i] !== undefined) {
lines.push(pad(i.toString(16), 4) + ":");
output.push("&nbsp;&nbsp;" + code[i]);
} else if (!used[i]) {
var words = [];
var all_zeros = true;
var old_i = i;
while (i < data.length && !used[i]) {
words.push(wrapAs("0x" + pad(data[i].toString(16), 4), "lit"));
if (data[i]) all_zeros = false;
i++;
}
if (all_zeros) {
if (i < data.length) {
lines.push("");
lines.push("");
output.push("");
output.push(wrapAs("ORG", "op") + " " + wrapAs("0x" + pad(i.toString(16), 4), "lit"));
}
} else {
lines.push(pad(old_i.toString(16), 4) + ":");
output.push("&nbsp;&nbsp;" + wrapAs("DAT", "op") + " " + words.join(", "));
}
i--;
}
} while (pc < end);
lines.push("");
output.push("");
}

// update UI
ge("da_lines").innerHTML = lines.join("<br/>");
ge("da_code").innerHTML = output.join("<br/>");
ge("log").innerHTML = log.join("<br/>");
ge("da_input").style.height = Math.max(560, ((linenum + 1) * 19 + 9)) + "px";
ge("da_input").style.height = Math.max(560, ((lines.length + 1) * 19 + 9)) + "px";
}

function disassembleDump() {
var dump = "";
var end = 0x7ffe;
Expand All @@ -726,10 +694,12 @@ <h4>Memory dump:</h4>
}

Screen.init();
Keyboard.init();
disassemble();
reset();
var lastCode = ge("code").value;
var lastInput = ge("da_input").value;

setInterval(function() {
Screen.blink = !Screen.blink;
Screen.update(memory);
Expand Down
67 changes: 46 additions & 21 deletions js/assembler.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

var Assembler = {
DIRECTIVES: [ "macro", "define" ],
REGISTERS: {
"a": 0, "b": 1, "c": 2, "x": 3, "y": 4, "z": 5, "i": 6, "j": 7 },
REGISTERS: { "a": 0, "b": 1, "c": 2, "x": 3, "y": 4, "z": 5, "i": 6, "j": 7 },
SPECIALS: {
"push": 0x18,
"pop": 0x18,
Expand All @@ -28,13 +27,13 @@ var Assembler = {
"div": 0x06,
"dvi": 0x07,
"mod": 0x08,
"and": 0x09,
"bor": 0x0a,
"xor": 0x0b,
"shr": 0x0c,
"asr": 0x0d,
"shl": 0x0e,
"mvi": 0x0f,
"mdi": 0x09,
"and": 0x0a,
"bor": 0x0b,
"xor": 0x0c,
"shr": 0x0d,
"asr": 0x0e,
"shl": 0x0f,
"ifb": 0x10,
"ifc": 0x11,
"ife": 0x12,
Expand All @@ -45,24 +44,31 @@ var Assembler = {
"ifu": 0x17,
// ...
"adx": 0x1a,
"sux": 0x1b
"sbx": 0x1b,
// ...
"sti": 0x1e,
"std": 0x1f,
},
OP_SPECIAL: {
"jsr": 0x01,
// ...
"hcf": 0x07,
"int": 0x08,
"iag": 0x09,
"ias": 0x0a,
"iap": 0x0b,
"iaq": 0x0c,
// ...
"hwn": 0x10,
"hwq": 0x11,
"hwi": 0x12
"hwi": 0x12,
},
OP_RESERVED: [ "set", "add", "sub", "mul", "mli", "div", "dvi", "mod",
"and", "bor", "xor", "shr", "asr", "shl", "mvi",
"mdi", "and", "bor", "xor", "shr", "asr", "shl",
"ifb", "ifc", "ife", "ifn", "ifg", "ifa", "ifl", "ifu",
"adx", "sux",
"jsr", "int", "iag", "ias", "hwn", "hwq", "hwi",
"adx", "sbx", "sti", "std",
"jsr", "hcf", "int", "iag", "ias", "iap", "iaq",
"hwn", "hwq", "hwi",
"jmp", "brk", "ret", "bra", "dat", "org" ],

SPACE: { ' ': true, '\n': true, '\r': true, '\t': true }, // to replace charAt(pos).match(/\s/), using regexps is very slow
Expand Down Expand Up @@ -112,10 +118,10 @@ var Assembler = {
return false;
}
operand = operand[0].toLowerCase();
pos += operand.length;
if (subst[operand]) {
operand = subst[operand];
}
pos += operand.length;
if (operand.match(/^[0-9]+$/g)) {
atom.literal = parseInt(operand, 10);
} else if (operand.match(/^0x[0-9a-fA-F]+$/g)) {
Expand Down Expand Up @@ -271,7 +277,7 @@ var Assembler = {
* Returns true if this line did contain some constant definition (even if it was an error),
* meaning you shouldn't bother compiling this line.
*/
parseConstant: function(text, labels, logger) {
parseConstant: function(text, labels, subst, logger) {
var match = text.match(/^\s*([A-Za-z_.][A-Za-z0-9_.]*)\s*=\s*(\S+)/);
if (!match) return false;
var name = match[1].toLowerCase();
Expand All @@ -284,7 +290,7 @@ var Assembler = {
// manually find position of expression, for displaying nice error messages.
var pos = text.indexOf('=') + 1;
while (this.SPACE[text.charAt(pos)]) pos++;
var state = { text: text, pos: pos, end: text.length, logger: logger };
var state = { text: text, pos: pos, end: text.length, subst: subst, logger: logger };
var expr = this.parseExpression(state, 0);
if (expr) {
var value = this.evalConstant(expr, labels, true);
Expand Down Expand Up @@ -482,6 +488,7 @@ var Assembler = {
case 'n': { rv += "\n"; break; }
case 'r': { rv += "\r"; break; }
case 't': { rv += "\t"; break; }
case 'z': { rv += "\x00"; break; }
case 'x': {
if (i < s.length - 2) {
rv += String.fromCharCode(parseInt(s.substr(i + 1, 2), 16));
Expand Down Expand Up @@ -515,6 +522,26 @@ var Assembler = {
info.size++;
info.dump.push(arg.charCodeAt(j));
}
} else if (arg.charAt(0) == 'p' && arg.charAt(1) == '"') {
// packed string
arg = this.unquoteString(arg.substr(2, arg.length - 3));
var word = 0, in_word = false;
for (var j = 0; j < arg.length; j++) {
var c = arg.charCodeAt(j);
if (in_word) {
word |= c;
info.size++;
info.dump.push(word);
in_word = false;
} else {
word = c << 8;
in_word = true;
}
}
if (in_word) {
info.size++;
info.dump.push(word);
}
} else {
var expr = this.parseExpression(this.stateFromArg(true, line, i, subst, logger), 0);
if (!expr) return false;
Expand Down Expand Up @@ -876,14 +903,12 @@ var Assembler = {
if (fatal) aborted = true;
};
labels["."] = pc;
if (!this.parseConstant(lines[i], labels, l_logger)) {
var info = this.compileLine(lines[i], pc, labels, macros, {}, l_logger);
if (!this.parseConstant(lines[i], labels, { }, l_logger)) {
var info = this.compileLine(lines[i], pc, labels, macros, { }, l_logger);
if (!info) break;
if (pc + info.size > 0xffff) {
l_logger(0, "Code is too big (exceeds 128 KB) &mdash; not enough memory", true);
break;
} else if (pc + info.size > 0x8000) {
l_logger(0, "Code is too big (exceeds 64 KB) &mdash; overlaps video memory");
}
if (info.org !== undefined) {
pc = info.org;
Expand Down
Loading