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:
- impostazione del problema e dei relativi dati iniziali
- elaborazione dei dati impostati
- valutazione dei risultati ottenuti
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:
- queste azioni sono in numero finito
- ogni azione può essere svolta in un tempo finito (tempo ragionevole)
- ogni azione successiva alla prima è una diretta conseguenza di quella precedente
- l'algoritmo deve essere interpretabile in modo univoco da chiunque lo applichi
- la sequenza di azioni deve portare sicuramente ad almeno un risultato
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:
- riempire una pentola d'acqua e metterla sul fornello acceso
- versare il sale
- attendere che l'acqua inizi a bollire
- versare la pasta
- attendere per il tempo di cottura specificato nella confezione
- scolare la pasta
- condire la pasta
- servire in tavola
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:
- impostazione dei dati del problema
- elaborazione dei dati
- presentazione dei risultati
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:
- un dispositivo per l'inserimento dei dati (input)
- un dispositivo per l'elaborazione dei dati (unità di elaborazione)
- un dispositivo per la presentazione dei risultati (output)
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:
- il peso del 3 più a destra è 100 = 1
- il peso del 3 centrale è 101 = 10
- il peso del 3 più a sinistra è 102 = 100
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.