diff --git a/addrregister.svg b/addrregister.svg
new file mode 100644
index 0000000..2162545
--- /dev/null
+++ b/addrregister.svg
@@ -0,0 +1,84 @@
+
+
diff --git a/addrregister.ts b/addrregister.ts
new file mode 100644
index 0000000..117a8ba
--- /dev/null
+++ b/addrregister.ts
@@ -0,0 +1,96 @@
+// Address Register component for the LogicSimulator used on the 8-bit-computer
+// Copyright (C) 2022 Sascha Nitsch
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+class AddrRegister extends BaseComponent {
+ private r = 0;
+ private R = false;
+ private W = false;
+ private CLK = false;
+ private oldCLK = false;
+ constructor(simulator: Simulator, id: string, param: BaseComponentParam) {
+ super(simulator, id, param);
+ for (let i = 0; i < 8; ++i) {
+ this.pins.set('A' + (7 - i), new TriState(this, -40, i * 10 - 80));
+ this.pins.set('B' + (7 - i), new TriState(this, -40, i * 10 + 10));
+ this.stateMapBool.set('A' + (7 - i), false);
+ this.stateMapBool.set('B' + (7 - i), false);
+ }
+ this.pins.set('R', new TriState(this, 40, 100));
+ this.pins.set('W', new TriState(this, 40, 110));
+ this.pins.set('CLK', new TriState(this, 40, 80));
+ this.pins.set('invCLK', new TriState(this, 40, 90));
+ for (let i = 0; i < 16; ++i) {
+ this.pins.set('O' + (15 - i), new TriState(this, 40, i * 10 - 80));
+ this.stateMapBool.set('O' + (15 - i), false);
+ }
+ }
+
+ setup(canvas: SVGElement) {
+ super.doSetup('addrregister', canvas);
+ }
+
+ update(): boolean {
+ let i;
+ let value;
+ // is R set ? then send out to addrbus
+ if (this.R) {
+ // which register is selected via R3...R0
+ value = this.r;
+ for (i = 0; i < 16; ++i) {
+ this.getPin('O' + i).setBool((value & (1 << i)) > 0 ? true : false);
+ }
+ }
+
+ const rising = this.CLK && this.oldCLK === false;
+ this.oldCLK = this.CLK;
+ // is W set ? read from bus on raising clock edge
+ if (this.W && rising) {
+ this.r =
+ this.asint(this.getStateBool('A7'), 32768) +
+ this.asint(this.getStateBool('A6'), 16384) +
+ this.asint(this.getStateBool('A5'), 8192) +
+ this.asint(this.getStateBool('A4'), 4096) +
+ this.asint(this.getStateBool('A3'), 2048) +
+ this.asint(this.getStateBool('A2'), 1024) +
+ this.asint(this.getStateBool('A1'), 512) +
+ this.asint(this.getStateBool('A0'), 256) +
+ this.asint(this.getStateBool('B7'), 128) +
+ this.asint(this.getStateBool('B6'), 64) +
+ this.asint(this.getStateBool('B5'), 32) +
+ this.asint(this.getStateBool('B4'), 16) +
+ this.asint(this.getStateBool('B3'), 8) +
+ this.asint(this.getStateBool('B2'), 4) +
+ this.asint(this.getStateBool('B1'), 2) +
+ this.asint(this.getStateBool('B0'), 1);
+ }
+ return false;
+ }
+
+ io() {
+ this.CLK = this.binary(this.getPin('CLK').getAndReset(), true);
+ this.getPin('invCLK').getAndReset();
+ this.R = this.binary(this.getPin('R').getAndReset(), true);
+ this.W = this.binary(this.getPin('W').getAndReset(), true);
+ for (let i = 0; i < 8; ++i) {
+ this.stateMapBool.set('A' + i, this.binary(this.getPin('A' + i).getAndReset(), true));
+ this.stateMapBool.set('B' + i, this.binary(this.getPin('B' + i).getAndReset(), true));
+ }
+ for (let i = 0; i < 16; ++i) {
+ this.stateMapBool.set('O' + i, this.binary(this.getPin('O' + i).getAndReset(), true));
+ }
+ }
+}
diff --git a/chipselect.svg b/chipselect.svg
new file mode 100644
index 0000000..966ca0a
--- /dev/null
+++ b/chipselect.svg
@@ -0,0 +1,27 @@
+
+
diff --git a/chipselect.ts b/chipselect.ts
new file mode 100644
index 0000000..3f43caa
--- /dev/null
+++ b/chipselect.ts
@@ -0,0 +1,65 @@
+// Chip select component for the LogicSimulator used on the 8-bit-computer
+// Copyright (C) 2022 Sascha Nitsch
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+class ChipSelect extends BaseComponent {
+ private addr = 0;
+ private rom = true;
+ private ram = true;
+ constructor(simulator: Simulator, id: string, param: BaseComponentParam) {
+ super(simulator, id, param);
+ for (let a = 0; a < 8; ++a) {
+ this.pins.set('A' + (15 - a), new TriState(this, -40, a * 10 - 35));
+ }
+ this.pins.set('rom', new TriState(this, 40, -25));
+ this.pins.set('ram', new TriState(this, 40, -35));
+ }
+
+ setup(canvas: SVGElement) {
+ super.doSetup('chipselect', canvas);
+ }
+
+ update(): boolean {
+ const addr = this.addr;
+ let changed = false;
+ if (addr < 0x3fff) {
+ // ROM
+ changed ||= this.rom !== false || this.ram !== true;
+ this.getPin('rom').setBool(false);
+ this.getPin('ram').setBool(true);
+ } else if (addr < 0x7fff) {
+ // RAM
+ changed ||= this.rom !== false || this.ram !== true;
+ this.getPin('rom').setBool(true);
+ this.getPin('ram').setBool(false);
+ } else {
+ this.getPin('rom').setBool(true);
+ this.getPin('ram').setBool(true);
+ }
+ return changed;
+ }
+
+ io() {
+ this.getPin('rom').getAndReset();
+ this.getPin('ram').getAndReset();
+ this.addr = 0;
+ let multi = 256;
+ for (let i = 8; i < 16; ++i) {
+ this.addr += this.asint(this.binary(this.getPin('A' + i).getAndReset(), true), multi);
+ multi <<= 1;
+ }
+ }
+}
diff --git a/dynamicclock.svg b/dynamicclock.svg
new file mode 100644
index 0000000..69d9cac
--- /dev/null
+++ b/dynamicclock.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/dynamicclock.ts b/dynamicclock.ts
new file mode 100644
index 0000000..237c0e0
--- /dev/null
+++ b/dynamicclock.ts
@@ -0,0 +1,76 @@
+// Clock generator component for the LogicSimulator used on the 8-bit-computer
+// Copyright (C) 2022 Sascha Nitsch
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+interface DynamicClockParam extends BaseComponentParam {
+ speed?: string;
+}
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+class DynamicClock extends BaseComponent {
+ private q = false;
+ private clockState = false;
+ private speed: number;
+ private input = '';
+ private speedInput: HTMLInputElement | null = null;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ private timer: any;
+ constructor(simulator: Simulator, id: string, param: DynamicClockParam) {
+ super(simulator, id, param);
+ this.pins.set('q', new TriState(this, 30, 0));
+ if (param.speed) {
+ this.speed = 0;
+ this.input = param.speed;
+ } else {
+ this.speed = 1;
+ }
+ }
+
+ tick() {
+ this.clockState = !this.clockState;
+ this.simulator.tick();
+ }
+
+ setup(canvas: SVGElement) {
+ super.doSetup('dynamicclock', canvas);
+ if (this.speed === 0) {
+ this.speedInput = document.getElementById(this.input);
+ if (this.speedInput) {
+ this.speedInput.addEventListener('change', this.changeSpeed.bind(this));
+ this.speed = parseFloat(this.speedInput.value);
+ this.timer = setInterval(this.tick.bind(this), 500 / this.speed);
+ }
+ } else {
+ this.timer = setInterval(this.tick.bind(this), 500 / this.speed);
+ }
+ }
+
+ update(): boolean {
+ const oldstate = this.q;
+ this.q = this.clockState;
+ this.getPin('q').setBool(this.q);
+ return oldstate !== this.q;
+ }
+
+ changeSpeed() {
+ if (!this.speedInput) return;
+ this.speed = parseFloat(this.speedInput.value);
+ clearInterval(this.timer);
+ this.timer = setInterval(this.tick.bind(this), 500 / this.speed);
+ }
+
+ close() {
+ clearInterval(this.timer);
+ }
+}
diff --git a/eightbitalu.svg b/eightbitalu.svg
new file mode 100644
index 0000000..bff5635
--- /dev/null
+++ b/eightbitalu.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/eightbitalu.ts b/eightbitalu.ts
new file mode 100644
index 0000000..8a335ee
--- /dev/null
+++ b/eightbitalu.ts
@@ -0,0 +1,83 @@
+// Breadboard component for the LogicSimulator used on the 8-bit-computer
+// Copyright (C) 2022 Sascha Nitsch
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+class EightBitALU extends BaseComponent {
+ private write: boolean | string;
+ private sub: boolean | string;
+ private ret: number;
+ private a = 0;
+ private b = 0;
+ private r = 0;
+ constructor(simulator: Simulator, id: string, param: BaseComponentParam) {
+ super(simulator, id, param);
+ this.pins.set('sub', new TriState(this, -15, 90));
+ this.sub = 'float';
+ this.pins.set('write', new TriState(this, 20, 90));
+ this.write = 'float';
+ this.ret = 0;
+ for (let i = 0; i < 8; ++i) {
+ this.pins.set('A' + (7 - i), new TriState(this, -40, i * 10 - 80));
+ this.pins.set('B' + (7 - i), new TriState(this, -40, i * 10 + 10));
+ this.pins.set('D' + (7 - i), new TriState(this, 40, i * 20 - 70));
+ this.stateMapBool.set('A' + (7 - i), true);
+ this.stateMapBool.set('B' + (7 - i), true);
+ this.stateMapBool.set('D' + (7 - i), true);
+ }
+ }
+
+ setup(canvas: SVGElement) {
+ super.doSetup('eightbitalu', canvas);
+ }
+
+ io() {
+ for (let i = 0; i < 8; ++i) {
+ this.stateMapBool.set('A' + i, this.binary(this.getPin('A' + i).getAndReset(), true));
+ this.stateMapBool.set('B' + i, this.binary(this.getPin('B' + i).getAndReset(), true));
+ this.stateMapBool.set('D' + i, this.binary(this.getPin('D' + i).getAndReset(), true));
+ }
+ this.sub = this.binary(this.getPin('sub').getAndReset(), true);
+ this.write = this.binary(this.getPin('write').getAndReset(), true);
+ this.a =
+ this.asint(this.getStateBool('A7'), 128) +
+ this.asint(this.getStateBool('A6'), 64) +
+ this.asint(this.getStateBool('A5'), 32) +
+ this.asint(this.getStateBool('A4'), 16) +
+ this.asint(this.getStateBool('A3'), 8) +
+ this.asint(this.getStateBool('A2'), 4) +
+ this.asint(this.getStateBool('A1'), 2) +
+ this.asint(this.getStateBool('A0'), 1);
+ this.b =
+ this.asint(this.getStateBool('B7'), 128) +
+ this.asint(this.getStateBool('B6'), 64) +
+ this.asint(this.getStateBool('B5'), 32) +
+ this.asint(this.getStateBool('B4'), 16) +
+ this.asint(this.getStateBool('B3'), 8) +
+ this.asint(this.getStateBool('B2'), 4) +
+ this.asint(this.getStateBool('B1'), 2) +
+ this.asint(this.getStateBool('B0'), 1);
+ }
+
+ update(): boolean {
+ const oldret = this.ret;
+ this.ret = (this.sub ? this.a - this.b : this.a + this.b) & 0xff;
+ for (let i = 0; i < 8; ++i) {
+ const v = (this.ret >> i) & 1 ? WireState.high : WireState.low;
+ this.getPin('D' + i).setState(this.write ? v : WireState.float);
+ }
+ return oldret !== this.ret;
+ }
+}
diff --git a/eightbitclock.svg b/eightbitclock.svg
new file mode 100644
index 0000000..515df97
--- /dev/null
+++ b/eightbitclock.svg
@@ -0,0 +1,19 @@
+
+
diff --git a/eightbitclock.ts b/eightbitclock.ts
new file mode 100644
index 0000000..3d48b4b
--- /dev/null
+++ b/eightbitclock.ts
@@ -0,0 +1,142 @@
+// Clock generation component for the LogicSimulator used on the 8-bit-computer
+// Copyright (C) 2022 Sascha Nitsch
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+function EightBitClockInit() {
+ interface EightBitClockText {
+ auto: string;
+ manual: string;
+ }
+
+ interface EightBitClockParam extends BaseComponentParam {
+ speed?: string;
+ mode?: string;
+ auto?: string;
+ manual?: string;
+ }
+ class EightBitClock extends LogicBase {
+ private clockState: boolean;
+ private speed: number;
+ private input: string;
+ private runAuto: boolean;
+ private texts: EightBitClockText;
+ private manual: NodeListOf | undefined;
+ private speedInput: HTMLInputElement | null = null;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ private timer: any = undefined;
+ private auto: NodeListOf | undefined;
+ private CLK = false;
+ constructor(simulator: Simulator, id: string, param: EightBitClockParam) {
+ super(simulator, id, param);
+ this.pins.set('CLK', new TriState(this, 30, -20));
+ this.pins.set('invCLK', new TriState(this, 30, 20));
+ this.clockState = false;
+ if (param.speed) {
+ this.speed = 0;
+ this.input = param.speed;
+ } else {
+ this.speed = 1;
+ this.input = 'undefined';
+ }
+ this.runAuto = param.mode ? param.mode === 'auto' : false;
+ this.texts = {
+ auto: param.auto ? param.auto : 'auto',
+ manual: param.manual ? param.manual : 'manual',
+ };
+ this.auto = undefined;
+ this.manual = undefined;
+ }
+ tick(): void {
+ this.clockState = !this.clockState;
+ if (this.manual) {
+ this.manual[0].textContent = this.clockState ? '1' : '0';
+ }
+ this.simulator.tick();
+ }
+ setup(canvas: SVGElement) {
+ super.doSetup('eightbitclock', canvas);
+ if (this.speed === 0) {
+ this.speedInput = document.getElementById(this.input);
+ if (this.speedInput) {
+ this.speedInput.addEventListener('change', this.changeSpeed.bind(this));
+ this.speed = parseFloat(this.speedInput.value);
+ if (this.runAuto) {
+ this.timer = setInterval(this.tick.bind(this), 500 / this.speed);
+ }
+ }
+ } else {
+ this.timer = setInterval(this.tick.bind(this), 500 / this.speed);
+ }
+ if (this.element) {
+ const gAuto = this.element.querySelectorAll('g.auto')[0];
+ this.auto = gAuto.querySelectorAll('text.inner');
+ gAuto.querySelectorAll('.label')[0].innerHTML = this.texts.auto;
+ gAuto.addEventListener('mousedown', this.toggleAuto.bind(this));
+
+ const gManual = this.element.querySelectorAll('g.manual')[0];
+ this.manual = gManual.querySelectorAll('text.inner');
+ gManual.querySelectorAll('.label')[0].innerHTML = this.texts.manual;
+ gManual.addEventListener('mousedown', this.toggleManual.bind(this));
+ }
+ }
+
+ update() {
+ const oldstate = this.CLK;
+ this.CLK = this.clockState;
+ this.getPin('CLK').setBool(this.clockState);
+ this.getPin('invCLK').setBool(!this.clockState);
+ return oldstate !== this.clockState;
+ }
+
+ changeSpeed() {
+ if (this.speedInput) {
+ this.speed = parseFloat(this.speedInput.value);
+ }
+ if (this.runAuto) {
+ clearInterval(this.timer);
+ this.timer = setInterval(this.tick.bind(this), 500 / this.speed);
+ }
+ }
+
+ close() {
+ clearInterval(this.timer);
+ }
+
+ toggleAuto(e: Event) {
+ e.preventDefault();
+ if (this.runAuto) {
+ clearInterval(this.timer);
+ } else {
+ this.timer = setInterval(this.tick.bind(this), 500 / this.speed);
+ }
+ this.runAuto = !this.runAuto;
+ if (this.auto) {
+ this.auto[0].textContent = this.runAuto ? '1' : '0';
+ }
+ }
+
+ toggleManual(e: Event) {
+ e.preventDefault();
+ this.tick();
+ }
+ }
+ window.EightBitClock = EightBitClock;
+}
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+function EightBitClockDepends() {
+ return 'LogicBase';
+}
diff --git a/logicrisingedge.svg b/logicrisingedge.svg
new file mode 100644
index 0000000..4346ecc
--- /dev/null
+++ b/logicrisingedge.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/logicrisingedge.ts b/logicrisingedge.ts
new file mode 100644
index 0000000..667b8b5
--- /dev/null
+++ b/logicrisingedge.ts
@@ -0,0 +1,55 @@
+// Logic Rising Edge component for the LogicSimulator used on the 8-bit-Computer
+// Copyright (C) 2022 Sascha Nitsch
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+interface LogicRisingEdgeParam extends BaseComponentParam {
+ delay?: number;
+}
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+class LogicRisingEdge extends BaseComponent {
+ private a = false;
+ private olda = false;
+ private q = false;
+ private delay: number;
+ private activeDelay = 0;
+ constructor(simulator: Simulator, id: string, param: LogicRisingEdgeParam) {
+ super(simulator, id, param);
+ this.pins.set('a', new TriState(this, -30, 0));
+ this.pins.set('q', new TriState(this, 30, 0));
+ this.delay = param.delay || 0;
+ }
+
+ setup(canvas: SVGElement) {
+ super.doSetup('logicraisingedge', canvas);
+ }
+
+ io() {
+ this.a = this.binary(this.getPin('a').getAndReset(), true);
+ }
+
+ update(): boolean {
+ const oldstate = this.q;
+ if (this.olda === false && this.a === true) {
+ this.activeDelay = this.delay;
+ }
+ this.q = this.activeDelay > 0;
+ this.getPin('q').setBool(this.q);
+ this.olda = this.a;
+ if (this.activeDelay > 0) {
+ this.activeDelay--;
+ }
+ return oldstate !== this.q || this.q === true;
+ }
+}
diff --git a/memory.svg b/memory.svg
new file mode 100644
index 0000000..ad1c2e1
--- /dev/null
+++ b/memory.svg
@@ -0,0 +1,99 @@
+
+
diff --git a/memory.ts b/memory.ts
new file mode 100644
index 0000000..280651f
--- /dev/null
+++ b/memory.ts
@@ -0,0 +1,206 @@
+// Memory RAM and ROM component for the LogicSimulator used on the 8-bit-computer
+// Copyright (C) 2022 Sascha Nitsch
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+interface MemoryParam extends BaseComponentParam {
+ type: string;
+ size: number;
+ source: string;
+ content?: string;
+}
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+class Memory extends BaseComponent {
+ private writeable: boolean;
+ private size: number;
+ private source: string;
+ private r: number[] = [];
+ private W = false;
+ private R = false;
+ private D = 0;
+ private endaddr: number;
+ private CLK = false;
+ private oldCLK = false;
+ private invEnable = true;
+ private memory: HTMLTextAreaElement | null = null;
+ private addr = 0;
+ constructor(simulator: Simulator, id: string, param: MemoryParam) {
+ super(simulator, id, param);
+ this.writeable = param.type === 'RAM';
+ this.size = param.size;
+ this.source = param.source;
+ if (param.content === null) {
+ for (let r = 0; r < this.size; ++r) {
+ this.r[r] = 0;
+ }
+ }
+ if (param.content === 'rising') {
+ for (let r = 0; r < this.size; ++r) {
+ this.r[r] = r & 255;
+ }
+ }
+ if (param.content === 'random') {
+ for (let r = 0; r < this.size; ++r) {
+ this.r[r] = Math.round(Math.random() * 255);
+ }
+ }
+ this.endaddr = 15;
+ let threshold = 65536;
+ // disable addr lines if needed
+ while (this.size < threshold) {
+ --this.endaddr;
+ threshold = threshold / 2;
+ }
+ for (let a = 0; a < 8; ++a) {
+ this.pins.set('D' + (7 - a), new TriState(this, 40, a * 10 - 75));
+ }
+ this.pins.set('R', new TriState(this, 40, 25));
+ if (this.writeable) {
+ this.pins.set('W', new TriState(this, 40, 35));
+ }
+ this.pins.set('CLK', new TriState(this, 40, 5));
+ this.pins.set('invEnable', new TriState(this, 40, 45));
+ for (let i = 0; i <= this.endaddr; ++i) {
+ this.pins.set('A' + (this.endaddr - i), new TriState(this, -40, i * 10 - 75));
+ }
+ }
+
+ setup(canvas: SVGElement) {
+ super.doSetup('memory', canvas);
+ if (this.element === null) {
+ return;
+ }
+ const offset = -(15 - this.endaddr) * 10;
+ for (let i = 0; i < 16; ++i) {
+ const e = this.element.getElementsByClassName('a' + i);
+ if (i <= this.endaddr) {
+ // move up
+ if (offset !== 0) {
+ e[0].setAttribute('transform', 'translate(0,' + offset + ')');
+ }
+ } else {
+ e[0].classList.add('hidden');
+ }
+ }
+ // resize box
+ const height = Math.max(170 + offset, 140);
+ this.element.getElementsByClassName('outer')[0].setAttribute('height', height.toString());
+ if (!this.writeable) {
+ this.element.getElementsByClassName('w')[0].classList.add('hidden');
+ }
+ this.memory = document.getElementById(this.source);
+ const memorysave = document.getElementById(this.source + 'save');
+ if (memorysave) {
+ memorysave.addEventListener('mousedown', this.updateMem.bind(this));
+ }
+ this.updateText();
+ }
+
+ updateMem() {
+ if (this.memory === null) return;
+ const txt = this.memory.value.split('\n');
+ let addr = 0;
+ for (let i = 0; i < txt.length - 1; ++i) {
+ const cells = txt[i].split(' ');
+ for (let j = 0; j < 36; ++j) {
+ if (j % 9 === 0) continue;
+ this.r[addr] = parseInt(cells[j], 16);
+ ++addr;
+ }
+ }
+ this.simulator.manualtick();
+ }
+
+ update(): boolean {
+ // is R set ? then send out to databus
+ if (this.R && this.invEnable === false) {
+ // which address is selected
+ const value = this.r[this.addr];
+ for (let i = 0; i < 8; ++i) {
+ this.getPin('D' + i).setBool((value & (1 << i)) > 0 ? true : false);
+ }
+ }
+
+ const rising = this.CLK && this.oldCLK === false;
+ this.oldCLK = this.CLK;
+ // is W set ? read from bus on raising clock edge
+ if (this.writeable && this.W && this.invEnable === false && rising) {
+ this.r[this.addr] = this.D;
+ // update text input
+ this.updateText();
+ }
+
+ return false;
+ }
+
+ hex(d: number, padding: number) {
+ let hex = d.toString(16).toUpperCase();
+ while (hex.length < padding) {
+ hex = '0' + hex;
+ }
+ return hex;
+ }
+
+ updateText() {
+ let txt = '';
+ for (let i = 0; i < this.size; ++i) {
+ if (i % 32 !== 0) {
+ txt += ' ';
+ if (i % 8 === 0) {
+ txt += '0x' + this.hex(i, 4) + ': ';
+ }
+ } else {
+ txt += '0x' + this.hex(i, 4) + ': ';
+ }
+ txt += (this.r[i] >> 4).toString(16) + (this.r[i] & 15).toString(16);
+ if (i % 32 === 31) {
+ txt += '\n';
+ }
+ }
+ if (this.memory) {
+ this.memory.value = txt;
+ }
+ }
+
+ io() {
+ this.addr = 0;
+ if (this.endaddr >= 15) this.addr += this.asint(this.binary(this.getPin('A15').getAndReset(), true), 32768);
+ if (this.endaddr >= 14) this.addr += this.asint(this.binary(this.getPin('A14').getAndReset(), true), 16384);
+ if (this.endaddr >= 13) this.addr += this.asint(this.binary(this.getPin('A13').getAndReset(), true), 8192);
+ if (this.endaddr >= 12) this.addr += this.asint(this.binary(this.getPin('A12').getAndReset(), true), 4096);
+ if (this.endaddr >= 11) this.addr += this.asint(this.binary(this.getPin('A11').getAndReset(), true), 2048);
+ if (this.endaddr >= 10) this.addr += this.asint(this.binary(this.getPin('A10').getAndReset(), true), 1024);
+ if (this.endaddr >= 9) this.addr += this.asint(this.binary(this.getPin('A9').getAndReset(), true), 512);
+ if (this.endaddr >= 8) this.addr += this.asint(this.binary(this.getPin('A8').getAndReset(), true), 256);
+ if (this.endaddr >= 7) this.addr += this.asint(this.binary(this.getPin('A7').getAndReset(), true), 128);
+ if (this.endaddr >= 6) this.addr += this.asint(this.binary(this.getPin('A6').getAndReset(), true), 64);
+ if (this.endaddr >= 5) this.addr += this.asint(this.binary(this.getPin('A5').getAndReset(), true), 32);
+ if (this.endaddr >= 4) this.addr += this.asint(this.binary(this.getPin('A4').getAndReset(), true), 16);
+ if (this.endaddr >= 3) this.addr += this.asint(this.binary(this.getPin('A3').getAndReset(), true), 8);
+ if (this.endaddr >= 2) this.addr += this.asint(this.binary(this.getPin('A2').getAndReset(), true), 4);
+ if (this.endaddr >= 1) this.addr += this.asint(this.binary(this.getPin('A1').getAndReset(), true), 2);
+ if (this.endaddr >= 0) this.addr += this.asint(this.binary(this.getPin('A0').getAndReset(), true), 1);
+ this.CLK = this.binary(this.getPin('CLK').getAndReset(), true);
+ this.R = this.binary(this.getPin('R').getAndReset(), true);
+ this.invEnable = this.binary(this.getPin('invEnable').getAndReset(), true);
+ if (this.writeable) {
+ this.W = this.binary(this.getPin('W').getAndReset(), true);
+ }
+ this.D = 0;
+ for (let i = 0; i < 8; ++i) {
+ this.D += this.asint(this.binary(this.getPin('D' + i).getAndReset(), true), 1 << i);
+ }
+ }
+}
diff --git a/multiregister.svg b/multiregister.svg
new file mode 100644
index 0000000..12495f3
--- /dev/null
+++ b/multiregister.svg
@@ -0,0 +1,103 @@
+
+
diff --git a/multiregister.ts b/multiregister.ts
new file mode 100644
index 0000000..b9381ae
--- /dev/null
+++ b/multiregister.ts
@@ -0,0 +1,147 @@
+// Multiple register component for the LogicSimulator used by the 8-bit-computer
+// Copyright (C) 2022 Sascha Nitsch
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+interface MultiRegisterParam extends BaseComponentParam {
+ count?: number;
+}
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+function MultiRegisterInit() {
+ class MultiRegister extends LogicBase {
+ private regcount: number;
+ private r: number[];
+ private oldCLK: boolean;
+ private CLK: boolean;
+ private RON: boolean;
+ private WON: boolean;
+ constructor(simulator: Simulator, id: string, param: MultiRegisterParam) {
+ super(simulator, id, param);
+ this.regcount = param.count || 1;
+ this.r = [];
+ for (let a = 0; a < 4; ++a) {
+ this.pins.set('R' + (3 - a), new TriState(this, 40, a * 10 + 30));
+ this.pins.set('W' + (3 - a), new TriState(this, 40, a * 10 + 80));
+ this.pins.set('ALU_A' + (3 - a), new TriState(this, -40, a * 10 + 100));
+ this.pins.set('ALU_B' + (3 - a), new TriState(this, -40, a * 10 + 140));
+ }
+ this.pins.set('RON', new TriState(this, 40, 70));
+ this.pins.set('WON', new TriState(this, 40, 120));
+ this.pins.set('CLK', new TriState(this, 40, 0, undefined, { id: 'CLK' }));
+ this.pins.set('invCLK', new TriState(this, 40, 10));
+ for (let i = 0; i < 8; ++i) {
+ this.pins.set('D' + (7 - i), new TriState(this, 40, i * 10 - 80));
+ this.pins.set('A' + (7 - i), new TriState(this, -40, i * 10 - 80));
+ this.pins.set('B' + (7 - i), new TriState(this, -40, i * 10 + 10));
+ }
+ for (let b = 0; b < this.regcount; ++b) {
+ this.r[b] = 0;
+ }
+ this.oldCLK = false;
+ this.CLK = false;
+ this.RON = false;
+ this.WON = false;
+ }
+ setup(canvas: SVGElement) {
+ super.doSetup('multiregister', canvas);
+ }
+
+ update(): boolean {
+ let i;
+ let r;
+ let value;
+ // is R_ON set ? then send out to databus
+ if (this.RON) {
+ // which register is selected via R3...R0
+ r =
+ this.asint(this.getStateBool('R3'), 8) +
+ this.asint(this.getStateBool('R2'), 4) +
+ this.asint(this.getStateBool('R1'), 2) +
+ this.asint(this.getStateBool('R0'), 1);
+ value = this.r[r];
+ for (i = 0; i < 8; ++i) {
+ this.getPin('D' + i).setBool((value & (1 << i)) > 0 ? true : false);
+ }
+ }
+ const rising = this.CLK && this.oldCLK === false;
+ this.oldCLK = this.CLK;
+ // is W_ON set ? read from bus on raising clock edge
+ if (this.WON && rising) {
+ r =
+ this.asint(this.getStateBool('W3'), 8) +
+ this.asint(this.getStateBool('W2'), 4) +
+ this.asint(this.getStateBool('W1'), 2) +
+ this.asint(this.getStateBool('W0'), 1);
+ value =
+ this.asint(this.getStateBool('D7'), 128) +
+ this.asint(this.getStateBool('D6'), 64) +
+ this.asint(this.getStateBool('D5'), 32) +
+ this.asint(this.getStateBool('D4'), 16) +
+ this.asint(this.getStateBool('D3'), 8) +
+ this.asint(this.getStateBool('D2'), 4) +
+ this.asint(this.getStateBool('D1'), 2) +
+ this.asint(this.getStateBool('D0'), 1);
+ this.r[r] = value;
+ }
+
+ // output to alu A
+ const alu_a =
+ this.asint(this.getStateBool('ALU_A3'), 8) +
+ this.asint(this.getStateBool('ALU_A2'), 4) +
+ this.asint(this.getStateBool('ALU_A1'), 2) +
+ this.asint(this.getStateBool('ALU_A0'), 1);
+ //console.log("Alu A set to", alu_a);
+ value = this.r[alu_a];
+ for (i = 0; i < 8; ++i) {
+ this.getPin('A' + i).setBool((value & (1 << i)) > 0 ? true : false);
+ }
+ // output to alu B
+ const alu_b =
+ this.asint(this.getStateBool('ALU_B3'), 8) +
+ this.asint(this.getStateBool('ALU_B2'), 4) +
+ this.asint(this.getStateBool('ALU_B1'), 2) +
+ this.asint(this.getStateBool('ALU_B0'), 1);
+ value = this.r[alu_b];
+ for (i = 0; i < 8; ++i) {
+ this.getPin('B' + i).setBool((value & (1 << i)) > 0 ? true : false);
+ }
+ return false;
+ }
+
+ io() {
+ this.CLK = this.binary(this.getPin('CLK').getAndReset(), false);
+ this.getPin('invCLK').getAndReset();
+ this.RON = this.binary(this.getPin('RON').getAndReset(), false);
+ this.WON = this.binary(this.getPin('WON').getAndReset(), false);
+ for (let i = 0; i < 8; ++i) {
+ this.stateMapBool.set('D' + i, this.binary(this.getPin('D' + i).getAndReset(), true));
+ this.stateMapBool.set('A' + i, this.binary(this.getPin('A' + i).getAndReset(), true));
+ this.stateMapBool.set('B' + i, this.binary(this.getPin('B' + i).getAndReset(), true));
+ }
+ for (let a = 0; a < 4; ++a) {
+ this.stateMapBool.set('R' + a, this.binary(this.getPin('R' + a).getAndReset(), true));
+ this.stateMapBool.set('W' + a, this.binary(this.getPin('W' + a).getAndReset(), true));
+ this.stateMapBool.set('ALU_A' + a, this.binary(this.getPin('ALU_A' + a).getAndReset(), true));
+ this.stateMapBool.set('ALU_B' + a, this.binary(this.getPin('ALU_B' + a).getAndReset(), true));
+ }
+ }
+ }
+ window.MultiRegister = MultiRegister;
+}
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+function MultiRegisterDepends() {
+ return 'LogicBase';
+}