disassemblerwindow.pas

Переключить прокрутку окна
Загрузить этот исходный код

{
    DisassemblerWindow используется для создания окна, в котором пользователь
    смотрит внутреннее устройство выполняемой Малик Эмулятором программы:
    дизассемблер, регистры, стак, глобальные переменные, точки останова и прочее.
    Этот исходный текст является частью Малик Эмулятора.

    Следующие файлы используются этим исходным текстом:
        disassemblerwindow.lfm
    На них так же распространяются те же права, как и на этот исходный текст.

    Copyright © 2016–2017, 2019–2023 Малик Разработчик

    Малик Эмулятор – свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Малик Эмулятор распространяется в надежде, что он может быть полезен,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
}

unit DisassemblerWindow;

{$MODE DELPHI}

interface

uses
    Windows,
    Themes,
    Classes,
    SysUtils,
    Forms,
    Graphics,
    Controls,
    ComCtrls,
    ExtCtrls,
    StdCtrls,
    Buttons,
    Menus,
    LCLType,
    Lang,
    FileIO,
    EmulProgrammes,
    EmulMalik,
    EmulatorInterfaces;

{%region public }
const
    PREFIX_CLASS = 'class:';
    PREFIX_VARIABLE = 'var:';
    PREFIX_FUNCTION = 'func:';

type
    DefaultRepresenter = class;
    DebugInfo = class;
    ClassDebugInfo = class;
    DisassemblerObject = class;
    GUIElement = class;
    ClickableGUIElement = class;
    FieldInfo = class;
    DebugInspectorHistoryElement = class;
    DebugInspectorHistory = class;
    TDisassemblerForm = class;
    GUIElementDisassembler = class;
    GUIElementGlobalVariables = class;
    GUIElementAllClasses = class;
    GUIElementAllFunctions = class;
    GUIElementExceptions = class;
    GUIElementBreakpoints = class;
    GUIElementContext = class;
    GUIElementStack = class;
    GUIElementCallStack = class;

    DebugInfo_Array1d = array of DebugInfo;
    ClassDebugInfo_Array1d = array of ClassDebugInfo;
    GUIElement_Array1d = array of GUIElement;
    DebugInspectorHistoryElement_Array1d = array of DebugInspectorHistoryElement;
    DebugInspectorHistory_Array1d = array of DebugInspectorHistory;

    DefaultRepresenter = class(RefCountInterfacedObject, Representer)
    public
        constructor create();
        function getUntypedValueRepresentation(): AnsiString; virtual;
        function getLongRepresentation(value: long; sourceType: int): AnsiString; virtual;
        function getFloatRepresentation(value: float): AnsiString; virtual;
        function getDoubleRepresentation(value: double): AnsiString; virtual;
        function getRealRepresentation(value: real): AnsiString; overload; virtual;
        function getRealRepresentation(value: real; sourceType: int): AnsiString; overload; virtual;
        function getObjectRepresentation(value: int): AnsiString; virtual;
        function getHexRepresentation(value: int): AnsiString; virtual;
        function getTypeRepresentation(const rawRepresentation: AnsiString): AnsiString; virtual;
    end;

    DebugInfo = class(_Object)
    strict private
        address: int;
        info: AnsiString;
    public
        constructor create(address: int; const info: AnsiString);
        function getAddress(): int;
        function getInfo(): AnsiString;
    end;

    ClassDebugInfo = class(DebugInfo)
    strict private
        parent: ClassDebugInfo;
        fields: DebugInfo_Array1d;
        count: int;
    public
        constructor create(address: int; const info: AnsiString);
        destructor destroy; override;
        function getParentsCount(): int;
        function getFieldsCount(): int;
        function getField(index: int): DebugInfo;
        function getParent(): ClassDebugInfo;
        procedure addField(offset: int; const info: AnsiString);
        procedure setParent(parent: ClassDebugInfo);
    end;

    DisassemblerObject = class(_Object)
    protected
        TDisassemblerForm_self: TDisassemblerForm;
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm);
    end;

    GUIElement = class(DisassemblerObject)
    private
        box: TPaintBox;
        scroll: TScrollBar;
        position: int;
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
        procedure paint(canvas: TCanvas; width, height, scrollPosition: int); virtual; abstract;
        procedure pointerPressed(x, y: int; auxButton: boolean); virtual;
        procedure pointerReleased(x, y: int; auxButton: boolean); virtual;
        function getLinesCount(): int; virtual;
        procedure repaint();
    end;

    ClickableGUIElement = class(GUIElement)
    private
        paintedFromLine: int;
        lineHeight: int;
        clickedLine: int;
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
        procedure paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean); virtual; abstract;
        procedure paint(canvas: TCanvas; width, height, scrollPosition: int); override;
        procedure pointerPressed(x, y: int; auxButton: boolean); override; final;
        procedure pointerReleased(x, y: int; auxButton: boolean); override; final;
        procedure lineClicked(lineIndex: int; auxButton: boolean); virtual;
    end;

    FieldInfo = class(_Object)
    private
        fieldAddress: int;
        fieldDataType: AnsiString;
        fieldRepresentation: AnsiString;
        valueRepresentation: AnsiString;
        color: int;
    public
        constructor create();
    end;

    DebugInspectorHistoryElement = class(DisassemblerObject)
    strict private
        dataType: AnsiString;
        address: int;
    private
        position: int;
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; const dataType: AnsiString; address: int);
        function getTypeRepresentation(): AnsiString;
        function getValueRepresentation(): AnsiString;
        function getFieldInfo(index: int; info: FieldInfo): boolean;
        function getFieldsCount(): int;
        function getAddress(): int;
        function getClassInfo(): ClassDebugInfo;
        function isObjectType(): boolean;
    end;

    DebugInspectorHistory = class(ClickableGUIElement)
    strict private
        menu: TPopupMenu;
        field: FieldInfo;
        history: DebugInspectorHistoryElement_Array1d;
        count: int;
        current: int;
        procedure insert(const dataType: AnsiString; address: int);
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar; const dataType: AnsiString; address: int);
        destructor destroy; override;
        procedure paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean); override;
        procedure paint(canvas: TCanvas; width, height, scrollPosition: int); override;
        procedure lineClicked(lineIndex: int; auxButton: boolean); override;
        function getLinesCount(): int; override;
        procedure gotoPrevElement();
        procedure gotoNextElement();
        procedure gotoElement(index: int);
        function getElementRepresentation(index: int): AnsiString;
        function getCurrentIndex(): int;
        function getCount(): int;
        function getMenu(): TPopupMenu;
    end;

    TDisassemblerForm = class(TForm)
        arrows: TImageList;
        controlPanel: TPanel;
        debugInspectorButton: TSpeedButton;
        stepIntoButton: TSpeedButton;
        stepOverButton: TSpeedButton;
        runButton: TSpeedButton;
        hideAndRunButton: TSpeedButton;
        controlBevel: TBevel;
        mainPanel: TPanel;
        disassemblerPanel: TPanel;
        disassembler: TPaintBox;
        disassemblerScrollbar: TScrollBar;
        mainPanelSplitter: TSplitter;
        statusPanel: TPanel;
        statusPages: TPageControl;
        statusGlobalVariables: TTabSheet;
        statusAllClasses: TTabSheet;
        statusAllFunctions: TTabSheet;
        statusExceptions: TTabSheet;
        statusBreakpoints: TTabSheet;
        statusBox: TPanel;
        status: TPaintBox;
        statusScrollbar: TScrollBar;
        mainSplitter: TSplitter;
        threadPanel: TPanel;
        threadList: TComboBox;
        contextSplitter: TPanel;
        contextPanel: TPanel;
        context: TPaintBox;
        contextScrollbar: TScrollBar;
        stackSplitter: TSplitter;
        stackPanel: TPanel;
        stackPages: TPageControl;
        stackAll: TTabSheet;
        stackCalls: TTabSheet;
        stackBox: TPanel;
        stack: TPaintBox;
        stackScrollbar: TScrollBar;
        debugInspectorPanel: TPanel;
        debugInspectorCaption: TPanel;
        debugInspectorCloseButton: TSpeedButton;
        debugInspectorPages: TPageControl;
        debugInspectorVarInfo: TPanel;
        debugInspectorBackButton: TSpeedButton;
        debugInspectorNextButton: TSpeedButton;
        debugInspectorMenuButton: TSpeedButton;
        debugInspectorCloseTabButton: TSpeedButton;
        debugInspectorAddressLabel: TLabel;
        debugInspectorAddress: TLabel;
        debugInspectorClassLabel: TLabel;
        debugInspectorClass: TLabel;
        debugInspectorValueLabel: TLabel;
        debugInspectorValue: TLabel;
        debugInspectorBox: TPanel;
        debugInspector: TPaintBox;
        debugInspectorScrollBox: TPanel;
        debugInspectorScrollbar: TScrollBar;
        debugInspectorSizeGrip: TPaintBox;
        procedure formShow(sender: TObject);
        procedure formKeyDown(sender: TObject; var key: Word; shift: TShiftState);
        procedure formKeyUp(sender: TObject; var key: Word; shift: TShiftState);
        procedure formResize(sender: TObject);
        procedure formWindowStateChange(sender: TObject);
        procedure formGUIElementPaint(sender: TObject);
        procedure formGUIElementPointerPressed(sender: TObject; button: TMouseButton; shift: TShiftState; x, y: integer);
        procedure formGUIElementPointerReleased(sender: TObject; button: TMouseButton; shift: TShiftState; x, y: integer);
        procedure formGUIElementScroll(sender: TObject);
        procedure stepIntoButtonClick(sender: TObject);
        procedure stepOverButtonClick(sender: TObject);
        procedure runButtonClick(sender: TObject);
        procedure hideAndRunButtonClick(sender: TObject);
        procedure debugInspectorButtonClick(sender: TObject);
        procedure disassemblerScrollbarKeyDown(sender: TObject; var key: Word; shift: TShiftState);
        procedure disassemblerScrollbarScroll(sender: TObject; action: TScrollCode; var position: integer);
        procedure threadListSelect(sender: TObject);
        procedure debugInspectorCaptionSizeGripMouseDown(sender: TObject; button: TMouseButton; shift: TShiftState; x, y: integer);
        procedure debugInspectorCaptionSizeGripMouseMove(sender: TObject; shift: TShiftState; x, y: integer);
        procedure debugInspectorBackButtonClick(sender: TObject);
        procedure debugInspectorNextButtonClick(sender: TObject);
        procedure debugInspectorMenuButtonClick(sender: TObject);
        procedure debugInspectorCloseTabButtonClick(sender: TObject);
        procedure debugInspectorSizeGripPaint(sender: TObject);
        procedure debugInspectorHistoryMenuClickItem(sender: TObject);
    private
        owner: EmulationWindowInterface;
        resizing: boolean;
        x: int;
        y: int;
        contextID: int;
        stepInto: boolean;
        stepOver: int;
        disassemblerPosition: int;
        maxVisibleAddress: int;
        breakpointsCount: int;
        image: TBitmap;
        processor: MalikProcessor;
        currentContext: MalikDebugContext;
        allDebugInfo: DebugInfo_Array1d;
        classesInfo: ClassDebugInfo_Array1d;
        variablesInfo: DebugInfo_Array1d;
        functionsInfo: DebugInfo_Array1d;
        breakpoints: int_Array1d;
        addresses: int_Array1d;
        guiDisassembler: GUIElement;
        guiStatus: GUIElement_Array1d;
        guiContext: GUIElement;
        guiStack: GUIElement_Array1d;
        guiDebugInspector: DebugInspectorHistory_Array1d;
        guiDebugInspectorCount: int;
        function findClassInfo(const className: AnsiString): ClassDebugInfo;
        function getDebugInfo(address: int; const info: DebugInfo_Array1d): AnsiString;
        function getClassInfo(address: int; const info: ClassDebugInfo_Array1d): ClassDebugInfo;
        function getFunctionAt(address: int): DebugInfo;
        function getAddressRepresentation(address: int): AnsiString;
        function getValueRepresentation(address: int; const typeInfo: AnsiString): AnsiString;
        function getInstructionByteCode(address: int): AnsiString;
        function getInstructionRepresentation(address: int): AnsiString;
        function getInstructionLength(address: int): int;
        function getInstructionsCount(address, size: int): int;
        function getGUIElementFor(sender: TObject): GUIElement;
        function hasBreakpointAt(address: int): boolean;
        procedure updateScreen();
        procedure showContext(id: int);
        procedure addDebugInspectorElement(const tabCaption, dataType: AnsiString; address: int);
        procedure paintGUIElement(element: GUIElement);
        procedure destroyGUI(const arr: GUIElement_Array1d); overload;
        procedure destroyGUI(const arr: DebugInspectorHistory_Array1d); overload;
        procedure destroyInfo(const arr: DebugInfo_Array1d); overload;
        procedure destroyInfo(const arr: ClassDebugInfo_Array1d); overload;
        procedure loadDataFrom(strs: TStrings);
        procedure setupScrollbar(scrollbar: TScrollBar; maxval, position: int); overload;
        procedure setupScrollbar(scrollbar: TScrollBar; page: int); overload;
        procedure switchFullScreenMode();
    private
        colorEmptyData: int;
        colorNormalData: int;
        colorObjectData: int;
        colorExceptData: int;
        colorOldEBPData: int;
        colorReturnData: int;
        colorIReturnData: int;
        colorNormalType: int;
        colorObjectType: int;
        colorExceptType: int;
        colorOldEBPType: int;
        colorReturnType: int;
        colorIReturnType: int;
        colorOverflowType: int;
        colorFunctionName: int;
        colorSourceLine: int;
        colorInherited: int;
    public
        constructor create(theOwner: TComponent); override;
        destructor destroy; override;
        procedure afterConstruction(); override;
        procedure loadData();
        procedure showContents(contextID: int);
        function hasBreakpoints(): boolean;
        function mustBreak(contextID, address: int): boolean;

    strict private
        INSTRUCTIONS_NAMES: AnsiString_Array1d; static;
    private
        class procedure clinit();
        class procedure cldone();
    public
        DATA_REPRESENTER: Representer; static;
        class function getFieldRepresentation(const fieldDebugInfoLine: AnsiString): AnsiString;
        class function getVariableRepresentation(const variableDebugInfoLine: AnsiString): AnsiString;
        class function getFunctionRepresentation(const functionDebugInfoLine: AnsiString): AnsiString;
    end;

    GUIElementDisassembler = class(GUIElement)
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
        procedure paint(canvas: TCanvas; width, height, scrollPosition: int); override;
        procedure pointerPressed(x, y: int; auxButton: boolean); override;
    end;

    GUIElementGlobalVariables = class(ClickableGUIElement)
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
        procedure paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean); override;
        procedure paint(canvas: TCanvas; width, height, scrollPosition: int); override;
        procedure lineClicked(lineIndex: int; auxButton: boolean); override;
        function getLinesCount(): int; override;
    end;

    GUIElementAllClasses = class(ClickableGUIElement)
    strict private
        classesRegion: int;
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
        procedure paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean); override;
        procedure lineClicked(lineIndex: int; auxButton: boolean); override;
        function getLinesCount(): int; override;
    end;

    GUIElementAllFunctions = class(ClickableGUIElement)
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
        procedure paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean); override;
        procedure lineClicked(lineIndex: int; auxButton: boolean); override;
        function getLinesCount(): int; override;
    end;

    GUIElementExceptions = class(ClickableGUIElement)
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
        procedure paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean); override;
        procedure paint(canvas: TCanvas; width, height, scrollPosition: int); override;
        procedure lineClicked(lineIndex: int; auxButton: boolean); override;
        function getLinesCount(): int; override;
    end;

    GUIElementBreakpoints = class(ClickableGUIElement)
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
        procedure paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean); override;
        procedure lineClicked(lineIndex: int; auxButton: boolean); override;
        function getLinesCount(): int; override;
    end;

    GUIElementContext = class(ClickableGUIElement)
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
        procedure paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean); override;
        procedure paint(canvas: TCanvas; width, height, scrollPosition: int); override;
        procedure lineClicked(lineIndex: int; auxButton: boolean); override;
        function getLinesCount(): int; override;
    end;

    GUIElementStack = class(ClickableGUIElement)
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
        procedure paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean); override;
        procedure paint(canvas: TCanvas; width, height, scrollPosition: int); override;
        procedure lineClicked(lineIndex: int; auxButton: boolean); override;
        function getLinesCount(): int; override;
    end;

    GUIElementCallStack = class(ClickableGUIElement)
    strict private
        addresses: int_Array1d;
    public
        constructor create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
        procedure paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean); override;
        procedure paint(canvas: TCanvas; width, height, scrollPosition: int); override;
        procedure lineClicked(lineIndex: int; auxButton: boolean); override;
        function getLinesCount(): int; override;
    end;
{%endregion}

implementation

{$R *.LFM}

