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