1. Ось таку реалізацію хочу помилок опкодів, див все пов'зане з
ErrorOpCode1;ErrorOpCode2;OpcodeERRValue і т.д.
uASM32.pas
unit uASM32;
interface
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..(16*1024*1024)] of Byte;
procedure LoadBIN(const FileName:string; LoadAddr:DWord);
procedure InitCPU;
procedure ENGINE;
{для INT $40} var
APM,{1=вихід з двигуна з procedure ENGINE, у разі якщо в таймері(Дельфі), для перерисуввааняб опросу клавыатури}
pProgramHalt {негайний вихід з програми Halt}
:DWord;
pConsoleMode:DWord=0;{якщо 0 - консольний режим не доступний}
TrapInvalidOpcode:DWord;{якщо 1 - програма обробляє опкоди}
InvalidOpcodeVector:DWord;{call-переходу переадреса}
OpcodeERRValue, OpcodeERReax, OpcodeERRecx, OpcodeERRedx, OpcodeERRebx, OpcodeERResp,
OpcodeERRebp, OpcodeERResi, OpcodeERRedi, OpcodeERReip, OpcodeERRRAMeip :DWord;
TrapInvalidINT:DWord;{якщо 1 - програма обробляє опкоди}
InvalidINTVector:DWord;{call-переходу переадреса}
{}
//:DWord;
implementation
uses SysUtils;
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;
{================ MEMORY =================}
function ReadDWord(a:DWord):DWord; begin Result := RAM[a] or (RAM[a+1] shl 8) or (RAM[a+2] shl 16) or (RAM[a+3] shl 24);end;
procedure WriteDWord(a,v:DWord);begin RAM[a] := v;RAM[a+1] := v shr 8; RAM[a+2] := v shr 16;RAM[a+3] := v shr 24;end;
{================ MODRM =================}
procedure DecodeModRM;var m:Byte; begin modrm := RAM[eip+1]; src := (modrm shr 3) and 7; dst := modrm and 7; m := modrm shr 6; 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));
else begin if TrapInvalidOpcode<>0 then begin OpcodeERRValue:=$FFFFFFFF;APM:=1;end;end; 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;
{================ FLAGS =================}
procedure SetFlags(v:DWord);begin pZF := v=0; pSF := (v and $80000000)<>0; end;
{================ ALU =================}
procedure opADD;var v:DWord;begin DecodeModRM;v := GetRM + Reg[src];SetRM(v);SetFlags(v);NextRM;end;
procedure opOR;var v:DWord;begin DecodeModRM;v := GetRM or Reg[src];SetRM(v);SetFlags(v);NextRM;end;
procedure opAND;var v:DWord;begin DecodeModRM;v := GetRM and Reg[src];SetRM(v);SetFlags(v);NextRM;end;
procedure opSUB;var v:DWord;begin DecodeModRM;v := GetRM - Reg[src];SetRM(v);SetFlags(v);NextRM;end;
procedure opXOR;var v:DWord;begin DecodeModRM;v := GetRM xor Reg[src];SetRM(v);SetFlags(v);NextRM;end;
procedure opCMP;var v:DWord;begin DecodeModRM;v := GetRM - Reg[src];SetFlags(v);NextRM;end;
{=========== ADD/SUB/CMP imm8 ============}
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;
{================ INC/DEC =================}
procedure opINC;begin dst := RAM[eip] and 7;Inc(Reg[dst]);SetFlags(Reg[dst]);inc(eip);end;
procedure opDEC;begin dst := RAM[eip] and 7;Dec(Reg[dst]);SetFlags(Reg[dst]);inc(eip);end;
{================ STACK =================}
procedure opPUSH_r32;begin dst := RAM[eip] and 7;Dec(esp,4);WriteDWord(esp,Reg[dst]);inc(eip);end;
procedure opPOP_r32;begin dst := RAM[eip] and 7;Reg[dst] := ReadDWord(esp);Inc(esp,4);inc(eip);end;
{================ MOV =================}
procedure opMOV_rm_r;begin DecodeModRM;SetRM(Reg[src]);NextRM;end;
procedure opMOV_r_rm;begin DecodeModRM;Reg[src] := GetRM;NextRM;end;
procedure opMOV_r32_imm;begin dst := RAM[eip] and 7;Reg[dst] := ReadDWord(eip+1);inc(eip,5);end;
{================ MUL =================}
procedure opMUL;var v:Int64;begin DecodeModRM;v := Int64(eax) * Int64(GetRM);eax := DWord(v);edx := DWord(v shr 32);NextRM;end;
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 =================}
procedure opJMP;begin eip := eip + 5 + Integer(ReadDWord(eip+1));end;
procedure opCALL;begin Dec(esp,4);WriteDWord(esp,eip+5);eip := eip + 5 + Integer(ReadDWord(eip+1));end;
procedure opRET;begin eip := ReadDWord(esp);Inc(esp,4);end;
procedure opJZ;begin if pZF then inc(eip,ShortInt(RAM[eip+1])+2) else inc(eip,2);end;
procedure opJNZ;begin if not pZF then inc(eip,ShortInt(RAM[eip+1])+2)else inc(eip,2);end;
procedure opJS;begin if pSF then inc(eip,ShortInt(RAM[eip+1])+2)else inc(eip,2);end;
{================ INT =================}
procedure opNOP;begin inc(eip);end;
procedure ErrorOpCode1;
begin
case TrapInvalidOpcode of
0:inc(eip,1);
1: {якщо значення 1 - Емулятор обробляє відсутність коду процесору, зупиняє програму та видає помилку}
begin If pConsoleMode<>0 then Begin OpcodeERRHasOperatedMistake;OpcodeERRValue:=1;Writeln('Unknown opcode ',eip,' ',RAM[eip]);Readln; APM:=1;pProgramHalt:=1;end;end;
2:{якщо значення 2 - У разі невідомого коду процесора, здійснюється перехід(за аналогією Call), за адресою вказаною, в InvalidOpcodeVector}
begin OpcodeERRHasOperatedMistake;OpcodeERRValue:=1;Dec(esp,4); WriteDWord(esp,eip); { return address } eip := InvalidOpcodeVector; end
end;{<=case TrapInvalidOpcode of}
end;
procedure ErrorOpCode2;
begin
case TrapInvalidOpcode of
0:inc(eip,2);
1: {якщо значення 1 - Емулятор обробляє відсутність коду процесору, зупиняє програму та видає помилку}
begin If pConsoleMode<>0 then Begin OpcodeERRHasOperatedMistake;OpcodeERRValue:=2;Writeln('Unknown opcode ',eip,' ',RAM[eip]);Readln; APM:=1;pProgramHalt:=1;end;end;
2:{якщо значення 2 - У разі невідомого коду процесора, здійснюється перехід(за аналогією Call), за адресою вказаною, в InvalidOpcodeVector}
begin OpcodeERRHasOperatedMistake;OpcodeERRValue:=2;Dec(esp,4); WriteDWord(esp,eip); { return address } eip := InvalidOpcodeVector; end
end;{<=case TrapInvalidOpcode of}end;
{================ INIT =================}
procedure opINT;Forward;
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; Optable[$83] := opGrp83;
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[$89] := opMOV_rm_r; Optable[$8B] := opMOV_r_rm;
for i:=$B8 to $BF do Optable[i] := opMOV_r32_imm; Optable[$F7] := opMUL; {спрощено}
Optable[$AF] := opIMUL; Optable[$E9] := opJMP; Optable[$E8] := opCALL; Optable[$C3] := opRET;
Optable[$74] := opJZ;Optable[$75] := opJNZ;Optable[$78] := opJS;
Optable[$CD] := opINT; Optable[$90] :=opNOP;
end;
procedure ENGINE;{================ ENGINE =================}
var opcode:DWord;
begin
APM:=0; repeat opcode := RAM[eip]; Optable[opcode]();until APM<>0;
if (pProgramHalt=0) and (OpcodeERRValue=$FFFFFFFF) then ErrorOpCode2;
end;
procedure opINT;
var IntERR:Dword;
begin IntERR:=0;
case RAM[eip+1] of $40: begin
case EAX of
{NOP} 0:begin end;
{Halt}1:begin APM:=1;pProgramHalt:=1;{Writeln('HALT ',eip,' ',RAM[eip]);Readln;} end;
{APM}2:begin APM:=1; end;
3:begin {функції та процедури консольного режиму}
if pConsoleMode<>0 then begin
case EBX of
1:begin Writeln;end;
2:begin Write('ECX='+IntToStr(ECX)+' ($'+IntToHEX(ECX,8)+')'); end;
3:begin Write(chr(ECX)); end;{вивести символ}
4:Write(ECX); {вивести число}
5:Readln;
else IntERR:=2;
end;
end else IntERR:=3;
end; {EAX=3 - функції та процедури консольного режиму}
4:begin {читання значення різних змінних}
case EBX of
1:ECX:=pConsoleMode;
2:ECX:=TrapInvalidOpcode;{якщо 1 - програма обробляє опкоди}
3:ECX:=InvalidOpcodeVector;{call-переходу переадреса}
4:ECX:=TrapInvalidINT;{якщо 1 - програма обробляє опкоди}
5:ECX:=InvalidINTVector;{call-переходу переадреса}
else IntERR:=2;
end
end {EAX=4 - читання значення різних змінних}
else IntERR:=1;
end;end;
end;
If IntERR <>0 then Begin
case TrapInvalidINT of
0:inc(eip,2);
1: {якщо значення 1 - Емулятор обробляє відсутність коду процесору, зупиняє програму та видає помилку}
begin If pConsoleMode<>0 then Begin Writeln('UnknownINT ',eip,' ',RAM[eip]);Readln; APM:=1;pProgramHalt:=1;end;end;
2:{якщо значення 2 - У разі невідомого коду процесора, здійснюється перехід(за аналогією Call), за адресою вказаною, в InvalidOpcodeVector}
begin Dec(esp,4); WriteDWord(esp,eip); { return address } eip := InvalidINTVector; end
end;{<=case TrapInvalidOpcode of}
end else inc(eip,2);end;
end.
SasEmul32.dpr
program Emul32;{Delphi7}
{$APPTYPE CONSOLE}
uses
Windows,
uASM32 in 'UNIT\uASM32.pas';
BEGIN
pConsoleMode:=1;
InitCPU; esp := SizeOf(RAM); {стек}
LoadBIN('BOOT.bin',0);
pProgramHalt:=0;
repeat
Engine;
until pProgramHalt<>0;
WinExec('ArhWinRAR.Bat',SW_ShowMaximized);
END.
Документація (чернетка)
int $40
EAX - номер функції EBX - номер подфунції (за потребою)
EAX = 0 - НОП. Тобто begin end;
EAX = 1 - Halt, негайний вихід з програми.
EAX = 2 - APM аналог Application.ProcessMessages (DELPHI)
яку потрібно використовувати після процедур виводу на екран,
перед опитуванням клавіатури та можливо в деяких інших ситуаціях,
в деяких середовищах програмування.
EAX = 3 - функції та процедури консольного режиму
(працюють якщо pConsoleMode<>0, тобто увімкнений консольний режим)
EBX = 1 - аналог Writeln; без параметрів
EBX = 2 - вивід на екран регістра ECX (для тестів)
EBX = 3 - вивід вивід символу на екран код в регістрі ECX
EBX = 4 - вивести число ECX (в десятичному вигляді)
EBX = 5 - аналог Readln;
EAX = 4 - читання значення різних змінних.
Повертання в ECX
EBX = 1, - повертає значення pConsoleMode.
EBX = 2, - повертає значення TrapInvalidOpcode.
EBX = 3, - повертає значення InvalidOpcodeVector.
EBX = 4, - повертає значення TrapInvalidINT.
EBX = 5, - повертає значення InvalidINTVector.
Опис змінних
1.pConsoleMode, якщо <> 0 якщо доступна консоль,роблять відповідні функції та процедури.
2.TrapInvalidOpcode, якщо значення 0 - невідомі коди процесора працюють, як НОП, тобто пропускаються і програма робить далі
якщо значення 1 - Емулятор обробляє відсутність коду процесору, зупиняє програму та видає помилку
якщо значення 2 - У разі невідомого коду процесора, здійснюється перехід(за аналогією Call), за адресою вказаною, в InvalidOpcodeVector
3.InvalidOpcodeVector, - адреса виклику (за аналогією Call), у разі виникнення невідомого коду процесора ( при встановленому TrapInvalidOpcode=2)
4.TrapInvalidINT та 5.InvalidINTVector, ці змінні працюють по аналогії з 2.TrapInvalidOpcode та 3.InvalidOpcodeVector, для int $40.
6.OpcodeERRValue, значення помилки 1 -спрацювало в першому коді команди процесору, 2 -спрацювало в другому коді команди процесору
Копія значення регістрів, коли виникла помилка.
7.OpcodeERReax, 8.OpcodeERRecx, 9.OpcodeERRedx, 10.OpcodeERRebx,
11.OpcodeERResp, 12.OpcodeERRebp, 13.OpcodeERResi, 14.OpcodeERRedi,
15.OpcodeERReip,
16.OpcodeERRRAMeip, значення RAM[eip] (першого байту помилки)