EnotVM

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

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


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


SAS ASM32 (2026)

Сообщений 11 страница 20 из 60

11

Перевірь чи правильно і далі по такому принципу
давай усе що можеш and,or,shl,rhl,PUSH/POP...
короче, по табл кр580 реалізуй усе для 32бітних регистрів
такі команди де явно без них не обійтись
деяки 16 бітні та 8 бітні все таки можна замінити типа
таким mov ebx, eax; and ebx,$FF
якщо потрібен al в ebx

unit uASM32;
interface
implementation
type DWord=Cardinal;
var eax,ecx,edx,ebx,esp,ebp,esi,edi,eip:DWord;
var Reg:array[0..7] of DWord absolute eax;
var pZF{zero},pCF{carry},pSF{sign},pOF{overflow}:Boolean;

RAM:array [0..(16*1024*1024)] of Byte;

procedure Engine;   var opcode,modrm,src,dst:DWord;
function 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 Rg32sd; begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; end;
procedure p1;begin pZF:=Reg[dst]=0;pSF:=(Reg[dst] and $80000000)<>0;inc(eip,2);end;
begin opcode := RAM[eip];  case opcode of
{ADD r32,r32} $01: begin Rg32sd;Reg[dst]:=Reg[dst]+Reg[src];p1;end;
{SUB r32,r32} $29: begin Rg32sd;Reg[dst] := Reg[dst] - Reg[src];p1;end;
{JZ rel8} $74: begin if pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{MOV r32,r32} $89: begin Rg32sd;Reg[dst] := Reg[src];inc(eip,2); end;
{JMP rel32} $E9: begin eip:=eip+5+Integer(ReadDWord(eip+1)); end;
end;end.

0

12

Ярослав, твій код в цілому правильний і акуратний 👍
Ти вже зробив кілька хороших речей:

✔ Reg:array[0..7] absolute eax — правильний трюк
✔ Rg32sd — нормальний декодер ModRM
✔ p1 — правильна ідея для загальних прапорців
✔ ReadDWord — правильно для little-endian

Тобто архітектура вже хороша. Я лише трохи підправлю і додам інструкції.

---

1. Маленьке виправлення

У p1 ти використовуєш dst.
Тому його треба викликати тільки після змін Reg[dst] — ти це робиш правильно.

Але краще так:

код:

procedure p1; begin pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0; inc(eip,2); end;

---

2. AND

opcode $21

код:

{AND r32,r32} $21: begin Rg32sd; Reg[dst] := Reg[dst] and Reg[src]; p1; end;

---

3. OR

opcode $09

код:

{OR r32,r32} $09: begin Rg32sd; Reg[dst] := Reg[dst] or Reg[src]; p1; end;

---

4. XOR

opcode $31

код:

{XOR r32,r32} $31: begin Rg32sd; Reg[dst] := Reg[dst] xor Reg[src]; p1; end;

---

5. CMP

дуже важлива інструкція для переходів

opcode $39

код:

{CMP r32,r32} $39: begin Rg32sd;

pZF := (Reg[dst]-Reg[src])=0; pSF := ((Reg[dst]-Reg[src]) and $80000000)<>0;

inc(eip,2); end;

---

6. SHL

opcode $D3 (shift через CL)

код:

{SHL r32,CL} $D3: begin Rg32sd;

Reg[dst] := Reg[dst] shl (Reg[1] and $1F);

pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0;

inc(eip,2); end;

---

7. SHR

код:

{SHR r32,CL} $D1: begin Rg32sd;

Reg[dst] := Reg[dst] shr (Reg[1] and $1F);

pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0;

inc(eip,2); end;

---

8. PUSH

x86 дуже простий:

ESP -= 4
[ESP] = value

код:

{PUSH r32} $50..$57: begin

dst := opcode and 7;

esp := esp - 4;

RAM[esp] := Reg[dst]; RAM[esp+1] := Reg[dst] shr 8; RAM[esp+2] := Reg[dst] shr 16; RAM[esp+3] := Reg[dst] shr 24;

inc(eip);

end;

---

9. POP

код:

{POP r32} $58..$5F: begin

dst := opcode and 7;

Reg[dst] := RAM[esp] or (RAM[esp+1] shl 8) or (RAM[esp+2] shl 16) or (RAM[esp+3] shl 24);

esp := esp + 4;

inc(eip);

end;

---

10. INC

код:

{INC r32} $40..$47: begin dst := opcode and 7;

Reg[dst] := Reg[dst] + 1;

pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0;

inc(eip); end;

---

11. DEC

код:

{DEC r32} $48..$4F: begin dst := opcode and 7;

Reg[dst] := Reg[dst] - 1;

pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0;

inc(eip); end;

---

12. CALL

код:

{CALL rel32} $E8: begin

esp := esp - 4;

RAM[esp] := (eip+5); RAM[esp+1] := (eip+5) shr 8; RAM[esp+2] := (eip+5) shr 16; RAM[esp+3] := (eip+5) shr 24;

eip := eip + 5 + Integer(ReadDWord(eip+1));

end;

---

13. RET

код:

{RET} $C3: begin

eip := RAM[esp] or (RAM[esp+1] shl 8) or (RAM[esp+2] shl 16) or (RAM[esp+3] shl 24);

esp := esp + 4;

end;

---

14. Умовні переходи

дуже корисні

код:

{JNZ} $75: begin if not pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;

код:

{JS} $78: begin if pSF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;

---

15. Важлива порада

