diff --git a/SourceGen/Examples/Scripts/Sample.S b/SourceGen/Examples/Scripts/Sample.S
index 8c2f85a..fa03b54 100644
--- a/SourceGen/Examples/Scripts/Sample.S
+++ b/SourceGen/Examples/Scripts/Sample.S
@@ -1,3 +1,8 @@
+; Copyright 2019 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+;
+; Assembler: Merlin 32
+
org $1000
jsr PrintInlineL1String
diff --git a/SourceGen/Examples/Scripts/FinishedSample.dis65 b/SourceGen/Examples/Scripts/Sample.dis65
similarity index 93%
rename from SourceGen/Examples/Scripts/FinishedSample.dis65
rename to SourceGen/Examples/Scripts/Sample.dis65
index 0f7e6d2..3cd1e55 100644
--- a/SourceGen/Examples/Scripts/FinishedSample.dis65
+++ b/SourceGen/Examples/Scripts/Sample.dis65
@@ -10,7 +10,7 @@
"Low":0,"High":0,"Hint":"Code"}],"StatusFlagOverrides":{
},
"Comments":{
-},
+"0":"requires exact name match","13":"requires name prefix match"},
"LongComments":{
},
"Notes":{
diff --git a/SourceGen/Examples/Tutorial/InlineL1String.cs b/SourceGen/Examples/Tutorial/InlineL1String.cs
new file mode 100644
index 0000000..508aa90
--- /dev/null
+++ b/SourceGen/Examples/Tutorial/InlineL1String.cs
@@ -0,0 +1,73 @@
+// Copyright 2019 faddenSoft. All Rights Reserved.
+// See the LICENSE.txt file for distribution terms (Apache 2.0).
+
+using System;
+using System.Collections.Generic;
+
+using PluginCommon;
+
+namespace ExtensionScriptSample {
+ ///
+ /// Sample class for handling a JSR followed by a string prefixed with a 1-byte length.
+ ///
+ public class InlineL1String: MarshalByRefObject, IPlugin, IPlugin_SymbolList,
+ IPlugin_InlineJsr {
+ private IApplication mAppRef;
+ private byte[] mFileData;
+
+ // Only one call.
+ private const string CALL_LABEL = "PrintInlineL1String";
+ private int mInlineL1StringAddr; // jsr
+
+ public string Identifier {
+ get {
+ return "Inline L1 ASCII string handler";
+ }
+ }
+
+ public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) {
+ mAppRef = appRef;
+ mFileData = fileData;
+
+ mAppRef.DebugLog("InlineL1String(id=" +
+ AppDomain.CurrentDomain.Id + "): prepare()");
+ }
+
+ public void UpdateSymbolList(List plSyms) {
+ // reset this every time, in case they remove the symbol
+ mInlineL1StringAddr = -1;
+
+ foreach (PlSymbol sym in plSyms) {
+ if (sym.Label == CALL_LABEL) {
+ mInlineL1StringAddr = sym.Value;
+ break;
+ }
+ }
+ mAppRef.DebugLog(CALL_LABEL + " @ $" + mInlineL1StringAddr.ToString("x6"));
+ }
+ public bool IsLabelSignificant(string beforeLabel, string afterLabel) {
+ return beforeLabel == CALL_LABEL || afterLabel == CALL_LABEL;
+ }
+
+ public void CheckJsr(int offset, int operand, out bool noContinue) {
+ noContinue = false;
+ if (operand != mInlineL1StringAddr) {
+ return;
+ }
+ if (offset + 3 >= mFileData.Length) {
+ return; // length byte is off end
+ }
+ int len = mFileData[3]; // first byte past JSR
+ if (offset + 4 + len > mFileData.Length) {
+ mAppRef.DebugLog("L1 string ran off end of file at +" +
+ (offset + 4).ToString("x6"));
+ return;
+ }
+
+ // Assuming ASCII. This can be hard-coded, use auto-detection, or look
+ // up a value in a project constant.
+ mAppRef.SetInlineDataFormat(offset + 3, len + 1,
+ DataType.StringL8, DataSubType.Ascii, null);
+ }
+ }
+}
diff --git a/SourceGen/Examples/Tutorial/InlineNullTermString.cs b/SourceGen/Examples/Tutorial/InlineNullTermString.cs
new file mode 100644
index 0000000..d568331
--- /dev/null
+++ b/SourceGen/Examples/Tutorial/InlineNullTermString.cs
@@ -0,0 +1,76 @@
+// Copyright 2019 faddenSoft. All Rights Reserved.
+// See the LICENSE.txt file for distribution terms (Apache 2.0).
+
+using System;
+using System.Collections.Generic;
+
+using PluginCommon;
+
+namespace ExtensionScriptSample {
+ ///
+ /// Sample class for handling a JSR followed by an inline null-terminated string. Any
+ /// label that starts with "PrintLineNullString" is matched.
+ ///
+ public class InlineNullTermString : MarshalByRefObject, IPlugin, IPlugin_SymbolList,
+ IPlugin_InlineJsr {
+ private IApplication mAppRef;
+ private byte[] mFileData;
+
+ private const string LABEL_PREFIX = "PrintInlineNullString";
+ private Dictionary mNullStringAddrs = new Dictionary();
+
+ public string Identifier {
+ get {
+ return "Inline null-terminated ASCII string handler";
+ }
+ }
+
+ public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) {
+ mAppRef = appRef;
+ mFileData = fileData;
+
+ mAppRef.DebugLog("InlineNullStringHandler(id=" +
+ AppDomain.CurrentDomain.Id + "): prepare()");
+ }
+
+ public void UpdateSymbolList(List plSyms) {
+ mNullStringAddrs.Clear();
+
+ foreach (PlSymbol sym in plSyms) {
+ if (sym.Label.StartsWith(LABEL_PREFIX)) {
+ mNullStringAddrs.Add(sym.Value, sym);
+ }
+ }
+ mAppRef.DebugLog(LABEL_PREFIX + " matched " + mNullStringAddrs.Count + " labels");
+ }
+ public bool IsLabelSignificant(string beforeLabel, string afterLabel) {
+ return beforeLabel.StartsWith(LABEL_PREFIX) || afterLabel.StartsWith(LABEL_PREFIX);
+ }
+
+ public void CheckJsr(int offset, int operand, out bool noContinue) {
+ noContinue = false;
+ if (!mNullStringAddrs.ContainsKey(operand)) {
+ return;
+ }
+
+ // search for the terminating null byte
+ int nullOff = offset + 3;
+ while (nullOff < mFileData.Length) {
+ if (mFileData[nullOff] == 0) {
+ break;
+ }
+ nullOff++;
+ }
+ if (nullOff == mFileData.Length) {
+ mAppRef.DebugLog("Unable to find end of null-terminated string at +" +
+ (offset+3).ToString("x6"));
+ return;
+ }
+
+ // Assuming ASCII. This can be hard-coded, use auto-detection, or look
+ // up a value in a project constant.
+ mAppRef.SetInlineDataFormat(offset + 3, nullOff - (offset + 3) + 1,
+ DataType.StringNullTerm, DataSubType.Ascii, null);
+ }
+ }
+}
diff --git a/SourceGen/Examples/Tutorial/Tutorial1.S b/SourceGen/Examples/Tutorial/Source/Tutorial1.S
similarity index 100%
rename from SourceGen/Examples/Tutorial/Tutorial1.S
rename to SourceGen/Examples/Tutorial/Source/Tutorial1.S
diff --git a/SourceGen/Examples/Tutorial/Tutorial2.S b/SourceGen/Examples/Tutorial/Source/Tutorial2.S
similarity index 100%
rename from SourceGen/Examples/Tutorial/Tutorial2.S
rename to SourceGen/Examples/Tutorial/Source/Tutorial2.S
diff --git a/SourceGen/Examples/Tutorial/Source/Tutorial4.S b/SourceGen/Examples/Tutorial/Source/Tutorial4.S
new file mode 100644
index 0000000..fa03b54
--- /dev/null
+++ b/SourceGen/Examples/Tutorial/Source/Tutorial4.S
@@ -0,0 +1,23 @@
+; Copyright 2019 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+;
+; Assembler: Merlin 32
+
+ org $1000
+
+ jsr PrintInlineL1String
+ str 'How long?'
+
+ jsr PrintInlineZString1
+ asc 'Test one',00
+
+ jsr PrintInlineZString2
+ asc 'Test two',00
+ rts
+
+PrintInlineL1String
+ rts
+PrintInlineZString1
+ rts
+PrintInlineZString2
+ rts
diff --git a/SourceGen/Examples/Tutorial/Tutorial4 b/SourceGen/Examples/Tutorial/Tutorial4
new file mode 100644
index 0000000..c8968a9
Binary files /dev/null and b/SourceGen/Examples/Tutorial/Tutorial4 differ
diff --git a/SourceGen/RuntimeData/Help/index.html b/SourceGen/RuntimeData/Help/index.html
index 23c4e6c..45e5157 100644
--- a/SourceGen/RuntimeData/Help/index.html
+++ b/SourceGen/RuntimeData/Help/index.html
@@ -164,6 +164,8 @@ and 65816 code. The official web site is
diff --git a/SourceGen/RuntimeData/Help/tutorials.html b/SourceGen/RuntimeData/Help/tutorials.html
index 0a01871..897427d 100644
--- a/SourceGen/RuntimeData/Help/tutorials.html
+++ b/SourceGen/RuntimeData/Help/tutorials.html
@@ -21,8 +21,8 @@ manual is recommended.
@@ -539,7 +539,7 @@ extra symbol in the opcode field, you need to look closely at what's going
on.
-
+
This tutorial covers one specific feature.
@@ -591,6 +591,11 @@ You could repeat these steps for the remaining items, but there's a faster
way. Click on the line at address $1d97, then shift-click the line at
address $1da9 (which should be .FILL 12,$1e
). Select
Actions > Format Address Table.
+Contrary to first impressions, this imposing dialog does not allow you
+to launch objects into orbit. There are a variety of common ways to
+structure an address table, all of which are handled here. You can
+configure the various parameters and see the effects as you make
+each change.
The message at the top should indicate that there are 30 bytes
selected. In Address Characteristics, click the "Parts are split across
sub-tables" checkbox and the "adjusted for RTS/RTL"
@@ -614,7 +619,7 @@ code entry point hint -- but did several of them at once.
SourceGen asks for confirmation, click Discard & Continue.
-
+
This tutorial covers one specific feature.
@@ -624,10 +629,9 @@ because code is expected.
An earlier tutorial demonstrated how to manually mark bytes as
inline data. We're going to do it a faster way. For this tutorial,
start a new project with "Generic 6502", and in the SourceGen
-installation directory find the Examples directory, open the Scripts
-subdirectory, and select "Sample".
+Tutorial directory select "Tutorial4".
We'll need to load scripts from the project directory, so we have to
-save the project. File > Save, call it the default name (Sample.dis65).
+save the project. File > Save, use the default name ("Tutorial4.dis65").
Take a look at the disassembly listing. The file starts with a JSR
followed by a string that begins with a small number. This appears to be
@@ -657,9 +661,9 @@ on line $1019 ("L1028"), setting the label to "PrintInlineNullStringTwo".
The entire project is now nicely formatted. In a real project the
"Print Inline" locations would be actual print functions, not just RTS
-instructions, and there would be multiple JSRs to a single function.
-So labeling a single print function could format dozens of inline strings
-and clean up the disassembly automatically.
+instructions. There would likely be multiple JSRs to the print function,
+so labeling a single function entry point could format dozens of inline
+strings and clean up the disassembly automatically.
Extension scripts can make your life much easier, but they do require
some programming experience. See the