Окей, Ярослав, давай по порядку, щоб усе стало зрозуміло і компактно.
---
1. SUB r32,imm32 та CMP r32,imm32 у case (код у касі)
Ці інструкції у x86 формуються через opcode $81 + ModRM:
81 /5 → SUB r32,imm32
81 /7 → CMP r32,imm32
Реалізація:
код:
{SUB r32,imm32} $81: begin modrm := RAM[eip+1]; if ((modrm shr 3) and 7)=5 then begin dst := modrm and 7; Reg[dst] := Reg[dst] - ReadDWord(eip+2); pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0; inc(eip,6); // 1 байт opcode + 1 байт modrm + 4 байти imm32 end; end;
{CMP r32,imm32} $81: begin modrm := RAM[eip+1]; if ((modrm shr 3) and 7)=7 then begin dst := modrm and 7; pZF := (Reg[dst]-ReadDWord(eip+2))=0; pSF := ((Reg[dst]-ReadDWord(eip+2)) and $80000000)<>0; inc(eip,6); end; end;
> Тобто $81 – загальний код, ModRM визначає конкретну операцію (/0 ADD, /5 SUB, /7 CMP).
---
2. Чому старі закоментовані $8B/ $89 різні від нових
Раніше ти робив окремо MOV r32,[reg] і MOV r32,[reg+disp8].
Але справжня x86 логіка така:
$89 → MOV r/m32,r32
$8B → MOV r32,r/m32
Тобто opcode + ModRM визначають, що конкретно робити.
У новому варіанті ти об’єднав все через ModRM:
якщо mod = 11b → регістр-регістр
інакше → доступ до пам’яті
Тому старі закоментовані рядки — просто “розбиті по частинах” для наочності.
---
3. 83 /0 ADD r32,imm8, 83 /5 SUB r32,imm8, 83 /7 CMP r32,imm8
Теж через ModRM (opcode $83) — короткий immediate (1 байт):
код:
{ADD/SUB/CMP r32,imm8} $83: begin modrm := RAM[eip+1]; dst := modrm and 7; case (modrm shr 3) and 7 of 0: begin // ADD r32,imm8 Reg[dst] := Reg[dst] + ShortInt(RAM[eip+2]); pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0; inc(eip,3); end; 5: begin // SUB r32,imm8 Reg[dst] := Reg[dst] - ShortInt(RAM[eip+2]); pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0; inc(eip,3); end; 7: begin // CMP r32,imm8 pZF := (Reg[dst]-ShortInt(RAM[eip+2]))=0; pSF := ((Reg[dst]-ShortInt(RAM[eip+2])) and $80000000)<>0; inc(eip,3); end; end; end;
> ShortInt тут важливий, бо immediate — 1 байт зі знаком.
---
4. Куди вставляти WriteDWord
Процедуру WriteDWord вставляй перед Engine, разом з ReadDWord.
код:
procedure ReadDWord(addr:DWord):DWord; begin ReadDWord := RAM[addr] or (RAM[addr+1] shl 8) or (RAM[addr+2] shl 16) or (RAM[addr+3] shl 24); end;
procedure WriteDWord(addr,val:DWord); begin RAM[addr] := val; RAM[addr+1] := val shr 8; RAM[addr+2] := val shr 16; RAM[addr+3] := val shr 24; end;
А далі заміни в PUSH/POP, MOV [reg] та CALL/RET на WriteDWord / ReadDWord — код стане чистішим.
Наприклад:
код:
{PUSH r32} $50..$57: begin dst := opcode and 7; Dec(esp,4); WriteDWord(esp, Reg[dst]); inc(eip); end;
{POP r32} $58..$5F: begin dst := opcode and 7; Reg[dst] := ReadDWord(esp); Inc(esp,4); inc(eip); end;
---
Якщо хочеш, я можу прямо переписати твій поточний Engine повністю з $81, $83, $89, $8B, $B8-$BF, PUSH/POP, MOVSX/MOVZX + ModRM, щоб все працювало чисто, без дублікатів і закоментованих рядків, і він буде <500 рядків.
Це буде максимально компактний базовий 32-бітний емулятор.
Не забудь написати "дяка" 😉