fpdebug - Add xtensa support

This commit is contained in:
ccrause 2024-01-14 13:12:42 +02:00 committed by Martin
parent 0ce49ed855
commit c6781743e8
10 changed files with 1639 additions and 5 deletions

View File

@ -11,7 +11,7 @@ type
// Target information, could be different from host debugger
TMachineType = (mtNone, mtSPARC, mt386, mt68K, mtPPC, mtPPC64, mtARM, mtARM64,
mtOLD_ALPHA, mtIA_64, mtX86_64, mtAVR8, mtALPHA,
mtMIPS, mtMIPSEL,mtLA64);
mtMIPS, mtMIPSEL,mtLA64, mtXTENSA);
TBitness = (bNone, b32, b64);
TByteOrder = (boNone, boLSB, boMSB);
TOperatingSystem = (osNone, osBSD, osDarwin, osEmbedded, osLinux, osUnix, osMac, osWindows);

File diff suppressed because it is too large Load Diff

View File

@ -856,7 +856,7 @@ begin
EnterCriticalSection(fCS);
try
result := SendCmdWaitForReply('g', reply) and ((length(reply) > 4) and (reply[1] <> 'E'))
and (length(reply) = 2*sz);
and (length(reply) >= 2*sz);
finally
LeaveCriticalSection(fCS);
end;

View File

@ -58,6 +58,9 @@ type
procedure SetRegisterValue(AName: string; AValue: QWord); override;
procedure StoreRegisters; override;
procedure RestoreRegisters; override;
// Read access to internal registers
property InternalRegs: TInitializedRegisters read FRegs;
end;
{ TDbgRspProcess }

View File

