1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-07-30 15:29:01 +00:00

Fix bug in inline JSR/JSL no-continue handling

JSR/JSL calls with inline data have the option of reporting that
they don't continue, which causes the code analyzer to treat them
as JMPs instead.  There was a bug that was causing the no-continue
flag to be lost in certain circumstances.

The code now explicitly records the plugin's response in an Anattrib
flag.  Test 2022-extension-scripts has been updated with a test case
that exercises this situation.
This commit is contained in:
Andy McFadden 2020-05-08 17:41:26 -07:00
parent 71af8bf117
commit 63d7a48705
11 changed files with 339 additions and 71 deletions

View File

@ -41,6 +41,7 @@ namespace SourceGen {
ExternalBranch = 1 << 10, // this abs/rel branch lands outside input file ExternalBranch = 1 << 10, // this abs/rel branch lands outside input file
NoContinue = 1 << 12, // execution does not continue to following instruction NoContinue = 1 << 12, // execution does not continue to following instruction
NoContinueScript = 1 << 13, // no-continue flag set by extension script
Visited = 1 << 16, // has the analyzer visited this byte? Visited = 1 << 16, // has the analyzer visited this byte?
Changed = 1 << 17, // set/cleared as the analyzer works Changed = 1 << 17, // set/cleared as the analyzer works
@ -148,10 +149,7 @@ namespace SourceGen {
} }
} }
} }
public bool DoesNotContinue { public bool NoContinue {
get {
return (mAttribFlags & AttribFlags.NoContinue) != 0;
}
set { set {
if (value) { if (value) {
mAttribFlags |= AttribFlags.NoContinue; mAttribFlags |= AttribFlags.NoContinue;
@ -160,6 +158,21 @@ namespace SourceGen {
} }
} }
} }
public bool NoContinueScript {
set {
if (value) {
mAttribFlags |= AttribFlags.NoContinueScript;
} else {
mAttribFlags &= ~AttribFlags.NoContinueScript;
}
}
}
public bool DoesNotContinue {
get {
return (mAttribFlags & AttribFlags.NoContinue) != 0 ||
(mAttribFlags & AttribFlags.NoContinueScript) != 0;
}
}
public bool DoesNotBranch { public bool DoesNotBranch {
get { get {
return (BranchTaken == OpDef.BranchTaken.Never); return (BranchTaken == OpDef.BranchTaken.Never);

View File

@ -690,11 +690,11 @@ namespace SourceGen {
} }
} }
if (!doContinue) { mAnattribs[offset].NoContinue = !doContinue;
mAnattribs[offset].DoesNotContinue = true; if (mAnattribs[offset].DoesNotContinue) {
// If we just decided not to continue, or an extension script set a flag
// on a previous visit, stop scanning forward.
break; break;
} else {
mAnattribs[offset].DoesNotContinue = false;
} }
// Sanity check to avoid infinite loop. // Sanity check to avoid infinite loop.
@ -710,14 +710,15 @@ namespace SourceGen {
break; break;
} }
// On first visit, check for JSR/JSL inline call. // On first visit, check for JSR/JSL inline call. If it's "no-continue",
// set a flag and halt here.
if (firstVisit) { if (firstVisit) {
// Currently ignoring OpDef.OpJSR_AbsIndexXInd // Currently ignoring OpDef.OpJSR_AbsIndexXInd
if (op == OpDef.OpJSR_Abs || op == OpDef.OpJSR_AbsLong) { if (op == OpDef.OpJSR_Abs || op == OpDef.OpJSR_AbsLong) {
bool noContinue = CheckForInlineCall(op, offset, false); bool noContinue = CheckForInlineCall(op, offset, false);
if (noContinue) { if (noContinue) {
LogD(offset, "Script declared inline call no-continue"); LogD(offset, "Script declared inline call no-continue");
mAnattribs[offset].DoesNotContinue = true; mAnattribs[offset].NoContinueScript = true;
break; break;
} }
} }

View File

@ -19,6 +19,8 @@ namespace RuntimeData.Test2022 {
private int mInlineL2StringAddr; // jsl private int mInlineL2StringAddr; // jsl
private int mInlineDciStringAddr; // jsl private int mInlineDciStringAddr; // jsl
private int mNoContAddr; // jsr
public string Identifier { public string Identifier {
get { get {
return "Test 2022-extension-scripts A"; return "Test 2022-extension-scripts A";
@ -58,6 +60,9 @@ namespace RuntimeData.Test2022 {
case "PrintInlineDciString": case "PrintInlineDciString":
mInlineDciStringAddr = sym.Value; mInlineDciStringAddr = sym.Value;
break; break;
case "NoCont":
mNoContAddr = sym.Value;
break;
} }
} }
} }
@ -101,6 +106,8 @@ namespace RuntimeData.Test2022 {
} }
mAppRef.SetInlineDataFormat(offset + 3, nullOff - (offset + 3) + 1, mAppRef.SetInlineDataFormat(offset + 3, nullOff - (offset + 3) + 1,
DataType.StringNullTerm, DataSubType.Ascii, null); DataType.StringNullTerm, DataSubType.Ascii, null);
} else if (operand == mNoContAddr) {
noContinue = true;
} }
} }

