Architetture I
Lez. V
La preparazione dei programmi
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Preparazione programmi
• La sequenza di azioni da intraprendere per
trasformare un programma scritto in
assembler, o in altro linguaggio evoluto, in
un programma in formato eseguibile, cioè
che possa essere compreso e tradotto da
un processore
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Assemble-Link Execute Cycle
• Il seguente diagramma descrive i passi da
eseguire per trasformare un programma da un
formato sorgente ad un formato eseguibile
• Ogni volta che il file sorgente viene modificato è
necessario ripetere i passi , da 2 a 4
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Quali programmi usare
• Per l'esecuzione della fase di preparazione
programmi vanno usati i seguenti
programmi:
• Un word processor (Wordpad) per scrivere il
programma sorgente su file
• NASM per assemblare il programma sorgente
• MinGW (compilatore C) per svolgere la fase di
linking
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
NASM
z
z
z
z
z
Sito ufficiale: www.nasm.us
Da questo sito cliccare su download
Quindi sulla directory 2.07/
Poi sulla directory win32/
Eseguire il programma nasm-2.07installer.exe
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
MinGW
• scaricabile da "www.mingw.org"
• Si installa eseguendo il file "MinGW-3.4.2.exe", è consigliabile
installare il minGW nella cartella C:\mingw
• Dopo l'installazione del MinGW, per un suo corretto utilizzo bisogna
settare alcune variabili d'ambiente,
• Risorse del computer->(tasto destro del mouse)Proprietà->Avanzate>Variabili D'ambiente
• andare nel riquadro variabili di sistema selezionare, dalla lista, la
variabile path e cliccare su Modifica, scrivere nel campo valore
della variabile "path" la stringa ";c:\mingw\bin“
• Cliccare quindi su Nuovo ed inserire in "nome variabile" la stringa
MINGDIR ed in "valore variabile" la stringa "c:\mingw".
• Cliccare quindi su Ok
• Per testare il corretto funzionamento del compilatore aprire una
finestra di Ms Dos (Prompt di Ms Dos), e scrivere in essa il
comando gcc, se il prompt vi risponde con "gcc: no input file"
allora l'installazione di MinGW è andata a buon fine
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Driver C per codice Assembler
• Creare un programma eseguibile usando
esclusivamente il linguaggio assembler non è facile
• L’approccio generalmente utilizzato è quello di scrivere
il programma assembler come “porzione” di un
programma scritto in un linguaggio di più alto livello
• In NASM il programma assembler è scritto come
porzione di un programma C noto driver.c
int main()
// C driver
{
int ret_status;
ret_status = asm_main();
return ret_status;
}
A.A. 09/10
...
add eax, ebx
mov ebx, [edi]
...
Architetture degli Elaboratori I
© Danilo Bruschi
Template di un programma
NASM
z
z
z
segment .data
; DX directives
z
segment .bss
; RESX directives
z
segment .text
z
z
z
z
z
z
z
z
z
z
A.A. 09/10
; include directives
global asm_main
asm_main:
enter
0,0
pusha
; LE ISTRUZIONI DEL NOSTRO PROGRAMMA
popa
mov
eax, 0
leave
ret
Architetture degli Elaboratori I
© Danilo Bruschi
I comandi
• Per Assemblare un file di nome prova.asm
●
nasm -f win32 -l prova.lst prova.asm
• Per compilare il driver (è sufficiente farlo
una sola volta)
●
gcc -c driver.c
• Per linkare e ottenere il file eseguibile di
nome prova
●
gcc -o prova driver.o first.obj asm_io.obj
• Per eseguire il programma prova
●
./prova
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Esercizio 1
• Scrivere un programma NASM che
effettua la somma tra due interi
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Soluzione #1
; file: skel.asm
; This file is a skeleton that can be used to start assembly
programs.
%include "asm_io.inc"
segment .data
;
; initialized data is put in the data segment here
;
operando1 dd 70
operando2 dd 82
A.A. 09/10
segment .bss
;
; uninitialized data is put in the bss segment
;
segment .text
global _asm_main
_asm_main:
enter 0,0
; setup routine
pusha
mov eax,[operando1]
mov ebx,[operando2]
add eax,ebx
popa
mov eax, 0
; return back to C
leave
ret
Architetture degli Elaboratori I
© Danilo Bruschi
Proviamo questa
; file: skel.asm
; This file is a skeleton that can be used to start assembly
programs.
%include "asm_io.inc"
segment .data
;
; initialized data is put in the data segment here
;
operando1 dd 70
operando2 dd 82
A.A. 09/10
segment .bss
;
; uninitialized data is put in the bss segment
;
segment .text
global _asm_main
_asm_main:
enter 0,0
; setup routine
pusha
mov eax,[operando1]
mov ebx,[operando2]
add eax,ebx
dump_regs 1
popa
mov eax, 0
; return back to C
leave
ret
Architetture degli Elaboratori I
© Danilo Bruschi
dump_regs e dump_mem
• La macro dump_regs stampa I byte memorizzati in tutti
i registri (in hex)
• La macro dump_memory stampa invece il contenuto
della memoria, la macro vuole tre argomenti:
• Un intero arbitrario per identificare l’output
• L’indirzzo di partenza da cui iniziare la stampa
• Il numero di segementi di 16-byte segments da stampare
• Esempio
dump_mem
15, prova, 2
• Stampa: “29”, e il contenuto di 32 byte di memoria a
partire dall’indirizzo simbolico prova
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
File oggetto
38
segment .data
39
;
40
; initialized data is put in the data segment here
41
;
42
43 00000000 46000000
operando1 dd 70
44 00000004 52000000
operando2 dd 82
45
46
segment .bss
47
;
48
; uninitialized data is put in the bss segment
49
;
53
segment .text
54
global _asm_main
55
_asm_main:
56 00000000 C8000000
enter 0,0
; setup routine
57 00000004 60
pusha
58 00000005 A1[00000000]
mov eax,[operando1]
59 0000000A 8B1D[04000000]
mov ebx,[operando2]
60 00000010 01D8
add eax,ebx
61
dump_regs 1
62 00000012 6801000000
<1> push dword %1
63 00000017 E8(00000000)
<1> call sub_dump_regs
64 0000001C 61
popa
65 0000001D B800000000
mov eax, 0
; return back to C
66 00000022 C9
leave
67 00000023 C3
ret
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Istruzioni di I/O
• call print_int
• Stampa sul video il valore del numero intero memorizzato in
EAX
• call print_char
• Stampa sul video il carattere il cui valore ASCII è
memorizzato in AL
• call print_string
• Stampa sul video la stringa il cui indirizzo è memorizzato in
EAX; la stringa deve essere “ null terminated “
• call print_nl
• Stampa sul video un carattere di new line
• call read_int
• Legge un intero da tastiera e lo memorizza in EAX
• call read_char
• Legge un singolo carattere da tastiera e memorizza il suo
codice ASCII in registro AL
Architetture degli Elaboratori I
© Danilo Bruschi
Esercizio 2
• Scrivere un programma che chiede in input
due numeri e ne stampa:
•
•
•
•
La somma
La differenza
Il prodotto
Il quoziente intero e l’eventuale resto
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Architetture I
Lez. VI
Assembler: istruzioni di controllo e
gestione stack
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
idiv
• idiv source
• Se source è un operando a 8-bit, allora AX
viene diviso per source, il quoziente è
memorizzato in AL e il resto in AH
• Se source è un operando di 16-bit, allora
DX:AX è diviso per source, il quoziente è
memorizzato in AX e il resto in DX
• If the source is 32-bit, then EDX:EAX is
divided by the operand and the quotient is
stored into EAX and the remainder into
EDX
Architetture degli Elaboratori I
© Danilo Bruschi
Istruzioni di controllo
•
Abbiamo sinora visto istruzioni che:
•
•
•
•
Vediamo ora alcune istruzioni di controllo, cioè istruzioni che modificano
l’ordine con cui sono eseguite le istruzioni presenti in un programma,
evitando così l’esecuzione sequenziale
I linguaggi di programmazione d’alto livello forniscono costrutti di alto
livello per lo svolgimento di tale operazione noti come strutture di
controllo
•
•
•
Muovono dati tra registri e memoria
Eseguono operazioni aritmetiche
Cicli for, cicli while, if-then-else, ecc.
Il linguaggio assembler fornisce di fatto un’unica istruzione per svolgere
questo compito assimilabile al goto
È comunque possibile tradurre con l’uso dei goto tutte le strutture di
controllo di alto livello
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Istruzioni di Branch
• Un “branch” è un’istruzione che dice:
invece di eseguire la prossima istruzione
esegui l’istruzione il cui indirizzo è indicato
come operando
• Esistono due tipi di
• incondizionato (chiamato anche “jump”)
• Salta sempre
• condizionato
• Il salto dell’istruzione successiva è vincolato al
verificarsi di certe condizioni
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
JMP
• JMP impone il salto ad una istruzione il cui
indirizzo simbolico (label) è specificato
nell’operando
• Esempio:
. . .
add eax, ebx
jmp here
sub al, bl
mvsx ax, al
Queste istruzioni non
saranno mai eseguite!
here:
call print_int
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Branch condizionali
• Indicano la prossima istruzione da eseguire in
base al confronto tra dati
• L’istruzione di confronto di base è
• cmp op1, op2
• L’istruzione modifica alcuni bit del registro
EFLAGS
• All’struzione cmp va fatta seguire un’istruzione
di jump condizionale secondo la seguente
tabella
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Branch condizionali
cmp x, y
signed
unsigned
Instruction
branches if
Instruction
branches if
JE
x=y
JE
x=y
JNE
x != y
JNE
x != y
JL, JNGE
x<y
JB, JNAE
x<y
JLE, JNG
x <= y
JBE, JNA
x <= y
JG, JNLE
x>y
JA, JNBE
x>y
JGE, JNL
x >= y
JAE, JNB
x >= y
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Esercizio
• Tradurre in assembler i seguenti costrutti:
• if (eax >= 100)
• then ebx = 5;
• else ebx = eax + 1;
• for (eax = 1; eax <= 10; eax ++)
• ebx = ebx + eax;
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Istruzione Loop
• L’assembler x86 fornisce anche
un’istruzione per seguire loop
• Si tratta dell’istruzione
• loop <label>
• Che si comporta nel seguente modo
• Decrementa ecx (ecx è l’indice del loop)
• If (ecx != 0), salta all’istruzione label
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Using the loop Instruction
• for (eax = 1; eax <= 10; eax ++)
• ebx = ebx + eax;
• In assembler:
mov ecx, 10
mov eax, 1
loop_start:
add ebx, eax
add eax,1
loop loop_start
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Esercizio
• Scrivere un programma che dato in input
un numero verifica se lo stesso è primo
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Esempio in C
unsigned int guess;
unsigned int factor;
unsigned int limit;
printf(“Find primes up to: “);
scanf(“%u”,&limit);
printf(“2\n3\n”); // prints the first 2 obvious primes
guess = 5;
// we start the guess at 5
while (guess <= limit) {
factor = 3;
// look for a possible factor
// we only look at factors < sqrt(guess)
while ( factor*factor < guess && guess % factor != 0 )
factor += 2;
if ( guess % factor != 0 ) // we never found a factor
printf(“%d\n”,guess);
guess += 2;
// skip even numbers
}
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Indirizzamento indiretto
• Data la dichiarazione
L dd
BCDD749Eh
• Sappiamo che L è l’indirizzo di una locazione di
memoria e che [L] denota invece il contenuto di
quella locazione
• La stessa notazione può essere usata anche con i
registri:
mov eax, L ; memorizza in eax un indirizzo
mov bx, [eax] ; carica in bx i 2 bytes il cui
; indirizzo d’inizio è conenuto in
; eax
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Stack
• Lo stack è una struttura di memoria messa a disposiozne di
ogni programma che viene gestita in modalità Last-In-FirstOut
• Sullo stack possono essere memorizzati solo dati la cui
dimensione è multiplo di 4 byte
• Sullo stack operano solo due istruzioni
• Push: mette qualcosa sullo stack
• Pop: rimuove un dato dallo stack
• L’indirizzo del dato da caricare o rimuovere è contenuto nel
registro ESP
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Stack
• Inizialmente lo stack è vuoto e ESP ha qualche valore
• L’istruzione Push 10 ha questi effetti:
• Decrementa ESP di 4
• Scrive 10 all’indirizzo contenuto in ESP
• L’istruzione pop ebx:
• Carica il dato il cui indirizzo è specificato da ESP in ebx
• Incrementa ESP di 4
• Per accedere ad un dato contenuto nello stack:
• mov eax, [esp]
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Esempio
1
; ESP = 0000001Ch
push
dword
2
; ESP = 00000018h
push
dword
3
; ESP = 00000014h
pop
pop
pop
A.A. 09/10
eax
ebx
ecx
; EAX = 3
; EBX = 2
; ECX = 1
increasing addresses
dword
0000001Bh
0000001Ah
00000019h
00000018h
00000017h
00000016h
00000015h
00000014h
0
0
0
3
little endian
push
0
0
0
1
0
0
0
2
little endian
Se ESP=00000020h
little endian
•
00000020h
0000001Fh
0000001Eh
0000001Dh
0000001Ch
Architetture degli Elaboratori I
© Danilo Bruschi
ESP
• Contiene sempre l’indirizzo dell’ultimo
elemento caricato sullo stack
• Non deve essere usato per altri scopi!
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
PUSHA and POPA
• L’assembler x86 offre due istruzioni particolarmente utili che
consentono il salvataggio “contmporaneo” del contenuto di
tutti i registri del processore sullo stack
• PUSHA: carica sullo stack EAX, EBX, ECX, EDX, ESI, EDI, and EBP
• POPA: ripristina tutti i registri al valore ripristinato aggiornando di
conseguenza il valore di ESP
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi
Esercizio
• Scrivere un programma che chiede in input
due numeri li memorizza sullo stack e ne
calcola:
•
•
•
•
Somma
Differenza
Prodotto
quoziente
A.A. 09/10
Architetture degli Elaboratori I
© Danilo Bruschi