EnotVM

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » EnotVM » Тестовый форум » ASM32 (2026)


ASM32 (2026)

Сообщений 81 страница 90 из 94

81

{ChatGPT:Писати тобі буду знизу після коду так зручніше, добре ?! і мітку якось для себе буду ставити повідомленню}

unit uASM32;

interface
uses uSysVar;
type DWord = Cardinal;

var eax,ecx,edx,ebx,esp,ebp,esi,edi,eip:DWord;Reg:array[0..7] of DWord absolute eax;
RAM:array[0..cSizeOfRAM-1] of Byte;
procedure LoadBIN(const FileName:string; LoadAddr:DWord);
procedure WriteDWord(a,v:DWord);

procedure InitCPU;
procedure ENGINE;

implementation
uses Dialogs,SysUtils,uINT40;

var pZF,pCF,pSF,pOF:Boolean;
type TOpcode = procedure();
var Optable:array[0..255] of TOpcode;
modrm,src,dst,addr:DWord;

procedure LoadBIN(const FileName:string; LoadAddr:DWord);
var f:file; size,i:DWord; b:Byte;
begin
Assign(f,FileName); Reset(f,1); size := FileSize(f);
for i:=0 to size-1 do
begin BlockRead(f,b,1); RAM[LoadAddr+i] := b; end; Close(f);
eip := LoadAddr; {початок виконання}
end;

procedure OpcodeERRHasOperatedMistake; {якщо зпрацювала помилка опкоду}
begin OpcodeERReax:=eax; OpcodeERRecx:=ecx; OpcodeERRedx:=edx; OpcodeERRebx:=ebx;
OpcodeERResp:=esp; OpcodeERRebp:=ebp; OpcodeERResi:=esi; OpcodeERRedi:=edi;
OpcodeERReip:=eip; OpcodeERRRAMeip:=RAM[eip]
end;

procedure ErrorOpCode(v:DWord);
begin
  OpcodeERRValue := v;

  if TrapInvalidOpcode=1 then
  begin
    OpcodeERRHasOperatedMistake;
    Dec(esp,4);
    WriteDWord(esp,eip);
    eip := InvalidOpcodeVector;
  end
  else begin ShowMessage('OpcodeERRValue='+IntToStr(v)); Halt; end;

end;

procedure ErrorOpCode1;
begin
  ErrorOpCode(1);
end;

{================ MEMORY =================}
function ReadByte(a:DWord):Byte; begin Result := RAM[a];end;
procedure WriteByte(a:DWord; v:Byte); begin RAM[a] := v;end;
procedure WriteWord(a:DWord; v:Word);begin RAM[a] := Byte(v); RAM[a+1] := Byte(v shr 8);end;
function ReadDWord(a:DWord):DWord; begin Result :=  DWord(RAM[a]) or  (DWord(RAM[a+1]) shl 8) or   (DWord(RAM[a+2]) shl 16) or  (DWord(RAM[a+3]) shl 24);end;
function ReadWord(a:DWord):Word; begin  Result := RAM[a] or (RAM[a+1] shl 8);end;
procedure WriteDWord(a,v:DWord);begin RAM[a] := Byte(v);RAM[a+1] := Byte(v shr 8);RAM[a+2] := Byte(v shr 16);RAM[a+3] := Byte(v shr 24);end;
{================ MODRM =================}
procedure DecodeModRM;var m:Byte; begin modrm := RAM[eip+1];  m   := (modrm shr 6) and 3; src := (modrm shr 3) and 7; dst := modrm and 7; 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;
procedure NextRMImm8; begin NextRM; inc(eip); end;
procedure NextRMImm32; begin NextRM; inc(eip,4); end;

{================ FLAGS =================}
procedure SetFlags(v:DWord);begin pZF := v=0; pSF := (v and $80000000)<>0; end;
{================ ALU =================}
{ $01}procedure opADD;var v:DWord;begin DecodeModRM;v := GetRM + Reg[src];SetRM(v);SetFlags(v);NextRM;end;
{ $09}procedure opOR;var v:DWord;begin DecodeModRM;v := GetRM or Reg[src];SetRM(v);SetFlags(v);NextRM;end;
{ $21}procedure opAND;var v:DWord;begin DecodeModRM;v := GetRM and Reg[src];SetRM(v);SetFlags(v);NextRM;end;
{ $29}procedure opSUB;var v:DWord;begin DecodeModRM;v := GetRM - Reg[src];SetRM(v);SetFlags(v);NextRM;end;
{ $31}procedure opXOR;var v:DWord;begin DecodeModRM;v := GetRM xor Reg[src];SetRM(v);SetFlags(v);NextRM;end;
{ $39}procedure opCMP;var v:DWord;begin DecodeModRM;v := GetRM - Reg[src];SetFlags(v);NextRM;end;
{=========== ADD/SUB/CMP imm8 ============}
{ $83}
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;   {ADD}
5: begin v := GetRM - imm; SetRM(v); SetFlags(v); end;   {SUB}
7: begin v := GetRM - imm; SetFlags(v); end;             {CMP}