View File

@ -1,39 +1,133 @@
### 6502bench SourceGen dis65 v1.0 ### ### 6502bench SourceGen dis65 v1.0 ###
{ {
"_ContentVersion":2,"FileDataLength":213,"FileDataCrc32":-798098677,"ProjectProps":{ "_ContentVersion":3,
"CpuName":"65816","IncludeUndocumentedInstr":false,"TwoByteBrk":true,"EntryFlags":32702671,"AutoLabelStyle":"Simple","AnalysisParams":{ "FileDataLength":544,
"AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":true,"SmartPlpHandling":true}, "FileDataCrc32":118019308,
"PlatformSymbolFileIdentifiers":["PROJ:2022-extension-scripts.sym65"],"ExtensionScriptFileIdentifiers":["PROJ:2022-extension-scripts-a.cs","PROJ:2022-extension-scripts-b.cs"],"ProjectSyms":{ "ProjectProps":{
"CpuName":"65816",
"IncludeUndocumentedInstr":false,
"TwoByteBrk":true,
"EntryFlags":32702671,
"AutoLabelStyle":"Simple",
"AnalysisParams":{
"AnalyzeUncategorizedData":true,
"DefaultTextScanMode":"LowHighAscii",
"MinCharsForString":4,
"SeekNearbyTargets":true,
"SmartPlpHandling":true},
"PlatformSymbolFileIdentifiers":["PROJ:2022-extension-scripts.sym65"],
"ExtensionScriptFileIdentifiers":["PROJ:2022-extension-scripts-a.cs",
"PROJ:2022-extension-scripts-b.cs"],
"ProjectSyms":{
"PrintInlineDciString":{ "PrintInlineDciString":{
"DataDescriptor":{ "DataDescriptor":{
"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null}, "Length":1,
"Comment":"","HasWidth":false,"Direction":"ReadWrite","MultiMask":null,"Label":"PrintInlineDciString","Value":77824,"Source":"Project","Type":"ExternalAddr"}}}, "Format":"NumericLE",
"SubFormat":"Hex",
"SymbolRef":null},
"Comment":"",
"HasWidth":false,
"Direction":"ReadWrite",
"MultiMask":null,
"Label":"PrintInlineDciString",
"Value":77824,
"Source":"Project",
"Type":"ExternalAddr",
"LabelAnno":"None"}}},
"AddressMap":[{ "AddressMap":[{
"Offset":0,"Addr":4096}, "Offset":0,
"Addr":4096},
{ {
"Offset":192,"Addr":4352}],"TypeHints":[{ "Offset":512,
"Low":0,"High":0,"Hint":"Code"}],"StatusFlagOverrides":{ "Addr":6144},
{
"Offset":523,
"Addr":6208}],
"TypeHints":[{
"Low":0,
"High":0,
"Hint":"Code"}],
"StatusFlagOverrides":{
}, },
"Comments":{ "Comments":{
"181":"split across address change"}, "183":"split across address change"},
"LongComments":{ "LongComments":{
}, },
"Notes":{ "Notes":{
}, },
"UserLabels":{ "UserLabels":{
"139":{
"Label":"PrintInline8String","Value":4225,"Source":"User","Type":"LocalOrGlobalAddr"},
"140":{
"Label":"PrintInlineRev8String","Value":4226,"Source":"User","Type":"LocalOrGlobalAddr"},
"141":{ "141":{
"Label":"PrintInlineNullString","Value":4227,"Source":"User","Type":"LocalOrGlobalAddr"}, "Label":"PrintInline8String",
"170":{ "Value":4237,
"Label":"data02","Value":4256,"Source":"User","Type":"LocalOrGlobalAddr"}, "Source":"User",
"173":{ "Type":"LocalOrGlobalAddr",
"Label":"data03","Value":4259,"Source":"User","Type":"LocalOrGlobalAddr"}, "LabelAnno":"None"},
"142":{ "142":{
"Label":"data01","Value":4228,"Source":"User","Type":"LocalOrGlobalAddr"}}, "Label":"PrintInlineRev8String",
"Value":4238,
"Source":"User",
"Type":"LocalOrGlobalAddr",
"LabelAnno":"None"},
"143":{
"Label":"PrintInlineNullString",
"Value":4239,
"Source":"User",
"Type":"LocalOrGlobalAddr",
"LabelAnno":"None"},
"172":{
"Label":"data02",
"Value":4268,
"Source":"User",
"Type":"LocalOrGlobalAddr",
"LabelAnno":"None"},
"175":{
"Label":"data03",
"Value":4271,
"Source":"User",
"Type":"LocalOrGlobalAddr",
"LabelAnno":"None"},
"144":{
"Label":"data01",
"Value":4240,
"Source":"User",
"Type":"LocalOrGlobalAddr",
"LabelAnno":"None"},
"196":{
"Label":"Next1",
"Value":4292,
"Source":"User",
"Type":"GlobalAddr",
"LabelAnno":"None"},
"183":{
"Label":"NoCont",
"Value":4279,
"Source":"User",
"Type":"GlobalAddr",
"LabelAnno":"None"}},
"OperandFormats":{ "OperandFormats":{
}, },
"LvTables":{ "LvTables":{
},
"Visualizations":[],
"VisualizationAnimations":[],
"VisualizationSets":{
}} }}

View File

@ -25,9 +25,9 @@ PrintInlineDciString = $013000
.text $14,$00,"string with length/2" .text $14,$00,"string with length/2"
jsl PrintInlineDciString jsl PrintInlineDciString
.shift "DCI string" .shift "DCI string"
jsr L10B5 jsr L1800
jsr L110F jsr L184F
jsr L1108 jsr L1848
brk #$01 brk #$01
.word data01 .word data01
brk #$02 brk #$02
@ -39,7 +39,7 @@ L1085 .byte $a9
.byte $00 .byte $00
sta $ff sta $ff
.byte $ea .byte $ea
rts jmp Next1
PrintInline8String rts PrintInline8String rts
@ -69,25 +69,53 @@ data02 .word _data03
.enc sg_hiascii .enc sg_hiascii
_data03 .text "AllEight" _data03 .text "AllEight"
L10B5 jsr PrintInlineNullString ;split across address change NoCont pla ;split across address change
per $802d pla
rts
L10BA jsr NoCont
.byte $00
.byte $80
L10BF jsr NoCont
.byte $00
.byte $80
Next1 jsr L10BA
jsr _L10CF
clc
jsr L10BF
rts
_L10CF sec
jsr L10BF
rts
.fill 300,$00
.logical $1800
L1800 jsr PrintInlineNullString
per $8778
rtl rtl
.byte $65 .byte $65
.byte $6e .byte $6e
.byte $20 .byte $20
.byte $01 .byte $01
.logical $1100 .here
.logical $1840
.enc sg_ascii .enc sg_ascii
.text "string" .text "string"
.byte $00 .byte $00
.byte $60 .byte $60
L1108 jsl PrintInlineL2String L1848 jsl PrintInlineL2String
asl a asl a
brk #$60 brk #$60
L110F jsr PrintInlineNullString L184F jsr PrintInlineNullString
adc $6e adc $6e
.byte $64 .byte $64
.here .here

View File

@ -18,9 +18,9 @@ PrintInlineDciString equ $013000
strl 'string with length/2' strl 'string with length/2'
jsl PrintInlineDciString jsl PrintInlineDciString
dci 'DCI string' dci 'DCI string'
jsr L10B5 jsr L1800
jsr L110F jsr L184F
jsr L1108 jsr L1848
brk $01 brk $01
dw data01 dw data01
brk $02 brk $02
@ -32,7 +32,7 @@ L1085 dfb $a9
dfb $00 dfb $00
sta $ff sta $ff
dfb $ea dfb $ea
rts jmp Next1
PrintInline8String rts PrintInline8String rts
@ -61,23 +61,50 @@ data02 dw :data03
dfb $80 dfb $80
:data03 asc "AllEight" :data03 asc "AllEight"
L10B5 jsr PrintInlineNullString ;split across address change NoCont pla ;split across address change
per $802d pla
rts
L10BA jsr NoCont
dfb $00
dfb $80
L10BF jsr NoCont
dfb $00
dfb $80
Next1 jsr L10BA
jsr :L10CF
clc
jsr L10BF
rts
:L10CF sec
jsr L10BF
rts
ds 300
org $1800
L1800 jsr PrintInlineNullString
per $8778
rtl rtl
dfb $65 dfb $65
dfb $6e dfb $6e
dfb $20 dfb $20
dfb $01 dfb $01
org $1100 org $1840
asc 'string' asc 'string'
dfb $00 dfb $00
dfb $60 dfb $60
L1108 jsl PrintInlineL2String L1848 jsl PrintInlineL2String
asl A asl A
brk $60 brk $60
L110F jsr PrintInlineNullString L184F jsr PrintInlineNullString
adc $6e adc $6e
dfb $64 dfb $64

View File

@ -21,9 +21,9 @@ PrintInlineDciString = $013000
!text $14,$00,"string with length/2" !text $14,$00,"string with length/2"
jsl PrintInlineDciString jsl PrintInlineDciString
!text "DCI strin",$e7 !text "DCI strin",$e7
jsr L10B5 jsr L1800
jsr L110F jsr L184F
jsr L1108 jsr L1848
!byte $00,$01 !byte $00,$01
!word data01 !word data01
!byte $00,$02 !byte $00,$02
@ -35,7 +35,7 @@ L1085 !byte $a9
!byte $00 !byte $00
sta $ff sta $ff
!byte $ea !byte $ea
rts jmp Next1
PrintInline8String rts PrintInline8String rts
@ -66,24 +66,52 @@ data02 !word @data03
@data03 !text "AllEight" @data03 !text "AllEight"
} }
L10B5 jsr PrintInlineNullString ;split across address change NoCont pla ;split across address change
per $802d pla
rts
L10BA jsr NoCont
!byte $00
!byte $80
L10BF jsr NoCont
!byte $00
!byte $80
Next1 jsr L10BA
jsr @L10CF
clc
jsr L10BF
rts
@L10CF sec
jsr L10BF
rts
!fill 300,$00
!pseudopc $1800 {
L1800 jsr PrintInlineNullString
per $8778
rtl rtl
!byte $65 !byte $65
!byte $6e !byte $6e
!byte $20 !byte $20
!byte $01 !byte $01
!pseudopc $1100 { } ;!pseudopc
!pseudopc $1840 {
!text "string" !text "string"
!byte $00 !byte $00
!byte $60 !byte $60
L1108 jsl PrintInlineL2String L1848 jsl PrintInlineL2String
asl asl
!byte $00,$60 !byte $00,$60
L110F jsr PrintInlineNullString L184F jsr PrintInlineNullString
adc $6e adc $6e
!byte $64 !byte $64
} ;!pseudopc } ;!pseudopc