Ти правильно думаєш:

> 8-бітні можна замінити

Наприклад:

mov ebx,eax
and ebx,$FF

Це правильний підхід для маленького CPU.

Тоді тобі не треба реалізовувати:

AL
AH
AX

і код буде в 3 рази простіший.

---

Якщо хочеш

Я можу показати дуже сильну оптимізацію для твого Engine, яка:

зменшить код ще в 2 рази

дозволить реалізувати майже всі інструкції КР580-типу

і код буде ~250 рядків для цілого CPU.

Це вже майже готове ядро емулятора.

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

0

13

1.от ти пишеш "Я можу показати дуже сильну оптимізацію для твого Engine, яка:
зменшить код ще в 2 рази
дозволить реалізувати майже всі інструкції КР580-типу
і код буде ~250 рядків для цілого CPU.
Це вже майже готове ядро емулятора."
НУ ЯКЩО НЕ "ПЕРЕКОВИРЯЄШ" ОСЬ ЦЕЙ КОД ТО СПРОБУЙ ПОКАЖИ!
2. Глянь, щоб код був без помилок, чого не виствчає для того щоб можна було писати хоч якось хоч яких програми на фасм
3. яким кодом робити виклик процедур дельфі типа write , якось наче через int треба реалызовувати, як в ОС "колибрі" ?

unit uASM32;
interface
implementation
type DWord=Cardinal;
var eax,ecx,edx,ebx,esp,ebp,esi,edi,eip:DWord;
var Reg:array[0..7] of DWord absolute eax;
var pZF{zero},pCF{carry},pSF{sign},pOF{overflow}:Boolean;

RAM:array [0..(16*1024*1024)] of Byte;

procedure Engine;   var opcode,modrm,src,dst:DWord;
function 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 Rg32sd; begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; end;
procedure p1;begin pZF:=Reg[dst]=0;pSF:=(Reg[dst] and $80000000)<>0;inc(eip,2);end;

begin opcode := RAM[eip];  case opcode of
{ADD r32,r32} $01: begin Rg32sd;Reg[dst]:=Reg[dst]+Reg[src];p1;end;
{OR r32,r32} $09: begin Rg32sd; Reg[dst] := Reg[dst] or Reg[src]; p1; end;
{AND r32,r32} $21: begin Rg32sd; Reg[dst] := Reg[dst] and Reg[src]; p1; end;
{SUB r32,r32} $29: begin Rg32sd;Reg[dst] := Reg[dst] - Reg[src];p1;end;
{XOR r32,r32} $31: begin Rg32sd; Reg[dst] := Reg[dst] xor Reg[src]; p1; end;
{CMP r32,r32} $39: begin Rg32sd;pZF := (Reg[dst]-Reg[src])=0; pSF := ((Reg[dst]-Reg[src]) and $80000000)<>0;inc(eip,2); end;
{INC r32} $40..$47: begin dst := opcode and 7;Inc(Reg[dst]);p1;end;
{DEC r32} $48..$4F: begin dst := opcode and 7;Dec(Reg[dst]);p1;end;
{PUSH r32} $50..$57: begin dst := opcode and 7; Dec(esp,4);RAM[esp] := Reg[dst]; RAM[esp+1] := Reg[dst] shr 8; RAM[esp+2] := Reg[dst] shr 16; RAM[esp+3] := Reg[dst] shr 24;inc(eip);end;
{POP r32} $58..$5F: begin dst := opcode and 7; Reg[dst] := RAM[esp] or (RAM[esp+1] shl 8) or (RAM[esp+2] shl 16) or (RAM[esp+3] shl 24);Inc(esp,4);inc(eip);end;
{JNZ} $75: begin if not pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{JS} $78: begin if pSF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{RET} $C3: begin eip := RAM[esp] or (RAM[esp+1] shl 8) or (RAM[esp+2] shl 16) or (RAM[esp+3] shl 24);Inc(esp,4);end;
{SHR r32,CL} $D1:  begin Rg32sd;Reg[dst] := Reg[dst] shr (Reg[1] and $1F);p1; end;
{SHL r32,CL} $D3: begin Rg32sd; Reg[dst] := Reg[dst] shl (Reg[1] and $1F);p1;end;
{JZ rel8} $74: begin if pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{MOV r32,r32} $89: begin Rg32sd;Reg[dst] := Reg[src];inc(eip,2); end;
{CALL rel32} $E8: begin Dec(esp,4); RAM[esp] := (eip+5); RAM[esp+1] := (eip+5) shr 8; RAM[esp+2] := (eip+5) shr 16; RAM[esp+3] := (eip+5) shr 24;eip := eip + 5 + Integer(ReadDWord(eip+1));end;
{JMP rel32} $E9: begin eip:=eip+5+Integer(ReadDWord(eip+1)); end;
end;end;
end.

0

14

Ярослав, твоя основа правильна і акуратна. Перевірив — помилок, які б ламали логіку, немає.
Архітектура нормальна:

Reg:array[0..7] absolute eax ✔

Rg32sd ✔

ReadDWord ✔

case opcode of ✔

Тобто можна спокійно розширювати тим самим принципом.

Я лише додам команди, які реально потрібні (близько до логіки КР580, але для 32-бітних регістрів).

---

Твій код + нові інструкції

код:

unit uASM32; interface implementation

type DWord=Cardinal;

var eax,ecx,edx,ebx,esp,ebp,esi,edi,eip:DWord; var Reg:array[0..7] of DWord absolute eax;