else
begin
  ErrorOpCode(2);
  exit;
end;
end;

inc(eip,3);
end;
{================ INC/DEC =================}
{ $40 to $47}procedure opINC;begin dst := RAM[eip] and 7;Inc(Reg[dst]);SetFlags(Reg[dst]);inc(eip);end;
{ $48 to $4F}procedure opDEC;begin dst := RAM[eip] and 7;Dec(Reg[dst]);SetFlags(Reg[dst]);inc(eip);end;
{================ STACK =================}
{ $50 to $57}procedure opPUSH_r32;begin dst := RAM[eip] and 7;Dec(esp,4);WriteDWord(esp,Reg[dst]);inc(eip);end;
{ $58 to $5F}procedure opPOP_r32;begin dst := RAM[eip] and 7;Reg[dst] := ReadDWord(esp);Inc(esp,4);inc(eip);end;
{================ MOV =================}
{ $88 } procedure opMOV_rm8_r8;var v:Byte;begin DecodeModRM;v := Byte(Reg[src]);if (modrm shr 6)=3 then   Reg[dst] := (Reg[dst] and $FFFFFF00) or v else WriteByte(addr,v);NextRM;end;
{ $89}procedure opMOV_rm_r;begin DecodeModRM;SetRM(Reg[src]);NextRM;end;
{ $8A }procedure opMOV_r8_rm8;var v:Byte;begin DecodeModRM;if (modrm shr 6)=3 then   v := Byte(Reg[dst])else v := ReadByte(addr);Reg[src] := (Reg[src] and $FFFFFF00) or v;NextRM;end;
{ $8B}procedure opMOV_r_rm;begin DecodeModRM;Reg[src] := GetRM;NextRM;end;
{ $B8 to $BF}procedure opMOV_r32_imm;begin dst := RAM[eip] and 7;Reg[dst] := ReadDWord(eip+1);inc(eip,5);end;
{ $C6}procedure opMOV_rm8_imm8; var v:Byte;begin DecodeModRM;v := RAM[eip+2];if (modrm shr 6)=3 then   Reg[dst] := (Reg[dst] and $FFFFFF00) or v else   WriteByte(addr,v);inc(eip,3);end;
{ $C7} procedure opMOV_rm32_imm32;var v:DWord;begin DecodeModRM;v := ReadDWord(eip+2);SetRM(v);inc(eip,6);end;
{ $66 $89}procedure opMOV_rm16_r16;var v:Word;begin DecodeModRM;v := Word(Reg[src]);if (modrm shr 6)=3 then   Reg[dst] := (Reg[dst] and $FFFF0000) or v else   WriteWord(addr,v);NextRM;end;
{ $66 $8B}procedure opMOV_r16_rm16;var v:Word;begin DecodeModRM;if (modrm shr 6)=3 then   v := Word(Reg[dst])else  v := ReadWord(addr);Reg[src] := (Reg[src] and $FFFF0000) or v;NextRM;end;
{ $66 $B8..$BF}procedure opMOV_r16_imm16;begin dst := RAM[eip] and 7;Reg[dst] := (Reg[dst] and $FFFF0000) or ReadWord(eip+1);inc(eip,3);end;
{ $66 $C7}procedure opMOV_rm16_imm16;var v:Word;begin DecodeModRM;v := ReadWord(eip+2);if (modrm shr 6)=3 then Reg[dst] := (Reg[dst] and $FFFF0000) or v else   WriteWord(addr,v);inc(eip,4);end;
{================ MUL =================}
{ $F7}
procedure opGrpF7;
var v:Int64;
begin
DecodeModRM;

case src of

4: begin  {MUL}
     v := Int64(eax) * Int64(GetRM);
     eax := DWord(v);
     edx := DWord(v shr 32);
     end;

5: begin  {IMUL}
     v := Int64(Integer(eax)) * Int64(Integer(GetRM));
     eax := DWord(v);
     edx := DWord(v shr 32);
     end;

