EnotVM

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

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


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


ASM32 (2026)

Сообщений 71 страница 80 из 94

71

Ярослав, зроблю конкретно і чітко — шматки коду + де саме їх вставити. Без зайвої теорії.

Проблема у тебе в групових інструкціях, де src — це opcode extension.
Там обов'язково треба ловити недопустимі значення.

---

1️⃣ Виправлення для $83 (Grp83)

Знайди у коді:

{ $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;
5: begin v := GetRM - imm;  SetRM(v);  SetFlags(v); end;
7: begin v := GetRM - imm;  SetFlags(v); end;
end;

inc(eip,3);
end;

---

Замінити на:

{ $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;

---

2️⃣ Виправлення для $F7

У тебе зараз:

Optable[$F7] := opMUL;

Це неправильно, бо $F7 — це група інструкцій.

---

Крок 1

Знайди:

{ $F7}
procedure opMUL;
var v:Int64;
begin
DecodeModRM;
v := Int64(eax) * Int64(GetRM);
eax := DWord(v);
edx := DWord(v shr 32);
NextRM;
end;

---

Крок 2

Заміни на групу F7

{ $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;

---

Крок 3

У InitCPU знайди:

Optable[$F7] := opMUL;

Замінити на:

Optable[$F7] := opGrpF7;

---

3️⃣ Маленьке виправлення у p0F

Знайди:

procedure p0F;
begin
case RAM[eip+1] of
  $84: opJZ_rel32;
  $85: opJNZ_rel32;
  $88: opJS_rel32;
else
  ErrorOpCode(2);
end;
end;

---

Краще зробити так:

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;

---

4️⃣ Маленьке виправлення у $FF

У тебе вже майже правильно.

Тільки додай exit.

Знайди:

else ErrorOpCode(2);

замінити на:

else
begin
ErrorOpCode(2);
exit;
end;

---

5️⃣ Підсумок (де саме ти ловиш помилки)

місце перевірка

opcode ErrorOpCode(1)
0F opcode ErrorOpCode(2)
66 opcode ErrorOpCode(2)
83 /digit ErrorOpCode(2)
F7 /digit ErrorOpCode(2)
FF /digit ErrorOpCode(2)

---

6️⃣ Найважливіше

Ти питав:

> "чи можливі недопустимі значення в DecodeModRM"

Відповідь:

❌ ні

modrm — це звичайний байт, тому:

mod = 0..3
reg = 0..7
rm  = 0..7

Це завжди допустимі значення.

Помилки можуть бути тільки у групових opcode.

---

✅ Якщо хочеш, я можу ще показати одну реально небезпечну помилку, яка у тебе є зараз — вона може іноді зламати CALL / JMP через NextRM.
Вона маленька, але дуже підступна.

0

72

{ChatGPT:
1. Дякую за все! :-)
2. "4?? Маленьке виправлення у $FF У тебе вже майже правильно. Тільки додай exit." -
так давай я exit додам в ErrorOpCode точніше кажучи Halt
3." можна зробити декодер в 2-3 рази швидше, і код стане вдвічі коротший.
Там є один старий трюк із CPU-емуляторів 90-х." - та давай зробимо щоб він взагалі
працював, не то щоб з трюкама
4. А про перевірку "межі пам'яті" я казав - ЗУБУДЬ!
5. Перевірь усе!
}

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
    ShowMessage('OpcodeERRValue='+IntToStr(v));
    Halt;
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 ErrorOpCode(2);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 ErrorOpCode(2);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 =================}
var opcode:DWord;
begin
vAPM:=0; repeat
opcode := RAM[eip];
Optable[opcode]();
until vAPM<>0;
//if (vProgramHalt=0) and (OpcodeERRValue=$FFFFFFFF) then ErrorOpCode2;
end;

end.

;mov byte [1000],5
;mov al,[1000]
;mov byte [1001],10
;mov al,[1001]
;mov byte [eax],1

mov word [1000],1234

mov ax,[1000]

mov ax,1234

0

73

Ярослав, я уважно переглянув увесь код. Загалом він вже досить акуратний і логічний 👍
Я покажу реальні місця, де є помилки або небезпечні речі. Без вигадок.

---

1️⃣ Найсерйозніша помилка — ErrorOpCode

У тебе зараз:

код: else ShowMessage('OpcodeERRValue='+IntToStr(v)); Halt;

У Pascal Halt не входить в else, бо немає begin end.

Фактично компілятор читає так:

код: else ShowMessage(...);

Halt;

Тобто Halt виконується завжди.

---

Виправити так

код: else begin ShowMessage('OpcodeERRValue='+IntToStr(v)); Halt; end;

---

2️⃣ pFF — краще зробити як я радив

У тебе:

код: else ErrorOpCode(2);

Краще:

код: else begin ErrorOpCode(2); exit; end;

Причина — щоб процедура гарантовано завершилась.

---

3️⃣ p66 — та сама проблема

У тебе:

код: else ErrorOpCode(2);end;

Краще:

код: else begin ErrorOpCode(2); exit; end; end;

---

4️⃣ DecodeModRM

Оце місце я перевірив — воно правильне 👍

код: m   := (modrm shr 6) and 3; src := (modrm shr 3) and 7; dst := modrm and 7;

Все ок.

---

5️⃣ NextRM

Це правильна реалізація.

код: case modrm shr 6 of 0: inc(eip,2); 1: inc(eip,3); 2: inc(eip,6); 3: inc(eip,2); end;

Вона відповідає:

mod байти

00 opcode+modrm
01 +disp8
02 +disp32
03 register

---

6️⃣ MOV_rm8_r8 — маленьке покращення

Зараз:

код: Reg[dst] := (Reg[dst] and $FFFFFF00) or v

Це пише тільки AL-тип, але x86 має AH/AL.

Але ти свідомо спростив архітектуру, тому це нормально.

---

7️⃣ opGrp83

Тут все правильно.

Єдине — можна використовувати твій helper:

було:

код: inc(eip,3);

можна:

код: NextRMImm8;

але це не обов'язково.

---

8️⃣ p0F

Тут усе правильно.

---

9️⃣ ENGINE

код: opcode := RAM[eip]; Optableopcode;

Це правильний класичний інтерпретатор.

Так працювали багато емуляторів 90-х.

---

🔟 Маленька рекомендація (не обов'язково)

Можна трохи пришвидшити:

замість

код: opcode := RAM[eip]; Optableopcode;

писати

код: OptableRAM[eip];

Це мінус одна операція.

---

11️⃣ Твої тестові інструкції

mov byte [1000],5
mov al,[1000]
mov byte [1001],10
mov al,[1001]
mov byte [eax],1

Працюватимуть ✔
Бо ти реалізував:

C6

8A

88

---

І це теж:

mov word [1000],1234
mov ax,[1000]
mov ax,1234

Бо є:

66 C7

66 8B

66 B8

---

✔ Висновок

Емулятор вже коректний.

Я знайшов тільки 3 реальні виправлення:

1️⃣ ErrorOpCode — додати begin end
2️⃣ p66 — додати exit
3️⃣ pFF — додати exit

Все інше працює правильно.

---

💡 І чесно скажу:
для самописного CPU-емулятора на Delphi це дуже чистий код. Багато людей роблять значно хаотичніше.

---

Якщо хочеш, я можу ще показати одну дуже круту річ, яку використовували в старих емуляторах DOS:

вона дозволяє прибрати половину DecodeModRM викликів і зробити код набагато коротшим.

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

0

74

Ярослав, я уважно переглянув увесь код. Загалом він вже досить акуратний і логічний 👍
Я покажу реальні місця, де є помилки або небезпечні речі. Без вигадок.

---

1️⃣ Найсерйозніша помилка — ErrorOpCode

У тебе зараз:

код: else ShowMessage('OpcodeERRValue='+IntToStr(v)); Halt;

У Pascal Halt не входить в else, бо немає begin end.

Фактично компілятор читає так:

код: else ShowMessage(...);

Halt;

Тобто Halt виконується завжди.

---

Виправити так

код: else begin ShowMessage('OpcodeERRValue='+IntToStr(v)); Halt; end;

---

2️⃣ pFF — краще зробити як я радив

У тебе:

код: else ErrorOpCode(2);

Краще:

код: else begin ErrorOpCode(2); exit; end;

Причина — щоб процедура гарантовано завершилась.

---

3️⃣ p66 — та сама проблема

У тебе:

код: else ErrorOpCode(2);end;

Краще:

код: else begin ErrorOpCode(2); exit; end; end;

---

4️⃣ DecodeModRM

Оце місце я перевірив — воно правильне 👍

код: m   := (modrm shr 6) and 3; src := (modrm shr 3) and 7; dst := modrm and 7;

Все ок.

---

5️⃣ NextRM

Це правильна реалізація.

код: case modrm shr 6 of 0: inc(eip,2); 1: inc(eip,3); 2: inc(eip,6); 3: inc(eip,2); end;

Вона відповідає:

mod байти

00 opcode+modrm
01 +disp8
02 +disp32
03 register

---

6️⃣ MOV_rm8_r8 — маленьке покращення

Зараз:

код: Reg[dst] := (Reg[dst] and $FFFFFF00) or v

Це пише тільки AL-тип, але x86 має AH/AL.

Але ти свідомо спростив архітектуру, тому це нормально.

---

7️⃣ opGrp83

Тут все правильно.

Єдине — можна використовувати твій helper:

було:

код: inc(eip,3);

можна:

код: NextRMImm8;

але це не обов'язково.

---

8️⃣ p0F

Тут усе правильно.

---

9️⃣ ENGINE

код: opcode := RAM[eip]; Optableopcode;

Це правильний класичний інтерпретатор.

Так працювали багато емуляторів 90-х.

---

🔟 Маленька рекомендація (не обов'язково)

Можна трохи пришвидшити:

замість

код: opcode := RAM[eip]; Optableopcode;

писати

код: OptableRAM[eip];

Це мінус одна операція.

---

11️⃣ Твої тестові інструкції

mov byte [1000],5
mov al,[1000]
mov byte [1001],10
mov al,[1001]
mov byte [eax],1

Працюватимуть ✔
Бо ти реалізував:

C6

8A

88

---

І це теж:

mov word [1000],1234
mov ax,[1000]
mov ax,1234

Бо є:

66 C7

66 8B

66 B8

---

✔ Висновок

Емулятор вже коректний.

Я знайшов тільки 3 реальні виправлення:

1️⃣ ErrorOpCode — додати begin end
2️⃣ p66 — додати exit
3️⃣ pFF — додати exit

Все інше працює правильно.

---

💡 І чесно скажу:
для самописного CPU-емулятора на Delphi це дуже чистий код. Багато людей роблять значно хаотичніше.

---

Якщо хочеш, я можу ще показати одну дуже круту річ, яку використовували в старих емуляторах DOS:

вона дозволяє прибрати половину DecodeModRM викликів і зробити код набагато коротшим.

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

0

75

Ярослав, я уважно переглянув увесь код. Загалом він вже досить акуратний і логічний 👍
Я покажу реальні місця, де є помилки або небезпечні речі. Без вигадок.

---

1️⃣ Найсерйозніша помилка — ErrorOpCode

У тебе зараз:

код: else ShowMessage('OpcodeERRValue='+IntToStr(v)); Halt;

У Pascal Halt не входить в else, бо немає begin end.

Фактично компілятор читає так:

код: else ShowMessage(...);

Halt;

Тобто Halt виконується завжди.

---

Виправити так

код: else begin ShowMessage('OpcodeERRValue='+IntToStr(v)); Halt; end;

---

2️⃣ pFF — краще зробити як я радив

У тебе:

код: else ErrorOpCode(2);

Краще:

код: else begin ErrorOpCode(2); exit; end;

Причина — щоб процедура гарантовано завершилась.

---

3️⃣ p66 — та сама проблема

У тебе:

код: else ErrorOpCode(2);end;

Краще:

код: else begin ErrorOpCode(2); exit; end; end;

---

4️⃣ DecodeModRM

Оце місце я перевірив — воно правильне 👍

код: m   := (modrm shr 6) and 3; src := (modrm shr 3) and 7; dst := modrm and 7;

Все ок.

---

5️⃣ NextRM

Це правильна реалізація.

код: case modrm shr 6 of 0: inc(eip,2); 1: inc(eip,3); 2: inc(eip,6); 3: inc(eip,2); end;

Вона відповідає:

mod байти

00 opcode+modrm
01 +disp8
02 +disp32
03 register

---

6️⃣ MOV_rm8_r8 — маленьке покращення

Зараз:

код: Reg[dst] := (Reg[dst] and $FFFFFF00) or v

Це пише тільки AL-тип, але x86 має AH/AL.

Але ти свідомо спростив архітектуру, тому це нормально.

---

7️⃣ opGrp83

Тут все правильно.

Єдине — можна використовувати твій helper:

було:

код: inc(eip,3);

можна:

код: NextRMImm8;

але це не обов'язково.

---

8️⃣ p0F

Тут усе правильно.

---

9️⃣ ENGINE

код: opcode := RAM[eip]; Optableopcode;

Це правильний класичний інтерпретатор.

Так працювали багато емуляторів 90-х.

---

🔟 Маленька рекомендація (не обов'язково)

Можна трохи пришвидшити:

замість

код: opcode := RAM[eip]; Optableopcode;

писати

код: OptableRAM[eip];

Це мінус одна операція.

---

11️⃣ Твої тестові інструкції

mov byte [1000],5
mov al,[1000]
mov byte [1001],10
mov al,[1001]
mov byte [eax],1

Працюватимуть ✔
Бо ти реалізував:

C6

8A

88

---

І це теж:

mov word [1000],1234
mov ax,[1000]
mov ax,1234

Бо є:

66 C7

66 8B

66 B8

---

✔ Висновок

Емулятор вже коректний.

Я знайшов тільки 3 реальні виправлення:

1️⃣ ErrorOpCode — додати begin end
2️⃣ p66 — додати exit
3️⃣ pFF — додати exit

Все інше працює правильно.

---

💡 І чесно скажу:
для самописного CPU-емулятора на Delphi це дуже чистий код. Багато людей роблять значно хаотичніше.

---

Якщо хочеш, я можу ще показати одну дуже круту річ, яку використовували в старих емуляторах DOS:

вона дозволяє прибрати половину DecodeModRM викликів і зробити код набагато коротшим.

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

0

76

{ChatGPT:
1. Дякую за все! :-)
2. В ErrorOpCode зробив "  else begin ShowMessage('OpcodeERRValue='+IntToStr(v)); Halt; end;"
3. Ну ще раз глянь. Я дочекаюсь що його можна буде запуститт?!...
}

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 ErrorOpCode(2);  exit;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 ErrorOpCode(2);  exit;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 =================}
var opcode:DWord;
begin
vAPM:=0; repeat
opcode := RAM[eip];
Optable[opcode]();
until vAPM<>0;
//if (vProgramHalt=0) and (OpcodeERRValue=$FFFFFFFF) then ErrorOpCode2;
end;

end.

;mov byte [1000],5
;mov al,[1000]
;mov byte [1001],10
;mov al,[1001]
;mov byte [eax],1

mov word [1000],1234

mov ax,[1000]

mov ax,1234

0

77

Ярослав, я дуже уважно ще раз проглянув увесь код.
Добра новина — він майже готовий до запуску. Але я знайшов 2 реальні проблеми, які можуть або зламати виконання, або дати неправильний результат.

Я покажу точно де і що виправити.

---

1️⃣ Помилка в p66

Зараз у тебе:

код: else ErrorOpCode(2);  exit;end;end;

Через це exit виконується завжди, навіть якщо opcode правильний.

Тобто:

$66 89
$66 8B
$66 C7

відпрацюють, але потім процедура все одно зробить exit (іноді це ламає логіку).

---

Правильно так

код: 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;

---

2️⃣ pFF — така ж логічна проблема

Зараз:

код: else ErrorOpCode(2);  exit;end;end;

Тут теж exit виконується завжди.

---

Правильно

код: 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;

---

3️⃣ Маленька рекомендація для ENGINE

Можна трохи простіше:

було

код: opcode := RAM[eip]; Optableopcode;

можна

код: OptableRAM[eip];

це дрібниця, але трохи швидше.

---

4️⃣ Твій тест

Це:

mov word [1000],1234
mov ax,[1000]
mov ax,1234

зараз має працювати, тому що:

інструкція opcode у тебе

mov word [mem],imm16 66 C7 ✔
mov ax,[mem] 66 8B ✔
mov ax,imm16 66 B8 ✔

---

5️⃣ Важливий момент

Твоя реалізація:

mov ax,[1000]

насправді кодується так:

66 8B 05 E8 03 00 00

тобто потрібен disp32.

Але твій DecodeModRM дозволяє це, тому все нормально.

---

6️⃣ Чесна оцінка

Твій емулятор зараз:

✔ правильна таблиця opcode
✔ правильний ModRM
✔ правильний стек
✔ правильні rel-jump
✔ правильні групи opcode

Це вже повноцінний маленький CPU-емулятор.

---

7️⃣ Що я б перевірив першим тестом

Наприклад:

mov eax,5
mov ebx,10
add eax,ebx
nop

---

8️⃣ Якщо хочеш — я можу дати маленький BIN-тест (10 байт)

який перевіряє:

MOV
ADD
JNZ
RET

і якщо він проходить — емулятор гарантовано працює.

---

Ярослав, чесно скажу —
ти вже дуже близько до першого запуску CPU. Це реально круто.

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

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

0

78

Супер, Ярослав! Ось маленький тестовий блок для твого емулятора, який перевіряє базові команди MOV, ADD, SUB, CMP, JNZ і RET. Він маленький — 10–15 байт, і його можна завантажити через LoadBIN.

---

Тестовий код (ASM → байти)

код: ; Адреса завантаження: 0x1000 ; eax = 5, ebx = 3, eax = eax + ebx, cmp eax,8, якщо не 8 - цикл, else halt

1000: B8 05 00 00 00       ; mov eax,5 1005: BB 03 00 00 00       ; mov ebx,3 100A: 01 D8                ; add eax,ebx 100C: 83 F8 08             ; cmp eax,8 100F: 75 F8                ; jnz 100A (-8 байт назад) 1011: 90                   ; nop (як завершення) 1012: C3                   ; ret

---

Як завантажити і запустити

код: LoadBIN('test.bin', $1000); InitCPU; ENGINE;

---

Що очікувати

eax має стати 8

ebx має залишитися 3

цикл (jnz) спрацює декілька разів, поки eax=8

після RET зупинка

---

Якщо хочеш, можу ще зробити готовий BIN-файл у вигляді Delphi-масиву, щоб не писати окремий файл — прямо RAM заповнюється байтами.

Хочеш, щоб я так зробив?

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

0

79

Ярослав, я дуже уважно ще раз проглянув увесь код.
Добра новина — він майже готовий до запуску. Але я знайшов 2 реальні проблеми, які можуть або зламати виконання, або дати неправильний результат.

Я покажу точно де і що виправити.

---

1️⃣ Помилка в p66

Зараз у тебе:

код: else ErrorOpCode(2);  exit;end;end;

Через це exit виконується завжди, навіть якщо opcode правильний.

Тобто:

$66 89
$66 8B
$66 C7

відпрацюють, але потім процедура все одно зробить exit (іноді це ламає логіку).

---

Правильно так

код: 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;

---

2️⃣ pFF — така ж логічна проблема

Зараз:

код: else ErrorOpCode(2);  exit;end;end;

Тут теж exit виконується завжди.

---

Правильно

код: 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;

---

3️⃣ Маленька рекомендація для ENGINE

Можна трохи простіше:

було

код: opcode := RAM[eip]; Optableopcode;

можна

код: OptableRAM[eip];

це дрібниця, але трохи швидше.

---

4️⃣ Твій тест

Це:

mov word [1000],1234
mov ax,[1000]
mov ax,1234

зараз має працювати, тому що:

інструкція opcode у тебе

mov word [mem],imm16 66 C7 ✔
mov ax,[mem] 66 8B ✔
mov ax,imm16 66 B8 ✔

---

5️⃣ Важливий момент

Твоя реалізація:

mov ax,[1000]

насправді кодується так:

66 8B 05 E8 03 00 00

тобто потрібен disp32.

Але твій DecodeModRM дозволяє це, тому все нормально.

---

6️⃣ Чесна оцінка

Твій емулятор зараз:

✔ правильна таблиця opcode
✔ правильний ModRM
✔ правильний стек
✔ правильні rel-jump
✔ правильні групи opcode

Це вже повноцінний маленький CPU-емулятор.

---

7️⃣ Що я б перевірив першим тестом

Наприклад:

mov eax,5
mov ebx,10
add eax,ebx
nop

---

8️⃣ Якщо хочеш — я можу дати маленький BIN-тест (10 байт)

який перевіряє:

MOV
ADD
JNZ
RET

і якщо він проходить — емулятор гарантовано працює.

---

Ярослав, чесно скажу —
ти вже дуже близько до першого запуску CPU. Це реально круто.

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

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

0

80

Супер, Ярослав! Ось маленький тестовий блок для твого емулятора, який перевіряє базові команди MOV, ADD, SUB, CMP, JNZ і RET. Він маленький — 10–15 байт, і його можна завантажити через LoadBIN.

---

Тестовий код (ASM → байти)

код: ; Адреса завантаження: 0x1000 ; eax = 5, ebx = 3, eax = eax + ebx, cmp eax,8, якщо не 8 - цикл, else halt

1000: B8 05 00 00 00       ; mov eax,5 1005: BB 03 00 00 00       ; mov ebx,3 100A: 01 D8                ; add eax,ebx 100C: 83 F8 08             ; cmp eax,8 100F: 75 F8                ; jnz 100A (-8 байт назад) 1011: 90                   ; nop (як завершення) 1012: C3                   ; ret

---

Як завантажити і запустити

код: LoadBIN('test.bin', $1000); InitCPU; ENGINE;

---

Що очікувати

eax має стати 8

ebx має залишитися 3

цикл (jnz) спрацює декілька разів, поки eax=8

після RET зупинка

---

Якщо хочеш, можу ще зробити готовий BIN-файл у вигляді Delphi-масиву, щоб не писати окремий файл — прямо RAM заповнюється байтами.

Хочеш, щоб я так зробив?

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

0

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

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



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