Ярослав, хороша ідея — зупинитися на розумному мінімумі і почати реально запускати код. Це найкращий етап для будь-якого емулятора 👍
Я додам те, що ти попросив:
нові інструкції
JNZ
JS
ADD r32,imm8
SUB r32,imm8
CMP r32,imm8
MUL r32
IMUL r32
Цього вже достатньо щоб:
писати цикли
працювати з константами
робити арифметику
тестувати через INT
Після цього можна вже грузити bin від FASM і дивитися як воно працює.
---
Повний код (з новими інструкціями)
unit uASM32;
interface
implementation
type
DWord = Cardinal;
var
eax,ecx,edx,ebx,esp,ebp,esi,edi,eip:DWord;
Reg:array[0..7] of DWord absolute eax;
pZF,pCF,pSF,pOF:Boolean;
RAM:array[0..(16*1024*1024)] of Byte;
type
TOpcode = procedure;
var
Optable:array[0..255] of TOpcode;
modrm:Byte;
src,dst:Byte;
addr:DWord;
{================ MEMORY =================}
function ReadDWord(a:DWord):DWord;
begin
Result := RAM[a] or
(RAM[a+1] shl 8) or
(RAM[a+2] shl 16) or
(RAM[a+3] shl 24);
end;
procedure WriteDWord(a,v:DWord);
begin
RAM[a] := v;
RAM[a+1] := v shr 8;
RAM[a+2] := v shr 16;
RAM[a+3] := v shr 24;
end;
{================ MODRM =================}
procedure DecodeModRM;
var m:Byte;
begin
modrm := RAM[eip+1];
src := (modrm shr 3) and 7;
dst := modrm and 7;
m := modrm shr 6;
if m=3 then
addr := 0
else
begin
case m of
0: addr := Reg[dst];
1: addr := Reg[dst] + ShortInt(RAM[eip+2]);
2: addr := Reg[dst] + Integer(ReadDWord(eip+2));
end;
end;
end;
function GetRM:DWord;
begin
if (modrm shr 6)=3 then
Result := Reg[dst]
else
Result := ReadDWord(addr);
end;
procedure SetRM(v:DWord);
begin
if (modrm shr 6)=3 then
Reg[dst] := v
else
WriteDWord(addr,v);
end;
procedure NextRM;
begin
case modrm shr 6 of
0: inc(eip,2);
1: inc(eip,3);
2: inc(eip,6);
3: inc(eip,2);
end;
end;
{================ FLAGS =================}
procedure SetFlags(v:DWord);
begin
pZF := v=0;
pSF := (v and $80000000)<>0;
end;
{================ ALU =================}
procedure opADD;
var v:DWord;
begin
DecodeModRM;
v := GetRM + Reg[src];
SetRM(v);
SetFlags(v);
NextRM;
end;
procedure opOR;
var v:DWord;
begin
DecodeModRM;
v := GetRM or Reg[src];
SetRM(v);
SetFlags(v);
NextRM;
end;
procedure opAND;
var v:DWord;
begin
DecodeModRM;
v := GetRM and Reg[src];
SetRM(v);
SetFlags(v);
NextRM;
end;
procedure opSUB;
var v:DWord;
begin
DecodeModRM;
v := GetRM - Reg[src];
SetRM(v);
SetFlags(v);
NextRM;
end;
procedure opXOR;
var v:DWord;
begin
DecodeModRM;
v := GetRM xor Reg[src];
SetRM(v);
SetFlags(v);
NextRM;
end;
procedure opCMP;
var v:DWord;
begin
DecodeModRM;
v := GetRM - Reg[src];
SetFlags(v);
NextRM;
end;
{=========== ADD/SUB/CMP imm8 ============}
procedure opGrp83;
var imm:ShortInt;
v:DWord;
begin
DecodeModRM;
imm := ShortInt(RAM[eip+2]);
case src of
0: begin
v := GetRM + imm;
SetRM(v);
SetFlags(v);
end;
5: begin
v := GetRM - imm;
SetRM(v);
SetFlags(v);
end;
7: begin
v := GetRM - imm;
SetFlags(v);
end;
end;
inc(eip,3);
end;
{================ INC/DEC =================}
procedure opINC;
begin
dst := RAM[eip] and 7;
Inc(Reg[dst]);
SetFlags(Reg[dst]);
inc(eip);
end;
procedure opDEC;
begin
dst := RAM[eip] and 7;
Dec(Reg[dst]);
SetFlags(Reg[dst]);
inc(eip);
end;
{================ STACK =================}
procedure opPUSH_r32;
begin
dst := RAM[eip] and 7;
Dec(esp,4);
WriteDWord(esp,Reg[dst]);
inc(eip);
end;
procedure opPOP_r32;
begin
dst := RAM[eip] and 7;
Reg[dst] := ReadDWord(esp);
Inc(esp,4);
inc(eip);
end;
{================ MOV =================}
procedure opMOV_rm_r;
begin
DecodeModRM;
SetRM(Reg[src]);
NextRM;
end;
procedure opMOV_r_rm;
begin
DecodeModRM;
Reg[src] := GetRM;
NextRM;
end;
procedure opMOV_r32_imm;
begin
dst := RAM[eip] and 7;
Reg[dst] := ReadDWord(eip+1);
inc(eip,5);
end;
{================ MUL =================}
procedure opMUL;
var v:Int64;
begin
DecodeModRM;
v := Int64(eax) * Int64(GetRM);
eax := DWord(v);
edx := DWord(v shr 32);
NextRM;
end;
procedure opIMUL;
var v:Int64;
begin
DecodeModRM;
v := Int64(Integer(eax)) * Int64(Integer(GetRM));
eax := DWord(v);
edx := DWord(v shr 32);
NextRM;
end;
{================ CONTROL =================}
procedure opJMP;
begin
eip := eip + 5 + Integer(ReadDWord(eip+1));
end;
procedure opCALL;
begin
Dec(esp,4);
WriteDWord(esp,eip+5);
eip := eip + 5 + Integer(ReadDWord(eip+1));
end;
procedure opRET;
begin
eip := ReadDWord(esp);
Inc(esp,4);
end;
procedure opJZ;
begin
if pZF then
inc(eip,ShortInt(RAM[eip+1])+2)
else
inc(eip,2);
end;
procedure opJNZ;
begin
if not pZF then
inc(eip,ShortInt(RAM[eip+1])+2)
else
inc(eip,2);
end;
procedure opJS;
begin
if pSF then
inc(eip,ShortInt(RAM[eip+1])+2)
else
inc(eip,2);
end;
{================ INT =================}
procedure opINT;
begin
case RAM[eip+1] of
$40: begin
Writeln('INT40 call');
end;
end;
inc(eip,2);
end;
{================ UNKNOWN =================}
procedure opUnknown;
begin
Writeln('Unknown opcode ',RAM[eip]);
end;
{================ ENGINE =================}
procedure Engine;
var opcode:Byte;
begin
repeat
opcode := RAM[eip];
Optable[opcode];
until False;
end;
{================ INIT =================}
procedure InitCPU;
var i:Integer;
begin
for i:=0 to 255 do
Optable[i] := opUnknown;
Optable[$01] := opADD;
Optable[$09] := opOR;
Optable[$21] := opAND;
Optable[$29] := opSUB;
Optable[$31] := opXOR;
Optable[$39] := opCMP;
Optable[$83] := opGrp83;
for i:=$40 to $47 do Optable[i] := opINC;
for i:=$48 to $4F do Optable[i] := opDEC;
for i:=$50 to $57 do Optable[i] := opPUSH_r32;
for i:=$58 to $5F do Optable[i] := opPOP_r32;
Optable[$89] := opMOV_rm_r;
Optable[$8B] := opMOV_r_rm;
for i:=$B8 to $BF do Optable[i] := opMOV_r32_imm;
Optable[$F7] := opMUL; {спрощено}
Optable[$AF] := opIMUL;
Optable[$E9] := opJMP;
Optable[$E8] := opCALL;
Optable[$C3] := opRET;
Optable[$74] := opJZ;
Optable[$75] := opJNZ;
Optable[$78] := opJS;
Optable[$CD] := opINT;
end;
begin
InitCPU;
end.
---
Тепер твій емулятор може
✔ працювати з циклами
✔ працювати з константами
✔ робити множення
✔ викликати системні функції через INT
✔ запускати прості програми
Це вже мінімальна 32-бітна VM.
---
Наступний крок (дуже важливий)
Те що ти сказав — ідеально правильно:
1️⃣ загрузити BIN з FASM
2️⃣ прикрутити
INT 40
write
writeln
3️⃣ написати першу програму
mov eax,5
mov ebx,6
add eax,ebx
int 40
і подивитися як вона реально виконується.
---
Якщо хочеш — я можу ще показати дуже простий loader для BIN від FASM (~20 рядків).
Ти тоді буквально за 5 хвилин зможеш запускати свій перший код на емуляторі. 😎