Introduzione al linguaggio assembly del microprocessore 8086 2a parte Il linguaggio macchina Il linguaggio naturale di un microprocessore è il linguaggio macchina. Nel linguaggio macchina non esistono riferimenti astratti o simbolici e tutte le operazioni sono eseguite direttamente sui registri o in locazioni assolute di memoria. La programmazione in linguaggio macchina è stata a lungo l’unica possibile, all’inizio dell’epoca del calcolo elettronico, prima dell’introduzione degli assemblatori e dei compilatori. Il linguaggio macchina non è altro che l’insieme delle istruzioni definite per un particolare processore. Ogni istruzione è identificata dal suo codice, di solito riportato in binario o esadecimale. Il linguaggio macchina Il linguaggio macchina si compone di istruzioni alle quali fanno immediatamente seguito i relativi operandi. Un esempio di linguaggio macchina per il processore Intel 8086 è il seguente codice per il confronto del contenuto dell’accumulatore AX con la costante 812h: 001111010001001000001000 Per facilitare la descrizione delle istruzioni e degli operandi, si fa uso della notazione esadecimale in alternativa a quella binaria. L’esempio precedente assume in esadecimale questo aspetto: 3D1208 Con debug Con l’uso della calcolatrice scientifica potremo verificare che il valore binario corrispondente è proprio: 001111010001001000001000 Difficile? Resta però difficile lavorare anche con questa notazione. Il codice rimane indistinguibile dagli operandi e solo a fatica, con l’aiuto di una tabella di conversione si riconosce in 3D l’istruzione di confronto. Un certo impegno è anche necessario per la traduzione di 08 12 in 12 08. Infatti viene memorizzato prima il byte della parte bassa e poi il byte della parte alta del numero considerato a 16 bit. Molto !!! Il codice binario (o la sua rappresentazione equivalente esadecimale) usato nel linguaggio macchina è molto scomodo; per l’uomo è molto più facile raffigurare e lavorare con simboli e messaggi piuttosto che con cifre. Una soluzione: I linguaggi assembler o assembly sono stati introdotti proprio per eliminare i problemi di uso del linguaggio macchina. Le caratteristiche principali dei linguaggi assembler sono le seguenti: • In assembler le istruzioni non sono identificate da codici astratti ma da simboli letterali con significato mnemonico. ADD significa ad esempio addizione. • • Alle variabili viene fatto riferimento per nome e non per locazione assoluta di memoria (indirizzo). E’ possibile definire istruzioni macro assembler, composte a loro volta da altre istruzioni, e richiamarle nel programma. Un esempio #include <stdio.h> main() { printf(“ciao \n”); } In Linguaggio C In Linguaggio Assembly DSEG SEGMENT ;segmento dati Outstr db “ciao”,13,10,”$” DSEG ENDS SSEG SEGMENT stack ;segmento catasta dw 32 dup (?) SSEG ENDS CSEG SEGMENT ;segmento codice assume cs:cseg, ds:dseg, ss:sseg start: mov bx,dseg mov ds,bx ;DS=DSEG via bx mov dx,offset outstr ;puntatore a “ciao” mov ah,09h ;uscita su schermo int 21h ;richiamo MS-DOS mov ah,4Ch ;termine programma int 21h ;richiamo MS-DOS CSEG ENDS END start ; termine programma ; inizio a start Complicato? Il primo programma è molto più compatto del secondo, oltre che più facile da leggere e capire. Il programma assembler è a prima vista certamente molto più complicato ed è necessario un certo tempo per analizzarne e comprenderne le funzioni, che in questo caso sono comunque molto semplici. Entrambi i programmi funzionano secondo lo stesso principio, richiamando un modulo del sistema operativo per la presentazione di una stringa sullo schermo. Nel programma in linguaggio C i dettagli di questa chiamata sono nascosti al programmatore al quale è sufficiente scrivere l’istruzione printf . Spetta al compilatore generare la chiamata al sistema operativo, aggiungere alla stringa i codici di controllo “a capo” e “ritorno carrello”, ecc. Nel programma assembler è necessario tenere esplicitamente conto di tutti questi aspetti. Le chiamate al sistema operativo hanno luogo per mezzo dell’istruzione INT 21h. La stringa di uscita è definita nell’area dati; con essa devono essere indicati esplicitamente i codici di “a capo” (ASCII 10) , “ritorno carrello” (ASCII 13) e termine stringa “$”. Facciamo qualche valutazione.. La differenza nello spazio occupato in memoria dai due programmi è evidente quando si passa a compilarli e collegarli. Le dimensioni in byte dei codici sorgente, oggetto (compilato) e eseguibile (collegato) dei due programmi sono qui confrontate: Programma C Programma Assembler Sorgente 57 byte 682 byte Oggetto 578 byte 194 byte Eseguibile 15 Kbyte 610 byte La differenza nelle dimensioni del programma sorgente assembler è dovuta al maggior spazio richiesto per le istruzioni e per i commenti necessari. Il codice compilato è però già più compatto per il programma assembler. La differenza più rilevante si nota dopo che i programmi sono stati collegati alle rispettive biblioteche e routine di servizio: il programma in assembler manca quasi completamente di overhead, che invece caratterizza il programma in C. Il programma scritto in assembler è più rapido a caricarsi e eseguirsi; entrambi i programmi producono lo stesso risultato e non sono distinguibili solo sulla base di quest’ultimo. Concludendo.. Non esistono criteri assoluti per optare per un linguaggio ad alto livello oppure assembler. Se con l’assembler è possibile scrivere programmi più efficienti, è anche vero che la loro stesura prende molto più tempo rispetto allo scrivere programmi in un linguaggio avanzato. Anche la documentazione e la manutenzione di programmi assembler sono più difficili e dispendiosi. La programmazione in assembler resta comunque di attualità in tutti i casi dove con la programmazione ad alto livello si raggiungono i limiti di capacità di memoria o velocità di esecuzione di una macchina. Alcune funzioni, in particolare quelle che agiscono direttamente sulle risorse del sistema, non sono realizzabili se non in assembler. Aspetto non trascurabile della programmazione assembler è il suo carattere didattico. Indipendentemente dal numero e tipo di prodotti software installati in un sistema, il processore, almeno con le architetture attuali, opera su istruzioni di macchina assimilabili a quelle di un programma assembler. L’assembler aiuta quindi a comprendere meglio i meccanismi di funzionamento della macchina.