@ -0,0 +1,528 @@
unit FpDbgXtensaClasses;
{$mode objfpc}{$H+}
{$packrecords c}
{$modeswitch advancedrecords}
interface
uses
Classes,
SysUtils,
FpDbgClasses,
DbgIntfBaseTypes, DbgIntfDebuggerBase,
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif},
FpDbgRsp, FpDbgRspClasses, FpDbgCommon, FpdMemoryTools,
FpErrorMessages;
const
SPindexDwarf = 1; // Also known as a1, Index refers to active window
PCIndexDwarf = 100; // Dwarf index (assumed)
nPC = 'PC';
nReturnPC = 'a0';
nSP = 'a1';
ReturnPCIndexDwarf = 0; // Also known as a0, Index refers to active window
WindowBaseIndex = 65;
WindowStartIndex = 66;
PSIndex = 67;
nWindowBase = 'windowbase';
nWindowStart = 'windowstart';
nPS = 'ps';
type
{ TDbgXtensaThread }
TDbgXtensaThread = class(TDbgRspThread)
private
const
lastCPURegIndex = 64; // PC + 64 registers
// Offsets to load specific registers from register data
// These are byte offsets, to be used when reading from the raw byte register data
WindowBaseOffset = 276; // Dependent on Windowed option...
WindowStartOffset = 280; // "
PSOffset = 292; // "
RegArrayByteLength = 420; // Depends on qemu options, but this seems to be the smallest size to handle. Only show basic registers, so rest can be ignored for now.
// Ordered registers index
// PC, ar0..ar63, wb, ws, ps
PCindex = 0; // Offset into raw register array
protected
procedure LoadRegisterCache; override;
procedure SaveRegisterCache; override;
function GetReturnPC: TDbgPtr;
function GetStackUnwinder: TDbgStackUnwinder; override;
public
destructor Destroy; override;
procedure LoadRegisterValues; override;
procedure SetRegisterValue(AName: string; AValue: QWord); override;
function GetInstructionPointerRegisterValue: TDbgPtr; override;
function GetStackBasePointerRegisterValue: TDbgPtr; override;
// procedure SetInstructionPointerRegisterValue(AValue: TDbgPtr); override;
function GetStackPointerRegisterValue: TDbgPtr; override;
// procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
end;
{ TDbgXtensaProcess }
TDbgXtensaProcess = class(TDbgRspProcess)
private const
FNumRegisters = 68; // pc, ar0..ar63, WindowBase, WindowStart, PS
protected
function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; override;
function CreateBreakPointTargetHandler: TFpBreakPointTargetHandler; override;
public
class function isSupported(target: TTargetDescriptor): boolean; override;
constructor Create(const AFileName: string; AnOsClasses: TOSDbgClasses;
AMemManager: TFpDbgMemManager; AMemModel: TFpDbgMemModel;
AProcessConfig: TDbgProcessConfig = nil); override;
destructor Destroy; override;
end;
TXtensaBreakInfo = object
const
_CODE: DWord = $F06D; // ILL.N -> this is a 16 bit narrow instruction
end;
TXtensaBreakPointTargetHandler = specialize TRspBreakPointTargetHandler<Word, TXtensaBreakInfo>;
{ TDbgStackUnwinderXtensa }
TDbgStackUnwinderXtensa = class(TDbgStackUnwinder)
private
FThread: TDbgThread;
FProcess: TDbgProcess;
FAddressSize: Integer;
FLastFrameBaseIncreased: Boolean;
FCodeReadErrCnt: integer;
FWindowBase: uint32;
FWindowStart: uint32;
protected
property Process: TDbgProcess read FProcess;
property Thread: TDbgThread read FThread;
property AddressSize: Integer read FAddressSize;
public
constructor Create(AProcess: TDbgProcess);
procedure InitForThread(AThread: TDbgThread); override;
procedure InitForFrame(ACurrentFrame: TDbgCallstackEntry; out CodePointer,
StackPointer, FrameBasePointer: TDBGPtr); override;
procedure GetTopFrame(out CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
out ANewFrame: TDbgCallstackEntry); override;
function Unwind(AFrameIndex: integer; var CodePointer, StackPointer,
FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; out
ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; override;
end;
implementation
uses
FpDbgDisasXtensa, FpDbgDwarfDataClasses;
var
DBG_VERBOSE, DBG_WARNINGS: PLazLoggerLogGroup;
{ TDbgXtensaThread }
procedure TDbgXtensaThread.LoadRegisterCache;
var
regs: TBytes;
i: integer;
begin
if not FRegs.Initialized then
begin
SetLength(regs, RegArrayByteLength);
FRegs.Initialized := TDbgXtensaProcess(Process).RspConnection.ReadRegisters(regs[0], length(regs));
// 32 bit LE registers
for i := 0 to lastCPURegIndex do // PC, ar0..ar63
FRegs.regs[i] := regs[4*i] + (regs[4*i + 1] shl 8) + (regs[4*i + 2] shl 16) + (regs[4*i + 3] shl 24);
i := WindowBaseOffset;
FRegs.regs[WindowBaseIndex] := regs[i] + (regs[i + 1] shl 8) + (regs[i + 2] shl 16) + (regs[i + 3] shl 24);
i := WindowStartOffset;
FRegs.regs[WindowStartIndex] := regs[i] + (regs[i + 1] shl 8) + (regs[i + 2] shl 16) + (regs[i + 3] shl 24);
i := PSOffset;
FRegs.regs[PSIndex] := regs[i] + (regs[i + 1] shl 8) + (regs[i + 2] shl 16) + (regs[i + 3] shl 24);
end;
end;
procedure TDbgXtensaThread.SaveRegisterCache;
procedure CopyDWordToByteArray( const val: DWord; regs: PByte);
begin
regs[0] := val;
regs[1] := val shr 8;
regs[2] := val shr 16;
regs[3] := val shr 24;
end;
var
regs: TBytes;
i: Integer;
begin
exit; // TODO: Need to determine which other registers will also change in case of a subroutine call on the debugger side.
if FRegsChanged then
begin
SetLength(regs, RegArrayByteLength);
for i := 0 to lastCPURegIndex do
CopyDWordToByteArray(FRegs.regs[i], @regs[4*i]);
CopyDWordToByteArray(FRegs.regs[WindowBaseIndex], @regs[WindowBaseOffset]);
CopyDWordToByteArray(FRegs.regs[WindowStartIndex], @regs[WindowStartOffset]);
CopyDWordToByteArray(FRegs.regs[PSIndex], @regs[PSOffset]);
end;
end;
function TDbgXtensaThread.GetReturnPC: TDbgPtr;
var
wb: TDBGPtr;
begin
Result := 0;
if TDbgXtensaProcess(Process).FIsTerminating then
begin
DebugLn(DBG_WARNINGS, 'TDbgXtensaProcess.GetStackPointerRegisterValue called while FIsTerminating is set.');
exit;
end;
if not ReadThreadState then
exit;
DebugLn(DBG_VERBOSE, 'TDbgXtensaProcess.GetStackPointerRegisterValue requesting stack registers.');
// Windowed ABI: retPC in a0
ReadDebugReg(WindowBaseIndex, wb);
wb := wb * 4;
ReadDebugReg(wb+1, result);
end;
function TDbgXtensaThread.GetStackUnwinder: TDbgStackUnwinder;
begin
if FUnwinder = nil then
FUnwinder := TDbgStackUnwinderXtensa.Create(Process);
Result := FUnwinder;
end;
destructor TDbgXtensaThread.Destroy;
begin
if Assigned(FUnwinder) then
FreeAndNil(FUnwinder);
inherited Destroy;
end;
procedure TDbgXtensaThread.LoadRegisterValues;
var
i, j, wb: integer;
begin
if TDbgXtensaProcess(Process).FIsTerminating or (TDbgXtensaProcess(Process).FStatus = SIGHUP) then
begin
DebugLn(DBG_WARNINGS, 'TDbgXtensaProcess.LoadRegisterValues called while FIsTerminating is set.');
exit;
end;
if not ReadThreadState then
exit;
LoadRegisterCache;
if FRegs.Initialized then
begin
{ FRegs.regs start with PC, then the 64 core registers,
then a bunch of other system and optionally user registers.
Only load currently visible registers, PC and WindowBaseOffset for now.
The start of currently visible core registers is given by WindowBaseOffset }
// WindowBaseOffset is a 4 bit value
wb := 4 * (FRegs.regs[WindowBaseIndex] and $0F);
// Returned registers start with PC, followed by core register file, so offset index by 1
for i := 0 to 15 do
begin
// Index should wrap around to start of register file
j := ((wb+i) and 63) + 1;
FRegisterValueList.DbgRegisterAutoCreate['a'+IntToStr(i)].SetValue(FRegs.regs[j], IntToStr(FRegs.regs[j]), 4, i);
end;
FRegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(FRegs.regs[0], IntToStr(FRegs.regs[0]), 4, 0);
FRegisterValueList.DbgRegisterAutoCreate[nWindowBase].SetValue(byte(FRegs.regs[WindowBaseIndex]), IntToStr(byte(FRegs.regs[WindowBaseIndex])),1 , WindowBaseIndex);
FRegisterValueList.DbgRegisterAutoCreate[nWindowStart].SetValue(word(FRegs.regs[WindowStartIndex]), IntToStr(word(FRegs.regs[WindowStartIndex])),2 , WindowStartIndex);
FRegisterValueList.DbgRegisterAutoCreate[nPS].SetValue(byte(FRegs.regs[PSIndex]), IntToStr(byte(FRegs.regs[PSIndex])),1 , 0);
FRegisterValueList.DbgRegisterAutoCreate[nPS].SetValue(byte(FRegs.regs[PSIndex]), IntToStr(byte(FRegs.regs[PSIndex])),1 , 0);
FRegisterValueListValid := true;
end
else
DebugLn(DBG_WARNINGS, 'Warning: Could not update registers');
end;
procedure TDbgXtensaThread.SetRegisterValue(AName: string; AValue: QWord);
var
i, err: integer;
res: boolean;
begin
if AName[1] = 'a' then
begin
val(copy(AName, 2, length(Aname)), i, err);
res := (err = 0) and (i <= 15);
if res then
res := TDbgRspProcess(Process).RspConnection.WriteDebugReg(i, byte(AValue));
end
else if AName = nPC then
res := TDbgRspProcess(Process).RspConnection.WriteDebugReg(PCindex, dword(AValue))
else
res := False;
if not res then
DebugLn(DBG_WARNINGS, 'Error setting register %s to %u', [AName, AValue]);
end;
function TDbgXtensaThread.GetInstructionPointerRegisterValue: TDbgPtr;
begin
Result := 0;
if TDbgXtensaProcess(Process).FIsTerminating then
begin
DebugLn(DBG_WARNINGS, 'TDbgXtensaProcess.GetInstructionPointerRegisterValue called while FIsTerminating is set.');
exit;
end;
if not ReadThreadState then
exit;
DebugLn(DBG_VERBOSE, 'TDbgXtensaProcess.GetInstructionPointerRegisterValue requesting PC.');
ReadDebugReg(PCindex, Result);
end;
function TDbgXtensaThread.GetStackBasePointerRegisterValue: TDbgPtr;
begin
Result := 0;
if TDbgXtensaProcess(Process).FIsTerminating then
begin
DebugLn(DBG_WARNINGS, 'TDbgXtensaProcess.GetStackBasePointerRegisterValue called while FIsTerminating is set.');
exit;
end;
if not ReadThreadState then
exit;
DebugLn(DBG_VERBOSE, 'TDbgXtensaProcess.GetStackBasePointerRegisterValue requesting base registers.');
// Todo: check FPC implementation of stack frame for ESP32
Result := GetStackPointerRegisterValue;
end;
function TDbgXtensaThread.GetStackPointerRegisterValue: TDbgPtr;
var
wb: TDBGPtr;
begin
Result := 0;
if TDbgXtensaProcess(Process).FIsTerminating then
begin
DebugLn(DBG_WARNINGS, 'TDbgXtensaProcess.GetStackPointerRegisterValue called while FIsTerminating is set.');
exit;
end;
if not ReadThreadState then
exit;
DebugLn(DBG_VERBOSE, 'TDbgXtensaProcess.GetStackPointerRegisterValue requesting stack registers.');
// Windowed ABI: SP in a1
if ReadDebugReg(WindowBaseIndex, wb) then
begin
wb := wb * 4;
ReadDebugReg(wb+2, result);
end
else
Result := 0;
end;
{ TDbgXtensaProcess }
function TDbgXtensaProcess.CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread;
begin
IsMainThread:=False;
if AthreadIdentifier<>feInvalidHandle then
begin
IsMainThread := AthreadIdentifier=ProcessID;
result := TDbgXtensaThread.Create(Self, AthreadIdentifier, AthreadIdentifier);
end
else
result := nil;
end;
function
TDbgXtensaProcess.CreateBreakPointTargetHandler: TFpBreakPointTargetHandler;
begin
Result := TXtensaBreakPointTargetHandler.Create(Self);
end;
constructor TDbgXtensaProcess.Create(const AFileName: string;
AnOsClasses: TOSDbgClasses; AMemManager: TFpDbgMemManager;
AMemModel: TFpDbgMemModel; AProcessConfig: TDbgProcessConfig);
begin
FRegArrayLength := FNumRegisters;
inherited Create(AFileName, AnOsClasses, AMemManager, AMemModel, AProcessConfig);
end;
destructor TDbgXtensaProcess.Destroy;
begin
inherited Destroy;
end;
class function TDbgXtensaProcess.isSupported(target: TTargetDescriptor): boolean;
begin
result := (target.OS = osEmbedded) and (target.machineType = mtXTENSA);
end;
{ TDbgStackUnwinderXtensa }
constructor TDbgStackUnwinderXtensa.Create(AProcess: TDbgProcess);
begin
FProcess := AProcess;
FAddressSize := 4;
FCodeReadErrCnt := 0;
end;
procedure TDbgStackUnwinderXtensa.InitForThread(AThread: TDbgThread);
begin
FThread := AThread;
end;
procedure TDbgStackUnwinderXtensa.InitForFrame(
ACurrentFrame: TDbgCallstackEntry; out CodePointer, StackPointer,
FrameBasePointer: TDBGPtr);
var
R: TDbgRegisterValue;
begin
CodePointer := ACurrentFrame.AnAddress;
// Frame pointer is a7 (if used)
FrameBasePointer := ACurrentFrame.FrameAdress;
StackPointer := 0;
R := ACurrentFrame.RegisterValueList.FindRegisterByDwarfIndex(SPindexDwarf);
if R = nil then exit;
StackPointer := R.NumValue;
end;
procedure TDbgStackUnwinderXtensa.GetTopFrame(out CodePointer, StackPointer,
FrameBasePointer: TDBGPtr; out ANewFrame: TDbgCallstackEntry);
var
i: Integer;
R: TDbgRegisterValue;
begin
FLastFrameBaseIncreased := True;
CodePointer := Thread.GetInstructionPointerRegisterValue;
StackPointer := Thread.GetStackPointerRegisterValue;
FrameBasePointer := Thread.GetStackBasePointerRegisterValue;
ANewFrame := TDbgCallstackEntry.create(Thread, 0, FrameBasePointer, CodePointer);
// Frame pointer may not have been updated yet
if FrameBasePointer > StackPointer then
FrameBasePointer := StackPointer;
i := Thread.RegisterValueList.Count;
while i > 0 do begin
dec(i);
R := Thread.RegisterValueList[i];
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[R.Name].SetValue(R.NumValue, R.StrValue, R.Size, R.DwarfIdx);
end;
FWindowBase := ANewFrame.RegisterValueList.FindRegisterByDwarfIndex(WindowBaseIndex).NumValue and $F;
FWindowStart := ANewFrame.RegisterValueList.FindRegisterByDwarfIndex(WindowStartIndex).NumValue and $FFFF;
end;
function TDbgStackUnwinderXtensa.Unwind(AFrameIndex: integer; var CodePointer,
StackPointer, FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry;
out ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult;
const
MAX_FRAMES = 500; // safety net
Size = 4;
var
Address, returnAddress: TDBGPtr;
callSize: byte;
j, k, stackRegCount: integer;
spilled: boolean;
stackRegs: array of TDBGPtr;
tmpReg: uint32;
LastFrameBase: TDBGPtr;
begin
ANewFrame := nil;
Result := suFailed;
if (StackPointer > $40000000) or (StackPointer < $3F000000) or
(CodePointer < $40000001) or not FLastFrameBaseIncreased then
exit;
LastFrameBase := FrameBasePointer;
ReturnAddress := ACurrentFrame.RegisterValueList.FindRegisterByName(nReturnPC).NumValue;
spilled := FWindowStart and (1 shl FWindowBase) = 0;
FCodeReadErrCnt := 0;
callSize := uint32(returnAddress) shr 30;
if callsize = 0 then
exit;
Address := (returnAddress and $3FFFFFFF) or $40000000;
// Check if start of this window frame is live or spilled
if not spilled then
begin
// Move window base to previous window frame
// Wraparound subtraction
FWindowBase := (FWindowBase + 16 - callsize) and $F;
spilled := FWindowStart and (1 shl FWindowBase) = 0;
end;
stackRegCount := 4*callSize;
SetLength(stackRegs, stackRegCount);
if not spilled then
begin
// Live registers, read from register file
j := ((4*FWindowBase) and 63);
for k := 0 to stackRegCount-1 do
stackRegs[k] := TDbgRspThread(Thread).InternalRegs.regs[((j+k) and 63)+1]; // wraparound indexing
end
else
begin
// Registers spilled to stack, read from memory
for j := 0 to callSize-1 do
begin
for k := 0 to 3 do
begin
if j = 0 then
begin
if not Process.ReadData(StackPointer-16+4*k, 4, tmpReg) then Break;
end
else
begin
if (stackRegs[1] < $3F000000) or
not Process.ReadData(stackRegs[1]-16+4*k, 4, tmpReg) then
Break;
end;
stackRegs[4*j + k] := NtoLE(tmpReg);
end;
end;
end;
returnAddress := stackRegs[0];
StackPointer := stackRegs[1];
FrameBasePointer := StackPointer;
ANewFrame:= TDbgCallstackEntry.create(Thread, AFrameIndex, FrameBasePointer, Address);
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(Address, IntToStr(Address),Size, PCIndexDwarf);
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[nReturnPC].SetValue(returnAddress, IntToStr(returnAddress),Size, ReturnPCIndexDwarf);
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPointer, IntToStr(StackPointer),Size, SPindexDwarf);
// In case a7 is used as frame pointer
ANewFrame.RegisterValueList.DbgRegisterAutoCreate['a7'].SetValue(byte(FrameBasePointer), IntToStr(byte(FrameBasePointer)),Size, 7);
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[nWindowBase].SetValue(FWindowBase, IntToStr(FWindowBase), 1, WindowBaseIndex);
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[nWindowStart].SetValue(FWindowStart, IntToStr(FWindowStart), 2 , WindowStartIndex);
FLastFrameBaseIncreased := (FrameBasePointer <> 0) and (FrameBasePointer > LastFrameBase);
Result := suSuccess;
end;
initialization
DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} );
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
RegisterDbgOsClasses(TOSDbgClasses.Create(
TDbgXtensaProcess,
TDbgXtensaThread,
TXtensaAsmDecoder));
end.