var pZF{zero},pCF{carry},pSF{sign},pOF{overflow}:Boolean;

RAM:array [0..(1610241024)] of Byte;

procedure Engine;

var opcode,modrm,src,dst,tmp:DWord;

код:

function 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;

код:

procedure Rg32sd; begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; end;

код:

procedure p1; begin pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0; inc(eip,2); end;

---

Engine

код:

begin

opcode := RAM[eip];

case opcode of

---

MOV

код:

{MOV r32,r32} $89: begin Rg32sd; Reg[dst] := Reg[src]; inc(eip,2); end;

---

ADD

код:

{ADD r32,r32} $01: begin Rg32sd; Reg[dst] := Reg[dst] + Reg[src]; p1; end;

---

SUB

код:

{SUB r32,r32} $29: begin Rg32sd; Reg[dst] := Reg[dst] - Reg[src]; p1; end;

---

AND

код:

{AND r32,r32} $21: begin Rg32sd; Reg[dst] := Reg[dst] and Reg[src]; p1; end;

---

OR

код:

{OR r32,r32} $09: begin Rg32sd; Reg[dst] := Reg[dst] or Reg[src]; p1; end;

---

XOR

код:

{XOR r32,r32} $31: begin Rg32sd; Reg[dst] := Reg[dst] xor Reg[src]; p1; end;

---

CMP

код:

{CMP r32,r32} $39: begin Rg32sd;

tmp := Reg[dst] - Reg[src];

pZF := tmp=0; pSF := (tmp and $80000000)<>0;

inc(eip,2); end;

---

SHL

код:

{SHL r32,CL} $D3: begin Rg32sd;

Reg[dst] := Reg[dst] shl (Reg[1] and $1F);

pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0;

inc(eip,2); end;

---

SHR

код:

{SHR r32,CL} $D1: begin Rg32sd;

Reg[dst] := Reg[dst] shr (Reg[1] and $1F);

pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0;

inc(eip,2); end;

---

PUSH

код:

{PUSH r32} $50..$57: begin

dst := opcode and 7;

esp := esp - 4;

WriteDWord(esp,Reg[dst]);

inc(eip);

end;

---

POP

код:

{POP r32} $58..$5F: begin

dst := opcode and 7;

Reg[dst] := ReadDWord(esp);

esp := esp + 4;

inc(eip);

end;

---

JMP

код:

{JMP rel32} $E9: begin eip := eip + 5 + Integer(ReadDWord(eip+1)); end;

---

JZ

код:

{JZ} $74: begin if pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;

---

JNZ

код:

{JNZ} $75: begin if not pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;

---

CALL

код:

{CALL rel32} $E8: begin

esp := esp - 4;

WriteDWord(esp,eip+5);

eip := eip + 5 + Integer(ReadDWord(eip+1));

end;

---

RET

код:

{RET} $C3: begin

eip := ReadDWord(esp);

esp := esp + 4;

end;

---

Чому цього вже достатньо

Цей набір дає:

MOV

ADD

SUB

AND

OR

XOR

CMP

SHL

SHR

PUSH

POP

JMP

JZ

JNZ

CALL

RET

Це приблизно аналог базового набору КР580, тільки для 32-бітних регістрів.

І як ти правильно сказав:

mov ebx,eax
and ebx,$FF

повністю замінює AL.

Тому 8- і 16-бітні регістри можна не реалізовувати.

---

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

MOV r32,imm32
MOV r32,[mem]
MOV [mem],r32

Після них твій ASM32 вже зможе виконувати реальні програми, а не тільки тестові інструкції.

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

0

15

Ярослав, твоя основа правильна і акуратна. Перевірив — помилок, які б ламали логіку, немає.
Архітектура нормальна:

Reg:array[0..7] absolute eax ✔

Rg32sd ✔

ReadDWord ✔

case opcode of ✔

Тобто можна спокійно розширювати тим самим принципом.

Я лише додам команди, які реально потрібні (близько до логіки КР580, але для 32-бітних регістрів).

---

Твій код + нові інструкції

код:

unit uASM32; interface implementation

type DWord=Cardinal;

var eax,ecx,edx,ebx,esp,ebp,esi,edi,eip:DWord; var Reg:array[0..7] of DWord absolute eax;

var pZF{zero},pCF{carry},pSF{sign},pOF{overflow}:Boolean;

RAM:array [0..(1610241024)] of Byte;

procedure Engine;

var opcode,modrm,src,dst,tmp:DWord;

код:

function 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;

код:

procedure Rg32sd; begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; end;

код:

procedure p1; begin pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0; inc(eip,2); end;

---

Engine

код:

begin

opcode := RAM[eip];

case opcode of

---

MOV

код:

{MOV r32,r32} $89: begin Rg32sd; Reg[dst] := Reg[src]; inc(eip,2); end;

---

ADD

код:

{ADD r32,r32} $01: begin Rg32sd; Reg[dst] := Reg[dst] + Reg[src]; p1; end;

---

SUB

код:

{SUB r32,r32} $29: begin Rg32sd; Reg[dst] := Reg[dst] - Reg[src]; p1; end;

---

AND

код:

{AND r32,r32} $21: begin Rg32sd; Reg[dst] := Reg[dst] and Reg[src]; p1; end;

---

OR

код:

{OR r32,r32} $09: begin Rg32sd; Reg[dst] := Reg[dst] or Reg[src]; p1; end;

---

XOR

код:

