//-------------------------------------------------------------------------------------------------- // // @ 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. // //-------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------- // // Static class used to format html listing // //-------------------------------------------------------------------------------------------------- package framework; import java.io.File; import java.math.BigInteger; import java.math.MathContext; import java.util.Date; import java.util.LinkedList; import java.util.Map; public class DVHtmlListing { protected String getListing(DVStatements statements) { String hexCodeHeader= statements.hexCodeHeader; int maxHexCodeLength= hexCodeHeader.length(); int lineOffset= 14+maxHexCodeLength; String lineOffsetSpaces= getSpaces(lineOffset); File input= new File(statements.parseInput.getFileName()); String path= input.getAbsolutePath(); String stmtOffsetSpaces= getSpaces(34+lineOffset); String msgOffsetSpaces= formatCode("\n" + getSpaces(stmtOffsetSpaces.length()-9)); String detailOffsetSpaces= getSpaces(34+lineOffset); String listing= DVHtmlListing.htmlHeader_1 + input.getName() + DVHtmlListing.htmlHeader_2 + formatCode("\n\n DVASM Listing for Input File: " + path +"\n" + " DVASM execution completed on: " + (new Date().toString()) + "\n"); // // Print log // listing+= "\n\n--- Log Start\n"; for (String msg : statements.logListFirstPhase) listing+= formatLog(msg); for (String msg : statements.logListSecondPhase) listing+= formatLog(msg); listing+= "--- Log End"; // // Initialize variable for macro expansion processing // int macroNest= 1; int macroNestNext= statements.statementList.get(0).lines[0].lineNo.split("\\.", 3).length; String macroName= null; LinkedList macroExpansionList= new LinkedList(); String expandAllList= "var expandAllList= ["; boolean macroToggleFlag= false; // // Initialize variable for statement listing loop // DVStatements.Statement lastStatement= statements.getLastStatement(); int stmtListSize= statements.statementList.size(); int stmtNo= 0; // // Start main statement listing loop // listing+= "
\n    Code Listing" +
                "\n    Stmt-Number- Macro Name------  Countr   " + hexCodeHeader +
                "   *-----Source----->
