1
0
mirror of https://github.com/fadden/6502bench.git synced 2025-02-20 06:29:04 +00:00

Change the way string formats are defined

We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.

What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding.  These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.

This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.

The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)

While I was at it, I finished removing DciReverse.  It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
This commit is contained in:
Andy McFadden 2019-08-07 15:23:23 -07:00
parent 89288c4d8c
commit 0d0854bda7
12 changed files with 420 additions and 367 deletions

View File

@ -126,7 +126,7 @@ namespace PluginCommon {
Unknown = 0, Unknown = 0,
NumericLE, NumericLE,
NumericBE, NumericBE,
String, StringGeneric,
Dense, Dense,
Fill Fill
} }

View File

@ -122,7 +122,6 @@ namespace SourceGen.AsmGen {
//StrLen8 //StrLen8
//StrLen16 //StrLen16
//StrDci //StrDci
//StrDciReverse
}; };
@ -388,7 +387,12 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr); OutputDenseHex(offset, length, labelStr, commentStr);
break; break;
case FormatDescriptor.Type.String: case FormatDescriptor.Type.StringGeneric:
case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.Type.StringNullTerm:
case FormatDescriptor.Type.StringL8:
case FormatDescriptor.Type.StringL16:
case FormatDescriptor.Type.StringDci:
multiLine = true; multiLine = true;
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputString(offset, labelStr, commentStr); OutputString(offset, labelStr, commentStr);
@ -532,7 +536,7 @@ namespace SourceGen.AsmGen {
Anattrib attr = Project.GetAnattrib(offset); Anattrib attr = Project.GetAnattrib(offset);
FormatDescriptor dfd = attr.DataDescriptor; FormatDescriptor dfd = attr.DataDescriptor;
Debug.Assert(dfd != null); Debug.Assert(dfd != null);
Debug.Assert(dfd.FormatType == FormatDescriptor.Type.String); Debug.Assert(dfd.IsString);
Debug.Assert(dfd.Length > 0); Debug.Assert(dfd.Length > 0);
bool highAscii = false; bool highAscii = false;
@ -541,36 +545,31 @@ namespace SourceGen.AsmGen {
bool showLeading = false; bool showLeading = false;
bool showTrailing = false; bool showTrailing = false;
switch (dfd.FormatSubType) { switch (dfd.FormatType) {
case FormatDescriptor.SubType.None: case FormatDescriptor.Type.StringGeneric:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
break; break;
case FormatDescriptor.SubType.Dci: case FormatDescriptor.Type.StringDci:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
trailingBytes = 1; trailingBytes = 1;
showTrailing = true; showTrailing = true;
break; break;
case FormatDescriptor.SubType.Reverse: case FormatDescriptor.Type.StringReverse:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
break; break;
case FormatDescriptor.SubType.DciReverse: case FormatDescriptor.Type.StringNullTerm:
highAscii = (data[offset + dfd.Length - 1] & 0x80) != 0;
leadingBytes = 1;
showLeading = true;
break;
case FormatDescriptor.SubType.CString:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
trailingBytes = 1; trailingBytes = 1;
showTrailing = true; showTrailing = true;
break; break;
case FormatDescriptor.SubType.L8String: case FormatDescriptor.Type.StringL8:
if (dfd.Length > 1) { if (dfd.Length > 1) {
highAscii = (data[offset + 1] & 0x80) != 0; highAscii = (data[offset + 1] & 0x80) != 0;
} }
leadingBytes = 1; leadingBytes = 1;
showLeading = true; showLeading = true;
break; break;
case FormatDescriptor.SubType.L16String: case FormatDescriptor.Type.StringL16:
if (dfd.Length > 2) { if (dfd.Length > 2) {
highAscii = (data[offset + 2] & 0x80) != 0; highAscii = (data[offset + 2] & 0x80) != 0;
} }
@ -598,18 +597,17 @@ namespace SourceGen.AsmGen {
string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.StrGeneric); string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.StrGeneric);
switch (dfd.FormatSubType) { switch (dfd.FormatType) {
case FormatDescriptor.SubType.None: case FormatDescriptor.Type.StringGeneric:
// TODO(someday): something fancy with encodings to handle high-ASCII text? // TODO(someday): something fancy with encodings to handle high-ASCII text?
break; break;
case FormatDescriptor.SubType.Dci: case FormatDescriptor.Type.StringDci:
case FormatDescriptor.SubType.Reverse: case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.SubType.DciReverse:
// Fully configured above. // Fully configured above.
break; break;
case FormatDescriptor.SubType.CString: case FormatDescriptor.Type.StringNullTerm:
case FormatDescriptor.SubType.L8String: case FormatDescriptor.Type.StringL8:
case FormatDescriptor.SubType.L16String: case FormatDescriptor.Type.StringL16:
// Implement as macro? // Implement as macro?
break; break;
default: default:

View File

@ -119,7 +119,6 @@ namespace SourceGen.AsmGen {
//StrLen8 // macro with .strlen? //StrLen8 // macro with .strlen?
//StrLen16 //StrLen16
//StrDci //StrDci
//StrDciReverse
}; };
@ -414,7 +413,12 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr); OutputDenseHex(offset, length, labelStr, commentStr);
break; break;
case FormatDescriptor.Type.String: case FormatDescriptor.Type.StringGeneric:
case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.Type.StringNullTerm:
case FormatDescriptor.Type.StringL8:
case FormatDescriptor.Type.StringL16:
case FormatDescriptor.Type.StringDci:
multiLine = true; multiLine = true;
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputString(offset, labelStr, commentStr); OutputString(offset, labelStr, commentStr);
@ -586,7 +590,7 @@ namespace SourceGen.AsmGen {
Anattrib attr = Project.GetAnattrib(offset); Anattrib attr = Project.GetAnattrib(offset);
FormatDescriptor dfd = attr.DataDescriptor; FormatDescriptor dfd = attr.DataDescriptor;
Debug.Assert(dfd != null); Debug.Assert(dfd != null);
Debug.Assert(dfd.FormatType == FormatDescriptor.Type.String); Debug.Assert(dfd.IsString);
Debug.Assert(dfd.Length > 0); Debug.Assert(dfd.Length > 0);
bool highAscii = false; bool highAscii = false;
@ -595,36 +599,31 @@ namespace SourceGen.AsmGen {
bool showLeading = false; bool showLeading = false;
bool showTrailing = false; bool showTrailing = false;
switch (dfd.FormatSubType) { switch (dfd.FormatType) {
case FormatDescriptor.SubType.None: case FormatDescriptor.Type.StringGeneric:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
break; break;
case FormatDescriptor.SubType.Dci: case FormatDescriptor.Type.StringDci:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
trailingBytes = 1; trailingBytes = 1;
showTrailing = true; showTrailing = true;
break; break;
case FormatDescriptor.SubType.Reverse: case FormatDescriptor.Type.StringReverse:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
break; break;
case FormatDescriptor.SubType.DciReverse: case FormatDescriptor.Type.StringNullTerm:
highAscii = (data[offset + dfd.Length - 1] & 0x80) != 0;
leadingBytes = 1;
showLeading = true;
break;
case FormatDescriptor.SubType.CString:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
trailingBytes = 1; trailingBytes = 1;
showTrailing = true; showTrailing = true;
break; break;
case FormatDescriptor.SubType.L8String: case FormatDescriptor.Type.StringL8:
if (dfd.Length > 1) { if (dfd.Length > 1) {
highAscii = (data[offset + 1] & 0x80) != 0; highAscii = (data[offset + 1] & 0x80) != 0;
} }
leadingBytes = 1; leadingBytes = 1;
showLeading = true; showLeading = true;
break; break;
case FormatDescriptor.SubType.L16String: case FormatDescriptor.Type.StringL16:
if (dfd.Length > 2) { if (dfd.Length > 2) {
highAscii = (data[offset + 2] & 0x80) != 0; highAscii = (data[offset + 2] & 0x80) != 0;
} }
@ -652,8 +651,8 @@ namespace SourceGen.AsmGen {
string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.StrGeneric); string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.StrGeneric);
switch (dfd.FormatSubType) { switch (dfd.FormatType) {
case FormatDescriptor.SubType.None: case FormatDescriptor.Type.StringGeneric:
// Special case for simple short high-ASCII strings. These have no // Special case for simple short high-ASCII strings. These have no
// leading or trailing bytes. We can improve this a bit by handling // leading or trailing bytes. We can improve this a bit by handling
// arbitrarily long strings by simply breaking them across lines. // arbitrarily long strings by simply breaking them across lines.
@ -673,19 +672,18 @@ namespace SourceGen.AsmGen {
highAscii = false; highAscii = false;
} }
break; break;
case FormatDescriptor.SubType.Dci: case FormatDescriptor.Type.StringDci:
case FormatDescriptor.SubType.Reverse: case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.SubType.DciReverse:
// Full configured above. // Full configured above.
break; break;
case FormatDescriptor.SubType.CString: case FormatDescriptor.Type.StringNullTerm:
if (gath.NumLinesOutput == 1 && !gath.HasDelimiter) { if (gath.NumLinesOutput == 1 && !gath.HasDelimiter) {
opcodeStr = sDataOpNames.StrNullTerm; opcodeStr = sDataOpNames.StrNullTerm;
showTrailing = false; showTrailing = false;
} }
break; break;
case FormatDescriptor.SubType.L8String: case FormatDescriptor.Type.StringL8:
case FormatDescriptor.SubType.L16String: case FormatDescriptor.Type.StringL16:
// Implement macros? // Implement macros?
break; break;
default: default:

View File

@ -118,7 +118,6 @@ namespace SourceGen.AsmGen {
StrLen16Hi = "strl", StrLen16Hi = "strl",
StrDci = "dci", StrDci = "dci",
StrDciHi = "dci", StrDciHi = "dci",
//StrDciReverse
}; };
@ -294,7 +293,12 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr); OutputDenseHex(offset, length, labelStr, commentStr);
break; break;
case FormatDescriptor.Type.String: case FormatDescriptor.Type.StringGeneric:
case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.Type.StringNullTerm:
case FormatDescriptor.Type.StringL8:
case FormatDescriptor.Type.StringL16:
case FormatDescriptor.Type.StringDci:
multiLine = true; multiLine = true;
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputString(offset, labelStr, commentStr); OutputString(offset, labelStr, commentStr);
@ -460,9 +464,6 @@ namespace SourceGen.AsmGen {
// backward order. Also, Merlin doesn't allow hex to be embedded in a REV // backward order. Also, Merlin doesn't allow hex to be embedded in a REV
// operation, so we can't use REV if the string contains a delimiter. // operation, so we can't use REV if the string contains a delimiter.
// //
// DciReverse is deprecated, but we can handle it as a Reverse string with a
// trailing byte on a following line.
//
// For aesthetic purposes, zero-length CString, L8String, and L16String // For aesthetic purposes, zero-length CString, L8String, and L16String
// should be output as DFB/DW zeroes rather than an empty string -- makes // should be output as DFB/DW zeroes rather than an empty string -- makes
// it easier to read. // it easier to read.
@ -472,7 +473,7 @@ namespace SourceGen.AsmGen {
Anattrib attr = Project.GetAnattrib(offset); Anattrib attr = Project.GetAnattrib(offset);
FormatDescriptor dfd = attr.DataDescriptor; FormatDescriptor dfd = attr.DataDescriptor;
Debug.Assert(dfd != null); Debug.Assert(dfd != null);
Debug.Assert(dfd.FormatType == FormatDescriptor.Type.String); Debug.Assert(dfd.IsString);
Debug.Assert(dfd.Length > 0); Debug.Assert(dfd.Length > 0);
bool highAscii = false; bool highAscii = false;
@ -483,22 +484,18 @@ namespace SourceGen.AsmGen {
bool showTrailing = false; bool showTrailing = false;
RevMode revMode = RevMode.Forward; RevMode revMode = RevMode.Forward;
switch (dfd.FormatSubType) { switch (dfd.FormatType) {
case FormatDescriptor.SubType.None: case FormatDescriptor.Type.StringGeneric:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
break; break;
case FormatDescriptor.SubType.Dci: case FormatDescriptor.Type.StringDci:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
break; break;
case FormatDescriptor.SubType.Reverse: case FormatDescriptor.Type.StringReverse:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
revMode = RevMode.Reverse; revMode = RevMode.Reverse;
break; break;
case FormatDescriptor.SubType.DciReverse: case FormatDescriptor.Type.StringNullTerm:
highAscii = (data[offset + dfd.Length - 1] & 0x80) != 0;
revMode = RevMode.Reverse;
break;
case FormatDescriptor.SubType.CString:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
if (dfd.Length == 1) { if (dfd.Length == 1) {
showZeroes = 1; // empty null-terminated string showZeroes = 1; // empty null-terminated string
@ -506,7 +503,7 @@ namespace SourceGen.AsmGen {
trailingBytes = 1; trailingBytes = 1;
showTrailing = true; showTrailing = true;
break; break;
case FormatDescriptor.SubType.L8String: case FormatDescriptor.Type.StringL8:
if (dfd.Length > 1) { if (dfd.Length > 1) {
highAscii = (data[offset + 1] & 0x80) != 0; highAscii = (data[offset + 1] & 0x80) != 0;
} else { } else {
@ -514,7 +511,7 @@ namespace SourceGen.AsmGen {
} }
leadingBytes = 1; leadingBytes = 1;
break; break;
case FormatDescriptor.SubType.L16String: case FormatDescriptor.Type.StringL16:
if (dfd.Length > 2) { if (dfd.Length > 2) {
highAscii = (data[offset + 2] & 0x80) != 0; highAscii = (data[offset + 2] & 0x80) != 0;
} else { } else {
@ -555,11 +552,11 @@ namespace SourceGen.AsmGen {
string opcodeStr; string opcodeStr;
switch (dfd.FormatSubType) { switch (dfd.FormatType) {
case FormatDescriptor.SubType.None: case FormatDescriptor.Type.StringGeneric:
opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric; opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric;
break; break;
case FormatDescriptor.SubType.Dci: case FormatDescriptor.Type.StringDci:
if (gath.NumLinesOutput == 1) { if (gath.NumLinesOutput == 1) {
opcodeStr = highAscii ? sDataOpNames.StrDciHi : sDataOpNames.StrDci; opcodeStr = highAscii ? sDataOpNames.StrDciHi : sDataOpNames.StrDci;
} else { } else {
@ -568,7 +565,7 @@ namespace SourceGen.AsmGen {
showTrailing = true; showTrailing = true;
} }
break; break;
case FormatDescriptor.SubType.Reverse: case FormatDescriptor.Type.StringReverse:
if (gath.HasDelimiter) { if (gath.HasDelimiter) {
// can't include escaped delimiters in REV // can't include escaped delimiters in REV
opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric; opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric;
@ -581,18 +578,11 @@ namespace SourceGen.AsmGen {
Debug.Assert(revMode == RevMode.Reverse); Debug.Assert(revMode == RevMode.Reverse);
} }
break; break;
case FormatDescriptor.SubType.DciReverse: case FormatDescriptor.Type.StringNullTerm:
// Mostly punt -- output as ASCII with special handling for first byte.
opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric;
revMode = RevMode.Forward;
leadingBytes = 1;
showLeading = true;
break;
case FormatDescriptor.SubType.CString:
//opcodeStr = sDataOpNames.StrNullTerm[highAscii ? 1 : 0]; //opcodeStr = sDataOpNames.StrNullTerm[highAscii ? 1 : 0];
opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric; opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric;
break; break;
case FormatDescriptor.SubType.L8String: case FormatDescriptor.Type.StringL8:
if (gath.NumLinesOutput == 1) { if (gath.NumLinesOutput == 1) {
opcodeStr = highAscii ? sDataOpNames.StrLen8Hi : sDataOpNames.StrLen8; opcodeStr = highAscii ? sDataOpNames.StrLen8Hi : sDataOpNames.StrLen8;
} else { } else {
@ -601,7 +591,7 @@ namespace SourceGen.AsmGen {
showLeading = true; showLeading = true;
} }
break; break;
case FormatDescriptor.SubType.L16String: case FormatDescriptor.Type.StringL16:
if (gath.NumLinesOutput == 1) { if (gath.NumLinesOutput == 1) {
opcodeStr = highAscii ? sDataOpNames.StrLen16Hi : sDataOpNames.StrLen16; opcodeStr = highAscii ? sDataOpNames.StrLen16Hi : sDataOpNames.StrLen16;
} else { } else {

View File

@ -129,7 +129,6 @@ namespace SourceGen.AsmGen {
StrLen8 = ".ptext", StrLen8 = ".ptext",
//StrLen16 //StrLen16
//StrDci //StrDci
//StrDciReverse
}; };
private const string HERE_PSEUDO_OP = ".here"; private const string HERE_PSEUDO_OP = ".here";
@ -360,7 +359,12 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr); OutputDenseHex(offset, length, labelStr, commentStr);
break; break;
case FormatDescriptor.Type.String: case FormatDescriptor.Type.StringGeneric:
case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.Type.StringNullTerm:
case FormatDescriptor.Type.StringL8:
case FormatDescriptor.Type.StringL16:
case FormatDescriptor.Type.StringDci:
multiLine = true; multiLine = true;
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputString(offset, labelStr, commentStr); OutputString(offset, labelStr, commentStr);
@ -525,7 +529,7 @@ namespace SourceGen.AsmGen {
Anattrib attr = Project.GetAnattrib(offset); Anattrib attr = Project.GetAnattrib(offset);
FormatDescriptor dfd = attr.DataDescriptor; FormatDescriptor dfd = attr.DataDescriptor;
Debug.Assert(dfd != null); Debug.Assert(dfd != null);
Debug.Assert(dfd.FormatType == FormatDescriptor.Type.String); Debug.Assert(dfd.IsString);
Debug.Assert(dfd.Length > 0); Debug.Assert(dfd.Length > 0);
bool highAscii = false; bool highAscii = false;
@ -534,36 +538,31 @@ namespace SourceGen.AsmGen {
bool showLeading = false; bool showLeading = false;
bool showTrailing = false; bool showTrailing = false;
switch (dfd.FormatSubType) { switch (dfd.FormatType) {
case FormatDescriptor.SubType.None: case FormatDescriptor.Type.StringGeneric:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
break; break;
case FormatDescriptor.SubType.Dci: case FormatDescriptor.Type.StringDci:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
trailingBytes = 1; trailingBytes = 1;
showTrailing = true; showTrailing = true;
break; break;
case FormatDescriptor.SubType.Reverse: case FormatDescriptor.Type.StringReverse:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
break; break;
case FormatDescriptor.SubType.DciReverse: case FormatDescriptor.Type.StringNullTerm:
highAscii = (data[offset + dfd.Length - 1] & 0x80) != 0;
leadingBytes = 1;
showLeading = true;
break;
case FormatDescriptor.SubType.CString:
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
trailingBytes = 1; trailingBytes = 1;
showTrailing = true; showTrailing = true;
break; break;
case FormatDescriptor.SubType.L8String: case FormatDescriptor.Type.StringL8:
if (dfd.Length > 1) { if (dfd.Length > 1) {
highAscii = (data[offset + 1] & 0x80) != 0; highAscii = (data[offset + 1] & 0x80) != 0;
} }
leadingBytes = 1; leadingBytes = 1;
showLeading = true; showLeading = true;
break; break;
case FormatDescriptor.SubType.L16String: case FormatDescriptor.Type.StringL16:
if (dfd.Length > 2) { if (dfd.Length > 2) {
highAscii = (data[offset + 2] & 0x80) != 0; highAscii = (data[offset + 2] & 0x80) != 0;
} }
@ -591,28 +590,27 @@ namespace SourceGen.AsmGen {
string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.StrGeneric); string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.StrGeneric);
switch (dfd.FormatSubType) { switch (dfd.FormatType) {
case FormatDescriptor.SubType.None: case FormatDescriptor.Type.StringGeneric:
// TODO(someday): something fancy with encodings to handle high-ASCII text? // TODO(someday): something fancy with encodings to handle high-ASCII text?
break; break;
case FormatDescriptor.SubType.Dci: case FormatDescriptor.Type.StringDci:
case FormatDescriptor.SubType.Reverse: case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.SubType.DciReverse:
// Fully configured above. // Fully configured above.
break; break;
case FormatDescriptor.SubType.CString: case FormatDescriptor.Type.StringNullTerm:
if (gath.NumLinesOutput == 1 && !gath.HasDelimiter) { if (gath.NumLinesOutput == 1 && !gath.HasDelimiter) {
opcodeStr = sDataOpNames.StrNullTerm; opcodeStr = sDataOpNames.StrNullTerm;
showTrailing = false; showTrailing = false;
} }
break; break;
case FormatDescriptor.SubType.L8String: case FormatDescriptor.Type.StringL8:
if (gath.NumLinesOutput == 1 && !gath.HasDelimiter) { if (gath.NumLinesOutput == 1 && !gath.HasDelimiter) {
opcodeStr = sDataOpNames.StrLen8; opcodeStr = sDataOpNames.StrLen8;
showLeading = false; showLeading = false;
} }
break; break;
case FormatDescriptor.SubType.L16String: case FormatDescriptor.Type.StringL16:
// Implement as macro? // Implement as macro?
break; break;
default: default:

View File

@ -1047,7 +1047,7 @@ namespace SourceGen {
return FormatDescriptor.Type.NumericBE; return FormatDescriptor.Type.NumericBE;
case DataType.Dense: case DataType.Dense:
return FormatDescriptor.Type.Dense; return FormatDescriptor.Type.Dense;
case DataType.String: case DataType.StringGeneric:
case DataType.Fill: case DataType.Fill:
default: default:
// not appropriate for operands, or inline data (?) // not appropriate for operands, or inline data (?)
@ -1068,8 +1068,6 @@ namespace SourceGen {
return FormatDescriptor.SubType.Decimal; return FormatDescriptor.SubType.Decimal;
case DataSubType.Binary: case DataSubType.Binary:
return FormatDescriptor.SubType.Binary; return FormatDescriptor.SubType.Binary;
case DataSubType.Ascii:
return FormatDescriptor.SubType.Ascii;
case DataSubType.Address: case DataSubType.Address:
return FormatDescriptor.SubType.Address; return FormatDescriptor.SubType.Address;
case DataSubType.Symbol: case DataSubType.Symbol:

View File

@ -737,7 +737,7 @@ namespace SourceGen {
if (asciiLen >= minStringChars) { if (asciiLen >= minStringChars) {
LogV(start, "ASCII string, len=" + asciiLen + " bytes"); LogV(start, "ASCII string, len=" + asciiLen + " bytes");
mAnattribs[start].DataDescriptor = FormatDescriptor.Create(asciiLen, mAnattribs[start].DataDescriptor = FormatDescriptor.Create(asciiLen,
FormatDescriptor.Type.String, FormatDescriptor.SubType.None); FormatDescriptor.Type.StringGeneric, FormatDescriptor.SubType.Ascii);
start += asciiLen; start += asciiLen;
continue; continue;
} }

View File

@ -34,27 +34,35 @@ namespace SourceGen {
/// </summary> /// </summary>
public class FormatDescriptor { public class FormatDescriptor {
/// <summary> /// <summary>
/// General data type. /// General data type. Generally corresponds to the pseudo-opcode.
/// ///
/// The UI only allows big-endian values in certain situations. Internally we want /// The UI only allows big-endian values in certain situations. Internally we want
/// to be orthogonal in case the policy changes. /// to be orthogonal in case the policy changes.
/// </summary> /// </summary>
public enum Type : byte { public enum Type : byte {
Unknown = 0, Unknown = 0,
REMOVE, // special type, only used by operand editor REMOVE, // special type, only used by operand editor
Default, // means "unformatted", same effect as not having a FormatDescriptor Default, // means "unformatted", same as not having a FormatDescriptor
NumericLE, // 1-4 byte number, little-endian
NumericBE, // 1-4 byte number, big-endian NumericLE, // 1-4 byte number, little-endian
String, // character string NumericBE, // 1-4 byte number, big-endian
Dense, // raw data, represented as compactly as possible
Fill // fill memory with a value StringGeneric, // character string
StringReverse, // character string, in reverse order
StringNullTerm, // C-style null-terminated string
StringL8, // string with 8-bit length prefix
StringL16, // string with 16-bit length prefix
StringDci, // string terminated by flipped high bit (Dextral Char Inverted)
Dense, // raw data, represented as compactly as possible
Fill // fill memory with a value
} }
/// <summary> /// <summary>
/// Additional data type detail. /// Additional data type detail. Generally affects the operand.
/// ///
/// Some things are extracted from the data itself, e.g. we don't need to specify if /// Some things are extracted from the data itself, e.g. we don't need to specify
/// a string is high- or low-ASCII, or what value to use for Fill. /// what value to use for Fill.
/// </summary> /// </summary>
public enum SubType : byte { public enum SubType : byte {
None = 0, None = 0,
@ -63,22 +71,18 @@ namespace SourceGen {
Hex, Hex,
Decimal, Decimal,
Binary, Binary,
Ascii, // aspirational; falls back on hex if data not suited Address, // wants to be an address, but no symbol defined
Address, // wants to be an address, but no symbol defined Symbol, // symbolic ref; replace with Expression, someday?
Symbol, // symbolic ref; replace with Expression, someday?
// String; default is straight text // Strings and NumericLE/BE (single character)
Reverse, // plain ASCII in reverse order Ascii, // ASCII (with or without the high bit set)
CString, // null-terminated C64Petscii, // C64 PETSCII
L8String, // 8-bit length prefix C64Screen, // C64 screen code
L16String, // 16-bit length prefix
Dci, // Dextral Character Inverted
DciReverse, // DCI with string backwards [deprecated -- no asm supports this]
// Dense; no sub-types // Dense; no sub-types
// Fill; default is non-ignore // Fill; default is non-ignore
Ignore // TODO(someday): use this for "don't care" sections Ignore // TODO(someday): use this for "don't care" sections
} }
private const int MAX_NUMERIC_LEN = 4; private const int MAX_NUMERIC_LEN = 4;
@ -264,6 +268,25 @@ namespace SourceGen {
} }
} }
/// <summary>
/// True if the FormatDescriptor is a string type.
/// </summary>
public bool IsString {
get {
switch (FormatType) {
case Type.StringGeneric:
case Type.StringReverse:
case Type.StringNullTerm:
case Type.StringL8:
case Type.StringL16:
case Type.StringDci:
return true;
default:
return false;
}
}
}
/// <summary> /// <summary>
/// True if the FormatDescriptor has a symbol or is Numeric/Address. /// True if the FormatDescriptor has a symbol or is Numeric/Address.
/// </summary> /// </summary>
@ -320,6 +343,48 @@ namespace SourceGen {
/// </summary> /// </summary>
public string ToUiString() { public string ToUiString() {
// NOTE: this should be made easier to localize // NOTE: this should be made easier to localize
if (IsString) {
string descr;
switch (FormatSubType) {
case SubType.Ascii:
descr = "ASCII";
break;
case SubType.C64Petscii:
descr = "C64 PETSCII";
break;
case SubType.C64Screen:
descr = "C64 Screen";
break;
default:
descr = "???";
break;
}
switch (FormatType) {
case Type.StringGeneric:
descr += " string";
break;
case Type.StringReverse:
descr += " string (reverse)";
break;
case Type.StringNullTerm:
descr += " string (null term)";
break;
case Type.StringL8:
descr += " string (1-byte len)";
break;
case Type.StringL16:
descr += " string (2-byte len)";
break;
case Type.StringDci:
descr += " string (DCI)";
break;
default:
descr += " ???";
break;
}
}
switch (FormatSubType) { switch (FormatSubType) {
case SubType.None: case SubType.None:
switch (FormatType) { switch (FormatType) {
@ -328,13 +393,12 @@ namespace SourceGen {
return "Numeric (little-endian)"; return "Numeric (little-endian)";
case Type.NumericBE: case Type.NumericBE:
return "Numeric (big-endian)"; return "Numeric (big-endian)";
case Type.String:
return "String (generic)";
case Type.Dense: case Type.Dense:
return "Dense"; return "Dense";
case Type.Fill: case Type.Fill:
return "Fill"; return "Fill";
default: default:
// strings handled earlier
return "???"; return "???";
} }
case SubType.Hex: case SubType.Hex:
@ -343,25 +407,16 @@ namespace SourceGen {
return "Numeric, Decimal"; return "Numeric, Decimal";
case SubType.Binary: case SubType.Binary:
return "Numeric, Binary"; return "Numeric, Binary";
case SubType.Ascii:
return "ASCII";
case SubType.Address: case SubType.Address:
return "Address"; return "Address";
case SubType.Symbol: case SubType.Symbol:
return "Symbol \"" + SymbolRef.Label + "\""; return "Symbol \"" + SymbolRef.Label + "\"";
case SubType.Ascii:
case SubType.Reverse: return "ASCII";
return "String (reverse)"; case SubType.C64Petscii:
case SubType.CString: return "C64 PETSCII";
return "String (null-term)"; case SubType.C64Screen:
case SubType.L8String: return "C64 Screen";
return "String (1-byte len)";
case SubType.L16String:
return "String (2-byte len)";
case SubType.Dci:
return "String (DCI)";
case SubType.DciReverse:
return "String (RevDCI)";
default: default:
return "???"; return "???";

View File

@ -52,7 +52,7 @@ namespace SourceGen {
// ignore stuff that's in one side but not the other. However, if we're opening a // ignore stuff that's in one side but not the other. However, if we're opening a
// newer file in an older program, it's worth letting the user know that some stuff // newer file in an older program, it's worth letting the user know that some stuff
// may get lost as soon as they save the file. // may get lost as soon as they save the file.
public const int CONTENT_VERSION = 1; public const int CONTENT_VERSION = 2;
private static readonly bool ADD_CRLF = true; private static readonly bool ADD_CRLF = true;
@ -669,6 +669,33 @@ namespace SourceGen {
dfd = null; dfd = null;
FormatDescriptor.Type format; FormatDescriptor.Type format;
FormatDescriptor.SubType subFormat; FormatDescriptor.SubType subFormat;
// File version 1 used a different set of enumerated values for defining strings.
// Parse it out here.
if ("String".Equals(sfd.Format)) {
subFormat = FormatDescriptor.SubType.Ascii;
if ("None".Equals(sfd.SubFormat)) {
format = FormatDescriptor.Type.StringGeneric;
} else if ("Reverse".Equals(sfd.SubFormat)) {
format = FormatDescriptor.Type.StringReverse;
} else if ("CString".Equals(sfd.SubFormat)) {
format = FormatDescriptor.Type.StringNullTerm;
} else if ("L8String".Equals(sfd.SubFormat)) {
format = FormatDescriptor.Type.StringL8;
} else if ("L16String".Equals(sfd.SubFormat)) {
format = FormatDescriptor.Type.StringL16;
} else if ("Dci".Equals(sfd.SubFormat)) {
format = FormatDescriptor.Type.StringDci;
} else {
// DciReverse no longer supported; output as dense hex
format = FormatDescriptor.Type.Dense;
subFormat = FormatDescriptor.SubType.None;
}
Debug.WriteLine("Found v1 string, fmt=" + format + ", sub=" + subFormat);
dfd = FormatDescriptor.Create(sfd.Length, format, subFormat);
return true;
}
try { try {
format = (FormatDescriptor.Type)Enum.Parse( format = (FormatDescriptor.Type)Enum.Parse(
typeof(FormatDescriptor.Type), sfd.Format); typeof(FormatDescriptor.Type), sfd.Format);

View File

@ -87,8 +87,6 @@ namespace SourceGen {
public string StrNullTermHi { get; set; } public string StrNullTermHi { get; set; }
public string StrDci { get; set; } public string StrDci { get; set; }
public string StrDciHi { get; set; } public string StrDciHi { get; set; }
public string StrDciReverse { get; set; }
public string StrDciReverseHi { get; set; }
public string GetDefineData(int width) { public string GetDefineData(int width) {
switch (width) { switch (width) {
@ -180,8 +178,6 @@ namespace SourceGen {
StrNullTermHi = ".zstrh", StrNullTermHi = ".zstrh",
StrDci = ".dstr", StrDci = ".dstr",
StrDciHi = ".dstrh", StrDciHi = ".dstrh",
StrDciReverse = ".rdstr",
StrDciReverseHi = ".rdstrh",
}; };
@ -192,6 +188,37 @@ namespace SourceGen {
/// <param name="dfd">Data format descriptor.</param> /// <param name="dfd">Data format descriptor.</param>
/// <returns>Line count.</returns> /// <returns>Line count.</returns>
public static int ComputeRequiredLineCount(Formatter formatter, FormatDescriptor dfd) { public static int ComputeRequiredLineCount(Formatter formatter, FormatDescriptor dfd) {
if (dfd.IsString) {
// Subtract two chars, to leave room for start/end delimiter. We use
// non-ASCII delimiters on-screen, so there's nothing to escape there.
int maxLen = MAX_OPERAND_LEN - 2;
// Remove leading length or trailing null byte from string length.
int textLen = dfd.Length;
switch (dfd.FormatType) {
case FormatDescriptor.Type.StringGeneric:
case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.Type.StringDci:
break;
case FormatDescriptor.Type.StringNullTerm:
case FormatDescriptor.Type.StringL8:
textLen--;
break;
case FormatDescriptor.Type.StringL16:
textLen -= 2;
break;
default:
Debug.Assert(false);
break;
}
int strLen = (textLen + maxLen - 1) / maxLen;
if (strLen == 0) {
// Empty string, but we still need to output a line.
strLen = 1;
}
return strLen;
}
switch (dfd.FormatType) { switch (dfd.FormatType) {
case FormatDescriptor.Type.Default: case FormatDescriptor.Type.Default:
case FormatDescriptor.Type.NumericLE: case FormatDescriptor.Type.NumericLE:
@ -204,37 +231,6 @@ namespace SourceGen {
int textLen = dfd.Length * 2; int textLen = dfd.Length * 2;
return (textLen + maxLen - 1) / maxLen; return (textLen + maxLen - 1) / maxLen;
} }
case FormatDescriptor.Type.String: {
// Subtract two chars, to leave room for start/end delimiter. We use
// non-ASCII delimiters on-screen, so there's nothing to escape there.
int maxLen = MAX_OPERAND_LEN - 2;
// Remove leading length or trailing null byte from string length.
int textLen = dfd.Length;
switch (dfd.FormatSubType) {
case FormatDescriptor.SubType.None:
case FormatDescriptor.SubType.Dci:
case FormatDescriptor.SubType.Reverse:
case FormatDescriptor.SubType.DciReverse:
break;
case FormatDescriptor.SubType.CString:
case FormatDescriptor.SubType.L8String:
textLen--;
break;
case FormatDescriptor.SubType.L16String:
textLen -= 2;
break;
default:
Debug.Assert(false);
break;
}
int strLen = (textLen + maxLen - 1) / maxLen;
if (strLen == 0) {
// Empty string, but we still need to output a line.
strLen = 1;
}
return strLen;
}
default: default:
Debug.Assert(false); Debug.Assert(false);
return 1; return 1;
@ -274,89 +270,94 @@ namespace SourceGen {
// multi-line items. // multi-line items.
PseudoOut po = new PseudoOut(); PseudoOut po = new PseudoOut();
switch (dfd.FormatType) { if (dfd.IsString) {
case FormatDescriptor.Type.Default: // It's hard to do strings in single-line pieces because of prefix lengths,
if (length != 1) { // terminating nulls, DCI polarity, and reverse-order strings. We
// This shouldn't happen. // really just want to convert the whole thing to a run of chars
Debug.Assert(false); // and then pull out a chunk. As an optimization we can handle
length = 1; // generic strings more efficiently, which should help if auto-analysis is
// creating massive strings (at least until auto-analysis learns how to do
// more complex things).
//
// TODO: consider storing the full string on the first line, then each
// subsequent line has a reference to it with offset+length
if (dfd.FormatType == FormatDescriptor.Type.StringGeneric) {
int maxPerLine = MAX_OPERAND_LEN - 2;
offset += subIndex * maxPerLine;
length -= subIndex * maxPerLine;
if (length > maxPerLine) {
length = maxPerLine;
} }
po.Opcode = opNames.GetDefineData(length); char[] ltext = BytesToChars(formatter, opNames, dfd.FormatType, data,
int operand = RawData.GetWord(data, offset, length, false); offset, length, out string lpopcode, out int unused);
po.Operand = formatter.FormatHexValue(operand, length * 2); po.Opcode = lpopcode;
break; po.Operand = "\u201c" + new string(ltext) + "\u201d";
case FormatDescriptor.Type.NumericLE: } else {
po.Opcode = opNames.GetDefineData(length); char[] text = BytesToChars(formatter, opNames, dfd.FormatType, data,
operand = RawData.GetWord(data, offset, length, false); offset, length, out string popcode, out int showHexZeroes);
po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd,
operand, length, FormatNumericOpFlags.None);
break;
case FormatDescriptor.Type.NumericBE:
po.Opcode = opNames.GetDefineBigData(length);
operand = RawData.GetWord(data, offset, length, true);
po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd,
operand, length, FormatNumericOpFlags.None);
break;
case FormatDescriptor.Type.Fill:
po.Opcode = opNames.Fill;
po.Operand = length + "," + formatter.FormatHexValue(data[offset], 2);
break;
case FormatDescriptor.Type.Dense: {
int maxPerLine = MAX_OPERAND_LEN / 2;
offset += subIndex * maxPerLine;
length -= subIndex * maxPerLine;
if (length > maxPerLine) {
length = maxPerLine;
}
po.Opcode = opNames.Dense;
po.Operand = formatter.FormatDenseHex(data, offset, length);
//List<PseudoOut> outList = new List<PseudoOut>();
//GenerateTextLines(text, "", "", po, outList);
//po = outList[subIndex];
}
break;
case FormatDescriptor.Type.String:
// It's hard to do strings in single-line pieces because of prefix lengths,
// terminating nulls, DCI polarity, and reverse-order strings. We
// really just want to convert the whole thing to a run of chars
// and then pull out a chunk. As an optimization we can handle
// generic strings (subtype=None) more efficiently, which should solve
// the problem of massive strings created by auto-analysis.
if (dfd.FormatSubType == FormatDescriptor.SubType.None) {
int maxPerLine = MAX_OPERAND_LEN - 2;
offset += subIndex * maxPerLine;
length -= subIndex * maxPerLine;
if (length > maxPerLine) {
length = maxPerLine;
}
char[] ltext = BytesToChars(formatter, opNames, dfd.FormatSubType, data,
offset, length, out string lpopcode, out int unused);
po.Opcode = lpopcode;
po.Operand = "\u201c" + new string(ltext) + "\u201d";
} else {
char[] text = BytesToChars(formatter, opNames, dfd.FormatSubType, data,
offset, length, out string popcode, out int showHexZeroes);
if (showHexZeroes == 1) { if (showHexZeroes == 1) {
po.Opcode = opNames.DefineData1; po.Opcode = opNames.DefineData1;
po.Operand = formatter.FormatHexValue(0, 2); po.Operand = formatter.FormatHexValue(0, 2);
} else if (showHexZeroes == 2) { } else if (showHexZeroes == 2) {
po.Opcode = opNames.DefineData2; po.Opcode = opNames.DefineData2;
po.Operand = formatter.FormatHexValue(0, 4); po.Operand = formatter.FormatHexValue(0, 4);
} else { } else {
Debug.Assert(showHexZeroes == 0); Debug.Assert(showHexZeroes == 0);
po.Opcode = popcode; po.Opcode = popcode;
List<PseudoOut> outList = new List<PseudoOut>(); List<PseudoOut> outList = new List<PseudoOut>();
GenerateTextLines(text, "\u201c", "\u201d", po, outList); GenerateTextLines(text, "\u201c", "\u201d", po, outList);
po = outList[subIndex]; po = outList[subIndex];
}
} }
break; }
default: } else {
Debug.Assert(false); switch (dfd.FormatType) {
po.Opcode = ".???"; case FormatDescriptor.Type.Default:
po.Operand = "$" + data[offset].ToString("x2"); if (length != 1) {
break; // This shouldn't happen.
Debug.Assert(false);
length = 1;
}
po.Opcode = opNames.GetDefineData(length);
int operand = RawData.GetWord(data, offset, length, false);
po.Operand = formatter.FormatHexValue(operand, length * 2);
break;
case FormatDescriptor.Type.NumericLE:
po.Opcode = opNames.GetDefineData(length);
operand = RawData.GetWord(data, offset, length, false);
po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd,
operand, length, FormatNumericOpFlags.None);
break;
case FormatDescriptor.Type.NumericBE:
po.Opcode = opNames.GetDefineBigData(length);
operand = RawData.GetWord(data, offset, length, true);
po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd,
operand, length, FormatNumericOpFlags.None);
break;
case FormatDescriptor.Type.Fill:
po.Opcode = opNames.Fill;
po.Operand = length + "," + formatter.FormatHexValue(data[offset], 2);
break;
case FormatDescriptor.Type.Dense: {
int maxPerLine = MAX_OPERAND_LEN / 2;
offset += subIndex * maxPerLine;
length -= subIndex * maxPerLine;
if (length > maxPerLine) {
length = maxPerLine;
}
po.Opcode = opNames.Dense;
po.Operand = formatter.FormatDenseHex(data, offset, length);
//List<PseudoOut> outList = new List<PseudoOut>();
//GenerateTextLines(text, "", "", po, outList);
//po = outList[subIndex];
}
break;
default:
Debug.Assert(false);
po.Opcode = ".???";
po.Operand = "$" + data[offset].ToString("x2");
break;
}
} }
return po; return po;
@ -368,7 +369,7 @@ namespace SourceGen {
/// are not shown. /// are not shown.
/// </summary> /// </summary>
/// <param name="formatter">Formatter object.</param> /// <param name="formatter">Formatter object.</param>
/// <param name="subType">String sub-type.</param> /// <param name="formatType">String layout.</param>
/// <param name="data">File data.</param> /// <param name="data">File data.</param>
/// <param name="offset">Offset, within data, of start of string.</param> /// <param name="offset">Offset, within data, of start of string.</param>
/// <param name="length">Number of bytes to convert.</param> /// <param name="length">Number of bytes to convert.</param>
@ -377,7 +378,7 @@ namespace SourceGen {
/// length or null-termination) instead of an empty string.</param> /// length or null-termination) instead of an empty string.</param>
/// <returns>Array of characters with string data.</returns> /// <returns>Array of characters with string data.</returns>
private static char[] BytesToChars(Formatter formatter, PseudoOpNames opNames, private static char[] BytesToChars(Formatter formatter, PseudoOpNames opNames,
FormatDescriptor.SubType subType, byte[] data, int offset, int length, FormatDescriptor.Type formatType, byte[] data, int offset, int length,
out string popcode, out int showHexZeroes) { out string popcode, out int showHexZeroes) {
Debug.Assert(length > 0); Debug.Assert(length > 0);
@ -389,32 +390,25 @@ namespace SourceGen {
showHexZeroes = 0; showHexZeroes = 0;
switch (subType) { switch (formatType) {
case FormatDescriptor.SubType.None: case FormatDescriptor.Type.StringGeneric:
// High or low ASCII, full width specified by formatter. // High or low ASCII, full width specified by formatter.
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
popcode = highAscii ? opNames.StrGenericHi : opNames.StrGeneric; popcode = highAscii ? opNames.StrGenericHi : opNames.StrGeneric;
break; break;
case FormatDescriptor.SubType.Dci: case FormatDescriptor.Type.StringDci:
// High or low ASCII, full width specified by formatter. // High or low ASCII, full width specified by formatter.
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
popcode = highAscii ? opNames.StrDciHi : opNames.StrDci; popcode = highAscii ? opNames.StrDciHi : opNames.StrDci;
break; break;
case FormatDescriptor.SubType.Reverse: case FormatDescriptor.Type.StringReverse:
// High or low ASCII, full width specified by formatter. Show characters // High or low ASCII, full width specified by formatter. Show characters
// in reverse order. // in reverse order.
highAscii = (data[offset + strLen - 1] & 0x80) != 0; highAscii = (data[offset + strLen - 1] & 0x80) != 0;
popcode = highAscii ? opNames.StrReverseHi : opNames.StrReverse; popcode = highAscii ? opNames.StrReverseHi : opNames.StrReverse;
reverse = true; reverse = true;
break; break;
case FormatDescriptor.SubType.DciReverse: case FormatDescriptor.Type.StringNullTerm:
// High or low ASCII, full width specified by formatter. Show characters
// in reverse order.
highAscii = (data[offset + strLen - 1] & 0x80) != 0;
popcode = highAscii ? opNames.StrDciReverseHi : opNames.StrDciReverse;
reverse = true;
break;
case FormatDescriptor.SubType.CString:
// High or low ASCII, with a terminating null. Don't show the null. If // High or low ASCII, with a terminating null. Don't show the null. If
// it's an empty string, just show the null byte as hex. // it's an empty string, just show the null byte as hex.
highAscii = (data[offset] & 0x80) != 0; highAscii = (data[offset] & 0x80) != 0;
@ -424,7 +418,7 @@ namespace SourceGen {
showHexZeroes = 1; showHexZeroes = 1;
} }
break; break;
case FormatDescriptor.SubType.L8String: case FormatDescriptor.Type.StringL8:
// High or low ASCII, with a leading length byte. Don't show the null. // High or low ASCII, with a leading length byte. Don't show the null.
// If it's an empty string, just show the length byte as hex. // If it's an empty string, just show the length byte as hex.
strOffset++; strOffset++;
@ -436,7 +430,7 @@ namespace SourceGen {
} }
popcode = highAscii ? opNames.StrLen8Hi : opNames.StrLen8; popcode = highAscii ? opNames.StrLen8Hi : opNames.StrLen8;
break; break;
case FormatDescriptor.SubType.L16String: case FormatDescriptor.Type.StringL16:
// High or low ASCII, with a leading length word. Don't show the null. // High or low ASCII, with a leading length word. Don't show the null.
// If it's an empty string, just show the length word as hex. // If it's an empty string, just show the length word as hex.
Debug.Assert(strLen > 1); Debug.Assert(strLen > 1);
@ -455,6 +449,7 @@ namespace SourceGen {
break; break;
} }
// TODO(petscii): convert character encoding
char[] text = new char[strLen]; char[] text = new char[strLen];
if (!reverse) { if (!reverse) {
for (int i = 0; i < strLen; i++) { for (int i = 0; i < strLen; i++) {
@ -547,6 +542,10 @@ namespace SourceGen {
case FormatDescriptor.SubType.Binary: case FormatDescriptor.SubType.Binary:
return formatter.FormatBinaryValue(operandValue, hexMinLen * 4); return formatter.FormatBinaryValue(operandValue, hexMinLen * 4);
case FormatDescriptor.SubType.Ascii: case FormatDescriptor.SubType.Ascii:
case FormatDescriptor.SubType.C64Petscii:
case FormatDescriptor.SubType.C64Screen:
// TODO(petscii): convert encoding; use a helper function *not* in
// formatter -- pass converted char value in along with operandValue
return formatter.FormatAsciiOrHex(operandValue); return formatter.FormatAsciiOrHex(operandValue);
case FormatDescriptor.SubType.Symbol: case FormatDescriptor.SubType.Symbol:
if (symbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) { if (symbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) {

View File

@ -528,6 +528,9 @@ namespace SourceGen.WpfGui {
radioSimpleDataBinary.IsChecked = true; radioSimpleDataBinary.IsChecked = true;
break; break;
case FormatDescriptor.SubType.Ascii: case FormatDescriptor.SubType.Ascii:
case FormatDescriptor.SubType.C64Petscii:
case FormatDescriptor.SubType.C64Screen:
// TODO(petscii): update UI
radioSimpleDataAscii.IsChecked = true; radioSimpleDataAscii.IsChecked = true;
break; break;
case FormatDescriptor.SubType.Address: case FormatDescriptor.SubType.Address:
@ -560,34 +563,23 @@ namespace SourceGen.WpfGui {
// preferred format not enabled; leave Hex/Low checked // preferred format not enabled; leave Hex/Low checked
} }
break; break;
case FormatDescriptor.Type.String: case FormatDescriptor.Type.StringGeneric:
switch (dfd.FormatSubType) { preferredFormat = radioStringMixed;
case FormatDescriptor.SubType.None: break;
preferredFormat = radioStringMixed; case FormatDescriptor.Type.StringReverse:
break; preferredFormat = radioStringMixedReverse;
case FormatDescriptor.SubType.Reverse: break;
preferredFormat = radioStringMixedReverse; case FormatDescriptor.Type.StringNullTerm:
break; preferredFormat = radioStringNullTerm;
case FormatDescriptor.SubType.CString: break;
preferredFormat = radioStringNullTerm; case FormatDescriptor.Type.StringL8:
break; preferredFormat = radioStringLen8;
case FormatDescriptor.SubType.L8String: break;
preferredFormat = radioStringLen8; case FormatDescriptor.Type.StringL16:
break; preferredFormat = radioStringLen16;
case FormatDescriptor.SubType.L16String: break;
preferredFormat = radioStringLen16; case FormatDescriptor.Type.StringDci:
break; preferredFormat = radioStringDci;
case FormatDescriptor.SubType.Dci:
preferredFormat = radioStringDci;
break;
case FormatDescriptor.SubType.DciReverse:
preferredFormat = radioDefaultFormat;
break;
default:
Debug.Assert(false);
preferredFormat = radioDefaultFormat;
break;
}
break; break;
case FormatDescriptor.Type.Dense: case FormatDescriptor.Type.Dense:
preferredFormat = radioDenseHex; preferredFormat = radioDenseHex;
@ -639,6 +631,7 @@ namespace SourceGen.WpfGui {
} else if (radioSimpleDataBinary.IsChecked == true) { } else if (radioSimpleDataBinary.IsChecked == true) {
subType = FormatDescriptor.SubType.Binary; subType = FormatDescriptor.SubType.Binary;
} else if (radioSimpleDataAscii.IsChecked == true) { } else if (radioSimpleDataAscii.IsChecked == true) {
// TODO(petscii): configure subType correctly
subType = FormatDescriptor.SubType.Ascii; subType = FormatDescriptor.SubType.Ascii;
} else if (radioSimpleDataAddress.IsChecked == true) { } else if (radioSimpleDataAddress.IsChecked == true) {
subType = FormatDescriptor.SubType.Address; subType = FormatDescriptor.SubType.Address;
@ -688,26 +681,25 @@ namespace SourceGen.WpfGui {
type = FormatDescriptor.Type.Dense; type = FormatDescriptor.Type.Dense;
} else if (radioFill.IsChecked == true) { } else if (radioFill.IsChecked == true) {
type = FormatDescriptor.Type.Fill; type = FormatDescriptor.Type.Fill;
subType = FormatDescriptor.SubType.Ascii; // TODO(petscii): set encoding
} else if (radioStringMixed.IsChecked == true) { } else if (radioStringMixed.IsChecked == true) {
type = FormatDescriptor.Type.String; type = FormatDescriptor.Type.StringGeneric;
subType = FormatDescriptor.SubType.Ascii;
} else if (radioStringMixedReverse.IsChecked == true) { } else if (radioStringMixedReverse.IsChecked == true) {
type = FormatDescriptor.Type.String; type = FormatDescriptor.Type.StringReverse;
subType = FormatDescriptor.SubType.Reverse; subType = FormatDescriptor.SubType.Ascii;
} else if (radioStringNullTerm.IsChecked == true) { } else if (radioStringNullTerm.IsChecked == true) {
type = FormatDescriptor.Type.String; type = FormatDescriptor.Type.StringNullTerm;
subType = FormatDescriptor.SubType.CString; subType = FormatDescriptor.SubType.Ascii;
} else if (radioStringLen8.IsChecked == true) { } else if (radioStringLen8.IsChecked == true) {
type = FormatDescriptor.Type.String; type = FormatDescriptor.Type.StringL8;
subType = FormatDescriptor.SubType.L8String; subType = FormatDescriptor.SubType.Ascii;
} else if (radioStringLen16.IsChecked == true) { } else if (radioStringLen16.IsChecked == true) {
type = FormatDescriptor.Type.String; type = FormatDescriptor.Type.StringL16;
subType = FormatDescriptor.SubType.L16String; subType = FormatDescriptor.SubType.Ascii;
} else if (radioStringDci.IsChecked == true) { } else if (radioStringDci.IsChecked == true) {
type = FormatDescriptor.Type.String; type = FormatDescriptor.Type.StringDci;
subType = FormatDescriptor.SubType.Dci; subType = FormatDescriptor.SubType.Ascii;
//} else if (radioStringDciReverse.Checked) {
// type = FormatDescriptor.Type.String;
// subType = FormatDescriptor.SubType.DciReverse;
} else { } else {
Debug.Assert(false); Debug.Assert(false);
// default/none // default/none
@ -720,26 +712,26 @@ namespace SourceGen.WpfGui {
while (iter.MoveNext()) { while (iter.MoveNext()) {
TypedRangeSet.TypedRange rng = iter.Current; TypedRangeSet.TypedRange rng = iter.Current;
if (type == FormatDescriptor.Type.String) { // TODO(petscii): handle encoding on all four calls
// We want to create one FormatDescriptor object per string. That way switch (type) {
// each string gets its own line. case FormatDescriptor.Type.StringGeneric:
if ((subType == FormatDescriptor.SubType.None || case FormatDescriptor.Type.StringReverse:
subType == FormatDescriptor.SubType.Reverse)) { CreateMixedStringEntries(rng.Low, rng.High, type, subType);
CreateMixedStringEntries(rng.Low, rng.High, subType); break;
} else if (subType == FormatDescriptor.SubType.CString) { case FormatDescriptor.Type.StringNullTerm:
CreateCStringEntries(rng.Low, rng.High, subType); CreateCStringEntries(rng.Low, rng.High, type, subType);
} else if (subType == FormatDescriptor.SubType.L8String || break;
subType == FormatDescriptor.SubType.L16String) { case FormatDescriptor.Type.StringL8:
CreateLengthStringEntries(rng.Low, rng.High, subType); case FormatDescriptor.Type.StringL16:
} else if (subType == FormatDescriptor.SubType.Dci || CreateLengthStringEntries(rng.Low, rng.High, type, subType);
subType == FormatDescriptor.SubType.DciReverse) { break;
CreateDciStringEntries(rng.Low, rng.High, subType); case FormatDescriptor.Type.StringDci:
} else { CreateDciStringEntries(rng.Low, rng.High, type, subType);
Debug.Assert(false); break;
CreateMixedStringEntries(rng.Low, rng.High, subType); // shrug default:
} CreateSimpleEntries(type, subType, chunkLength, symbolRef,
} else { rng.Low, rng.High);
CreateSimpleEntries(type, subType, chunkLength, symbolRef, rng.Low, rng.High); break;
} }
} }
} }
@ -793,7 +785,7 @@ namespace SourceGen.WpfGui {
/// <param name="low">Offset of first byte in range.</param> /// <param name="low">Offset of first byte in range.</param>
/// <param name="high">Offset of last byte in range.</param> /// <param name="high">Offset of last byte in range.</param>
/// <param name="subType">String sub-type.</param> /// <param name="subType">String sub-type.</param>
private void CreateMixedStringEntries(int low, int high, private void CreateMixedStringEntries(int low, int high, FormatDescriptor.Type type,
FormatDescriptor.SubType subType) { FormatDescriptor.SubType subType) {
int stringStart = -1; int stringStart = -1;
int highBit = 0; int highBit = 0;
@ -846,11 +838,12 @@ namespace SourceGen.WpfGui {
Debug.Assert(length > 0); Debug.Assert(length > 0);
if (length == 1) { if (length == 1) {
// single byte, output as single ASCII char rather than 1-byte string // single byte, output as single ASCII char rather than 1-byte string
// TODO(petscii): low/high?
CreateByteFD(offset, FormatDescriptor.SubType.Ascii); CreateByteFD(offset, FormatDescriptor.SubType.Ascii);
} else { } else {
FormatDescriptor dfd; FormatDescriptor dfd;
dfd = FormatDescriptor.Create(length, dfd = FormatDescriptor.Create(length,
FormatDescriptor.Type.String, subType); FormatDescriptor.Type.StringGeneric, subType);
Results.Add(offset, dfd); Results.Add(offset, dfd);
} }
} }
@ -873,14 +866,14 @@ namespace SourceGen.WpfGui {
/// <param name="low">Offset of first byte in range.</param> /// <param name="low">Offset of first byte in range.</param>
/// <param name="high">Offset of last byte in range.</param> /// <param name="high">Offset of last byte in range.</param>
/// <param name="subType">String sub-type.</param> /// <param name="subType">String sub-type.</param>
private void CreateCStringEntries(int low, int high, private void CreateCStringEntries(int low, int high, FormatDescriptor.Type type,
FormatDescriptor.SubType subType) { FormatDescriptor.SubType subType) {
int startOffset = low; int startOffset = low;
for (int i = low; i <= high; i++) { for (int i = low; i <= high; i++) {
if (mFileData[i] == 0x00) { if (mFileData[i] == 0x00) {
// End of string. Zero-length strings are allowed. // End of string. Zero-length strings are allowed.
FormatDescriptor dfd = FormatDescriptor.Create( FormatDescriptor dfd = FormatDescriptor.Create(
i - startOffset + 1, FormatDescriptor.Type.String, subType); i - startOffset + 1, type, subType);
Results.Add(startOffset, dfd); Results.Add(startOffset, dfd);
startOffset = i + 1; startOffset = i + 1;
} else { } else {
@ -899,20 +892,19 @@ namespace SourceGen.WpfGui {
/// <param name="low">Offset of first byte in range.</param> /// <param name="low">Offset of first byte in range.</param>
/// <param name="high">Offset of last byte in range.</param> /// <param name="high">Offset of last byte in range.</param>
/// <param name="subType">String sub-type.</param> /// <param name="subType">String sub-type.</param>
private void CreateLengthStringEntries(int low, int high, private void CreateLengthStringEntries(int low, int high, FormatDescriptor.Type type,
FormatDescriptor.SubType subType) { FormatDescriptor.SubType subType) {
int i; int i;
for (i = low; i <= high;) { for (i = low; i <= high;) {
int length = mFileData[i]; int length = mFileData[i];
if (subType == FormatDescriptor.SubType.L16String) { if (type == FormatDescriptor.Type.StringL16) {
length |= mFileData[i + 1] << 8; length |= mFileData[i + 1] << 8;
length += 2; length += 2;
} else { } else {
length++; length++;
} }
// Zero-length strings are allowed. // Zero-length strings are allowed.
FormatDescriptor dfd = FormatDescriptor.Create(length, FormatDescriptor dfd = FormatDescriptor.Create(length, type, subType);
FormatDescriptor.Type.String, subType);
Results.Add(i, dfd); Results.Add(i, dfd);
i += length; i += length;
} }
@ -927,36 +919,25 @@ namespace SourceGen.WpfGui {
/// <param name="low">Offset of first byte in range.</param> /// <param name="low">Offset of first byte in range.</param>
/// <param name="high">Offset of last byte in range.</param> /// <param name="high">Offset of last byte in range.</param>
/// <param name="subType">String sub-type.</param> /// <param name="subType">String sub-type.</param>
private void CreateDciStringEntries(int low, int high, private void CreateDciStringEntries(int low, int high, FormatDescriptor.Type type,
FormatDescriptor.SubType subType) { FormatDescriptor.SubType subType) {
int start, end, adj, endMask; int end, endMask;
if (subType == FormatDescriptor.SubType.Dci) {
start = low; end = high + 1;
end = high + 1;
adj = 1;
} else if (subType == FormatDescriptor.SubType.DciReverse) {
start = high;
end = low - 1;
adj = -1;
} else {
Debug.Assert(false);
return;
}
// Zero-length strings aren't a thing for DCI. The analyzer requires that all // Zero-length strings aren't a thing for DCI. The analyzer requires that all
// strings in a region have the same polarity, so just grab the last byte. // strings in a region have the same polarity, so just grab the last byte.
endMask = mFileData[end - 1] & 0x80; endMask = mFileData[end - 1] & 0x80;
int stringStart = start; int stringStart = low;
for (int i = start; i != end; i += adj) { for (int i = low; i != end; i++) {
byte val = mFileData[i]; byte val = mFileData[i];
if ((val & 0x80) == endMask) { if ((val & 0x80) == endMask) {
// found the end of a string // found the end of a string
int length = (i - stringStart) * adj + 1; int length = (i - stringStart) + 1;
FormatDescriptor dfd = FormatDescriptor.Create(length, FormatDescriptor dfd = FormatDescriptor.Create(length, type, subType);
FormatDescriptor.Type.String, subType);
Results.Add(stringStart < i ? stringStart : i, dfd); Results.Add(stringStart < i ? stringStart : i, dfd);
stringStart = i + adj; stringStart = i + 1;
} }
} }

View File

@ -333,6 +333,7 @@ namespace SourceGen.WpfGui {
preview.Append(mFormatter.FormatBinaryValue(mOperandValue, 8)); preview.Append(mFormatter.FormatBinaryValue(mOperandValue, 8));
break; break;
case FormatDescriptor.SubType.Ascii: case FormatDescriptor.SubType.Ascii:
// TODO(petscii): encoding
preview.Append(mFormatter.FormatAsciiOrHex(mOperandValue)); preview.Append(mFormatter.FormatAsciiOrHex(mOperandValue));
break; break;
case FormatDescriptor.SubType.Symbol: case FormatDescriptor.SubType.Symbol:
@ -470,6 +471,7 @@ namespace SourceGen.WpfGui {
binaryButton.IsChecked = true; binaryButton.IsChecked = true;
break; break;
case FormatDescriptor.SubType.Ascii: case FormatDescriptor.SubType.Ascii:
// TODO(petscii): encoding
asciiButton.IsChecked = true; asciiButton.IsChecked = true;
break; break;
case FormatDescriptor.SubType.Symbol: case FormatDescriptor.SubType.Symbol:
@ -499,7 +501,13 @@ namespace SourceGen.WpfGui {
} }
break; break;
case FormatDescriptor.Type.NumericBE: case FormatDescriptor.Type.NumericBE:
case FormatDescriptor.Type.String: case FormatDescriptor.Type.StringGeneric:
case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.Type.StringNullTerm:
case FormatDescriptor.Type.StringL8:
case FormatDescriptor.Type.StringL16:
case FormatDescriptor.Type.StringDci:
case FormatDescriptor.Type.Dense:
case FormatDescriptor.Type.Fill: case FormatDescriptor.Type.Fill:
default: default:
// Unexpected; used to be data? // Unexpected; used to be data?
@ -543,6 +551,7 @@ namespace SourceGen.WpfGui {
} else if (binaryButton.IsChecked == true) { } else if (binaryButton.IsChecked == true) {
subType = FormatDescriptor.SubType.Binary; subType = FormatDescriptor.SubType.Binary;
} else if (asciiButton.IsChecked == true) { } else if (asciiButton.IsChecked == true) {
// TODO(petscii): encoding
subType = FormatDescriptor.SubType.Ascii; subType = FormatDescriptor.SubType.Ascii;
} else if (symbolButton.IsChecked == true) { } else if (symbolButton.IsChecked == true) {
subType = FormatDescriptor.SubType.Symbol; subType = FormatDescriptor.SubType.Symbol;