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'; +}