Справица:DiagramGenerator/actions.js
Пређи на навигацију
Пређи на претрагу
/* eslint {"max-statements": ["error", 50]} */
(function() {
'use strict';
var ALU_SIGNALS = ['add', 'sub', 'inc', 'dec', 'and', 'or', 'xor', 'not', 'shr', 'shl'];
var MAX_ITER_DEPENDENT_SIGNALS = 10;
function translateRegName(regName) {
var regPrefix = regName.slice(0, -1);
var regSuffix = regName.slice(-1);
if (regSuffix === 'H') {
return regPrefix + '15..8';
}
if (regName.endsWith('L')) {
return regPrefix + '7..0';
}
if (regName.endsWith('0')) {
return regPrefix + '31..24';
}
if (regName.endsWith('1')) {
return regPrefix + '23..16';
}
if (regName.endsWith('2')) {
return regPrefix + '15..8';
}
if (regName.endsWith('3')) {
return regPrefix + '7..0';
}
if (regName === 'GPR') {
return 'GPR[AR]';
}
if (regName === 'IVTDSP') {
return 'BR << 1';
}
return regName;
}
function classifySignals(signals) {
var classification = {
bridge: [],
cl: [],
dec: [],
errors: [],
inc: [],
ld: [],
ldPSW: [],
map: {},
out: [],
st: []
};
signals.forEach(function(signal) {
classification.map[signal] = true;
var regName;
if (ALU_SIGNALS.includes(signal)) {
if (classification.alu) {
classification.errors.push(mw.libs.diagramGenerator.msg('error-2-alu', signal, classification.alu));
} else {
classification.alu = signal;
}
}
if (signal.includes('out')) {
var splitOutSignal = signal.split('out');
regName = translateRegName(splitOutSignal[0]);
if (regName === 'ALU' || regName === 'ADD') {
// Dependent output signals
return;
}
var busNum = Number(splitOutSignal[1]);
if (!isNaN(busNum)) {
classification.out.push([regName, busNum]);
}
}
if (signal.startsWith('ld') || signal === 'wrGPR') {
regName = signal.substring(2);
if (regName.length === 1) {
classification.ldPSW.push(regName);
} else {
classification.ld.push(translateRegName(regName));
}
}
if (signal.startsWith('MOST')) {
var bridgeData = signal.substring(4).split('_');
var srcBridge = Number(bridgeData[0]);
var dstBridge = Number(bridgeData[1]);
classification.bridge.push([srcBridge, dstBridge]);
}
if (signal.startsWith('inc') && signal !== 'inc') {
classification.inc.push(translateRegName(signal.substring(3)));
}
if (signal.startsWith('dec') && signal !== 'dec') {
classification.dec.push(translateRegName(signal.substring(3)));
}
if (signal.startsWith('st')) {
classification.st.push(translateRegName(signal.substring(2)));
}
if (signal.startsWith('cl')) {
classification.cl.push(translateRegName(signal.substring(2)));
}
});
return classification;
}
function calculateMemory(clsSignals, buses) {
var rd = clsSignals.map.rdCPU;
var wr = clsSignals.map.wrCPU;
if (rd && wr) {
clsSignals.errors.push(mw.libs.diagramGenerator.msg('error-rd-wr'));
return;
}
if (rd) {
if (clsSignals.map.eMDR) {
clsSignals.errors.push(mw.libs.diagramGenerator.msg('error-dbus-conflict'));
return;
}
if (!clsSignals.map.eMAR) {
clsSignals.errors.push(mw.libs.diagramGenerator.msg('error-rd-no-mar'));
return;
}
buses.mdrI = 'MEM[MAR]';
if (!clsSignals.ld.includes('MDR')) {
clsSignals.ld.push('MDR');
}
} else if (wr) {
if (!clsSignals.map.eMDR) {
clsSignals.errors.push(mw.libs.diagramGenerator.msg('error-wr-no-mdr'));
return;
}
if (!clsSignals.map.eMAR) {
clsSignals.errors.push(mw.libs.diagramGenerator.msg('error-wr-no-mar'));
return;
}
buses.mdrI = 'MDR';
return 'MEM[MAR] <= MDR';
}
}
function calculateIndependentBusOutput(clsSignals, buses) {
clsSignals.out.forEach(function(outData) {
var regName = outData[0];
var busNum = outData[1];
var busName = 'iBus' + busNum;
var hadValue = buses[busName] !== 'HiZ' && buses[busName] !== regName;
if (hadValue) {
var errMsg = mw.libs.diagramGenerator.msg('error-conflict', busName, buses[busName], regName);
clsSignals.errors.push(errMsg);
}
buses[busName] = hadValue ? 'CONFLICT' : regName;
});
}
function isConstantOperation(A, B) {
return !isNaN(Number(A)) && !isNaN(Number(B));
}
function evaluateConstantOperation(operation, A, B) {
var a = Number(A);
var b = Number(B);
switch (operation) {
case 'add':
return a + b;
case 'sub':
return a - b;
case 'inc':
return a + 1;
case 'dec':
return a - 1;
case 'and':
return a & b;
case 'or':
return a | b;
case 'xor':
return a ^ b;
case 'not':
return ~a;
case 'shr':
return a >> b;
case 'shl':
return a << b;
default:
return 0;
}
}
function getAluOutput(buses, signal) {
// ALU inputs on HiZ are treated as 0.
var A = buses.iBus3 === 'HiZ' ? '0' : buses.iBus3;
var B = buses.iBus2 === 'HiZ' ? '0' : buses.iBus2;
if (isConstantOperation(A, B)) {
return String(evaluateConstantOperation(signal, A, B));
}
switch (signal) {
case 'add':
return A + ' + ' + B;
case 'sub':
return A + ' - ' + B;
case 'inc':
return A + ' + 1';
case 'dec':
return A + ' - 1';
case 'and':
return A + ' & ' + B;
case 'or':
return A + ' | ' + B;
case 'xor':
return A + ' ^ ' + B;
case 'not':
return '~' + A;
case 'shr':
return A + ' >> ' + B;
case 'shl':
return A + ' << ' + B;
default:
return '0';
}
}
function writeBus(buses, busName, value) {
if (buses[busName] === value) {
return false;
}
buses[busName] = value;
return true;
}
function getValueToLoad(buses, regName) {
switch (regName) {
case 'MDR':
return buses.mdrI;
case 'DW15..8':
case 'DW7..0':
case 'GPRAR':
case 'IR31..24':
case 'IR23..16':
case 'IR15..8':
case 'IR7..0':
case 'AB':
case 'BB':
case 'PSW15..8':
case 'PSW7..0':
return buses.iBus1;
case 'MAR':
case 'CW':
case 'PC':
case 'AW':
case 'BW':
return buses.iBus2;
case 'GPR[AR]':
case 'SP':
case 'IMR':
case 'BR':
case 'IVTP':
return buses.iBus3;
default:
return 'HiZ';
}
}
function calculateDependentBusOutput(clsSignals, buses) {
var diffFound = 1;
for (var iter = 0; iter < MAX_ITER_DEPENDENT_SIGNALS && diffFound; ++iter) {
diffFound = 0;
diffFound |= writeBus(buses, 'aluOut', getAluOutput(buses, clsSignals.alu));
if (clsSignals.map.ALUout1) {
diffFound |= writeBus(buses, 'iBus1', buses.aluOut);
}
if (clsSignals.map.ADDout2) {
// If any adder input is HiZ, the output is zero.
if (buses.iBus1 === 'HiZ' || buses.iBus3 === 'HiZ') {
diffFound |= writeBus(buses, 'iBus2', '0');
} else if (isConstantOperation(buses.iBus1, buses.iBus3)) {
var result = evaluateConstantOperation('add', buses.iBus1, buses.iBus3);
diffFound |= writeBus(buses, 'iBus2', String(result));
} else {
diffFound |= writeBus(buses, 'iBus2', buses.iBus1 + ' + ' + buses.iBus3);
}
}
diffFound |= clsSignals.bridge.map(function(bridge) {
var busValue = buses['iBus' + bridge[0]];
// Tri-state buffers output -1 if their input is HiZ.
return writeBus(buses, 'iBus' + bridge[1], busValue === 'HiZ' ? '-1' : busValue);
}).reduce(function(a, b) {
return a + b;
}, 0);
}
if (diffFound) {
clsSignals.errors.push(mw.libs.diagramGenerator.msg('error-infinite-loop'));
}
if (clsSignals.map.mxMDR) {
// Multiplexer outputs zero if the selected input is HiZ.
buses.mdrI = buses.iBus3 === 'HiZ' ? '0' : buses.iBus3;
}
}
function executeActions(clsSignals, buses) {
var ldActions = clsSignals.ld.map(function(regName) {
return regName + ' <= ' + getValueToLoad(buses, regName);
});
var ldPSWActions = clsSignals.ldPSW.map(function(bitName) {
return 'PSW' + bitName + ' <= ' + bitName;
});
var incActions = clsSignals.inc.map(function(regName) {
return regName + '++';
});
var decActions = clsSignals.dec.map(function(regName) {
return regName + '--';
});
var stActions = clsSignals.st.map(function(regName) {
return regName + ' <= 1';
});
var clActions = clsSignals.cl.map(function(regName) {
return regName + ' <= 0';
});
return ldActions
.concat(ldPSWActions)
.concat(incActions)
.concat(decActions)
.concat(stActions)
.concat(clActions);
}
function main(signals) {
var actions = [];
var clsSignals = classifySignals(signals);
// Initial bus state
var buses = {
aluOut: '0',
iBus1: 'HiZ',
iBus2: 'HiZ',
iBus3: 'HiZ',
mdrI: 'HiZ'
};
// Calculate memory-related values
var memoryAction = calculateMemory(clsSignals, buses);
if (memoryAction) {
actions.push(memoryAction);
}
// Calculate independent register bus output values
calculateIndependentBusOutput(clsSignals, buses);
// Calculate bus output values that depend on each other
calculateDependentBusOutput(clsSignals, buses);
// All signals are calculated, decide which actions these signals result in
actions = actions.concat(executeActions(clsSignals, buses));
// All done, check and report errors
if (actions.some(function(action) {
return action.includes('HiZ');
})) {
clsSignals.errors.push();
}
return {
errors: clsSignals.errors,
result: actions
};
}
window.mw = $.extend(true, window.mw, {
libs: {
diagramGenerator: {
signalsToActions: main
}
}
});
})();