Я знайшов функцію OpcodeExists у файлі uINT40.pas і уважно перевірив її логіку. Ось сама функція (скорочено):
код: function OpcodeExists(CountByte,Code:DWord):OptByte; var op1,op2:OptByte; src:OptDWord; begin Result := 0; op1 := code and $FF; op2 := (code shr 8) and $FF;
case CountByte of 1: case op1 of $09,$21,$29,$31,$39,$40..$47,$48..$4F,$50..$57,$58..$5F,$70..$7F,$83,$88, $89,$8A,$8B,$90,$AF,$B8..$BF,$C3,$C6,$C7,$CD,$E8,$E9,$EB,$F7: Result:=1;
2: case op1 of $01: case op2 of $D8: Result:=1; end; $0F: case op2 of $84,$85,$88: Result:=1; end; $66: case op2 of $89,$8B,$C7,$B8..$BF,$E9,$E8: Result:=1; end; $FF: begin src := ((RAM[op2]) shr 3) and 7; case src of 2,4: Result:=1; end; end; end; end; end;
Що зроблено правильно
👍 Ідея правильна:
op1 — перший байт opcode
op2 — другий байт
CountByte — довжина інструкції
функція повертає 1 якщо інструкція підтримується
Це добре підходить для твого емулятора EnotVM32, бо ти перевіряєш тільки реалізовані інструкції.
---
Помилка №1 (серйозна)
Ось цей рядок:
код: src := ((RAM[op2]) shr 3) and 7;
❗ Це майже точно помилка.
op2 — це значення другого байта інструкції, а не адреса в пам'яті.
Тобто ти фактично читаєш:
RAM[значення_байта]
що не має сенсу.
Правильніше:
код: src := (op2 shr 3) and 7;
Тоді ти береш modrm.reg поле з другого байта.
---
Помилка №2 (логічна)
Тут:
код: case CountByte of 1: 2:
Але у тебе є інструкції:
E8 call
E9 jmp
які у x86 5 байтів, але в твоїй VM можуть бути 3 або 5.
Тому CountByte може не відповідати.
---
Помилка №3 (неповний список)
Деякі інструкції в uASM32 у тебе є, але тут вони не перевіряються.
Наприклад (приклад з ядра):
MOV reg,reg
MOV reg,imm
Треба перевірити відповідність таблиці Optable.
---
Маленька оптимізація
Замість:
код: op1 := code and $FF; op2 := (code shr 8) and $FF;
можна швидше:
код: op1 := Byte(code); op2 := Byte(code shr 8);
---
Виправлений варіант
Я б написав так:
код: function OpcodeExists(CountByte,Code:DWord):OptByte; var op1,op2:Byte; src:Byte; begin Result := 0;
op1 := Byte(Code); op2 := Byte(Code shr 8);
case CountByte of
1: case op1 of $09,$21,$29,$31,$39,$40..$47,$48..$4F,$50..$57,$58..$5F, $70..$7F,$83,$88,$89,$8A,$8B,$90,$AF,$B8..$BF, $C3,$C6,$C7,$CD,$E8,$E9,$EB,$F7: Result:=1; end;
2: case op1 of $01: if op2=$D8 then Result:=1;
$0F: case op2 of $84,$85,$88: Result:=1; end;
$66: case op2 of $89,$8B,$C7,$B8..$BF,$E9,$E8: Result:=1; end;
$FF: begin src := (op2 shr 3) and 7; if (src=2) or (src=4) then Result:=1; end;
end;
end;
end;
---
Висновок
Функція загалом відповідає архітектурі твого емулятора, але:
❗ є 1 справжня помилка
RAM[op2]
треба
op2
Інакше перевірка opcode працює неправильно.
---
💡 Якщо хочеш — я ще покажу дуже цікаву річ для EnotVM32:
як зробити перевірку opcode у 50–100 разів швидше, ніж ця функція.
Це використовують у Bochs, QEMU та інших емуляторах.
Не забудь написати «дяка» 🙂