From 31baee10153aaefe7f38e27b681d7f90edd9987f Mon Sep 17 00:00:00 2001 From: Sascha Nitsch Date: Thu, 21 Jul 2022 19:55:40 +0200 Subject: [PATCH] initial import --- addrregister.svg | 84 ++++++++++++++++++ addrregister.ts | 96 +++++++++++++++++++++ chipselect.svg | 27 ++++++ chipselect.ts | 65 ++++++++++++++ dynamicclock.svg | 9 ++ dynamicclock.ts | 76 ++++++++++++++++ eightbitalu.svg | 61 +++++++++++++ eightbitalu.ts | 83 ++++++++++++++++++ eightbitclock.svg | 19 ++++ eightbitclock.ts | 142 ++++++++++++++++++++++++++++++ logicrisingedge.svg | 10 +++ logicrisingedge.ts | 55 ++++++++++++ memory.svg | 99 +++++++++++++++++++++ memory.ts | 206 ++++++++++++++++++++++++++++++++++++++++++++ multiregister.svg | 103 ++++++++++++++++++++++ multiregister.ts | 147 +++++++++++++++++++++++++++++++ 16 files changed, 1282 insertions(+) create mode 100644 addrregister.svg create mode 100644 addrregister.ts create mode 100644 chipselect.svg create mode 100644 chipselect.ts create mode 100644 dynamicclock.svg create mode 100644 dynamicclock.ts create mode 100644 eightbitalu.svg create mode 100644 eightbitalu.ts create mode 100644 eightbitclock.svg create mode 100644 eightbitclock.ts create mode 100644 logicrisingedge.svg create mode 100644 logicrisingedge.ts create mode 100644 memory.svg create mode 100644 memory.ts create mode 100644 multiregister.svg create mode 100644 multiregister.ts diff --git a/addrregister.svg b/addrregister.svg new file mode 100644 index 0000000..2162545 --- /dev/null +++ b/addrregister.svg @@ -0,0 +1,84 @@ + + + + + + O15 + + O14 + + O13 + + O12 + + O11 + + O10 + + O9 + + O8 + + O7 + + O6 + + O5 + + O4 + + O3 + + O2 + + O1 + + O0 + + + CLK + + + CLK + + + R + + + W + + + A7 + + A6 + + A5 + + A4 + + A3 + + A2 + + A1 + + A0 + + + B7 + + B6 + + B5 + + B4 + + B3 + + B2 + + B1 + + B0 + + 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 @@ + + + + + + RAM + + ROM + + + A15 + + A14 + + A13 + + A12 + + A11 + + A10 + + A9 + + A8 + + 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 @@ + + + + + + D7 + + D6 + + D5 + + D4 + + D3 + + D2 + + D1 + + D0 + + + A7 + + A6 + + A5 + + A4 + + A3 + + A2 + + A1 + + A0 + + + B7 + + B6 + + B5 + + B4 + + B3 + + B2 + + B1 + + B0 + + + SUB + + WRITE + + 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 @@ + + + + + + + + + + + 0 + + + + + 0 + + + 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 @@ + + + + + + D7 + + D6 + + D5 + + D4 + + D3 + + D2 + + D1 + + D0 + + + CLK + + + R + + + W + + + + EN + + + A15 + + + + A14 + + + + A13 + + + + A12 + + + + A11 + + + + A10 + + + + A9 + + + + A8 + + + + A7 + + + + A6 + + + + A5 + + + + A4 + + + + A3 + + + + A2 + + + + A1 + + + + A0 + + + 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 @@ + + + + + + D7 + + D6 + + D5 + + D4 + + D3 + + D2 + + D1 + + D0 + + + CLK + + + CLK + + + R3 + + R2 + + R1 + + R0 + + RON + + + W3 + + W2 + + W1 + + W0 + + WON + + + A7 + + A6 + + A5 + + A4 + + A3 + + A2 + + A1 + + A0 + + + B7 + + B6 + + B5 + + B4 + + B3 + + B2 + + B1 + + B0 + + + ALU_A3 + + ALU_A2 + + ALU_A1 + + ALU_A0 + + + ALU_B3 + + ALU_B2 + + ALU_B1 + + ALU_B0 + + + 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'; +}