Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add read-only memory component #24

Merged
merged 1 commit into from Nov 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 31 additions & 1 deletion app/css/dialog.css
Expand Up @@ -63,6 +63,36 @@
color: red;
}

#dialog textarea {
font-family: Monospaced;
font-size: 15px;
background: rgba(255,255,255,0);
box-shadow: 0 2px 0 0 rgba(255,255,255,.1);
border: none;
outline: none;
margin: 8 8 0 8px;
padding: 2px;
color: #ddd;
}

#dialog textarea.error {
box-shadow: 0 2px 0 0 #500;
color: red;
}

#dialog select {
font-family: Monospaced;
font-size: 15px;
background: rgba(255,255,255,0);
box-shadow: 0 2px 0 0 rgba(255,255,255,.1);
border: none;
outline: none;
margin: 8 8 0 8px;
padding: 2px;
color: #ddd;
width: 20%;
}

#dialog .errormsg {
opacity: 0;
transition: opacity .5s;
Expand Down Expand Up @@ -168,4 +198,4 @@
#dialog .options button:active {
box-shadow: 0 0 0 0 #111;
top: 3px;
}
}
1 change: 1 addition & 0 deletions app/index.html
Expand Up @@ -247,6 +247,7 @@ <h1>Tutorial</h1>
<li onclick="select(Counter)">Counter</li>
<li onclick="select(TimerStart)">TimerStart</li>
<li onclick="select(TimerEnd)">TimerEnd</li>
<li onclick="select(ROM)">ROM</li>
</ul>
</div>
<div id="toolbartip"></div>
Expand Down
27 changes: 27 additions & 0 deletions app/js/components.js
Expand Up @@ -2943,6 +2943,33 @@ class Display extends Component {
// }
// }

class ROM extends Component {
constructor(name,pos,data=[]) {
super(name,pos,3,8,{ type: "char", text: "ROM" });

setTimeout(() => {
if(!this.properties.hasOwnProperty("data") ||
!this.properties.hasOwnProperty("addressWidth")) {
dialog.editRom(this);
}
}, 100);
}

function() {
let addr = 0;
for (let i = 0; i < this.input.length; i++) {
addr |= (this.input[i].value > 0) << i;
}
let rom = this.properties.rom;
if (rom) {
let content = this.properties.rom[addr];
for (let i = 0; i < this.output.length; i++) {
this.output[i].value = (content & (1 << i)) > 0 ? 1 : 0;
}
}
}
}

class Custom extends Component {
constructor(
name,
Expand Down
175 changes: 174 additions & 1 deletion app/js/editDialogs.js
Expand Up @@ -21,6 +21,52 @@
return input;
}

function createTextArea(
component,
property,
value,
valid,
errormsg,
apply) {
const input = document.createElement("textarea");
input.value = value;

input.valid = valid;
input.errormsg = errormsg;
input.apply = apply;

dialog.container.appendChild(document.createTextNode(property.slice(0,1).toUpperCase() + property.slice(1) + ":"));
dialog.container.appendChild(input);
dialog.container.appendChild(document.createElement("br"));
return input;
}

function createSelect(
component,
property,
value,
options,
apply) {
const input = document.createElement("select");
for (let i = 0; i < options.length; i++) {
const option = document.createElement("option");
option.value = options[i].value;
if (option.value === value) {
option.selected = true;
}
option.appendChild(document.createTextNode(options[i].text));
input.appendChild(option);
}
input.valid = () => true;
input.errormsg = "";
input.apply = apply;

dialog.container.appendChild(document.createTextNode(property.slice(0,1).toUpperCase() + property.slice(1) + ":"));
dialog.container.appendChild(input);
dialog.container.appendChild(document.createElement("br"));
return input;
}

dialog.editComponent = function(component) {
dialog.show();
dialog.name.innerHTML = "Edit component";
Expand Down Expand Up @@ -120,6 +166,31 @@
dialog.container.appendChild(document.createElement("br"));
}

if(component.properties.hasOwnProperty("data")) {
inputs.push(
createTextArea(
component.properties, "data", component.properties.data,
() => true,
"Enter hex-encoded data",
function() {
component.properties.data = this.value;
const dataWidth = component.properties.dataWidth;
const contents = this.value.replace(/\s/g, '').toUpperCase();
let data = Array(Math.pow(2, component.properties.addressWidth)).fill(0);
for (let i = 0; i < data.length; i++) {
const start = i * dataWidth / 4;
const end = start + dataWidth / 4;
const content = contents.slice(start, end);
data[i] = parseInt(content, 16);
}
component.properties.rom = data;
}
)
);
dialog.container.removeChild(dialog.container.children[dialog.container.children.length - 1]);
dialog.container.appendChild(document.createElement("br"));
}

const errormsg = document.createElement("p");
errormsg.className = "errormsg";
errormsg.innerHTML = ".";
Expand Down Expand Up @@ -271,4 +342,106 @@
}
});
}
})();
dialog.editRom = function(component,callback) {
if(!component) return;
dialog.show();
dialog.name.innerHTML = "Edit ROM";
dialog.container.innerHTML += "<i class='material-icons' style='font-size: 60px'>memory<i>";
dialog.container.innerHTML += `<p>Enter a hex-encoded data for component <i>${component.name}</i></p>`;


const addressWidthInput = createInput(
component.properties, "addressWidth", component.properties.addressWidth || "4",
addressWidth => !isNaN(parseVariableInput(addressWidth)),
"Address width in bits",
function() {
component.properties.addressWidth = parseVariableInput(this.value);
component.height =
Math.max(
component.properties.addressWidth,
component.properties.dataWidth);
component.input = [];
for(let i = 0; i < component.properties.addressWidth; ++i) {
component.addInputPort({ side: 3, pos: i });
}
createVariableReference(this.value,component,["properties","addressWidth"]);
}
);
const dataWidthInput = createSelect(
component.properties, "dataWidth", component.properties.dataWidth || 4,
[{"value": 4, "text": "4"},
{"value": 8, "text": "8"},
{"value": 16, "text": "16"},
{"value": 32, "text": "32"}],
function() {
component.properties.dataWidth = +this.value;
component.height =
Math.max(
component.properties.addressWidth,
component.properties.dataWidth);
component.output = [];
for(let i = 0; i < component.properties.dataWidth; ++i) {
component.addOutputPort({ side: 1, pos: i });
}
}
);
const dataInput = createTextArea(
component.properties, "data", component.properties.data || "",
// TODO better validation?
() => true,
"Enter hex-encoded data",
function() {
// Keep original data
component.properties.data = this.value;
// Sanatize and store parsed data as an array of numbers
const contents = this.value.replace(/\s/g, '').toUpperCase();
const dataWidth = component.properties.dataWidth;
let data = Array(Math.pow(2, component.properties.addressWidth)).fill(0);
for (let i = 0; i < data.length; i++) {
const start = i * dataWidth / 4;
const end = start + dataWidth / 4;
const content = contents.slice(start, end);
data[i] = parseInt(content, 16);
}
component.properties.rom = data;
createVariableReference(this.value,component,["properties","rom"]);
}
);
setTimeout(() => addressWidthInput.focus(),10);
dialog.container.removeChild(dialog.container.children[dialog.container.children.length - 1]);

const errormsg = document.createElement("p");
errormsg.className = "errormsg";
errormsg.innerHTML = ".";
errormsg.hide = null;
errormsg.show = function(text) {
clearTimeout(this.hide);
this.innerHTML = text;
this.style.opacity = 1;
this.hide = setTimeout(() => this.style.opacity = 0, 2500);
}
dialog.container.appendChild(errormsg);

dialog.addOption("Cancel", function() {
if(!component.properties.addressWidth && !component.properties.data) {
component.properties.addressWidth = 0;
component.properties.data = "";
callback && callback();
}
});
dialog.addOption("OK", function() {
if(addressWidthInput.valid(addressWidthInput.value) &&
dataInput.valid(dataInput.value)) {
addressWidthInput.apply();
dataWidthInput.apply();
dataInput.apply();
callback && callback();
} else {
input.className = "error";
errormsg.show(addressWidthInput.errormsg);
errormsg.show(dataInput.errormsg);
this.onmouseup = () => this.onmouseup = dialog.hide;
}
});
}
})();
5 changes: 3 additions & 2 deletions app/js/localStorage2.js
Expand Up @@ -97,7 +97,8 @@ const constructors = {
Input,Output,NOT,AND,OR,XOR,
Button,Constant,Delay,Clock,Debug,
Beep,Counter,LED,Display,
Custom, TimerStart, TimerEnd
Custom, TimerStart, TimerEnd,
ROM
};

/*
Expand Down Expand Up @@ -465,4 +466,4 @@ function readFile(input) {

reader.readAsText(input.files[0]);
dialog.hide();
}
}