Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

refactored the disassembler. :) #18

Merged
merged 8 commits into from

2 participants

@robey

No description provided.

@deNULL
Owner

The disassembler seems to work worse than it was working before...

For example, the disassembler should be able to parse this code:

8b82 
7c61 
7c61 8000 
8b83

Into this:

ADD PC, 1
DAT 0x7c61
SET X, 0x8000
SUB PC, 1

But your version produces

ADD PC, 1
SET X, 0x7c61
??? -1
SUB PC, 1

(i.e. it doesn't understand that after ADD PC, 1 there is an unreachable word and thus it must be treated as DAT until the next reachable block will be found)

@robey

oops, sorry, missed your comment last night.

hmm. i see. one fix might be to notice if an opcode is crossing into a target, and stop (just display DAT) in that case. i'll try that.

@robey

ok, it now disassembles to:

0000:   BRA label0
0001:   DAT 0x7c61
      :label0
0002:   SET X, 0x8000
      :label1
0004:   BRA label1
@deNULL deNULL merged commit d8af46a into deNULL:master
@deNULL
Owner

Still wasn't totally happy with your way of disassembling code... Restored much of the refactored code to its initial state.

The idea is to disassemble only the chunks of code that are actually could be accessible (based on jumps, jsr's, ias'es and other manipulations on PC). Source code shouldn't be used at all (or else disassembler will be totally useless in case of self-modifying code or for disassembling someone else's binaries).

@robey

i'll work on this more on a branch. i think i see what the intent was, now, and why it wasn't working for me.

i think it would be cool if the disassembly was automatic -- you didn't need the button. as you edit in the assembler, it compiles all the time, then when you go to the disassembly tab, it disassembles whatever is in memory, and you can edit the memory, changing the disassembly.

it's kinda like the memory dump should be a 3rd tab, and be editable.

i'm trying to get the html/css bootstrapped, so maybe i'll fiddle with that too.

@deNULL
Owner

The problem with disassembling current memory is, it's too much work to do (for the disassembler). For example, I don't want to display all memory dump in one large textarea (and disassemble every word of it) - it'll make page too heavy.

And I already have an image of the new page layout in my mind - please keep that in mind when you'll make any serious changes to the interface.

@robey

i'll post a merge request that splits out the html, css, and ui js so that it's at least easier to hack on the ui incrementally.

i didn't get bootstrap finished yet, but it keeps your design, just using bootstrap divs instead of manual layout. you can see what it looks like (but doesn't scroll right) here: http://www.lag.net/dcpu/play.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 26, 2012
  1. Merge remote-tracking branch 'denull/master' into disasm

    Robey Pointer authored
    Conflicts:
    	dcpu.htm
    	js/dcpu.js
  2. fix bug in parsing constants.

    Robey Pointer authored
  3. BRK moved. :)

    Robey Pointer authored
  4. add keyboard support.

    Robey Pointer authored
  5. fix imported-font format to match the current spec.

    Robey Pointer authored
  6. make sure to always disassemble starting from every target. as a bonu…

    Robey Pointer authored
    …s, decode BRA now too.
