// 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); } } }