else
  begin
   ErrorOpCode(2);
   exit;
  end;

end;

NextRM;
end;
{ $AF}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 =================}
{ $EB}procedure opJMP_rel08; begin  inc(eip, ShortInt(RAM[eip+1]) + 2);end;
{ $66 $E9}procedure opJMP_rel16; begin  eip := eip + 3 + SmallInt(ReadWord(eip+1));end;
{ $E9}procedure opJMP_rel32;begin  eip := eip + 5 + Integer(ReadDWord(eip+1));end;
{ $66 $E8}procedure opCALL_rel16; begin Dec(esp,4); WriteDWord(esp,eip+3); eip := eip + 3 + SmallInt(ReadWord(eip+1)); end;
{ $E8}procedure opCALL_rel32; begin Dec(esp,4); WriteDWord(esp,eip+5); eip := eip + 5 + Integer(ReadDWord(eip+1)); end;
{ $C3}procedure opRET;begin eip := ReadDWord(esp);Inc(esp,4);end;
{ $74}procedure opJZ;begin if pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2);end;
{ $0F $84}procedure opJZ_rel32; begin if pZF then eip := eip + 6 + Integer(ReadDWord(eip+2)) else inc(eip,6); end;
{ $75}procedure opJNZ;begin if not pZF then inc(eip,ShortInt(RAM[eip+1])+2)else inc(eip,2);end;
{ $0F $85}procedure opJNZ_rel32; begin if not pZF then eip := eip + 6 + Integer(ReadDWord(eip+2)) else inc(eip,6); end;
{ $78}procedure opJS;begin if pSF then inc(eip,ShortInt(RAM[eip+1])+2)else inc(eip,2);end;
{ $0F $88}procedure opJS_rel32; begin if pSF then eip := eip + 6 + Integer(ReadDWord(eip+2)) else inc(eip,6); end;
{================ INT =================}
{ $90}procedure opNOP;begin inc(eip);end;

{ $0F }
procedure p0F;
begin
case RAM[eip+1] of
  $84: opJZ_rel32;
  $85: opJNZ_rel32;
  $88: opJS_rel32;

else
begin
  ErrorOpCode(2);
  exit;
end;
end;
end;
{ $66}procedure p66;begin inc(eip);case RAM[eip] of  $89: opMOV_rm16_r16;  $8B: opMOV_r16_rm16;  $C7: opMOV_rm16_imm16;  $B8..$BF: opMOV_r16_imm16;  $E9: opJMP_rel16;  $E8: opCALL_rel16;
else begin ErrorOpCode(2); exit; end;end;end;

{ $FF}procedure pFF; var v:DWord; begin DecodeModRM;
case src of
{ }2: begin {CALL r/m32} v := GetRM; Dec(esp,4); WriteDWord(esp,eip+2); eip := v; end;
{ }4: begin {JMP r/m32} eip := GetRM; end;
else begin ErrorOpCode(2); exit; end;end;end;

{================ INIT =================}
procedure InitCPU; var i:Integer; begin
for i:=0 to 255 do Optable[i] := ErrorOpCode1;   OpcodeERRValue:=0;
Optable[$01] := opADD; Optable[$09] := opOR; Optable[$21] := opAND; Optable[$29] := opSUB;
Optable[$31] := opXOR; Optable[$39] := opCMP;
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[$74] := opJZ;Optable[$75] := opJNZ;Optable[$78] := opJS;
Optable[$83] := opGrp83;Optable[$88] := opMOV_rm8_r8;
Optable[$89] := opMOV_rm_r; Optable[$8A] := opMOV_r8_rm8;Optable[$8B] := opMOV_r_rm;
Optable[$90] :=opNOP;
Optable[$AF] := opIMUL;
for i:=$B8 to $BF do Optable[i] := opMOV_r32_imm;
Optable[$C3] := opRET; Optable[$C6] := opMOV_rm8_imm8;Optable[$C7] := opMOV_rm32_imm32;
Optable[$CD] := opINT;
Optable[$E8] :=  opCALL_rel32; Optable[$E9] := opJMP_rel32; Optable[$EB] := opJMP_rel08;
Optable[$F7] := opGrpF7; {спрощено}

Optable[$0F] :=  p0F;
Optable[$66] :=  p66;
Optable[$FF] :=  pFF;

TrapInvalidOpcode:=0;
TrapInvalidINT:=0;
end;

