Assembly Base con MASM

Capitolo 2: Introduzione


L'idea fondamentale che ha portato alla nascita del computer è legata al fatto che nell'affrontare un qualsiasi problema, non solo matematico, si possono distinguere tre fasi ben precise: La prima e la terza fase sono quelle che possiamo definire "creative" in quanto richiedono l'uso del nostro cervello; viceversa, la seconda fase è sicuramente quella più fastidiosa in quanto, una volta impostato il problema, l'elaborazione dei dati si riduce ad una sequenza di azioni da svolgere in modo meccanico e spesso anche ripetitivo.
Un aspetto piuttosto delicato della seconda fase consiste nel fatto che essa, in certi casi, può richiedere tempi lunghissimi con conseguente aumento esponenziale del rischio di commettere banali errori dovuti a qualche svista; a tale proposito, nel mondo dell'informatica è diventato famoso il caso dell'astronomo francese Charles Delaunay, che verso la metà del XVIII secolo cominciò ad impostare un calcolo algebrico che gli avrebbe permesso di determinare con precisione la posizione della Luna rispetto alla Terra in funzione del tempo. L'impostazione di questo problema è alla portata di qualunque astrofisico, ma ciò che scoraggerebbe chiunque è che lo svolgimento manuale dei calcoli richiede tempi lunghissimi; Delaunay impiegò 10 anni per terminare i calcoli, più altri 10 per la verifica, dopo di che pubblicò i risultati in due grossi volumi!
All'inizio degli anni 70, gli studi di Delaunay cominciarono ad assumere una importanza enorme visto che si stava entrando nell'era dei satelliti artificiali e, come si sa, la Luna si comporta esattamente come un satellite artificiale in orbita attorno alla Terra. Alcuni scienziati della Boeing Aerospace decisero allora di sottoporre a verifica i calcoli di Delaunay e, a tale proposito, scrissero un software capace di risolvere equazioni algebriche; con l'ausilio di tale software installato su un computer dell'epoca, il lavoro di elaborazione e verifica dei calcoli di Delaunay venne eseguito in circa 20 ore, con la scoperta anche di un errore dovuto ad una banale svista!
Questo esempio ci fa capire quanto sia importante trovare il modo di delegare la fase di elaborazione dei dati di un problema, ad una macchina capace di svolgere i calcoli con estrema precisione e in tempi ridottissimi; ed è proprio questo ragionamento che ha spinto gli esseri umani ad inventare macchine di calcolo automatico sempre più complesse ed evolute. Il prodotto finale di questa evoluzione è rappresentato proprio dal computer.

2.1 Il concetto di algoritmo

In generale possiamo dire che il computer può risolvere un qualunque problema rappresentabile con una sequenza precisa di azioni da compiere; tale sequenza viene definita algoritmo. Semplificando al massimo possiamo definire l'algoritmo come una sequenza di azioni aventi le seguenti caratteristiche: Quando si parla di algoritmi, non bisogna pensare necessariamente a problemi di tipo matematico; anche la ricetta per cuocere la pasta, infatti, è un algoritmo costituito dalle seguenti azioni: Le azioni da compiere per cuocere la pasta sono in numero finito e ciascuna di esse viene svolta in un tempo ragionevole nel senso che, ad esempio, il tempo di cottura non richiede certo 20 anni; inoltre, la sequenza delle azioni è chiara ed interpretabile da tutti in modo univoco visto che ogni azione successiva alla prima è un'ovvia conseguenza di quella precedente.

2.2 L'architettura di Von Neumann

Riassumendo quanto appena esposto, possiamo dire che la risoluzione di un problema con l'ausilio del computer può essere separata in tre fasi: Tutte le macchine di calcolo automatico posseggono, in linea di principio, una struttura legata allo schema appena presentato; tale struttura viene definita architettura di Von Neumann, dal nome dello scienziato che l'ha teorizzata. Una macchina basata sull'architettura di Von Neumann è quindi costituita da tre blocchi principali: Consideriamo, ad esempio, un generico computer caratterizzato da una struttura estremamente semplificata; in questo caso possiamo individuare la tastiera come dispositivo di input dei dati. Attraverso la tastiera e un editor di testo possiamo scrivere (cioè, inserire) un programma contenente, sia i dati in input, sia l'algoritmo che dovrà elaborare questi dati; tali informazioni vengono usualmente immagazzinate in un dispositivo chiamato memoria. Come dispositivo per l'elaborazione dei dati possiamo individuare il microprocessore (o CPU); infine, come dispositivo di output possiamo individuare lo schermo del computer.

