Compago

...free knowledge

 
  • Increase font size
  • Default font size
  • Decrease font size
Home Manuali Programmazione Introduzione all'inline assembler

Introduzione all'inline assembler

E-mail Stampa PDF

Un codice in pascal può contenere delle istruzioni assembler inserite all'interno di blocchi asm-end :

...
asm
 istruzioni assembler
end;
...

Una generica istruzione assembler ha questa forma:

Label: Prefix Opcode Operand1, Operand2

Esempio

add eax,ebx


Più istruzioni possono essere separate in modi diversi, ma solitamente si usa scriverle su righe diverse:

asm
  add ebx,edx; add eax,[ebx]; {istruzioni separate da un punto e virgola}
  // listruzioni separate da una nuova riga
  mov ebx,ecx
  sub eax,ebx (*istruzioni separate da un commento*) mov ebx, edx
end;

Le istruzioni sono quelle che si trovano in ogni manuale assembler quindi non mi dilungherò sulla loro descrizione.

Label

Le label sono usate nelle parti di codice assembly (built-in assembly) come delle etichette di parti di codice, in pratica simboleggiano l'indirizzo della istruzione successiva.
Le label globali devono essere sempre dichiarate, come accade in pascal, infatti esse non sono una esclusiva del built-in assembly, ma possono essere usate anche in un normale codice pascal. Esempio:

label lab1;       //dichiarazione label
begin
  asm
    jmp lab1      //uso un riferimento alla label
    xor eax,eax
    lab1:         //label
    xor eax,eax
  end;
end.

Non vi sono limiti di lunghezza del loro nome e possono essere richiamate anche all'interno di altri blocchi assembly.
Le label locali invece non devono essere dichiarare e si differenziano da quelle globali perché iniziano col simbolo "at"(@).
Vediamo un esempio che mostra le limitazioni di una label locale:

begin
  asm              //primo blocco assembly
    jmp @lab2      //errore: la label è locale ed appartiene ad un altro blocco
    xor eax,eax
  end;

  asm              //secondo blocco assembly
    @lab2:         //dichiarazione di una label locale
    xor eax,eax
  end;
end.

per un uso corretto dovremo fare riferimento alle label locali solo all'interno dello stesso blocco assembly:

begin
  asm
    jmp @lab2
    xor eax,eax
    @lab2:
    xor eax,eax
  end;
end.

un altra cosa che vorrei far notare è che all'interno di un blocco assembler potremo richiamare una funzione esterna allo stesso modo in cui viene usata una label, dato che entrambe rappresentano l'indirizzo di una parte di codice.

function somma(a,b:integer):integer;
begin
  result:=a+b;
end;

begin
  asm
    mov eax,1 //imposta i parametri della funzione
    mov edx,2 //
    call somma    //chiama la funzione
  end;
end.


Usando i blocchi assembler all'interno del codice pascal è possibile scrivere intere funzioni anche se è bene attenersi ad alcune regole, infatti usando la keyword asm è come se fi facesse riferimento ad una funzione o procedura esterna, inoltre devono essere preservati i valori nei registri EDI, ESI, ESP, EBPEBX mentre possono essere liberamente utilizzati i registri EAX, ECX e EDX. Se non ci si attiene a queste regole e non si fa nulla per preservare i registri è possibile che il programma compilato dia dei risultati imprevisti.
Potremo inserire un blocco assembler nel codice di una funzione oppure scriverla interamente in assembler, per fare questo potremo farlo in questa forma:

function ...;
begin
  asm
...
  end;
end;

Oppure in quest'altra:

function ...;
asm

...
end
;

ma tenete presente che non sempre sono equivalenti, dato che il compilatore potrebbe eseguire delle ottimizzazioni aggiungendo del codice e questo potrebbe modificare le funzionalità del codice, vediamo un esempio:

function somma1(a,b:integer):integer;
begin
  asm
    add eax,edx
  end;
end;
function somma2(a,b:integer):integer;
  asm
    add eax,edx
  end;

begin
  writeln(somma1(2,3));
  writeln(somma2(2,3));
end.

I risultati nonostante le istruzioni siano le stesse saranno diversi.

Il compilatore esegue varie ottimizzazioni sul codice relativo alle funzioni:

A seconda della presenza di variabili locali o di parametri il codice generato automaticamente all'inizio e alla fine potrebbe assumere genericamente questa forma:

 PUSH EBP         //conserva il valore di EBP nello stack
 MOV EBP, ESP     //salva in EBP il puntatore alla cima dello stack da usare come riferimento
 SUB ESP, n //riserva lo spazio nello stack per le variabili locali
 .
 .
 .
 MOV ESP, EBP     //ripristina il puntatore iniziale alla cima dello stack
 POP EBP          //ripristina il valore iniziale di EBP
 RET n      //eventualente pulisce lo stack dai parametri inseriti per la funzione

questo accade ogni qualvolta il codice della funzione debba fare riferimento a variabili o parametri conservati nello stack, quindi dipende molto anche dal tipo di chiamata di funzione ( fastcall, stdcall, cdecl, pascal, register, safecall).

Per quanto riguarda i risultati:

  • I valori ordinali vengono restituiti in AL (valori da 8 bit), AX (valori a 16 bit), o EAX (valori a 32 bit).
  • I valori di tipo real sono restituiti nel registro ST (0) del coprocessore. (Valori di tipo currency sono scalati di 10000.)
  • Puntatori, comprese le long string, vengono restituiti in EAX.
  • Short string e varianti vengono restituiti in una posizione temporanea puntata da @result.













Ultimo aggiornamento ( Giovedì 22 Marzo 2012 19:29 )  
Loading

Login




Chiudi