View File

@ -22,9 +22,9 @@ PrintInlineDciString = $013000
.byte $14,$00,"string with length/2" .byte $14,$00,"string with length/2"
jsl PrintInlineDciString jsl PrintInlineDciString
.byte "DCI strin",$e7 .byte "DCI strin",$e7
jsr L10B5 jsr L1800
jsr L110F jsr L184F
jsr L1108 jsr L1848
brk $01 brk $01
.word data01 .word data01
brk $02 brk $02
@ -36,7 +36,7 @@ L1085: .byte $a9
.byte $00 .byte $00
sta $ff sta $ff
.byte $ea .byte $ea
rts jmp Next1
PrintInline8String: rts PrintInline8String: rts
@ -70,24 +70,52 @@ data02: .word @data03
.endmacro .endmacro
@data03: HiAscii "AllEight" @data03: HiAscii "AllEight"
L10B5: jsr PrintInlineNullString ;split across address change NoCont: pla ;split across address change
per $802d pla
rts
L10BA: jsr NoCont
.byte $00
.byte $80
L10BF: jsr NoCont
.byte $00
.byte $80
Next1: jsr L10BA
jsr @L10CF
clc
jsr L10BF
rts
@L10CF: sec
jsr L10BF
rts
.res 300,$00
; .segment "SEG001"
.org $1800
L1800: jsr PrintInlineNullString
per $8778
rtl rtl
.byte $65 .byte $65
.byte $6e .byte $6e
.byte $20 .byte $20
.byte $01 .byte $01
; .segment "SEG001" ; .segment "SEG002"
.org $1100 .org $1840
.byte "string" .byte "string"
.byte $00 .byte $00
.byte $60 .byte $60
L1108: jsl PrintInlineL2String L1848: jsl PrintInlineL2String
asl A asl A
brk $60 brk $60
L110F: jsr PrintInlineNullString L184F: jsr PrintInlineNullString
adc $6e adc $6e
.byte $64 .byte $64