{XOR r32,r32} $31: begin Rg32sd; Reg[dst] := Reg[dst] xor Reg[src]; p1; end;

---

CMP

код:

{CMP r32,r32} $39: begin Rg32sd;

tmp := Reg[dst] - Reg[src];

pZF := tmp=0; pSF := (tmp and $80000000)<>0;

inc(eip,2); end;

---

SHL

код:

{SHL r32,CL} $D3: begin Rg32sd;

Reg[dst] := Reg[dst] shl (Reg[1] and $1F);

pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0;

inc(eip,2); end;

---

SHR

код:

{SHR r32,CL} $D1: begin Rg32sd;

Reg[dst] := Reg[dst] shr (Reg[1] and $1F);

pZF := Reg[dst]=0; pSF := (Reg[dst] and $80000000)<>0;

inc(eip,2); end;

---

PUSH

код:

{PUSH r32} $50..$57: begin

dst := opcode and 7;

esp := esp - 4;

WriteDWord(esp,Reg[dst]);

inc(eip);

end;

---

POP

код:

{POP r32} $58..$5F: begin

dst := opcode and 7;

Reg[dst] := ReadDWord(esp);

esp := esp + 4;

inc(eip);

end;

---

JMP

код:

{JMP rel32} $E9: begin eip := eip + 5 + Integer(ReadDWord(eip+1)); end;

---

JZ

код:

{JZ} $74: begin if pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;

---

JNZ

код:

{JNZ} $75: begin if not pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;

---

CALL

код:

{CALL rel32} $E8: begin

esp := esp - 4;

WriteDWord(esp,eip+5);

eip := eip + 5 + Integer(ReadDWord(eip+1));

end;

---

RET

код:

{RET} $C3: begin

eip := ReadDWord(esp);

esp := esp + 4;

end;

---

Чому цього вже достатньо

Цей набір дає:

MOV

ADD

SUB

AND

OR

XOR

CMP

SHL

SHR

PUSH

POP

JMP

JZ

JNZ

CALL

RET

Це приблизно аналог базового набору КР580, тільки для 32-бітних регістрів.

І як ти правильно сказав:

mov ebx,eax
and ebx,$FF

повністю замінює AL.

Тому 8- і 16-бітні регістри можна не реалізовувати.

---

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

MOV r32,imm32
MOV r32,[mem]
MOV [mem],r32

Після них твій ASM32 вже зможе виконувати реальні програми, а не тільки тестові інструкції.

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

0

16

1.Поясни що це за MOV r32,imm32   MOV r32,[mem]   MOV [mem],r32
2. Потрібні майже всі команди щоб працювали зі значенням типа mov,3  add eax,52
3. з інструкції до ОС "колибри" -
"Номер функции помещается в регистр eax.
Вызов системной функции осуществляется командой "int 0x40"."
int 0x40 - код cd. Так визиваються функції колибрі.
Мабуть потрібно і мене таке
4. Потрібно реалізація команд типа mov eax,[esp+1]  mov eax,[esp+2],
щоб можна було передавати параметри в процедуру за принципом stdCall
5. Думаю потрібна реалізація команд типа movsx movzx

unit uASM32;
interface
implementation
type DWord=Cardinal;
var eax,ecx,edx,ebx,esp,ebp,esi,edi,eip:DWord;
var Reg:array[0..7] of DWord absolute eax;
var pZF{zero},pCF{carry},pSF{sign},pOF{overflow}:Boolean;

RAM:array [0..(16*1024*1024)] of Byte;

procedure Engine;   var opcode,modrm,src,dst:DWord;
function 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 Rg32sd; begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; end;
procedure p1;begin pZF:=Reg[dst]=0;pSF:=(Reg[dst] and $80000000)<>0;inc(eip,2);end;

begin opcode := RAM[eip];  case opcode of
{ADD r32,r32} $01: begin Rg32sd;Reg[dst]:=Reg[dst]+Reg[src];p1;end;
{OR r32,r32} $09: begin Rg32sd; Reg[dst] := Reg[dst] or Reg[src]; p1; end;
{AND r32,r32} $21: begin Rg32sd; Reg[dst] := Reg[dst] and Reg[src]; p1; end;
{SUB r32,r32} $29: begin Rg32sd;Reg[dst] := Reg[dst] - Reg[src];p1;end;
{XOR r32,r32} $31: begin Rg32sd; Reg[dst] := Reg[dst] xor Reg[src]; p1; end;
{CMP r32,r32} $39: begin Rg32sd;pZF := (Reg[dst]-Reg[src])=0; pSF := ((Reg[dst]-Reg[src]) and $80000000)<>0;inc(eip,2); end;
{INC r32} $40..$47: begin dst := opcode and 7;Inc(Reg[dst]);p1;end;
{DEC r32} $48..$4F: begin dst := opcode and 7;Dec(Reg[dst]);p1;end;
{PUSH r32} $50..$57: begin dst := opcode and 7; Dec(esp,4);RAM[esp] := Reg[dst]; RAM[esp+1] := Reg[dst] shr 8; RAM[esp+2] := Reg[dst] shr 16; RAM[esp+3] := Reg[dst] shr 24;inc(eip);end;
{POP r32} $58..$5F: begin dst := opcode and 7; Reg[dst] := RAM[esp] or (RAM[esp+1] shl 8) or (RAM[esp+2] shl 16) or (RAM[esp+3] shl 24);Inc(esp,4);inc(eip);end;
{JZ rel8} $74: begin if pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{JNZ} $75: begin if not pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{JS} $78: begin if pSF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{RET} $C3: begin eip := RAM[esp] or (RAM[esp+1] shl 8) or (RAM[esp+2] shl 16) or (RAM[esp+3] shl 24);Inc(esp,4);end;
{SHR r32,CL} $D1:  begin Rg32sd;Reg[dst] := Reg[dst] shr (Reg[1] and $1F);p1; end;
{SHL r32,CL} $D3: begin Rg32sd; Reg[dst] := Reg[dst] shl (Reg[1] and $1F);p1;end;
{MOV r32,r32} $89: begin Rg32sd;Reg[dst] := Reg[src];inc(eip,2); end;
{CALL rel32} $E8: begin Dec(esp,4); RAM[esp] := (eip+5); RAM[esp+1] := (eip+5) shr 8; RAM[esp+2] := (eip+5) shr 16; RAM[esp+3] := (eip+5) shr 24;eip := eip + 5 + Integer(ReadDWord(eip+1));end;
{JMP rel32} $E9: begin eip:=eip+5+Integer(ReadDWord(eip+1)); end;
end;end;
end.

