diff --git a/PluginCommon/Interfaces.cs b/PluginCommon/Interfaces.cs
index a2b173f..2054b78 100644
--- a/PluginCommon/Interfaces.cs
+++ b/PluginCommon/Interfaces.cs
@@ -134,6 +134,34 @@ namespace PluginCommon {
void CheckBrk(int offset, bool isTwoBytes, out bool noContinue);
}
+ ///
+ /// Extension scripts that want to handle inline COPs must implement this interface.
+ ///
+ public interface IPlugin_InlineCop {
+ ///
+ /// Checks to see if code/data near a COP instruction should be formatted.
+ ///
+ /// The file data is only guaranteed to hold the COP opcode byte.
+ ///
+ /// Offset of the COP instruction.
+ /// Set to true if the COP doesn't actually return.
+ void CheckCop(int offset, out bool noContinue);
+ }
+
+ ///
+ /// Extension scripts that want to handle inline WDMs must implement this interface.
+ ///
+ public interface IPlugin_InlineWdm {
+ ///
+ /// Checks to see if code/data near a WDM instruction should be formatted.
+ ///
+ /// The file data is only guaranteed to hold the WDM opcode byte.
+ ///
+ /// Offset of the WDM instruction.
+ /// Set to true if the WDM doesn't actually return.
+ void CheckWdm(int offset, out bool noContinue);
+ }
+
///
/// Extension scripts that want to generate visualizations must implement this interface.
///
diff --git a/SourceGen/CodeAnalysis.cs b/SourceGen/CodeAnalysis.cs
index ebdf0b1..50327c2 100644
--- a/SourceGen/CodeAnalysis.cs
+++ b/SourceGen/CodeAnalysis.cs
@@ -126,7 +126,14 @@ namespace SourceGen {
private IPlugin[] mScriptArray;
[Flags]
- private enum PluginCap { NONE = 0, JSR = 1 << 0, JSL = 1 << 1, BRK = 1 << 2 };
+ private enum PluginCap {
+ NONE = 0,
+ JSR = 1 << 0,
+ JSL = 1 << 1,
+ BRK = 1 << 2,
+ COP = 1 << 3,
+ WDM = 1 << 4,
+ };
private PluginCap[] mPluginCaps;
///
@@ -356,6 +363,12 @@ namespace SourceGen {
if (mScriptArray[i] is IPlugin_InlineBrk) {
cap |= PluginCap.BRK;
}
+ if (mScriptArray[i] is IPlugin_InlineCop) {
+ cap |= PluginCap.COP;
+ }
+ if (mScriptArray[i] is IPlugin_InlineWdm) {
+ cap |= PluginCap.WDM;
+ }
mPluginCaps[i] = cap;
}
@@ -788,11 +801,17 @@ namespace SourceGen {
// On first visit, check for JSR/JSL inline call. If it's "no-continue",
// set a flag and halt here.
+ // Also check COP/WDM, since we assume they continue.
if (firstVisit) {
// Currently ignoring OpDef.OpJSR_AbsIndexXInd
- if (op == OpDef.OpJSR_Abs || op == OpDef.OpJSR_AbsLong) {
+ if (op == OpDef.OpJSR_Abs || op == OpDef.OpJSR_AbsLong ||
+ op == OpDef.OpCOP_StackInt || op == OpDef.OpWDM_WDM) {
bool noContinue = CheckForInlineCall(op, offset, false);
if (noContinue) {
+ // The "NoContinue" attribute can be updated if the code gets
+ // revisited, so we use a separate flag to ensure the script's
+ // decision doesn't get lost. See 20180-extension-scripts
+ // for a test case.
LogD(offset, "Script declared inline call no-continue");
mAnattribs[offset].NoContinueScript = true;
break;
@@ -1086,6 +1105,12 @@ namespace SourceGen {
} else if (op == OpDef.OpJSR_AbsLong && (mPluginCaps[i] & PluginCap.JSL) != 0) {
((IPlugin_InlineJsl)script).CheckJsl(offset, operand, out bool noCont);
noContinue |= noCont;
+ } else if (op == OpDef.OpCOP_StackInt && (mPluginCaps[i] & PluginCap.COP) != 0){
+ ((IPlugin_InlineCop)script).CheckCop(offset, out bool noCont);
+ noContinue |= noCont;
+ } else if (op == OpDef.OpWDM_WDM && (mPluginCaps[i] & PluginCap.WDM) != 0) {
+ ((IPlugin_InlineWdm)script).CheckWdm(offset, out bool noCont);
+ noContinue |= noCont;
} else if ((op == OpDef.OpBRK_Implied || op == OpDef.OpBRK_StackInt) &&
(mPluginCaps[i] & PluginCap.BRK) != 0) {
((IPlugin_InlineBrk)script).CheckBrk(offset, op == OpDef.OpBRK_StackInt,
diff --git a/SourceGen/SGTestData/20180-extension-scripts-c.cs b/SourceGen/SGTestData/20180-extension-scripts-c.cs
new file mode 100644
index 0000000..0ca9175
--- /dev/null
+++ b/SourceGen/SGTestData/20180-extension-scripts-c.cs
@@ -0,0 +1,63 @@
+// Copyright 2025 faddenSoft. All Rights Reserved.
+// See the LICENSE.txt file for distribution terms (Apache 2.0).
+
+using System;
+using System.Collections.Generic;
+
+using PluginCommon;
+
+// COP/WDM handler
+namespace RuntimeData.Test20180 {
+ public class Test20180C : MarshalByRefObject, IPlugin, IPlugin_InlineCop, IPlugin_InlineWdm {
+ private IApplication mAppRef;
+ private byte[] mFileData;
+ private AddressTranslate mAddrTrans;
+
+ public string Identifier {
+ get {
+ return "Test 20180-extension-scripts C";
+ }
+ }
+
+ public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) {
+ mAppRef = appRef;
+ mFileData = fileData;
+ mAddrTrans = addrTrans;
+
+ mAppRef.DebugLog("Test20180-C(id=" + AppDomain.CurrentDomain.Id + "): prepare()");
+ }
+
+ public void Unprepare() {
+ mAppRef = null;
+ mFileData = null;
+ mAddrTrans = null;
+ }
+
+ public void CheckCop(int offset, out bool noContinue) {
+ HandleString(offset, out noContinue);
+ }
+
+ public void CheckWdm(int offset, out bool noContinue) {
+ HandleString(offset, out noContinue);
+ }
+
+ private void HandleString(int offset, out bool noContinue) {
+ const int INSTR_LEN = 2; // opcode + byte
+ noContinue = false;
+
+ // Handle as string with 8-bit length prefix.
+ if (offset + INSTR_LEN >= mFileData.Length) {
+ return; // length byte is off end
+ }
+ int len = mFileData[offset + INSTR_LEN];
+ if (offset + INSTR_LEN + 1 + len > mFileData.Length) {
+ // ran off the end
+ mAppRef.DebugLog("L1 string ran off end of file at +" +
+ (offset + INSTR_LEN).ToString("x6"));
+ return;
+ }
+ mAppRef.SetInlineDataFormat(offset + INSTR_LEN, len + 1,
+ DataType.StringL8, DataSubType.Ascii, null);
+ }
+ }
+}
diff --git a/SourceGen/SGTestData/20182-extension-scripts b/SourceGen/SGTestData/20182-extension-scripts
index fe0819c..5fca543 100644
Binary files a/SourceGen/SGTestData/20182-extension-scripts and b/SourceGen/SGTestData/20182-extension-scripts differ
diff --git a/SourceGen/SGTestData/20182-extension-scripts.dis65 b/SourceGen/SGTestData/20182-extension-scripts.dis65
index 2c88148..18898a4 100644
--- a/SourceGen/SGTestData/20182-extension-scripts.dis65
+++ b/SourceGen/SGTestData/20182-extension-scripts.dis65
@@ -2,7 +2,7 @@
{
"_ContentVersion":7,
"FileDataLength":544,
-"FileDataCrc32":1948144462,
+"FileDataCrc32":371480162,
"ProjectProps":{
"CpuName":"65816",
"IncludeUndocumentedInstr":false,
@@ -20,7 +20,8 @@
"PlatformSymbolFileIdentifiers":["PROJ:20182-extension-scripts.sym65"],
"ExtensionScriptFileIdentifiers":["PROJ:20180-extension-scripts-a.cs",
-"PROJ:20180-extension-scripts-b.cs"],
+"PROJ:20180-extension-scripts-b.cs",
+"PROJ:20180-extension-scripts-c.cs"],
"ProjectSyms":{
"PrintInlineDciString":{
"DataDescriptor":{
diff --git a/SourceGen/SGTestData/Expected/20182-extension-scripts_64tass.S b/SourceGen/SGTestData/Expected/20182-extension-scripts_64tass.S
index 6ae242b..b48029d 100644
--- a/SourceGen/SGTestData/Expected/20182-extension-scripts_64tass.S
+++ b/SourceGen/SGTestData/Expected/20182-extension-scripts_64tass.S
@@ -74,9 +74,14 @@ Next clc
.byte $00
.byte $80
-_L10B8 rts
+_L10B8 cop #$03
+ .enc "sg_ascii"
+ .ptext "COP!"
+ .byte $42,$04
+ .ptext "WDM!"
+ rts
- .fill 327,$00
+ .fill 313,$00
.logical $1800
L1800 jsr PrintInlineNullString
@@ -89,7 +94,6 @@ L1800 jsr PrintInlineNullString
.byte $01
.here
.logical $1840
- .enc "sg_ascii"
.text "string"
.byte $00
.byte $60
diff --git a/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S b/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S
index 566b02b..6b0ae81 100644
--- a/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S
+++ b/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S
@@ -68,9 +68,13 @@ Next clc
!byte $00
!byte $80
-@L10B8 rts
+@L10B8 cop $03
+ !text $04,"COP!"
+ !byte $42,$04
+ !text $04,"WDM!"
+ rts
- !fill 327,$00
+ !fill 313,$00
!pseudopc $1800 {
L1800 jsr PrintInlineNullString
diff --git a/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.S b/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.S
index 7251bef..9df3cb0 100644
--- a/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.S
+++ b/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.S
@@ -71,9 +71,13 @@ Next: clc
.byte $00
.byte $80
-@L10B8: rts
+@L10B8: cop $03
+ .byte $04,"COP!"
+ wdm $04
+ .byte $04,"WDM!"
+ rts
- .res 327,$00
+ .res 313,$00
.org $1800
L1800: jsr PrintInlineNullString
diff --git a/SourceGen/SGTestData/Expected/20182-extension-scripts_merlin32.S b/SourceGen/SGTestData/Expected/20182-extension-scripts_merlin32.S
index de1601d..45b21ba 100644
--- a/SourceGen/SGTestData/Expected/20182-extension-scripts_merlin32.S
+++ b/SourceGen/SGTestData/Expected/20182-extension-scripts_merlin32.S
@@ -63,9 +63,13 @@ Next clc
dfb $00
dfb $80
-:L10B8 rts
+:L10B8 cop $03
+ str 'COP!'
+ wdm $04
+ str 'WDM!'
+ rts
- ds 327
+ ds 313
org $1800
L1800 jsr PrintInlineNullString
diff --git a/SourceGen/SGTestData/Source/20180-extension-scripts.S b/SourceGen/SGTestData/Source/20180-extension-scripts.S
index d8e7d94..7d86893 100644
--- a/SourceGen/SGTestData/Source/20180-extension-scripts.S
+++ b/SourceGen/SGTestData/Source/20180-extension-scripts.S
@@ -10,7 +10,7 @@
; check basics
jsr PrintInline8String
- asc '01234567'
+ asc '01234567' ;hard-coded to 8 bytes long
jsr PrintInlineRev8String
asc '76543210'
jsr PrintInlineNullString
diff --git a/SourceGen/SGTestData/Source/20182-extension-scripts.S b/SourceGen/SGTestData/Source/20182-extension-scripts.S
index 809d3a3..bf7cd0a 100644
--- a/SourceGen/SGTestData/Source/20182-extension-scripts.S
+++ b/SourceGen/SGTestData/Source/20182-extension-scripts.S
@@ -18,7 +18,7 @@ PrintInlineDciString equ $013000 ;EDIT: add to project symbols
; check basics
jsr PrintInline8String
- asc '01234567'
+ asc '01234567' ;hard-coded to 8 bytes long
jsr PrintInlineRev8String
asc '76543210'
jsr PrintInlineNullString
@@ -81,6 +81,12 @@ Next
brk $80
:BrkNext
+; test COP and WDM
+ cop $03
+ str 'COP!'
+ wdm $04
+ str 'WDM!'
+
rts