/// <summary> /// Gets a copy of the AppSettings with a standard set of formatting options (e.g. lower /// case for everything). /// </summary> /// <returns>New app settings object.</returns> private AppSettings CreateNormalizedSettings() { AppSettings settings = AppSettings.Global.GetCopy(); // Override all asm formatting options. We can ignore ShiftBeforeAdjust and the // pseudo-op names because those are set by the generators. settings.SetBool(AppSettings.FMT_UPPER_HEX_DIGITS, false); settings.SetBool(AppSettings.FMT_UPPER_OP_MNEMONIC, false); settings.SetBool(AppSettings.FMT_UPPER_PSEUDO_OP_MNEMONIC, false); settings.SetBool(AppSettings.FMT_UPPER_OPERAND_A, true); settings.SetBool(AppSettings.FMT_UPPER_OPERAND_S, true); settings.SetBool(AppSettings.FMT_UPPER_OPERAND_XY, false); settings.SetBool(AppSettings.FMT_ADD_SPACE_FULL_COMMENT, false); // Don't show the assembler ident line. You can make a case for this being // mandatory, since the generated code is only guaranteed to work with the // assembler for which it was targeted, but I expect we'll quickly get to a // place where we don't have to work around assembler bugs, and this will just // become a nuisance. settings.SetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, false); // Don't break lines with long labels. That way we can redefine "long" // without breaking our tests. (This is purely cosmetic.) settings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false); // This could be on or off. Off seems less distracting. settings.SetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false); // Disable label localization. We want to be able to play with this a bit // without disrupting all the other tests. Use a test-only feature to enable // it for the localization test. settings.SetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION, true); IEnumerator <AssemblerInfo> iter = AssemblerInfo.GetInfoEnumerator(); while (iter.MoveNext()) { AssemblerInfo.Id asmId = iter.Current.AssemblerId; AssemblerConfig curConfig = AssemblerConfig.GetConfig(settings, asmId); AssemblerConfig defConfig = AssemblerInfo.GetAssembler(asmId).GetDefaultConfig(); // Merge the two together. We want the default assembler config for most // things, but the executable path from the current config. defConfig.ExecutablePath = curConfig.ExecutablePath; // Write it into the test settings. AssemblerConfig.SetConfig(settings, asmId, defConfig); } return(settings); }
private void RunAssemblerButton_Click(object sender, RoutedEventArgs e) { IAssembler asm = AssemblerInfo.GetAssembler(mSelectedAssemblerId); if (asm == null) { Debug.WriteLine("Unable to get assembler for " + mSelectedAssemblerId); return; } asm.Configure(mGenerationResults, mWorkDirectory); AsmWorker aw = new AsmWorker(asm); WorkProgress dlg = new WorkProgress(this, aw, true); dlg.ShowDialog(); //Debug.WriteLine("Dialog returned: " + dlg.DialogResult); if (dlg.DialogResult != true) { // Canceled, or failed to even run the assembler. return; } AssemblerResults results = aw.Results; if (results == null) { Debug.WriteLine("Dialog returned OK, but no assembler results found"); Debug.Assert(false); return; } StringBuilder sb = new StringBuilder(results.Stdout.Length + results.Stderr.Length + 200); sb.Append(results.CommandLine); sb.Append("\r\n"); sb.AppendFormat("ExitCode={0} - ", results.ExitCode); if (results.ExitCode == 0) { FileInfo fi = new FileInfo(results.OutputPathName); if (!fi.Exists) { MessageBox.Show(this, Res.Strings.ASM_OUTPUT_NOT_FOUND, Res.Strings.ASM_MISMATCH_CAPTION, MessageBoxButton.OK, MessageBoxImage.Error); sb.Append(Res.Strings.ASM_MATCH_FAILURE); } else if (!CommonUtil.FileUtil.CompareBinaryFile(mProject.FileData, results.OutputPathName, out int offset, out byte fileVal)) { if (fi.Length != mProject.FileData.Length && offset == fi.Length || offset == mProject.FileData.Length) { // The files matched up to the point where one ended. string msg = string.Format(Res.Strings.ASM_MISMATCH_LENGTH_FMT, fi.Length, mProject.FileData.Length); MessageBox.Show(msg, Res.Strings.ASM_MISMATCH_CAPTION, MessageBoxButton.OK, MessageBoxImage.Error); sb.Append(msg); } else { string msg = string.Format(Res.Strings.ASM_MISMATCH_DATA_FMT, offset, fileVal, mProject.FileData[offset]); MessageBox.Show(msg, Res.Strings.ASM_MISMATCH_CAPTION, MessageBoxButton.OK, MessageBoxImage.Error); sb.Append(msg); } } else { sb.Append(Res.Strings.ASM_MATCH_SUCCESS); } }
/// <summary> /// Generates source code for the specified test case, assembles it, and compares /// the output of both steps to expected values. The process is repeated for every /// known assembler. /// /// If an assembler is known but not configured, the assembly step is skipped, and /// does not count as a failure. /// </summary> /// <param name="pathName">Full path to test case.</param> /// <returns>True if all assemblers worked as expected.</returns> private bool GenerateAndAssemble(string pathName) { ReportProgress(Path.GetFileName(pathName) + "...\r\n"); // Create DisasmProject object, either as a new project for a plain data file, // or from a project file. DisasmProject project = InstantiateProject(pathName, out FileLoadReport projectLoadReport); if (project == null) { ReportFailure(); return(false); } int testNum = GetTestNum(pathName); // Create a temporary directory to work in. string workDir = CreateWorkDirectory(pathName); if (string.IsNullOrEmpty(workDir)) { ReportFailure(); project.Cleanup(); return(false); } AppSettings settings = CreateNormalizedSettings(); ApplyProjectSettings(settings, project); // Iterate through all known assemblers. bool didFail = false; foreach (AssemblerInfo.Id asmId in (AssemblerInfo.Id[])Enum.GetValues(typeof(AssemblerInfo.Id))) { if (asmId == AssemblerInfo.Id.Unknown) { continue; } string fileName = Path.GetFileName(pathName); TaskTimer timer = new TaskTimer(); timer.StartTask("Full Test Duration"); // Create results object and add it to the list. We'll add stuff to it for // as far as we get. GenTestResults results = new GenTestResults(pathName, asmId); mResults.Add(results); results.ProjectLoadReport = projectLoadReport; // Generate source code. ReportProgress(" " + asmId.ToString() + " generate..."); IGenerator gen = AssemblerInfo.GetGenerator(asmId); if (gen == null) { ReportErrMsg("generator unavailable"); ReportProgress("\r\n"); //didFail = true; continue; } timer.StartTask("Generate Source"); gen.Configure(project, workDir, fileName, AssemblerVersionCache.GetVersion(asmId), settings); List <string> genPathNames = gen.GenerateSource(mWorker); timer.EndTask("Generate Source"); if (mWorker.CancellationPending) { // The generator will stop early if a cancellation is requested. If we // don't break here, the compare function will report a failure, which // isn't too problematic but looks funny. break; } ReportProgress(" verify..."); timer.StartTask("Compare Source to Expected"); bool match = CompareGeneratedToExpected(pathName, genPathNames); timer.EndTask("Compare Source to Expected"); if (match) { ReportSuccess(); results.GenerateOkay = true; } else { ReportFailure(); didFail = true; // The fact that it doesn't match the expected sources doesn't mean it's // invalid. Go ahead and try to build it. //continue; } // Assemble code. ReportProgress(" " + asmId.ToString() + " assemble..."); IAssembler asm = AssemblerInfo.GetAssembler(asmId); if (asm == null) { ReportErrMsg("assembler unavailable"); ReportProgress("\r\n"); continue; } timer.StartTask("Assemble Source"); asm.Configure(genPathNames, workDir); AssemblerResults asmResults = asm.RunAssembler(mWorker); timer.EndTask("Assemble Source"); if (asmResults == null) { ReportErrMsg("unable to run assembler"); ReportFailure(); didFail = true; continue; } if (asmResults.ExitCode != 0) { ReportErrMsg("assembler returned code=" + asmResults.ExitCode); ReportFailure(); didFail = true; continue; } results.AsmResults = asmResults; ReportProgress(" verify..."); timer.StartTask("Compare Binary to Expected"); FileInfo fi = new FileInfo(asmResults.OutputPathName); if (!fi.Exists) { // This can happen if the assembler fails to generate output but doesn't // report an error code (e.g. Merlin 32 in certain situations). ReportErrMsg("asm output missing"); ReportFailure(); didFail = true; continue; } else if (fi.Length != project.FileData.Length) { ReportErrMsg("asm output mismatch: length is " + fi.Length + ", expected " + project.FileData.Length); ReportFailure(); didFail = true; continue; } else if (!FileUtil.CompareBinaryFile(project.FileData, asmResults.OutputPathName, out int badOffset, out byte badFileVal)) { ReportErrMsg("asm output mismatch: offset +" + badOffset.ToString("x6") + " has value $" + badFileVal.ToString("x2") + ", expected $" + project.FileData[badOffset].ToString("x2")); ReportFailure(); didFail = true; continue; } timer.EndTask("Compare Binary to Expected"); // Victory! results.AssembleOkay = true; ReportSuccess(); timer.EndTask("Full Test Duration"); results.Timer = timer; // We don't scrub the directory on success at this point. We could, but we'd // need to remove only those files associated with the currently assembler. // Otherwise, a failure followed by a success would wipe out the unsuccessful // temporaries. } // If something failed, leave the bits around for examination. Otherwise, try to // remove the directory and all its contents. if (!didFail && !RetainOutput) { ScrubWorkDirectory(workDir, testNum); RemoveWorkDirectory(workDir); } project.Cleanup(); return(!didFail); }