This page is out of date. Refresh to see the latest.
View
168 dcpu.htm
@@ -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>
@@ -379,14 +380,10 @@
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;
@@ -396,19 +393,22 @@
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";
@@ -505,7 +505,7 @@
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;
}
@@ -524,7 +524,7 @@
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;
@@ -616,102 +616,70 @@
}
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;
@@ -726,10 +694,12 @@
}
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);
View
11 js/assembler.js
@@ -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,
@@ -271,7 +270,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();
@@ -284,7 +283,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);
@@ -876,8 +875,8 @@ 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);
View
180 js/dcpu.js
@@ -1,8 +1,7 @@
/*
-* DCPU-16 Assembler & Emulator Library
-* by deNULL (me@denull.ru)
-*
-*/
+ * DCPU-16 Assembler & Emulator Library
+ * by deNULL (me@denull.ru)
+ */
// some common functions, shouldn't actually be here...
String.prototype.trim = function() {
@@ -206,7 +205,7 @@ step: function(memory, registers, hardware) {
DCPU.cycles = 0;
// check for BRK (SUB PC, 1)
- if (cur == 0x85c3) {
+ if (cur == 0x8b83) {
return -1;
}
switch (op) {
@@ -462,175 +461,4 @@ step: function(memory, registers, hardware) {
}
},
-
-// -------
-
-disassembleValue: function(is_a, code, memory, offset, logger) {
-
- if (code < 0x8) {
- return {size: 0, str: wrapAs(DCPU.regs[code], "reg")};
- } else
- if (code < 0x10) {
- return {size: 0, str: "[" + wrapAs(DCPU.regs[code - 0x08], "reg") + "]"};
- } else
- if (code < 0x18) {
- if (offset >= memory.length) {
- logger(offset, "Disassembler reached end of the file", true);
- return false;
- }
- var nw = memory[offset];
- return {size: 1, str: "[" + wrapAs(DCPU.regs[code - 0x10], "reg") + "+" + wrapAs("0x" + nw.toString(16), "lit") + "]"};
- } else
- if (code == 0x18) { // POP
- return {size: 0, str: wrapAs(is_a ? "POP" : "PUSH", "kw")};
- } else
- if (code == 0x19) { // PEEK
- return {size: 0, str: wrapAs("PEEK", "kw")};
- } else
- if (code == 0x1a) { // PICK
- if (offset >= memory.length) {
- logger(offset, "Disassembler reached end of the file", true);
- return false;
- }
- var nw = memory[offset];
- return {size: 0, str: wrapAs("PICK", "kw") + " " + wrapAs(nw.toString(10), "lit")};
- } else
- if (code == 0x1b) {
- return {size: 0, str: wrapAs("SP", "kw")};
- } else
- if (code == 0x1c) {
- return {size: 0, str: wrapAs("PC", "kw")};
- } else
- if (code == 0x1d) {
- return {size: 0, str: wrapAs("EX", "kw")};
- } else
- if (code == 0x1e) {
- if (offset >= memory.length) {
- logger(offset, "Disassembler reached end of the file", true);
- return false;
- }
- var nw = memory[offset];
- return {size: 1, str: "[" + wrapAs("0x" + pad(nw.toString(16), 4), "lit") + "]"};
- } else
- if (code == 0x1f) {
- if (offset >= memory.length) {
- logger(offset, "Disassembler reached end of the file");
- return false;
- }
- var nw = memory[offset];
- return {size: 1, str: wrapAs("0x" + pad(nw.toString(16), 4), "lit"), literal: nw};
- } else {
- return {size: 0, str: wrapAs((code - 0x21).toString(10), "lit"), literal: (code == 0x20) ? 0xffff : (code - 0x21)};
- }
-},
-/*
-* Disassembles code in memory at specified offset
-*
-* Returns object with fields
-* - code
-* - branch
-* - terminal
-* - size
-*/
-disassemble: function(memory, offset, labels, logger) {
- var res = {size: 1};
- if (offset >= memory.length) {
- logger(offset, "Disassembler reached end of the file");
- return {size: 0, terminal: true};
- }
- var cur = memory[offset];
- var op = cur & 0x1f;
- var aa = (cur >> 10) & 0x3f;
- var bb = (cur >> 5) & 0x1f;
-
- switch (op) {
- case 0x00: {
- switch (bb) {
- case 0x01: // JSR
- case 0x0a: { // IAS
- var va = DCPU.disassembleValue(true, aa, memory, offset + res.size, logger);
- res.size += va.size;
- res.code = wrapAs(DCPU.nbops[bb], "op") + " " + va.str;
- if (va.literal !== undefined) {
- res.branch = va.literal;
- if (!labels[res.branch]) {
- labels.last++;
- labels[res.branch] = (bb == 0x0a ? "int_handler" : "label") + labels.last;
- }
- res.code = wrapAs(DCPU.nbops[bb], "op") + " " + wrapAs(labels[res.branch], "lbl");
- }
- return res;
- }
- default: {
- if (!DCPU.nbops[bb]) {
- logger(offset, "Unknown non-basic instruction: " + aa.toString(16));
- return {size: 0, terminal: true};
- }
-
- var va = DCPU.disassembleValue(true, aa, memory, offset + res.size, logger);
- res.size += va.size;
- res.code = wrapAs(DCPU.nbops[bb], "op") + " " + va.str;
- return res;
- }
- }
- }
- default: {
- if (!DCPU.bops[op]) {
- logger(offset, "Unknown basic instruction: " + op.toString(16));
- return {size: 0, terminal: true};
- }
-
- var vb = DCPU.disassembleValue(false, bb, memory, offset + res.size, logger);
- res.size += vb.size;
- var va = DCPU.disassembleValue(true, aa, memory, offset + res.size, logger);
- res.size += va.size;
-
- res.code = wrapAs(DCPU.bops[op], "op") + " " + vb.str + ", " + va.str;
- if (op >= 0x10 && op <= 0x17) {
- res.conditional = true;
- } else
- if (bb == 0x1c) {
- offset += res.size;
- res.terminal = true;
- if (va.literal === undefined) {
- if (aa != 0x18) // assuming SET PC, POP - RET
- logger(offset, "(Warning) Can't predict the value of PC after " + res.code + ". Some instructions may be not disassembled.");
- } else
- switch (op) {
- case 0x01:
- case 0x0f: {
- res.branch = va.literal;
- if (!labels[res.branch]) {
- labels.last++;
- labels[res.branch] = "label" + labels.last;
- }
- res.code = wrapAs(DCPU.bops[op], "op") + " " + vb.str + ", " + wrapAs(labels[res.branch], "lbl");
- break;
- }
- case 0x02: { res.branch = (offset + va.literal) & 0xffff; break; }
- case 0x03: { res.branch = (offset - va.literal) & 0xffff; break; }
- case 0x04: { res.branch = (offset * va.literal) & 0xffff; break; }
- case 0x05: { res.branch = (DCPU.extendSign(offset) * DCPU.extendSign(va.literal)) & 0xffff; break; }
- case 0x06: { res.branch = parseInt(offset / va.literal) & 0xffff; break; }
- case 0x07: { res.branch = parseInt(DCPU.extendSign(offset) / DCPU.extendSign(va.literal)) & 0xffff; break; }
- case 0x08: { res.branch = (va.literal == 0) ? 0 : (offset % va.literal); break; }
- case 0x09: { res.branch = (offset & va.literal); break; }
- case 0x0a: { res.branch = (offset | va.literal); break; }
- case 0x0b: { res.branch = (offset ^ va.literal); break; }
- case 0x0c: { res.branch = (offset >>> va.literal) & 0xffff; break; }
- case 0x0d: { res.branch = (DCPU.extendSign(offset) >> va.literal) & 0xffff; break; }
- case 0x0e: { res.branch = (offset << va.literal) & 0xffff; break; }
- case 0x1a: { res.branch = (offset + va.literal) & 0xffff; break; }
- case 0x1b: { res.branch = (offset - va.literal) & 0xffff; break; }
- }
- }
-
- return res;
- }
- }
-}
-
-
-
-
};
View
196 js/disassembler.js
@@ -0,0 +1,196 @@
+/*
+ * DCPU-16 Assembler & Emulator Library
+ * by deNULL (me@denull.ru)
+ */
+
+var Disassembler = {
+ REGISTERS: [ "A", "B", "C", "X", "Y", "Z", "I", "J" ],
+ SPECIALS: {
+ 0x18: "PUSH",
+ 0x19: "PEEK",
+ 0x1a: "PICK",
+ 0x1b: "SP",
+ 0x1c: "PC",
+ 0x1d: "EX"
+ },
+ OP_BINARY: {
+ 0x01: "SET",
+ 0x02: "ADD",
+ 0x03: "SUB",
+ 0x04: "MUL",
+ 0x05: "MLI",
+ 0x06: "DIV",
+ 0x07: "DVI",
+ 0x08: "MOD",
+ 0x09: "AND",
+ 0x0a: "BOR",
+ 0x0b: "XOR",
+ 0x0c: "SHR",
+ 0x0d: "ASR",
+ 0x0e: "SHL",
+ 0x0f: "MVI",
+ 0x10: "IFB",
+ 0x11: "IFC",
+ 0x12: "IFE",
+ 0x13: "IFN",
+ 0x14: "IFG",
+ 0x15: "IFA",
+ 0x16: "IFL",
+ 0x17: "IFU",
+ 0x1A: "ADX",
+ 0x1B: "SUX"
+ },
+ OP_SPECIAL: {
+ 0x01: "JSR",
+ // ...
+ 0x08: "INT",
+ 0x09: "IAG",
+ 0x0a: "IAS",
+ // ...
+ 0x10: "HWN",
+ 0x11: "HWQ",
+ 0x12: "HWI"
+ },
+
+ address: function(n, labels) {
+ if (labels[n]) return labels[n];
+ return (n > 15) ? ("0x" + pad(n.toString(16), 4)) : n.toString(10);
+ },
+
+ decodeValue: function(is_a, code, immediate, labels, wrapAs) {
+ if (code < 0x08) {
+ return wrapAs("reg", this.REGISTERS[code]);
+ } else if (code < 0x10) {
+ return "[" + wrapAs("reg", this.REGISTERS[code - 0x08]) + "]";
+ } else if (code < 0x18) {
+ return "[" + wrapAs("reg", this.REGISTERS[code - 0x10]) + "+" +
+ wrapAs("lit", "0x" + pad(immediate.toString(16), 4)) + "]";
+ } else if (code == 0x18) {
+ return wrapAs("kw", is_a ? "PUSH" : "POP");
+ } else if (code == 0x1a) {
+ return wrapAs("kw", "PICK") + " " + wrapAs("lit", immediate.toString(10));
+ } else if (code < 0x1e) {
+ return wrapAs("kw", this.SPECIALS[code]);
+ } else if (code == 0x1e) {
+ return "[" + wrapAs("lit", this.address(immediate, labels)) + "]";
+ } else if (code == 0x1f) {
+ return wrapAs("lit", this.address(immediate, labels));
+ } else { // embedded immediate
+ return wrapAs("lit", this.address(immediate, labels));
+ }
+ },
+
+ hasArg: function(code) {
+ return ((code >= 0x10 && code < 0x18) || code == 0x1a || code == 0x1e || code == 0x1f);
+ },
+
+ nextOp: function(memory, offset) {
+ var start = offset;
+ if (offset >= memory.length) return { op: 0, a: 0, b: 0 };
+ var word = memory[offset++];
+ var op = { opcode: (word & 0x1f), a: (word >> 10 & 0x3f), b: (word >> 5 & 0x1f) };
+ if (this.hasArg(op.b)) {
+ if (offset >= memory.length) return { op: 0, a: 0, b: 0 };
+ op.b_immediate = memory[offset++];
+ }
+ if (this.hasArg(op.a)) {
+ if (offset >= memory.length) return { op: 0, a: 0, b: 0 };
+ op.a_immediate = memory[offset++];
+ }
+ // go ahead and decode embedded immediates.
+ if (op.a >= 0x20) op.a_immediate = (op.a == 0x20 ? 0xffff : (op.a - 0x21));
+ if (op.b >= 0x20) op.b_immediate = (op.b == 0x20 ? 0xffff : (op.b - 0x21));
+ op.size = offset - start;
+ return op;
+ },
+
+ /**
+ * Build a list of memory addresses targeted by JMP/JSR instructions.
+ */
+ findTargets: function(memory, offset, end) {
+ var targets = [ ];
+ var jsr = Assembler.OP_SPECIAL["jsr"];
+ var add = Assembler.OP_BINARY["add"];
+ var sub = Assembler.OP_BINARY["sub"];
+ var set = Assembler.OP_BINARY["set"];
+ var pc = Assembler.SPECIALS["pc"];
+ while (offset < end) {
+ var op = this.nextOp(memory, offset);
+ var a = op.a_immediate;
+ var b = op.a_immediate;
+
+ if (op.opcode == 0 && op.b == jsr && a !== undefined) {
+ targets.push(a);
+ } else if (op.opcode == set && op.b == pc && a !== undefined) {
+ targets.push(a);
+ } else if (op.opcode == add && op.b == pc && a !== undefined) {
+ targets.push(offset + op.size + a);
+ } else if (op.opcode == sub && op.b == pc && a !== undefined) {
+ targets.push(offset + op.size - a);
+ }
+
+ if (op.size > 1 && targets.indexOf(offset + 1) >= 0) {
+ offset++;
+ } else if (op.size > 2 && targets.indexOf(offset + 2) >= 0) {
+ offset += 2;
+ } else {
+ offset += op.size;
+ }
+ }
+ return targets;
+ },
+
+ /**
+ * Disassemble a single operation in memory at the specified offset.
+ * Returns:
+ * - code: string for displaying
+ * - conditional: if this is a conditional op
+ * - size: # of words consumed
+ */
+ disassemble: function(memory, offset, labels, wrapAs) {
+ var res = { };
+ var op = this.nextOp(memory, offset);
+
+ // if this op would stretch into a labeled target, scrap it. it's just data.
+ if ((op.size == 2 && labels[offset + 1]) ||
+ (op.size == 3 && (labels[offset + 1] || labels[offset + 2]))) {
+ res.size = 1;
+ res.code = wrapAs("op", "DAT") + " " + wrapAs("lit", "0x" + pad(memory[offset].toString(16), 4));
+ return res;
+ }
+
+ res.size = op.size;
+
+ // for convenience, decode BRA.
+ var add = Assembler.OP_BINARY["add"];
+ var sub = Assembler.OP_BINARY["sub"];
+ var pc = Assembler.SPECIALS["pc"];
+
+ if (op.opcode == add && op.b == pc && op.a_immediate !== undefined) {
+ res.code = wrapAs("op", "BRA") + " " + this.address(offset + op.size + op.a_immediate, labels);
+ return res;
+ }
+ if (op.opcode == sub && op.b == pc && op.a_immediate !== undefined) {
+ res.code = wrapAs("op", "BRA") + " " + this.address(offset + op.size - op.a_immediate, labels);
+ return res;
+ }
+
+ var va = this.decodeValue(true, op.a, op.a_immediate, labels, wrapAs);
+ var vb = this.decodeValue(false, op.b, op.b_immediate, labels, wrapAs);
+
+ if (op.opcode == 0) {
+ // special
+ var code = this.OP_SPECIAL[op.b];
+ if (code === undefined) code = "???";
+ res.code = wrapAs("op", code) + " " + va;
+ } else {
+ var code = this.OP_BINARY[op.opcode];
+ if (code === undefined) code = "???";
+ res.code = wrapAs("op", code) + " " + vb + ", " + va;
+ if (op.opcode >= 0x10 && op.opcode <= 0x17) {
+ res.conditional = true;
+ }
+ }
+ return res;
+ },
+};
View
140 js/keyboard.js
@@ -0,0 +1,140 @@
+// Generic Keyboard (compatible)
+
+var Keyboard = {
+ type: 0x30cf7406,
+ revision: 1,
+ manufacturer: 0x904b3115,
+
+ CLEAR_BUFFER: 0,
+ GET_KEY: 1,
+ SCAN_KEYBOARD: 2,
+ SET_INT: 3,
+
+ BS: 0x10,
+ ENTER: 0x11,
+ INSERT: 0x12,
+ DELETE: 0x13,
+ UP: 0x80,
+ DOWN: 0x81,
+ LEFT: 0x82,
+ RIGHT: 0x83,
+ SHIFT: 0x90,
+ CONTROL: 0X91,
+
+ JS: {
+ BS: 8,
+ ENTER: 13,
+ SHIFT: 16,
+ CONTROL: 17,
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40,
+ INSERT: 45,
+ DELETE: 46,
+ },
+
+ init: function() {
+ this.buffer = [ ];
+ this.shift_down = false;
+ this.control_down = false;
+ this.translate = { };
+ this.translate[this.JS.BS] = this.BS;
+ this.translate[this.JS.ENTER] = this.ENTER;
+ this.translate[this.JS.INSERT] = this.INSERT;
+ this.translate[this.JS.DELETE] = this.DELETE;
+ this.translate[this.JS.UP] = this.UP;
+ this.translate[this.JS.DOWN] = this.DOWN;
+ this.translate[this.JS.LEFT] = this.LEFT;
+ this.translate[this.JS.RIGHT] = this.RIGHT;
+ },
+
+ onkeydown: function(event) {
+ var code = window.event ? event.keyCode : event.which;
+ switch (code) {
+ case this.JS.SHIFT: {
+ this.shift_down = true;
+ return true;
+ }
+ case this.JS.CONTROL: {
+ this.control_down = true;
+ return true;
+ }
+ }
+ // have to intercept BS or chrome will do something weird.
+ if (code == this.JS.BS || code == this.JS.UP || code == this.JS.DOWN ||
+ code == this.JS.LEFT || code == this.JS.RIGHT) {
+ this.onkeypress(event);
+ return false;
+ }
+ return true;
+ },
+
+ onkeypress: function(event) {
+ var code = window.event ? event.keyCode : event.which;
+ if (this.translate[code]) {
+ code = this.translate[code];
+ } else if (code < 0x20 || code > 0x7e) {
+ // ignore
+ return;
+ }
+ this.buffer.push(code);
+ // small 8-character buffer
+ if (this.buffer.length > 8) this.buffer.shift();
+ // keychar = String.fromCharCode(keynum);
+ },
+
+ onkeyup: function(event) {
+ var code = window.event ? event.keyCode : event.which;
+ switch (code) {
+ case this.JS.SHIFT: {
+ this.shift_down = false;
+ break;
+ }
+ case this.JS.CONTROL: {
+ this.control_down = false;
+ break;
+ }
+ }
+ },
+
+ interrupt: function(memory, registers) {
+ switch (registers.A) {
+ case this.CLEAR_BUFFER: {
+ this.buffer = [ ];
+ break;
+ }
+ case this.GET_KEY: {
+ var key = this.buffer.shift();
+ registers.C = (key === undefined) ? 0 : key;
+ break;
+ }
+ case this.SCAN_KEYBOARD: {
+ // FIXME
+ break;
+ }
+ case this.SET_INT: {
+ // FIXME
+ break;
+ }
+ }
+ return 0;
+ },
+};
+
+/*
+Interrupts do different things depending on contents of the A register:
+
+ A | BEHAVIOR
+---+----------------------------------------------------------------------------
+ 0 | Clear keyboard buffer
+ 1 | Store next key typed in C register, or 0 if the buffer is empty
+ 2 | Set C register to 1 if the key specified by the B register is pressed, or
+ | 0 if it's not pressed
+ 3 | If register B is non-zero, turn on interrupts with message B. If B is zero,
+ | disable interrupts
+---+----------------------------------------------------------------------------
+
+When interrupts are enabled, the keyboard will trigger an interrupt when one or
+more keys have been pressed, released, or typed.
+*/
View
48 js/screen.js
@@ -34,8 +34,41 @@ var Screen = {
[0xff, 0xff, 0xff, 0xff]
],
- // not so much of data, we can store it right here
- defaultFont: [0x000f, 0x0808, 0x080f, 0x0808, 0x08f8, 0x0808, 0x00ff, 0x0808, 0x0808, 0x0808, 0x08ff, 0x0808, 0x00ff, 0x1414, 0xff00, 0xff08, 0x1f10, 0x1714, 0xfc04, 0xf414, 0x1710, 0x1714, 0xf404, 0xf414, 0xff00, 0xf714, 0x1414, 0x1414, 0xf700, 0xf714, 0x1417, 0x1414, 0x0f08, 0x0f08, 0x14f4, 0x1414, 0xf808, 0xf808, 0x0f08, 0x0f08, 0x001f, 0x1414, 0x00fc, 0x1414, 0xf808, 0xf808, 0xff08, 0xff08, 0x14ff, 0x1414, 0x080f, 0x0000, 0x00f8, 0x0808, 0xffff, 0xffff, 0xf0f0, 0xf0f0, 0xffff, 0x0000, 0x0000, 0xffff, 0x0f0f, 0x0f0f, 0x0000, 0x0000, 0x005f, 0x0000, 0x0300, 0x0300, 0x3e14, 0x3e00, 0x266b, 0x3200, 0x611c, 0x4300, 0x3629, 0x7650, 0x0002, 0x0100, 0x1c22, 0x4100, 0x4122, 0x1c00, 0x2a1c, 0x2a00, 0x083e, 0x0800, 0x4020, 0x0000, 0x0808, 0x0800, 0x0040, 0x0000, 0x601c, 0x0300, 0x3e41, 0x3e00, 0x427f, 0x4000, 0x6259, 0x4600, 0x2249, 0x3600, 0x0f08, 0x7f00, 0x2745, 0x3900, 0x3e49, 0x3200, 0x6119, 0x0700, 0x3649, 0x3600, 0x2649, 0x3e00, 0x0024, 0x0000, 0x4024, 0x0000, 0x0814, 0x2241, 0x1414, 0x1400, 0x4122, 0x1408, 0x0259, 0x0600, 0x3e59, 0x5e00, 0x7e09, 0x7e00, 0x7f49, 0x3600, 0x3e41, 0x2200, 0x7f41, 0x3e00, 0x7f49, 0x4100, 0x7f09, 0x0100, 0x3e49, 0x3a00, 0x7f08, 0x7f00, 0x417f, 0x4100, 0x2040, 0x3f00, 0x7f0c, 0x7300, 0x7f40, 0x4000, 0x7f06, 0x7f00, 0x7f01, 0x7e00, 0x3e41, 0x3e00, 0x7f09, 0x0600, 0x3e41, 0xbe00, 0x7f09, 0x7600, 0x2649, 0x3200, 0x017f, 0x0100, 0x7f40, 0x7f00, 0x1f60, 0x1f00, 0x7f30, 0x7f00, 0x7708, 0x7700, 0x0778, 0x0700, 0x7149, 0x4700, 0x007f, 0x4100, 0x031c, 0x6000, 0x0041, 0x7f00, 0x0201, 0x0200, 0x8080, 0x8000, 0x0001, 0x0200, 0x2454, 0x7800, 0x7f44, 0x3800, 0x3844, 0x2800, 0x3844, 0x7f00, 0x3854, 0x5800, 0x087e, 0x0900, 0x4854, 0x3c00, 0x7f04, 0x7800, 0x447d, 0x4000, 0x2040, 0x3d00, 0x7f10, 0x6c00, 0x417f, 0x4000, 0x7c18, 0x7c00, 0x7c04, 0x7800, 0x3844, 0x3800, 0x7c14, 0x0800, 0x0814, 0x7c00, 0x7c04, 0x0800, 0x4854, 0x2400, 0x043e, 0x4400, 0x3c40, 0x7c00, 0x1c60, 0x1c00, 0x7c30, 0x7c00, 0x6c10, 0x6c00, 0x4c50, 0x3c00, 0x6454, 0x4c00, 0x0836, 0x4100, 0x0077, 0x0000, 0x4136, 0x0800, 0x0201, 0x0201, 0x704c, 0x7000], // default font by Notch
+ // default font by Notch
+ defaultFont: [
+ 0x000f, 0x0808, 0x080f, 0x0808, 0x08f8, 0x0808, 0x00ff, 0x0808,
+ 0x0808, 0x0808, 0x08ff, 0x0808, 0x00ff, 0x1414, 0xff00, 0xff08,
+ 0x1f10, 0x1714, 0xfc04, 0xf414, 0x1710, 0x1714, 0xf404, 0xf414,
+ 0xff00, 0xf714, 0x1414, 0x1414, 0xf700, 0xf714, 0x1417, 0x1414,
+ 0x0f08, 0x0f08, 0x14f4, 0x1414, 0xf808, 0xf808, 0x0f08, 0x0f08,
+ 0x001f, 0x1414, 0x00fc, 0x1414, 0xf808, 0xf808, 0xff08, 0xff08,
+ 0x14ff, 0x1414, 0x080f, 0x0000, 0x00f8, 0x0808, 0xffff, 0xffff,
+ 0xf0f0, 0xf0f0, 0xffff, 0x0000, 0x0000, 0xffff, 0x0f0f, 0x0f0f,
+ 0x0000, 0x0000, 0x005f, 0x0000, 0x0300, 0x0300, 0x3e14, 0x3e00,
+ 0x266b, 0x3200, 0x611c, 0x4300, 0x3629, 0x7650, 0x0002, 0x0100,
+ 0x1c22, 0x4100, 0x4122, 0x1c00, 0x2a1c, 0x2a00, 0x083e, 0x0800,
+ 0x4020, 0x0000, 0x0808, 0x0800, 0x0040, 0x0000, 0x601c, 0x0300,
+ 0x3e41, 0x3e00, 0x427f, 0x4000, 0x6259, 0x4600, 0x2249, 0x3600,
+ 0x0f08, 0x7f00, 0x2745, 0x3900, 0x3e49, 0x3200, 0x6119, 0x0700,
+ 0x3649, 0x3600, 0x2649, 0x3e00, 0x0024, 0x0000, 0x4024, 0x0000,
+ 0x0814, 0x2241, 0x1414, 0x1400, 0x4122, 0x1408, 0x0259, 0x0600,
+ 0x3e59, 0x5e00, 0x7e09, 0x7e00, 0x7f49, 0x3600, 0x3e41, 0x2200,
+ 0x7f41, 0x3e00, 0x7f49, 0x4100, 0x7f09, 0x0100, 0x3e49, 0x3a00,
+ 0x7f08, 0x7f00, 0x417f, 0x4100, 0x2040, 0x3f00, 0x7f0c, 0x7300,
+ 0x7f40, 0x4000, 0x7f06, 0x7f00, 0x7f01, 0x7e00, 0x3e41, 0x3e00,
+ 0x7f09, 0x0600, 0x3e41, 0xbe00, 0x7f09, 0x7600, 0x2649, 0x3200,
+ 0x017f, 0x0100, 0x7f40, 0x7f00, 0x1f60, 0x1f00, 0x7f30, 0x7f00,
+ 0x7708, 0x7700, 0x0778, 0x0700, 0x7149, 0x4700, 0x007f, 0x4100,
+ 0x031c, 0x6000, 0x0041, 0x7f00, 0x0201, 0x0200, 0x8080, 0x8000,
+ 0x0001, 0x0200, 0x2454, 0x7800, 0x7f44, 0x3800, 0x3844, 0x2800,
+ 0x3844, 0x7f00, 0x3854, 0x5800, 0x087e, 0x0900, 0x4854, 0x3c00,
+ 0x7f04, 0x7800, 0x447d, 0x4000, 0x2040, 0x3d00, 0x7f10, 0x6c00,
+ 0x417f, 0x4000, 0x7c18, 0x7c00, 0x7c04, 0x7800, 0x3844, 0x3800,
+ 0x7c14, 0x0800, 0x0814, 0x7c00, 0x7c04, 0x0800, 0x4854, 0x2400,
+ 0x043e, 0x4400, 0x3c40, 0x7c00, 0x1c60, 0x1c00, 0x7c30, 0x7c00,
+ 0x6c10, 0x6c00, 0x4c50, 0x3c00, 0x6454, 0x4c00, 0x0836, 0x4100,
+ 0x0077, 0x0000, 0x4136, 0x0800, 0x0201, 0x0201, 0x704c, 0x7000
+ ],
init: function() {
this.screen = ge("screen").getContext("2d");
@@ -61,15 +94,14 @@ var Screen = {
for (var j = 0; j < 32; j++) {
var fontData = fontCtx.getImageData(j * charWidth, i * charHeight, charWidth, charHeight), charId = (i * 32) + j;
var glyph = 0;
- for (var y = charHeight - 1; y >= 0; y--) {
- var row = 0;
- for (var x = 0; x < charWidth; x++) {
+ for (var x = 0; x < charWidth; x++) {
+ for (var y = charHeight - 1; y >= 0; y--) {
var pixelId = y * charWidth + x;
- row = (row << 1) | ((fontData.data[pixelId * charWidth + 1] > 128) * 1);
+ glyph = (glyph << 1) | ((fontData.data[pixelId * charWidth + 1] > 128) * 1);
}
- glyph = (glyph << 4) | row;
}
- self.font[i * 32 + j] = glyph;
+ self.defaultFont[(i * 32 + j) * 2] = (glyph >> 16);
+ self.defaultFont[(i * 32 + j) * 2 + 1] = (glyph & 0xffff);
}
}
};
Something went wrong with that request. Please try again.