From 037b982d948e250e3fe258ec0ae44b0990c3e968 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sun, 30 Nov 2025 14:59:39 -0800 Subject: [PATCH] Allow scripts to handle COP and WDM inlines The code that allows scripts to catch and handle BRK/JSR/JSL followed by inline data now also handles COP and WDM. (issue #177) --- PluginCommon/Interfaces.cs | 28 ++++++++ SourceGen/CodeAnalysis.cs | 29 +++++++- .../SGTestData/20180-extension-scripts-c.cs | 63 ++++++++++++++++++ SourceGen/SGTestData/20182-extension-scripts | Bin 544 -> 544 bytes .../SGTestData/20182-extension-scripts.dis65 | 5 +- .../Expected/20182-extension-scripts_64tass.S | 10 ++- .../Expected/20182-extension-scripts_acme.S | 8 ++- .../Expected/20182-extension-scripts_cc65.S | 8 ++- .../20182-extension-scripts_merlin32.S | 8 ++- .../Source/20180-extension-scripts.S | 2 +- .../Source/20182-extension-scripts.S | 8 ++- 11 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 SourceGen/SGTestData/20180-extension-scripts-c.cs 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 fe0819c51b9261922356aa575028e34da06e9970..5fca543a2047ab62bbe7c5c949302abce95771b3 100644 GIT binary patch delta 29 lcmZ3$vVdj64t^$P7H9teMJE=Pa2H?2gpCI+87DC*004Fk2fqLS delta 18 acmZ3$vVdj64#tFy7xft@K9HEiqyPX!_y*ws 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