UNPKG

cirsim

Version:

Cirsim Circuit Simulator

347 lines (279 loc) 8.87 kB
import {Component} from '../Component'; import {ComponentPropertiesDlg} from '../Dlg/ComponentPropertiesDlg'; import {PaletteImage} from '../Graphics/PaletteImage'; /** * Component: General purpose decoder. * * Supports bus or single-bit inputs. * @constructor */ export const BusDecoder = function() { Component.call(this); this.height = 100; this.width = 52; this.value = null; let bus = true; Object.defineProperty(this,"bus",{ get: function() { return bus; }, set: function(value) { if(bus !== value) { if(this.ins[0].bus !== value) { for(let i=0; i<this.ins.length; i++) { this.ins[i].clear(); } this.ins = []; } bus = value; this.ensureIO(); } } }); this.setSize(3); // Size output and one input this.circuitOuts = []; this.ensureIO(); }; BusDecoder.prototype = Object.create(Component.prototype); BusDecoder.prototype.constructor = BusDecoder; BusDecoder.prototype.prefix = 'U'; BusDecoder.type = "BusDecoder"; ///< Name to use in files BusDecoder.label = "Decoder"; ///< Label for the palette BusDecoder.desc = "Configurable Decoder"; ///< Description for the palette BusDecoder.description = `<h2>Decoder</h2> <p>The Decoder component converts a 2 to 4-bit binary value on <strong>In</strong> to a true on one of four to sixteen output lines. The number of outputs is determined by the bit size and is configurable.</p>`; BusDecoder.order = 400; BusDecoder.help = 'busdecoder'; BusDecoder.prototype.setSize = function(size) { this.size = size; this.outputs = 1; for(let i=0; i<size; i++) { this.outputs *= 2; } this.ensureIO(); } /** * Compute the gate result * @param state */ BusDecoder.prototype.compute = function(state) { if(this.bus) { if(Array.isArray(state[0])) { let c = 0; let pow = 1; for(let i=0; i<state[0].length; i++) { c += (state[0][i] ? pow : 0); pow *= 2; } this.set(c); } else { this.set(null); } } else { let c = 0; let pow = 1; let undef = false; for(let i=0; i<state.length; i++) { if(state[i] === undefined) { undef = true; break; } c += (state[i] ? pow : 0); pow *= 2; } this.set(undef ? null : c); } }; /** * Set the value of this component. * @param c Value to set. */ BusDecoder.prototype.set = function(c) { if(c !== null && c !== undefined) { c = c & (this.outputs - 1); for(let i=0; i<this.outputs; i++) { this.outs[i].set(i == c); } } else { for(let i=0; i<this.outputs; i++) { this.outs[i].set(undefined); } } this.value = c; } /** * Clone this component object. * @returns {BusDecoder} */ BusDecoder.prototype.clone = function() { const copy = new BusDecoder(); copy.bus = this.bus; copy.setSize(this.size); copy.copyFrom(this); return copy; }; /** * Ensure the actual number of inputs matches the * defined bus size. */ BusDecoder.prototype.ensureIO = function() { const spacing = 16; const pinLen = 14; let i; let recompute = false; this.height = this.outputs * spacing + 26; if(this.height < 80) { this.height = 80; } // // Inputs // if(this.ins.length > 0) { // // Test if we switched input types // If so, disconnect everything and zero the inputs // if(this.ins[0].bus !== this.bus) { for(i=0; i<this.ins.length; i++) { this.ins[i].clear(); } this.ins = []; } } if(this.bus) { if(this.ins.length < 1) { this.addIn(-this.width / 2, 0, pinLen, "In").bus = true; recompute = true; } } else { let startY = this.size / 2 * spacing - 8; for(i=0; i<this.size; i++) { let pinY = startY - i * spacing; let inp = null; if(i < this.ins.length) { inp = this.ins[i]; inp.x = -this.width / 2; inp.y = pinY; inp.len = pinLen; } else { // Add any new pins inp = this.addIn(-this.width / 2, pinY, pinLen, "I" + i); recompute = true; inp.orientation = 'w'; } } // Delete pins that have ceased to exist if(i < this.ins.length) { for( ; i<this.ins.length; i++) { this.ins[i].clear(); } this.ins.splice(this.size); } } // // Outputs // let x = this.width / 2; let startY = this.outputs / 2 * spacing - 8; for(i=0; i<this.outputs; i++) { if(i >= this.outs.length) { break; } this.outs[i].name = "O" + i; this.outs[i].x = x; this.outs[i].y = startY - i * spacing; this.outs[i].len = pinLen; } // Add any new pins for(; i<this.outputs; i++) { this.addOut(x, startY - i * spacing, pinLen, "O" + i); recompute = true; } // Delete pins that have ceased to exist if(i < this.outs.length) { for( ; i<this.outs.length; i++) { this.outs[i].clear(); } this.outs.splice(this.outputs); } if(recompute) { this.set(this.value); } } /** * Draw component object. * @param context Display context * @param view View object */ BusDecoder.prototype.draw = function(context, view) { this.selectStyle(context, view); var leftX = this.x - this.width/2 - 0.5; var rightX = this.x + this.width/2 + 0.5; var topY = this.y - this.height/2 - 0.5; var botY = this.y + this.height/2 + 0.5; this.drawBox(context); context.font = "12px Times"; context.textAlign = "center"; context.fillText("decoder", this.x, this.y + this.height/2 - 2); if(this.value !== null && this.value >= 0 && this.value < this.outputs) { let y = this.outs[this.value].y; let rx = this.value >= 10 ? 26 : 19; this.jaggedLine(context, leftX + 15, this.y, rightX - rx, this.y + y, 0.5); } this.drawName(context, 0, -this.height/2 + 12); this.drawIO(context, view); }; /** * Load this object from an object converted from JSON * @param obj Object from JSON */ BusDecoder.prototype.load = function(obj) { this.bus = obj["bus"] !== false; this.setSize(obj["size"]); Component.prototype.load.call(this, obj); }; /** * Create a save object suitable for conversion to JSON for export. * @returns {*} */ BusDecoder.prototype.save = function() { const obj = Component.prototype.save.call(this); obj.size = this.size; obj.bus = this.bus; return obj; }; BusDecoder.prototype.properties = function(main) { const dlg = new ComponentPropertiesDlg(this, main); const id = dlg.uniqueId(); let html = `<div class="control1 center"><label for="${id}">Size (bits): </label> <input class="number" type="text" name="${id}" id="${id}" value="${this.size}"></div>`; html += '<div class="control center"><div class="choosers">'; const busId = dlg.uniqueId(); html += ` <label><input type="radio" name="${busId}" ${this.bus ? 'checked' : ''} value="1"> Bus Input</label> <label><input type="radio" name="${busId}" ${!this.bus ? 'checked' : ''} value="0"> Single Bit Inputs</label>`; html += '</div></div>'; dlg.extra(html, function() { const size = parseInt(document.getElementById(id).value); if(isNaN(size) || size < 2 || size > 4) { return "Size must be an integer from 2 to 4"; } return null; }, () => { this.setSize(document.getElementById(id).value); this.bus = document.querySelector(`input[name=${busId}]:checked`).value === '1'; }); dlg.open(); }; /** * Create a PaletteImage object for a the component */ BusDecoder.paletteImage = function() { const pi = new PaletteImage(60, 44); pi.box(20, 42); pi.io(10, -17.5, 'e', 8, 5); pi.io(-10, 0, 'w'); pi.drawText("Decoder", 0, 20, "4px Times"); return pi; }