1
0
mirror of https://github.com/fadden/6502bench.git synced 2025-07-04 13:23:59 +00:00

Rework tutorial for changes in v1.8

Biggest changes were to the address region handling in Tutorial1
and the use of StdInline.cs for the inline strings in Tutorial4.

Also, fixed the off-by-one error in Tutorial1.
This commit is contained in:
Andy McFadden
2021-11-14 09:02:53 -08:00
parent 713bb74cc0
commit 4537f24958
59 changed files with 349 additions and 545 deletions

View File

@ -1,77 +0,0 @@
// 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 {
/// <summary>
/// Sample class for handling a JSR followed by a string prefixed with a 1-byte length.
/// </summary>
public class InlineL1String: MarshalByRefObject, IPlugin, IPlugin_SymbolList,
IPlugin_InlineJsr {
private IApplication mAppRef;
private byte[] mFileData;
// Only one label.
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 Unprepare() {
mAppRef = null;
mFileData = null;
}
public void UpdateSymbolList(List<PlSymbol> 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[offset + 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);
}
}
}

View File

@ -1,153 +0,0 @@
// Copyright 2021 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 {
/// <summary>
/// Formats three different kinds of inline functions:
///
/// PrintInlineL1String: format following data as L1 string
/// PrintInlineNullString*: format following data as null-term string.
/// PrintInlineAddrString*: format following data as 16-bit pointer to null-term string.
/// </summary>
public class InlineMultiData : MarshalByRefObject, IPlugin, IPlugin_SymbolList,
IPlugin_InlineJsr {
private IApplication mAppRef;
private byte[] mFileData;
private AddressTranslate mAddrTrans;
private const string L1STR_NAME = "PrintInlineL1String";
private const string NULLSTR_PREFIX = "PrintInlineNullString";
private const string ADDRSTR_PREFIX = "PrintInlineAddrString";
private enum InlineKind { Unknown = 0, L1Str, NullStr, AddrStr };
private Dictionary<int, InlineKind> mInlineLabels = new Dictionary<int, InlineKind>();
public string Identifier {
get {
return "Inline multi-data formatter";
}
}
public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) {
mAppRef = appRef;
mFileData = fileData;
mAddrTrans = addrTrans;
mAppRef.DebugLog("InlineMultiData(id=" +
AppDomain.CurrentDomain.Id + "): prepare()");
}
public void Unprepare() {
mAppRef = null;
mFileData = null;
mAddrTrans = null;
}
public void UpdateSymbolList(List<PlSymbol> plSyms) {
mInlineLabels.Clear();
// Find matching symbols. Save the symbol's value (its address) and the type.
// We want an exact match on L1STR_NAME, and prefix matches on the other two.
foreach (PlSymbol sym in plSyms) {
if (sym.Label.Equals(L1STR_NAME)) {
mInlineLabels.Add(sym.Value, InlineKind.L1Str);
} else if (sym.Label.StartsWith(NULLSTR_PREFIX)) {
mInlineLabels.Add(sym.Value, InlineKind.NullStr);
} else if (sym.Label.StartsWith(ADDRSTR_PREFIX)) {
mInlineLabels.Add(sym.Value, InlineKind.AddrStr);
}
}
mAppRef.DebugLog("Found matches for " + mInlineLabels.Count + " labels");
}
public bool IsLabelSignificant(string beforeLabel, string afterLabel) {
return DoesLabelMatch(beforeLabel) || DoesLabelMatch(afterLabel);
}
private static bool DoesLabelMatch(string label) {
return (label.Equals(L1STR_NAME) ||
label.StartsWith(NULLSTR_PREFIX) ||
label.StartsWith(ADDRSTR_PREFIX));
}
public void CheckJsr(int offset, int operand, out bool noContinue) {
noContinue = false;
InlineKind kind;
if (!mInlineLabels.TryGetValue(operand, out kind)) {
return;
}
switch (kind) {
case InlineKind.L1Str:
// Length-delimited ASCII string
FormatL1String(offset + 3);
break;
case InlineKind.NullStr:
// Null-terminated ASCII string.
FormatNullTermString(offset + 3);
break;
case InlineKind.AddrStr:
// Pointer to data. Format as address. Start by confirming next two
// bytes are inside the file bounds.
if (!Util.IsInBounds(mFileData, offset + 3, 2)) {
return;
}
// Format 16-bit value as an address.
mAppRef.SetInlineDataFormat(offset + 3, 2,
DataType.NumericLE, DataSubType.Address, null);
// Now format the string that the address points to. Extract the
// address from the operand.
int strAddr = Util.GetWord(mFileData, offset + 3, 2, false);
// Convert the address to a file offset. Returns -1 if not in file bounds.
int strOff = mAddrTrans.AddressToOffset(offset, strAddr);
// Format it if it's in bounds.
FormatNullTermString(strOff);
break;
}
}
private void FormatL1String(int offset) {
if (offset < 0 || offset >= mFileData.Length) {
return; // length byte is not inside file
}
int len = mFileData[offset];
if (offset + 1 + len > mFileData.Length) {
mAppRef.DebugLog("L1 string ran off end of file at +" +
(offset + 1).ToString("x6"));
return;
}
// Assuming ASCII.
mAppRef.SetInlineDataFormat(offset, len + 1,
DataType.StringL8, DataSubType.Ascii, null);
}
private void FormatNullTermString(int offset) {
if (offset < 0 || offset >= mFileData.Length) {
return; // start is not inside file
}
// search for the terminating null byte
int nullOff = offset;
while (nullOff < mFileData.Length) {
if (mFileData[nullOff] == 0x00) {
break;
}
nullOff++;
}
if (nullOff == mFileData.Length) {
mAppRef.DebugLog("Unable to find end of null-terminated string at +" +
offset.ToString("x6"));
return;
}
// Assuming ASCII.
mAppRef.SetInlineDataFormat(offset, nullOff - offset + 1,
DataType.StringNullTerm, DataSubType.Ascii, null);
}
}
}