View File

@ -1,13 +1,15 @@
# 6502bench SourceGen generated linker script for 2022-extension-scripts # 6502bench SourceGen generated linker script for 2022-extension-scripts
MEMORY { MEMORY {
MAIN: file=%O, start=%S, size=65536; MAIN: file=%O, start=%S, size=65536;
# MEM000: file=%O, start=$1000, size=192; # MEM000: file=%O, start=$1000, size=512;
# MEM001: file=%O, start=$1100, size=21; # MEM001: file=%O, start=$1800, size=11;
# MEM002: file=%O, start=$1840, size=21;
} }
SEGMENTS { SEGMENTS {
CODE: load=MAIN, type=rw; CODE: load=MAIN, type=rw;
# SEG000: load=MEM000, type=rw; # SEG000: load=MEM000, type=rw;
# SEG001: load=MEM001, type=rw; # SEG001: load=MEM001, type=rw;
# SEG002: load=MEM002, type=rw;
} }
FEATURES {} FEATURES {}
SYMBOLS {} SYMBOLS {}

View File

@ -66,7 +66,7 @@ edge1 dfb $a9 ;2: LDA imm
dfb $ff ;1: address $eaff dfb $ff ;1: address $eaff
nop ;2: nop ;2:
rts jmp Next1
PrintInline8String rts ;EDIT: set label PrintInline8String rts ;EDIT: set label
PrintInlineRev8String rts ;EDIT: set label PrintInlineRev8String rts ;EDIT: set label
@ -93,10 +93,50 @@ data03 ;EDIT: set label
asc "AllEight" asc "AllEight"
;
; The analyzer is trying to avoid calling the inline JSR check on code it
; has previously visited. There was a bug that caused us to lose the
; no-continue value if code was revisited.
;
; To ensure we revisit the function, we need to call it, then call
; something else that calls it, changing up the flags.
;
NoCont pla
pla
rts
TestNC1 jsr NoCont
dfb $00,$80
TestNC2 jsr NoCont
dfb $00,$80
Next1 jsr TestNC1
jsr PushThing
clc
jsr TestNC2
rts
PushThing
sec
jsr TestNC2
rts
; Failed call tests. Some of these need to be at the end of the file.
; We use an align directive to ensure their offsets don't change as we
; update stuff earlier in the file.
ds \
ds 256
org $1800
; check address split across string ; check address split across string
broken jsr PrintInlineNullString broken jsr PrintInlineNullString
asc 'broken ',01 asc 'broken ',01
org $1100 ;EDIT: split address org $1840 ;EDIT: split address
asc 'string',00 asc 'string',00
rts rts