diff --git a/index.html b/index.html index d448a29..1eccac7 100644 --- a/index.html +++ b/index.html @@ -1,23 +1,109 @@
+ + - + ++ + + + +
+ +LEDs: +
+ D7 D6 D5 D4 D3 D2 D1 D0
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+
+ A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+ ○
+
CPU dump: +
+ + +Memory dump: +
+ diff --git a/js/sim8800.js b/js/sim8800.js index 4d94978..4f43d31 100644 --- a/js/sim8800.js +++ b/js/sim8800.js @@ -24,12 +24,18 @@ class Sim8800 { /** * @param {number} memSize The memory size, in bytes. * @param {number} clockRate The clock rate. + * @param {Element?} dumpCpuElem The DOM element used to render + * dumped CPU status. null to disable the feature. + * @param {Element?} dumpMemElem The DOM element used to render + * dumped memory contents. null to disable the feature. */ - constructor(memSize, clockRate) { - /** @type {number} */ + constructor(memSize, clockRate, + dumpCpuElem, dumpMemElem) { this.clockRate = clockRate; - /** @type {Array\n']; + for (let i = 0; i < this.mem.length; i += 16) { + sb.push(Sim8800.toHex(i, 4)); + sb.push(' '); + for (let j = i; + j < Math.min(this.mem.length, i + 16); + j++) { + sb.push(Sim8800.toHex(this.mem[j], 2)); + sb.push((j + 1) % 8 == 0 ? ' ' : ' '); + } + sb.push('\n'); + } + sb.push('\n'); + this.dumpMemElem.innerHTML = sb.join(''); + } + } + + /** + * Decodes the FLAGs register. + * @param {number} flags The value of the FLAGs register. + * @return {Object} The decoded flags. + */ + decodeFlags(flags) { + var ret = {}; + ret.sign = flags & 0x80 != 0; + ret.zero = flags & 0x40 != 0; + ret.auxiliaryCarry = flags & 0x10 != 0; + ret.parity = flags & 0x04 != 0; + ret.carry = flags & 0x01 != 0; + return ret; + } + + /** + * Dumps the internal CPU status to HTML, for debugging or mornitoring. + */ + dumpCpu() { + if (this.dumpCpuElem) { + var cpu = CPU8080.status(); + var sb = ['
\n']; + sb.push('PC = ' + Sim8800.toHex(cpu.pc, 4) + ' '); + sb.push('SP = ' + Sim8800.toHex(cpu.sp, 4) + '\n'); + sb.push('A = ' + Sim8800.toHex(cpu.a, 2) + ' '); + sb.push('B = ' + Sim8800.toHex(cpu.b, 2) + ' '); + sb.push('C = ' + Sim8800.toHex(cpu.c, 2) + ' '); + sb.push('D = ' + Sim8800.toHex(cpu.d, 2) + '\n'); + sb.push('E = ' + Sim8800.toHex(cpu.e, 2) + ' '); + sb.push('F = ' + Sim8800.toHex(cpu.f, 2) + ' '); + sb.push('H = ' + Sim8800.toHex(cpu.h, 2) + ' '); + sb.push('L = ' + Sim8800.toHex(cpu.l, 2) + '\n'); + var flags = this.decodeFlags(cpu.f); + sb.push('FLAGS: '); + if (flags.sign) sb.push('SIGN '); + if (flags.zero) sb.push('ZERO '); + if (flags.auxiliaryCarry) sb.push('AC '); + if (flags.parity) sb.push('PARITY '); + if (flags.carry) sb.push('CARRY '); + sb.push('\n'); + this.dumpCpuElem.innerHTML = sb.join(''); + } + } + /** * Returns the byteTo (write memory) callback. * @return {function(number, number)} @@ -47,8 +182,6 @@ class Sim8800 { return function(address, value) { address = address % self.mem.length; self.mem[address] = value; - window.console.log('writing byte @' + Sim8800.toHex(address, 8) - + ' : ' + Sim8800.toHex(value, 2)); }; } @@ -61,8 +194,6 @@ class Sim8800 { return function(address) { address = address % self.mem.length; var value = self.mem[address]; - window.console.log('reading byte @' + Sim8800.toHex(address, 8) - + ' : ' + Sim8800.toHex(value, 2)); return value; }; } @@ -74,8 +205,6 @@ class Sim8800 { getWritePortCallback() { var self = this; return function(address, value) { - window.console.log('writing port @' + Sim8800.toHex(address, 8) - + ' : ' + Sim8800.toHex(value, 2)); if (address == 0xff) { // We only care about port 0xff. } @@ -93,70 +222,73 @@ class Sim8800 { if (address == 0xff) { // We only care about port 0xff. } - window.console.log('reading port @' + Sim8800.toHex(address, 8) - + ' : ' + Sim8800.toHex(value, 2)); return value; }; } /** - * Formats a number to fixed length hex string. - * @param {number} n The number to be formatted. - * @param {number} len The output length, with leading zeros. + * Gets the clock ticker callback. + * @return {function()} */ - static toHex(n, len) { - var leadingZeros = (new Array(len)).fill('0').join(''); - return (leadingZeros + n.toString(16)).substr(-len); - } - - /** - * Fills the memory with random numbers. - */ - initMem() { - for (let i = 0; i < this.mem.length; i++) { - this.mem[i] = Math.floor(Math.random() * 256); - } - } - - /** - * Dumps the memory to HTML, for debugging or monitoring. - * @param {Element} containerElem The DOM element to hold the generated HTML. - */ - dumpMem(containerElem) { - var sb = ['
\n']; - for (let i = 0; i < this.mem.length; i += 16) { - sb.push(Sim8800.toHex(i, 4)); - sb.push(' '); - for (let j = i; - j < Math.min(this.mem.length, i + 16); - j++) { - sb.push(Sim8800.toHex(this.mem[j], 2)); - sb.push((j + 1) % 8 == 0 ? ' ' : ' '); + getClockTickerCallback() { + var self = this; + return function(timestamp) { + if (self.running) { + var cycles = self.clockRate / 1000; + CPU8080.steps(cycles); + self.dumpCpu(); + self.dumpMem(); + window.setTimeout(self.getClockTickerCallback(), 1); } - sb.push('\n'); - } - sb.push('\n'); - containerElem.innerHTML = sb.join(''); + }; } /** - * Dumps the internal CPU status to HTML, for debugging or mornitoring. - * @param {Element} containerElem The DOM element to hold the generated HTML. + * Runs a single CPU step. */ - dumpCpu(containerElem) { - var cpu = CPU8080.status(); - var sb = ['
\n']; - sb.push('PC = ' + Sim8800.toHex(cpu.pc, 4) + ' '); - sb.push('SP = ' + Sim8800.toHex(cpu.sp, 4) + '\n'); - sb.push('A = ' + Sim8800.toHex(cpu.a, 2) + ' '); - sb.push('B = ' + Sim8800.toHex(cpu.b, 2) + ' '); - sb.push('C = ' + Sim8800.toHex(cpu.c, 2) + ' '); - sb.push('D = ' + Sim8800.toHex(cpu.d, 2) + '\n'); - sb.push('E = ' + Sim8800.toHex(cpu.e, 2) + ' '); - sb.push('F = ' + Sim8800.toHex(cpu.f, 2) + ' '); - sb.push('H = ' + Sim8800.toHex(cpu.h, 2) + ' '); - sb.push('L = ' + Sim8800.toHex(cpu.l, 2) + '\n'); - sb.push('\n'); - containerElem.innerHTML = sb.join(''); + singleStep() { + CPU8080.steps(1); + this.dumpCpu(); + this.dumpMem(); + } + + /** + * Powers on the machine. + */ + powerOn() { + this.stop(); + reset(); + this.initMem(); + } + + /** + * Powers off the machine. + */ + powerOff() { + this.stop(); + reset(); + this.initMem(); + } + + /** + * Resets the machine. + */ + reset() { + CPU8080.reset(); + } + + /** + * Stops the CPU. + */ + stop() { + this.running = false; + } + + /** + * Starts the CPU. + */ + start() { + this.running = true; + window.setTimeout(this.getClockTickerCallback(), 1); } };