//MACRO __CondGen Condition, AndSequence{boolean}, CloseLabel, FarLabel, Far{boolean}, WReg= !"" //-------------------------------------------------------------------------------------------------- // // @ CopyRight Roberti & Parau Enterprises, Inc. 2021-2023 // // This work is licensed under the Creative Commons Attribution-NoDerivatives 4.0 International License. // To view a copy of this license, visit http://creativecommons.org/licenses/by-nd/4.0/ // or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. // //-------------------------------------------------------------------------------------------------- // // // Class for branch condition and following boolean operator if any // class BranchElement { constructor(label, r1, branch, r2, branchLabel) { this.label= label; this.r1= r1; this.branch= branch; this.r2= r2; this.branchLabel= branchLabel; } } // // Function to generate branch opCodes base on boolean condition // function getBranchOpCode(condition, negateFlag) { condition= condition.toUpperCase(); const condList= [ "==", "!=", "<", ">", "<=", ">=", "{U}<", "{U}>", "{U}<=", "{U}>=" ]; const opCodes= [ "BEQ", "BNE", "BLT", "BGT", "BLE", "BGE", "BLTU", "BGTU", "BLEU", "BGEU" ]; negatedOpCodes= [ "BNE", "BEQ", "BGE", "BLE", "BGT", "BLT", "BGEU", "BLEU", "BGTU", "BLTU" ]; if (condList.includes(condition)) return negateFlag ? negatedOpCodes[condList.indexOf(condition)] : opCodes[condList.indexOf(condition)]; const fCondList= [ "{S}==", "{S}!=", "{S}<", "{S}>", "{S}<=", "{S}>=", "{D}==", "{D}!=", "{D}<", "{D}>", "{D}<=", "{D}>=", "{Q}==", "{Q}!=", "{Q}<", "{Q}>", "{Q}<=", "{Q}>=" ]; const fOpCode= [ "FEQ.S", "FEQ.S", "FLT.S", "FLE.S", "FLE.S", "FLT.S", "FEQ.D", "FEQ.D", "FLT.D", "FLE.D", "FLE.D", "FLT.D", "FEQ.Q", "FEQ.Q", "FLT.Q", "FLE.Q", "FLE.Q", "FLT.Q" ]; const fBranch= [ "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ" ]; const fNegBranch= [ "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE", "BEQ", "BNE" ]; return [ fOpCode[fCondList.indexOf(condition)], negateFlag ? fNegBranch[fCondList.indexOf(condition)] : fBranch[fCondList.indexOf(condition)] ]; } // // Negate opCode for FAR branching // function negateOpCode(opCode) { return [ "BNE", "BEQ", "BGE", "BLE", "BGT", "BLT", "BGEU", "BLEU", "BGTU", "BLTU" ] [[ "BEQ", "BNE", "BLT", "BGT", "BLE", "BGE", "BLTU", "BGTU", "BLEU", "BGEU" ].indexOf(opCode)]; } // // Function to list the execList for debugging // function printExecList(execList, indent) { indent+= " -" for (var i= 0; i < execList.length; i++) { if (Array.isArray(execList[i])) { printExecList(execList[i], indent); continue; } if (typeof(execList[i]) == "object") print(indent + "Branch element: " + (execList[i].label == null ? "**null**" : execList[i].label) + " " + execList[i].r1 + " " + execList[i].branch + " " +execList[i].r2 + " " + (execList[i].branchLabel == null ? "**null**" : execList[i].branchLabel)); else print(indent + "Boolean : " + execList[i]); } } // // Generate code // function generateCode(execList, andSequence, andBranchElement, orBranchElement) { for (var i= execList.length-1; i >= 0; i-= 2) { if (Array.isArray(execList[i])) { if (i < execList.length-1 && andSequence) generateCode(execList[i], true, andBranchElement, execList[i+2]); else generateCode(execList[i], andSequence, andBranchElement, orBranchElement); } else { if (andSequence) { if (andBranchElement.label == null) andBranchElement.label= DVASM.getNewLabel(); execList[i].branchLabel= andBranchElement.label; execList[i].branch= getBranchOpCode(execList[i].branch, true); } else { if (orBranchElement.label == null) orBranchElement.label= DVASM.getNewLabel(); execList[i].branchLabel= orBranchElement.label; execList[i].branch= getBranchOpCode(execList[i].branch, false); } } if (! andSequence && i > 0 && execList[i-1] == "AND") { andSequence= true; if (i+2 < execList.length) andBranchElement= execList[i+2]; } else if (andSequence && i > 0 && execList[i-1] == "OR") andSequence= false; } } // // Punch code // function punchCode(execList) { for (var i= 0; i < execList.length; i+= 2) { if (Array.isArray(execList[i])) punchCode(execList[i]); else { var el= execList[i]; if (typeof(el.branch) == "string") { if (el.branchLabel == farBranchLabel) { var newLabel= DVASM.getNewLabel(); DVASM.formatLine((el.label == null ? "" : el.label), negateOpCode(el.branch), el.r1 + ", " + el.r2 + ", " + newLabel, ""); DVASM.formatLine("", "JAL", el.branchLabel, ""); DVASM.formatLine(newLabel, "BYTE", "0[0]", ""); } else DVASM.formatLine((el.label == null ? "" : el.label), el.branch, el.r1 + ", " + el.r2 + ", " + el.branchLabel, ""); } else { DVASM.putInfo("WReg: [" + (WReg === null ? "null" : WReg) + "]"); if (WReg === "" || WReg === null) { DVASM.putError("Floating point comparison requested but working register WReg parameter not specified"); return; } DVASM.formatLine((el.label == null ? "" : el.label), el.branch[0], el.r1 + ", " + el.r2 + ", " + WReg, ""); if (el.branchLabel == farBranchLabel) { var newLabel= DVASM.getNewLabel(); DVASM.formatLine("", negateOpCode(el.branch[1]), WReg + ", 0, " + newLabel, ""); DVASM.formatLine("", "JAL", el.branchLabel, ""); DVASM.formatLine(newLabel, "BYTE", "0[0]", ""); } else DVASM.formatLine("", el.branch[1], WReg + ", 0, " +el.branchLabel, ""); } } } } // // Function to issue an error message // function putCondError(error, branch, errPos) { return DVASM.putError("Parsing error of Condition String at position [" + errPos + "]\n" + "Error: " + error + "\nCondition String: \"" + branch + "\"\n".padEnd(20+errPos) + "^"); } // // Use or reuse regEx // We use the Pattern and Matcher java classes // Access is provided by the assembler framework engine // var compRegExp= DVASM.getJSGlobal("__arch_condCompRegExp"); if (compRegExp == null) { compRegExp= Pattern.compile( "(?:([\\s\\t]*)" + "(?:" + "(?\\()|" + "(?\\))|" + "(?:" + "(?(?:[A-Z_]\\w*(?:[.]\\w+)*)|(?:\\d+))(?:[\\s\\t])*" + "(?(?:" + "(?:==)|(?:!=)|(?:<)|(?:>)|(?:<=)|(?:>=)|" + "(?:\\{U\\}<)|(?:\\{U\\}>)|(?:\\{U\\}<=)|(?:\\{U\\}>=)|" + "(?:\\{S\\}==)|(?:\\{S\\}!=)|(?:\\{S\\}<)|(?:\\{S\\}>)|(?:\\{S\\}<=)|(?:\\{S\\}>=)|" + "(?:\\{D\\}==)|(?:\\{D\\}!=)|(?:\\{D\\}<)|(?:\\{D\\}>)|(?:\\{D\\}<=)|(?:\\{D\\}>=)|" + "(?:\\{Q\\}==)|(?:\\{Q\\}!=)|(?:\\{Q\\}<)|(?:\\{Q\\}>)|(?:\\{Q\\}<=)|(?:\\{Q\\}>=)" + "))(?:[\\s\\t])*" + "(?(?:[A-Z_]\\w*(?:[.]\\w+)*)|(?:\\d+))" + ")|" + "(?\\&\\&)|" + "(?\\|\\|)|" + "(?$)" + ")(?:[\\s\\t]*)" + ")", Pattern.CASE_INSENSITIVE); DVASM.putJSGlobal("__arch_condCompRegExp", compRegExp); } // // Parse condition and put code in an array // if (Condition.substring(0, 1) === "(" && Condition.substring(Condition.length-1) === ")") Condition= Condition.substring(1, Condition.length-1).trim(); var matcher= compRegExp.matcher(Condition); var listStack= []; var currList= []; var state= "BL"; while(true) { if (! matcher.lookingAt()) return putCondError("Invalid token found - Expecting [ OPEN PARENTHESIS | R1-BRANCH_CONDITION-R2 ]", Condition, matcher.regionStart()+1); switch(state) { case "BL" : if (matcher.group("lp") != null) { listStack.push(currList); currList= []; } else if (matcher.group("r1") != null) { currList.push(new BranchElement(null, matcher.group("r1"), matcher.group("bc"), matcher.group("r2"), null)); state= "BC"; } else return putCondError("Unexpected token [" + matcher.group() + " ] - Expecting [ OPEN PARENTHESIS | R1-BRANCH_CONDITION-R2 ]", Condition, matcher.start()); break; case "BC" : if (matcher.group("rp") != null) { if (listStack.length == 0) return putCondError("Un-matched RIGHT_PARENTHESIS", Condition, matcher.start()); listStack[listStack.length-1].push(currList); if (listStack.length <= 0) return DVASM.putError("Missing RIGHT_PARENTHESIS at end of positional parameter [2]"); currList= listStack.pop(); } else if(matcher.group("and") != null) { currList.push("AND"); state= "BL"; } else if (matcher.group("or") != null) { currList.push("OR"); state= "BL"; } else return putCondError("Unexpected token [" + matcher.group() + "] - Expecting [ AND | OR" + (listStack.length > 0 ? " | RIGHT_PARENTHESIS ]" : " ]"), Condition, errIndex); break; default : print("** Software error in macro: [" + match + "]"); return DVASM.putError("** Software error in macro: [" + match + "]"); } if (matcher.hitEnd() || matcher.end() == Condition.length) break; matcher= matcher.region(matcher.end(), matcher.regionEnd()); } if (listStack.length > 0) return putConditionError("Missing right parenthesis at end of condition string", Condition, Condition.length); //print("Condition: [" + Condition + "]"); //printExecList(currList, ">>"); // // Generate code // generateCode(currList, AndSequence, new BranchElement(FarLabel), new BranchElement(CloseLabel)); currList[0].label= Label; //print("\n-------------------------------"); //printExecList(currList, "<<"); var farBranchLabel= null; if (Far) farBranchLabel= AndSequence ? FarLabel : CloseLabel; var wrFlag= false; punchCode(currList); if (wrFlag) return DVASM.putError("Floating point comparison requires a work register"); DVASM.formatLine((AndSequence ? CloseLabel : FarLabel), "BYTE", "0[0]", "");