{
    EmulMalik реализует эмулирующий движок Малик Эмулятора.
    Этот исходный текст является частью Малик Эмулятора.

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

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

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

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

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

unit EmulMalik;

{$MODE DELPHI}
{$ASMMODE INTEL}

interface

uses
    Lang,
    IOStreams,
    FileIO,
    Zlib;

{%region public }
const
    STACKITEM_INT = int($107900ff);
    STACKITEM_LONG = int($10f900ff);
    STACKITEM_FLOAT = int($7e4100ff);
    STACKITEM_DOUBLE = int($7e8100ff);
    STACKITEM_REAL = int($7ea100ff);
    STACKITEM_OBJECT = int($0b7ec1ff);
    STACKITEM_OVERFLOW = int($0fe700ff);
    STACKITEM_OLDEBP = int($01db00ff);
    STACKITEM_RETURN = int($7e107dff);
    STACKITEM_IRETURN = int($7e107eff);
    STACKITEM_EXCEPT = int($177000ff);
    STACKITEM_EMPTY = int($00000000);

const
    EXCEPTION_DIVIDE_BY_ZERO = int($00);
    EXCEPTION_NULL_POINTER = int($01);
    EXCEPTION_MEMORY_FAULT = int($02);
    EXCEPTION_EXECUTION_FAULT = int($03);
    EXCEPTION_OPERAND_FAULT = int($04);
    EXCEPTION_STACK_FAULT = int($05);
    EXCEPTION_STACK_OVERFLOW = int($06);
    EXCEPTION_UNKNOWN_OPERATION = int($07);
    EXCEPTION_ARRAY_FAULT = int($08);
    EXCEPTION_UNHANDLED = int($09);

const
    CONTEXT_STATE_NOEXECUTE = int(0);
    CONTEXT_STATE_ACTIVE = int(1);
    CONTEXT_STATE_PAUSED = int(2);
    CONTEXT_STATE_WAITING = int(3);

const
    PROCESSOR_LISTENER_GUID = '{74C86B60-AC5B-4E8D-8ACD-29A50B5C1512}';

type
    ProcessorListener = interface;
    MalikDebugContext = class;
    MalikContext = class;
    MalikMemoryRegion = class;
    MalikProcessor = class;

    PDateTimeRecord = ^DateTimeRecord;
    PInterruptRecord = ^InterruptRecord;
    PStackItem = ^StackItem;

    DateTimeRecord = packed record
    case int of
    0: (representation: long);
    1: (millisecond: Word;
        minute: byte;
        hour: byte;
        day: byte;
        month: byte;
        year: Word);
    end;

    InterruptRecord = packed record
        number: int;
        reserved: int;
        parameter: long;
    end;

    StackItem = packed record
    case int of
    STACKITEM_INT: (
        valueInt: int);
    STACKITEM_LONG: (
        valueLong: long);
    STACKITEM_FLOAT: (
        valueFloat: float);
    STACKITEM_DOUBLE: (
        valueDouble: double);
    STACKITEM_REAL: (
        valueReal: real;
        valueRealFrom: byte);
    STACKITEM_OBJECT,
    STACKITEM_OVERFLOW: (
        valueObject: int;
        valueOverflowRetESP: int);
    STACKITEM_EXCEPT: (
        handlerEIP: int;
        handlerEBP: int;
        handlerIF: byte);
    STACKITEM_OLDEBP: (
        oldEBP: int);
    STACKITEM_RETURN,
    STACKITEM_IRETURN: (
        returnEIP: int;
        ireturnReserved: int;
        ireturnIF: int);
    STACKITEM_EMPTY: (
        reserved1: int;
        reserved2: int;
        reserved3: int;
        itemType: int);
    end;

    Pointer_Array1d = packed array of Pointer;
    MalikContext_Array1d = packed array of MalikContext;
    MalikMemoryRegion_Array1d = packed array of MalikMemoryRegion;
    InterruptRecord_Array1d = packed array of InterruptRecord;

    ProcessorListener = interface(_Interface) [PROCESSOR_LISTENER_GUID]
        procedure programmePause();
        procedure programmeResume();
        procedure programmeTerminated();
        procedure programmeBreakpoint();
        procedure instructionExecuting(contextID: int);
        function getCurrentUTCOffset(): int;
        function getCurrentUTCTime(): long;
        function getMilliseconds(): long;
        function syscall(func: int; argument: long): long;
    end;

    MalikDebugContext = class(_Object)
    public
        id: int;
        state: int;
        regEIP: int;
        regESP: int;
        regEBP: int;
        regIF: int;
        stackBegin: int;
        stackEnd: int;
        lockedCount: int;
        nextID: int;
        constructor create();
    end;

    MalikContext = class(_Object)
    private
        id: int;
        state: int;
        regEIP: int;
        regEBP: int;
        regIF: int; { -- boolean -- }
        priority: int;
        priorityCount: int;
        lockedCount: int;
        stackBegin: Pointer;
        stackEnd: Pointer;
        stackTop: PStackItem;
        stackBlock: PStackItem;
        next: MalikContext;
    public
        constructor create();
    end;

    MalikMemoryRegion = class(_Object)
    public
        memory: byte_Array1d;
        start: int;
        size: int;
        constructor create(start, size: int);
    end;

    MalikProcessor = class(_Object)
    strict private
        executableFileName: UnicodeString;
        listener: ProcessorListener;
        contexts: MalikContext_Array1d;
        contextsCount: int;
        regions: MalikMemoryRegion_Array1d;
        regionsCount: int;
        pages: Pointer_Array1d;
        interrupts: InterruptRecord_Array1d;
        interruptsLock: ThreadLock;
        interruptsHead: int;
        interruptsTail: int;
        interruptsCounter: int;
        defaultStackSize: int;
        threadExitAddress: int;
        executionStarted: long;
        loadingError: boolean;
        paused: boolean;
        debugging: boolean;
        executing: boolean;
        terminated: boolean;
        procedure invokeProgrammePause();
        procedure invokeProgrammeResume();
        procedure invokeProgrammeTerminated();
        procedure invokeProgrammeBreakpoint();
        procedure invokeInstructionExecuting(contextID: int);
        function invokeGetCurrentUTCOffset(): int;
        function invokeGetCurrentUTCTime(): long;
        function invokeGetMilliseconds(): long;
        function invokeSyscall(func: int; parameter: long): long;
        function syscall(context: MalikContext; func: int; argument: long): long;
        function createContext(stackSize, startingEIP: int): MalikContext;
        procedure destroyContext(context: MalikContext);
        procedure createStack(forContext: MalikContext; size: int);
        procedure destroyStack(forContext: MalikContext);
        procedure createMap(region, size: int; address: Pointer);
        procedure destroyMap(region, size: int);
        function writeEBP(newEBP: int; context: MalikContext): Pointer;
        function readEBP(context: MalikContext): int;
        function getRegionIndex(address: int): int;
        function virtualAddressToPhysical(address: int): Pointer;
        function physicalAddressToVirtual(address: Pointer): int;
        procedure addref(obj: int);
        procedure release(obj: int);
        procedure idle();
        procedure lifecycle();
    public
        constructor create(const executableFileName: UnicodeString; listener: ProcessorListener);
        destructor destroy; override;
        procedure setDebug(debugging: boolean);
        procedure execute();
        procedure terminate();
        procedure interrupt(number: int; parameter: long);
        procedure interruptIfAllow(number: int; parameter: long);
        procedure getContext(id: int; contents: MalikDebugContext);
        procedure destroyRegion(address: int);
        function createRegion(size: int): int;
        function getMemory(address, size: int): Pointer;
        function isDebug(): boolean;
        function isExecuting(): boolean;
        function isTerminated(): boolean;
        function load(): boolean;

    private const
        MAX_REGIONS = int($200);
        ADDITIONAL_BYTES = int($20);
        REGION_DISTANCE = int($400);
        PAGE_SIZE = int($400);
        SIGNATURE_NORMAL = int($004b4c4d);
        SIGNATURE_COMPRESSED = int($014b4c4d);
    public const
        MEMORY_START = int($00800000);
        INTERRUPTS_TABLE_SIZE = int($800);
        INTERRUPTS_QUEUE_LENGTH = int($100);
        MAX_CONTEXTS = int($100);
    end;
{%endregion}

implementation

{$T-}

{%region private }
type
    ExtLongRecord = packed record
    case int of
    0: (value: long);
    1: (lo: int;
        hi: int);
    2: (time: DateTimeRecord);
    3: (bytes: packed array [0..7] of byte);
    4: (shorts: packed array [0..3] of short);
    end;
{%endregion}

{%region routine }
    function isLeapYear(year: int): boolean;
    begin
        if (year mod 100) <> 0 then begin
            result := (year mod 4) = 0;
        end else begin
            result := (year mod 400) = 0;
        end;
    end;

    function convertTimeToMilliseconds(const time: DateTimeRecord; utcOffset: int): long;
    const
        DAYS_IN_MONTHS: array [boolean, 0..11] of byte =
        (
            ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ),
            ( 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 )
        );
    var
        leap: boolean;
        y: int;
        i: int;
    begin
        result := 0;
        with time do begin
            leap := isLeapYear(year);
            y := int(year) - 1;
            inc(result, long(12622780800000) * long(y div 400));
            y := y mod 400;
            inc(result, long(3155673600000) * long(y div 100));
            y := y mod 100;
            inc(result, long(126230400000) * long(y div 4));
            y := y mod 4;
            inc(result, long(31536000000) * long(y));
            for i := 0 to int(month) - 2 do begin
                inc(result, long(86400000) * long(DAYS_IN_MONTHS[leap, i]));
            end;
            i := int(day) - 1;
            inc(result, long(86400000) * long(i));
            i := int(3600000) * int(hour) + int(60000) * int(minute) + int(millisecond) - utcOffset;
            inc(result, long(i));
        end;
    end;

    function Pointer_Array1d_create(length: int): Pointer_Array1d;
    begin
        setLength(result, length);
    end;

    function MalikContext_Array1d_create(length: int): MalikContext_Array1d;
    begin
        setLength(result, length);
    end;

    function MalikMemoryRegion_Array1d_create(length: int): MalikMemoryRegion_Array1d;
    begin
        setLength(result, length);
    end;

    function InterruptRecord_Array1d_create(length: int): InterruptRecord_Array1d;
    begin
        setLength(result, length);
    end;
{%endregion}

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

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