{%region routine }
    function DebugInfo_Array1d_create(length: int): DebugInfo_Array1d;
    begin
        setLength(result, length);
    end;

    procedure arraycopy(const src: DebugInfo_Array1d; srcOffset: int; const dst: DebugInfo_Array1d; dstOffset: int; length: int); overload;
    var
        lim: int;
        len: int;
    begin
        lim := srcOffset + length;
        len := System.length(src);
        if (lim > len) or (lim < srcOffset) or (srcOffset < 0) or (srcOffset > len) then begin
            raise ArrayIndexOutOfBoundsException.create('arraycopy: индекс элемента массива выходит из диапазона.');
        end;
        lim := dstOffset + length;
        len := System.length(dst);
        if (lim > len) or (lim < dstOffset) or (dstOffset < 0) or (dstOffset > len) then begin
            raise ArrayIndexOutOfBoundsException.create('arraycopy: индекс элемента массива выходит из диапазона.');
        end;
        move(src[srcOffset], dst[dstOffset], length * sizeof(_Object));
    end;

    function ClassDebugInfo_Array1d_create(length: int): ClassDebugInfo_Array1d;
    begin
        setLength(result, length);
    end;

    function GUIElement_Array1d_create(length: int): GUIElement_Array1d;
    begin
        setLength(result, length);
    end;

    function toGUIElementArray1d(const arr: array of GUIElement): GUIElement_Array1d;
    begin
        setLength(result, length(arr));
        move(arr[0], result[0], length(result) * sizeof(_Object));
    end;

    function DebugInspectorHistoryElement_Array1d_create(length: int): DebugInspectorHistoryElement_Array1d;
    begin
        setLength(result, length);
    end;

    procedure arraycopy(const src: DebugInspectorHistoryElement_Array1d; srcOffset: int; const dst: DebugInspectorHistoryElement_Array1d; dstOffset: int; length: int); overload;
    var
        lim: int;
        len: int;
    begin
        lim := srcOffset + length;
        len := System.length(src);
        if (lim > len) or (lim < srcOffset) or (srcOffset < 0) or (srcOffset > len) then begin
            raise ArrayIndexOutOfBoundsException.create('arraycopy: индекс элемента массива выходит из диапазона.');
        end;
        lim := dstOffset + length;
        len := System.length(dst);
        if (lim > len) or (lim < dstOffset) or (dstOffset < 0) or (dstOffset > len) then begin
            raise ArrayIndexOutOfBoundsException.create('arraycopy: индекс элемента массива выходит из диапазона.');
        end;
        move(src[srcOffset], dst[dstOffset], length * sizeof(_Object));
    end;

    function DebugInspectorHistory_Array1d_create(length: int): DebugInspectorHistory_Array1d;
    begin
        setLength(result, length);
    end;

    procedure arraycopy(const src: DebugInspectorHistory_Array1d; srcOffset: int; const dst: DebugInspectorHistory_Array1d; dstOffset: int; length: int); overload;
    var
        lim: int;
        len: int;
    begin
        lim := srcOffset + length;
        len := System.length(src);
        if (lim > len) or (lim < srcOffset) or (srcOffset < 0) or (srcOffset > len) then begin
            raise ArrayIndexOutOfBoundsException.create('arraycopy: индекс элемента массива выходит из диапазона.');
        end;
        lim := dstOffset + length;
        len := System.length(dst);
        if (lim > len) or (lim < dstOffset) or (dstOffset < 0) or (dstOffset > len) then begin
            raise ArrayIndexOutOfBoundsException.create('arraycopy: индекс элемента массива выходит из диапазона.');
        end;
        move(src[srcOffset], dst[dstOffset], length * sizeof(_Object));
    end;

    function typeSize(const rawRepresentation: AnsiString): int;
    begin
        if length(rawRepresentation) <= 0 then begin
            result := 0;
            exit;
        end;
        case rawRepresentation[1] of
        SIGNATURE_VOID: begin
            result := 0;
        end;
        SIGNATURE_BOOLEAN, SIGNATURE_BYTE: begin
            result := 1;
        end;
        SIGNATURE_SHORT, SIGNATURE_WCHAR: begin
            result := 2;
        end;
        SIGNATURE_OBJECT, SIGNATURE_CLASS, SIGNATURE_ARRAY,
        SIGNATURE_INT, SIGNATURE_ICHAR, SIGNATURE_FLOAT: begin
            result := 4;
        end;
        SIGNATURE_LONG, SIGNATURE_DOUBLE: begin
            result := 8;
        end;
        SIGNATURE_REAL: begin
            result := 10;
        end;
        else
            result := 0;
        end;
    end;
{%endregion}

