Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #4 from oxling/disassembler

Added disassembler
  • Loading branch information...
commit bbea5e34fc89fe1d0cd05a231b0cf2a059a40b4f 2 parents fbd773e + a31221d
@nicholasbs authored
View
3  .gitmodules
@@ -0,0 +1,3 @@
+[submodule "CodeMirror2"]
+ path = CodeMirror2
+ url = https://github.com/marijnh/CodeMirror2.git
1  CodeMirror2
@@ -0,0 +1 @@
+Subproject commit 0080ea58cebef857ed2fff11ac89dc96a3fdd923
View
4 appletoo.js
@@ -987,6 +987,10 @@ function extend(base, add) {
}
function formatHex(number, len) {
+ if (typeof number === "undefined" || number === null || isNaN(number)) {
+ throw new Error("Invalid value \"" + number + "\" passed to formatHex()");
+ }
+
var str = number.toString(16).toUpperCase();
if (!len) {
View
174 disassembler.html
@@ -0,0 +1,174 @@
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+
+ <title>AppleToo.js</title>
+ <script src="appletoo.js"></script>
+ <script src="disk2.js"></script>
+ <script src="disassembler.js"></script>
+
+ <link rel="stylesheet" href="CodeMirror2/lib/codemirror.css">
+ <script src="CodeMirror2/lib/codemirror.js"></script>
+ <script src="CodeMirror2/mode/javascript/javascript.js"></script>
+
+ <style type="text/css" media="screen">
+ canvas {
+ border: 1px solid #DDD;
+ margin: 0 auto;
+ display: block;
+ }
+ body {
+ background-color: #000000; /*#222;*/
+ }
+ #control {
+ width: 820px; /*Same width as canvas */
+ border: 1px solid #DDD;
+ margin: 1em auto;
+ padding: 10px;
+ color: #EEEEEE;
+ }
+ #main {
+ float: left;
+ }
+ #editor_div {
+ float: left;
+ width: 350;
+ height: 578;
+ margin-left:20px;
+ background-color: #EEEEEE;
+ }
+ #hex_field {
+ width: 820px;
+ height: 100px;
+ background-color: #EEEEEE;
+ }
+ #start_field {
+ background-color: #EEEEEE;
+ }
+ label {
+ display: block;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ }
+
+ .CodeMirror-Lines pre.highlighted {
+ background-color: yellow;
+ }
+
+ .CodeMirror-scroll {
+ height: 578;
+ overflow-y: hidden;
+ overflow-x: hidden;
+ }
+ </style>
+
+ <script type="text/javascript" charset="utf-8">
+ window.onload = function() {
+ var appleScreen = document.getElementById("screen");
+ a = new AppleToo({compatibility: false});
+ appleScreen.width = a.char_w * 40;
+ appleScreen.height = a.char_h * 24;
+ document.onkeypress = function(e) {
+ console.log(e);
+ a._write_memory(0xC000, e.charCode + 128);
+ };
+ document.onkeydown = function(e) {
+ var value = 0;
+ switch (e.keyCode) {
+ case 37:
+ value = 8;
+ break;
+ case 38:
+ value = 11;
+ break;
+ case 39:
+ value = 21;
+ break;
+ case 40:
+ value = 10;
+ break;
+ }
+ if (e.keyCode > 36 && e.keyCode < 41){
+ a._write_memory(0xC000, value + 128);
+ }
+ };
+
+ window.d = new DiskII(a);
+ a.setPeripheral(d, 1);
+
+ var editor_div = document.getElementById("editor_div");
+
+ mirror = CodeMirror(function (elt) {
+ editor_div.appendChild(elt);
+ },
+ {
+ lineNumbers: true,
+ readOnly: true
+ });
+ };
+
+ function load() {
+ var data = document.getElementById("hex_field").value;
+ var hex_location = document.getElementById("start_field").value;
+
+ var parsed_hex_loc = parseInt(hex_location, 16);
+
+ a.load_memory(parsed_hex_loc, data);
+
+ var disassembled_str = "";
+ disassemble(data, parsed_hex_loc, function(str) {
+ disassembled_str += (str + "\n")
+ });
+
+ mirror.setOption("firstLineNumber", parsed_hex_loc);
+ mirror.setValue(disassembled_str);
+ };
+
+ function run() {
+ var pc = document.getElementById("pc_field").value;
+ var pc_num = a.read_word(0xFFFC);
+
+ if (pc.length > 0) {
+ pc_num = parseInt(pc, 16);
+
+ if (isNaN(pc_num)) {
+ throw new Error(pc + " is not a valid program counter.");
+ }
+ }
+
+ a.PC = pc_num;
+ a.run_loop();
+ };
+
+ function stop() {
+ a.stopRunning();
+ var pc_field = document.getElementById("pc_field");
+ pc_field.value = "0x" + formatHex(a.PC);
+
+ var line = a.PC - parseInt(document.getElementById("start_field").value, 16);
+ mirror.setLineClass(line, "", "highlighted");
+ };
+
+ </script>
+</head>
+
+<body>
+ <div id="main">
+ <canvas id="screen"></canvas>
+ <div id="control">
+ <label for="hex">Data</label>
+ <textarea name="hex" id="hex_field"></textarea><br/>
+ <label for="location">Start Address</label>
+ <input type="text" name="location" id="start_field" value="0xC000"></input><br/>
+ <label for="pc">Program Counter (Leave blank for reset vector) </label>
+ <input type="text" name="pc" id="pc_field" value=""></input></br>
+ <br/>
+ <input type="submit" value="Load" onclick="load()"></input>
+ <input type="submit" value="Run" onclick="run()"></input>
+ <input type="submit" value="Stop" onclick="stop()"></input>
+ </div>
+ </div>
+ <div id="editor_div">
+ </div>
+</body>
+</html>
View
256 disassembler.js
@@ -0,0 +1,256 @@
+/*
+
+Disassemble a string of hex values into the corresponding opcodes.
+Parameters:
+- data: A string containing hex values, separated by spaces - ie "AB BC 12 34"
+- offset: Number of bytes to offset the printed index.
+- print_func: A function to call for each printed line. By default, this is console.log(str)
+
+*/
+
+function disassemble (data, offset, print_func) {
+ var hex = [];
+ data = data.replace(/\s+/g, "");
+
+ for (var i = 0; i < data.length; i += 2) {
+ hex.push(parseInt(data.substr(i, 2), 16));
+ }
+
+ if (typeof offset === "undefined") {
+ offset = 0;
+ }
+
+ //By default, log to console
+ if (typeof print_func === "undefined") {
+ print_func = function (str) {
+ console.log(str);
+ }
+ }
+
+ for (var i = 0; i < hex.length;) {
+ var opcode = OPS[hex[i]];
+
+ if (typeof opcode === "undefined") {
+ print_func("#" + formatHex(hex[i], 2));
+ i += 1;
+ } else {
+ var bytes = opcode.bytes;
+ var args = hex.slice(i+1, i+1+bytes);
+
+ print_func(opcode.toString.call(opcode, args));
+
+ i += bytes;
+ }
+ }
+
+ return hex;
+};
+
+function formatImmediate (args) {
+ return this.name + " #$" + formatHex(args[0]);
+}
+
+function formatZeroPage (args) {
+ return this.name + " $" + formatHex(args[0]);
+}
+
+function formatZeroPageIndexedX (args) {
+ return formatZeroPage.call(this, args) + ",X";
+}
+
+function formatZeroPageIndexedY (args) {
+ return formatZeroPage.call(this, args) + ",Y";
+}
+
+function formatAbsolute (args) {
+ return this.name + " $" + formatHex(args[1], 2) + formatHex(args[0], 2);
+}
+
+function formatAbsoluteIndexedX (args) {
+ return formatAbsolute.call(this, args) + ",X";
+}
+
+function formatAbsoluteIndexedY (args) {
+ return formatAbsolute.call(this, args) + ",Y";
+}
+
+function formatIndirect (args) {
+ return this.name + " ($" + formatHex(args[0], 2) + ")";
+}
+
+function formatIndirectIndexedX (args) {
+ return this.name + " ($" + formatHex(args[0], 2) + ",X)";
+}
+
+function formatIndirectIndexedY (args) {
+ return this.name + " ($" + formatHex(args[0], 2) + "),Y";
+}
+
+function formatName() {
+ return this.name;
+}
+
+function formatRelative (args) {
+ return this.name + " $" + formatHex(args[0], 2);
+}
+
+function formatAccumulator (args) {
+ return this.name + " A";
+}
+
+
+
+var OPS = {
+ 0xA0 : { name: "LDY", bytes: 2, toString: formatImmediate },
+ 0xA4 : { name: "LDY", bytes: 2, toString: formatZeroPage },
+ 0xB4 : { name: "LDY", bytes: 2, toString: formatZeroPageIndexedX },
+ 0xAC : { name: "LDY", bytes: 3, toString: formatAbsolute },
+ 0xBC : { name: "LDY", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0xA2 : { name: "LDX", bytes: 2, toString: formatImmediate },
+ 0xA6 : { name: "LDX", bytes: 2, toString: formatZeroPage },
+ 0xB6 : { name: "LDX", bytes: 2, toString: formatZeroPageIndexedY },
+ 0xAE : { name: "LDX", bytes: 3, toString: formatAbsolute },
+ 0xBE : { name: "LDX", bytes: 3, toString: formatAbsoluteIndexedY },
+ 0xA9 : { name: "LDA", bytes: 2, toString: formatImmediate },
+ 0xA5 : { name: "LDA", bytes: 2, toString: formatZeroPage },
+ 0xB5 : { name: "LDA", bytes: 2, toString: formatZeroPageIndexedX },
+ 0xAD : { name: "LDA", bytes: 3, toString: formatAbsolute },
+ 0xBD : { name: "LDA", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0xB9 : { name: "LDA", bytes: 3, toString: formatAbsoluteIndexedY },
+ 0xA1 : { name: "LDA", bytes: 2, toString: formatIndirectIndexedX },
+ 0xB1 : { name: "LDA", bytes: 2, toString: formatIndirectIndexedY },
+ 0x86 : { name: "STX", bytes: 2, toString: formatZeroPage },
+ 0x96 : { name: "STX", bytes: 2, toString: formatZeroPageIndexedY },
+ 0x8E : { name: "STX", bytes: 3, toString: formatAbsolute },
+ 0x84 : { name: "STY", bytes: 2, toString: formatZeroPage },
+ 0x94 : { name: "STY", bytes: 2, toString: formatZeroPageIndexedX },
+ 0x8C : { name: "STY", bytes: 3, toString: formatAbsolute },
+ 0x85 : { name: "STA", bytes: 2, toString: formatZeroPage },
+ 0x95 : { name: "STA", bytes: 2, toString: formatZeroPageIndexedX },
+ 0x8D : { name: "STA", bytes: 3, toString: formatAbsolute },
+ 0x9D : { name: "STA", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0x99 : { name: "STA", bytes: 3, toString: formatAbsoluteIndexedY },
+ 0x81 : { name: "STA", bytes: 2, toString: formatIndirectIndexedX },
+ 0x91 : { name: "STA", bytes: 2, toString: formatIndirectIndexedY },
+ 0xE8 : { name: "INX", bytes: 1, toString: formatName },
+ 0xC8 : { name: "INY", bytes: 1, toString: formatName },
+ 0xCA : { name: "DEX", bytes: 1, toString: formatName },
+ 0x88 : { name: "DEY", bytes: 1, toString: formatName },
+ 0xE6 : { name: "INC", bytes: 2, toString: formatZeroPage },
+ 0xF6 : { name: "INC", bytes: 2, toString: formatZeroPageIndexedX },
+ 0xEE : { name: "INC", bytes: 3, toString: formatAbsolute },
+ 0xFE : { name: "INC", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0xC6 : { name: "DEC", bytes: 2, toString: formatZeroPage },
+ 0xD6 : { name: "DEC", bytes: 2, toString: formatZeroPageIndexedX },
+ 0xCE : { name: "DEC", bytes: 3, toString: formatAbsolute },
+ 0xDE : { name: "DEC", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0x38 : { name: "SEC", bytes: 1, toString: formatName },
+ 0xF8 : { name: "SED", bytes: 1, toString: formatName },
+ 0x78 : { name: "SEI", bytes: 1, toString: formatName },
+ 0x18 : { name: "CLC", bytes: 1, toString: formatName },
+ 0xD8 : { name: "CLD", bytes: 1, toString: formatName },
+ 0x58 : { name: "CLI", bytes: 1, toString: formatName },
+ 0xB8 : { name: "CLV", bytes: 1, toString: formatName },
+ 0xAA : { name: "TAX", bytes: 1, toString: formatName },
+ 0x8A : { name: "TXA", bytes: 1, toString: formatName },
+ 0xA8 : { name: "TAY", bytes: 1, toString: formatName },
+ 0x98 : { name: "TYA", bytes: 1, toString: formatName },
+ 0xBA : { name: "TSX", bytes: 1, toString: formatName },
+ 0x9A : { name: "TXS", bytes: 1, toString: formatName },
+ 0x48 : { name: "PHA", bytes: 1, toString: formatName },
+ 0x08 : { name: "PHP", bytes: 1, toString: formatName },
+ 0x68 : { name: "PLA", bytes: 1, toString: formatName },
+ 0x28 : { name: "PLP", bytes: 1, toString: formatName },
+ 0x29 : { name: "AND", bytes: 2, toString: formatImmediate },
+ 0x25 : { name: "AND", bytes: 2, toString: formatZeroPage },
+ 0x35 : { name: "AND", bytes: 2, toString: formatZeroPageIndexedX },
+ 0x2D : { name: "AND", bytes: 3, toString: formatAbsolute },
+ 0x3D : { name: "AND", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0x39 : { name: "AND", bytes: 3, toString: formatAbsoluteIndexedY },
+ 0x21 : { name: "AND", bytes: 2, toString: formatIndirectIndexedX },
+ 0x31 : { name: "AND", bytes: 2, toString: formatIndirectIndexedY },
+ 0x09 : { name: "ORA", bytes: 2, toString: formatImmediate },
+ 0x05 : { name: "ORA", bytes: 2, toString: formatZeroPage },
+ 0x15 : { name: "ORA", bytes: 2, toString: formatZeroPageIndexedX },
+ 0x0D : { name: "ORA", bytes: 3, toString: formatAbsolute },
+ 0x1D : { name: "ORA", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0x19 : { name: "ORA", bytes: 3, toString: formatAbsoluteIndexedY },
+ 0x01 : { name: "ORA", bytes: 2, toString: formatIndirectIndexedX },
+ 0x11 : { name: "ORA", bytes: 2, toString: formatIndirectIndexedY },
+ 0x49 : { name: "EOR", bytes: 2, toString: formatImmediate },
+ 0x45 : { name: "EOR", bytes: 2, toString: formatZeroPage },
+ 0x55 : { name: "EOR", bytes: 2, toString: formatZeroPageIndexedX },
+ 0x4D : { name: "EOR", bytes: 3, toString: formatAbsolute },
+ 0x5D : { name: "EOR", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0x59 : { name: "EOR", bytes: 3, toString: formatAbsoluteIndexedY },
+ 0x41 : { name: "EOR", bytes: 2, toString: formatIndirectIndexedX },
+ 0x51 : { name: "EOR", bytes: 2, toString: formatIndirectIndexedY },
+ 0x4C : { name: "JMP", bytes: 3, toString: formatAbsolute },
+ 0x6C : { name: "JMP", bytes: 3, toString: formatIndirect },
+ 0x20 : { name: "JSR", bytes: 3, toString: formatAbsolute },
+ 0x60 : { name: "RTS", bytes: 1, toString: formatName },
+ 0x40 : { name: "RTI", bytes: 1, toString: formatName },
+ 0x90 : { name: "BCC", bytes: 2, toString: formatRelative },
+ 0xB0 : { name: "BCS", bytes: 2, toString: formatRelative },
+ 0xF0 : { name: "BEQ", bytes: 2, toString: formatRelative },
+ 0xD0 : { name: "BNE", bytes: 2, toString: formatRelative },
+ 0x10 : { name: "BPL", bytes: 2, toString: formatRelative },
+ 0x30 : { name: "BMI", bytes: 2, toString: formatRelative },
+ 0x50 : { name: "BVC", bytes: 2, toString: formatRelative },
+ 0x70 : { name: "BVS", bytes: 2, toString: formatRelative },
+ 0x2A : { name: "ROL", bytes: 1, toString: formatAccumulator },
+ 0x26 : { name: "ROL", bytes: 2, toString: formatZeroPage },
+ 0x36 : { name: "ROL", bytes: 2, toString: formatZeroPageIndexedX },
+ 0x2E : { name: "ROL", bytes: 3, toString: formatAbsolute },
+ 0x3E : { name: "ROL", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0x6A : { name: "ROR", bytes: 1, toString: formatAccumulator },
+ 0x66 : { name: "ROR", bytes: 2, toString: formatZeroPage },
+ 0x76 : { name: "ROR", bytes: 2, toString: formatZeroPageIndexedX },
+ 0x6E : { name: "ROR", bytes: 3, toString: formatAbsolute },
+ 0x7E : { name: "ROR", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0x4A : { name: "LSR", bytes: 1, toString: formatAccumulator },
+ 0x46 : { name: "LSR", bytes: 2, toString: formatZeroPage },
+ 0x56 : { name: "LSR", bytes: 2, toString: formatZeroPageIndexedX },
+ 0x4E : { name: "LSR", bytes: 3, toString: formatAbsolute },
+ 0x5E : { name: "LSR", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0x0A : { name: "ASL", bytes: 1, toString: formatAccumulator },
+ 0x06 : { name: "ASL", bytes: 2, toString: formatZeroPage },
+ 0x16 : { name: "ASL", bytes: 2, toString: formatZeroPageIndexedX },
+ 0x0E : { name: "ASL", bytes: 3, toString: formatAbsolute },
+ 0x1E : { name: "ASL", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0xC9 : { name: "CMP", bytes: 2, toString: formatImmediate },
+ 0xC5 : { name: "CMP", bytes: 2, toString: formatZeroPage },
+ 0xD5 : { name: "CMP", bytes: 2, toString: formatZeroPageIndexedX },
+ 0xCD : { name: "CMP", bytes: 3, toString: formatAbsolute },
+ 0xDD : { name: "CMP", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0xD9 : { name: "CMP", bytes: 3, toString: formatAbsoluteIndexedY },
+ 0xC1 : { name: "CMP", bytes: 2, toString: formatIndirectIndexedX },
+ 0xD1 : { name: "CMP", bytes: 2, toString: formatIndirectIndexedY },
+ 0xE0 : { name: "CPX", bytes: 2, toString: formatImmediate },
+ 0xE4 : { name: "CPX", bytes: 2, toString: formatZeroPage },
+ 0xEC : { name: "CPX", bytes: 3, toString: formatAbsolute },
+ 0xC0 : { name: "CPY", bytes: 2, toString: formatImmediate },
+ 0xC4 : { name: "CPY", bytes: 2, toString: formatZeroPage },
+ 0xCC : { name: "CPY", bytes: 3, toString: formatAbsolute },
+ 0x24 : { name: "BIT", bytes: 2, toString: formatZeroPage },
+ 0x2C : { name: "BIT", bytes: 3, toString: formatAbsolute },
+ 0x69 : { name: "ADC", bytes: 2, toString: formatImmediate },
+ 0x65 : { name: "ADC", bytes: 2, toString: formatZeroPage },
+ 0x75 : { name: "ADC", bytes: 2, toString: formatZeroPageIndexedX },
+ 0x6D : { name: "ADC", bytes: 3, toString: formatAbsolute },
+ 0x7D : { name: "ADC", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0x79 : { name: "ADC", bytes: 3, toString: formatAbsoluteIndexedY },
+ 0x61 : { name: "ADC", bytes: 2, toString: formatIndirectIndexedX },
+ 0x71 : { name: "ADC", bytes: 2, toString: formatIndirectIndexedY },
+ 0xE9 : { name: "SBC", bytes: 2, toString: formatImmediate },
+ 0xE5 : { name: "SBC", bytes: 2, toString: formatZeroPage },
+ 0xF5 : { name: "SBC", bytes: 2, toString: formatZeroPageIndexedX },
+ 0xED : { name: "SBC", bytes: 3, toString: formatAbsolute },
+ 0xFD : { name: "SBC", bytes: 3, toString: formatAbsoluteIndexedX },
+ 0xF9 : { name: "SBC", bytes: 3, toString: formatAbsoluteIndexedY },
+ 0xE1 : { name: "SBC", bytes: 2, toString: formatIndirectIndexedX },
+ 0xF1 : { name: "SBC", bytes: 2, toString: formatIndirectIndexedY },
+ 0xEA : { name: "NOP", bytes: 1, toString: formatName },
+ 0x00 : { name: "BRK", bytes: 1, toString: formatName },
+};
+
View
5 index.html
@@ -6,6 +6,7 @@
<!-- script src="otrail.js"></script -->
<script src="appletoo.js"></script>
<script src="disk2.js"></script>
+ <script src="disassembler.js"></script>
<style type="text/css" media="screen">
canvas {
border: 1px solid #DDD;
@@ -79,8 +80,10 @@
};
function run() {
+ var RESET_VECTOR = 0xFFFC;
+ var pc_num = a.read_word(RESET_VECTOR);
+
var pc = document.getElementById("pc_field").value;
- var pc_num = a.read_word(0xFFFC);
if (pc.length > 0) {
pc_num = parseInt(pc, 16);
Please sign in to comment.
Something went wrong with that request. Please try again.