";
             
        boolean eolFlag= true;
        listing+= formatCode("    ");
        for (DVStatements.Statement s : statements.statementList) { 
        	String stmtName= "stmt_" + s.lines[0].lineNo.replace(".",  "_");
        	String stmtListing= "";
        	
        	if (s.warning || s.error)
        		for (DVStatements.Statement xs= s.lines[0].macroParent; xs != null; xs= xs.lines[0].macroParent)
        			if (! (xs.warning && xs.error))
        				macroExpansionList.add("stmt_" + xs.lines[0].lineNo.replace(".",  "_"));
        	
        	int macroNestPrev= macroNest;
        	macroNest= macroNestNext;
        	if (stmtNo+1 < stmtListSize)
        	    macroNestNext= statements.statementList.get(stmtNo+1).lines[0].lineNo.split("\\.").length;
        	else
                macroNestNext= 1;
        	
        	String prefix= "";
        	if (macroNestPrev > macroNest) {
        		for (int i= 0; i < macroNestPrev-macroNest; i++)
        			stmtListing+= "
"; stmtListing+= "
";
                
                if (macroNest == 1) {
                    macroName= null;
                    this.classPrefix= "";
                }
                
    			prefix= formatCode("    ");
    			eolFlag= true;
        	}
        	
        	if (macroNestPrev < macroNest) {
    	        stmtListing+= "
";
        this.classPrefix= "";
        
        listing+= "\n";
        
        //
        //	Add section list
        //
        listing+= "
\n    List of Sections" +
            "\n    Name------------  Type---  Align  Size----  External-Name---   ElfType---  ElfAttr-" +
        	"
";
        
        int n= 0;
        for (Map.Entry entry : statements.sections.sectionMap.entrySet()) {
        	DVSections.Section section= entry.getValue();
        	
        	String id= String.format("sec_%d", n);
        	
        	listing+= formatCode("    ") + formatShortLong(id, section.name.image,
        		"_tns", "_tnl", 16) + formatCode("  ") + 
        		formatShortLong(id, section.type.name(), "_tts", "_ttl", 7) +
        		formatCode("  " + DVUtil.formatHex(section.alignValue, 5) + "  " +
        		DVUtil.formatHex(BigInteger.valueOf(section.size), 8));
        	
        	if (section.type == DVSections.SectionType.MMAP) {
        		listing+= formatCode("\n");
        		continue;
        	}
        	
        	String type;
        	if (section.typeValue == null)
        		type= "UNRESOLVED";
        	else if (section.typeValue.equals(DVUtil.sectionTypeProgbits))
        		type= "PROGBITS  ";
        	else if (section.typeValue.equals(DVUtil.sectionTypeNote))
        		type= "NOTE      ";
        	else if (section.typeValue.equals(DVUtil.sectionTypeNobits))
    		type= "NOBITS    ";
        	else
        		type= DVUtil.formatHex(section.typeValue, 8) + "  ";
        	
        	String attr= "";
        	if (section.attributeValue == null)
        		attr= "UNRESOLVED";
        	else {
        		if (! section.attributeValue.and(DVUtil.sectionAttributeAlloc).equals(BigInteger.ZERO))
	        		attr+= "ALLOC+";
	        	if (! section.attributeValue.and(DVUtil.sectionAttributeExec).equals(BigInteger.ZERO))
	        		attr+= "EXECUTE+";
	        	if (! section.attributeValue.and(DVUtil.sectionAttributeWrite).equals(BigInteger.ZERO))
	        		attr+= "WRITE+";
	        	
	        	if (attr.endsWith("+"))
	        		attr= attr.substring(0, attr.length()-1);
	        	else
	        		attr= DVUtil.formatHex(section.attributeValue, 8);
        	}
        	
        	listing+= formatCode("  ") + formatShortLong(id,
        		(section.externalName == null) ? "" : section.externalName, "_txs", "_txl", 16) +
            	formatCode(" ") + formatCode("  " + type + "  " + attr + "\n");
        	
        	n++;
        }
        listing+= "\n";
        
        //
        //	Add symbol list
        //
        listing+=  "
\n    List of Symbols" +
        	"\n    Name--------------------  External-Name-----------  " +
            "ExpType----  XBind-----   Type------------------------  " +
        	"Align Len-- Repl-  Size--    Value-or-Relocation-AddEnd--->
";
        
        n= 0;
        for (Map.Entry entry : statements.symbols.symbolMap.entrySet()) {
           	DVSymbols.Symbol symbol= entry.getValue();
           	
        	if (symbol.name.startsWith("#"))
        		continue;
        	
        	String id= String.format("sym_%d", n);
       		
    		String line= "[+]" +
    			"";
        	line+= formatCode(" ") + formatShortLong(id, symbol.name, "_sns", "_snl", 24) +
        		formatCode("  ");
        	
        	String xname= "";
        	if (symbol.isExternal || symbol.isCommon)
        		xname= symbol.externalName;
        	else if (symbol.isExported)
        		xname= symbol.exportName;
        	line+= formatShortLong(id, (xname == null) ? "" : xname, "_xns", "_xnl", 24);
        	
    		String type= "          ";
    		if (symbol.isExported) {
    			if (symbol.exportType == null)
    				type= "UNRESOLVED";
    			else {
	    			if (symbol.exportType.equals(DVUtil.symbolTypeObject))
	    				type= "OBJECT    ";
	    			else if(symbol.exportType.equals(DVUtil.symbolTypeFunc))
	    				type= "FUNCTION  ";
	    			else if (symbol.exportType.equals(DVUtil.symbolTypeFile))
	    				type= "FILE      ";
	    			else
	    				type= DVUtil.formatHex(symbol.exportType, 2) + "        ";
    			}
    		}
    		line+= formatCode("  " + type);
    		
    		String bind= "          ";
    		if (symbol.isExported) {
    			if (symbol.exportBind == null)
    				bind= "UNRESOLVED";
    			else {
	    			if (symbol.exportBind.equals(DVUtil.symbolBindGlobal))
	    				bind= "GLOBAL    ";
	    			else if (symbol.isLocalExported)
	    				bind= "LOCAL     ";
	    			else
	    				bind= DVUtil.formatHex(symbol.exportBind, 2) + "        ";
    			}
    		}
    		else if (symbol.isExternal) {
    			if (symbol.externalBind == null)
    				bind= "UNRESOLVED";
    			else {
	    			if (symbol.externalBind.equals(DVUtil.symbolBindGlobal))
	    				bind= "GLOBAL    ";
	    			else if (symbol.externalBind.equals(DVUtil.symbolBindWeak))
	    				bind= "WEAK      ";
	    			else
	    				bind= DVUtil.formatHex(symbol.externalBind, 2) + "        ";
    			}
    		}
    		line+= formatCode("   " + bind);
    		
    		if (symbol.isCommon)
    			type= "COMMON";
    		else if(symbol.isExternal)
    			type= "EXTERNAL";
    		else if (symbol.owner == null)
    			type= "UDENFINED";
    		else 
    			type= symbol.resultValue == null ? "UNRESOLVED" : 
    				(symbol.resultValue.type != DVExpression.TokenType.OFFSET ? symbol.resultValue.type.name() :
    				"OFFSET [" + symbol.resultValue.getOffsetSection().name.image + "]");
    		
			line+= formatCode("   ") + formatShortLong(id, type, "_sts", "_stl", 28) +
				formatCode("  " + DVUtil.formatHex(symbol.align, 5)) +
				formatCode(" " + DVUtil.formatHex(symbol.length, 5) + " ") +
				formatShortLong(id, DVUtil.formatHex(symbol.resultRepl == null ? null : symbol.resultRepl.intConstant, 5),
					"_rns", "_rnl", 5);
			
			String size= "";
			if (symbol.isCommon)
				size= DVUtil.formatHex(symbol.commonSize, 6);
			else if (! symbol.isExternal)
				size= (symbol.resultRepl == null) ? "UNRESOLVED" :
					DVUtil.formatHex(symbol.resultRepl.intConstant.multiply(symbol.length), 6);
			line+= formatCode("  ") + formatShortLong(id, size, "_sss", "_ssl", 6);
			
			String value= "";
    		if (symbol.resultValue == null) {
    			if (symbol.isExternal || symbol.isCommon)
    				value= "";
    			else
    				value= "UNRESOLVED";
    		}
    		else if (symbol.resultValue.type == DVExpression.TokenType.STRING)
    			value= "\"" + DVUtil.escapeString(symbol.resultValue.stringConstant) + "\"";
    		else if (symbol.resultValue.type == DVExpression.TokenType.FLOAT) 
    			value= symbol.resultValue.floatConstant.round(DVHtmlListing.precision).toString();
    		else 
        		value= DVUtil.formatHex(symbol.resultValue.intConstant, 8);

    		line+= formatCode("    ") + formatShortLong(id, value, "_szs", "_szl", 30);
    		
    		//
    		//	Print symbol cross references
    		//
    		this.classPrefix= "x";
    		line+= "
";
    		this.classPrefix= "";
			
        	n++;
        	listing+= line;
        }
        listing+= "";
        
        //
        //	Add relocation
        //
        listing+= "
\n    List of Relocations" +
        	"\n    Section---------  Offset--  Symbol-Name-------------  " +
        	"Symbol-External-Name----  Relocation-ID-----  AddEnd----------
";
        
        n= 0;
        for (Map.Entry entry : statements.sections.sectionMap.entrySet()) {
        	DVSections.Section section= entry.getValue();
        	
        	if (section.type == DVSections.SectionType.MMAP)
        		continue;
        	
        	for (DVSections.Section.Relocation relocation : section.relocationList) {
        		String id= String.format("rel_%d", n);
        		
        		listing+= formatCode("    ") +
        			formatShortLong(id, section.name.image, "_rts", "_rtl", 16) +
        			formatCode("  " + DVUtil.formatHex(relocation.offset, 8) + "  " ) +
        			formatShortLong(id, relocation.symbol.name, "_rns", "_rnl", 24) + formatCode("  ") +
        			formatShortLong(id, (relocation.symbol.externalName == null) ? "UNDEFINED" :
        			relocation.symbol.externalName, "_rns", "_rnl", 24) + formatCode("  ") +
        			formatShortLong(id, relocation.relocationID.nameID, "_ris", "_ril", 18) + 
        			formatCode("  " + DVUtil.formatHex(relocation.addEnd, 16) + "\n");
        		n++;
        	}
        }
        
        listing+= "\n" + 
        	formatCode("\n    ----- End of DVASM listing -----\n\n" +
    		"    CopyRight Roberti & Parau Enterprises, Inc.  2021\n\n" +
        	"    This work is licensed under the Creative Commons Attribution-NoDerivatives 4.0 International License.\n" +
        	"    To view a copy of this license, visit ") +
        	"http://creativecommons.org/licenses/by-nd/4.0" +
        	formatCode("\n" +
        	"    or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.\n\n" +
			"    DVASM™ is a trademark of Roberti & Parau Enterprises, Inc.\n\n") +
        	DVHtmlListing.htmlTrailer_1 +
        	expandAllList.substring(0, expandAllList.length()-1) + "];\n" ; 
        
        //
        //	Add macro expansions for macro with warnnings or errors
        //
        for (String s : macroExpansionList)
        	listing+= "  toggleMacroExpand('" + s + "');\n";
        
        //
        //	Return the whole HTML listing file
        //
		return listing + DVHtmlListing.htmlTrailer_2;
	}
	
	//
	//	Macro enry for nested macro stack
	//
	class	MacroEntry {
		
		private		MacroEntry(String name, boolean warnErrFlag) {
			this.name= name;
			this.warnErrFlag= warnErrFlag;
		}
		
		private		String		name;
		private		boolean		warnErrFlag;
	}
	
	//
	//	Utility methods
	//
	private		static	String	formatLog(String logEntry) {
    	
		String logPrint= "";
    	
    	int x;
    	while((x= logEntry.indexOf('\n')) >= 0) {
    		if (logEntry.length() == 1)
    			return logPrint + "\n";
    		logPrint+= "    " + logEntry.substring(0, x+1);
    		logEntry= logEntry.substring(x+1);
    	}
    	
    	if (logPrint.toUpperCase().indexOf("WARNING") >= 0)
    		logPrint= "" + logPrint + "";
    	else if (logPrint.toUpperCase().indexOf("ERROR") >= 0)
    		logPrint= "" + logPrint + "";
    	return logPrint;
    }
    	
	private		static	String	getSpaces(int noSpace) {
		
		String pad= "";
		for (int i= 0; i < noSpace; i++)
			pad+= " ";
		return pad;
	}
	
	private				String	formatCode(String field) {
		return "" + 
	        escapeCode(field) + "";
	}
	
	private				String	formatShortLong(String stmtName, String field, String shortPostfix,
										String longPostfix, int maxLength) {
		
		if (field.length() > maxLength)
			return "" + escapeCode(field.substring(0, maxLength)) + "" + escapeCode(field) + "";

		return "" + escapeCode(field) +
	        getSpaces(maxLength-field.length()) + "";
		
	}
	
	private				String	formatShortLongRight(String stmtName, String field, String shortPostfix,
										String longPostfix, int maxLength) {
		
		if (field.length() > maxLength)
			return "" + escapeCode(field.substring(field.length()-maxLength)) + "" + escapeCode(field) + "";

		return "" + escapeCode(field) +
	        getSpaces(maxLength-field.length()) + "";
		
	}
	
    private    	String  escapeCode(String source) {
        return source.replaceAll("<", "<").replaceAll(">", ">");
    }
	
	//
	//	Class fields
	//
	private					String		classPrefix= "";
	
	private		static		MathContext	precision= new MathContext(42);
	
	private		static	final	String	htmlHeader_1=			
		"\n" + 
		" \n" +
		"  \n" +
		"   " + 
		"   ";
	private		static	final	String	htmlHeader_2=
		"\n" +
		"   \n" +
        "  \n" +
        
        "  \n" +
        
        "  \n" +
        "  \n" +
        "   
\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + "
" + "
";
	
	private		static	final	String	htmlTrailer_1=		
	    "  
\n" + " \n" + " \n" + "\n"; }