{%region DefaultRepresenter }
    constructor DefaultRepresenter.create();
    begin
        inherited create();
    end;

    function DefaultRepresenter.getUntypedValueRepresentation(): AnsiString;
    begin
        result := '<пусто>';
    end;

    function DefaultRepresenter.getLongRepresentation(value: long; sourceType: int): AnsiString;
    var
        cc: int;
        s: UnicodeString;
    begin
        case sourceType of
        DATA_TYPE_BOOLEAN: begin
            if value <> 0 then begin
                result := 'true';
                if int(value and $ff) <> 1 then begin
                    result := result + ' ($' + toHexString(value and $ff, 2) + ')';
                end;
            end else begin
                result := 'false';
            end;
        end;
        DATA_TYPE_BYTE: begin
            result := toDecString(byte(value)) + ' ($' + toHexString(value and $ff, 1) + ')';
        end;
        DATA_TYPE_SHORT: begin
            result := toDecString(short(value)) + ' ($' + toHexString(value and $ffff, 1) + ')';
        end;
        DATA_TYPE_INT: begin
            result := toDecString(int(value)) + ' ($' + toHexString(zeroExtend(int(value)), 1) + ')';
        end;
        DATA_TYPE_LONG: begin
            result := toDecString(value) + ' ($' + toHexString(value, 1) + ')';
        end;
        DATA_TYPE_WCHAR: begin
            cc := int(value) and $ffff;
            result := toDecString(cc) + ' ($' + toHexString(cc, 4);
            if (cc < $d800) or (cc >= $e000) then begin
                result := result + ', ''' + toUTF8String(wchar(cc)) + '''';
            end;
            result := result + ')';
        end;
        DATA_TYPE_ICHAR: begin
            cc := int(value);
            result := toDecString(cc) + ' ($' + toHexString(zeroExtend(cc), 4);
            if (cc >= $0020) and (cc < $d800) or (cc >= $e000) and (cc < $110000) then begin
                if cc >= $010000 then begin
                    dec(cc, $010000);
                    s := wchar($d800 + (cc shr 10)) + wchar($dc00 + (cc and $3ff));
                end else begin
                    s := wchar(cc);
                end;
                result := result + ', ''' + toUTF8String(s) + '''';
            end;
            result := result + ')';
        end;
        else
            result := '';
        end;
    end;

    function DefaultRepresenter.getFloatRepresentation(value: float): AnsiString;
    begin
        result := toDecString(toReal(value)) + ' ($' + toHexString(zeroExtend(floatToIntBits(value)), 8) + ')';
    end;

    function DefaultRepresenter.getDoubleRepresentation(value: double): AnsiString;
    begin
        result := toDecString(toReal(value)) + ' ($' + toHexString(doubleToLongBits(value), 16) + ')';
    end;

    function DefaultRepresenter.getRealRepresentation(value: real): AnsiString;
    begin
        result := toDecString(value) + ' ($' + toHexString(extractExponentAndSign(value), 4) + toHexString(extractSignificand(value), 16) + ')';
    end;

    function DefaultRepresenter.getRealRepresentation(value: real; sourceType: int): AnsiString;
    var
        s: AnsiString;
    begin
        case sourceType of
         1: s := 'F';
         2: s := 'D';
        else
            s := '';
        end;
        result := toDecString(value) + s + ' ($' + toHexString(extractExponentAndSign(value), 4) + toHexString(extractSignificand(value), 16) + ')';
    end;

    function DefaultRepresenter.getObjectRepresentation(value: int): AnsiString;
    begin
        if value <> 0 then begin
            result := '$' + toHexString(zeroExtend(value), 8);
        end else begin
            result := 'null';
        end;
    end;

    function DefaultRepresenter.getHexRepresentation(value: int): AnsiString;
    begin
        result := '$' + toHexString(zeroExtend(value), 1);
    end;

    function DefaultRepresenter.getTypeRepresentation(const rawRepresentation: AnsiString): AnsiString;
    var
        d: int;
        i: int;
    begin
        i := length(rawRepresentation);
        if i > 0 then begin
            case rawRepresentation[1] of
            SIGNATURE_VOID: begin
                result := '';
            end;
            SIGNATURE_BOOLEAN: begin
                result := 'boolean';
            end;
            SIGNATURE_BYTE: begin
                result := 'byte';
            end;
            SIGNATURE_SHORT: begin
                result := 'short';
            end;
            SIGNATURE_INT: begin
                result := 'int';
            end;
            SIGNATURE_LONG: begin
                result := 'long';
            end;
            SIGNATURE_WCHAR: begin
                result := 'wchar';
            end;
            SIGNATURE_ICHAR: begin
                result := 'ichar';
            end;
            SIGNATURE_FLOAT: begin
                result := 'float';
            end;
            SIGNATURE_DOUBLE: begin
                result := 'double';
            end;
            SIGNATURE_REAL: begin
                result := 'real';
            end;
            SIGNATURE_OBJECT: begin
                if i = 1 then begin
                    result := 'object';
                    exit;
                end;
                if rawRepresentation[i] = SIGNATURE_SUFFIX then begin
                    result := copy(rawRepresentation, 2, i - 2);
                end else begin
                    result := copy(rawRepresentation, 2, i - 1);
                end;
                for i := 1 to length(result) do begin
                    if result[i] in ['/', '|', '\'] then begin
                        result[i] := '.';
                    end;
                end;
            end;
            SIGNATURE_CLASS: begin
                result := 'class of ' + getTypeRepresentation(SIGNATURE_OBJECT + copy(rawRepresentation, 2, i - 1));
            end;
            SIGNATURE_ARRAY: begin
                d := 1;
                while (i > d) and (rawRepresentation[d + 1] = SIGNATURE_ARRAY) do begin
                    inc(d);
                end;
                result := getTypeRepresentation(copy(rawRepresentation, d + 1, i - d));
                for i := 1 to d do begin
                    result := result + '[]';
                end;
            end;
            else
                result := '???';
            end;
        end else begin
            result := '???';
        end;
    end;
{%endregion}

{%region DebugInfo }
    constructor DebugInfo.create(address: int; const info: AnsiString);
    begin
        inherited create();
        self.address := address;
        self.info :=  info;
    end;

    function DebugInfo.getAddress(): int;
    begin
        result := address;
    end;

    function DebugInfo.getInfo(): AnsiString;
    begin
        result := info;
    end;
{%endregion}

{%region ClassDebugInfo }
    constructor ClassDebugInfo.create(address: int; const info: AnsiString);
    begin
        inherited create(address, info);
    end;

    destructor ClassDebugInfo.destroy;
    var
        a: DebugInfo_Array1d;
        i: int;
    begin
        a := fields;
        for i := count - 1 downto 0 do begin
            a[i].free();
        end;
        inherited destroy;
    end;

    function ClassDebugInfo.getParentsCount(): int;
    var
        d: ClassDebugInfo;
    begin
        result := 0;
        d := parent;
        while (d <> nil) and (d <> self) do begin
            inc(result);
            d := d.parent;
        end;
    end;

    function ClassDebugInfo.getFieldsCount(): int;
    begin
        result := count;
    end;

    function ClassDebugInfo.getField(index: int): DebugInfo;
    begin
        if (index < 0) or (index >= count) then begin
            raise ArrayIndexOutOfBoundsException.create(index);
        end;
        result := fields[index];
    end;

    function ClassDebugInfo.getParent(): ClassDebugInfo;
    begin
        result := parent;
    end;

    procedure ClassDebugInfo.addField(offset: int; const info: AnsiString);
    var
        a: DebugInfo_Array1d;
        l: int;
    begin
        a := fields;
        l := count;
        if l = length(a) then begin
            a := DebugInfo_Array1d_create(l + 16);
            arraycopy(fields, 0, a, 0, l);
            fields := a;
        end;
        a[l] := DebugInfo.create(offset, info);
        count := l + 1;
    end;

    procedure ClassDebugInfo.setParent(parent: ClassDebugInfo);
    begin
        if self.parent = nil then begin
            self.parent := parent;
        end;
    end;
{%endregion}

{%region DisassemblerObject }
    constructor DisassemblerObject.create(TDisassemblerForm_self: TDisassemblerForm);
    begin
        inherited create();
        self.TDisassemblerForm_self := TDisassemblerForm_self;
    end;
{%endregion}

{%region GUIElement }
    constructor GUIElement.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
    begin
        inherited create(TDisassemblerForm_self);
        self.box := box;
        self.scroll := scroll;
    end;

    procedure GUIElement.pointerPressed(x, y: int; auxButton: boolean);
    begin
    end;

    procedure GUIElement.pointerReleased(x, y: int; auxButton: boolean);
    begin
    end;

    function GUIElement.getLinesCount(): int;
    begin
        result := -1;
    end;

    procedure GUIElement.repaint();
    begin
        TDisassemblerForm_self.paintGUIElement(self);
    end;
{%endregion}

{%region ClickableGUIElement }
    constructor ClickableGUIElement.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
    begin
        inherited create(TDisassemblerForm_self, box, scroll);
        clickedLine := -1;
    end;

    procedure ClickableGUIElement.paint(canvas: TCanvas; width, height, scrollPosition: int);
    var
        i: int;
        j: int;
        b: int;
        h: int;
        m: int;
        highlighted: boolean;
    begin
        h := abs(canvas.font.height);
        if h = 0 then begin
            exit;
        end;
        self.paintedFromLine := scrollPosition;
        self.lineHeight := h;
        b := scrollPosition + (height div h);
        if (height mod h) > 0 then begin
            inc(b);
        end;
        m := getLinesCount();
        if b > m then begin
            b := m;
        end;
        j := 0;
        for i := scrollPosition to b - 1 do begin
            highlighted := clickedLine = i;
            if highlighted then begin
                canvas.brush.style := bsSolid;
                canvas.brush.color := clHighlight;
                canvas.fillRect(0, j, width, j + h + 2);
            end;
            paintLine(canvas, 0, j, width, h, i, highlighted);
            inc(j, h);
        end;
    end;

    procedure ClickableGUIElement.pointerPressed(x, y: int; auxButton: boolean);
    var
        h: int;
    begin
        h := lineHeight;
        if h = 0 then begin
            exit;
        end;
        clickedLine := paintedFromLine + (y div h);
        if clickedLine < getLinesCount() then begin
            repaint();
        end;
    end;

    procedure ClickableGUIElement.pointerReleased(x, y: int; auxButton: boolean);
    var
        h: int;
        i: int;
    begin
        h := lineHeight;
        if h = 0 then begin
            exit;
        end;
        try
            i := clickedLine;
            if (i < getLinesCount()) and (paintedFromLine + (y div h) = i) then begin
                lineClicked(i, auxButton);
            end;
        finally
            if clickedLine >= 0 then begin
                clickedLine := -1;
                repaint();
            end;
        end;
    end;

    procedure ClickableGUIElement.lineClicked(lineIndex: int; auxButton: boolean);
    begin
    end;
{%endregion}

{%region FieldInfo }
    constructor FieldInfo.create();
    begin
        inherited create();
    end;
{%endregion}

{%region DebugInspectorHistoryElement }
    constructor DebugInspectorHistoryElement.create(TDisassemblerForm_self: TDisassemblerForm; const dataType: AnsiString; address: int);
    begin
        inherited create(TDisassemblerForm_self);
        self.dataType := dataType;
        self.address := address;
    end;

    function DebugInspectorHistoryElement.getTypeRepresentation(): AnsiString;
    var
        d: ClassDebugInfo;
        r: Representer;
        s: AnsiString;
    begin
        r := TDisassemblerForm.DATA_REPRESENTER;
        s := dataType;
        if (length(s) > 0) and (s[1] in [SIGNATURE_OBJECT, SIGNATURE_CLASS, SIGNATURE_ARRAY]) then begin
            d := getClassInfo();
            if d <> nil then begin
                result := r.getTypeRepresentation(d.getInfo());
                exit;
            end;
            result := r.getTypeRepresentation(SIGNATURE_OBJECT);
            exit;
        end;
        result := r.getTypeRepresentation(s);
    end;

    function DebugInspectorHistoryElement.getValueRepresentation(): AnsiString;
    begin
        result := TDisassemblerForm_self.getValueRepresentation(address, dataType);
    end;

    function DebugInspectorHistoryElement.getFieldInfo(index: int; info: FieldInfo): boolean;
    var
        f: TDisassemblerForm;
        r: Representer;
        clsinfo: ClassDebugInfo;
        current: ClassDebugInfo;
        fieldInfo: DebugInfo;
        intArray: PIntArray;
        typeInfo: AnsiString;
        len: int;
        objValue: int;
        arrayLength: int;
        arrayOffset: int;
        fields: int;
        fieldsCount: int;
        fieldIndex: int;
        colonPos: int;
    begin
        result := false;
        clsinfo := getClassInfo();
        if clsinfo = nil then begin
            exit;
        end;
        f := TDisassemblerForm_self;
        intArray := PIntArray(f.processor.getMemory(address, sizeof(int)));
        if intArray = nil then begin
            exit;
        end;
        objValue := intArray[0];
        if objValue = 0 then begin
            exit;
        end;
        r := TDisassemblerForm.DATA_REPRESENTER;
        current := clsinfo;
        fields := 0;
        fieldIndex := index - fields;
        repeat
            if current <> clsinfo then begin
                if fieldIndex = 0 then begin
                    info.fieldAddress := 0;
                    info.fieldDataType := '';
                    info.fieldRepresentation := 'Унаследовано от ' + r.getTypeRepresentation(current.getInfo());
                    info.valueRepresentation := '';
                    info.color := f.colorInherited;
                    result := true;
                    exit;
                end;
                inc(fields);
                fieldIndex := index - fields;
            end;
            typeInfo := current.getInfo();
            len := length(typeInfo);
            if (len > 0) and (typeInfo[1] = SIGNATURE_ARRAY) then begin
                intArray := PIntArray(f.processor.getMemory(objValue + 8, 2 * sizeof(int)));
                if intArray = nil then begin
                    exit;
                end;
                arrayLength := intArray[0];
                arrayOffset := intArray[1];
                if (fieldIndex >= 0) and (fieldIndex < arrayLength + 1) then begin
                    if fieldIndex > 0 then begin
                        dec(fieldIndex);
                        dec(len);
                        typeInfo := copy(typeInfo, 2, len);
                        info.fieldAddress := objValue + fieldIndex * typeSize(typeInfo) + arrayOffset + 16;
                        info.fieldDataType := typeInfo;
                        info.fieldRepresentation := '[' + r.getLongRepresentation(fieldIndex, DATA_TYPE_INT) + ']: ' + r.getTypeRepresentation(typeInfo);
                        info.valueRepresentation := f.getValueRepresentation(info.fieldAddress, typeInfo);
                        if (len > 0) and (typeInfo[1] in [SIGNATURE_OBJECT, SIGNATURE_CLASS, SIGNATURE_ARRAY]) then begin
                            info.color := f.colorObjectData;
                        end else begin
                            info.color := f.colorNormalData;
                        end;
                    end else begin
                        info.fieldAddress := objValue + 8;
                        info.fieldDataType := SIGNATURE_INT;
                        info.fieldRepresentation := f.getFieldRepresentation('длина:' + SIGNATURE_INT);
                        info.valueRepresentation := f.getValueRepresentation(info.fieldAddress, SIGNATURE_INT);
                        info.color := f.colorNormalData;
                    end;
                    result := true;
                    exit;
                end;
                inc(fields, arrayLength + 1);
                fieldIndex := index - fields;
            end else begin
                fieldsCount := current.getFieldsCount();
                if (fieldIndex >= 0) and (fieldIndex < fieldsCount) then begin
                    fieldInfo := current.getField(fieldIndex);
                    typeInfo := fieldInfo.getInfo();
                    info.fieldRepresentation := TDisassemblerForm.getFieldRepresentation(typeInfo);
                    colonPos := pos(':', typeInfo);
                    typeInfo := trim(copy(typeInfo, colonPos + 1, length(typeInfo) - colonPos));
                    info.fieldAddress := objValue + fieldInfo.getAddress();
                    info.fieldDataType := typeInfo;
                    info.valueRepresentation := f.getValueRepresentation(info.fieldAddress, typeInfo);
                    len := length(typeInfo);
                    if (len > 0) and (typeInfo[1] in [SIGNATURE_OBJECT, SIGNATURE_CLASS, SIGNATURE_ARRAY]) then begin
                        info.color := f.colorObjectData;
                    end else begin
                        info.color := f.colorNormalData;
                    end;
                    result := true;
                    exit;
                end;
                inc(fields, fieldsCount);
                fieldIndex := index - fields;
            end;
            current := current.getParent();
        until (current = nil) or (current = clsinfo);
    end;

    function DebugInspectorHistoryElement.getFieldsCount(): int;
    var
        p: MalikProcessor;
        clsinfo: ClassDebugInfo;
        current: ClassDebugInfo;
        intArray: PIntArray;
        typeInfo: AnsiString;
        objValue: int;
        arrayLength: int;
    begin
        result := 0;
        clsinfo := getClassInfo();
        if clsinfo = nil then begin
            exit;
        end;
        p := TDisassemblerForm_self.processor;
        intArray := PIntArray(p.getMemory(address, sizeof(int)));
        if intArray = nil then begin
            exit;
        end;
        objValue := intArray[0];
        if objValue = 0 then begin
            exit;
        end;
        current := clsinfo;
        repeat
            if current <> clsinfo then begin
                inc(result);
            end;
            typeInfo := current.getInfo();
            if (length(typeInfo) > 0) and (typeInfo[1] = SIGNATURE_ARRAY) then begin
                intArray := PIntArray(p.getMemory(objValue + 8, sizeof(int)));
                if intArray = nil then begin
                    exit;
                end;
                arrayLength := intArray[0];
                inc(result, arrayLength + 1);
            end else begin
                inc(result, current.getFieldsCount());
            end;
            current := current.getParent();
        until (current = nil) or (current = clsinfo);
    end;

    function DebugInspectorHistoryElement.getAddress(): int;
    begin
        result := address;
    end;

    function DebugInspectorHistoryElement.getClassInfo(): ClassDebugInfo;
    var
        f: TDisassemblerForm;
        p: MalikProcessor;
        typeInfo: AnsiString;
        intArray: PIntArray;
        objValue: int;
        clsValue: int;
    begin
        result := nil;
        typeInfo := dataType;
        if (length(typeInfo) <= 0) or (not (typeInfo[1] in [SIGNATURE_OBJECT, SIGNATURE_CLASS, SIGNATURE_ARRAY])) then begin
            exit;
        end;
        f := TDisassemblerForm_self;
        p := f.processor;
        intArray := PIntArray(p.getMemory(address, sizeof(int)));
        if intArray = nil then begin
            exit;
        end;
        objValue := intArray[0];
        if objValue = 0 then begin
            exit;
        end;
        intArray := PIntArray(p.getMemory(objValue + 4, sizeof(int)));
        if intArray = nil then begin
            exit;
        end;
        clsValue := intArray[0];
        result := f.getClassInfo(clsValue, f.classesInfo);
    end;

    function DebugInspectorHistoryElement.isObjectType(): boolean;
    var
        typeInfo: AnsiString;
    begin
        typeInfo := dataType;
        result := (length(typeInfo) > 0) and (typeInfo[1] in [SIGNATURE_OBJECT, SIGNATURE_CLASS, SIGNATURE_ARRAY]);
    end;
{%endregion}

{%region DebugInspectorHistory }
    constructor DebugInspectorHistory.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar; const dataType: AnsiString; address: int);
    var
        item: TMenuItem;
    begin
        inherited create(TDisassemblerForm_self, box, scroll);
        menu := TPopupMenu.create(TDisassemblerForm_self);
        field := FieldInfo.create();
        history := DebugInspectorHistoryElement_Array1d_create(15);
        history[0] := DebugInspectorHistoryElement.create(TDisassemblerForm_self, dataType, address);
        count := 1;
        item := TMenuItem.create(TDisassemblerForm_self);
        menu.items.add(item);
        item.caption := getElementRepresentation(0);
        item.autoCheck := true;
        item.groupIndex := 1;
        item.radioItem := true;
        item.tag := 0;
        item.checked := true;
        item.onClick := TDisassemblerForm_self.debugInspectorHistoryMenuClickItem;
    end;

    destructor DebugInspectorHistory.destroy;
    var
        i: int;
        h: DebugInspectorHistoryElement_Array1d;
        m: TMenuItem;
    begin
        h := history;
        m := menu.items;
        for i := count - 1 downto 0 do begin
            h[i].free();
            m.items[i].free();
        end;
        menu.free();
        field.free();
        inherited destroy;
    end;

    procedure DebugInspectorHistory.insert(const dataType: AnsiString; address: int);
    var
        f: TDisassemblerForm;
        item: TMenuItem;
        m: TMenuItem;
        h: DebugInspectorHistoryElement_Array1d;
        i: int;
        l: int;
    begin
        h := history;
        l := current + 1;
        m := menu.items;
        for i := count - 1 downto l do begin
            h[i].free();
            h[i] := nil;
            m.items[i].free();
        end;
        if l = length(h) then begin
            h := DebugInspectorHistoryElement_Array1d_create((l shl 1) + 1);
            arraycopy(history, 0, h, 0, l);
            history := h;
        end;
        f := TDisassemblerForm_self;
        h[l] := DebugInspectorHistoryElement.create(f, dataType, address);
        item := TMenuItem.create(f);
        m.add(item);
        item.caption := getElementRepresentation(l);
        item.autoCheck := true;
        item.groupIndex := 1;
        item.radioItem := true;
        item.tag := l;
        item.checked := true;
        item.onClick := f.debugInspectorHistoryMenuClickItem;
        count := l + 1;
        gotoElement(l);
    end;

    procedure DebugInspectorHistory.paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean);
    var
        x: int;
        w: int;
        fld: FieldInfo;
        field: UnicodeString;
        value: AnsiString;
    begin
        fld := self.field;
        if not history[current].getFieldInfo(lineIndex, fld) then begin
            exit;
        end;
        canvas.brush.style := bsClear;
        canvas.font.style := [];
        if length(fld.valueRepresentation) = 0 then begin
            canvas.font.style := [fsBold];
            x := 0;
        end else begin
            x := 32;
        end;
        if highlighted then begin
            canvas.font.color := clHighlightText;
        end else begin
            canvas.font.color := fld.color;
        end;
        field := toUTF16String(fld.fieldRepresentation);
        value := fld.valueRepresentation;
        w := width - canvas.textWidth(value) - x;
        while (length(field) > 2) and (canvas.textWidth(toUTF8String(field)) > w) do begin
            field := copy(field, 1, length(field) - 2) + #$2026;
            { $2026 – код символа многоточия («…») }
        end;
        canvas.textOut(left + x, top, toUTF8String(field));
        canvas.textOut(left + x + w, top, value);
    end;

    procedure DebugInspectorHistory.paint(canvas: TCanvas; width, height, scrollPosition: int);
    var
        f: TDisassemblerForm;
        e: DebugInspectorHistoryElement;
        r: Representer;
        i: int;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        e := history[current];
        r := TDisassemblerForm.DATA_REPRESENTER;
        f.debugInspectorBackButton.enabled := current > 0;
        f.debugInspectorNextButton.enabled := current < count - 1;
        f.debugInspectorAddress.caption := r.getHexRepresentation(e.getAddress());
        f.debugInspectorClass.caption := e.getTypeRepresentation();
        f.debugInspectorValue.caption := e.getValueRepresentation();
        if e.isObjectType() then begin
            f.debugInspectorValue.font.color := f.colorObjectData;
        end else begin
            f.debugInspectorValue.font.color := f.colorNormalData;
        end;
        for i := 0 to count - 1 do begin
            menu.items.items[i].caption := getElementRepresentation(i);
        end;
        inherited paint(canvas, width, height, scrollPosition);
    end;

    procedure DebugInspectorHistory.lineClicked(lineIndex: int; auxButton: boolean);
    var
        f: TDisassemblerForm;
        fld: FieldInfo;
    begin
        f := TDisassemblerForm_self;
        fld := field;
        if f.owner.isNowDebugging() and history[current].getFieldInfo(lineIndex, fld) then begin
            if auxButton then begin
                f.addDebugInspectorElement(fld.fieldRepresentation, fld.fieldDataType, fld.fieldAddress);
            end else begin
                insert(fld.fieldDataType, fld.fieldAddress);
            end;
        end;
    end;

    function DebugInspectorHistory.getLinesCount(): int;
    begin
        result := history[current].getFieldsCount();
    end;

    procedure DebugInspectorHistory.gotoPrevElement();
    begin
        gotoElement(current - 1);
    end;

    procedure DebugInspectorHistory.gotoNextElement();
    begin
        gotoElement(current + 1);
    end;

    procedure DebugInspectorHistory.gotoElement(index: int);
    var
        f: TDisassemblerForm;
    begin
        f := TDisassemblerForm_self;
        if index < 0 then begin
            index := 0;
        end;
        if index >= count then begin
            index := count - 1;
        end;
        history[current].position := f.debugInspectorScrollbar.position;
        current := index;
        menu.items.items[index].checked := true;
        repaint();
        if f.debugInspectorScrollbar.enabled then begin
            f.debugInspectorScrollbar.position := history[index].position;
        end;
    end;

    function DebugInspectorHistory.getElementRepresentation(index: int): AnsiString;
    var
        r: Representer;
        e: DebugInspectorHistoryElement;
    begin
        r := TDisassemblerForm.DATA_REPRESENTER;
        e := history[index];
        result := r.getHexRepresentation(e.getAddress()) + ' : ' + e.getTypeRepresentation();
    end;

    function DebugInspectorHistory.getCurrentIndex(): int;
    begin
        result := current;
    end;

    function DebugInspectorHistory.getCount(): int;
    begin
        result := count;
    end;

    function DebugInspectorHistory.getMenu(): TPopupMenu;
    begin
        result := menu;
    end;
{%endregion}

{%region TDisassemblerForm }
    class procedure TDisassemblerForm.clinit();
    begin
        INSTRUCTIONS_NAMES := toStringArray1d([
            'nop',
            'load    byte as long ',
            'load    short as long ',
            'load    int as long ',
            'load    long ',
            'load    float ',
            'load    double ',
            'load    real ',
            'load    except ',
            'load    byte as int ',
            'load    short as int ',
            'load    int ',
            'load    wchar ',
            'load    float as double ',
            'load    float as real ',
            'load    double as real ',
            'pop',
            'pop     ',
            'dup',
            'dup     ',
            'dup     x 1',
            'dup     x ',
            'swap',
            'dup     ',
            'popdw',
            'popdw2',
            'dupdw',
            'dupdw2',
            'dupdw   x 1',
            'dupdw2  x 1',
            'dupdw   x 2',
            'dupdw2  x 2',
            'load    byte as long ',
            'load    short as long ',
            'load    int as long ',
            'load    long ',
            'load    float ',
            'load    double ',
            'load    real ',
            'load    object ',
            'load    byte as int ',
            'load    short as int ',
            'load    int ',
            'load    wchar ',
            'load    float as double ',
            'load    float as real ',
            'load    double as real ',
            'load    null',
            'store   long as byte ',
            'store   long as short ',
            'store   long as int ',
            'store   long ',
            'store   float ',
            'store   double ',
            'store   real ',
            'store   object ',
            'store   int as byte ',
            'store   int as short ',
            'store   int ',
            'store   wchar ',
            'store   double as float ',
            'store   real as float ',
            'store   real as double ',
            'breakpoint',
            'load    ',
            'load    ',
            'load    int ',
            'load    long ',
            'load    float ',
            'load    double ',
            'load    real ',
            'load    object ',
            'store   ',
            'store   ',
            'store   int ',
            'store   long ',
            'store   float ',
            'store   double ',
            'store   real ',
            'store   object ',
            'add     int',
            'add     long',
            'add     float',
            'add     double',
            'sub     int',
            'sub     long',
            'sub     float',
            'sub     double',
            'mul     int',
            'mul     long',
            'mul     float',
            'mul     double',
            'div     int',
            'div     long',
            'div     float',
            'div     double',
            'or      int',
            'or      long',
            'and     int',
            'and     long',
            'xor     int',
            'xor     long',
            'sar     int',
            'sar     long',
            'sal     int',
            'sal     long',
            'shr     int',
            'shr     long',
            'rem     int',
            'rem     long',
            'rem     float',
            'rem     double',
            'add     real',
            'sub     real',
            'mul     real',
            'div     real',
            'sar     int ',
            'sal     int ',
            'shr     int ',
            'rem     real',
            'sar     long ',
            'sal     long ',
            'shr     long ',
            'neg     real',
            'neg     int',
            'neg     long',
            'neg     float',
            'neg     double',
            'cast    int to long',
            'cast    int to float',
            'cast    int to double',
            'cast    int to real',
            'cast    long to int',
            'cast    long to float',
            'cast    long to double',
            'cast    long to real',
            'cast    float to int',
            'cast    float to long',
            'cast    float to double',
            'cast    float to real',
            'cast    double to int',
            'cast    double to long',
            'cast    double to float',
            'cast    double to real',
            'cast    real to int',
            'cast    real to long',
            'cast    real to float',
            'cast    real to double',
            'cast    int to byte',
            'cast    int to short',
            'cast    int to wchar',
            'cast    int to object',
            'cast    long to byte as int',
            'cast    long to short as int',
            'cast    long to wchar as int',
            'cast    object to int',
            'cast    long to byte as long',
            'cast    long to short as long',
            'cast    long to wchar as long',
            'cast    long to int as long',
            'eq      object null ?',
            'ne      object null ?',
            'gt      int 0 ?',
            'le      int 0 ?',
            'eq      int 0 ?',
            'ne      int 0 ?',
            'lt      int 0 ?',
            'ge      int 0 ?',
            'eq      object ?',
            'ne      object ?',
            'gt      int ?',
            'le      int ?',
            'eq      int ?',
            'ne      int ?',
            'lt      int ?',
            'ge      int ?',
            'jmp     ',
            'jmp     ',
            'jmp     ',
            'jmp     ',
            'call    ',
            'call    ',
            'call    ',
            'call    ',
            'jmp     int',
            'call    int',
            'iret1',
            'iret2',
            'ret',
            'ret     ',
            'retval',
            'retval  ',
            'getfield byte as long ',
            'getfield short as long ',
            'getfield int as long ',
            'getfield long ',
            'getfield float ',
            'getfield double ',
            'getfield real ',
            'getfield object ',
            'getfield byte as int ',
            'getfield short as int ',
            'getfield int ',
            'getfield wchar ',
            'getfield float as double ',
            'getfield float as real ',
            'getfield double as real ',
            'getclass',
            'setfield long as byte ',
            'setfield long as short ',
            'setfield long as int ',
            'setfield long ',
            'setfield float ',
            'setfield double ',
            'setfield real ',
            'setfield object ',
            'setfield int as byte ',
            'setfield int as short ',
            'setfield int ',
            'setfield wchar ',
            'setfield double as float ',
            'setfield real as float ',
            'setfield real as double ',
            'setclass',
            'load    ',
            'load    ',
            'load    int ',
            'load    long ',
            'load    float ',
            'load    double ',
            'load    real ',
            'load    object ',
            'store   ',
            'store   ',
            'store   int ',
            'store   long ',
            'store   float ',
            'store   double ',
            'store   real ',
            'store   object ',
            'getrefcount',
            'cmp     long',
            'cmpl    float',
            'cmpg    float',
            'cmpl    double',
            'cmpg    double',
            'cmpl    real',
            'cmpg    real',
            'setrefcount',
            'syscall ',
            'interrupt object ',
            'enter   ',
            'leave',
            'leaveval',
            'currthrd int',
            '',
            'getarraycell byte as long',
            'getarraycell short as long',
            'getarraycell int as long',
            'getarraycell long',
            'getarraycell float',
            'getarraycell double',
            'getarraycell real',
            'getarraycell object',
            'getarraycell byte as int',
            'getarraycell short as int',
            'getarraycell int',
            'getarraycell wchar',
            'getarraycell float as double',
            'getarraycell float as real',
            'getarraycell double as real',
            'getarraylength',
            'setarraycell long as byte',
            'setarraycell long as short',
            'setarraycell long as int',
            'setarraycell long',
            'setarraycell float',
            'setarraycell double',
            'setarraycell real',
            'setarraycell object',
            'setarraycell int as byte',
            'setarraycell int as short',
            'setarraycell int',
            'setarraycell wchar',
            'setarraycell double as float',
            'setarraycell real as float',
            'setarraycell real as double',
            'setarraylength',
            'je      object null ',
            'jne     object null ',
            'jg      int 0 ',
            'jle     int 0 ',
            'je      int 0 ',
            'jne     int 0 ',
            'jl      int 0 ',
            'jge     int 0 ',
            'je      object ',
            'jne     object ',
            'jg      int ',
            'jle     int ',
            'je      int ',
            'jne     int ',
            'jl      int ',
            'jge     int ',
            'je      object null ',
            'jne     object null ',
            'jg      int 0 ',
            'jle     int 0 ',
            'je      int 0 ',
            'jne     int 0 ',
            'jl      int 0 ',
            'jge     int 0 ',
            'je      object ',
            'jne     object ',
            'jg      int ',
            'jle     int ',
            'je      int ',
            'jne     int ',
            'jl      int ',
            'jge     int ',
            'je      object null ',
            'jne     object null ',
            'jg      int 0 ',
            'jle     int 0 ',
            'je      int 0 ',
            'jne     int 0 ',
            'jl      int 0 ',
            'jge     int 0 ',
            'je      object ',
            'jne     object ',
            'jg      int ',
            'jle     int ',
            'je      int ',
            'jne     int ',
            'jl      int ',
            'jge     int ',
            'interrupt ',
            'interrupt int ',
            'interrupt long ',
            'interrupt float ',
            'interrupt double ',
            'interrupt real ',
            'check   arraybound',
            'runexcept object',
            'tableswitch byte ',
            'tableswitch short ',
            'tableswitch int ',
            'tableswitch long ',
            'lookupswitch byte ',
            'lookupswitch short ',
            'lookupswitch int ',
            'lookupswitch long ',
            'int     float',
            'frac    float',
            'sqrt    float',
            'atan    float',
            'sin     float',
            'cos     float',
            'pow2    float',
            'log2    float',
            'int     double',
            'frac    double',
            'sqrt    double',
            'atan    double',
            'sin     double',
            'cos     double',
            'pow2    double',
            'log2    double',
            'int     real',
            'frac    real',
            'sqrt    real',
            'atan    real',
            'sin     real',
            'cos     real',
            'pow2    real',
            'log2    real',
            'floor   float',
            'ceil    float',
            'floor   double',
            'ceil    double',
            'floor   real',
            'ceil    real',
            'round   double',
            'round   real',
            'add     int ',
            'add     long ',
            'add     float ',
            'add     double ',
            'sub     int ',
            'sub     long ',
            'sub     float ',
            'sub     double ',
            'mul     int ',
            'mul     long ',
            'mul     float ',
            'mul     double ',
            'div     int ',
            'div     long ',
            'div     float ',
            'div     double ',
            'or      int ',
            'or      long ',
            'and     int ',
            'and     long ',
            'xor     int ',
            'xor     long ',
            'sar     int ',
            'sar     long ',
            'sal     int ',
            'sal     long ',
            'shr     int ',
            'shr     long ',
            'rem     int ',
            'rem     long ',
            'rem     float ',
            'rem     double ',
            'add     real ',
            'sub     real ',
            'mul     real ',
            'div     real ',
            'sar     int ',
            'sal     int ',
            'shr     int ',
            'rem     real ',
            'sar     long ',
            'sal     long ',
            'shr     long ',
            'neg     real ',
            'neg     int ',
            'neg     long ',
            'neg     float ',
            'neg     double ',
            'roundtn real to integer',
            'roundtn real to double',
            'roundtn real to float',
            'roundtn double to float',
            'roundtni real to integer',
            'roundtni real to double',
            'roundtni real to float',
            'roundtni double to float',
            'roundtpi real to integer',
            'roundtpi real to double',
            'roundtpi real to float',
            'roundtpi double to float',
            'roundtz real to integer',
            'roundtz real to double',
            'roundtz real to float',
            'roundtz double to float',
            'add     int ',
            'add     long ',
            'add     float ',
            'add     double ',
            'sub     int ',
            'sub     long ',
            'sub     float ',
            'sub     double ',
            'mul     int ',
            'mul     long ',
            'mul     float ',
            'mul     double ',
            'div     int ',
            'div     long ',
            'div     float ',
            'div     double ',
            'or      int ',
            'or      long ',
            'and     int ',
            'and     long ',
            'xor     int ',
            'xor     long ',
            'sar     int ',
            'sar     long ',
            'sal     int ',
            'sal     long ',
            'shr     int ',
            'shr     long ',
            'rem     int ',
            'rem     long ',
            'rem     float ',
            'rem     double ',
            'add     real ',
            'sub     real ',
            'mul     real ',
            'div     real ',
            'sar     int ',
            'sal     int ',
            'shr     int ',
            'rem     real ',
            'sar     long ',
            'sal     long ',
            'shr     long ',
            'neg     real ',
            'neg     int ',
            'neg     long ',
            'neg     float ',
            'neg     double ',
            'inc     int ',
            'inc     int ',
            'inc     int',
            'mkreal  real',
            'inc     long ',
            'inc     long ',
            'inc     long',
            'mkreal  float',
            'dec     int ',
            'dec     int ',
            'dec     int',
            'mkreal  double',
            'dec     long ',
            'dec     long ',
            'dec     long',
            '',
            'arraycopyf byte',
            'arraycopyf short',
            'arraycopyf int',
            'arraycopyf long',
            'arraycopyf float',
            'arraycopyf double',
            'arraycopyf real',
            'arraycopyf object',
            'arraycopyb byte',
            'arraycopyb short',
            'arraycopyb int',
            'arraycopyb long',
            'arraycopyb float',
            'arraycopyb double',
            'arraycopyb real',
            'arraycopyb object',
            'arrayfindf byte',
            'arrayfindf short',
            'arrayfindf int',
            'arrayfindf long',
            'arrayfindf float',
            'arrayfindf double',
            'arrayfindf real',
            'arrayfindf object',
            'arrayfindb byte',
            'arrayfindb short',
            'arrayfindb int',
            'arrayfindb long',
            'arrayfindb float',
            'arrayfindb double',
            'arrayfindb real',
            'arrayfindb object',
            'arrayfill byte',
            'arrayfill short',
            'arrayfill int',
            'arrayfill long',
            'arrayfill float',
            'arrayfill double',
            'arrayfill real',
            'arrayfill object',
            'findfreef',
            'findfreeb',
            'findzerof',
            'findzerob',
            'getobjectrefs',
            'getarrayrefs',
            'blockfindf',
            'blockfindb',
            'check   object null ',
            'check   object null ',
            'jmponce '
        ]);
        DATA_REPRESENTER := DefaultRepresenter.create();
    end;

    class procedure TDisassemblerForm.cldone();
    begin
        DATA_REPRESENTER := nil;
        INSTRUCTIONS_NAMES := nil;
    end;

    class function TDisassemblerForm.getFieldRepresentation(const fieldDebugInfoLine: AnsiString): AnsiString;
    var
        colonPos: int;
        fieldName: AnsiString;
        typeName: AnsiString;
    begin
        colonPos := pos(':', fieldDebugInfoLine);
        if colonPos > 0 then begin
            fieldName := trim(copy(fieldDebugInfoLine, 1, colonPos - 1));
            typeName := trim(copy(fieldDebugInfoLine, colonPos + 1, length(fieldDebugInfoLine) - colonPos));
            result := fieldName + ': ' + DATA_REPRESENTER.getTypeRepresentation(typeName);
        end else begin
            result := fieldDebugInfoLine;
        end;
    end;

    class function TDisassemblerForm.getVariableRepresentation(const variableDebugInfoLine: AnsiString): AnsiString;
    var
        periodPos: int;
        colonPos: int;
        className: AnsiString;
        varName: AnsiString;
        typeName: AnsiString;
    begin
        periodPos := pos('.', variableDebugInfoLine);
        colonPos := pos(':', variableDebugInfoLine);
        if (periodPos > 0) and (colonPos > periodPos) then begin
            className := trim(copy(variableDebugInfoLine, 1, periodPos - 1));
            varName := trim(copy(variableDebugInfoLine, periodPos + 1, colonPos - periodPos - 1));
            typeName := trim(copy(variableDebugInfoLine, colonPos + 1, length(variableDebugInfoLine) - colonPos));
            result := DATA_REPRESENTER.getTypeRepresentation(SIGNATURE_OBJECT + className + SIGNATURE_SUFFIX) + ' . ' + varName + ': ' + DATA_REPRESENTER.getTypeRepresentation(typeName);
        end else begin
            result := getFieldRepresentation(variableDebugInfoLine);
        end;
    end;

    class function TDisassemblerForm.getFunctionRepresentation(const functionDebugInfoLine: AnsiString): AnsiString;
    var
        className: AnsiString;
        funcName: AnsiString;
        arguments: AnsiString_Array1d;
        count: int;
        periodPos: int;
        lparenPos: int;
        rparenPos: int;
        i: int;
        j: int;
        r: Representer;

        procedure addArgument(const arg: AnsiString);
        var
            a: AnsiString_Array1d;
            c: int;
        begin
            a := arguments;
            c := count;
            if c = length(a) then begin
                a := String_Array1d_create((c shl 1) + 1);
                arraycopy(arguments, 0, a, 0, c);
                arguments := a;
            end;
            a[c] := arg;
            count := c + 1;
        end;

    begin
        periodPos := pos('.', functionDebugInfoLine);
        lparenPos := pos('(', functionDebugInfoLine);
        rparenPos := pos(')', functionDebugInfoLine);
        if (periodPos > 0) and (lparenPos > periodPos) and (rparenPos > lparenPos) then begin
            r := DATA_REPRESENTER;
            className := r.getTypeRepresentation(SIGNATURE_OBJECT + copy(functionDebugInfoLine, 1, periodPos - 1) + SIGNATURE_SUFFIX);
            funcName := copy(functionDebugInfoLine, periodPos + 1, lparenPos - periodPos - 1);
            count := 0;
            i := lparenPos + 1;
            while i < rparenPos do begin
                case functionDebugInfoLine[i] of
                SIGNATURE_BOOLEAN, SIGNATURE_WCHAR, SIGNATURE_ICHAR, SIGNATURE_FLOAT, SIGNATURE_DOUBLE,
                SIGNATURE_REAL, SIGNATURE_BYTE, SIGNATURE_SHORT, SIGNATURE_INT, SIGNATURE_LONG: begin
                    addArgument(r.getTypeRepresentation(functionDebugInfoLine[i]));
                    inc(i);
                end;
                SIGNATURE_OBJECT, SIGNATURE_CLASS: begin
                    j := i + 1;
                    while (j < rparenPos) and (functionDebugInfoLine[j] <> SIGNATURE_SUFFIX) do begin
                        inc(j);
                    end;
                    if j <> rparenPos then begin
                        addArgument(r.getTypeRepresentation(copy(functionDebugInfoLine, i, j - i + 1)));
                        inc(j);
                    end else begin
                        addArgument(r.getTypeRepresentation(copy(functionDebugInfoLine, i, j - i) + SIGNATURE_SUFFIX));
                    end;
                    i := j;
                end;
                SIGNATURE_ARRAY: begin
                    j := i + 1;
                    while (j < rparenPos) and (functionDebugInfoLine[j] = SIGNATURE_ARRAY) do begin
                        inc(j);
                    end;
                    case functionDebugInfoLine[j] of
                    SIGNATURE_BOOLEAN, SIGNATURE_WCHAR, SIGNATURE_ICHAR, SIGNATURE_FLOAT, SIGNATURE_DOUBLE,
                    SIGNATURE_REAL, SIGNATURE_BYTE, SIGNATURE_SHORT, SIGNATURE_INT, SIGNATURE_LONG: begin
                        inc(j);
                    end;
                    SIGNATURE_OBJECT: begin
                        inc(j);
                        while (j < rparenPos) and (functionDebugInfoLine[j] <> SIGNATURE_SUFFIX) do begin
                            inc(j);
                        end;
                        if j <> rparenPos then begin
                            inc(j);
                        end;
                    end;
                    else
                        i := j;
                        continue;
                    end;
                    addArgument(r.getTypeRepresentation(copy(functionDebugInfoLine, i, j - i)));
                    i := j;
                end;
                else
                    inc(i);
                end;
            end;
            result := className + ' . ' + funcName + '(';
            for i := 0 to count - 1 do begin
                result := result + arguments[i];
                if i < count - 1 then begin
                    result := result + ', ';
                end;
            end;
            result := result + ')';
            j := length(functionDebugInfoLine);
            if (j > rparenPos) and (functionDebugInfoLine[rparenPos + 1] <> SIGNATURE_VOID) then begin
                result := result + ': ' + r.getTypeRepresentation(copy(functionDebugInfoLine, rparenPos + 1, j - rparenPos));
            end;
        end else begin
            result := functionDebugInfoLine;
        end;
    end;

    constructor TDisassemblerForm.create(theOwner: TComponent);
    var
        mainwnd: MainWindowInterface;
    begin
        inherited create(theOwner);
        theOwner.getInterface(EMULATION_WINDOW_INTERFACE_GUID, owner);
        image := TBitmap.create();
        processor := owner.getMalikProcessor();
        currentContext := MalikDebugContext.create();
        guiDebugInspector := DebugInspectorHistory_Array1d_create(16);
        mainwnd := owner.getMainWindow();
        colorEmptyData := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_EMPTY_DATA, ''), DEFAULT_DEBUGGER_COLOR_EMPTY_DATA)) shr 8;
        colorNormalData := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_NORMAL_DATA, ''), DEFAULT_DEBUGGER_COLOR_NORMAL_DATA)) shr 8;
        colorObjectData := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_OBJECT_DATA, ''), DEFAULT_DEBUGGER_COLOR_OBJECT_DATA)) shr 8;
        colorExceptData := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_EXCEPT_DATA, ''), DEFAULT_DEBUGGER_COLOR_EXCEPT_DATA)) shr 8;
        colorOldEBPData := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_OLDEBP_DATA, ''), DEFAULT_DEBUGGER_COLOR_OLDEBP_DATA)) shr 8;
        colorReturnData := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_RETURN_DATA, ''), DEFAULT_DEBUGGER_COLOR_RETURN_DATA)) shr 8;
        colorIReturnData := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_IRETURN_DATA, ''), DEFAULT_DEBUGGER_COLOR_IRETURN_DATA)) shr 8;
        colorNormalType := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_NORMAL_TYPE, ''), DEFAULT_DEBUGGER_COLOR_NORMAL_TYPE)) shr 8;
        colorObjectType := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_OBJECT_TYPE, ''), DEFAULT_DEBUGGER_COLOR_OBJECT_TYPE)) shr 8;
        colorExceptType := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_EXCEPT_TYPE, ''), DEFAULT_DEBUGGER_COLOR_EXCEPT_TYPE)) shr 8;
        colorOldEBPType := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_OLDEBP_TYPE, ''), DEFAULT_DEBUGGER_COLOR_OLDEBP_TYPE)) shr 8;
        colorReturnType := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_RETURN_TYPE, ''), DEFAULT_DEBUGGER_COLOR_RETURN_TYPE)) shr 8;
        colorIReturnType := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_IRETURN_TYPE, ''), DEFAULT_DEBUGGER_COLOR_IRETURN_TYPE)) shr 8;
        colorOverflowType := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_OVERFLOW_TYPE, ''), DEFAULT_DEBUGGER_COLOR_OVERFLOW_TYPE)) shr 8;
        colorFunctionName := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_FUNCTION_NAME, ''), DEFAULT_DEBUGGER_COLOR_FUNCTION_NAME)) shr 8;
        colorSourceLine := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_SOURCE_LINE, ''), DEFAULT_DEBUGGER_COLOR_SOURCE_LINE)) shr 8;
        colorInherited := byteSwap(parseHexInt(mainwnd.getSetting(SECTION_DEBUGGER_COLORS, KEY_INHERITED, ''), DEFAULT_DEBUGGER_COLOR_INHERITED)) shr 8;
    end;

    destructor TDisassemblerForm.destroy;
    begin
        destroyInfo(allDebugInfo);
        destroyInfo(classesInfo);
        destroyInfo(variablesInfo);
        destroyInfo(functionsInfo);
        destroyGUI(guiStack);
        destroyGUI(guiDebugInspector);
        guiContext.free();
        destroyGUI(guiStatus);
        guiDisassembler.free();
        currentContext.free();
        image.free();
        inherited destroy;
    end;

    procedure TDisassemblerForm.formShow(sender: TObject);
    begin
        disassemblerScrollbar.position := 0;
        updateScreen();
    end;

    procedure TDisassemblerForm.formKeyDown(sender: TObject; var key: Word; shift: TShiftState);
    begin
        if (key = VK_F7) and (shift * [ssShift, ssCtrl, ssAlt] = []) then begin
            { F7 – шаг со входом }
            stepIntoButtonClick(stepIntoButton);
            key := 0;
            exit;
        end;
        if (key = VK_F11) and (shift * [ssShift, ssCtrl, ssAlt] = []) and debugInspectorButton.enabled then begin
            { F11 – инспектор отладки }
            debugInspectorButtonClick(debugInspectorButton);
            key := 0;
            exit;
        end;
        if (key = VK_RETURN) and (shift * [ssShift, ssCtrl, ssAlt] = [ssAlt]) then begin
            { Alt+Enter – переключение между оконным и полноэкранным режимом }
            switchFullScreenMode();
            key := 0;
            exit;
        end;
        if (key = VK_D) and (shift * [ssShift, ssCtrl, ssAlt] = [ssAlt]) then begin
            { Alt+D – показать окно программы }
            owner.showEmulationWindow();
            key := 0;
            exit;
        end;
        if (key = VK_M) and (shift * [ssShift, ssCtrl, ssAlt] = [ssAlt]) then begin
            { Alt+M – показать главное окно }
            owner.getMainWindow().showMainWindow();
            key := 0;
            exit;
        end;
    end;

    procedure TDisassemblerForm.formKeyUp(sender: TObject; var key: Word; shift: TShiftState);
    begin
        if (key = VK_F8) and (shift * [ssShift, ssCtrl, ssAlt] = []) then begin
            { F8 – шаг в обход }
            stepOverButtonClick(stepOverButton);
            key := 0;
            exit;
        end;
        if (key = VK_F9) and (shift * [ssShift, ssCtrl, ssAlt] = []) then begin
            { F9 – запуск }
            runButtonClick(runButton);
            key := 0;
            exit;
        end;
        if (key = VK_F9) and (shift * [ssShift, ssCtrl, ssAlt] = [ssShift]) then begin
            { Shift+F9 – скрыть дизассемблер и запуск }
            hideAndRunButtonClick(hideAndRunButton);
            key := 0;
            exit;
        end;
    end;

    procedure TDisassemblerForm.formResize(sender: TObject);
    var
        h: int;
    begin
        with debugInspectorPanel do begin
            if left + width > self.clientWidth then begin
                left := self.clientWidth - width;
            end;
            if top + height > self.clientHeight then begin
                top := self.clientHeight - height;
            end;
            if left < 0 then begin
                left := 0;
            end;
            if top < 0 then begin
                top := 0;
            end;
            if left + width > self.clientWidth then begin
                width := self.clientWidth - left;
            end;
            if top + height > self.clientHeight then begin
                height := self.clientHeight - top;
            end;
        end;
        h := abs(image.canvas.font.height);
        if h = 0 then begin
            exit;
        end;
        setupScrollbar(statusScrollbar, max(1, status.height div h));
        setupScrollbar(contextScrollbar, max(1, context.height div h));
        setupScrollbar(stackScrollbar, max(1, stack.height div h));
        setupScrollbar(debugInspectorScrollbar, max(1, debugInspector.height div h));
    end;

    procedure TDisassemblerForm.formWindowStateChange(sender: TObject);
    begin
        disassemblerScrollbar.repaint();
        statusScrollbar.repaint();
        contextScrollbar.repaint();
        stackScrollbar.repaint();
        debugInspectorScrollbar.repaint();
    end;

    procedure TDisassemblerForm.formGUIElementPaint(sender: TObject);
    var
        element: GUIElement;
    begin
        element := getGUIElementFor(sender);
        if element <> nil then begin
            paintGUIElement(element);
        end;
    end;

    procedure TDisassemblerForm.formGUIElementPointerPressed(sender: TObject; button: TMouseButton; shift: TShiftState; x, y: integer);
    var
        element: GUIElement;
    begin
        element := getGUIElementFor(sender);
        if element <> nil then begin
            element.pointerPressed(x, y, button <> mbLeft);
        end;
    end;

    procedure TDisassemblerForm.formGUIElementPointerReleased(sender: TObject; button: TMouseButton; shift: TShiftState; x, y: integer);
    var
        element: GUIElement;
    begin
        element := getGUIElementFor(sender);
        if element <> nil then begin
            element.pointerReleased(x, y, button <> mbLeft);
        end;
    end;

    procedure TDisassemblerForm.formGUIElementScroll(sender: TObject);
    var
        element: GUIElement;
    begin
        element := getGUIElementFor(sender);
        if element <> nil then begin
            element.position := TScrollBar(sender).position;
            paintGUIElement(element);
        end;
    end;

    procedure TDisassemblerForm.stepIntoButtonClick(sender: TObject);
    var
        i: int;
    begin
        if not owner.isNowDebugging() then begin
            exit;
        end;
        i := contextID;
        threadList.items.strings[i] := 'Поток ' + toDecString(i);
        contextID := threadList.itemIndex;
        stepInto := true;
        owner.setDebug(true);
        owner.programmeResume();
    end;

    procedure TDisassemblerForm.stepOverButtonClick(sender: TObject);
    var
        bytes: PByteArray;
        opcode: int;
        eip: int;
        i: int;
    begin
        if not owner.isNowDebugging() then begin
            exit;
        end;
        i := contextID;
        threadList.items.strings[i] := 'Поток ' + toDecString(i);
        contextID := threadList.itemIndex;
        eip := currentContext.regEIP;
        bytes := PByteArray(processor.getMemory(eip, 2));
        if bytes <> nil then begin
            if int(bytes[0]) = -1 then begin
                opcode := $ff00 + (int(bytes[1]) and $ff)
            end else begin
                opcode := int(bytes[0]) and $ff;
            end;
        end else begin
            opcode := 0;
        end;
        case opcode of
        $b4..$b7, $b9, $fa, $ff50..$ff55: begin
            stepOver := eip + getInstructionLength(eip);
            owner.showEmulationWindow();
        end;
        else
            stepInto := true;
        end;
        owner.setDebug(true);
        owner.programmeResume();
    end;

    procedure TDisassemblerForm.runButtonClick(sender: TObject);
    begin
        if not owner.isNowDebugging() then begin
            exit;
        end;
        owner.showEmulationWindow();
        owner.setDebug(hasBreakpoints());
        owner.programmeResume();
    end;

    procedure TDisassemblerForm.hideAndRunButtonClick(sender: TObject);
    begin
        if not owner.isNowDebugging() then begin
            exit;
        end;
        windowState := wsMinimized;
        owner.showEmulationWindow();
        owner.setDebug(hasBreakpoints());
        owner.programmeResume();
    end;

    procedure TDisassemblerForm.debugInspectorButtonClick(sender: TObject);
    begin
        if debugInspectorPanel.visible then begin
            disassemblerScrollbar.setFocus();
            debugInspectorPanel.visible := false;
        end else begin
            debugInspectorPanel.visible := true;
            if debugInspectorScrollbar.enabled then begin
                debugInspectorScrollbar.setFocus();
            end;
        end;
    end;

    procedure TDisassemblerForm.disassemblerScrollbarKeyDown(sender: TObject; var key: Word; shift: TShiftState);
    begin
        if ((key = VK_DOWN) or (key = VK_RIGHT)) and (shift * [ssShift, ssCtrl, ssAlt] = [ssCtrl]) then begin
            if disassemblerPosition < MAX_INT then begin
                inc(disassemblerPosition);
                paintGUIElement(guiDisassembler);
            end;
            key := 0;
            exit;
        end;
        if ((key = VK_UP) or (key = VK_LEFT)) and (shift * [ssShift, ssCtrl, ssAlt] = [ssCtrl]) then begin
            if disassemblerPosition > 0 then begin
                dec(disassemblerPosition);
                paintGUIElement(guiDisassembler);
            end;
            key := 0;
            exit;
        end;
        formKeyDown(sender, key, shift);
    end;

    procedure TDisassemblerForm.disassemblerScrollbarScroll(sender: TObject; action: TScrollCode; var position: integer);
    var
        a: int;
        i: int;
    begin
        position := 0;
        case action of
        scLineUp: begin
            i := 1;
            while (i <= 10) and (disassemblerPosition - i >= 0) do begin
                if getInstructionLength(disassemblerPosition - i) = i then begin
                    break;
                end;
                inc(i);
            end;
            dec(disassemblerPosition, i);
            if disassemblerPosition < 0 then begin
                disassemblerPosition := 0;
            end;
        end;
        scLineDown: begin
            inc(disassemblerPosition, getInstructionLength(disassemblerPosition));
            if disassemblerPosition < 0 then begin
                disassemblerPosition := MAX_INT;
            end;
        end;
        scPageUp: begin
            a := (disassembler.height div abs(image.canvas.font.height)) - 1;
            i := 1;
            while (i > 0) and (disassemblerPosition - i >= 0) do begin
                if getInstructionsCount(disassemblerPosition - i, i) >= a then begin
                    break;
                end;
                inc(i);
            end;
            dec(disassemblerPosition, i);
            if disassemblerPosition < 0 then begin
                disassemblerPosition := 0;
            end;
        end;
        scPageDown: begin
            disassemblerPosition := maxVisibleAddress;
            if disassemblerPosition < 0 then begin
                disassemblerPosition := MAX_INT;
            end;
        end;
        scTop: begin
            disassemblerPosition := 0;
        end;
        scBottom: begin
            disassemblerPosition := MAX_INT;
        end;
        end;
        paintGUIElement(guiDisassembler);
    end;

    procedure TDisassemblerForm.threadListSelect(sender: TObject);
    begin
        processor.getContext(threadList.itemIndex, currentContext);
        updateScreen();
    end;

    procedure TDisassemblerForm.debugInspectorCaptionSizeGripMouseDown(sender: TObject; button: TMouseButton; shift: TShiftState; x, y: integer);
    begin
        if button = mbLeft then begin
            resizing := sender = debugInspectorSizeGrip;
            self.x := x;
            self.y := y;
        end;
    end;

    procedure TDisassemblerForm.debugInspectorCaptionSizeGripMouseMove(sender: TObject; shift: TShiftState; x, y: integer);
    var
        l, t, w, h: int;
    begin
        if not (ssLeft in shift) then begin
            exit;
        end;
        with debugInspectorPanel do begin
            if resizing then begin
                w := width + x - self.x;
                h := height + y - self.y;
                if left + w > self.width then begin
                    w := self.width - left;
                end;
                if top + h > self.height then begin
                    h := self.height - top;
                end;
                width := w;
                height := h;
                setupScrollbar(debugInspectorScrollbar, max(1, debugInspector.height div abs(image.canvas.font.height)));
            end else begin
                l := left + x - self.x;
                t := top + y - self.y;
                if l < 0 then begin
                    l := 0;
                end;
                if l + width > self.width then begin
                    l := self.width - width;
                end;
                if t < 0 then begin
                    t := 0;
                end;
                if t + height > self.height then begin
                    t := self.height - height;
                end;
                left := l;
                top := t;
            end;
        end;
    end;

    procedure TDisassemblerForm.debugInspectorBackButtonClick(sender: TObject);
    begin
        guiDebugInspector[debugInspectorPages.activePageIndex].gotoPrevElement();
    end;

    procedure TDisassemblerForm.debugInspectorNextButtonClick(sender: TObject);
    begin
        guiDebugInspector[debugInspectorPages.activePageIndex].gotoNextElement();
    end;

    procedure TDisassemblerForm.debugInspectorMenuButtonClick(sender: TObject);
    var
        p: TPoint;
    begin
        p := debugInspectorBackButton.clientToScreen(point(0, debugInspectorBackButton.height));
        guiDebugInspector[debugInspectorPages.activePageIndex].getMenu().popup(p.x, p.y);
    end;

    procedure TDisassemblerForm.debugInspectorCloseTabButtonClick(sender: TObject);
    var
        i: int;
        j: int;
    begin
        j := debugInspectorPages.activePageIndex;
        guiDebugInspector[j].free();
        for i := j to guiDebugInspectorCount - 2 do begin
            guiDebugInspector[i] := guiDebugInspector[i + 1];
        end;
        dec(guiDebugInspectorCount);
        guiDebugInspector[guiDebugInspectorCount] := nil;
        debugInspectorPages.pages[j].free();
        if guiDebugInspectorCount > 0 then begin
            debugInspector.repaint();
        end else begin
            debugInspectorPanel.visible := false;
            debugInspectorButton.enabled := false;
        end;
    end;

    procedure TDisassemblerForm.debugInspectorSizeGripPaint(sender: TObject);
    begin
        with debugInspectorSizeGrip.canvas, themeServices() do begin
            if themesAvailable and themesEnabled then begin
                drawElement(handle, getElementDetails(TThemedScrollBar.tsSizeBoxRightAlign), bounds(0, 0, debugInspectorSizeGrip.width, debugInspectorSizeGrip.height));
            end else begin
                drawFrameControl(handle, bounds(0, 0, debugInspectorSizeGrip.width, debugInspectorSizeGrip.height), DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
            end;
        end;
    end;

    procedure TDisassemblerForm.debugInspectorHistoryMenuClickItem(sender: TObject);
    begin
        guiDebugInspector[debugInspectorPages.activePageIndex].gotoElement(TMenuItem(sender).tag);
    end;

    function TDisassemblerForm.findClassInfo(const className: AnsiString): ClassDebugInfo;
    var
        i: int;
        d: ClassDebugInfo;
        c: ClassDebugInfo_Array1d;
    begin
        c := classesInfo;
        for i := 0 to length(c) - 1 do begin
            d := c[i];
            if (d <> nil) and (d.getInfo() = className) then begin
                result := d;
                exit;
            end;
        end;
        result := nil;
    end;

    function TDisassemblerForm.getDebugInfo(address: int; const info: DebugInfo_Array1d): AnsiString;
    var
        a: int;
        b: int;
        c: int;
        p: int;
        i: DebugInfo;
    begin
        result := '';
        a := 0;
        b := length(info) - 1;
        while a <= b do begin
            c := (a + b) shr 1;
            i := info[c];
            p := address - i.getAddress();
            if p > 0 then begin
                a := c + 1;
            end else
            if p < 0 then begin
                b := c - 1;
            end else begin
                result := i.getInfo();
                break;
            end;
        end;
    end;

    function TDisassemblerForm.getClassInfo(address: int; const info: ClassDebugInfo_Array1d): ClassDebugInfo;
    var
        a: int;
        b: int;
        c: int;
        p: int;
        i: ClassDebugInfo;
    begin
        result := nil;
        a := 0;
        b := length(info) - 1;
        while a <= b do begin
            c := (a + b) shr 1;
            i := info[c];
            if i <> nil then begin
                p := address - i.getAddress();
            end else begin
                p := 0;
            end;
            if p > 0 then begin
                a := c + 1;
            end else
            if p < 0 then begin
                b := c - 1;
            end else begin
                result := i;
                break;
            end;
        end;
    end;

    function TDisassemblerForm.getFunctionAt(address: int): DebugInfo;
    var
        a: int;
        b: int;
        c: int;
        i: int;
        len: int;
        info: DebugInfo_Array1d;
    begin
        info := functionsInfo;
        len := length(info);
        if (len = 0) or (address < info[0].getAddress()) then begin
            result := nil;
            exit;
        end;
        a := 0;
        b := len - 1;
        c := -1;
        while a <= b do begin
            c := (a + b) shr 1;
            i := address - info[c].getAddress();
            if i > 0 then begin
                a := c + 1;
            end else
            if i < 0 then begin
                b := c - 1;
            end else begin
                break;
            end;
        end;
        if a > b then begin
            if (a < len) and (b >= 0) then begin
                for i := a downto b do begin
                    if info[i].getAddress() < address then begin
                        c := i;
                        break;
                    end;
                end;
            end;
        end else begin
            for i := c - 1 downto 0 do begin
                if info[i].getAddress() <> address then begin
                    c := i + 1;
                    break;
                end;
            end;
        end;
        if c >= 0 then begin
            result := info[c];
        end else begin
            result := nil;
        end;
    end;

    function TDisassemblerForm.getAddressRepresentation(address: int): AnsiString;
    var
        func: DebugInfo;
    begin
        result := getDebugInfo(address, functionsInfo);
        if length(result) > 0 then begin
            result := getFunctionRepresentation(result);
            exit;
        end;
        result := getDebugInfo(address, variablesInfo);
        if length(result) > 0 then begin
            result := getVariableRepresentation(result);
            exit;
        end;
        func := getFunctionAt(address);
        if func <> nil then begin
            result := getFunctionRepresentation(func.getInfo()) + ' + ' + DATA_REPRESENTER.getHexRepresentation(address - func.getAddress()) + ' (' + DATA_REPRESENTER.getHexRepresentation(address) + ')';
            exit;
        end;
        result := DATA_REPRESENTER.getHexRepresentation(address);
    end;

    function TDisassemblerForm.getValueRepresentation(address: int; const typeInfo: AnsiString): AnsiString;
    var
        p: Pointer;
    begin
        p := processor.getMemory(address, 10);
        if p = nil then begin
            result := '???';
            exit;
        end;
        if length(typeInfo) <= 0 then begin
            result := 'N/T'; { Not a Type - пустая строка «не является типом» }
            exit;
        end;
        case typeInfo[1] of
        SIGNATURE_BOOLEAN: begin
            result := DATA_REPRESENTER.getLongRepresentation(byte(p^), DATA_TYPE_BOOLEAN);
        end;
        SIGNATURE_BYTE: begin
            result := DATA_REPRESENTER.getLongRepresentation(byte(p^), DATA_TYPE_BYTE);
        end;
        SIGNATURE_SHORT: begin
            result := DATA_REPRESENTER.getLongRepresentation(short(p^), DATA_TYPE_SHORT);
        end;
        SIGNATURE_INT: begin
            result := DATA_REPRESENTER.getLongRepresentation(int(p^), DATA_TYPE_INT);
        end;
        SIGNATURE_LONG: begin
            result := DATA_REPRESENTER.getLongRepresentation(long(p^), DATA_TYPE_LONG);
        end;
        SIGNATURE_WCHAR: begin
            result := DATA_REPRESENTER.getLongRepresentation(short(p^), DATA_TYPE_WCHAR);
        end;
        SIGNATURE_ICHAR: begin
            result := DATA_REPRESENTER.getLongRepresentation(int(p^), DATA_TYPE_ICHAR);
        end;
        SIGNATURE_FLOAT: begin
            result := DATA_REPRESENTER.getFloatRepresentation(float(p^));
        end;
        SIGNATURE_DOUBLE: begin
            result := DATA_REPRESENTER.getDoubleRepresentation(double(p^));
        end;
        SIGNATURE_REAL: begin
            result := DATA_REPRESENTER.getRealRepresentation(real(p^));
        end;
        SIGNATURE_OBJECT, SIGNATURE_CLASS, SIGNATURE_ARRAY: begin
            result := DATA_REPRESENTER.getObjectRepresentation(int(p^));
        end;
        else
            result := 'U/T'; { Unknown Type - неизвестный тип }
        end;
    end;

    function TDisassemblerForm.getInstructionByteCode(address: int): AnsiString;
    var
        bytes: PByteArray;
        b: int;
        i: int;
        len: int;
    begin
        len := getInstructionLength(address);
        if len > 11 then begin
            len := 11;
        end;
        bytes := PByteArray(processor.getMemory(address, len));
        if bytes = nil then begin
            result := '??';
            exit;
        end;
        result := String_create(len * 2);
        for i := 0 to len - 1 do begin
            b := int(bytes[i]) and $ff;
            result[2 * i + 1] := HEX_DIGITS[(b shr $4) + 1];
            result[2 * i + 2] := HEX_DIGITS[(b and $f) + 1];
        end;
    end;

    function TDisassemblerForm.getInstructionRepresentation(address: int): AnsiString;
    var
        bytes: PByteArray;
        byte1: int;
        byte2: int;
        byte3: int;
        deflb: int;
        value: int;
        val1: long;
        val2: long;
        size: long;
        r: Representer;
    begin
        bytes := PByteArray(processor.getMemory(address, 22));
        if bytes = nil then begin
            result := '???';
            exit;
        end;
        byte1 := int(bytes[0]) and $ff;
        byte2 := int(bytes[1]) and $ff;
        byte3 := int(bytes[2]) and $ff;
        r := DATA_REPRESENTER;
        result := INSTRUCTIONS_NAMES[byte1];
        case byte1 of
        $00, $10, $12, $14, $16, $18..$1f, $2f, $3f, $50..$73, $77, $7b..$af, $b8..$bb, $bc, $be, $cf,
        $df, $f0..$f8, $fc..$fe: { для проверки };
        $01, $09: begin
            result := result + r.getLongRepresentation(bytes[1], DATA_TYPE_BYTE);
        end;
        $02, $0a: begin
            result := result + r.getLongRepresentation(short((@(bytes[1]))^), DATA_TYPE_SHORT);
        end;
        $03, $0b: begin
            result := result + r.getLongRepresentation(int((@(bytes[1]))^), DATA_TYPE_ICHAR);
        end;
        $04: begin
            result := result + r.getLongRepresentation(long((@(bytes[1]))^), DATA_TYPE_LONG);
        end;
        $05, $0d..$0e: begin
            result := result + r.getFloatRepresentation(float((@(bytes[1]))^));
        end;
        $06, $0f: begin
            result := result + r.getDoubleRepresentation(double((@(bytes[1]))^));
        end;
        $07: begin
            result := result + r.getRealRepresentation(real((@(bytes[1]))^));
        end;
        $b3, $b7: begin
            result := result + getAddressRepresentation(int((@(bytes[1]))^));
        end;
        $0c: begin
            result := result + r.getLongRepresentation(short((@(bytes[1]))^), DATA_TYPE_WCHAR);
        end;
        $11, $13, $15: begin
            result := result + toDecString(int(short((@(bytes[1]))^)) and $ffff);
        end;
        $17: begin
            result := result + toDecString(int(short((@(bytes[1]))^)) and $ffff) + ' x ' + toDecString(int(short((@(bytes[3]))^)) and $ffff);
        end;
        $20..$2e, $30..$3e: begin
            result := result + '[' + getAddressRepresentation(int((@(bytes[1]))^)) + ']';
        end;
        $40, $42..$48, $4a..$4f: begin
            result := result + '[esp+' + r.getHexRepresentation((int(short((@(bytes[1]))^)) and $ffff) shl 4) + ']';
        end;
        $41, $49: begin
            result := result + '[esp+' + r.getHexRepresentation(int((@(bytes[1]))^) shl 4) + ']';
        end;
        $74..$76, $78..$7a: begin
            result := result + r.getLongRepresentation(int(bytes[1]) and $ff, DATA_TYPE_INT);
        end;
        $b0, $b4: begin
            result := result + getAddressRepresentation(address + int(bytes[1]) + 2);
        end;
        $b1, $b5: begin
            result := result + getAddressRepresentation(address + int(short((@(bytes[1]))^)) + 3);
        end;
        $b2, $b6, $08: begin
            result := result + getAddressRepresentation(address + int((@(bytes[1]))^) + 5);
        end;
        $bd, $bf: begin
            result := result + toDecString(int(bytes[1]) and $ff);
        end;
        $fa: begin
            result := result + r.getHexRepresentation(int(bytes[1]) and $ff);
        end;
        $c0..$ce, $d0..$de: begin
            value := sar(int((@(bytes[0]))^), 8);
            if value >= 0 then begin
                result := result + '+' + r.getHexRepresentation(value);
            end else begin
                result := result + '-' + r.getHexRepresentation(-value);
            end;
        end;
        $e0, $e2..$e8, $ea..$ef: begin
            value := int(short((@(bytes[1]))^)) shl 4;
            if value >= 0 then begin
                result := result + '[ebp+' + r.getHexRepresentation(value) + ']';
            end else begin
                result := result + '[ebp-' + r.getHexRepresentation(-value) + ']';
            end;
        end;
        $e1, $e9: begin
            value := int((@(bytes[1]))^) shl 4;
            if value >= 0 then begin
                result := result + '[ebp+' + r.getHexRepresentation(value) + ']';
            end else begin
                result := result + '[ebp-' + r.getHexRepresentation(-value) + ']';
            end;
        end;
        $f9, $fb: begin
            result := result + r.getHexRepresentation(int(short((@(bytes[1]))^)) and $ffff);
        end;
        $ff: begin
            result := INSTRUCTIONS_NAMES[byte2 + $100];
            case byte2 of
            $00..$1f, $56..$57, $60..$7f, $b0..$bf, $f2..$f3, $f6..$f7, $fa..$fb, $fe:
                { для проверки };
            $20..$2f: begin
                result := result + getAddressRepresentation(address + int(bytes[2]) + 3);
            end;
            $30..$3f: begin
                result := result + getAddressRepresentation(address + int(short((@(bytes[2]))^)) + 4);
            end;
            $40..$4f: begin
                result := result + getAddressRepresentation(address + int((@(bytes[2]))^) + 6);
            end;
            $50..$55: begin
                result := result + r.getHexRepresentation(int(bytes[2]) and $ff);
            end;
            $58: begin
                val1 := bytes[2];
                val2 := bytes[3];
                size := (val2 - val1) * 4 + 12;
                deflb := address + int((@(bytes[4]))^) + int(size);
                result := result +
                    '{size=' + r.getLongRepresentation(size, DATA_TYPE_LONG) +
                    ', min=' + r.getLongRepresentation(val1, DATA_TYPE_BYTE) +
                    ', max=' + r.getLongRepresentation(val2, DATA_TYPE_BYTE) +
                    ', default=' + r.getHexRepresentation(deflb) + '}'
                ;
            end;
            $59: begin
                val1 := short((@(bytes[2]))^);
                val2 := short((@(bytes[4]))^);
                size := (val2 - val1) * 4 + 14;
                deflb := address + int((@(bytes[6]))^) + int(size);
                result := result +
                    '{size=' + r.getLongRepresentation(size, DATA_TYPE_LONG) +
                    ', min=' + r.getLongRepresentation(val1, DATA_TYPE_SHORT) +
                    ', max=' + r.getLongRepresentation(val2, DATA_TYPE_SHORT) +
                    ', default=' + r.getHexRepresentation(deflb) + '}'
                ;
            end;
            $5a: begin
                val1 := int((@(bytes[2]))^);
                val2 := int((@(bytes[6]))^);
                size := (val2 - val1) * 4 + 18;
                deflb := address + int((@(bytes[10]))^) + int(size);
                result := result +
                    '{size=' + r.getLongRepresentation(size, DATA_TYPE_LONG) +
                    ', min=' + r.getLongRepresentation(val1, DATA_TYPE_INT) +
                    ', max=' + r.getLongRepresentation(val2, DATA_TYPE_INT) +
                    ', default=' + r.getHexRepresentation(deflb) + '}'
                ;
            end;
            $5b: begin
                val1 := long((@(bytes[2]))^);
                val2 := long((@(bytes[10]))^);
                size := (val2 - val1) * 4 + 26;
                deflb := address + int((@(bytes[18]))^) + int(size);
                result := result +
                    '{size=' + r.getLongRepresentation(size, DATA_TYPE_LONG) +
                    ', min=' + r.getLongRepresentation(val1, DATA_TYPE_LONG) +
                    ', max=' + r.getLongRepresentation(val2, DATA_TYPE_LONG) +
                    ', default=' + r.getHexRepresentation(deflb) + '}'
                ;
            end;
            $5c: begin
                value := int(short((@(bytes[2]))^)) and $ffff;
                size := long(value) * 5 + 8;
                deflb := address + int((@(bytes[4]))^) + int(size);
                result := result +
                    '{size=' + r.getLongRepresentation(size, DATA_TYPE_LONG) +
                    ', labels=' + r.getLongRepresentation(value, DATA_TYPE_INT) +
                    ', default=' + r.getHexRepresentation(deflb) + '}'
                ;
            end;
            $5d: begin
                value := int(short((@(bytes[2]))^)) and $ffff;
                size := long(value) * 6 + 8;
                deflb := address + int((@(bytes[4]))^) + int(size);
                result := result +
                    '{size=' + r.getLongRepresentation(size, DATA_TYPE_LONG) +
                    ', labels=' + r.getLongRepresentation(value, DATA_TYPE_INT) +
                    ', default=' + r.getHexRepresentation(deflb) + '}'
                ;
            end;
            $5e: begin
                value := int(short((@(bytes[2]))^)) and $ffff;
                size := long(value) * 8 + 8;
                deflb := address + int((@(bytes[4]))^) + int(size);
                result := result +
                    '{size=' + r.getLongRepresentation(size, DATA_TYPE_LONG) +
                    ', labels=' + r.getLongRepresentation(value, DATA_TYPE_INT) +
                    ', default=' + r.getHexRepresentation(deflb) + '}'
                ;
            end;
            $5f: begin
                value := int(short((@(bytes[2]))^)) and $ffff;
                size := long(value) * 12 + 8;
                deflb := address + int((@(bytes[4]))^) + int(size);
                result := result +
                    '{size=' + r.getLongRepresentation(size, DATA_TYPE_LONG) +
                    ', labels=' + r.getLongRepresentation(value, DATA_TYPE_INT) +
                    ', default=' + r.getHexRepresentation(deflb) + '}'
                ;
            end;
            $80..$a3, $a7, $ab..$af, $f0, $f4, $f8, $fc: begin
                result := result + '[esp+' + r.getHexRepresentation((int(short((@(bytes[2]))^)) and $ffff) shl 4) + ']';
            end;
            $a4..$a6, $a8..$aa: begin
                result := result + '[esp+' + r.getHexRepresentation((int(short((@(bytes[2]))^)) and $ffff) shl 4) + '], ' + r.getLongRepresentation(int(bytes[4]) and $ff, DATA_TYPE_INT);
            end;
            $c0..$e3, $e7, $eb..$ef, $f1, $f5, $f9, $fd: begin
                value := int(short((@(bytes[2]))^)) shl 4;
                if value >= 0 then begin
                    result := result + '[ebp+' + r.getHexRepresentation(value) + ']';
                end else begin
                    result := result + '[ebp-' + r.getHexRepresentation(-value) + ']';
                end;
            end;
            $e4..$e6, $e8..$ea: begin
                value := int(short((@(bytes[2]))^)) shl 4;
                if value >= 0 then begin
                    result := result + '[ebp+' + r.getHexRepresentation(value) + '], ';
                end else begin
                    result := result + '[ebp-' + r.getHexRepresentation(-value) + '], ';
                end;
                result := result + r.getLongRepresentation(int(bytes[4]) and $ff, DATA_TYPE_INT);
            end;
            $ff: begin
                if byte3 + $200 < length(INSTRUCTIONS_NAMES) then begin
                    result := INSTRUCTIONS_NAMES[byte3 + $200];
                    case byte3 of
                    $30: begin
                        result := result + '[esp+' + r.getHexRepresentation((int(short((@(bytes[3]))^)) and $ffff) shl 4) + ']';
                    end;
                    $31: begin
                        value := int(short((@(bytes[3]))^)) shl 4;
                        if value >= 0 then begin
                            result := result + '[ebp+' + r.getHexRepresentation(value) + ']';
                        end else begin
                            result := result + '[ebp-' + r.getHexRepresentation(-value) + ']';
                        end;
                    end;
                    $32: begin
                        result := result + getAddressRepresentation(address + int(bytes[3]) + 4);
                    end;
                    end;
                end else begin
                    result := '(bad)';
                end;
            end;
            end;
        end;
        end;
    end;

    function TDisassemblerForm.getInstructionLength(address: int): int;
    var
        bytes: PByteArray;
        byte1: int;
        byte2: int;
        byte3: int;
        value: int;
        val1: long;
        val2: long;
    begin
        bytes := processor.getMemory(address, 22);
        if bytes = nil then begin
            result := 1;
            exit;
        end;
        byte1 := int(bytes[0]) and $ff;
        byte2 := int(bytes[1]) and $ff;
        byte3 := int(bytes[2]) and $ff;
        case byte1 of
        $00, $10, $12, $14, $16, $18..$1f, $2f, $3f, $50..$73, $77, $7b..$af, $b8..$bb, $bc, $be, $cf,
        $df, $f0..$f8, $fc..$fe: begin
            result := 1;
        end;
        $01, $09, $74..$76, $78..$7a, $b0, $b4, $bd, $bf, $fa: begin
            result := 2;
        end;
        $02, $0a, $0c, $11, $13, $15, $40, $42..$48, $4a..$4f, $b1, $b5, $e0, $e2..$e8, $ea..$ef,
        $f9, $fb: begin
            result := 3;
        end;
        $c0..$ce, $d0..$de: begin
            result := 4;
        end;
        $03, $05, $08, $0b, $0d..$0e, $17, $20..$2e, $30..$3e, $41, $49, $b2..$b3, $b6..$b7,
        $e1, $e9: begin
            result := 5;
        end;
        $04, $06, $0f: begin
            result := 9;
        end;
        $07: begin
            result := 11;
        end;
        $ff: begin
            case byte2 of
            $00..$1f, $56..$57, $60..$7f, $b0..$bf, $f2..$f3, $f6..$f7, $fa..$fb, $fe: begin
                result := 2;
            end;
            $20..$2f, $50..$55: begin
                result := 3;
            end;
            $30..$3f, $80..$a3, $a7, $ab..$af, $c0..$e3, $e7, $eb..$f1,
            $f4..$f5, $f8..$f9, $fc..$fd: begin
                result := 4;
            end;
            $a4..$a6, $a8..$aa, $e4..$e6, $e8..$ea: begin
                result := 5;
            end;
            $40..$4f: begin
                result := 6;
            end;
            $58: begin
                val1 := bytes[2];
                val2 := bytes[3];
                result := int(val2 - val1) * 4 + 12;
            end;
            $59: begin
                val1 := short((@(bytes[2]))^);
                val2 := short((@(bytes[4]))^);
                result := int(val2 - val1) * 4 + 14;
            end;
            $5a: begin
                val1 := int((@(bytes[2]))^);
                val2 := int((@(bytes[6]))^);
                result := int(val2 - val1) * 4 + 18;
            end;
            $5b: begin
                val1 := long((@(bytes[2]))^);
                val2 := long((@(bytes[10]))^);
                result := int(val2 - val1) * 4 + 26;
            end;
            $5c: begin
                value := int(short((@(bytes[2]))^)) and $ffff;
                result := value * 5 + 8;
            end;
            $5d: begin
                value := int(short((@(bytes[2]))^)) and $ffff;
                result := value * 6 + 8;
            end;
            $5e: begin
                value := int(short((@(bytes[2]))^)) and $ffff;
                result := value * 8 + 8;
            end;
            $5f: begin
                value := int(short((@(bytes[2]))^)) and $ffff;
                result := value * 12 + 8;
            end;
            $ff: begin
                case byte3 of
                $30..$31: begin
                    result := 5;
                end;
                $32, $ff: begin
                    result := 4;
                end;
                else
                    result := 3;
                end;
            end;
            else
                result := 0;
            end;
        end;
        else
            result := 0;
        end;
    end;

    function TDisassemblerForm.getInstructionsCount(address, size: int): int;
    var
        len: int;
    begin
        result := 0;
        while size > 0 do begin
            if length(getDebugInfo(address, functionsInfo)) > 0 then begin
                inc(result);
            end;
            if length(getDebugInfo(address, allDebugInfo)) > 0 then begin
                inc(result);
            end;
            len := getInstructionLength(address);
            inc(address, len);
            dec(size, len);
            inc(result);
        end;
    end;

    function TDisassemblerForm.getGUIElementFor(sender: TObject): GUIElement;
    begin
        if sender = disassembler then begin
            result := guiDisassembler;
        end else
        if (sender = status) or (sender = statusPages) or (sender = statusScrollbar) then begin
            result := guiStatus[statusPages.activePageIndex];
        end else
        if (sender = context) or (sender = contextScrollbar) then begin
            result := guiContext;
        end else
        if (sender = stack) or (sender = stackPages) or (sender = stackScrollbar) then begin
            result := guiStack[stackPages.activePageIndex];
        end else
        if ((sender = debugInspector) or (sender = debugInspectorPages) or (sender = debugInspectorScrollbar)) and (guiDebugInspectorCount > 0) then begin
            result := guiDebugInspector[debugInspectorPages.activePageIndex];
        end else begin
            result := nil;
        end;
    end;

    function TDisassemblerForm.hasBreakpointAt(address: int): boolean;
    var
        i: int;
    begin
        result := false;
        for i := 0 to breakpointsCount - 1 do begin
            if breakpoints[i] = address then begin
                result := true;
                break;
            end;
        end;
    end;

    procedure TDisassemblerForm.updateScreen();
    var
        c: MalikDebugContext;
    begin
        if (maxVisibleAddress = 0) or (currentContext.regEIP < disassemblerPosition) or (currentContext.regEIP >= maxVisibleAddress) then begin
            disassemblerPosition := currentContext.regEIP;
            if disassemblerPosition < 0 then begin
                disassemblerPosition := MAX_INT;
            end;
        end;
        paintGUIElement(guiDisassembler);
        paintGUIElement(guiStatus[statusPages.activePageIndex]);
        paintGUIElement(guiContext);
        paintGUIElement(guiStack[stackPages.activePageIndex]);
        if debugInspectorPanel.visible then begin
            paintGUIElement(guiDebugInspector[debugInspectorPages.activePageIndex]);
        end;
        if stackPages.activePageIndex = 0 then begin
            c := currentContext;
            stackScrollbar.position := ((c.regESP - c.stackBegin) div sizeof(StackItem)) - 8;
        end;
    end;

    procedure TDisassemblerForm.showContext(id: int);
    var
        i: int;
    begin
        i := contextID;
        threadList.items.strings[i] := 'Поток ' + toDecString(i);
        threadList.items.strings[id] := 'Поток ' + toDecString(id) + ' (текущий)';
        threadList.itemIndex := id;
        processor.getContext(id, currentContext);
        contextID := id;
        updateScreen();
    end;

    procedure TDisassemblerForm.addDebugInspectorElement(const tabCaption, dataType: AnsiString; address: int);
    var
        d: DebugInspectorHistory_Array1d;
        c: int;
    begin
        d := guiDebugInspector;
        c := guiDebugInspectorCount;
        if c = length(d) then begin
            d := DebugInspectorHistory_Array1d_create(c + 16);
            arraycopy(guiDebugInspector, 0, d, 0, c);
            guiDebugInspector := d;
        end;
        d[c] := DebugInspectorHistory.create(self, debugInspector, debugInspectorScrollbar, dataType, address);
        guiDebugInspectorCount := c + 1;
        debugInspectorButton.enabled := true;
        debugInspectorPanel.visible := true;
        with debugInspectorPages.addTabSheet() do begin
            caption := tabCaption;
            show();
        end;
        debugInspector.repaint();
    end;

    procedure TDisassemblerForm.paintGUIElement(element: GUIElement);
    var
        w: int;
        h: int;
        box: TPaintBox;
        scroll: TScrollBar;
    begin
        if element = nil then begin
            exit;
        end;
        box := element.box;
        scroll := element.scroll;
        if scroll <> nil then begin
            scroll.onChange := nil;
            try
                setupScrollbar(scroll, element.getLinesCount(), element.position);
            finally
                scroll.onChange := formGUIElementScroll;
            end;
        end;
        w := box.clientWidth;
        h := box.clientHeight;
        with image do begin
            setSize(w, h);
            canvas.brush.style := bsSolid;
            canvas.brush.color := clBtnFace;
            canvas.fillRect(0, 0, w, h);
            if scroll <> nil then begin
                element.paint(canvas, w, h, scroll.position);
            end else begin
                element.paint(canvas, w, h, disassemblerPosition);
            end;
        end;
        box.parent.update();
        box.canvas.clipRect := rect(0, 0, w, h);
        box.canvas.draw(0, 0, image);
    end;

    procedure TDisassemblerForm.destroyGUI(const arr: GUIElement_Array1d);
    var
        i: int;
    begin
        for i := 0 to length(arr) - 1 do begin
            arr[i].free();
        end;
    end;

    procedure TDisassemblerForm.destroyGUI(const arr: DebugInspectorHistory_Array1d);
    var
        i: int;
    begin
        for i := 0 to length(arr) - 1 do begin
            arr[i].free();
        end;
    end;

    procedure TDisassemblerForm.destroyInfo(const arr: DebugInfo_Array1d);
    var
        i: int;
    begin
        for i := 0 to length(arr) - 1 do begin
            arr[i].free();
        end;
    end;

    procedure TDisassemblerForm.destroyInfo(const arr: ClassDebugInfo_Array1d);
    var
        i: int;
    begin
        for i := 0 to length(arr) - 1 do begin
            arr[i].free();
        end;
    end;

    procedure TDisassemblerForm.loadDataFrom(strs: TStrings);
    var
        info: AnsiString;
        s: AnsiString;
        i: int;
        j: int;
        k: int;
        classCount: int;
        varCount: int;
        funcCount: int;
        debugCount: int;
        address: int;
        clinfo: ClassDebugInfo;
    begin
        classCount := 0;
        varCount := 0;
        funcCount := 0;
        debugCount := 0;
        for i := 0 to strs.count - 1 do begin
            s := strs.strings[i];
            j := pos('=', s);
            if (length(s) = 0) or (s[1] in ['+', '-']) or (j < 2) then begin
                continue;
            end;
            info := trim(copy(s, j + 1, length(s) - j));
            if pos(PREFIX_CLASS, info) = 1 then begin
                inc(classCount);
            end else
            if pos(PREFIX_VARIABLE, info) = 1 then begin
                inc(varCount);
            end else
            if pos(PREFIX_FUNCTION, info) = 1 then begin
                inc(funcCount);
            end else begin
                inc(debugCount);
            end;
        end;
        classesInfo := ClassDebugInfo_Array1d_create(classCount);
        variablesInfo := DebugInfo_Array1d_create(varCount);
        functionsInfo := DebugInfo_Array1d_create(funcCount);
        allDebugInfo := DebugInfo_Array1d_create(debugCount);
        classCount := 0;
        varCount := 0;
        funcCount := 0;
        debugCount := 0;
        for i := 0 to strs.count - 1 do begin
            s := strs.strings[i];
            j := pos('=', s);
            if (length(s) = 0) or (s[1] in ['+', '-']) or (j < 2) then begin
                continue;
            end;
            address := parseHexInt(copy(s, 1, j - 1), -1);
            info := trim(copy(s, j + 1, length(s) - j));
            if pos(PREFIX_CLASS, info) = 1 then begin
                info := trim(copy(info, length(PREFIX_CLASS) + 1, length(s) - length(PREFIX_CLASS)));
                j := pos(':', info);
                if j = 0 then begin
                    j := length(info) + 1;
                end;
                classesInfo[classCount] := ClassDebugInfo.create(address, trim(copy(info, 1, j - 1)));
                inc(classCount);
            end else
            if pos(PREFIX_VARIABLE, info) = 1 then begin
                info := trim(copy(info, length(PREFIX_VARIABLE) + 1, length(s) - length(PREFIX_VARIABLE)));
                variablesInfo[varCount] := DebugInfo.create(address, info);
                inc(varCount);
            end else
            if pos(PREFIX_FUNCTION, info) = 1 then begin
                info := trim(copy(info, length(PREFIX_FUNCTION) + 1, length(s) - length(PREFIX_FUNCTION)));
                functionsInfo[funcCount] := DebugInfo.create(address, info);
                inc(funcCount);
            end else begin
                info := trim(info);
                allDebugInfo[debugCount] := DebugInfo.create(address, info);
                inc(debugCount);
            end;
        end;
        classCount := 0;
        for i := 0 to strs.count - 1 do begin
            s := strs.strings[i];
            j := pos('=', s);
            if (length(s) > 0) and (s[1] in ['+', '-']) and (j > 1) then begin
                address := parseHexInt(trim(copy(s, 2, j - 2)), 0);
                if s[1] = '-' then begin
                    address := -address;
                end;
                info := trim(copy(s, j + 1, length(s) - j));
                j := pos('.', info);
                if j = 0 then begin
                    continue;
                end;
                clinfo := findClassInfo(SIGNATURE_OBJECT + trim(copy(info, 1, j - 1)) + SIGNATURE_SUFFIX);
                info := trim(copy(info, j + 1, length(info) - j));
                if (clinfo <> nil) and (pos(':', info) > 0) then begin
                    clinfo.addField(address, info);
                end;
                continue;
            end;
            if (length(s) = 0) or (s[1] in ['+', '-']) or (j < 2) then begin
                continue;
            end;
            info := copy(s, j + 1, length(s) - j);
            if pos(PREFIX_CLASS, info) = 1 then begin
                info := trim(copy(info, length(PREFIX_CLASS) + 1, length(s) - length(PREFIX_CLASS)));
                k := pos(':', info);
                if k = 0 then begin
                    continue;
                end;
                clinfo := getClassInfo(parseHexInt(copy(s, 1, j - 1), -1), classesInfo);
                if clinfo <> nil then begin
                    clinfo.setParent(findClassInfo(trim(copy(info, k + 1, length(info) - k))));
                end;
            end;
        end;
    end;

    procedure TDisassemblerForm.setupScrollbar(scrollbar: TScrollBar; maxval, position: int);
    begin
        scrollbar.max := max(0, maxval - scrollbar.largeChange);
        scrollbar.position := position;
        scrollbar.enabled := true;
        scrollbar.enabled := scrollbar.max > 0;
        scrollbar.repaint();
    end;

    procedure TDisassemblerForm.setupScrollbar(scrollbar: TScrollBar; page: int);
    var
        element: GUIElement;
    begin
        element := getGUIElementFor(scrollbar);
        if element <> nil then begin
            scrollbar.max := max(0, element.getLinesCount() - page);
        end;
        scrollbar.largeChange := page;
        scrollbar.enabled := true;
        scrollbar.enabled := scrollbar.max > 0;
        scrollbar.repaint();
    end;

    procedure TDisassemblerForm.switchFullScreenMode();
    begin
        if windowState = wsNormal then begin
            windowState := wsMaximized;
        end else begin
            windowState := wsNormal;
        end;
    end;

    procedure TDisassemblerForm.afterConstruction();
    begin
        inherited afterConstruction();
        statusPages.onKeyDown := formKeyDown;
        statusPages.onKeyUp := formKeyUp;
        stackPages.onKeyDown := formKeyDown;
        stackPages.onKeyUp := formKeyUp;
        debugInspectorPages.onKeyDown := formKeyDown;
        debugInspectorPages.onKeyUp := formKeyUp;
        image.canvas.font := disassembler.font;
        guiDisassembler := GUIElementDisassembler.create(self, disassembler, nil);
        guiStatus := toGUIElementArray1d([
            GUIElementGlobalVariables.create(self, status, statusScrollbar),
            GUIElementAllClasses.create(self, status, statusScrollbar),
            GUIElementAllFunctions.create(self, status, statusScrollbar),
            GUIElementExceptions.create(self, status, statusScrollbar),
            GUIElementBreakpoints.create(self, status, statusScrollbar)
        ]);
        guiContext := GUIElementContext.create(self, context, contextScrollbar);
        guiStack := toGUIElementArray1d([
            GUIElementStack.create(self, stack, stackScrollbar),
            GUIElementCallStack.create(self, stack, stackScrollbar)
        ]);
    end;

    procedure TDisassemblerForm.loadData();
    var
        strs: TStrings;
        info: ProgrammeInfo;
        fs: FileInputStream;
        ss: TStream;
        i: int;
    begin
        with threadList, items do begin
            clear();
            for i := 0 to processor.MAX_CONTEXTS - 1 do begin
                add('Поток ' + toDecString(i));
            end;
            itemIndex := 0;
        end;
        strs := TStringList.create();
        try
            info := ProgrammeInfo.create(owner.getProgrammeDirectory());
            try
                fs := FileInputStream.create(info.getDebugInfoFileName());
                try
                    if fs.isInvalidHandle() then begin
                        exit;
                    end;
                    ss := THandleStream.create(fs.getHandle());
                    try
                        strs.loadFromStream(ss);
                        loadDataFrom(strs);
                    finally
                        ss.free();
                    end;
                finally
                    fs.free();
                end;
            finally
                info.free();
            end;
        finally
            strs.free();
        end;
    end;

    procedure TDisassemblerForm.showContents(contextID: int);
    begin
        stepInto := false;
        stepOver := 0;
        showContext(contextID);
    end;

    function TDisassemblerForm.hasBreakpoints(): boolean;
    begin
        result := breakpointsCount > 0;
    end;

    function TDisassemblerForm.mustBreak(contextID, address: int): boolean;
    begin
        result := ((self.contextID = contextID) and (stepInto or ((stepOver <> 0) and (address = stepOver)))) or hasBreakpointAt(address);
    end;
{%endregion}

{%region GUIElementDisassembler }
    constructor GUIElementDisassembler.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
    begin
        inherited create(TDisassemblerForm_self, box, scroll);
    end;

    procedure GUIElementDisassembler.paint(canvas: TCanvas; width, height, scrollPosition: int);
    var
        f: TDisassemblerForm;
        c1: int;
        c2: int;
        len: int;
        eip: int;
        ih: int;
        a: int;
        h: int;
        i: int;
        j: int;
        l: int;
        s: AnsiString;
        b: AnsiString;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        h := abs(canvas.font.height);
        if h = 0 then begin
            exit;
        end;
        c2 := canvas.textWidth('A');
        c1 := c2 * 9 + 32;
        c2 := c2 * 23 + c1;
        eip := f.currentContext.regEIP;
        ih := -1;
        a := scrollPosition;
        i := 0;
        j := 0;
        len := height div h;
        f.addresses := int_Array1d_create(len + 1);
        while i < height do begin
            canvas.brush.style := bsClear;
            s := f.getDebugInfo(a, f.functionsInfo);
            if length(s) > 0 then begin
                s := TDisassemblerForm.getFunctionRepresentation(s);
                canvas.font.color := f.colorFunctionName;
                canvas.font.style := [fsBold];
                canvas.textOut(0, i, s);
                if j < length(f.addresses) then begin
                    f.addresses[j] := a;
                end;
                inc(i, h);
                inc(j);
            end;
            s := f.getDebugInfo(a, f.allDebugInfo);
            if length(s) > 0 then begin
                canvas.font.color := f.colorSourceLine;
                canvas.font.style := [fsBold];
                canvas.textOut(16, i, s);
                if j < length(f.addresses) then begin
                    f.addresses[j] := a;
                end;
                inc(i, h);
                inc(j);
            end;
            if a = f.currentContext.regEIP then begin
                ih := i;
            end;
            s := f.getInstructionRepresentation(a);
            b := f.getInstructionByteCode(a);
            l := f.getInstructionLength(a);
            canvas.font.color := f.colorNormalData;
            canvas.font.style := [];
            canvas.textOut(32, i, toHexString(zeroExtend(a), 8));
            canvas.textOut(c1, i, b);
            canvas.TextOut(c2, i, s);
            if f.hasBreakpointAt(a) then begin
                f.arrows.draw(canvas, 0, i + ((h - f.arrows.height) div 2) + 1, 3);
            end;
            if j < length(f.addresses) then begin
                f.addresses[j] := a;
            end;
            if (a < eip) and (a + l > eip) then begin
                a := eip;
            end else begin
                inc(a, l);
            end;
            inc(i, h);
            inc(j);
        end;
        f.maxVisibleAddress := f.addresses[len];
        if ih >= 0 then begin
            canvas.pen.style := psSolid;
            canvas.pen.width := 1;
            canvas.pen.color := $78c030;
            canvas.moveTo(32, ih + 1);
            canvas.lineTo(width, ih + 1);
            canvas.moveTo(32, ih + h + 1);
            canvas.lineTo(width, ih + h + 1);
            f.arrows.draw(canvas, 32 - f.arrows.width, ih + ((h - f.arrows.height) div 2) + 1, 1);
        end;
    end;

    procedure GUIElementDisassembler.pointerPressed(x, y: int; auxButton: boolean);
    var
        f: TDisassemblerForm;
        e: GUIElement;
        h: int;
        i: int;
        len: int;
        line: int;
        index: int;
        address: int;
        b: int_Array1d;
    begin
        f := TDisassemblerForm_self;
        if (not f.owner.isNowDebugging()) or auxButton or (x >= 32) then begin
            exit;
        end;
        h := abs(f.image.canvas.font.height);
        if h = 0 then begin
            exit;
        end;
        line := y div h;
        if line >= length(f.addresses) then begin
            exit;
        end;
        address := f.addresses[line];
        index := -1;
        len := f.breakpointsCount;
        for i := 0 to len - 1 do begin
            if f.breakpoints[i] = address then begin
                index := i;
                break;
            end;
        end;
        if index >= 0 then begin
            for i := index to len - 2 do begin
                f.breakpoints[i] := f.breakpoints[i + 1];
            end;
            f.breakpointsCount := len - 1;
        end else begin
            if length(f.breakpoints) = len then begin
                b := int_Array1d_create(len + 16);
                arraycopy(f.breakpoints, 0, b, 0, len);
                f.breakpoints := b;
            end;
            f.breakpoints[len] := address;
            f.breakpointsCount := len + 1;
        end;
        e := f.guiStatus[f.statusPages.activePageIndex];
        repaint();
        if e is GUIElementBreakpoints then begin
            e.repaint();
        end;
    end;
{%endregion}

{%region GUIElementGlobalVariables }
    constructor GUIElementGlobalVariables.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
    begin
        inherited create(TDisassemblerForm_self, box, scroll);
    end;

    procedure GUIElementGlobalVariables.paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean);
    var
        f: TDisassemblerForm;
        info: DebugInfo;
        global: UnicodeString;
        value: AnsiString;
        varInfo: AnsiString;
        typeInfo: AnsiString;
        w: int;
        colonPos: int;
    begin
        f := TDisassemblerForm_self;
        info := f.variablesInfo[lineIndex];
        varInfo := info.getInfo();
        colonPos := pos(':', varInfo);
        if colonPos > 0 then begin
            typeInfo := trim(copy(varInfo, colonPos + 1, length(varInfo) - colonPos));
            value := f.getValueRepresentation(info.getAddress(), typeInfo);
        end else begin
            typeInfo := '';
            value := 'N/T';
        end;
        canvas.brush.style := bsClear;
        canvas.font.style := [];
        if highlighted then begin
            canvas.font.color := clHighlightText;
        end else begin
            if (length(typeInfo) > 0) and (typeInfo[1] in [SIGNATURE_OBJECT, SIGNATURE_CLASS, SIGNATURE_ARRAY]) then begin
                canvas.font.color := f.colorObjectData;
            end else begin
                canvas.font.color := f.colorNormalData;
            end;
        end;
        global := toUTF16String(TDisassemblerForm.getVariableRepresentation(varInfo));
        w := width - canvas.textWidth(value) - 32;
        while (length(global) > 2) and (canvas.textWidth(toUTF8String(global)) > w) do begin
            global := copy(global, 1, length(global) - 2) + #$2026;
            { $2026 – код символа многоточия («…») }
        end;
        canvas.textOut(left + 32, top, toUTF8String(global));
        canvas.textOut(left + w + 32, top, value);
    end;

    procedure GUIElementGlobalVariables.paint(canvas: TCanvas; width, height, scrollPosition: int);
    begin
        if TDisassemblerForm_self.owner.isNowDebugging() then begin
            inherited paint(canvas, width, height, scrollPosition);
        end;
    end;

    procedure GUIElementGlobalVariables.lineClicked(lineIndex: int; auxButton: boolean);
    var
        f: TDisassemblerForm;
        info: DebugInfo;
        varInfo: AnsiString;
        typeInfo: AnsiString;
        colonPos: int;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        info := f.variablesInfo[lineIndex];
        varInfo := info.getInfo();
        colonPos := pos(':', varInfo);
        if colonPos > 0 then begin
            typeInfo := trim(copy(varInfo, colonPos + 1, length(varInfo) - colonPos));
        end else begin
            typeInfo := '';
        end;
        f.addDebugInspectorElement(TDisassemblerForm.getVariableRepresentation(varInfo), typeInfo, info.getAddress());
    end;

    function GUIElementGlobalVariables.getLinesCount(): int;
    begin
        result := length(TDisassemblerForm_self.variablesInfo);
    end;
{%endregion}

{%region GUIElementAllClasses }
    constructor GUIElementAllClasses.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
    begin
        inherited create(TDisassemblerForm_self, box, scroll);
    end;

    procedure GUIElementAllClasses.paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean);
    var
        f: TDisassemblerForm;
        clinfo: ClassDebugInfo;
        parent: ClassDebugInfo;
        r: Representer;
    begin
        canvas.brush.style := bsClear;
        canvas.font.style := [];
        f := TDisassemblerForm_self;
        if highlighted then begin
            canvas.font.color := clHighlightText;
        end else begin
            canvas.font.color := f.colorObjectData;
        end;
        clinfo := f.classesInfo[lineIndex];
        parent := clinfo.getParent();
        r := TDisassemblerForm.DATA_REPRESENTER;
        if parent <> nil then begin
            canvas.textOut(left + 32, top, r.getTypeRepresentation(clinfo.getInfo()) + ' : ' + r.getTypeRepresentation(parent.getInfo()));
        end else begin
            canvas.textOut(left + 32, top, r.getTypeRepresentation(clinfo.getInfo()));
        end;
    end;

    procedure GUIElementAllClasses.lineClicked(lineIndex: int; auxButton: boolean);
    var
        f: TDisassemblerForm;
        clinfo: ClassDebugInfo;
        c: ClassDebugInfo_Array1d;
        r: Representer;
        i: int;
        len: int;
        size: int;
        intArray: PIntArray;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        if classesRegion = 0 then begin
            c := f.classesInfo;
            len := length(c);
            if len > 0 then begin
                size := len * sizeof(int);
                classesRegion := f.processor.createRegion(size);
                intArray := PIntArray(f.processor.getMemory(classesRegion, size));
                for i := 0 to len - 1 do begin
                    intArray[i] := c[i].getAddress();
                end;
            end;
        end;
        clinfo := f.classesInfo[lineIndex];
        r := TDisassemblerForm.DATA_REPRESENTER;
        f.addDebugInspectorElement(r.getTypeRepresentation(clinfo.getInfo()), SIGNATURE_OBJECT, classesRegion + lineIndex * sizeof(int));
    end;

    function GUIElementAllClasses.getLinesCount(): int;
    begin
        result := length(TDisassemblerForm_self.classesInfo);
    end;
{%endregion}

{%region GUIElementAllFunctions }
    constructor GUIElementAllFunctions.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
    begin
        inherited create(TDisassemblerForm_self, box, scroll);
    end;

    procedure GUIElementAllFunctions.paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean);
    var
        f: TDisassemblerForm;
        funcInfo: DebugInfo;
    begin
        canvas.brush.style := bsClear;
        canvas.font.style := [];
        f := TDisassemblerForm_self;
        if highlighted then begin
            canvas.font.color := clHighlightText;
        end else begin
            canvas.font.color := f.colorFunctionName;
        end;
        funcInfo := f.functionsInfo[lineIndex];
        canvas.textOut(left, top, TDisassemblerForm.getFunctionRepresentation(funcInfo.getInfo()));
    end;

    procedure GUIElementAllFunctions.lineClicked(lineIndex: int; auxButton: boolean);
    var
        f: TDisassemblerForm;
        funcInfo: DebugInfo;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        funcInfo := f.functionsInfo[lineIndex];
        f.disassemblerPosition := funcInfo.getAddress();
        f.paintGUIElement(f.guiDisassembler);
    end;

    function GUIElementAllFunctions.getLinesCount(): int;
    begin
        result := length(TDisassemblerForm_self.functionsInfo);
    end;
{%endregion}

{%region GUIElementExceptions }
    constructor GUIElementExceptions.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
    begin
        inherited create(TDisassemblerForm_self, box, scroll);
    end;

    procedure GUIElementExceptions.paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean);
    var
        f: TDisassemblerForm;
        intArray: PIntArray;
        intnum: int;
        s: AnsiString;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        canvas.brush.style := bsClear;
        canvas.font.style := [];
        intnum := (lineIndex shr 1) and $ff;
        intArray := PIntArray(f.processor.getMemory(MalikProcessor.MEMORY_START + (lineIndex * sizeof(int)), sizeof(int)));
        if intArray = nil then begin
            exit;
        end;
        if (lineIndex and 1) = 0 then begin
            if highlighted then begin
                canvas.font.color := clHighlightText;
            end else begin
                canvas.font.color := f.colorNormalData;
            end;
            s := HEX_DIGITS[(intnum shr $4) + 1] + HEX_DIGITS[(intnum and $f) + 1];
            canvas.textOut(left, top, s);
            if highlighted then begin
                canvas.font.color := clHighlightText;
            end else begin
                canvas.font.color := f.colorFunctionName;
            end;
            s := f.getAddressRepresentation(intArray[0]);
            canvas.textOut(left + 32, top, s);
        end else begin
            if highlighted then begin
                canvas.font.color := clHighlightText;
            end else begin
                canvas.font.color := f.colorObjectData;
            end;
            s := TDisassemblerForm.DATA_REPRESENTER.getObjectRepresentation(intArray[0]);
            canvas.textOut(left + 32, top, s);
        end;
    end;

    procedure GUIElementExceptions.paint(canvas: TCanvas; width, height, scrollPosition: int);
    begin
        if TDisassemblerForm_self.owner.isNowDebugging() then begin
            inherited paint(canvas, width, height, scrollPosition);
        end;
    end;

    procedure GUIElementExceptions.lineClicked(lineIndex: int; auxButton: boolean);
    var
        f: TDisassemblerForm;
        intArray: PIntArray;
        intnum: int;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        intnum := lineIndex shr 1;
        if (lineIndex and 1) = 0 then begin
            intArray := PIntArray(f.processor.getMemory(MalikProcessor.MEMORY_START + (intnum shl 3), sizeof(int)));
            if intArray = nil then begin
                exit;
            end;
            f.disassemblerPosition := intArray[0];
            f.guiDisassembler.repaint();
        end else begin
            f.addDebugInspectorElement('Обработчик прерывания ' + TDisassemblerForm.DATA_REPRESENTER.getHexRepresentation(intnum), SIGNATURE_OBJECT, MalikProcessor.MEMORY_START + (intnum shl 3) + sizeof(int));
        end;
    end;

    function GUIElementExceptions.getLinesCount(): int;
    begin
        result := $200;
    end;
{%endregion}

{%region GUIElementBreakpoints }
    constructor GUIElementBreakpoints.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
    begin
        inherited create(TDisassemblerForm_self, box, scroll);
    end;

    procedure GUIElementBreakpoints.paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean);
    var
        f: TDisassemblerForm;
    begin
        canvas.brush.style := bsClear;
        canvas.font.style := [];
        f := TDisassemblerForm_self;
        if highlighted then begin
            canvas.font.color := clHighlightText;
        end else begin
            canvas.font.color := f.colorNormalData;
        end;
        canvas.textOut(left + 32, top, f.getAddressRepresentation(f.breakpoints[lineIndex]));
    end;

    procedure GUIElementBreakpoints.lineClicked(lineIndex: int; auxButton: boolean);
    var
        f: TDisassemblerForm;
        b: int_Array1d;
        i: int;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        b := f.breakpoints;
        if auxButton then begin
            for i := lineIndex to f.breakpointsCount - 2 do begin
                b[i] := b[i + 1];
            end;
            dec(f.breakpointsCount);
            repaint();
        end else begin
            f.disassemblerPosition := b[lineIndex];
        end;
        f.guiDisassembler.repaint();
    end;

    function GUIElementBreakpoints.getLinesCount(): int;
    begin
        result := TDisassemblerForm_self.breakpointsCount;
    end;
{%endregion}

{%region GUIElementContext }
    constructor GUIElementContext.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
    begin
        inherited create(TDisassemblerForm_self, box, scroll);
    end;

    procedure GUIElementContext.paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean);
    var
        f: TDisassemblerForm;
        r: Representer;
        ctx: MalikDebugContext;
        s: AnsiString;
    begin
        canvas.brush.style := bsClear;
        canvas.font.style := [];
        f := TDisassemblerForm_self;
        if highlighted then begin
            canvas.font.color := clHighlightText;
        end else begin
            canvas.font.color := f.colorNormalData;
        end;
        r := TDisassemblerForm.DATA_REPRESENTER;
        ctx := f.currentContext;
        case lineIndex of
         0: begin
            s := 'EIP ' + r.getHexRepresentation(ctx.regEIP);
        end;
         1: begin
            s := 'ESP ' + r.getHexRepresentation(ctx.regESP);
        end;
         2: begin
            s := 'EBP ' + r.getHexRepresentation(ctx.regEBP);
        end;
         3: begin
            s := 'S.LIM ' + r.getHexRepresentation(ctx.stackBegin);
        end;
         4: begin
            s := 'S.BOT ' + r.getHexRepresentation(ctx.stackEnd);
        end;
         5: begin
            s := 'IF ' + toDecString(ctx.regIF);
        end;
         6: begin
            case ctx.state of
            CONTEXT_STATE_NOEXECUTE:
                s := 'STATE NO EXECUTE';
            CONTEXT_STATE_ACTIVE:
                s := 'STATE ACTIVE';
            CONTEXT_STATE_PAUSED:
                s := 'STATE PAUSED';
            CONTEXT_STATE_WAITING:
                s := 'STATE WAITING';
            else
                s := '';
            end;
        end;
        7: begin
            s := 'L.CNT ' + toDecString(ctx.lockedCount);
        end;
        8: begin
            s := 'N.ID ' + toDecString(ctx.nextID);
        end;
        else
            s := '';
        end;
        canvas.textOut(left + 32, top, s);
    end;

    procedure GUIElementContext.paint(canvas: TCanvas; width, height, scrollPosition: int);
    begin
        if TDisassemblerForm_self.owner.isNowDebugging() then begin
            inherited paint(canvas, width, height, scrollPosition);
        end;
    end;

    procedure GUIElementContext.lineClicked(lineIndex: int; auxButton: boolean);
    var
        f: TDisassemblerForm;
        ctx: MalikDebugContext;
        funcInfo: DebugInfo;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        ctx := f.currentContext;
        case lineIndex of
         0: begin
            if auxButton then begin
                funcInfo := f.getFunctionAt(ctx.regEIP);
                if funcInfo <> nil then begin
                    f.disassemblerPosition := funcInfo.getAddress();
                end else begin
                    f.disassemblerPosition := ctx.regEIP;
                end;
            end else begin
                f.disassemblerPosition := ctx.regEIP;
            end;
            f.guiDisassembler.repaint();
        end;
         1.. 2: begin
            f.stackPages.pages[0].show();
            f.stackScrollbar.position := ((ctx.regESP - ctx.stackBegin) div sizeof(StackItem)) - 8;
            f.guiStack[0].repaint();
        end;
        end;
    end;

    function GUIElementContext.getLinesCount(): int;
    begin
        result := 9;
    end;
{%endregion}

{%region GUIElementStack }
    constructor GUIElementStack.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
    begin
        inherited create(TDisassemblerForm_self, box, scroll);
    end;

    procedure GUIElementStack.paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean);
    var
        f: TDisassemblerForm;
        item: PStackItem;
        r: Representer;
        w: int;
        valueColor: int;
        typeColor: int;
        valueRepresentation: AnsiString;
        typeRepresentation: AnsiString;
        s: AnsiString;
    begin
        f := TDisassemblerForm_self;
        r := TDisassemblerForm.DATA_REPRESENTER;
        item := PStackItem(f.processor.getMemory(f.currentContext.stackBegin + lineIndex * sizeof(StackItem), sizeof(StackItem)));
        if item = nil then begin
            exit;
        end;
        canvas.brush.style := bsClear;
        canvas.font.style := [];
        if highlighted then begin
            valueColor := clHighlightText;
            typeColor := clHighlightText;
        end else begin
            case item.itemType of
            STACKITEM_FLOAT, STACKITEM_DOUBLE, STACKITEM_REAL,
            STACKITEM_INT, STACKITEM_LONG: begin
                valueColor := f.colorNormalData;
                typeColor := f.colorNormalType;
            end;
            STACKITEM_OBJECT: begin
                valueColor := f.colorObjectData;
                typeColor := f.colorObjectType;
            end;
            STACKITEM_OVERFLOW: begin
                valueColor := f.colorObjectData;
                typeColor := f.colorOverflowType;
            end;
            STACKITEM_EXCEPT: begin
                valueColor := f.colorExceptData;
                typeColor := f.colorExceptType;
            end;
            STACKITEM_OLDEBP: begin
                valueColor := f.colorOldEBPData;
                typeColor := f.colorOldEBPType;
            end;
            STACKITEM_RETURN: begin
                valueColor := f.colorReturnData;
                typeColor := f.colorReturnType;
            end;
            STACKITEM_IRETURN: begin
                valueColor := f.colorIReturnData;
                typeColor := f.colorIReturnType;
            end;
            else
                valueColor := f.colorEmptyData;
                typeColor := f.colorEmptyData;
            end;
        end;
        case item.itemType of
        STACKITEM_INT: begin
            valueRepresentation := r.getLongRepresentation(item.valueInt, DATA_TYPE_INT);
            typeRepresentation := 'int';
        end;
        STACKITEM_LONG: begin
            valueRepresentation := r.getLongRepresentation(item.valueLong, DATA_TYPE_LONG);
            typeRepresentation := 'long';
        end;
        STACKITEM_FLOAT: begin
            valueRepresentation := r.getFloatRepresentation(item.valueFloat);
            typeRepresentation := 'float';
        end;
        STACKITEM_DOUBLE: begin
            valueRepresentation := r.getDoubleRepresentation(item.valueDouble);
            typeRepresentation := 'double';
        end;
        STACKITEM_REAL: begin
            valueRepresentation := r.getRealRepresentation(item.valueReal, item.valueRealFrom);
            typeRepresentation := 'real';
        end;
        STACKITEM_OBJECT: begin
            valueRepresentation := r.getObjectRepresentation(item.valueObject);
            typeRepresentation := 'object';
        end;
        STACKITEM_OVERFLOW: begin
            valueRepresentation := r.getObjectRepresentation(item.valueObject);
            typeRepresentation := 'overflow';
        end;
        STACKITEM_EXCEPT: begin
            valueRepresentation := 'EIP=' + r.getHexRepresentation(item.handlerEIP) + ' EBP=' + r.getHexRepresentation(item.handlerEBP) + ' IF=' + toDecString(item.handlerIF);
            typeRepresentation := 'except';
        end;
        STACKITEM_OLDEBP: begin
            valueRepresentation := r.getHexRepresentation(item.oldEBP);
            typeRepresentation := 'oldebp';
        end;
        STACKITEM_RETURN: begin
            valueRepresentation := r.getHexRepresentation(item.returnEIP);
            typeRepresentation := 'return';
        end;
        STACKITEM_IRETURN: begin
            valueRepresentation := r.getHexRepresentation(item.returnEIP) + ' IF=' + toDecString(byte(item.ireturnIF));
            typeRepresentation := 'ireturn';
        end;
        else
            valueRepresentation := r.getUntypedValueRepresentation();
            typeRepresentation := '';
        end;
        s := toUTF16String(valueRepresentation);
        w := width - canvas.textWidth(typeRepresentation) - 32;
        while (length(s) > 2) and (canvas.textWidth(toUTF8String(s)) > w) do begin
            s := copy(s, 1, length(s) - 2) + #$2026;
            { $2026 – код символа многоточия («…») }
        end;
        valueRepresentation := toUTF8String(s);
        canvas.font.color := valueColor;
        canvas.textOut(left + 32, top, valueRepresentation);
        canvas.font.color := typeColor;
        canvas.textOut(left + w + 32, top, typeRepresentation);
    end;

    procedure GUIElementStack.paint(canvas: TCanvas; width, height, scrollPosition: int);
    var
        f: TDisassemblerForm;
        ctx: MalikDebugContext;
        h: int;
        ih: int;
        lastIndex: int;
        espIndex: int;
        ebpIndex: int;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        inherited paint(canvas, width, height, scrollPosition);
        h := abs(canvas.font.height);
        if h = 0 then begin
            exit;
        end;
        ctx := f.currentContext;
        lastIndex := scrollPosition + (height div h);
        if (height mod h) > 0 then begin
            inc(lastIndex);
        end;
        espIndex := (ctx.regESP - ctx.stackBegin) div sizeof(StackItem);
        ebpIndex := (ctx.regEBP - ctx.stackBegin) div sizeof(StackItem);
        if (ebpIndex >= scrollPosition) and (ebpIndex < lastIndex) then begin
            ih := (ebpIndex - scrollPosition) * h;
            canvas.pen.style := psSolid;
            canvas.pen.width := 1;
            canvas.pen.color := $ffa040;
            canvas.moveTo(32, ih + 1);
            canvas.lineTo(width, ih + 1);
            canvas.moveTo(32, ih + h + 1);
            canvas.lineTo(width, ih + h + 1);
            f.arrows.draw(canvas, 32 - f.arrows.width, ih + ((h - f.arrows.height) div 2) + 1, 2);
        end;
        if (espIndex >= scrollPosition) and (espIndex < lastIndex) and (espIndex <> ebpIndex) then begin
            ih := (espIndex - scrollPosition) * h;
            canvas.pen.style := psSolid;
            canvas.pen.width := 1;
            canvas.pen.color := $40a0ff;
            canvas.moveTo(32, ih + 1);
            canvas.lineTo(width, ih + 1);
            canvas.moveTo(32, ih + h + 1);
            canvas.lineTo(width, ih + h + 1);
            f.arrows.draw(canvas, 32 - f.arrows.width, ih + ((h - f.arrows.height) div 2) + 1, 0);
        end;
    end;

    procedure GUIElementStack.lineClicked(lineIndex: int; auxButton: boolean);
    var
        f: TDisassemblerForm;
        r: Representer;
        item: PStackItem;
        funcInfo: DebugInfo;
        address: int;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        r := TDisassemblerForm.DATA_REPRESENTER;
        address := f.currentContext.stackBegin + lineIndex * sizeof(StackItem);
        item := PStackItem(f.processor.getMemory(address, sizeof(StackItem)));
        if item = nil then begin
            exit;
        end;
        case item.itemType of
        STACKITEM_INT: begin
            f.addDebugInspectorElement(r.getHexRepresentation(address), SIGNATURE_INT, address);
        end;
        STACKITEM_LONG: begin
            f.addDebugInspectorElement(r.getHexRepresentation(address), SIGNATURE_LONG, address);
        end;
        STACKITEM_FLOAT: begin
            f.addDebugInspectorElement(r.getHexRepresentation(address), SIGNATURE_FLOAT, address);
        end;
        STACKITEM_DOUBLE: begin
            f.addDebugInspectorElement(r.getHexRepresentation(address), SIGNATURE_DOUBLE, address);
        end;
        STACKITEM_REAL: begin
            f.addDebugInspectorElement(r.getHexRepresentation(address), SIGNATURE_REAL, address);
        end;
        STACKITEM_OBJECT, STACKITEM_OVERFLOW: begin
            f.addDebugInspectorElement(r.getHexRepresentation(address), SIGNATURE_OBJECT, address);
        end;
        STACKITEM_EXCEPT: begin
            if auxButton then begin
                funcInfo := f.getFunctionAt(item.handlerEIP);
                if funcInfo <> nil then begin
                    f.disassemblerPosition := funcInfo.getAddress();
                end else begin
                    f.disassemblerPosition := item.handlerEIP;
                end;
            end else begin
                f.disassemblerPosition := item.handlerEIP;
            end;
            f.guiDisassembler.repaint();
        end;
        STACKITEM_RETURN, STACKITEM_IRETURN: begin
            if auxButton then begin
                funcInfo := f.getFunctionAt(item.returnEIP);
                if funcInfo <> nil then begin
                    f.disassemblerPosition := funcInfo.getAddress();
                end else begin
                    f.disassemblerPosition := item.returnEIP;
                end;
            end else begin
                f.disassemblerPosition := item.returnEIP;
            end;
            f.guiDisassembler.repaint();
        end;
        end;
    end;

    function GUIElementStack.getLinesCount(): int;
    var
        ctx: MalikDebugContext;
    begin
        ctx := TDisassemblerForm_self.currentContext;
        result := (ctx.stackEnd - ctx.stackBegin) div sizeof(StackItem);
    end;
{%endregion}

{%region GUIElementCallStack }
    constructor GUIElementCallStack.create(TDisassemblerForm_self: TDisassemblerForm; box: TPaintBox; scroll: TScrollBar);
    begin
        inherited create(TDisassemblerForm_self, box, scroll);
    end;

    procedure GUIElementCallStack.paintLine(canvas: TCanvas; left, top, width, height, lineIndex: int; highlighted: boolean);
    var
        f: TDisassemblerForm;
        s: AnsiString;
    begin
        canvas.brush.style := bsClear;
        canvas.font.style := [];
        f := TDisassemblerForm_self;
        if highlighted then begin
            canvas.font.color := clHighlightText;
        end else begin
            canvas.font.color := f.colorNormalData;
        end;
        s := f.getAddressRepresentation(addresses[lineIndex]);
        canvas.textOut(left + 32, top, s);
    end;

    procedure GUIElementCallStack.paint(canvas: TCanvas; width, height, scrollPosition: int);
    begin
        if TDisassemblerForm_self.owner.isNowDebugging() then begin
            inherited paint(canvas, width, height, scrollPosition);
        end;
    end;

    procedure GUIElementCallStack.lineClicked(lineIndex: int; auxButton: boolean);
    var
        f: TDisassemblerForm;
        funcInfo: DebugInfo;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            exit;
        end;
        if auxButton then begin
            funcInfo := f.getFunctionAt(addresses[lineIndex]);
            if funcInfo <> nil then begin
                f.disassemblerPosition := funcInfo.getAddress();
            end else begin
                f.disassemblerPosition := addresses[lineIndex];
            end;
        end else begin
            f.disassemblerPosition := addresses[lineIndex];
        end;
        f.guiDisassembler.repaint();
    end;

    function GUIElementCallStack.getLinesCount(): int;
    var
        f: TDisassemblerForm;
        p: MalikProcessor;
        ctx: MalikDebugContext;
        item: PStackItem;
        c: int;
        a: int;
    begin
        f := TDisassemblerForm_self;
        if not f.owner.isNowDebugging() then begin
            result := length(addresses);
            exit;
        end;
        p := f.processor;
        ctx := f.currentContext;
        c := 1;
        a := ctx.regESP;
        while a < ctx.stackEnd do begin
            item := PStackItem(p.getMemory(a, sizeof(StackItem)));
            if (item.itemType = STACKITEM_RETURN) or (item.itemType = STACKITEM_IRETURN) then begin
                inc(c);
            end;
            inc(a, sizeof(StackItem));
        end;
        addresses := int_Array1d_create(c);
        addresses[0] := ctx.regEIP;
        c := 1;
        a := ctx.regESP;
        while a < ctx.stackEnd do begin
            item := PStackItem(p.getMemory(a, sizeof(StackItem)));
            if (item.itemType = STACKITEM_RETURN) or (item.itemType = STACKITEM_IRETURN) then begin
                addresses[c] := item.returnEIP;
                inc(c);
            end;
            inc(a, sizeof(StackItem));
        end;
        result := c;
    end;
{%endregion}

initialization {%region}
    TDisassemblerForm.clinit();
{%endregion}

finalization {%region}
    TDisassemblerForm.cldone();
{%endregion}

end.