0

17

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

---

1. Що таке MOV r32,imm32

Це команда:

mov eax,123
mov ebx,52

Opcode:

B8..BF

тобто:

B8 mov eax,imm32
B9 mov ecx,imm32
BA mov edx,imm32
...
BF mov edi,imm32

Реалізація дуже проста.

код:

{MOV r32,imm32} $B8..$BF: begin dst := opcode and 7; Reg[dst] := ReadDWord(eip+1); inc(eip,5); end;

Це дуже важлива команда, без неї майже нічого не працює.

---

2. MOV r32,[mem]

Це читання з пам'яті:

mov eax,[1234]

або

mov eax,[esp+4]

Opcode:

8B /r

Спрощений варіант (тільки [reg]):

код:

{MOV r32,[reg]} $8B: begin Rg32sd;

Reg[dst] := ReadDWord(Reg[src]);

inc(eip,2); end;

---

3. MOV [mem],r32

Запис у пам'ять.

mov [esp],eax

Opcode:

89 /r

У тебе вже є $89, але це reg→reg.

Можна додати:

код:

{MOV [reg],r32} $89: begin Rg32sd;

RAM[Reg[dst]] := Reg[src]; RAM[Reg[dst]+1] := Reg[src] shr 8; RAM[Reg[dst]+2] := Reg[src] shr 16; RAM[Reg[dst]+3] := Reg[src] shr 24;

inc(eip,2); end;

---

4. mov eax,[esp+4]

Це дуже важливо для stdcall.

Приклад:

push 10
push 20
call func

У процедурі:

mov eax,[esp+4]
mov ebx,[esp+8]

Тобто:

base + offset

Простий варіант реалізації:

код:

{MOV r32,[reg+disp8]} $8B: begin modrm := RAM[eip+1];

src := (modrm shr 3) and 7; dst := modrm and 7;

Reg[src] := ReadDWord(Reg[dst] + ShortInt(RAM[eip+2]));

inc(eip,3); end;

---

5. INT 40h як у KolibriOS

У x86:

CD xx

Тобто:

int 40h

Реалізація:

код:

{INT} $CD: begin

case RAM[eip+1] of

$40: begin {тут системний виклик} end;

end;

inc(eip,2);

end;

Можеш зробити:

eax = номер функції

як у KolibriOS.

---

6. MOVZX

Zero extend.

movzx eax,byte ptr [...]

код:

{MOVZX} $0F: if RAM[eip+1]=$B6 then begin Rg32sd;

Reg[dst] := RAM[Reg[src]];

inc(eip,3); end;

---

7. MOVSX

Sign extend.

код:

if RAM[eip+1]=$BE then begin Rg32sd;

Reg[dst] := ShortInt(RAM[Reg[src]]);

inc(eip,3); end;

---

8. Що ще реально потрібно CPU

Ось мінімальний набір:

MOV
ADD
SUB
CMP
AND
OR
XOR
INC
DEC

SHL
SHR

PUSH
POP

CALL
RET

JMP
JZ
JNZ
JS

MOV r32,imm32
MOV r32,[mem]
MOV [mem],r32

INT

Це приблизно 80% програм покриває.

---

9. Маленька порада

Ти дуже правильно робиш:

32-bit only

І:

AL/AX не реалізовувати

Це дуже сильно спрощує CPU.

Багато маленьких емуляторів роблять так само.

---

10. Найважливіше, що ще треба

Щоб програми реально працювали, треба ще:

PUSH imm32
CMP r32,imm32
ADD r32,imm32
SUB r32,imm32

Бо компілятори дуже часто їх використовують.

---

Якщо хочеш, я можу показати дуже маленький набір (~35 інструкцій), який дозволяє запускати майже будь-які прості програми, і твій емулятор буде менше 500 рядків.

Це вже буде майже готовий мінімальний CPU як у DOSBox mini.

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

0

18