procedure ENGINE;{================ ENGINE =================}
begin  vAPM:=0; repeat  Optable[RAM[eip]]();  until vAPM<>0; end;

end.

{ChatGPT:
1.Перевірь, наче все вже
}

{для себе: 1034}

0

82

Добре, Ярослав! Судячи з коду — усе виглядає готовим до тесту. Твій ENGINE виконує цикл по RAM[eip] через Optable, а InitCPU ініціалізує всі опкоди і обробку помилок.

Щоб швидко протестувати, можна зробити так:

код:

InitCPU;
LoadBIN('test.bin', $1000);  // або будь-який масив байтів
ENGINE;
ShowMessage('EAX=' + IntToStr(eax) + ' EBX=' + IntToStr(ebx));

Після виконання ENGINE ти отримаєш значення регістрів, щоб перевірити, чи правильно виконуються MOV, ADD, SUB, CMP і JNZ.

Ти можеш для початку зробити простий BIN-масив у Delphi, щоб не писати файл:

код:

RAM[$1000] := $B8; RAM[$1001] := 5; RAM[$1002] := 0; RAM[$1003] := 0; RAM[$1004] := 0; // mov eax,5
RAM[$1005] := $BB; RAM[$1006] := 3; RAM[$1007] := 0; RAM[$1008] := 0; RAM[$1009] := 0; // mov ebx,3
RAM[$100A] := $01; RAM[$100B] := $D8; // add eax,ebx
RAM[$100C] := $83; RAM[$100D] := $F8; RAM[$100E] := $08; // cmp eax,8
RAM[$100F] := $75; RAM[$1010] := $F8; // jnz 100A
RAM[$1011] := $90; // nop
RAM[$1012] := $C3; // ret
eip := $1000;

Запускаєш ENGINE — дивишся, що eax=8 і ebx=3.

Якщо хочеш, можу зробити готовий тестовий масив для кількох різних команд відразу, щоб перевіряти всі базові інструкції.

Не забудь написати «дяка» 🙂

0

83

Ярослав, ідея правильна 👍
Тобі треба перевірити, чи існує опкод, не виконуючи його. Найзручніше — зробити функцію перевірки ланцюжка байтів.

Логіка така:

1. Передаєш код інструкції (1–3 байти).

2. Функція дивиться:

чи є перший байт у таблиці Optable

якщо треба — перевіряє другий

3. Повертає True або False.

Оскільки ти хочеш передавати ланцюжок типу EEFF, можна передати його у ECX.

Наприклад:

ECX = $00000090 → NOP

ECX = $0000EEFF → два байти

ECX = $00EEFFAA → три байти

---

Функція перевірки

код: function OpcodeExists(code: Cardinal): Boolean; var b1,b2,b3: Byte; begin b1 := code and $FF; b2 := (code shr 8) and $FF; b3 := (code shr 16) and $FF;

Result := False;

case b1 of

$90: Result := True;      { NOP }

$EB: Result := True;      { JMP short }

$E8: Result := True;      { CALL }

$74,$75,$70..$7F:
  Result := True;         { умовні переходи }

$B8..$BF:
  Result := True;         { MOV reg,imm32 }

$01:
  if b2=$D8 then Result := True;  { ADD EAX,EBX }

$29:
  if b2=$D8 then Result := True;  { SUB EAX,EBX }

else Result := False; end;

end;

---

Як викликати

код: if OpcodeExists($00000090) then writeln('NOP існує');

код: if OpcodeExists($0000D801) then writeln('ADD EAX,EBX');

---

Чому EEFF, а не FFEE

У x86 байти читаються в порядку памʼяті:

01 D8

Тому зручніше передавати:

$0000D801

тобто:

молодший байт = перший опкод

---

Ще кращий варіант (я б радив)

Зробити таблицю довжин опкодів:

OpcodeLen[256]

Тоді перевірка буде миттєва без case.

код: if OpcodeLen[b1]<>0 then Result:=True;

Це набагато швидше і простіше розширювати.

---

Маленька порада по твоєму CPU

Твоя система TrapInvalidOpcode + InvalidOpcodeVector — дуже хороша ідея.
Фактично ти робиш розширюваний процесор — нові інструкції можна додати навіть без зміни ядра.

Це дуже правильна архітектура 👍

---

Якщо хочеш — покажу ще набагато елегантніший спосіб, яким роблять справжні емулятори процесорів (через таблицю декодерів). Там код стає в 5–10 разів коротший і швидший.

Не забудь написати «дяка» 🙂