2.3 Codifica dell'informazione

Una volta stabilito che vogliamo usare il computer per risolvere un problema, rimane da superare un ostacolo che ci appare abbastanza impegnativo: come può una macchina come il computer capire cosa sia un numero, un colore, una lettera dell'alfabeto, un segno di punteggiatura, una figura geometrica, una equazione algebrica?
Per superare questo ostacolo, la cosa migliore da fare consiste nel trovare un sistema che ci permetta di rappresentare queste entità attraverso un apposito codice comprensibile dal computer; appare intuitivo il fatto che il sistema di codifica scelto deve anche essere il più semplice possibile perché questo si traduce poi in una conseguente semplificazione dell'architettura della macchina (semplificazione circuitale, nel caso del computer). Non bisogna fare molti sforzi per capire che il sistema di codifica più semplice consiste nell'utilizzare i numeri, attraverso i quali possiamo rappresentare teoricamente qualunque cosa; vediamo allora quale può essere il sistema di numerazione più adatto per la codifica dell'informazione sul computer.

Nel corso della storia gli esseri umani hanno concepito diversi sistemi di numerazione che possiamo raggruppare in due categorie: la prima comprende i sistemi di numerazione basati su precise regole logico matematiche, mentre la seconda comprende i sistemi di numerazione basati solo su aspetti di carattere simbolico; questa seconda categoria naturalmente è da escludere non avendo alcun fondamento logico. Si può citare, ad esempio, il sistema di numerazione simbolico usato dai romani che inizialmente veniva utilizzato per rappresentare numeri molto piccoli (come, ad esempio, gli anni); ogni volta che si presentava la necessità di rappresentare un numero più grande del solito, si introduceva arbitrariamente un nuovo simbolo.
Nel sistema di numerazione romano il simbolo I rappresenta una unità, il simbolo II rappresenta due unità (una più una), mentre il simbolo III rappresenta tre unità (una più una più una); a questo punto, seguendo la logica, il simbolo IIII dovrebbe rappresentare quattro unità. I romani però come sappiamo hanno introdotto il nuovo simbolo V per indicare cinque unità e hanno rappresentato quattro unità con il simbolo IV (cinque meno una); in sostanza, se la barretta appare alla destra del simbolo V viene sommata, mentre se appare alla sinistra viene sottratta. Seguendo sempre la logica, per rappresentare dieci unità dovremmo allora scrivere VV (cinque più cinque); anche in questo caso però i romani hanno introdotto il nuovo simbolo X per rappresentare dieci unità. Questa situazione poco razionale si ripete con il simbolo L che rappresenta cinquanta unità, con il simbolo C che rappresenta cento unità e così via; appare evidente il fatto che non avrebbe alcun senso pensare di implementare sul computer un sistema di questo genere. Tra i principali difetti del sistema di numerazione romano si può citare l'ambiguità di rappresentazione (ad esempio, per rappresentare otto unità si potrebbe utilizzare il simbolo VIII, oppure il simbolo IIX); un altro difetto è dato dall'assenza di un simbolo per la rappresentazione dello zero.