1. MOVSX Sign extend. код:if RAM[eip+1]=$BE then begin Rg32sd;
Reg[dst] := ShortInt(RAM[Reg[src]]); inc(eip,3); end;
А код команди ?
2. PUSH imm32  CMP r32,imm32  ADD r32,imm32  SUB r32,imm32
- ну то дай код реалізації.
3. пишеш - "я можу показати дуже маленький набір (~35 інструкцій), який дозволяє запускати майже будь-які прості програми, і твій емулятор буде менше 500 рядків."
покажи
4. глянь код, щось якісь негаразди з кодами $89,$8B дублікати в касе як бачиш
// {MOV [reg],r32} $89: begin Rg32sd; RAM[Reg[dst]] := Reg[src]; RAM[Reg[dst]+1] := Reg[src] shr 8; RAM[Reg[dst]+2] := Reg[src] shr 16; RAM[Reg[dst]+3] := Reg[src] shr 24;inc(eip,2); end;
  {MOV r32,r32} $89: begin Rg32sd;Reg[dst] := Reg[src];inc(eip,2); end;
  {MOV r32,[reg]} $8B: begin Rg32sd;Reg[dst] := ReadDWord(Reg[src]);inc(eip,2); end;
//  {MOV r32,[reg+disp8]} $8B: begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; Reg[src] := ReadDWord(Reg[dst] + ShortInt(RAM[eip+2]));inc(eip,3);end;

КОД:
unit uASM32;
interface
implementation
type DWord=Cardinal;
var eax,ecx,edx,ebx,esp,ebp,esi,edi,eip:DWord;
var Reg:array[0..7] of DWord absolute eax;
var pZF{zero},pCF{carry},pSF{sign},pOF{overflow}:Boolean;

RAM:array [0..(16*1024*1024)] of Byte;

procedure Engine;   var opcode,modrm,src,dst:DWord;
function 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 Rg32sd; begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; end;
procedure p1;begin pZF:=Reg[dst]=0;pSF:=(Reg[dst] and $80000000)<>0;inc(eip,2);end;

begin opcode := RAM[eip];  case opcode of
{ADD r32,r32} $01: begin Rg32sd;Reg[dst]:=Reg[dst]+Reg[src];p1;end;
{OR r32,r32} $09: begin Rg32sd; Reg[dst] := Reg[dst] or Reg[src]; p1; end;
{MOVZX} $0F: if RAM[eip+1]=$B6 then begin Rg32sd; Reg[dst] := RAM[Reg[src]]; inc(eip,3); end;
{AND r32,r32} $21: begin Rg32sd; Reg[dst] := Reg[dst] and Reg[src]; p1; end;
{SUB r32,r32} $29: begin Rg32sd;Reg[dst] := Reg[dst] - Reg[src];p1;end;
{XOR r32,r32} $31: begin Rg32sd; Reg[dst] := Reg[dst] xor Reg[src]; p1; end;
{CMP r32,r32} $39: begin Rg32sd;pZF := (Reg[dst]-Reg[src])=0; pSF := ((Reg[dst]-Reg[src]) and $80000000)<>0;inc(eip,2); end;
{INC r32} $40..$47: begin dst := opcode and 7;Inc(Reg[dst]);p1;end;
{DEC r32} $48..$4F: begin dst := opcode and 7;Dec(Reg[dst]);p1;end;
{PUSH r32} $50..$57: begin dst := opcode and 7; Dec(esp,4);RAM[esp] := Reg[dst]; RAM[esp+1] := Reg[dst] shr 8; RAM[esp+2] := Reg[dst] shr 16; RAM[esp+3] := Reg[dst] shr 24;inc(eip);end;
{POP r32} $58..$5F: begin dst := opcode and 7; Reg[dst] := RAM[esp] or (RAM[esp+1] shl 8) or (RAM[esp+2] shl 16) or (RAM[esp+3] shl 24);Inc(esp,4);inc(eip);end;
{JZ rel8} $74: begin if pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{JNZ} $75: begin if not pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{JS} $78: begin if pSF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
// {MOV [reg],r32} $89: begin Rg32sd; RAM[Reg[dst]] := Reg[src]; RAM[Reg[dst]+1] := Reg[src] shr 8; RAM[Reg[dst]+2] := Reg[src] shr 16; RAM[Reg[dst]+3] := Reg[src] shr 24;inc(eip,2); end;
  {MOV r32,r32} $89: begin Rg32sd;Reg[dst] := Reg[src];inc(eip,2); end;
  {MOV r32,[reg]} $8B: begin Rg32sd;Reg[dst] := ReadDWord(Reg[src]);inc(eip,2); end;
//  {MOV r32,[reg+disp8]} $8B: begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; Reg[src] := ReadDWord(Reg[dst] + ShortInt(RAM[eip+2]));inc(eip,3);end;
{MOV r32,imm32} $B8..$BF: begin dst := opcode and 7; Reg[dst] := ReadDWord(eip+1); inc(eip,5); end;
{RET} $C3: begin eip := RAM[esp] or (RAM[esp+1] shl 8) or (RAM[esp+2] shl 16) or (RAM[esp+3] shl 24);Inc(esp,4);end;
{INT} $CD: begin case RAM[eip+1] of $40: begin {òóò ñèñòåìíèé âèêëèê} end; end; inc(eip,2);

end;
{SHR r32,CL} $D1:  begin Rg32sd;Reg[dst] := Reg[dst] shr (Reg[1] and $1F);p1; end;
{SHL r32,CL} $D3: begin Rg32sd; Reg[dst] := Reg[dst] shl (Reg[1] and $1F);p1;end;

{CALL rel32} $E8: begin Dec(esp,4); RAM[esp] := (eip+5); RAM[esp+1] := (eip+5) shr 8; RAM[esp+2] := (eip+5) shr 16; RAM[esp+3] := (eip+5) shr 24;eip := eip + 5 + Integer(ReadDWord(eip+1));end;
{JMP rel32} $E9: begin eip:=eip+5+Integer(ReadDWord(eip+1)); end;
end;end;
end.

