Initial commit (a clone of the jsmachines project as of v1.15.3)
This commit is contained in:
commit
a5e3e6a59d
714 changed files with 130602 additions and 0 deletions
327
my_modules/ecpjs-client/lib/parallelAdder.js
Normal file
327
my_modules/ecpjs-client/lib/parallelAdder.js
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
* parallelAdder.js
|
||||
* by Jeff Parsons, May 11, 2012
|
||||
*/
|
||||
|
||||
/*
|
||||
* Creation of a ParallelAdder object is controlled by the following properties of the
|
||||
* parmsAdder object:
|
||||
*
|
||||
* idResident: ID of the resident register
|
||||
* idIncident: ID of the incident register
|
||||
* idCarries: ID of the carries register
|
||||
*
|
||||
* However, by the time we're called, initParallelAdders() has already looked up the
|
||||
* corresponding Register components by the above IDs (so that we don't have to be
|
||||
* involved in the "wiring" process) and passes them to us as:
|
||||
*
|
||||
* regResident
|
||||
* regIncident
|
||||
* regCarries
|
||||
*
|
||||
* Our constructor also creates an internal Register object (regScratch) that's used
|
||||
* to make a copy of the carry bits in the "carries" register after each internal add cycle.
|
||||
*/
|
||||
function ParallelAdder(parmsAdder, regResident, regIncident, regCarries)
|
||||
{
|
||||
Component.call(this, "ParallelAdder", parmsAdder);
|
||||
this.regResident = regResident;
|
||||
this.regIncident = regIncident;
|
||||
this.regCarries = regCarries;
|
||||
this.aBitsResident = regResident.getBits();
|
||||
this.aBitsIncident = regIncident.getBits();
|
||||
this.aBitsCarries = regCarries.getBits();
|
||||
this.regScratch = new Register({nBits:regCarries.count()});
|
||||
this.aBitsScratch = this.regScratch.getBits();
|
||||
}
|
||||
|
||||
Component.subclass(Component, ParallelAdder, {
|
||||
/*
|
||||
* These functions implement the necessary Register interfaces that other components,
|
||||
* like the Selector, expect us to support.
|
||||
*/
|
||||
count: function() {
|
||||
return this.regResident.count();
|
||||
},
|
||||
readBit: function(iBit) {
|
||||
return this.regResident.readBit(iBit);
|
||||
},
|
||||
writeBit: function(iBit, b) {
|
||||
this.regResident.writeBit(iBit, b);
|
||||
},
|
||||
/*
|
||||
* These are the functions unique to ParallelAdder.
|
||||
*/
|
||||
add: function(fnNotify) {
|
||||
this.stopSteps();
|
||||
this.cCarryCycles = 0;
|
||||
this.regResident.writeUndefined(false);
|
||||
this.regResident.updateAll();
|
||||
this.regIncident.writeUndefined(false);
|
||||
this.regIncident.updateAll();
|
||||
this.firstStep(this.stepAddRegisters, fnNotify);
|
||||
},
|
||||
stop: function() {
|
||||
this.stopSteps();
|
||||
this.regResident.stopSteps();
|
||||
this.regIncident.stopSteps();
|
||||
},
|
||||
stepAddRegisters: function(n) {
|
||||
this.cCarryCycles++;
|
||||
/*
|
||||
* We call the next step directly, to give this step something concrete to do;
|
||||
* we could have incorporated that code into this step, but it seems cleaner this way.
|
||||
*/
|
||||
this.stepClearCarries(n);
|
||||
if (this.cCarryCycles == 1)
|
||||
this.addStep(this.stepAddIncidentBits);
|
||||
else
|
||||
this.addStep(this.stepAddScratchBits);
|
||||
this.addStep(this.stepCheckCarries);
|
||||
return true;
|
||||
},
|
||||
stepClearCarries: function(n) {
|
||||
this.printStep(n, "Clearing carry bits");
|
||||
this.regCarries.writeAll(false);
|
||||
if (n !== undefined)
|
||||
this.regCarries.updateAll();
|
||||
return true;
|
||||
},
|
||||
stepCheckCarries: function(n) {
|
||||
if (!this.cCarries) {
|
||||
this.printStep(n, "No carries, addition complete");
|
||||
return false;
|
||||
}
|
||||
this.printStep(n, "Copying carry bits to scratch");
|
||||
this.regScratch.copyAll(this.regCarries);
|
||||
return true;
|
||||
},
|
||||
stepAddIncidentBits: function(n) {
|
||||
this.cCarries = 0;
|
||||
/*
|
||||
* The following table describes the 4 possible cases of resident(i) and incident(i) bits,
|
||||
* and what result(i) and carry(i+1) bits must be generated in each of those cases to simulate
|
||||
* the addition of a resident and incident bit:
|
||||
*
|
||||
* case resident(i) incident(i) result(i) carry(i+1)
|
||||
* ---- ----------- ----------- --------- ----------
|
||||
* 1 false "+" false "=" false false
|
||||
* 2 false "+" true "=" true false
|
||||
* 3 true "+" false "=" true false
|
||||
* 4 true "+" true "=" false true
|
||||
*
|
||||
* Prior to adding the incident bits, we cleared all the carry bits, so we only have to *set* carry bits,
|
||||
* never *clear* them. As for the result bits, they must be written back to the resident bits register,
|
||||
* so we must always write the resident result bit -- which you can tell from the table above is equivalent
|
||||
* to an XOR operation (ie, true if only ONE of either the resident or incident bits is true, false if BOTH
|
||||
* are false or true).
|
||||
*/
|
||||
this.printStep(n, "Adding incident bits");
|
||||
for (var i=0; i < this.aBitsIncident.length; i++) {
|
||||
bIncident = this.aBitsIncident[i];
|
||||
bResident = this.aBitsResident[i];
|
||||
if (bResident && bIncident) {
|
||||
if (i+1 < this.aBitsCarries.length) {
|
||||
this.aBitsCarries[i+1] = true;
|
||||
this.cCarries++;
|
||||
}
|
||||
}
|
||||
this.aBitsResident[i] = (bResident? !bIncident : bIncident); // the closest thing we have to a logical XOR operation
|
||||
}
|
||||
if (n !== undefined) {
|
||||
this.regResident.updateAll();
|
||||
this.regCarries.updateAll();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
stepAddScratchBits: function(n) {
|
||||
this.cCarries = 0;
|
||||
/*
|
||||
* We take advantage of the fact that on iteration i, all scratch bits below bit index i must be zero,
|
||||
* so we can start checking scratch bits (ie, the carry bits from the previous iteration) at bit index "cCarryCycles".
|
||||
*
|
||||
* NOTE: We should assert that all bits below that index are indeed zero, and that we never end up here when
|
||||
* cCarryCycles == aBitsScratch.length (because there shouldn't have been any saved carries from the length-1 iteration).
|
||||
*/
|
||||
this.printStep(n, "Adding previous carry bits");
|
||||
for (var i=this.cCarryCycles; i < this.aBitsScratch.length; i++) {
|
||||
bScratch = this.aBitsScratch[i];
|
||||
bResident = this.aBitsResident[i];
|
||||
if (bResident && bScratch) {
|
||||
if (i+1 < this.aBitsCarries.length) {
|
||||
this.aBitsCarries[i+1] = true;
|
||||
this.cCarries++;
|
||||
}
|
||||
}
|
||||
this.aBitsResident[i] = (bResident? !bScratch : bScratch); // the closest thing we have to a logical XOR operation
|
||||
}
|
||||
if (n !== undefined) {
|
||||
this.regResident.updateAll();
|
||||
this.regCarries.updateAll();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* initParallelAdders()
|
||||
*
|
||||
* Initializes all the necessary HTML to construct the component as spec'ed.
|
||||
*
|
||||
* Note that each element (e) of class "parallelAdder" is expected to have a "data-value"
|
||||
* attribute containing the same JSON-encoded parameters that the ParallelAdder constructor
|
||||
* expects; specifically:
|
||||
*
|
||||
* idResident: ID of register containing "resident" binary digits
|
||||
* idIncident: ID of register containing "incident" binary digits
|
||||
* idCarries: ID of register used for recording resulting carries
|
||||
*/
|
||||
function initParallelAdders()
|
||||
{
|
||||
var aeAdders = Component.getElementsByClass(window.document, "parallelAdder");
|
||||
for (var iAdder=0; iAdder < aeAdders.length; iAdder++) {
|
||||
var eAdder = aeAdders[iAdder];
|
||||
var parmsAdder = Component.getComponentParms(eAdder);
|
||||
//
|
||||
// Let's find all the prerequisite register components next....
|
||||
//
|
||||
var regResident = Component.getComponentByID(parmsAdder.idResident);
|
||||
var regIncident = Component.getComponentByID(parmsAdder.idIncident);
|
||||
var regCarries = Component.getComponentByID(parmsAdder.idCarries);
|
||||
|
||||
if (!regResident) regResident = new Register();
|
||||
if (!regIncident) regIncident = new Register();
|
||||
if (!regCarries) regCarries = new Register();
|
||||
|
||||
//
|
||||
// Now we can create the ParallelAdder object, record it, and wire it up to the associated document elements.
|
||||
//
|
||||
var adder = new ParallelAdder(parmsAdder, regResident, regIncident, regCarries);
|
||||
initParallelAdderControls(adder, eAdder);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* initParallelAdderControls(reg, eReg)
|
||||
*
|
||||
* For each Adder object created by initParallelAdders(), this function looks for any controls that have been defined
|
||||
* along with the adder element in the current document, and "wires" them as needed.
|
||||
*
|
||||
* The following controls are supported:
|
||||
*
|
||||
* One optional 'output' control of class "status"
|
||||
* One optional 'button' control of class "add"
|
||||
* One optional 'button' control of class "test"
|
||||
* One optional 'button' control of class "step" (if this exists, it will override any "step" setting above)
|
||||
*/
|
||||
function initParallelAdderControls(adder, eAdder)
|
||||
{
|
||||
adder.eAdder = eAdder;
|
||||
var aeControls = Component.getElementsByClass(eAdder.parentNode, "controls");
|
||||
for (var iControl = 0; iControl < aeControls.length; iControl++) {
|
||||
var aeChildren = aeControls[iControl].childNodes;
|
||||
for (var i=0; i < aeChildren.length; i++) {
|
||||
var e = aeChildren[i];
|
||||
if (e.nodeType != document.ELEMENT_NODE)
|
||||
continue;
|
||||
var sClass = e.getAttribute("class");
|
||||
if (e.nodeName == "BUTTON" && sClass == "add") {
|
||||
e.onclick = function() {
|
||||
adder.stop();
|
||||
addParallelValues(adder);
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if (e.nodeName == "BUTTON" && sClass == "test") {
|
||||
adder.eTest = e;
|
||||
e.onclick = function() {addStartParallelTest(adder);};
|
||||
continue;
|
||||
}
|
||||
if (e.nodeName == "BUTTON" && sClass == "step") {
|
||||
adder.setStep(e);
|
||||
continue;
|
||||
}
|
||||
if (e.nodeName == "DIV" && sClass == "status") {
|
||||
adder.setStatusUpdate(
|
||||
function(e) {
|
||||
return function(s) {
|
||||
e.innerHTML = (s? "<span>"+s+"</span>" : "");
|
||||
};
|
||||
}(e)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function called by the anonymous click handler for the "Add" button.
|
||||
*/
|
||||
function addParallelValues(adder)
|
||||
{
|
||||
adder.stop();
|
||||
var vResident = adder.regResident.getValue();
|
||||
// adder.printStatus("Resident register loaded: " + vResident);
|
||||
var vIncident = adder.regIncident.getValue();
|
||||
// adder.printStatus("Incident register loaded: " + vIncident);
|
||||
adder.add(function() {
|
||||
var s1 = "Success", s2 = "==";
|
||||
var vResult = adder.regResident.getValue();
|
||||
if (vResult != vResident + vIncident) {s1 = "Error"; s2 = "!=";}
|
||||
adder.printStatus(s1 + ": result (" + vResult + ") " + s2 + " resident (" + vResident + ") + incident (" + vIncident + ")");
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Function called by the anonymous click handler for the "Test" button.
|
||||
*/
|
||||
function addParallelTestValues(adder)
|
||||
{
|
||||
adder.cTests++;
|
||||
var vResident = Math.random() * 0.5;
|
||||
var vIncident = Math.random() * 0.5;
|
||||
adder.regResident.writeValue(vResident, function() {
|
||||
vResident = adder.regResident.getValue();
|
||||
// adder.printStatus("Resident register loaded: " + vResident);
|
||||
adder.regIncident.writeValue(vIncident, function() {
|
||||
vIncident = adder.regIncident.getValue();
|
||||
// adder.printStatus("Incident register loaded: " + vIncident);
|
||||
adder.add(function() {
|
||||
var vResult = adder.regResident.getValue();
|
||||
adder.cTotalCycles += adder.cCarryCycles - 1; // we don't want to count the initial addition, just the addition of carries
|
||||
adder.printStatus("Addition complete: " + vResult + " (" + adder.cCarryCycles + " carry cycles, " + (adder.cTotalCycles/adder.cTests) + " average)");
|
||||
if (vResult != vResident + vIncident)
|
||||
adder.printStatus("Error: result (" + vResult + ") != resident (" + vResident + ") + incident (" + vIncident + "): " + (vResident + vIncident));
|
||||
else
|
||||
if (adder.cTests < 1000) {
|
||||
addParallelTestValues(adder);
|
||||
return;
|
||||
}
|
||||
addStopParallelTest(adder);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addStartParallelTest(adder)
|
||||
{
|
||||
adder.stop();
|
||||
adder.eTest.innerHTML = "Stop";
|
||||
adder.eTest.onclick = function() {addStopParallelTest(adder);};
|
||||
adder.cTests = 0;
|
||||
adder.cTotalCycles = 0;
|
||||
addParallelTestValues(adder);
|
||||
}
|
||||
|
||||
function addStopParallelTest(adder)
|
||||
{
|
||||
adder.stop();
|
||||
adder.eTest.innerHTML = "Test";
|
||||
adder.eTest.onclick = function() {addStartParallelTest(adder);};
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize all the components on the page.
|
||||
*/
|
||||
web.onInit(initParallelAdders);
|
||||
Loading…
Reference in a new issue