Non ci resta allora che ricorrere ai sistemi di numerazione basati su precise regole logico matematiche; tra questi sistemi di numerazione, il più razionale appare quello anticamente in uso presso le popolazioni Indù e trasmesso poi attraverso gli arabi anche nell'Europa occidentale nel periodo medioevale (per questo motivo si parla anche di "sistema di numerazione arabo"). Questo sistema di numerazione consiste nel definire un insieme S di simboli chiamati cifre; una sequenza di cifre rappresenta un numero. Il numero dei simboli usati prende il nome di base del sistema di numerazione (b); ad esempio, nel mondo occidentale viene utilizzato un sistema di numerazione costituito come sappiamo dal seguente insieme di dieci simboli:
S = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Proprio per questo motivo si parla di sistema di numerazione in base 10 (b = 10).
Il sistema arabo viene chiamato anche posizionale e questa è sicuramente una delle caratteristiche più importanti; posizionale significa che le cifre che compongono il numero assumono un diverso peso a seconda della loro posizione nel numero stesso. Nel caso del sistema arabo, il peso delle cifre cresce scorrendo il numero da destra verso sinistra; si ricordi a tale proposito che gli arabi scrivono da destra verso sinistra.
Consideriamo, ad esempio, il numero 14853 espresso nel sistema di numerazione posizionale in base 10; possiamo dire che la cifra 3 (cifra meno significativa) ha posizione p=0, la cifra 5 ha posizione p=1, la cifra 8 ha posizione p=2, la cifra 4 ha posizione p=3 e infine la cifra 1 (cifra più significativa) ha posizione p=4.
Per capire bene questo concetto importantissimo, si osservi che i 10 simboli usati rappresentano le cosiddette unità (zero unità, una unità, due unità, ... , nove unità); aggiungendo una unità a nove unità, non sappiamo come rappresentare questo numero perché non ci sono più simboli disponibili. Invece di inventare un nuovo simbolo (come nel sistema di numerazione dei romani), passiamo alle decine, cioè ai gruppi di dieci unità rappresentando il numero:
9 + 1
come
10
(cioè, zero unità più una decina); con le decine possiamo arrivare sino a 99 (nove unità più nove decine). Aggiungendo una unità a 99 unità, ci imbattiamo nella situazione precedente; introduciamo allora le centinaia, cioè i gruppi di cento unità rappresentando il numero:
99 + 1
come
100
(cioè, zero unità più zero decine più un centinaio). A questo punto il meccanismo è chiaro e si intuisce subito che con questo sistema si possono rappresentare infiniti numeri.
Vediamo un esempio pratico rappresentato dal numero 333; il numero 333 è formato da tre cifre uguali ma il loro peso è differente. Osserviamo, infatti, che il 3 di destra rappresenta tre unità, il 3 centrale rappresenta tre decine, mentre il 3 di sinistra rappresenta tre centinaia; possiamo scrivere allora: Dato un numero n in base b formato da i cifre aventi posizione:
p = 0, 1, 2, ... , i - 1
(a partire dalla cifra più a destra), si definisce peso di una cifra di n la potenza di ordine p della base b; dato allora il numero 333: Con questo sistema è facile rappresentare anche i numeri con la virgola; ad esempio: Possiamo dire in definitiva (anche per quanto si vedrà in seguito) che il sistema posizionale arabo appare il più adatto per la codifica dell'informazione in una macchina di calcolo automatico.

Abbiamo già incontrato un primo esempio di applicazione di questo sistema di codifica tramite i numeri e cioè, il set di codici ASCII che definisce un insieme di 256 simboli riconosciuti dai PC di tutto il mondo; si tratta di un vero e proprio alfabeto del computer costituito da codici di controllo, lettere maiuscole e minuscole dell'alfabeto anglosassone, cifre, segni di punteggiatura, etc. Questi 256 simboli vengono rappresentati da 256 codici numerici (da 0 a 255) che permettono a qualunque PC di interpretare allo stesso modo una determinata sequenza di codici; ad esempio, la sequenza:
65 115 115 101 109 98 108 121
verrà visualizzata da un editor di testo come la stringa:
Assembly
su qualunque PC e in qualunque parte del mondo.
Naturalmente, con la diffusione planetaria dei computer e con l'interconnessione mondiale tra computer via Internet, 256 simboli appaiono assolutamente insufficienti (si pensi solo all'enorme numero di simboli linguistici usati in Cina, Giappone, Russia, India etc.); per questo motivo si sta diffondendo internazionalmente tra i computer il nuovo sistema di codifica UNICODE che permette di rappresentare decine di migliaia di simboli. Per garantire la cosiddetta "compatibilità verso il basso", i primi 128 simboli UNICODE coincidono esattamente con i primi 128 simboli del codice ASCII.
Per maggiori informazioni si può visitare il sito ufficiale del Consorzio Unicode.