{%region MalikMemoryRegion }
    constructor MalikMemoryRegion.create(start, size: int);
    begin
        inherited create();
        self.memory := byte_Array1d_create(size + MalikProcessor.ADDITIONAL_BYTES);
        self.start := start;
        self.size := size;
    end;
{%endregion}

{%region MalikProcessor }
    constructor MalikProcessor.create(const executableFileName: UnicodeString; listener: ProcessorListener);
    begin
        inherited create();
        self.executableFileName := executableFileName;
        self.listener := listener;
        self.loadingError := true;
        self.interruptsLock := ThreadLock.create();
    end;

    destructor MalikProcessor.destroy;
    var
        i: int;
    begin
        for i := length(contexts) - 1 downto 0 do begin
            contexts[i].free();
            contexts[i] := nil;
        end;
        for i := length(regions) - 1 downto 0 do begin
            regions[i].free();
            regions[i] := nil;
        end;
        interruptsLock.free();
        inherited destroy;
    end;

    procedure MalikProcessor.invokeProgrammePause();
    begin
        listener.programmePause();
    end;

    procedure MalikProcessor.invokeProgrammeResume();
    begin
        listener.programmeResume();
    end;

    procedure MalikProcessor.invokeProgrammeTerminated();
    begin
        listener.programmeTerminated();
    end;

    procedure MalikProcessor.invokeProgrammeBreakpoint();
    begin
        listener.programmeBreakpoint();
    end;

    procedure MalikProcessor.invokeInstructionExecuting(contextID: int);
    begin
        listener.instructionExecuting(contextID);
    end;

    function MalikProcessor.invokeGetCurrentUTCOffset(): int;
    begin
        result := listener.getCurrentUTCOffset();
    end;

    function MalikProcessor.invokeGetCurrentUTCTime(): long;
    begin
        result := listener.getCurrentUTCTime();
    end;

    function MalikProcessor.invokeGetMilliseconds(): long;
    begin
        result := listener.getMilliseconds();
    end;

    function MalikProcessor.invokeSyscall(func: int; parameter: long): long;
    begin
        result := listener.syscall(func, parameter);
    end;

    function MalikProcessor.syscall(context: MalikContext; func: int; argument: long): long;
    var
        argumentRec: ExtLongRecord absolute argument;
        resultRec: ExtLongRecord absolute result;
        ctx: MalikContext;
        item: PStackItem;
        time: DateTimeRecord;
        id: int;
    begin
        result := 0;
        case func of
        $00: begin
            { Создаёт новый поток управления. }
            ctx := createContext(defaultStackSize, argumentRec.lo);
            if ctx <> nil then begin
                addref(argumentRec.hi);
                item := ctx.stackTop;
                dec(item);
                item.valueObject := argumentRec.hi;
                item.itemType := STACKITEM_OBJECT;
                dec(item);
                item.returnEIP := threadExitAddress;
                item.itemType := STACKITEM_RETURN;
                ctx.stackTop := item;
                resultRec.lo := (ctx.id and $ff) or (((ctx.stackEnd - ctx.stackBegin) - 1) and (-$100));
                resultRec.hi := physicalAddressToVirtual(ctx.stackBegin);
            end else begin
                result := -1;
            end;
        end;
        $01: begin
            { Завершает поток управления. }
            id := int(argumentRec.bytes[0]) and $ff;
            if (id >= 0) and (id < MAX_CONTEXTS) then begin
                destroyContext(contexts[id]);
            end;
        end;
        $02: begin
            { Приостанавливает поток управления. }
            id := int(argumentRec.bytes[0]) and $ff;
            if (id >= 0) and (id < MAX_CONTEXTS) and (contexts[id] <> nil) then begin
                contexts[id].state := CONTEXT_STATE_PAUSED;
            end;
        end;
        $03: begin
            { Возобновляет выполнение потока управления. }
            id := int(argumentRec.bytes[0]) and $ff;
            if (id >= 0) and (id < MAX_CONTEXTS) and (contexts[id] <> nil) then begin
                contexts[id].state := CONTEXT_STATE_ACTIVE;
            end;
        end;
        $04: begin
            { Блокирует текущий поток управления. }
            if context.lockedCount < MAX_INT then begin
                inc(context.lockedCount);
            end;
        end;
        $05: begin
            { Разблокирует текущий поток управления. }
            if context.lockedCount > MIN_INT then begin
                dec(context.lockedCount);
            end;
        end;
        $06: begin
            { Возвращает границы стака текущего потока управления. }
            resultRec.lo := physicalAddressToVirtual(context.stackBegin);
            resultRec.hi := (context.stackEnd - context.stackBegin) - 1;
        end;
        $07: begin
            { Читает регистр EIP, ESP или EBP в заданном потоке. }
            id := int(argumentRec.bytes[0]) and $ff;
            if (id >= 0) and (id < MAX_CONTEXTS) and (contexts[id] <> nil) then begin
                ctx := contexts[id];
                case int(argumentRec.bytes[1]) and $03 of
                 0: begin
                    if context = ctx then begin
                        resultRec.lo := ctx.regEIP + 3;
                    end else begin
                        resultRec.lo := ctx.regEIP;
                    end;
                end;
                 1: begin
                    resultRec.lo := physicalAddressToVirtual(ctx.stackTop);
                end;
                 2: begin
                    resultRec.lo := readEBP(ctx);
                end;
                end;
            end;
        end;
        $08: begin
            { Возвращает приоритет потока управления. }
            id := int(argumentRec.bytes[0]) and $ff;
            if (id >= 0) and (id < MAX_CONTEXTS) and (contexts[id] <> nil) then begin
                result := long(contexts[id].priority) - 1;
            end else begin
                result := -1;
            end;
        end;
        $09: begin
            { Задаёт приоритет потока управления. }
            id := int(argumentRec.bytes[0]) and $ff;
            if (id >= 0) and (id < MAX_CONTEXTS) and (contexts[id] <> nil) then begin
                contexts[id].priority := (int(argumentRec.bytes[4]) and $1f) + 1;
            end;
        end;
        $0a: begin
            { Возвращает размер стака для новых потоков управления. }
            resultRec.lo := defaultStackSize - 1;
        end;
        $0b: begin
            { Задаёт новый размер стака для новых потоков управления. }
            inc(argumentRec.lo);
            defaultStackSize := max($00001000, min($10000000, argumentRec.lo));
        end;
        $0c: begin
            { Возвращает текущую дату и время, текущее смещение от UTC в миллисекундах. }
            if argumentRec.hi = 0 then begin
                case argumentRec.lo of
                 0: begin
                    result := invokeGetCurrentUTCTime();
                end;
                 1: begin
                    result := invokeGetCurrentUTCOffset();
                end;
                 2: begin
                    time.representation := invokeGetCurrentUTCTime();
                    result := convertTimeToMilliseconds(time, 0);
                end;
                end;
            end;
        end;
        $0d: begin
            { Возвращает время выполненяи программы. }
            result := invokeGetMilliseconds() - executionStarted;
        end;
        $0e: begin
            { Заставляет текущий поток управления ждать аппаратного прерывания. }
            context.state := CONTEXT_STATE_WAITING;
        end;
        $0f: begin
            { Возвращает количество доступной памяти и максимальный идентификатор потока управления (т.е. максимально возможное количество потоков управления минус один). }
            resultRec.lo := regions[0].size;
            resultRec.bytes[4] := byte(MAX_CONTEXTS - 1);
        end;
        else
            result := invokeSyscall(func, argument);
        end;
    end;

    function MalikProcessor.createContext(stackSize, startingEIP: int): MalikContext;
    var
        i: int;
        j: int;
        buf: MalikContext;
    begin
        if contextsCount = 0 then begin
            result := MalikContext.create();
            contexts[0] := result;
            with result do begin
                id := 0;
                state := CONTEXT_STATE_ACTIVE;
                regEIP := startingEIP;
                regEBP := 0;
                regIF := 0;
                priority := 16;
                priorityCount := priority;
                lockedCount := 0;
                next := result;
            end;
            inc(contextsCount);
            createStack(result, stackSize);
            exit;
        end;
        if contextsCount < MAX_CONTEXTS then begin
            result := MalikContext.create();
            j := contextsCount;
            for i := 1 to MAX_CONTEXTS - 1 do begin
                if contexts[i] = nil then begin
                    j := i;
                    break;
                end;
            end;
            contexts[j] := result;
            with result do begin
                id := j;
                state := CONTEXT_STATE_PAUSED;
                regEIP := startingEIP;
                regEBP := 0;
                regIF := 0;
                priority := 16;
                priorityCount := priority;
                lockedCount := 0;
                buf := contexts[0].next;
                contexts[0].next := result;
                next := buf;
            end;
            inc(contextsCount);
            createStack(result, stackSize);
            exit;
        end;
        result := nil;
    end;

    procedure MalikProcessor.destroyContext(context: MalikContext);
    var
        i: int;
        prev: MalikContext;
        item: PStackItem;
        send: PStackItem;
    begin
        if (contextsCount = 0) or (context = nil) then begin
            context.free();
            exit;
        end;
        item := context.stackTop;
        send := context.stackEnd;
        while item < send do begin
            case item.itemType of
            STACKITEM_OBJECT: begin
                release(item.valueObject);
                item.itemType := 0;
            end;
            STACKITEM_OVERFLOW: begin
                release(item.valueObject);
                item.valueObject := 0;
            end;
            else
                item.itemType := 0;
            end;
            inc(item);
        end;
        destroyStack(context);
        dec(contextsCount);
        prev := contexts[0];
        for i := 0 to contextsCount do begin
            if prev.next = context then begin
                prev.next := context.next;
                break;
            end;
            prev := prev.next;
        end;
        if context.id = 0 then begin
            terminated := true;
        end;
        contexts[context.id] := nil;
        context.free();
    end;

    procedure MalikProcessor.createStack(forContext: MalikContext; size: int);
    begin
        with forContext do begin
            stackBegin := virtualAddressToPhysical(createRegion(size));
            stackEnd := stackBegin + size;
            stackTop := stackEnd;
            stackBlock := nil;
        end;
    end;

    procedure MalikProcessor.destroyStack(forContext: MalikContext);
    begin
        destroyRegion(physicalAddressToVirtual(forContext.stackBegin));
    end;

    procedure MalikProcessor.createMap(region, size: int; address: Pointer);
    var
        i: int;
    begin
        inc(size, region);
        if (region < MEMORY_START) or (size < 0) then begin
            exit;
        end;
        dec(region, MEMORY_START);
        dec(size, MEMORY_START);
        region := region div PAGE_SIZE;
        size := size div PAGE_SIZE;
        for i := region to size - 1 do begin
            pages[i] := address;
            inc(address, PAGE_SIZE);
        end;
    end;

    procedure MalikProcessor.destroyMap(region, size: int);
    var
        i: int;
    begin
        inc(size, region);
        if (region < MEMORY_START) or (size < 0) then begin
            exit;
        end;
        dec(region, MEMORY_START);
        dec(size, MEMORY_START);
        region := region div PAGE_SIZE;
        size := size div PAGE_SIZE;
        for i := region to size - 1 do begin
            pages[i] := nil;
        end;
    end;

    {$I LIFECYCLE.INC}

    procedure MalikProcessor.setDebug(debugging: boolean);
    begin
        self.debugging := debugging;
    end;

    procedure MalikProcessor.execute();
    begin
        if loadingError or executing or terminated then begin
            exit;
        end;
        executing := true;
        try
            executionStarted := invokeGetMilliseconds();
            lifecycle();
        finally
            executing := false;
            invokeProgrammeTerminated();
        end;
    end;

    procedure MalikProcessor.terminate();
    begin
        if loadingError or terminated then begin
            exit;
        end;
        terminated := true;
        if paused then begin
            invokeProgrammeResume();
        end;
    end;

    procedure MalikProcessor.interrupt(number: int; parameter: long);
    var
        interrupts: InterruptRecord_Array1d;
        intr: PInterruptRecord;
        head: int;
        tail: int;
        newHead: int;
    begin
        interrupts := self.interrupts;
        if (interrupts = nil) or loadingError or terminated then begin
            exit;
        end;
        interruptsLock.enter();
        try
            head := interruptsHead;
            tail := interruptsTail;
            newHead := (head + 1) mod INTERRUPTS_QUEUE_LENGTH;
            if newHead <> tail then begin
                intr := @(interrupts[head]);
                intr.number := number and $ff;
                intr.parameter := parameter;
                interruptsHead := newHead;
                if paused then begin
                    invokeProgrammeResume();
                end;
            end;
        finally
            interruptsLock.leave();
        end;
    end;

    procedure MalikProcessor.interruptIfAllow(number: int; parameter: long);
    begin
        if (interrupts = nil) or loadingError or terminated then begin
            exit;
        end;
        if interruptsCounter = 0 then begin
            interrupt(number, parameter);
        end;
        interruptsCounter := (interruptsCounter + 1) and $07;
    end;

    procedure MalikProcessor.getContext(id: int; contents: MalikDebugContext);
    var
        c: MalikContext;
    begin
        if (id >= 0) and (id < MAX_CONTEXTS) and (contexts[id] <> nil) then begin
            c := contexts[id];
            contents.id := c.id;
            contents.state := c.state;
            contents.regEIP := c.regEIP;
            contents.regESP := physicalAddressToVirtual(c.stackTop);
            contents.regEBP := readEBP(c);
            contents.regIF := c.regIF;
            contents.stackBegin := physicalAddressToVirtual(c.stackBegin);
            contents.stackEnd := physicalAddressToVirtual(c.stackEnd);
            contents.lockedCount := c.lockedCount;
            contents.nextID := c.next.id;
        end else begin
            contents.id := id;
            contents.state := CONTEXT_STATE_NOEXECUTE;
            contents.regEIP := 0;
            contents.regESP := 0;
            contents.regEBP := 0;
            contents.regIF := 0;
            contents.stackBegin := 0;
            contents.stackEnd := 0;
            contents.lockedCount := 0;
            contents.nextID := -1;
        end;
    end;

    procedure MalikProcessor.destroyRegion(address: int);
    var
        i: int;
        j: int;
        regions: MalikMemoryRegion_Array1d;
    begin
        regions := self.regions;
        for i := 0 to regionsCount - 1 do begin
            if (regions[i].start = address) and (regions[i].size > 0) then begin
                destroyMap(address, regions[i].size);
                if i < regionsCount - 1 then begin
                    regions[i].start := regions[i + 1].start;
                    regions[i].size := 0;
                    regions[i].memory := nil;
                    for j := i - 1 downto 0 do begin
                        if regions[j].size = 0 then begin
                            regions[j].start := regions[i].start;
                        end else begin
                            break;
                        end;
                    end;
                end else begin
                    repeat
                        dec(regionsCount);
                        regions[regionsCount].free();
                        regions[regionsCount] := nil;
                    until (regionsCount = 0) or (regions[regionsCount - 1].size > 0);
                end;
                break;
            end;
        end;
    end;

    function MalikProcessor.createRegion(size: int): int;
    var
        i: int;
        regions: MalikMemoryRegion_Array1d;
    begin
        if loadingError then begin
            result := 0;
            exit;
        end;
        if (size and (PAGE_SIZE - 1)) <> 0 then begin
            size := (size + PAGE_SIZE) and (-PAGE_SIZE);
        end;
        regions := self.regions;
        for i := 1 to regionsCount - 2 do begin
            if (regions[i].size = 0) and (regions[i + 1].start - regions[i - 1].start - regions[i - 1].size >= size + 2 * REGION_DISTANCE) then begin
                regions[i].memory := byte_Array1d_create(size + ADDITIONAL_BYTES);
                regions[i].start := regions[i - 1].start + regions[i - 1].size + REGION_DISTANCE;
                regions[i].size := size;
                result := regions[i].start;
                createMap(result, size, regions[i].memory);
                exit;
            end;
        end;
        if regionsCount >= MAX_REGIONS then begin
            result := 0;
            exit;
        end;
        if regionsCount > 0 then begin
            i := regionsCount;
            inc(regionsCount);
            regions[i] := MalikMemoryRegion.create(regions[i - 1].start + regions[i - 1].size + REGION_DISTANCE, size);
        end else begin
            i := 0;
            regionsCount := 1;
            regions[0] := MalikMemoryRegion.create(MEMORY_START, size);
        end;
        result := regions[i].start;
        createMap(result, size, regions[i].memory);
    end;

    function MalikProcessor.getMemory(address, size: int): Pointer;
    var
        i: int;
        j: int;
        s: int;
        limit: int;
        regions: MalikMemoryRegion_Array1d;
        r: MalikMemoryRegion;
    begin
        if size <= 0 then begin
            result := nil;
            exit;
        end;
        limit := address + size;
        regions := self.regions;
        for i := regionsCount - 1 downto 0 do begin
            r := regions[i];
            s := r.start;
            j := s + r.size;
            if (address >= s) and (address <= j) and (limit >= s) and (limit <= j) then begin
                result := @(r.memory[address - s]);
                exit;
            end;
        end;
        result := nil;
    end;

    function MalikProcessor.isDebug(): boolean;
    begin
        result := debugging;
    end;

    function MalikProcessor.isExecuting(): boolean;
    begin
        result := executing;
    end;

    function MalikProcessor.isTerminated(): boolean;
    begin
        result := terminated;
    end;

    function MalikProcessor.load(): boolean;
    var
        f: DataInput;
        compressed: boolean;
        imageBase: int;
        imageSize: int;
        startingEIP: int;
        availableMemory: int;
        stackSize: int;
        size: long;
        image: byte_Array1d;
        region: MalikMemoryRegion;
        context: MalikContext;
        item: PStackItem;
    begin
        if executing or terminated or (not loadingError) or (listener = nil) then begin
            result := false;
            exit;
        end;
        f := DataInputStream.create(FileInputStream.create(executableFileName));
        if f.available() < $10 then begin
            result := false;
            exit;
        end;
        case f.readIntLE() of
        SIGNATURE_NORMAL: begin
            compressed := false;
        end;
        SIGNATURE_COMPRESSED: begin
            compressed := true;
        end;
        else
            result := false;
            exit;
        end;
        imageBase := f.readIntLE();
        startingEIP := f.readIntLE();
        availableMemory := f.readUnsignedShortLE() shl 20;
        stackSize := f.readUnsignedShortLE() shl 20;
        size := f.available();
        if size > $10000000 then begin
            result := false;
            exit;
        end;
        imageSize := int(size);
        image := byte_Array1d_create(imageSize);
        f.read(image);
        if compressed then begin
            image := Zlib.decompress(image);
            imageSize := length(image);
        end;
        if
            (imageSize < 0) or (imageBase < MEMORY_START) or (imageBase + imageSize < 0) or
            (availableMemory < 0) or (availableMemory > $10000000) or
            (stackSize < $00100000) or (stackSize > $10000000) or
            (imageBase + imageSize > MEMORY_START + availableMemory) or
            (startingEIP < imageBase) or (startingEIP >= imageBase + imageSize)
        then begin
            result := false;
            exit;
        end;
        loadingError := false;
        contexts := MalikContext_Array1d_create(MAX_CONTEXTS);
        regions := MalikMemoryRegion_Array1d_create(MAX_REGIONS);
        pages := Pointer_Array1d_create(((int(int($80000000) - PAGE_SIZE) - MEMORY_START) div PAGE_SIZE) + 1);
        interrupts := InterruptRecord_Array1d_create(INTERRUPTS_QUEUE_LENGTH);
        defaultStackSize := stackSize;
        createRegion(availableMemory);
        region := regions[0];
        arraycopy(image, 0, region.memory, imageBase - region.start, imageSize);
        threadExitAddress := createRegion(PAGE_SIZE);
        image := regions[1].memory;
        image[0] := byte($fe);
        image[1] := byte($80);
        image[2] := byte($f9);
        image[3] := byte($01);
        image[4] := byte($00);
        context := createContext(stackSize, startingEIP);
        dec(context.stackTop);
        item := context.stackTop;
        item.returnEIP := threadExitAddress;
        item.reserved2 := 0;
        item.reserved3 := 0;
        item.itemType := STACKITEM_RETURN;
        result := true;
    end;
{%endregion}

end.

