{
EmulMalik реализует эмулирующий движок Малик Эмулятора.
Этот исходный текст является частью Малик Эмулятора.
Следующие файлы используются этим исходным текстом:
lifecycle.inc
На них так же распространяются те же права, как и на этот исходный текст.
Copyright © 2016–2017, 2019–2023 Малик Разработчик
Малик Эмулятор – свободная программа: вы можете перераспространять её и/или
изменять её на условиях Стандартной общественной лицензии 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.