Oltre ai codici ASCII e UNICODE, si possono fare numerosi altri esempi di codifica numerica dell'informazione sul computer; considerando, ad esempio, la tastiera, ad ogni tasto viene associato un codice numerico chiamato codice di scansione. Questi codici di scansione o Scan Codes, permettono al computer di sapere quale tasto è stato premuto; generalmente, i programmi di gestione della tastiera leggono il codice di scansione del tasto appena premuto e lo convertono nel codice ASCII del simbolo stampato sul tasto stesso.
Un altro esempio è legato al mouse il cui programma di gestione determina, istante per istante, le coordinate x, y (ascissa e ordinata) della posizione del cursore sullo schermo; ad ogni pulsante del mouse, inoltre, viene associato un apposito codice numerico che ci permette di sapere se il pulsante stesso è premuto o meno.
Un ulteriore esempio riguarda la gestione dello schermo grafico del computer; supponendo, ad esempio, che una schermata grafica sia costituita da una matrice di 320 x 200 punti (pixel) e che ciascun punto possa assumere uno tra 256 colori distinti, possiamo rappresentare tutti i punti dello schermo attraverso un vettore di 320 x 200 = 64000 elementi dove ogni elemento è un numero compreso tra 0 e 255 (che rappresenta il colore del punto stesso). Per accedere in lettura o in scrittura ad un pixel dello schermo, basta specificare l'indice (cioè un numero compreso tra 0 e 63999) del vettore dei pixel; a questo punto è possibile leggere il colore di quel pixel o modificarlo.

Tutte le considerazioni appena svolte ci fanno capire che per impostare sul computer un determinato problema, dobbiamo prima convertire il problema stesso in una serie di codici numerici, cioè in un formato comprensibile dal computer; questa fase prende anche il nome di codifica dell'informazione. A questo punto si passa alla fase di elaborazione che viene svolta dalla CPU del computer; è facile convincersi che questa fase consiste nell'applicazione di una serie di operazioni matematiche sui codici numerici appena inseriti. Per rendercene conto, vediamo un esempio pratico molto semplice.
Supponiamo di avere una sequenza di lettere dell'alfabeto, cioè una stringa di testo, che vogliamo convertire nella stessa stringa scritta però tutta in maiuscolo; è chiaro che la stringa, per poter essere gestita dal computer, deve essere espressa sotto forma di codici numerici. Come abbiamo visto in precedenza, la codifica di una stringa può essere effettuata attraverso il codice ASCII; in sostanza, possiamo dire che una stringa di testo viene gestita dal computer sotto forma di vettore di codici ASCII. Una volta che la stringa è stata convertita in codici numerici, la fase di elaborazione consiste in una sequenza di istruzioni che esaminano il codice numerico di ogni carattere e, se questo codice appartiene ad un carattere minuscolo, lo convertono nel codice del corrispondente carattere maiuscolo; l'insieme di tutte queste istruzioni costituisce il programma di elaborazione e cioè, l'algoritmo che effettua la conversione da minuscolo in maiuscolo.
La fase di output consiste nel mostrare sullo schermo questa nuova sequenza di lettere convertite in maiuscolo; in questo modo l'utente può vedere sullo schermo il risultato delle elaborazioni.

Il programma che elabora la stringa è molto semplice; osservando, infatti, la tabella dei codici ASCII, possiamo notare che le 26 lettere minuscole dell'alfabeto inglese, hanno codici numerici che vanno da 97 a 122 e, inoltre, questi codici sono consecutivi e contigui. Le 26 lettere maiuscole hanno, invece, codici numerici che vanno da 65 a 90 e anche in questo caso questi codici sono consecutivi e contigui; di conseguenza, tra il codice di una lettera minuscola e il codice della stessa lettera maiuscola esiste una distanza fissa pari a 32. Nel caso, ad esempio, della lettera 'a' abbiamo: È chiaro quindi che, per convertire una lettera minuscola in maiuscola, basta sottrarre 32 al suo codice numerico; il programma di elaborazione (scritto in pseudo codice) è costituito allora dalle seguenti istruzioni: Come si può notare da questo esempio, tutto sul computer è rappresentato da numeri; le stringhe di testo non sono altro che una sequenza di codici ASCII (cioè una sequenza di numeri). Per sapere se un carattere della stringa è minuscolo, bisogna verificare se il suo codice numerico è compreso tra 97 e 122 (confronto tra numeri); per convertire un carattere minuscolo in maiuscolo bisogna sottrarre al suo codice numerico il valore 32 (sottrazione tra numeri).

Visto e considerato che qualunque informazione viene gestita dal computer attraverso codici numerici, l'elaborazione di queste informazioni consiste nell'eseguire su tali codici numerici delle operazioni matematiche; la conseguenza di tutto ciò è che, per capire il modo di lavorare del computer, bisogna studiare in dettaglio quella che possiamo definire la matematica del computer. Questo è proprio l'argomento del prossimo capitolo.