0

19

Ярослав, ти вже дуже близько до нормального маленького CPU 👍
Я перевірив код і відповім по пунктах.

---

1. Який opcode у MOVSX

Є два основні:

0F BE  MOVSX r32,r/m8
0F BF  MOVSX r32,r/m16

Тобі достатньо першого.

Реалізація:

код:

{MOVSX r32,r/m8} $0F: case RAM[eip+1] of $BE: begin Rg32sd; Reg[dst] := ShortInt(RAM[Reg[src]]); inc(eip,3); end;

$B6: {MOVZX} begin Rg32sd; Reg[dst] := RAM[Reg[src]]; inc(eip,3); end;

end;

---

2. PUSH imm32

Opcode:

68

код:

{PUSH imm32} $68: begin

Dec(esp,4);

RAM[esp]   := RAM[eip+1]; RAM[esp+1] := RAM[eip+2]; RAM[esp+2] := RAM[eip+3]; RAM[esp+3] := RAM[eip+4];

inc(eip,5);

end;

---

3. ADD r32,imm32

Opcode:

81 /0

Тобто ModRM визначає операцію.

Спрощений варіант:

код:

{ADD r32,imm32} $81: begin

modrm := RAM[eip+1];

if ((modrm shr 3) and 7)=0 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); end;

end;

---

4. SUB r32,imm32

81 /5

код:

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);

end;

---

5. CMP r32,imm32

81 /7

код:

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;

---

6. Проблема з $89 і $8B

Ти правильно помітив — дублювати case не можна.

Бо opcode однаковий.

Правильна логіка:

89  MOV r/m32,r32
8B  MOV r32,r/m32

Але що саме — визначає modrm.

Тобто:

mod = modrm shr 6

---

Правильна реалізація $89

код:

{MOV r/m32,r32} $89: begin

modrm := RAM[eip+1];

src := (modrm shr 3) and 7; dst := modrm and 7;

if (modrm and $C0)=$C0 then Reg[dst] := Reg[src] else begin RAM[Reg[dst]] := Reg[src]; RAM[Reg[dst]+1] := Reg[src] shr 8; RAM[Reg[dst]+2] := Reg[src] shr 16; RAM[Reg[dst]+3] := Reg[src] shr 24; end;

inc(eip,2);

end;

---

Правильна реалізація $8B

код:

{MOV r32,r/m32} $8B: begin

modrm := RAM[eip+1];

src := (modrm shr 3) and 7; dst := modrm and 7;

if (modrm and $C0)=$C0 then Reg[src] := Reg[dst] else Reg[src] := ReadDWord(Reg[dst]);

inc(eip,2);

end;

---

7. Мінімальний набір (~35 інструкцій)

Ось реально достатній набір.

арифметика

ADD
SUB
INC
DEC
CMP

логіка

AND
OR
XOR

shift

SHL
SHR

mov

MOV r32,r32
MOV r32,imm32
MOV r32,[mem]
MOV [mem],r32
MOVSX
MOVZX

стек

PUSH r32
POP r32
PUSH imm32

переходи

JMP
CALL
RET
JZ
JNZ
JS

системні

INT

Це приблизно 30–40 інструкцій, але вони покривають 80% програм.

---

8. Дуже важлива інструкція яку ще варто додати

Компилятори часто генерують:

83 /0 ADD r32,imm8
83 /5 SUB r32,imm8
83 /7 CMP r32,imm8

Це короткі інструкції.

І вони дуже часто зустрічаються.

---

9. Маленьке покращення твого CPU

Ти часто пишеш:

RAM[esp]
RAM[esp+1]
RAM[esp+2]
RAM[esp+3]

Краще зробити процедуру:

код:

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;

І код стане набагато чистішим.

---

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

як зробити повну підтримку

[reg+disp8]
[reg+disp32]

буквально у 15 рядках коду.

І тоді у тебе буде повноцінна адресація x86.

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

0

20

1. SUB r32,imm32 та  CMP r32,imm32 нормально напиши який у них код в касе
2.
//  {MOV r32,[reg]} $8B: begin Rg32sd;Reg[dst] := ReadDWord(Reg[src]);inc(eip,2); end;
//  {MOV r32,[reg+disp8]} $8B: begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; Reg[src] := ReadDWord(Reg[dst] + ShortInt(RAM[eip+2]));inc(eip,3);end;
{MOV r32,r/m32} $8B: begin Rg32sd;
                if (modrm and $C0)=$C0 then Reg[src] := Reg[dst]
                else {MOV r32,[reg]} Reg[src] := ReadDWord(Reg[dst]);inc(eip,2);end;
Щось я не розумію, чого попередні це ті що закоментовані реалізації
різні з новими ?
3. 83 /0 ADD r32,imm8   83 /5 SUB r32,imm8  83 /7 CMP r32,imm8
ну так реалізуй код

4. ну й куди procedure WriteDWord вставлять?

unit uASM32;
interface
implementation
type DWord=Cardinal;
var eax,ecx,edx,ebx,esp,ebp,esi,edi,eip:DWord;
var Reg:array[0..7] of DWord absolute eax;
var pZF{zero},pCF{carry},pSF{sign},pOF{overflow}:Boolean;

RAM:array [0..(16*1024*1024)] of Byte;

