mirror of https://github.com/fadden/6502bench.git
SGEC update, part 2 (of 2)
Expand SGEC to include long comments and notes. These are serialized in JavaScript form. SGEC now accepts addresses and relative position deltas. Exported content uses addresses, and can be configured for deltas.
This commit is contained in:
parent
c5d764d11f
commit
0860a00a54
|
@ -78,5 +78,22 @@ namespace CommonWPF {
|
||||||
|
|
||||||
return fmt.Width;
|
return fmt.Width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a System.Windows.Media.Color value into 32-bit ARGB.
|
||||||
|
/// </summary>
|
||||||
|
public static int ColorToInt(Color color) {
|
||||||
|
return (color.A << 24) | (color.R << 16) | (color.G << 8) | color.B;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a System.Windows.Media.Color value from 32-bit ARGB.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="colorInt"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Color ColorFromInt(int colorInt) {
|
||||||
|
return Color.FromArgb((byte)(colorInt >> 24), (byte)(colorInt >> 16),
|
||||||
|
(byte)(colorInt >> 8), (byte)colorInt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4387,7 +4387,19 @@ namespace SourceGen {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
string sgecPathName = Path.GetFullPath(fileDlg.FileName);
|
string sgecPathName = Path.GetFullPath(fileDlg.FileName);
|
||||||
if (!Sgec.ExportToFile(sgecPathName, mProject, out string detailMsg)) {
|
|
||||||
|
MessageBoxResult res = MessageBox.Show("Use relative offsets?", "Question",
|
||||||
|
MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
|
||||||
|
bool resMode;
|
||||||
|
if (res == MessageBoxResult.Cancel) {
|
||||||
|
return;
|
||||||
|
} else if (res == MessageBoxResult.Yes) {
|
||||||
|
resMode = true;
|
||||||
|
} else {
|
||||||
|
resMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Sgec.ExportToFile(sgecPathName, mProject, resMode, out string detailMsg)) {
|
||||||
MessageBox.Show("Failed: " + detailMsg);
|
MessageBox.Show("Failed: " + detailMsg);
|
||||||
} else {
|
} else {
|
||||||
MessageBox.Show("Success: " + detailMsg);
|
MessageBox.Show("Success: " + detailMsg);
|
||||||
|
|
|
@ -273,7 +273,7 @@ namespace SourceGen {
|
||||||
Text = mlc.Text;
|
Text = mlc.Text;
|
||||||
BoxMode = mlc.BoxMode;
|
BoxMode = mlc.BoxMode;
|
||||||
MaxWidth = mlc.MaxWidth;
|
MaxWidth = mlc.MaxWidth;
|
||||||
BackgroundColor = ColorToInt(mlc.BackgroundColor);
|
BackgroundColor = CommonWPF.Helper.ColorToInt(mlc.BackgroundColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public class SerSymbol {
|
public class SerSymbol {
|
||||||
|
@ -730,7 +730,7 @@ namespace SourceGen {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
proj.Notes[intKey] = new MultiLineComment(kvp.Value.Text,
|
proj.Notes[intKey] = new MultiLineComment(kvp.Value.Text,
|
||||||
ColorFromInt(kvp.Value.BackgroundColor));
|
CommonWPF.Helper.ColorFromInt(kvp.Value.BackgroundColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deserialize user-defined labels.
|
// Deserialize user-defined labels.
|
||||||
|
@ -1261,14 +1261,5 @@ namespace SourceGen {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int ColorToInt(Color color) {
|
|
||||||
return (color.A << 24) | (color.R << 16) | (color.G << 8) | color.B;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Color ColorFromInt(int colorInt) {
|
|
||||||
return Color.FromArgb((byte)(colorInt >> 24), (byte)(colorInt >> 16),
|
|
||||||
(byte)(colorInt >> 8), (byte)colorInt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,10 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Web.Script.Serialization;
|
||||||
|
|
||||||
|
using CommonUtil;
|
||||||
|
|
||||||
namespace SourceGen {
|
namespace SourceGen {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -30,6 +34,56 @@ namespace SourceGen {
|
||||||
public static class Sgec {
|
public static class Sgec {
|
||||||
public const string SGEC_EXT = "_sgec.txt";
|
public const string SGEC_EXT = "_sgec.txt";
|
||||||
|
|
||||||
|
// Commands.
|
||||||
|
private const string SET_COMMENT = "set-comment";
|
||||||
|
private const string SET_LONG_COMMENT = "set-long-comment";
|
||||||
|
private const string SET_NOTE = "set-note";
|
||||||
|
|
||||||
|
// Regular expression used for parsing lines.
|
||||||
|
private const string LINE_PATTERN = @"^([A-Za-z-]+)\s([^:]+):(.+)$";
|
||||||
|
private static Regex sLineRegex = new Regex(LINE_PATTERN);
|
||||||
|
private const int GROUP_CMD = 1;
|
||||||
|
private const int GROUP_POS = 2;
|
||||||
|
private const int GROUP_VALUE = 3;
|
||||||
|
|
||||||
|
private static bool OUTPUT_ADDR = true; // addr vs. offset
|
||||||
|
|
||||||
|
// Lifted from ProjectFile.
|
||||||
|
public class SerMultiLineComment {
|
||||||
|
// NOTE: Text must be CRLF at line breaks.
|
||||||
|
public string Text { get; set; }
|
||||||
|
public bool BoxMode { get; set; }
|
||||||
|
public int MaxWidth { get; set; }
|
||||||
|
public int BackgroundColor { get; set; }
|
||||||
|
|
||||||
|
public SerMultiLineComment() { }
|
||||||
|
public SerMultiLineComment(MultiLineComment mlc) {
|
||||||
|
Text = mlc.Text;
|
||||||
|
BoxMode = mlc.BoxMode;
|
||||||
|
MaxWidth = mlc.MaxWidth;
|
||||||
|
BackgroundColor = CommonWPF.Helper.ColorToInt(mlc.BackgroundColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a position string, which may be a file offset, an address, or a
|
||||||
|
/// position delta.
|
||||||
|
/// </summary>
|
||||||
|
private static string PositionStr(int offset, int prevOffset, AddressMap addrMap,
|
||||||
|
bool relMode) {
|
||||||
|
if (prevOffset < 0 || !relMode) {
|
||||||
|
// hex offset or address
|
||||||
|
if (OUTPUT_ADDR) {
|
||||||
|
return '$' + addrMap.OffsetToAddress(offset).ToString("x4");
|
||||||
|
} else {
|
||||||
|
return '+' + offset.ToString("x6");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// decimal delta
|
||||||
|
return '>' + (offset - prevOffset).ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exports comments in SGEC format.
|
/// Exports comments in SGEC format.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -37,19 +91,43 @@ namespace SourceGen {
|
||||||
/// <param name="proj">Project object.</param>
|
/// <param name="proj">Project object.</param>
|
||||||
/// <param name="detailMsg">Details on failure or success.</param>
|
/// <param name="detailMsg">Details on failure or success.</param>
|
||||||
/// <returns>True on success.</returns>
|
/// <returns>True on success.</returns>
|
||||||
public static bool ExportToFile(string pathName, DisasmProject proj, out string detailMsg) {
|
public static bool ExportToFile(string pathName, DisasmProject proj, bool relMode,
|
||||||
int numComments = 0;
|
out string detailMsg) {
|
||||||
|
int numItems = 0;
|
||||||
|
|
||||||
|
JavaScriptSerializer ser = new JavaScriptSerializer();
|
||||||
|
|
||||||
|
int prevOffset = -1;
|
||||||
using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) {
|
using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) {
|
||||||
for (int offset = 0; offset < proj.FileDataLength; offset++) {
|
for (int offset = 0; offset < proj.FileDataLength; offset++) {
|
||||||
if (!string.IsNullOrEmpty(proj.Comments[offset])) {
|
if (!string.IsNullOrEmpty(proj.Comments[offset])) {
|
||||||
sw.WriteLine("set-comment +" + offset.ToString("x6") + ':' +
|
sw.WriteLine(SET_COMMENT + " " +
|
||||||
|
PositionStr(offset, prevOffset, proj.AddrMap, relMode) + ':' +
|
||||||
proj.Comments[offset]);
|
proj.Comments[offset]);
|
||||||
numComments++;
|
prevOffset = offset;
|
||||||
|
numItems++;
|
||||||
|
}
|
||||||
|
if (proj.LongComments.TryGetValue(offset, out MultiLineComment lc)) {
|
||||||
|
SerMultiLineComment serCom = new SerMultiLineComment(lc);
|
||||||
|
string cereal = ser.Serialize(serCom);
|
||||||
|
sw.WriteLine(SET_LONG_COMMENT + " " +
|
||||||
|
PositionStr(offset, prevOffset, proj.AddrMap, relMode) + ':' +
|
||||||
|
cereal);
|
||||||
|
prevOffset = offset;
|
||||||
|
numItems++;
|
||||||
|
}
|
||||||
|
if (proj.Notes.TryGetValue(offset, out MultiLineComment nt)) {
|
||||||
|
SerMultiLineComment serCom = new SerMultiLineComment(nt);
|
||||||
|
string cereal = ser.Serialize(serCom);
|
||||||
|
sw.WriteLine(SET_NOTE + " " +
|
||||||
|
PositionStr(offset, prevOffset, proj.AddrMap, relMode) + ':' +
|
||||||
|
cereal);
|
||||||
|
prevOffset = offset;
|
||||||
|
numItems++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
detailMsg = "Exported " + numComments + " comments.";
|
detailMsg = "exported " + numItems + " items.";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,49 +150,139 @@ namespace SourceGen {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<int> changed = new List<int>(lines.Length);
|
JavaScriptSerializer ser = new JavaScriptSerializer();
|
||||||
|
|
||||||
string setComment = "set-comment +";
|
int lineNum = 0;
|
||||||
|
int prevOffset = -1;
|
||||||
foreach (string line in lines) {
|
foreach (string line in lines) {
|
||||||
if (!line.StartsWith(setComment)) {
|
lineNum++; // first line is 1
|
||||||
Debug.WriteLine("Ignoring " + line);
|
if (string.IsNullOrEmpty(line) || line[0] == '#') {
|
||||||
|
// ignore
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
MatchCollection matches = sLineRegex.Matches(line);
|
||||||
int offset;
|
if (matches.Count != 1) {
|
||||||
try {
|
detailMsg = "Line " + lineNum + ": unable to parse into tokens";
|
||||||
offset = Convert.ToInt32(line.Substring(setComment.Length, 6), 16);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Debug.WriteLine("Failed on " + line);
|
|
||||||
detailMsg = ex.Message;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed.Contains(offset)) {
|
string posStr = matches[0].Groups[GROUP_POS].Value;
|
||||||
Debug.WriteLine("Skipping repeated entry +" + offset.ToString("X6"));
|
int offset;
|
||||||
continue;
|
if (posStr[0] == '+') {
|
||||||
|
// offset
|
||||||
|
if (!Asm65.Number.TryParseIntHex(posStr.Substring(1), out offset)) {
|
||||||
|
detailMsg = "Line " + lineNum + ": unable to parse offset '" +
|
||||||
|
posStr + "'";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (posStr[0] == '$') {
|
||||||
|
// address
|
||||||
|
if (!Asm65.Address.ParseAddress(posStr, (1 << 24) - 1, out int addr)) {
|
||||||
|
detailMsg = "Line " + lineNum + ": unable to parse address '" +
|
||||||
|
posStr + "'";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
offset = proj.AddrMap.AddressToOffset(0, addr);
|
||||||
|
} else if (posStr[0] == '>') {
|
||||||
|
// relative offset
|
||||||
|
if (prevOffset < 0) {
|
||||||
|
detailMsg = "Line " + lineNum + ": first address/offset cannot be relative";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Asm65.Number.TryParseInt(posStr.Substring(1), out int delta, out int _)) {
|
||||||
|
detailMsg = "Line " + lineNum + ": unable to parse delta";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
offset = prevOffset + delta;
|
||||||
|
} else {
|
||||||
|
detailMsg = "Line " + lineNum + ": unknown position type '" + posStr[0] + "'";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string oldComment = proj.Comments[offset];
|
prevOffset = offset;
|
||||||
string newComment = line.Substring(setComment.Length + 7);
|
|
||||||
if (oldComment == newComment) {
|
string cmdStr = matches[0].Groups[GROUP_CMD].Value;
|
||||||
// no change
|
string valueStr = matches[0].Groups[GROUP_VALUE].Value;
|
||||||
continue;
|
switch (cmdStr) {
|
||||||
}
|
case SET_COMMENT: {
|
||||||
if (!string.IsNullOrEmpty(oldComment)) {
|
string oldComment = proj.Comments[offset];
|
||||||
// overwriting existing entry
|
string newComment = valueStr;
|
||||||
Debug.WriteLine("Replacing comment +" + offset.ToString("x6") +
|
if (oldComment == newComment) {
|
||||||
" '" + oldComment + "'");
|
// no change
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(oldComment)) {
|
||||||
|
// overwriting existing entry; make a note
|
||||||
|
Debug.WriteLine("Replacing comment +" + offset.ToString("x6") +
|
||||||
|
" '" + oldComment + "'");
|
||||||
|
}
|
||||||
|
UndoableChange uc = UndoableChange.CreateCommentChange(offset,
|
||||||
|
oldComment, newComment);
|
||||||
|
cs.Add(uc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SET_LONG_COMMENT: {
|
||||||
|
if (!DeserializeMlc(ser, valueStr, false,
|
||||||
|
out MultiLineComment newComment)) {
|
||||||
|
detailMsg = "Line " + lineNum + ": failed to deserialize value";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
proj.LongComments.TryGetValue(offset, out MultiLineComment oldComment);
|
||||||
|
if (oldComment == newComment) {
|
||||||
|
// no change
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
UndoableChange uc = UndoableChange.CreateLongCommentChange(offset,
|
||||||
|
oldComment, newComment);
|
||||||
|
cs.Add(uc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SET_NOTE: {
|
||||||
|
if (!DeserializeMlc(ser, valueStr, true,
|
||||||
|
out MultiLineComment newNote)) {
|
||||||
|
detailMsg = "Line " + lineNum + ": failed to deserialize value";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
proj.Notes.TryGetValue(offset, out MultiLineComment oldNote);
|
||||||
|
if (oldNote == newNote) {
|
||||||
|
// no change
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
UndoableChange uc = UndoableChange.CreateNoteChange(offset,
|
||||||
|
oldNote, newNote);
|
||||||
|
cs.Add(uc);
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
detailMsg = "Line " + lineNum + ": unknown command '" + cmdStr + "'";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UndoableChange uc = UndoableChange.CreateCommentChange(offset,
|
|
||||||
oldComment, newComment);
|
|
||||||
cs.Add(uc);
|
|
||||||
changed.Add(offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
detailMsg = "applied " + cs.Count + " changes.";
|
detailMsg = "applied " + cs.Count + " changes.";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool DeserializeMlc(JavaScriptSerializer ser, string cereal, bool isNote,
|
||||||
|
out MultiLineComment mlc) {
|
||||||
|
mlc = null;
|
||||||
|
SerMultiLineComment smlc;
|
||||||
|
try {
|
||||||
|
smlc = ser.Deserialize<SerMultiLineComment>(cereal);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Debug.WriteLine("Deserialization failed: " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNote) {
|
||||||
|
mlc = new MultiLineComment(smlc.Text,
|
||||||
|
CommonWPF.Helper.ColorFromInt(smlc.BackgroundColor));
|
||||||
|
} else {
|
||||||
|
mlc = new MultiLineComment(smlc.Text, smlc.BoxMode, smlc.MaxWidth);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue