690 lines
24 KiB
JavaScript
690 lines
24 KiB
JavaScript
class LogicWireNode extends Element {
|
|
constructor(_Container, RestoreData = null, logicengine) {
|
|
super(_Container, RestoreData,logicengine,1);
|
|
this.removeProperty("Inputs");
|
|
this.Name = "WireNode";
|
|
this.Width = 20;
|
|
this.Height = 20;
|
|
}
|
|
getOutput(Output = 0) {
|
|
if (Output > 0) return false;
|
|
return this.Inputs[0];
|
|
}
|
|
drawElement(x,y,ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
let ElementCatalog_WireNode = new ElementCatalog_Element("WireNode","A simple compact wire node for organizing wiring","-O-",LogicWireNode,[]);
|
|
ElementReferenceTable.push(ElementCatalog_WireNode);
|
|
ElementCategory_Other.addElement(ElementCatalog_WireNode);
|
|
|
|
class LogicAND extends Element {
|
|
constructor(_Container, RestoreData = null, logicengine,Inputs) {
|
|
super(_Container, RestoreData,logicengine,Inputs);
|
|
this.Name = "AND";
|
|
}
|
|
|
|
getOutput(Output=0) {
|
|
if (super.getOutput() === 0) return false;
|
|
let ANDResult = true;
|
|
for (let a = 0; a < this.totalInputs();a++) {
|
|
if (!this.Inputs[a]) ANDResult = false;
|
|
}
|
|
return ANDResult;
|
|
}
|
|
|
|
drawElement(x,y,ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
//this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height);
|
|
let xOffset = 20;
|
|
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.moveTo(x+xOffset,y);
|
|
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
|
ctx.quadraticCurveTo(x+(this.Width - xOffset),y,x+(this.Width-xOffset),y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(this.Width - xOffset),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
|
ctx.lineTo(x+xOffset,y+this.Height);
|
|
ctx.lineTo(x+xOffset,y);
|
|
ctx.lineWidth = "3";
|
|
ctx.fillStyle = "#f7e979";
|
|
ctx.shadowColor = shadowColor;
|
|
ctx.shadowBlur = "6";
|
|
ctx.shadowOffsetX = 2;
|
|
ctx.shadowOffsetY = 2;
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
ctx.restore();
|
|
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
let ElementCatalog_AND = new ElementCatalog_Element("AND","The AND gate only outputs high when all of the inputs are high.","&",LogicAND,[2]);
|
|
ElementReferenceTable.push(ElementCatalog_AND);
|
|
ElementCategory_LOGIC.addElement(ElementCatalog_AND);
|
|
|
|
class LogicNAND extends LogicAND {
|
|
constructor(_Container, RestoreData = null, logicengine,Inputs) {
|
|
super(_Container, RestoreData,logicengine,Inputs);
|
|
this.Name = "NAND";
|
|
this.Width = 110;
|
|
}
|
|
|
|
getOutput(Output=0) {
|
|
if (this.Disconnecting) return false;
|
|
if (super.getOutput()) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
drawElement(x,y,ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
let xOffset = 20;
|
|
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.moveTo(x+xOffset,y);
|
|
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
|
ctx.quadraticCurveTo(x+(this.Width - (xOffset*1.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(this.Width - (xOffset*1.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
|
ctx.lineTo(x+xOffset,y+this.Height);
|
|
ctx.lineTo(x+xOffset,y);
|
|
ctx.lineWidth = "3";
|
|
ctx.fillStyle = "#f7e979";
|
|
ctx.strokeStyle = "#000000";
|
|
ctx.shadowColor = shadowColor;
|
|
ctx.shadowBlur = "6";
|
|
ctx.shadowOffsetX = 2;
|
|
ctx.shadowOffsetY = 2;
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
|
|
ctx.beginPath();
|
|
ctx.fillStyle = "#000000";
|
|
ctx.strokeStyle = "#000000";
|
|
ctx.lineWidth = "1";
|
|
ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
|
|
ctx.restore();
|
|
this.drawTextCentered(ctx,x,y,this.Width-xOffset,this.Height,this.Designator,"12px Console","#000");
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
let ElementCatalog_NAND = new ElementCatalog_Element("NAND","The NAND gate always outputs a high signal unless all the inputs are high then it goes low.","!&",LogicNAND,[2]);
|
|
ElementReferenceTable.push(ElementCatalog_NAND);
|
|
ElementCategory_LOGIC.addElement(ElementCatalog_NAND);
|
|
|
|
class LogicOR extends Element {
|
|
constructor(_Container, RestoreData = null, logicengine,Inputs) {
|
|
super(_Container, RestoreData,logicengine,Inputs);
|
|
this.Name = "OR";
|
|
}
|
|
|
|
getOutput(Output=0) {
|
|
if (super.getOutput() === 0) return false;
|
|
let ORResult = false;
|
|
for (let a = 0; a < this.totalInputs();a++) {
|
|
if (this.Inputs[a]) ORResult = true;
|
|
}
|
|
return ORResult;
|
|
}
|
|
drawElement(x,y,ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
let drawWidth = this.Width;
|
|
let drawHeight = this.Height;
|
|
let xOffset = 20;
|
|
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
|
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.moveTo(x+(xOffset/4),y);
|
|
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
|
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y,x+(this.Width-xOffset),y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
|
ctx.lineTo(x+(xOffset/4),y+this.Height);
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
|
ctx.lineWidth = "3";
|
|
ctx.fillStyle = "#f7e979";
|
|
ctx.shadowColor = shadowColor;
|
|
ctx.shadowBlur = "6";
|
|
ctx.shadowOffsetX = 2;
|
|
ctx.shadowOffsetY = 2;
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
ctx.restore();
|
|
|
|
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
let ElementCatalog_OR = new ElementCatalog_Element("OR","The OR gate outputs a high when any input is HIGH.","|",LogicOR,[2]);
|
|
ElementReferenceTable.push(ElementCatalog_OR);
|
|
ElementCategory_LOGIC.addElement(ElementCatalog_OR);
|
|
|
|
class LogicNOR extends LogicOR {
|
|
constructor(_Container, RestoreData = null, logicengine,Inputs) {
|
|
super(_Container, RestoreData,logicengine,Inputs);
|
|
this.Name = "NOR";
|
|
this.Width = 110;
|
|
}
|
|
getOutput(Output=0) {
|
|
if (super.getOutput() === 0) return false;
|
|
if (super.getOutput()) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
drawElement(x,y,ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
let xOffset = 20;
|
|
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
|
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.moveTo(x+(xOffset/4),y);
|
|
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
|
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
|
ctx.lineTo(x+(xOffset/4),y+this.Height);
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
|
ctx.lineWidth = "3";
|
|
ctx.fillStyle = "#f7e979";
|
|
ctx.shadowColor = shadowColor;
|
|
ctx.shadowBlur = "6";
|
|
ctx.shadowOffsetX = 2;
|
|
ctx.shadowOffsetY = 2;
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
|
|
ctx.beginPath();
|
|
ctx.fillStyle = "#000000";
|
|
ctx.strokeStyle = "#000000";
|
|
ctx.lineWidth = "1";
|
|
ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
|
|
ctx.restore();
|
|
|
|
this.drawTextCentered(ctx,x,y,this.Width-xOffset,this.Height,this.Designator,"12px Console","#000");
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
let ElementCatalog_NOR = new ElementCatalog_Element("NOR","The NOR gate outputs a high only when all inputs are low.","!|",LogicNOR,[2]);
|
|
ElementReferenceTable.push(ElementCatalog_NOR);
|
|
ElementCategory_LOGIC.addElement(ElementCatalog_NOR);
|
|
|
|
class LogicXOR extends Element {
|
|
constructor(_Container, RestoreData = null, logicengine) {
|
|
super(_Container, RestoreData,logicengine,2); // Only 2 inputs on XOR
|
|
this.Name = "XOR";
|
|
this.removeProperty("Inputs");
|
|
}
|
|
|
|
getOutput(Output=0) {
|
|
if (super.getOutput() === 0) return false;
|
|
let ORResult = false;
|
|
if ( (this.Inputs[0] && !this.Inputs[1]) || (!this.Inputs[0] && this.Inputs[1]) ) ORResult = true;
|
|
return ORResult;
|
|
}
|
|
|
|
drawElement(x,y,ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
let drawWidth = this.Width;
|
|
let drawHeight = this.Height;
|
|
|
|
let xOffset = 20;
|
|
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
|
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.moveTo(x+(xOffset/4),y);
|
|
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
|
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y,x+(this.Width-xOffset),y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
|
ctx.lineTo(x+(xOffset/4),y+this.Height);
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
|
ctx.lineWidth = "3";
|
|
ctx.fillStyle = "#f7e979";
|
|
ctx.shadowColor = shadowColor;
|
|
ctx.shadowBlur = "6";
|
|
ctx.shadowOffsetX = 2;
|
|
ctx.shadowOffsetY = 2;
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
|
|
ctx.lineWidth = "2";
|
|
ctx.beginPath();
|
|
ctx.shadowColor = "transparent";
|
|
ctx.moveTo(x+(xOffset/4)+10,y+this.Height);
|
|
ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height - (this.Height/32)),x+xOffset+10,y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height/32),x+(xOffset/4)+10,y);
|
|
ctx.stroke();
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(x+(xOffset/4),y+this.Height);
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
|
ctx.stroke();
|
|
ctx.restore();
|
|
|
|
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
let ElementCatalog_XOR = new ElementCatalog_Element("XOR","The XOR gate outputs a high when only 1 input is high.","^",LogicXOR,[]);
|
|
ElementReferenceTable.push(ElementCatalog_XOR);
|
|
ElementCategory_LOGIC.addElement(ElementCatalog_XOR);
|
|
|
|
class LogicXNOR extends Element {
|
|
constructor(_Container, RestoreData = null, logicengine) {
|
|
super(_Container, RestoreData,logicengine,2); // Only 2 inputs on XOR
|
|
this.Name = "XNOR";
|
|
this.removeProperty("Inputs");
|
|
this.Width = 110;
|
|
}
|
|
|
|
getOutput(Output=0) {
|
|
if (super.getOutput() === 0) return false;
|
|
let ORResult = false;
|
|
if ( (this.Inputs[0] && !this.Inputs[1]) || (!this.Inputs[0] && this.Inputs[1]) ) ORResult = true;
|
|
return !ORResult;
|
|
}
|
|
|
|
drawElement(x,y,ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
let drawWidth = this.Width;
|
|
let drawHeight = this.Height;
|
|
|
|
let xOffset = 20;
|
|
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
|
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.moveTo(x+(xOffset/4),y);
|
|
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
|
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
|
ctx.lineTo(x+(xOffset/4),y+this.Height);
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
|
ctx.lineWidth = "3";
|
|
ctx.fillStyle = "#f7e979";
|
|
ctx.shadowColor = shadowColor;
|
|
ctx.shadowBlur = "6";
|
|
ctx.shadowOffsetX = 2;
|
|
ctx.shadowOffsetY = 2;
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
|
|
ctx.beginPath();
|
|
ctx.fillStyle = "#000000";
|
|
ctx.strokeStyle = "#000000";
|
|
ctx.lineWidth = "1";
|
|
ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
|
|
ctx.lineWidth = "2";
|
|
ctx.beginPath();
|
|
ctx.shadowColor = "transparent";
|
|
ctx.moveTo(x+(xOffset/4)+10,y+this.Height);
|
|
ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height - (this.Height/32)),x+xOffset+10,y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height/32),x+(xOffset/4)+10,y);
|
|
ctx.stroke();
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(x+(xOffset/4),y+this.Height);
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
|
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
|
ctx.stroke();
|
|
ctx.restore();
|
|
|
|
this.drawTextCentered(ctx,x,y,this.Width-(xOffset/2),this.Height,this.Designator,"10px Console","#000");
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
|
|
let ElementCatalog_XNOR = new ElementCatalog_Element("XNOR","The XNOR gate outputs a high only when both inputs are either low or high.","!^",LogicXNOR,[]);
|
|
ElementReferenceTable.push(ElementCatalog_XNOR);
|
|
ElementCategory_LOGIC.addElement(ElementCatalog_XNOR);
|
|
|
|
class LogicNOT extends Element {
|
|
constructor(_Container, RestoreData = null, logicengine) {
|
|
super(_Container, RestoreData,logicengine,1); // Only 1 inputs on NOT
|
|
this.Name = "NOT";
|
|
this.removeProperty("Inputs");
|
|
this.Width = 110;
|
|
}
|
|
|
|
getOutput(Output=0) {
|
|
if (super.getOutput() === 0) return false;
|
|
if (this.Inputs[0]) return false;
|
|
return true;
|
|
}
|
|
|
|
drawElement(x,y,ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
let drawWidth = this.Width;
|
|
let drawHeight = this.Height;
|
|
|
|
let xOffset = 20;
|
|
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
|
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.moveTo(x+xOffset,y);
|
|
ctx.lineTo(x+(this.Width-(xOffset+10)),y+(this.Height/2));
|
|
ctx.lineTo(x+xOffset,y+this.Height);
|
|
ctx.lineTo(x+xOffset,y);
|
|
ctx.lineWidth = "3";
|
|
ctx.fillStyle = "#f7e979";
|
|
ctx.shadowColor = shadowColor;
|
|
ctx.shadowBlur = "6";
|
|
ctx.shadowOffsetX = 2;
|
|
ctx.shadowOffsetY = 2;
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
|
|
ctx.beginPath();
|
|
ctx.fillStyle = "#000000";
|
|
ctx.strokeStyle = "#000000";
|
|
ctx.lineWidth = "1";
|
|
ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
|
|
ctx.restore();
|
|
this.drawTextCentered(ctx,x,y,this.Width-(xOffset),this.Height,this.Designator,"12px Console","#000");
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
let ElementCatalog_NOT = new ElementCatalog_Element("NOT","The NOT gate outputs the opposite of the input.","!",LogicNOT,[]);
|
|
ElementReferenceTable.push(ElementCatalog_NOT);
|
|
ElementCategory_LOGIC.addElement(ElementCatalog_NOT);
|
|
|
|
class LogicBuffer extends Element {
|
|
ClockTick() {
|
|
this.Output = this.Inputs[0];
|
|
this.Task.Enabled = false;
|
|
this.Task.LastCall = 0;
|
|
|
|
for (let a = 0; a < this.OutputConnections.length; a++) {
|
|
this.LogicEngine.RecursionCount = 0;
|
|
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
|
}
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
}
|
|
|
|
Delete() {
|
|
super.Delete();
|
|
this.LogicEngine.Scheduler.deleteTask(this.Task);
|
|
}
|
|
|
|
getOutput(Output=0) {
|
|
if (super.getOutput() === 0) return false;
|
|
return this.Output;
|
|
}
|
|
|
|
setInput(Input, Value) {
|
|
if (Input > 0) return;
|
|
//super.setInput(Input, Value);
|
|
Value = this._Container.isHigh(this,Input);
|
|
if (Value !== false) Value = true;
|
|
this.Inputs[Input] = Value;
|
|
this.redraw = true;
|
|
|
|
if (!this.Task.Enabled) {
|
|
this.Task.LastCall = 0;
|
|
this.Task.Enabled = true;
|
|
}
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
}
|
|
|
|
constructor(_Container, RestoreData = null, logicengine) {
|
|
super(_Container, RestoreData,logicengine,1);
|
|
this.removeProperty("Inputs");
|
|
this.Output = false;
|
|
this.Name = "Buffer";
|
|
this.Width = 100;
|
|
this.Task = new Task("BufferTask","Buffer",0,9999999,this.ClockTick.bind(this));
|
|
this.Task.Time = 4;
|
|
this.Task.Enabled = false;
|
|
this.Task.LastCall = 0;
|
|
this.removeProperty("Inputs");
|
|
if (RestoreData) this.Output = RestoreData.Output;
|
|
this.LogicEngine.Scheduler.addTask(this.Task);
|
|
}
|
|
|
|
toJSON(key) {
|
|
let superjson = super.toJSON(key);
|
|
superjson.Task = {
|
|
Enabled: this.Task.Enabled,
|
|
Time: this.Task.Time,
|
|
LastCall: Date.now() - this.Task.LastCall,
|
|
CallCount: this.Task.CallCount
|
|
};
|
|
return superjson;
|
|
}
|
|
|
|
|
|
drawElement(x, y, ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
let drawWidth = this.Width;
|
|
let drawHeight = this.Height;
|
|
|
|
let xOffset = 20;
|
|
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
|
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.moveTo(x+xOffset,y);
|
|
ctx.lineTo(x+(this.Width-(xOffset)),y+(this.Height/2));
|
|
ctx.lineTo(x+xOffset,y+this.Height);
|
|
ctx.lineTo(x+xOffset,y);
|
|
ctx.lineWidth = "3";
|
|
ctx.fillStyle = "#f7e979";
|
|
ctx.shadowColor = shadowColor;
|
|
ctx.shadowBlur = "6";
|
|
ctx.shadowOffsetX = 2;
|
|
ctx.shadowOffsetY = 2;
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
ctx.restore();
|
|
this.drawTextCentered(ctx,x,y,this.Width-(xOffset),this.Height,this.Designator,"12px Console","#000");
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
let ElementCatalog_BUFFER = new ElementCatalog_Element("Buffer","The buffer is a special gate to allow the prevention of recursion loops.","|>",LogicBuffer,[]);
|
|
ElementReferenceTable.push(ElementCatalog_BUFFER);
|
|
ElementCategory_LOGIC.addElement(ElementCatalog_BUFFER);
|
|
|
|
class RAMElement extends Element {
|
|
constructor(_Container, RestoreData = null, logicengine) {
|
|
super(_Container, RestoreData = null, logicengine,2);
|
|
this.Name = "RAM";
|
|
this.removeProperty("Inputs");
|
|
this.AddressWidth = 16;
|
|
this.DataWidth = 8;
|
|
this._Data = new Array((2 ** this.AddressWidth));
|
|
|
|
for (let a = 0; a < this._Data.length; a++) {
|
|
this._Data[a] = (2 ** this.DataWidth)-1;
|
|
}
|
|
|
|
this.Inputs = new Array(this.AddressWidth + this.DataWidth + 3);
|
|
this.Outputs = new Array(this.DataWidth);
|
|
|
|
this.Height = this.Inputs.length * (this.inputCircleRadius + 14)+10;
|
|
this.Width = (12*(this.DataWidth/4)) + 220;
|
|
|
|
this.InputLabels = new Array(this.Inputs.length);
|
|
for (let a = 0; a < this.DataWidth; a++) {
|
|
this.InputLabels[a] = "I" + a;
|
|
this.OutputLabels[a] = "O" + a;
|
|
}
|
|
for (let a = 0; a < this.AddressWidth; a++) {
|
|
this.InputLabels[this.DataWidth+a] = "A" + a;
|
|
}
|
|
this.InputLabels[this.DataWidth+this.AddressWidth] = "WriteEnable";
|
|
this.InputLabels[this.DataWidth+this.AddressWidth+1] = "OutputEnable";
|
|
this.InputLabels[this.DataWidth+this.AddressWidth+2] = "CLK";
|
|
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
}
|
|
|
|
setInput(Input, Value) {
|
|
if (Value) {
|
|
Value = true;
|
|
} else {
|
|
Value = false;
|
|
}
|
|
|
|
let oldInput = this.Inputs[Input];
|
|
if (Input < this.totalInputs()) {
|
|
this.Inputs[Input] = Value;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
let isHigh = this._Container.isHigh(this,Input);
|
|
if (isHigh !== false) this.Inputs[Input] = true;
|
|
if (isHigh === false) this.Inputs[Input] = false;
|
|
if (oldInput != this.Inputs[Input]) {
|
|
|
|
if (this.Inputs[this.Inputs.length-2]) {
|
|
// Output Enable
|
|
this.setOutput();
|
|
} else {
|
|
this.setOutput(true);
|
|
}
|
|
|
|
if (this.Inputs[this.Inputs.length-3] && this.Inputs[this.Inputs.length-1] && (Input == this.Inputs.length-1)) {
|
|
this._Data[this.currentAddress()] = this.currentInputValue();
|
|
if (this.Inputs[this.Inputs.length-2]) {
|
|
// Output Enable
|
|
this.setOutput();
|
|
} else {
|
|
this.setOutput(true);
|
|
}
|
|
}
|
|
}
|
|
this.setConnections();
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
|
|
}
|
|
currentAddress() {
|
|
let addr = 0;
|
|
for (let a = 0; a < this.AddressWidth; a++) {
|
|
addr |= ((this.Inputs[this.DataWidth+a]) ? 1 : 0) << a;
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
currentInputValue() {
|
|
let val = 0;
|
|
for (let a = 0; a < this.DataWidth; a++) {
|
|
val |= ((this.Inputs[a]) ? 1 : 0) << a;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
currentValue() {
|
|
return this._Data[this.currentAddress()];
|
|
}
|
|
|
|
setOutput(clear = false) {
|
|
let currentValue = this.currentValue();
|
|
for (let a = 0; a < this.DataWidth; a++) {
|
|
this.Outputs[a] = false;
|
|
if (!clear && (currentValue & (0b1 << a))) this.Outputs[a] = true;
|
|
}
|
|
}
|
|
|
|
getOutput(output) {
|
|
if (this.Outputs[output]) return true;
|
|
return false;
|
|
}
|
|
|
|
drawElement(x, y, ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
if (!this._Data) return;
|
|
|
|
this.drawBorderBox(ctx,x+20,y,this.Width-40,this.Height);
|
|
this.drawTextCentered(ctx,x+20,(y+(this.Height-16)),this.Width-40,14,this.Designator,"12px Console","#000");
|
|
|
|
let totalAddr = Math.floor((this.Height - 40)/14);
|
|
let startAddr = this.currentAddress() - Math.floor(totalAddr/2);
|
|
if (startAddr < 0) startAddr = 0;
|
|
let endAddr = startAddr + totalAddr;
|
|
if (endAddr > this._Data.length) startAddr = this._Data.length - totalAddr;
|
|
if (startAddr < 0) startAddr = 0;
|
|
|
|
for (let a = 0; a < ((this._Data.length > totalAddr) ? totalAddr : this._Data.length) ; a++) {
|
|
let getAddr = a+startAddr;
|
|
if (this.currentAddress() == a + startAddr) this.drawBorderBox(ctx,x+60,(y+10+(a*14)),this.Width-100,14,0,undefined,"#ffffaa");
|
|
this.drawTextCentered(ctx,x+57,(y+10+(a*14)),(this.Width/2)-20,14,parseInt(a+startAddr).toString(16).toUpperCase().padStart(parseInt(this._Data.length-1).toString(16).length,'0') + `: `,"12px Console","#000");
|
|
this.drawText(ctx,x+(Math.floor(this.Width/2)+10),(y+22+Math.floor(a*14)), this._Data[(a+startAddr)].toString(16).toUpperCase().padStart(parseInt((2 ** this.DataWidth)-1).toString(16).length,'0'),"12px Console","#000");
|
|
}
|
|
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
let ElementCatalog_RAM = new ElementCatalog_Element("RAM","RAM, configurable address and data width",":::",RAMElement,[]);
|
|
ElementReferenceTable.push(ElementCatalog_RAM);
|
|
ElementCategory_Other.addElement(ElementCatalog_RAM);
|