procedure Engine;   var opcode,modrm,src,dst:DWord;
function 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;
procedure Rg32sd; begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; end;
procedure p1;begin pZF:=Reg[dst]=0;pSF:=(Reg[dst] and $80000000)<>0;inc(eip,2);end;

begin opcode := RAM[eip];  case opcode of
{ADD r32,r32} $01: begin Rg32sd;Reg[dst]:=Reg[dst]+Reg[src];p1;end;
{OR r32,r32} $09: begin Rg32sd; Reg[dst] := Reg[dst] or Reg[src]; p1; end;

$0F: case RAM[eip+1] of
{MOVSX r32,r/m8}$BE: begin Rg32sd; Reg[dst] := ShortInt(RAM[Reg[src]]); inc(eip,3); end;
{MOVZX}         $B6: begin Rg32sd; Reg[dst] := RAM[Reg[src]]; inc(eip,3); end;
      end;

{AND r32,r32} $21: begin Rg32sd; Reg[dst] := Reg[dst] and Reg[src]; p1; end;
{SUB r32,r32} $29: begin Rg32sd;Reg[dst] := Reg[dst] - Reg[src];p1;end;
{XOR r32,r32} $31: begin Rg32sd; Reg[dst] := Reg[dst] xor Reg[src]; p1; end;
{CMP r32,r32} $39: begin Rg32sd;pZF := (Reg[dst]-Reg[src])=0; pSF := ((Reg[dst]-Reg[src]) and $80000000)<>0;inc(eip,2); end;
{INC r32} $40..$47: begin dst := opcode and 7;Inc(Reg[dst]);p1;end;
{DEC r32} $48..$4F: begin dst := opcode and 7;Dec(Reg[dst]);p1;end;
{PUSH r32} $50..$57: begin dst := opcode and 7; Dec(esp,4);RAM[esp] := Reg[dst]; RAM[esp+1] := Reg[dst] shr 8; RAM[esp+2] := Reg[dst] shr 16; RAM[esp+3] := Reg[dst] shr 24;inc(eip);end;
{POP r32} $58..$5F: begin dst := opcode and 7; Reg[dst] := RAM[esp] or (RAM[esp+1] shl 8) or (RAM[esp+2] shl 16) or (RAM[esp+3] shl 24);Inc(esp,4);inc(eip);end;
{PUSH imm32} $68: begin Dec(esp,4);RAM[esp]:= RAM[eip+1];RAM[esp+1]:= RAM[eip+2];RAM[esp+2]:= RAM[eip+3];RAM[esp+3]:=RAM[eip+4];inc(eip,5);end;
{JZ rel8} $74: begin if pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{JNZ} $75: begin if not pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{JS} $78: begin if pSF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2); end;
{ADD r32,imm32} $81: begin modrm:=RAM[eip+1]; if ((modrm shr 3) and 7)=0 then begin dst:=modrm and 7; Reg[dst]:=Reg[dst]+ReadDWord(eip+2);p1;inc(eip,4);end;end;

{MOV r/m32,r32} $89: begin Rg32sd;
       if (modrm and $C0)=$C0 then {MOV r32,r32} Reg[dst] := Reg[src]
                              else {MOV [reg],r32}  begin RAM[Reg[dst]] := Reg[src]; RAM[Reg[dst]+1] := Reg[src] shr 8; RAM[Reg[dst]+2] := Reg[src] shr 16; RAM[Reg[dst]+3] := Reg[src] shr 24; end;
           inc(eip,2);end;

//  {MOV r32,[reg]} $8B: begin Rg32sd;Reg[dst] := ReadDWord(Reg[src]);inc(eip,2); end;
//  {MOV r32,[reg+disp8]} $8B: begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; Reg[src] := ReadDWord(Reg[dst] + ShortInt(RAM[eip+2]));inc(eip,3);end;
{MOV r32,r/m32} $8B: begin Rg32sd;
                if (modrm and $C0)=$C0 then Reg[src] := Reg[dst]
                else {MOV r32,[reg]} Reg[src] := ReadDWord(Reg[dst]);inc(eip,2);end;

{MOV r32,imm32} $B8..$BF: begin dst := opcode and 7; Reg[dst] := ReadDWord(eip+1); inc(eip,5); end;
{RET} $C3: begin eip := RAM[esp] or (RAM[esp+1] shl 8) or (RAM[esp+2] shl 16) or (RAM[esp+3] shl 24);Inc(esp,4);end;
{INT} $CD: begin case RAM[eip+1] of $40: begin {òóò ñèñòåìíèé âèêëèê} end; end; inc(eip,2);

end;
{SHR r32,CL} $D1:  begin Rg32sd;Reg[dst] := Reg[dst] shr (Reg[1] and $1F);p1; end;
{SHL r32,CL} $D3: begin Rg32sd; Reg[dst] := Reg[dst] shl (Reg[1] and $1F);p1;end;

{CALL rel32} $E8: begin Dec(esp,4); RAM[esp] := (eip+5); RAM[esp+1] := (eip+5) shr 8; RAM[esp+2] := (eip+5) shr 16; RAM[esp+3] := (eip+5) shr 24;eip := eip + 5 + Integer(ReadDWord(eip+1));end;
{JMP rel32} $E9: begin eip:=eip+5+Integer(ReadDWord(eip+1)); end;

$B6: {MOVZX} begin Rg32sd; Reg[dst] := RAM[Reg[src]]; inc(eip,3); end;

end;
end;
end.

0

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

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



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