diff --git a/PluginCommon/Interfaces.cs b/PluginCommon/Interfaces.cs index 4014c83..d1e1c45 100644 --- a/PluginCommon/Interfaces.cs +++ b/PluginCommon/Interfaces.cs @@ -448,7 +448,9 @@ namespace PluginCommon { StringL16, StringDci, Dense, - Fill + Fill, + Uninit, + Junk } /// @@ -473,5 +475,7 @@ namespace PluginCommon { HighAscii, C64Petscii, C64Screen + + // skipping alignment sub-types for now } } diff --git a/SourceGen/AsmGen/AsmAcme.cs b/SourceGen/AsmGen/AsmAcme.cs index 3c7c72c..fd67ac6 100644 --- a/SourceGen/AsmGen/AsmAcme.cs +++ b/SourceGen/AsmGen/AsmAcme.cs @@ -136,6 +136,7 @@ namespace SourceGen.AsmGen { //DefineBigData4 { "Fill", "!fill" }, { "Dense", "!hex" }, + { "Uninit", "!skip" }, //Junk { "Align", "!align" }, { "StrGeneric", "!text" }, // can use !xor for high ASCII @@ -458,16 +459,19 @@ namespace SourceGen.AsmGen { opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); break; + case FormatDescriptor.Type.Uninit: case FormatDescriptor.Type.Junk: + bool canAlign = (dfd.FormatType == FormatDescriptor.Type.Junk); int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length); - if (fillVal >= 0 && GenCommon.CheckJunkAlign(offset, dfd, Project.AddrMap)) { + if (canAlign && fillVal >= 0 && + GenCommon.CheckJunkAlign(offset, dfd, Project.AddrMap)) { // !align ANDVALUE, EQUALVALUE [, FILLVALUE] opcodeStr = sDataOpNames.Align; int alignVal = 1 << FormatDescriptor.AlignmentToPower(dfd.FormatSubType); operandStr = (alignVal - 1).ToString() + ",0," + formatter.FormatHexValue(fillVal, 2); - } else if (fillVal >= 0) { - // treat same as Fill + } else if (fillVal >= 0 && (length > 1 || fillVal == 0x00)) { + // If multi-byte, or single byte and zero, treat same as Fill. opcodeStr = sDataOpNames.Fill; operandStr = length + "," + formatter.FormatHexValue(fillVal, 2); } else { diff --git a/SourceGen/AsmGen/AsmCc65.cs b/SourceGen/AsmGen/AsmCc65.cs index 0ad57ba..0306d50 100644 --- a/SourceGen/AsmGen/AsmCc65.cs +++ b/SourceGen/AsmGen/AsmCc65.cs @@ -136,6 +136,7 @@ namespace SourceGen.AsmGen { //DefineBigData4 { "Fill", ".res" }, { "Dense", ".byte" }, // really just just comma-separated bytes + { "Uninit", ".res" }, //Junk { "StrGeneric", ".byte" }, //StrReverse @@ -447,13 +448,14 @@ namespace SourceGen.AsmGen { opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); break; + case FormatDescriptor.Type.Uninit: case FormatDescriptor.Type.Junk: // The ca65 .align directive has a dependency on the alignment of the // segment as a whole. We're not currently declaring multiple segments, // so we can't use .align without generating complaints. int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length); - if (fillVal >= 0) { - // treat same as Fill + if (fillVal >= 0 && (length > 1 || fillVal == 0x00)) { + // If multi-byte, or single byte and zero, treat same as Fill. opcodeStr = sDataOpNames.Fill; operandStr = length + "," + formatter.FormatHexValue(fillVal, 2); } else { diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs index 3a69e67..4e5513b 100644 --- a/SourceGen/AsmGen/AsmMerlin32.cs +++ b/SourceGen/AsmGen/AsmMerlin32.cs @@ -130,6 +130,7 @@ namespace SourceGen.AsmGen { //DefineBigData4 { "Fill", "ds" }, { "Dense", "hex" }, + { "Uninit", "ds" }, //Junk //Align { "StrGeneric", "asc" }, @@ -312,6 +313,7 @@ namespace SourceGen.AsmGen { opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); break; + case FormatDescriptor.Type.Uninit: case FormatDescriptor.Type.Junk: int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length); if (fillVal >= 0) { @@ -324,6 +326,12 @@ namespace SourceGen.AsmGen { } else { operandStr = "\\," + formatter.FormatHexValue(fillVal, 2); } + } else if (length == 1 && fillVal != 0x00) { + // Single-byte HEX looks better than "ds 1,$xx", and will match up + // with adjacent multi-byte junk/uninit. + multiLine = true; + opcodeStr = operandStr = null; + OutputDenseHex(offset, length, labelStr, commentStr); } else { if (fillVal == 0) { operandStr = length.ToString(); diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs index 01c71f1..5c02c34 100644 --- a/SourceGen/AsmGen/AsmTass64.cs +++ b/SourceGen/AsmGen/AsmTass64.cs @@ -155,6 +155,7 @@ namespace SourceGen.AsmGen { //DefineBigData4 { "Fill", ".fill" }, { "Dense", ".byte" }, // not really dense, just comma-separated bytes + { "Uninit", ".fill" }, //Junk { "Align", ".align" }, { "StrGeneric", ".text" }, @@ -552,16 +553,20 @@ namespace SourceGen.AsmGen { opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); break; + case FormatDescriptor.Type.Uninit: + // TODO: use the special syntax for uninit byte/word/dword if possible. case FormatDescriptor.Type.Junk: + bool canAlign = (dfd.FormatType == FormatDescriptor.Type.Junk); int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length); - if (fillVal >= 0 && GenCommon.CheckJunkAlign(offset, dfd, Project.AddrMap)) { + if (canAlign && fillVal >= 0 && + GenCommon.CheckJunkAlign(offset, dfd, Project.AddrMap)) { // .align [, ] opcodeStr = sDataOpNames.Align; int alignVal = 1 << FormatDescriptor.AlignmentToPower(dfd.FormatSubType); operandStr = alignVal.ToString() + "," + formatter.FormatHexValue(fillVal, 2); - } else if (fillVal >= 0) { - // treat same as Fill + } else if (fillVal >= 0 && (length > 1 || fillVal == 0x00)) { + // If multi-byte, or single byte and zero, treat same as Fill. opcodeStr = sDataOpNames.Fill; operandStr = length + "," + formatter.FormatHexValue(fillVal, 2); } else { diff --git a/SourceGen/CodeAnalysis.cs b/SourceGen/CodeAnalysis.cs index 3b584a2..3990d5e 100644 --- a/SourceGen/CodeAnalysis.cs +++ b/SourceGen/CodeAnalysis.cs @@ -1271,6 +1271,8 @@ namespace SourceGen { return FormatDescriptor.Type.StringDci; case DataType.Fill: return FormatDescriptor.Type.Fill; + case DataType.Uninit: + return FormatDescriptor.Type.Uninit; case DataType.Dense: return FormatDescriptor.Type.Dense; default: diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs index 9bb5c8f..c9f4674 100644 --- a/SourceGen/DisasmProject.cs +++ b/SourceGen/DisasmProject.cs @@ -1648,7 +1648,7 @@ namespace SourceGen { if (mAnattribs[symOffset].Symbol != null && mAnattribs[symOffset].Symbol.Label == dfd.SymbolRef.Label) { Messages.Add(new MessageList.MessageEntry( - MessageList.MessageEntry.SeverityLevel.Warning, + MessageList.MessageEntry.SeverityLevel.Error, offset, MessageList.MessageEntry.MessageType.NonAddrLabelRef, dfd.SymbolRef.Label, @@ -1766,7 +1766,8 @@ namespace SourceGen { offset += attr.Length; if (attr.DataDescriptor != null && - attr.DataDescriptor.FormatType == FormatDescriptor.Type.Junk) { + (attr.DataDescriptor.FormatType == FormatDescriptor.Type.Uninit || + attr.DataDescriptor.FormatType == FormatDescriptor.Type.Junk)) { ByteCounts.JunkByteCount += attr.Length; } else { ByteCounts.DataByteCount += attr.Length; diff --git a/SourceGen/FormatDescriptor.cs b/SourceGen/FormatDescriptor.cs index 9fd836e..fb0ea2d 100644 --- a/SourceGen/FormatDescriptor.cs +++ b/SourceGen/FormatDescriptor.cs @@ -61,6 +61,7 @@ namespace SourceGen { Dense, // raw data, represented as compactly as possible Fill, // fill memory with a value + Uninit, // uninitialized data storage area Junk // contents of memory are not interesting } @@ -91,6 +92,8 @@ namespace SourceGen { // Fill; no sub-types + // Uninit; no sub-types + // Junk; data may exist for alignment purposes. Sub-type indicates boundary. // (SubType=None indicates no alignment) Align2, // must be consecutive ascending powers of 2 @@ -497,6 +500,9 @@ namespace SourceGen { case Type.Fill: retstr += "fill"; break; + case Type.Uninit: + retstr += "uninitialized data"; + break; case Type.Junk: retstr += "unaligned junk"; break; diff --git a/SourceGen/PseudoOp.cs b/SourceGen/PseudoOp.cs index 52d3631..cfb4572 100644 --- a/SourceGen/PseudoOp.cs +++ b/SourceGen/PseudoOp.cs @@ -78,6 +78,7 @@ namespace SourceGen { public string DefineBigData4 { get; private set; } public string Fill { get; private set; } public string Dense { get; private set; } + public string Uninit { get; private set; } public string Junk { get; private set; } public string Align { get; private set; } public string StrGeneric { get; private set; } @@ -129,6 +130,7 @@ namespace SourceGen { a.DefineBigData4 == b.DefineBigData4 && a.Fill == b.Fill && a.Dense == b.Dense && + a.Uninit == b.Uninit && a.Junk == b.Junk && a.Align == b.Align && a.StrGeneric == b.StrGeneric && @@ -242,6 +244,7 @@ namespace SourceGen { { "DefineBigData4", ".dbd4" }, { "Fill", ".fill" }, { "Dense", ".bulk" }, + { "Uninit", ".ds" }, { "Junk", ".junk" }, { "Align", ".align" }, @@ -275,6 +278,7 @@ namespace SourceGen { case FormatDescriptor.Type.NumericLE: case FormatDescriptor.Type.NumericBE: case FormatDescriptor.Type.Fill: + case FormatDescriptor.Type.Uninit: case FormatDescriptor.Type.Junk: return 1; case FormatDescriptor.Type.Dense: { @@ -356,6 +360,10 @@ namespace SourceGen { po.Opcode = opNames.Fill; po.Operand = length + "," + formatter.FormatHexValue(data[offset], 2); break; + case FormatDescriptor.Type.Uninit: + po.Opcode = opNames.Uninit; + po.Operand = length.ToString(); + break; case FormatDescriptor.Type.Junk: if (dfd.FormatSubType != FormatDescriptor.SubType.None) { po.Opcode = opNames.Align; diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml index 32e2092..bae056e 100644 --- a/SourceGen/Res/Strings.xaml +++ b/SourceGen/Res/Strings.xaml @@ -185,7 +185,7 @@ limitations under the License. C64 Screen Code {1} CPU @ {2} MHz Show - {0:F1}KB ({1:F1}% code, {2:F1}% data, {3:F1}% junk) + {0:F1}KB ({1:F1}% code, {2:F1}% data, {3:F1}% uninit/junk) Ready DCI string has mixed data DCI string not terminated diff --git a/SourceGen/SGTestData/20000-numeric-types b/SourceGen/SGTestData/20000-numeric-types index bdeee08..60267ea 100644 Binary files a/SourceGen/SGTestData/20000-numeric-types and b/SourceGen/SGTestData/20000-numeric-types differ diff --git a/SourceGen/SGTestData/20000-numeric-types.dis65 b/SourceGen/SGTestData/20000-numeric-types.dis65 index 7ff704c..39897e3 100644 --- a/SourceGen/SGTestData/20000-numeric-types.dis65 +++ b/SourceGen/SGTestData/20000-numeric-types.dis65 @@ -1,8 +1,8 @@ ### 6502bench SourceGen dis65 v1.0 ### { -"_ContentVersion":3, -"FileDataLength":1200, -"FileDataCrc32":1114187983, +"_ContentVersion":5, +"FileDataLength":1227, +"FileDataCrc32":516168842, "ProjectProps":{ "CpuName":"6502", "IncludeUndocumentedInstr":false, @@ -14,7 +14,9 @@ "DefaultTextScanMode":"LowHighAscii", "MinCharsForString":4, "SeekNearbyTargets":true, -"SmartPlpHandling":true}, +"UseRelocData":false, +"SmartPlpHandling":true, +"SmartPlbHandling":true}, "PlatformSymbolFileIdentifiers":[], "ExtensionScriptFileIdentifiers":["PROJ:20000-numeric-types.cs"], @@ -23,15 +25,24 @@ "AddressMap":[{ "Offset":0, -"Addr":4096}, +"Addr":4096, +"Length":-1024, +"PreLabel":"", +"IsRelative":false}, { "Offset":1032, -"Addr":5128}, +"Addr":5128, +"Length":-1024, +"PreLabel":"", +"IsRelative":false}, { "Offset":1048, -"Addr":5160}], +"Addr":5160, +"Length":-1024, +"PreLabel":"", +"IsRelative":false}], "TypeHints":[{ "Low":0, "High":0, @@ -230,6 +241,60 @@ "Length":1, "Format":"NumericLE", "SubFormat":"Binary", +"SymbolRef":null}, + +"1200":{ +"Length":1, +"Format":"Uninit", +"SubFormat":"None", +"SymbolRef":null}, + +"1201":{ +"Length":2, +"Format":"Uninit", +"SubFormat":"None", +"SymbolRef":null}, + +"1203":{ +"Length":3, +"Format":"Uninit", +"SubFormat":"None", +"SymbolRef":null}, + +"1206":{ +"Length":4, +"Format":"Uninit", +"SubFormat":"None", +"SymbolRef":null}, + +"1210":{ +"Length":1, +"Format":"Uninit", +"SubFormat":"None", +"SymbolRef":null}, + +"1211":{ +"Length":2, +"Format":"Uninit", +"SubFormat":"None", +"SymbolRef":null}, + +"1213":{ +"Length":3, +"Format":"Uninit", +"SubFormat":"None", +"SymbolRef":null}, + +"1216":{ +"Length":4, +"Format":"Uninit", +"SubFormat":"None", +"SymbolRef":null}, + +"1221":{ +"Length":5, +"Format":"Uninit", +"SubFormat":"None", "SymbolRef":null}}, "LvTables":{ @@ -245,4 +310,10 @@ "VisualizationAnimations":[], "VisualizationSets":{ "1160":{ -"Tags":["vis000488"]}}} +"Tags":["vis000488"]}}, + +"RelocList":{ +}, + +"DbrValues":{ +}} diff --git a/SourceGen/SGTestData/Expected/20000-numeric-types_64tass.S b/SourceGen/SGTestData/Expected/20000-numeric-types_64tass.S index f572c4a..6951418 100644 --- a/SourceGen/SGTestData/Expected/20000-numeric-types_64tass.S +++ b/SourceGen/SGTestData/Expected/20000-numeric-types_64tass.S @@ -88,4 +88,15 @@ L14A8 .fill 8,$8b .fill 8,$8c .byte %10001100 .fill 7,$8c + .byte $90 + .byte $92,$91 + .byte $95,$94,$93 + .byte $99,$98,$97,$96 + .fill 1,$00 + .fill 2,$00 + .fill 3,$00 + .fill 4,$00 + .byte $80 + .fill 5,$00 + .byte $80 .here diff --git a/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S b/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S index 65415a8..15f866b 100644 --- a/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S +++ b/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S @@ -84,4 +84,15 @@ L14A8 !fill 8,$8b !fill 8,$8c !byte %10001100 !fill 7,$8c + !hex 90 + !hex 9291 + !hex 959493 + !hex 99989796 + !fill 1,$00 + !fill 2,$00 + !fill 3,$00 + !fill 4,$00 + !byte $80 + !fill 5,$00 + !byte $80 } diff --git a/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.S b/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.S index 65ad205..f691f95 100644 --- a/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.S +++ b/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.S @@ -85,3 +85,14 @@ L14A8: .res 8,$8b .res 8,$8c .byte %10001100 .res 7,$8c + .byte $90 + .byte $92,$91 + .byte $95,$94,$93 + .byte $99,$98,$97,$96 + .res 1,$00 + .res 2,$00 + .res 3,$00 + .res 4,$00 + .byte $80 + .res 5,$00 + .byte $80 diff --git a/SourceGen/SGTestData/Expected/20000-numeric-types_merlin32.S b/SourceGen/SGTestData/Expected/20000-numeric-types_merlin32.S index c103745..80bae86 100644 --- a/SourceGen/SGTestData/Expected/20000-numeric-types_merlin32.S +++ b/SourceGen/SGTestData/Expected/20000-numeric-types_merlin32.S @@ -82,3 +82,14 @@ L14A8 ds 8,$8b ds 8,$8c dfb %10001100 ds 7,$8c + hex 90 + hex 9291 + hex 959493 + hex 99989796 + ds 1 + ds 2 + ds 3 + ds 4 + dfb $80 + ds 5 + dfb $80 diff --git a/SourceGen/SGTestData/Source/20000-numeric-types.S b/SourceGen/SGTestData/Source/20000-numeric-types.S index 166b499..60ae98f 100644 --- a/SourceGen/SGTestData/Source/20000-numeric-types.S +++ b/SourceGen/SGTestData/Source/20000-numeric-types.S @@ -71,3 +71,16 @@ dref ds 16,$85 ;has a data reference ds 16,$8a ;EDIT: add visualization cref ds 16,$8b ;has a code reference ds 16,$8c ;EDIT: format byte as binary + +; Some uninitialized data for the "uninit" op. + dfb $90 + dw $9192 + adr $939495 + adrl $96979899 + ds 1 + ds 2 + ds 3 + ds 4 + dfb $80 + ds 5 + dfb $80 diff --git a/SourceGen/WpfGui/EditAppSettings.xaml b/SourceGen/WpfGui/EditAppSettings.xaml index c6ba446..5a580a1 100644 --- a/SourceGen/WpfGui/EditAppSettings.xaml +++ b/SourceGen/WpfGui/EditAppSettings.xaml @@ -661,7 +661,12 @@ limitations under the License. VerticalAlignment="Center" Margin="{StaticResource TBS}" Text=".placeho" MaxLength="12" FontFamily="{StaticResource GeneralMonoFont}"/> - + + + diff --git a/SourceGen/WpfGui/EditDataOperand.xaml.cs b/SourceGen/WpfGui/EditDataOperand.xaml.cs index 6bc6da6..d88981f 100644 --- a/SourceGen/WpfGui/EditDataOperand.xaml.cs +++ b/SourceGen/WpfGui/EditDataOperand.xaml.cs @@ -507,7 +507,7 @@ namespace SourceGen.WpfGui { // Check for run of bytes (2 or more of the same thing). Remember that // we check this one region at a time, and each region could have different - // bytes, but so long as the bytes are all the same within a region we're good. + // bytes, but so long as the bytes are all the same within each region we're good. if (radioFill.IsEnabled && count > 1 && DataAnalysis.RecognizeRun(mFileData, rng.Low, rng.High) == count) { // LGTM @@ -897,6 +897,9 @@ namespace SourceGen.WpfGui { case FormatDescriptor.Type.Fill: preferredFormat = radioFill; break; + case FormatDescriptor.Type.Uninit: + preferredFormat = radioUninit; + break; case FormatDescriptor.Type.Junk: preferredFormat = radioJunk; break; @@ -1070,6 +1073,8 @@ namespace SourceGen.WpfGui { type = FormatDescriptor.Type.Dense; } else if (radioFill.IsChecked == true) { type = FormatDescriptor.Type.Fill; + } else if (radioUninit.IsChecked == true) { + type = FormatDescriptor.Type.Uninit; } else if (radioJunk.IsChecked == true) { type = FormatDescriptor.Type.Junk; JunkAlignmentItem comboItem = (JunkAlignmentItem)junkAlignComboBox.SelectedItem; diff --git a/SourceGen/WpfGui/EditInstructionOperand.xaml.cs b/SourceGen/WpfGui/EditInstructionOperand.xaml.cs index ce4d590..fb4c975 100644 --- a/SourceGen/WpfGui/EditInstructionOperand.xaml.cs +++ b/SourceGen/WpfGui/EditInstructionOperand.xaml.cs @@ -682,6 +682,8 @@ namespace SourceGen.WpfGui { case FormatDescriptor.Type.StringDci: case FormatDescriptor.Type.Dense: case FormatDescriptor.Type.Fill: + case FormatDescriptor.Type.Uninit: + case FormatDescriptor.Type.Junk: default: // Unexpected; used to be data? break; diff --git a/docs/sgmanual/editors.html b/docs/sgmanual/editors.html index c705caf..5abb2f9 100644 --- a/docs/sgmanual/editors.html +++ b/docs/sgmanual/editors.html @@ -252,9 +252,14 @@ the data file, each address will be assigned a label.