View File

@ -233,6 +233,18 @@ File(s) with other licenses (see also header in file(s):
<Filename Value="fpdbgcpux86.pas"/>
<UnitName Value="fpdbgcpux86"/>
</Item>
<Item>
<Filename Value="fpdbgrspclasses.pas"/>
<UnitName Value="FpDbgRspClasses"/>
</Item>
<Item>
<Filename Value="fpdbgdisasxtensa.pas"/>
<UnitName Value="FpDbgDisasXtensa"/>
</Item>
<Item>
<Filename Value="fpdbgxtensaclasses.pas"/>
<UnitName Value="FpDbgXtensaClasses"/>
</Item>
</Files>
<i18n>
<EnableI18N Value="True"/>

View File

@ -16,7 +16,8 @@ uses
FpDbgDwarfDataClasses, FpDbgDwarfFreePascal, fpDbgSymTableContext,
fpDbgSymTable, FpDbgAvrClasses, FpDbgDisasAvr, FpDbgRsp, FpDbgCommon,
FpImgReaderWinPETypes, FpDbgHardcodedFreepascalInfo, FpDbgCallContextInfo,
FpWatchResultData, FpDbgDwarfCFI, FpDbgCpuX86;
FpWatchResultData, FpDbgDwarfCFI, FpDbgCpuX86, FpDbgRspClasses,
FpDbgDisasXtensa, FpDbgXtensaClasses;
implementation

View File

@ -141,6 +141,7 @@ begin
EM_IA_64: result := mtIA_64;
EM_X86_64: result := mtX86_64;
EM_AVR: result := mtAVR8;
EM_XTENSA: result := mtXTENSA;
EM_ALPHA: result := mtALPHA;
else
result := mtNone;
@ -149,7 +150,7 @@ begin
// If OS is not encoded in header, take some guess based on machine type
if FTargetInfo.OS = osNone then
begin
if result = mtAVR8 then
if result in [mtAVR8, mtXTENSA] then
FTargetInfo.OS := osEmbedded
else
// Default to the same as host...

View File

@ -154,6 +154,7 @@ const
EM_IA_64 = 50;
EM_X86_64 = 62;
EM_AVR = 83;
EM_XTENSA = 94;
EM_ALPHA = $9026; //unofficial, but used by gnu toolchain
//elf version {Elf32_Hdr.e_version}

View File

@ -16,7 +16,7 @@ uses
FpPascalParser, FPDbgController, FpDbgDwarfDataClasses, FpDbgDwarfFreePascal,
FpDbgDwarf, FpDbgUtil,
DebuggerPropertiesBase,
FpDbgRsp, FpDbgAvrClasses;
FpDbgRsp, FpDbgAvrClasses, FpDbgXtensaClasses;
type