/// <summary> /// Generates the listing. /// Listing format looks something like this: /// file.asm/1 (0x1234): DE AD BE EF ld a, 0xBEEF /// file.asm/2 (0x1236): label: /// file.asm/3 (0x1236): #directive /// </summary> /// <returns>The listing.</returns> /// <param name="output">Output.</param> public static string GenerateListing(AssemblyOutput output) { // I know this can be optimized, I might optmize it eventually int maxLineNumber = output.Listing.Max(l => l.CodeType == CodeType.Directive ? 0 : l.LineNumber).ToString().Length; int maxFileLength = output.Listing.Max(l => l.FileName.Length); int maxBinaryLength = output.Listing.Max(l => { if (l.Output == null || l.Output.Length == 0) { return(0); } return(l.Output.Length * 3 - 1); }); maxBinaryLength = 8; int addressLength = output.InstructionSet.WordSize / 4 + 2; string formatString = "{0,-" + maxFileLength + "}:{1,-" + maxLineNumber + "} ({2}): {3,-" + maxBinaryLength + "} {4}" + Environment.NewLine; string errorFormatString = "{0,-" + maxFileLength + "}:{1,-" + maxLineNumber + "} {2}: {3}" + Environment.NewLine; string addressFormatString = "X" + addressLength; var builder = new StringBuilder(); string file, address, binary, code; int line; foreach (var entry in output.Listing) { file = entry.FileName; line = entry.LineNumber; address = "0x" + entry.Address.ToString(addressFormatString); code = entry.Code; if (entry.Output != null && entry.Output.Length != 0 && entry.CodeType != CodeType.Directive) { binary = string.Empty; for (int i = 0; i < entry.Output.Length; i++) { binary += entry.Output[i].ToString("X2") + " "; } binary = binary.Remove(binary.Length - 1); code = " " + code; } else { binary = string.Empty; } if (entry.Error != AssemblyError.None) { builder.AppendFormat(errorFormatString, file, line, "Error", entry.Error); } if (entry.Warning != AssemblyWarning.None) { builder.AppendFormat(errorFormatString, file, line, "Warning", entry.Warning); } builder.AppendFormat(formatString, file, line, address, binary, code); } return(builder.ToString()); }
private static byte[] fillMegaROM(int pages, Assembler assembler, AssemblyOutput output) { int size = pages * 8 * 1024; byte[] rom = setROMHeader(size, assembler.ROMStart); int srcAdress = 0; uint subpage = 0; uint SubpageORG = 0; foreach (Assembler.ORGItem item in assembler.ORGsList) { if (settings.Verbose == VerboseLevels.Diagnostic) { Console.WriteLine("ORG: 0x{0:X}-0x{1:X} Subpage:{2}", item.ORGAdress, item.ORGLength, item.Subpage); } uint orgIni; if (item.Subpage != subpage) { subpage = item.Subpage; SubpageORG = item.ORGAdress; } if (srcAdress == 0) { orgIni = 16; subpage = item.Subpage; SubpageORG = item.ORGAdress; } else { orgIni = (item.ORGAdress - SubpageORG) + item.Subpage * 0x2000; } if (settings.Verbose == VerboseLevels.Diagnostic) { Console.WriteLine("Megarom ORG: 0x{0:X}-0x{1:X} Subpage:{2} (Current Subpage:{3} SubpageORG:{4:x})", orgIni, item.ORGLength - item.ORGAdress, item.Subpage, subpage, SubpageORG); } if (item.ORGAdress < 0xc000) { uint len = (item.ORGAdress - SubpageORG) + item.Subpage * 0x2000 + (item.ORGLength - item.ORGAdress); for (uint i = orgIni; i < len; i++) { rom [i] = output.Data [srcAdress]; srcAdress++; } } } return(rom); }
public static int Main(string[] args) { InstructionSets = new Dictionary <string, InstructionSet>(); InstructionSets.Add("z80", LoadInternalSet("sasSX.Tables.z80.table")); InstructionSets.Add("z80alt", LoadInternalSet("sasSX.Tables.z80alt.table")); string instructionSet = "z80"; // Default string inputFile = null, outputFile = null; settings = new AssemblySettings(); List <string> defines = new List <string>(); Console.WriteLine("-------------------------------------------------------------------------------"); Console.WriteLine("sasSX v" + version + " WIP MSX cross-assembler.KnightOS [2015],Libertium Games[2016/07/30]"); Console.WriteLine("-------------------------------------------------------------------------------"); // Assembling labels, calls and jumps // Output text file game.txt saved // Binary file game.rom saved // Symbol file game.sym saved // Completed in 1.07 seconds for (int i = 0; i < args.Length; i++) { string arg = args[i]; if (arg.StartsWith("-") && arg != "-") { try { switch (arg) { case "-d": case "--define": defines.AddRange(args[++i].Split(',')); break; case "--debug-mode": Thread.Sleep(10000); break; case "--encoding": try { settings.Encoding = Encoding.GetEncoding(args[++i]); } catch { Console.Error.WriteLine( "The specified encoding was not recognized. Use sasSX --list-encodings to see available encodings."); return(1); } break; case "-h": case "-?": case "/?": case "/help": case "-help": case "--help": DisplayHelp(); return(0); case "--inc": case "--include": settings.IncludePath = args[++i].Split(';'); break; case "--input": case "--input-file": inputFile = args[++i]; break; case "--instr": case "--instruction-set": instructionSet = args[++i]; break; case "-as": case "--asmsx": instructionSet = "z80alt"; asmsx = true; break; case "-l": case "--listing": if (i <= arg.Length && !args[i + 1].StartsWith("-")) { settings.ListingOutput = args[++i]; } else { Console.WriteLine("!!--listing whithout parameter !!"); } break; case "--list-encodings": Console.WriteLine("The default encoding is UTF-8. The following are available: "); foreach (var encoding in Encoding.GetEncodings()) { Console.WriteLine("{0} [{1}]", encoding.DisplayName, encoding.Name); } Console.WriteLine("Use the identifier (in [brackets]) with --encoding)."); return(0); case "--nest-macros": settings.AllowNestedMacros = true; break; case "--output": case "--output-file": outputFile = args[++i]; break; case "-s": case "--symbols": if (i <= arg.Length && !args[i + 1].StartsWith("-")) { settings.SymbolOutput = args[++i]; } else { settings.SymbolOutput = ""; } break; case "-v": case "--verbose": settings.Verbose = VerboseLevels.Minimal; if (i + 1 < args.Length && !args[i + 1].StartsWith("-")) { int level = 0; Int32.TryParse(args[++i], out level); settings.Verbose = (VerboseLevels)level; } //settings.Verbose = VerboseLevel; Console.WriteLine("Verbose level: {0}", settings.Verbose); break; } } catch (ArgumentOutOfRangeException) { Console.Error.WriteLine("Error: Invalid usage. Use sasSX.exe --help for usage information."); return(1); } } else { if (inputFile == null) { inputFile = args[i]; } else if (outputFile == null) { outputFile = args[i]; } else { Console.Error.WriteLine("Error: Invalid usage. Use sasSX.exe --help for usage information."); return(1); } } } if (inputFile == null) { Console.Error.WriteLine("Syntax: sasSX [file.asm]"); Console.Error.WriteLine("No input file specified. Use sasSX.exe --help for usage information."); return(1); } if (outputFile == null) { outputFile = Path.GetFileNameWithoutExtension(inputFile) + ".bin"; } InstructionSet selectedInstructionSet; if (!InstructionSets.ContainsKey(instructionSet)) { if (File.Exists(instructionSet)) { selectedInstructionSet = InstructionSet.Load(File.ReadAllText(instructionSet)); } else { Console.Error.WriteLine("Specified instruction set was not found."); return(1); } } else { selectedInstructionSet = InstructionSets[instructionSet]; } var assembler = new Assembler(selectedInstructionSet, settings); assembler.ASMSX = asmsx; foreach (var define in defines) { assembler.ExpressionEngine.Symbols.Add(define.ToLower(), new Symbol(1)); } string file = ""; if (inputFile == "-") { file = Console.In.ReadToEnd(); } else if (File.Exists(inputFile)) { file = File.ReadAllText(inputFile); } else { Console.Error.WriteLine("File not found: {0}", inputFile); Console.Error.WriteLine("Press any key to continue..."); Console.ReadKey(true); } var watch = new Stopwatch(); AssemblyOutput output = null; if (file != "") { Console.WriteLine("Assembling labels, calls and jumps"); VerboseLevels v = settings.Verbose; watch.Start(); output = assembler.Assemble(file, inputFile); //watch.Stop (); if (settings.Verbose != v) { Console.WriteLine("Verbose level from .asm: {0}", settings.Verbose); } var errors = from l in output.Listing where l.Warning != AssemblyWarning.None || l.Error != AssemblyError.None orderby l.RootLineNumber select l; if (settings.Verbose == VerboseLevels.Quiet || settings.Verbose == VerboseLevels.Diagnostic) //!settings.Verbose) { foreach (var listing in errors) { if (listing.Error != AssemblyError.None) { //Console.Error.WriteLine (listing.FileName + ":" + listing.LineNumber + " Error: " + listing.Error +". Code: " + listing.Code); Console.Error.WriteLine(listing.FileName + " " + listing.LineNumber + " " + listing.Code + " : " + listing.Error); } // Cesc if (settings.Verbose == VerboseLevels.Diagnostic && listing.Warning != AssemblyWarning.None) { Console.Error.WriteLine(listing.FileName + ":" + listing.LineNumber + " Warning: " + listing.Warning + ". Code: " + listing.Code); } } } if (settings.ListingOutput != null || (settings.Verbose != VerboseLevels.Quiet && settings.Verbose != VerboseLevels.Diagnostic)) { var listing = GenerateListing(output); if (settings.Verbose != VerboseLevels.Quiet && settings.Verbose != VerboseLevels.Minimal) { Console.Write(listing); } if (settings.ListingOutput != null) { File.WriteAllText(settings.ListingOutput, listing); Console.WriteLine("Output text file " + settings.ListingOutput + " saved"); } } if (settings.SymbolOutput != null) { if (settings.SymbolOutput == "") { settings.SymbolOutput = inputFile.ToLower().Replace(".asm", ".sym"); } using (var symbolWriter = new StreamWriter(settings.SymbolOutput)) { symbolWriter.WriteLine("; This symbol table file was generated from {0}", inputFile); symbolWriter.WriteLine("; sasSX Version " + version); symbolWriter.WriteLine(); symbolWriter.WriteLine("; global and local labels"); WriteSymbols(assembler, symbolWriter); Console.WriteLine("Symbols file " + settings.SymbolOutput + " saved"); } } // Cesc: todo pendent de validar amb casos de prova i en especial les ROM de 48KB if (assembler.IsROM) { // Sizes: 16KB to 48KB in steps of 16KB int pages = 8; // TODO Default 32KB int size = pages * 16 * 1024; // Default 128KB byte[] rom = setROMHeader(size, assembler.ROMStart); int srcAdress = 0; foreach (Assembler.ORGItem item in assembler.ORGsList) { if (settings.Verbose == VerboseLevels.Diagnostic) { Console.WriteLine("ORG: 0x{0:X}-0x{1:X} Subpage:{2}", item.ORGAdress, item.ORGLength, item.Subpage); } uint orgIni; if (srcAdress == 0) { orgIni = item.ORGAdress - 0x4000 + 16; } else { orgIni = item.ORGAdress - 0x4000; } if (item.ORGAdress < 0xc000) { for (uint i = orgIni; i < (item.ORGLength - 0x4000); i++) { rom [i] = output.Data [srcAdress]; srcAdress++; } } } File.WriteAllBytes(outputFile.Replace(".bin", ".rom"), rom); } else if (assembler.IsMegaROM) { // Sizes: 128KB to 512KB in steps of 8KB int pages = 0; // Default 128KB foreach (Assembler.ORGItem item in assembler.ORGsList) { pages = (int)Math.Max(pages, item.Subpage); if (settings.Verbose == VerboseLevels.Diagnostic) { Console.WriteLine("ORG: 0x{0:X}-0x{1:X} Subpage:{2}", item.ORGAdress, item.ORGLength, item.Subpage); } } pages++; if (settings.Verbose == VerboseLevels.Diagnostic) { Console.WriteLine("Pages count:{0}", pages); } if (errors.Count(e => e.Error != AssemblyError.None) == 0) { byte[] rom = fillMegaROM(pages, assembler, output); File.WriteAllBytes(outputFile.Replace(".bin", ".rom"), rom); Console.WriteLine("Binary file " + outputFile.Replace(".bin", ".rom") + " saved"); if (settings.Verbose == VerboseLevels.Diagnostic) { File.WriteAllBytes(outputFile, output.Data); Console.WriteLine("Binary file " + outputFile + " saved"); } } else { Console.WriteLine("Errors count:{0}", errors.Count(e => e.Error != AssemblyError.None)); } } else { if (outputFile == "-") { Console.OpenStandardOutput().Write(output.Data, 0, output.Data.Length); } else { File.WriteAllBytes(outputFile, output.Data); } } watch.Stop(); Console.Error.WriteLine("Assembly done: {0:F2} s", watch.ElapsedMilliseconds / 1000d); if (Debugger.IsAttached) { Console.Error.WriteLine("Press any key to continue..."); Console.ReadKey(true); } return(errors.Count(e => e.Error != AssemblyError.None)); } return(-1); }