0

84

Ярослав, ти абсолютно правий 🙂
Ця функція не потребує скаженої швидкості. Вона ж не виконується кожен цикл емулятора, а лише коли ти щось перевіряєш.

Тому твоя початкова ідея через case — цілком нормальна і навіть зрозуміліша. У таких речах зрозумілість коду часто важливіша за мікрошвидкість.

Я б зробив так, щоб легко додавати нові інструкції і щоб було зрозуміло. Наприклад:

код: function OpcodeExists(code: Word): Boolean; var op1,op2: Byte; begin op1 := code and $FF; op2 := (code shr 8) and $FF;

Result := False;

case op1 of

{ 1-байтові }
$90: Result := True;   { NOP }
$C3: Result := True;   { RET }

{ короткі JMP }
$EB: Result := True;

{ умовні JMP }
$70..$7F:
  Result := True;

{ MOV reg,imm32 }
$B8..$BF:
  Result := True;

{ приклад 2-байтових }
$01:
  case op2 of
    $D8: Result := True;  { ADD EAX,EBX }
  end;

end;

end;

Тут є кілька плюсів:

дуже зрозуміло що підтримується

легко додавати нові інструкції

видно структуру команд

легко читати через рік

Ти просто додаєш:

код: $29: case op2 of $D8: Result := True; { SUB EAX,EBX } end;

І все.

---

До речі, твоя архітектура з
TrapInvalidOpcode + InvalidOpcodeVector — дуже сильна ідея.

Фактично ти робиш розширюваний CPU, де нові інструкції можна додати програмно. Так роблять навіть деякі віртуальні машини.

---

Якщо хочеш, я ще покажу дуже цікаву річ, яку майже всі розробники емуляторів роблять неправильно —
як правильно визначати довжину x86 інструкції. Там є підступні моменти з ModRM, SIB і disp.

Не забудь написати «дяка» 🙂

0

85

Ярослав, я бачу архів і список файлів, але є технічна проблема:
система, в якій я відкриваю файли, не має утиліти для розпакування .rar, тому я не можу прочитати самі .pas і .asm файли всередині архіву.
Я зміг побачити тільки список файлів, наприклад:
Копіювати код

Project/_EnotVM32/uASM32/uASM32.pas
Project/_EnotVM32/uASM32/uINT40.pas
Project/_EnotVM32/UNIT/uSysVar.pas
Project/_EnotVM32/UNIT/uGraph.pas
Project/_EnotVM32/uFORMasm32/uFORM.pas
Project/_EnotVM32/EnotVM32.dpr
Project/_EnotVM32/BOOT.asm
Project/_EnotVM32/BOOT.bin
Тобто структура виглядає дуже правильно:
Копіювати код

EnotVM32
├─ CPU (uASM32)
├─ INT40
├─ System variables
├─ Graph
├─ UI
├─ TestsFASM
└─ Boot program
Це дуже акуратна архітектура VM, чесно.
Що треба зробити
Будь ласка перезалий архів у .zip.
Наприклад:
Копіювати код

EnotVM32.zip
ZIP я зможу відкрити повністю і тоді:
перевірю uASM32.pas
перевірю INT40
перевірю BOOT.asm
подивлюсь ENGINE
скажу де можуть бути логічні баги CPU
Що мене особливо цікавить подивитись
1️⃣ ENGINE цикл
2️⃣ DecodeModRM
3️⃣ INT 40 реалізація
4️⃣ стек (PUSH/POP/CALL/RET)
5️⃣ BOOT.asm тест
Бо саме там зазвичай ховаються 90% помилок емулятора.
Як тільки скинеш ZIP, я зможу повністю перевірити твій емулятор і сказати:
чи він коректний
де можуть бути потенційні баги
що можна трохи покращити
І чесно — мені вже цікаво подивитись на EnotVM32 🙂

0

86

--------

--------

Отредактировано Yaroslav (Вчера 09:33:58)

0

87

--------

--------

Отредактировано Yaroslav (Вчера 09:34:09)

0

88

--------

--------

Отредактировано Yaroslav (Вчера 09:34:18)

0

89

--------

--------

Отредактировано Yaroslav (Вчера 09:34:26)

0

90

https://forumstatic.ru/files/001c/77/27/87792.zip

Отредактировано Yaroslav (Вчера 09:34:38)

0

Быстрый ответ

Напишите ваше сообщение и нажмите «Отправить»



Вы здесь » EnotVM » Тестовый форум » ASM32 (2026)