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, EBP e EBX 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.