View File

@ -1,81 +0,0 @@
// 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 {
/// <summary>
/// Sample class for handling a JSR followed by an inline null-terminated string. Any
/// label that starts with "PrintLineNullString" is matched.
/// </summary>
public class InlineNullTermString : MarshalByRefObject, IPlugin, IPlugin_SymbolList,
IPlugin_InlineJsr {
private IApplication mAppRef;
private byte[] mFileData;
private const string LABEL_PREFIX = "PrintInlineNullString";
private Dictionary<int, PlSymbol> mNullStringAddrs = new Dictionary<int, PlSymbol>();
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 Unprepare() {
mAppRef = null;
mFileData = null;
}
public void UpdateSymbolList(List<PlSymbol> plSyms) {
mNullStringAddrs.Clear();
// Find matching symbols.
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] == 0x00) {
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);
}
}
}

View File

@ -1,62 +1,61 @@
; Copyright 2018 faddenSoft. All Rights Reserved.
; See the LICENSE.txt file for distribution terms (Apache 2.0).
;
; Assembler: Merlin 32
INPUT equ $3000
OUTPUT equ $0400
org $1000
ldy #END-AFTER
copy lda BEFORE,y
sta AFTER,y
dey
bmi done
bpl copy
dfb $00
stuff asc 'hello!'
done jmp AFTER
BEFORE
org $2000
AFTER
lda INPUT ;expecting 0-3
cmp #4
blt :valid
lda #4 ;error message
:valid asl A
tax
lda stringtab,x ;set load to address
sta _load+1
lda stringtab+1,x
sta _load+2
ldy #12 ;fixed-width strings
_load lda $0000,y ;self-modifying code
ora #$80
sta OUTPUT,y
dey
bpl _load
rts
stringtab
dw string0
dw string1
dw string2
dw string3
dw stringX
dfb $00
string0 asc 'string zero '
string1 asc 'string one '
string2 asc 'string two '
string3 asc 'string three '
stringX asc 'invalid index'
END
; Copyright 2018 faddenSoft. All Rights Reserved.
; See the LICENSE.txt file for distribution terms (Apache 2.0).
;
; Assembler: Merlin 32
INPUT equ $3000
OUTPUT equ $0400
org $1000
ldy #END-AFTER-1
copy lda BEFORE,y
sta AFTER,y
dey
bpl copy
bmi done
dfb $00
stuff asc 'hello!'
done jmp AFTER
BEFORE
org $2000
AFTER
lda INPUT ;expecting 0-3
cmp #4
blt :valid
lda #4 ;error message
:valid asl A
tax
lda stringtab,x ;set load to address
sta _load+1
lda stringtab+1,x
sta _load+2
ldy #12 ;fixed-width strings
_load lda $0000,y ;self-modifying code
ora #$80
sta OUTPUT,y
dey
bpl _load
rts
stringtab
dw string0
dw string1
dw string2
dw string3
dw stringX
dfb $00
string0 asc 'string zero '
string1 asc 'string one '
string2 asc 'string two '
string3 asc 'string three '
stringX asc 'invalid index'
END