The "Bulk Data" items can represent large chunks of data compactly. The "fill" option is only available if all selected bytes have the -same value. -If a region of bytes is irrelevant, perhaps used only as padding, you -can mark it as "junk". If it appears to be adding bytes to reach a +same value.

+

If a region of bytes is used for data storage, but the initial values +don't matter, you can mark it as "uninitialized data". (The code +generated will usually use an initialized bulk data directive rather +than a "leave space" directive, because SourceGen wants to guarantee +that the assembled binary matches the original.)

+

If a region of bytes is irrelevant, e.g. dead code or padding, +you can mark it as "junk". If it appears to be adding bytes to reach a power-of-two address boundary, you can designate it as an alignment directive. If you have multiple regions selected, only options that work for all regions will be shown.

diff --git a/docs/sgmanual/intro-details.html b/docs/sgmanual/intro-details.html index 743cc77..07d4f2c 100644 --- a/docs/sgmanual/intro-details.html +++ b/docs/sgmanual/intro-details.html @@ -899,6 +899,9 @@ code generator figure out the implementation details.

the assembler that the width has changed.
  • .DBANK - specifies what value the Data Bank Register holds (65816 only). Used when matching operands to labels.
  • +
  • .DS - identifies space set aside for variable storage. The storage + is initialized by the program before first use, so the values + in the binary don't actually matter.
  • .JUNK - indicates that the data in a range of bytes is irrelevant. (When generating sources, this will become .FILL or .BULK depending on the contents of the memory region and the assembler's diff --git a/docs/sgmanual/mainwin.html b/docs/sgmanual/mainwin.html index 2b534dd..b187772 100644 --- a/docs/sgmanual/mainwin.html +++ b/docs/sgmanual/mainwin.html @@ -84,6 +84,10 @@ saved in the application settings file.

    If you hover your mouse over them, a tooltip with an explanation will appear.

    +

    A status bar at the bottom displays a summary of the amount of +code, data, and uninitialized data (variable storage or junk) found +in the program. These values are updated as you work.

    +

    Code List