mirror of
https://github.com/fadden/6502bench.git
synced 2026-04-26 12:18:26 +00:00
Add jump table formatting
When asked to tag an absolute JMP instruction ($4C) as code, we now scan forward to see if it's the start of a series of JMPs. If we find more than one, we offer the opportunity to tag the entire set all at once. A series of unformatted JMPs has been added to 20200-ui-edge-cases for manual testing. (issue #22)
This commit is contained in:
@@ -4310,6 +4310,14 @@ namespace SourceGen {
|
||||
public void MarkAsType(CodeAnalysis.AnalyzerTag atag, bool firstByteOnly) {
|
||||
RangeSet sel;
|
||||
|
||||
if (atag == CodeAnalysis.AnalyzerTag.Code && SelectionAnalysis.mNumItemsSelected == 1) {
|
||||
// We're applying a code tag to a single line. Analyze the file to see if special
|
||||
// handling for jump tables can be applied here.
|
||||
if (TryMarkJumpTable(out bool cancel) || cancel) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstByteOnly) {
|
||||
sel = new RangeSet();
|
||||
foreach (int index in mMainWin.CodeDisplayList.SelectedIndices) {
|
||||
@@ -4335,6 +4343,98 @@ namespace SourceGen {
|
||||
sel = OffsetSetFromSelected();
|
||||
}
|
||||
|
||||
DoMarkAsType(atag, sel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts special handling for "jump tables", i.e. chunks of code with multiple
|
||||
/// consecutive JMP abs instructions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The current scan will skip over already-formatted JMP instructions to look for more
|
||||
/// beyond. This is extended behavior to make it easier to tag something when the first
|
||||
/// JMP is already tagged. However, if it only finds one unformatted entry it won't fire.
|
||||
/// The user really ought to be tagging the first unformatted $4c byte; it might be better
|
||||
/// to require this.
|
||||
/// </remarks>
|
||||
/// <param name="cancel">Result: true if user asked to cancel the operation.</param>
|
||||
/// <returns>True if a jump table was found and processed.</returns>
|
||||
private bool TryMarkJumpTable(out bool cancel) {
|
||||
cancel = false;
|
||||
int selIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
|
||||
int offset = CodeLineList[selIndex].FileOffset;
|
||||
byte JMP = OpDef.OpJMP_Abs.Opcode; // 0x4c
|
||||
|
||||
RangeSet sel = new RangeSet();
|
||||
while (offset + 2 < mProject.FileDataLength) {
|
||||
if (mProject.FileData[offset] != JMP) {
|
||||
break;
|
||||
}
|
||||
Anattrib attr0 = mProject.GetAnattrib(offset);
|
||||
bool isInst = attr0.IsInstructionStart;
|
||||
bool halt = false;
|
||||
|
||||
// Confirm that all bytes are data/inline-data, or are part of a previously-known
|
||||
// JMP instruction.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Anattrib attr = mProject.GetAnattrib(offset + i);
|
||||
if (attr.IsInstruction && !isInst) {
|
||||
halt = true; // found instruction, but offset+0 wasn't an inst start
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't continue if a user label is defined here, unless it's on the JMP
|
||||
// opcode byte.
|
||||
if (i != 0 && mProject.UserLabels.TryGetValue(offset + i, out Symbol unused)) {
|
||||
halt = true;
|
||||
break;
|
||||
}
|
||||
// Don't continue if any byte has been formatted.
|
||||
if (mProject.OperandFormats.TryGetValue(offset + i, out FormatDescriptor unu)) {
|
||||
halt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (halt) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If not already marked as an instruction, add the JMP opcode byte to the set.
|
||||
if (!attr0.IsInstructionStart) {
|
||||
Debug.WriteLine("JumpTab: adding offset +" + offset.ToString("x6"));
|
||||
sel.Add(offset);
|
||||
}
|
||||
|
||||
offset += 3;
|
||||
}
|
||||
if (sel.Count <= 1) {
|
||||
// Didn't find anything to do, or found only one entry. Let the general code
|
||||
// handle it.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ask the user to confirm.
|
||||
string msg = string.Format(Res.Strings.ANALYZER_TAG_JMP_TABLE_FMT, sel.Count);
|
||||
MessageBoxResult result =
|
||||
MessageBox.Show(msg, Res.Strings.ANALYZER_TAG_JMP_TABLE_CAPTION,
|
||||
MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
|
||||
switch (result) {
|
||||
case MessageBoxResult.Cancel:
|
||||
default:
|
||||
cancel = true;
|
||||
return false;
|
||||
case MessageBoxResult.No:
|
||||
return false;
|
||||
case MessageBoxResult.Yes:
|
||||
DoMarkAsType(CodeAnalysis.AnalyzerTag.Code, sel);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the change set for analyzer tag updates.
|
||||
/// </summary>
|
||||
private void DoMarkAsType(CodeAnalysis.AnalyzerTag atag, RangeSet sel) {
|
||||
TypedRangeSet newSet = new TypedRangeSet();
|
||||
TypedRangeSet undoSet = new TypedRangeSet();
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ limitations under the License.
|
||||
<system:String x:Key="str_AbbrevConstant">Const</system:String>
|
||||
<system:String x:Key="str_AbbrevStackRelative">StkRl</system:String>
|
||||
<system:String x:Key="str_AnalyzerTagMultiChk">Setting start/stop tags on multiple consecutive bytes is rarely a good idea. Continue?</system:String>
|
||||
<system:String x:Key="str_AnalyzerTagJmpTableCaption">Format As Jump Table?</system:String>
|
||||
<system:String x:Key="str_AnalyzerTagJmpTableFmt">Found {0} consecutive JMP instructions. Do you want to format all of them, rather than just the first?</system:String>
|
||||
<system:String x:Key="str_AsmLatestVersion">[latest version]</system:String>
|
||||
<system:String x:Key="str_AsmMatchFailure">output DOES NOT match data file</system:String>
|
||||
<system:String x:Key="str_AsmMatchSuccess">output matches data file</system:String>
|
||||
|
||||
@@ -31,6 +31,10 @@ namespace SourceGen.Res {
|
||||
(string)Application.Current.FindResource("str_AbbrevStackRelative");
|
||||
public static string ANALYZER_TAG_MULTI_CHK =
|
||||
(string)Application.Current.FindResource("str_AnalyzerTagMultiChk");
|
||||
public static string ANALYZER_TAG_JMP_TABLE_CAPTION =
|
||||
(string)Application.Current.FindResource("str_AnalyzerTagJmpTableCaption");
|
||||
public static string ANALYZER_TAG_JMP_TABLE_FMT =
|
||||
(string)Application.Current.FindResource("str_AnalyzerTagJmpTableFmt");
|
||||
public static string ASM_LATEST_VERSION =
|
||||
(string)Application.Current.FindResource("str_AsmLatestVersion");
|
||||
public static string ASM_MATCH_FAILURE =
|
||||
|
||||
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
### 6502bench SourceGen dis65 v1.0 ###
|
||||
{
|
||||
"_ContentVersion":6,
|
||||
"FileDataLength":193,
|
||||
"FileDataCrc32":960553569,
|
||||
"FileDataLength":222,
|
||||
"FileDataCrc32":1648676931,
|
||||
"ProjectProps":{
|
||||
"CpuName":"6502",
|
||||
"IncludeUndocumentedInstr":false,
|
||||
@@ -132,7 +132,7 @@
|
||||
{
|
||||
"Offset":53,
|
||||
"Addr":8704,
|
||||
"Length":140,
|
||||
"Length":169,
|
||||
"PreLabel":"",
|
||||
"DisallowInward":false,
|
||||
"DisallowOutward":false,
|
||||
@@ -197,13 +197,6 @@
|
||||
"Type":"GlobalAddr",
|
||||
"LabelAnno":"None"},
|
||||
|
||||
"192":{
|
||||
"Label":"done",
|
||||
"Value":8843,
|
||||
"Source":"User",
|
||||
"Type":"GlobalAddr",
|
||||
"LabelAnno":"None"},
|
||||
|
||||
"163":{
|
||||
"Label":"bitsy",
|
||||
"Value":8814,
|
||||
|
||||
@@ -64,7 +64,7 @@ next1 lda addr1
|
||||
bcc bitsy+1
|
||||
bitsy .byte $2c
|
||||
lda #$ff
|
||||
jmp done
|
||||
jmp _L228B
|
||||
|
||||
.word FOO
|
||||
.word FOO_5
|
||||
@@ -79,6 +79,22 @@ bitsy .byte $2c
|
||||
.word zf4
|
||||
.byte $80
|
||||
|
||||
done rts
|
||||
_L228B nop
|
||||
jmp _L22A8
|
||||
|
||||
.byte $4c
|
||||
.byte $8b
|
||||
.text $22,"L9",$22,"L"
|
||||
.byte $95
|
||||
.byte $22
|
||||
.byte $4c
|
||||
.byte $00
|
||||
.byte $30
|
||||
.byte $ea
|
||||
.text "L9",$22,"L"
|
||||
.byte $00
|
||||
.text "1LLLLLL"
|
||||
|
||||
_L22A8 rts
|
||||
|
||||
.here
|
||||
|
||||
@@ -63,7 +63,7 @@ next1 lda addr1
|
||||
bcc bitsy+1
|
||||
bitsy !byte $2c
|
||||
lda #$ff
|
||||
jmp done
|
||||
jmp @L228B
|
||||
|
||||
!word FOO
|
||||
!word FOO_5
|
||||
@@ -78,6 +78,22 @@ bitsy !byte $2c
|
||||
!word zf4
|
||||
!byte $80
|
||||
|
||||
done rts
|
||||
@L228B nop
|
||||
jmp @L22A8
|
||||
|
||||
!byte $4c
|
||||
!byte $8b
|
||||
!text $22,"L9",$22,"L"
|
||||
!byte $95
|
||||
!byte $22
|
||||
!byte $4c
|
||||
!byte $00
|
||||
!byte $30
|
||||
!byte $ea
|
||||
!text "L9",$22,"L"
|
||||
!byte $00
|
||||
!text "1LLLLLL"
|
||||
|
||||
@L22A8 rts
|
||||
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ next1: lda addr1
|
||||
bcc bitsy+1
|
||||
bitsy: .byte $2c
|
||||
lda #$ff
|
||||
jmp done
|
||||
jmp @L228B
|
||||
|
||||
.word FOO
|
||||
.word FOO_5
|
||||
@@ -74,5 +74,21 @@ bitsy: .byte $2c
|
||||
.word zf4
|
||||
.byte $80
|
||||
|
||||
done: rts
|
||||
@L228B: nop
|
||||
jmp @L22A8
|
||||
|
||||
.byte $4c
|
||||
.byte $8b
|
||||
.byte $22,"L9",$22,"L"
|
||||
.byte $95
|
||||
.byte $22
|
||||
.byte $4c
|
||||
.byte $00
|
||||
.byte $30
|
||||
.byte $ea
|
||||
.byte "L9",$22,"L"
|
||||
.byte $00
|
||||
.byte "1LLLLLL"
|
||||
|
||||
@L22A8: rts
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ next1 lda addr1
|
||||
bcc bitsy+1
|
||||
bitsy dfb $2c
|
||||
lda #$ff
|
||||
jmp done
|
||||
jmp :L228B
|
||||
|
||||
dw FOO
|
||||
dw FOO_5
|
||||
@@ -73,5 +73,21 @@ bitsy dfb $2c
|
||||
dw zf4
|
||||
dfb $80
|
||||
|
||||
done rts
|
||||
:L228B nop
|
||||
jmp :L22A8
|
||||
|
||||
dfb $4c
|
||||
dfb $8b
|
||||
asc '"L9"L'
|
||||
dfb $95
|
||||
dfb $22
|
||||
dfb $4c
|
||||
dfb $00
|
||||
dfb $30
|
||||
dfb $ea
|
||||
asc 'L9"L'
|
||||
dfb $00
|
||||
asc '1LLLLLL'
|
||||
|
||||
:L22A8 rts
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ next1
|
||||
bcc bitsy+1 ;EDIT: set symbol "bitsy"
|
||||
bitsy bit $ffa9 ;EDIT: set label "bitsy" on BIT instruction
|
||||
|
||||
jmp done
|
||||
jmp next2
|
||||
|
||||
; EDIT: format as 16-bit addresses
|
||||
dw FOO
|
||||
@@ -102,4 +102,18 @@ bitsy bit $ffa9 ;EDIT: set label "bitsy" on BIT instruction
|
||||
dw zf4
|
||||
dfb $80
|
||||
|
||||
done rts
|
||||
next2 nop
|
||||
|
||||
; jump table
|
||||
jmp next3
|
||||
jmp next2
|
||||
jmp next1
|
||||
self jmp self
|
||||
jmp plataddr
|
||||
nop
|
||||
jmp next1
|
||||
jmp OVERL
|
||||
jmp $4c4c
|
||||
jmp $4c4c
|
||||
|
||||
next3 rts
|
||||
|
||||
@@ -532,8 +532,17 @@ or simply by hitting <kbd class="key">Ctrl+D</kbd>. Hit that, tag the
|
||||
byte or bytes, then hit it again to re-enable the
|
||||
string & fill analyzer.</p>
|
||||
<p>Another approach is to use the <samp>Toggle Single-Byte Format</samp>
|
||||
menu item to "flatten" the item, explicitly formatting everything as
|
||||
individual hex bytes.</p>
|
||||
action (<kbd class="key">Ctrl+B</kbd>) to "flatten" the item, explicitly
|
||||
formatting everything as individual hex bytes. This formats the bytes
|
||||
as data, however, so it's not recommended for sections that you think
|
||||
are actually code.</p>
|
||||
|
||||
<p>As a special case, if you tag the start of an absolute JMP instruction
|
||||
($4C) as code, the next several bytes will be checked to see if they hold
|
||||
a consecutive series of JMPs. If so, you will be offered the opportunity
|
||||
to tag all of them at once. This can be convenient for "jump tables".
|
||||
The forward scan will halt if formatted data or mis-placed labels are
|
||||
encountered.</p>
|
||||
|
||||
|
||||
<h3 id="address-table">Format Address Table</h3>
|
||||
|
||||
Reference in New Issue
Block a user