Справица:DiagramGenerator/microcode.js
Пређи на навигацију
Пређи на претрагу
/* eslint {"max-statements": ["error", 40]} */
(function() {
'use strict';
var lib = mw.libs.diagramGenerator;
var signals = [
'MOST1_2',
'MOST1_3',
'MOST2_1',
'MOST3_2',
'rdCPU',
'wrCPU',
'ldMDR',
'mxMDR',
'MDRout1',
'eMDR',
'ldMAR',
'incMAR',
'eMAR',
'ldDWL',
'ldDWH',
'DWout2',
'wrGPR',
'GPRout1',
'ldGPRAR',
'incGPRAR',
'ldSP',
'SPout2',
'incSP',
'decSP',
'ldCW',
'CWout3',
'ADDout2',
'ldPC',
'incPC',
'PCHout3',
'PCLout3',
'PCout1',
'ldIR0',
'ldIR1',
'ldIR2',
'ldIR3',
'IRPOMout3',
'IRJAout2',
'IRDAout3',
'IRBRout3',
'add',
'sub',
'inc',
'dec',
'and',
'or',
'xor',
'not',
'ALUout1',
'ldAB',
'ABout3',
'shr',
'shl',
'ldBB',
'BBout2',
'ldAW',
'AWout3',
'AWHout3',
'ldBW',
'BWout2',
'ldPSWH',
'ldPSWL',
'ldN',
'ldZ',
'ldC',
'ldV',
'ldL',
'stPSWI',
'clPSWI',
'stPSWT',
'clPSWT',
'PSWHout3',
'PSWLout3',
'clSTART',
'ldIMR',
'IMRout2',
'ldBR',
'IVTDSPout3',
'ldIVTP',
'IVTPout1',
'UINTout3',
'UEXTout3',
'stPRCOD',
'stPRADR',
'stPRINS',
'clPRCOD',
'clPRADR',
'clPRINS',
'clPRINM',
'clINTR',
'stPSWR',
'clPSWR'
];
var BranchType = {
NONE: 'none',
CONDITIONAL: 'conditional',
UNCONDITIONAL: 'unconditional',
BRADR: 'bradr',
BROPR: 'bropr'
};
var CONDITIONAL_BRANCH = /^br\s*\(\s*if\s+(\S+)\s+then\s+(\S+)\s*\)$/;
var UNCONDITIONAL_BRANCH = /^br\s+(\S+)$/;
function prepareLine(line) {
return line
.toLowerCase()
.replace(/\(/g, ' ( ')
.replace(/\)/g, ' ) ')
.replace(/\s+/g, ' ')
.replace(/if !/g, 'if #')
.replace(/(.*?)!.*/g, '$1')
.replace(/(.*?);.*/, '$1')
.replace(/\t/g, ' ')
.replace(/\s+:/g, ': ')
.replace(/\s+/g, ' ')
.trim();
}
function parseAddress(line) {
if (line === '') {
return -1;
}
var addressLine = line.split(' ')[0];
if (addressLine.endsWith(':') || !addressLine.startsWith('madr')) {
return -1;
}
var address = parseInt(addressLine.substring(4), 16);
if (isNaN(address)) {
return -1;
}
return address;
}
function parseLabels(line) {
var labels = [];
var splitLine = line.split(' ');
for (var i = 0, l = splitLine.length; i < l; ++i) {
if (!splitLine[i].endsWith(':')) {
break;
}
var label = splitLine[i].slice(0, -1);
if (!labels.includes(label)) {
labels.push(label);
}
}
return labels;
}
function fixSignalNames(signalsToFix) {
return signalsToFix.map(function(signal) {
return signals.find(function(signal2) {
return signal.toLowerCase() === signal2.toLowerCase();
}) || signal;
});
}
function parseLine(comment, line, config) {
var preparedLine = prepareLine(line);
var instruction = {
address: parseAddress(preparedLine),
comment: comment,
errors: [],
line: line
};
var unparsedLine = preparedLine;
if (instruction.address >= 0) {
unparsedLine = unparsedLine.split(' ').slice(1).join(' ');
}
instruction.labels = parseLabels(unparsedLine);
instruction.signals = [];
instruction.branchType = BranchType.NONE;
unparsedLine = unparsedLine.split(' ').slice(instruction.labels.length).join(' ');
var hadBranch = false;
var conditionsLowercase = lib.parseConditions(config).map(function(condition) {
return condition.toLowerCase();
});
unparsedLine.split(',').map(function(segment) {
return segment.trim();
}).forEach(function(arg) {
var isBropr = arg === 'bropr';
var isBradr = arg === 'bradr';
var isAddressBranch = arg.includes('br ');
var isConditional = arg.includes('if ');
var isBranch = isBropr || isBradr || isAddressBranch;
var isSignal = signals.find(function(signal) {
return signal.toLowerCase() === arg;
});
// Check for multiple branches.
if (isBranch && hadBranch) {
instruction.errors.push(lib.msg('error-more-1-cond'));
} else if (isBranch) {
hadBranch = true;
}
// Set relevant instruction fields.
var res;
if (isSignal) {
if (!instruction.signals.includes(arg)) {
instruction.signals.push(arg);
}
} else if (isBropr) {
instruction.branchType = BranchType.BROPR;
} else if (isBradr) {
instruction.branchType = BranchType.BRADR;
} else if (isBranch && isConditional) {
res = CONDITIONAL_BRANCH.exec(arg);
if (!res) {
instruction.errors.push(lib.msg('error-invalid-cond-branch'));
return;
}
instruction.branchType = BranchType.CONDITIONAL;
var splitCondition = res[1].split('.');
var complement = res[1].startsWith('#') && splitCondition.length > 1 ? '#' : '';
instruction.condition = complement + splitCondition[splitCondition.length - 1];
if (!conditionsLowercase.includes(instruction.condition)) {
instruction.errors.push(lib.msg('error-unknown-condition', instruction.condition));
return;
}
instruction.destination = res[2];
// TODO: Destination validation
} else if (isBranch && !isConditional) {
res = UNCONDITIONAL_BRANCH.exec(arg);
if (!res) {
instruction.errors.push(lib.msg('error-invalid-uncond-branch'));
return;
}
instruction.branchType = BranchType.UNCONDITIONAL;
instruction.destination = res[1];
// TODO: Destination validation
} else {
instruction.errors.push(lib.msg('error-unknown-operation', arg));
}
});
instruction.signals = fixSignalNames(instruction.signals);
return instruction;
}
function getInstructions(config, contents) {
var instructions = [];
var comment = '';
var lines = contents.split('\n');
var labels = {};
var errors = [];
for (var i = 0, l = lines.length; i < l; ++i) {
var line = lines[i].trim();
if (!line) {
continue;
}
if (line.startsWith('!')) {
comment = line;
} else {
var instruction = parseLine(comment, line, config);
comment = '';
for (var j = 0, l2 = instruction.labels.length; j < l2; ++j) {
var label = instruction.labels[j];
if (labels[label]) {
instruction.errors.push(lib.msg('duplicate-label', label));
break;
}
labels[label] = instruction.address;
}
if (instruction.errors.length > 0) {
errors = errors.concat(instruction.errors.map(function(lineNum, error) {
return lib.msg('error-microcode', error, lineNum);
}.bind(null, i + 1)));
errors.push();
} else {
instructions.push(instruction);
}
}
}
return {
errors: errors,
result: instructions
};
}
window.mw = $.extend(true, window.mw, {
libs: {
diagramGenerator: {
BranchType: BranchType,
getInstructions: getInstructions
}
}
});
})();