|
|
Le mie prime esperienze di programmazione furono sull'elaboratore
Selenia GP-16. Il linguaggio di programmazione era il cosiddetto "assembler" (altrimenti detto "codice mnemonico"). A
differenza del "codice macchina" (assolutamente
incomprensibile per un normale essere umano, costituito com'era da
interminabili sequenze di numeri in formato esadecimale),
l'assembler era costituito da una limitata serie di semplici
istruzioni: "ADD" per "addiziona", "DEC" per
"decrementa", "JMP" per "jump" (salta: per
saltare ad una determinata locazione di memoria), e così
via.
Ogni singola routine doveva essere composta di istruzioni elementari,
al termine delle quali il programma doveva "saltare" (con
l'istruzione di "jump") o "diramarsi" ad un'altra
routine con l'istruzione di "branch" (letteralmente:
ramo): in pratica un salto condizionato al verificarsi di una
condizione, quale un bit settato a zero o a uno, il valore di uno
specifico "registro" del processore, ecc. ...
Per realizzare une semplicissima operazione, quale una moltiplicazione fra due numeri (interi: lasciamo stare le operazioni in virgola
mobile ...) occorreva creare un complicatissimo loop di programma del
tipo:
SET-UP REGISTRI
- carica il numero da moltiplicare (moltiplicando) nel
Registro A
- carica il numero per cui moltiplicare (moltiplicatore) nel
Registro B
- predisponi il risultato (per ora uguale a zero) nel Registro
C
ROUTINE DI MOLTIPLICAZIONE
- somma il contenuto del Registro A (moltiplicando) al
contenuto del Registro C (risultato)
- decrementa di un'unità il valore del contenuto del Registro B (moltiplicatore)
- se il contenuto del Registro B (moltiplicatore) è
uguale a 0 (ZERO) vai a "FINE MOLTIPLICAZIONE" (istruzione di
branch), altrimenti riesegui "ROUTINE DI MOLTIPLICAZIONE"
FINE MOLTIPLICAZIONE
- il Registro C (risultato) contiene il valore della
moltiplicazione tra i due numeri
Questo in linea di principio: la scrittura del programma reale era
leggermente più complessa di quanto illustrato, e la sua
correttezza di esecuzione andava verificata attentamente prima di
potervi fare affidamento.
Per scrivere il programma, occorreva caricare in memoria
dell'elaboratore (con le modalità precedentemente illustrate)
il programma su nastro perforato che conteneva l'editor, dopo
di ché si procedeva con la digitazione (su telescrivente)
delle istruzioni (nel caso si partisse da zero) o si leggeva un
programma pre-esistente da modificare (sempre caricandolo dal lettore
di nastro perforato).
Terminata la sessione di editing (scrittura o modifica di un
programma) si poteva stampare sulla telescrivente il programma stesso
(ottenendone il listato, su cui si potevano rileggere e
controllare a vista la sequenza delle istruzioni) e lo si poteva (e doveva ...) perforare su nastro, per memorizzarlo e per renderlo
riutilizzabile/caricabile successivamente dall'elaboratore. Se la
telescrivente era rumorosa quanto una mitragliatrice durante la
lettura di un nastro perforato, potete solo immaginare come potesse
essere durante la sua perforazione ....
Prima di poter utilizzare questo programma sull'elaboratore, era
indispensabile compiere però altri passi, il primo dei quali
era costituito dalla verifica della sua corretta sintassi, e della
rispondenza della sequenza di istruzioni alle effettive
funzionalità che da loro ci si aspettava. A tale scopo si
caricava in memoria il programma di "de-bug", con il quale era
possibile lanciare in esecuzione "passo-passo" il programma in
linguaggio assembler verificandone il funzionamento. Se
qualcosa non funzionava (ed accadeva spesso: quando mai un programma
appena scritto funziona al primo colpo?) occorreva annotarsi le
modifiche, ri-caricare l'editor, modificare il programma,
ri-stamparlo, ri-perforarlo, e quindi ri-verificarlo con il programma
di de-bug.
Se, alla fine di svariati cicli di correzione / modifica il programma
si comportava in maniera ineccepibile, si era pronti per la fase
successiva.
ALTRO GIRO, ALTRO REGALO
Il programma così ottenuto non era ancora adatto (purchè
fosse scritto correttamente, e senza errori) all'utilizzo
sull'elaboratore: infatti era scritto in linguaggio assembler,
comprensibile dagli esseri umani (purchè adeguatamente
istruiti allo scopo) ma non dall'elaboratore, che conosceva solo il
linguaggio macchina.
Occorreva quindi caricare in memoria un altro programma (altro giro
di nastro ...) chiamato "compilatore", il cui compito era di
tradurre il programma appena scritto in linguaggio assembler
(detto "sorgente") in uno equivalente, ma scritto in codice
macchina (o "compilato"), e quindi eseguibile
dall'elaboratore.
Avviato il compilatore, e letto il sorgente, lo si
convertiva quindi in compilato, creandone una copia su nastro
perforato.
Questa copia era (potenzialmente) eseguibile direttamente
dell'elaboratore: naturalmente era indispensabile una attenta
verifica "sul campo", prima di affidare qualsiasi compito degno di
nota al nostro programma. Lo si caricava quindi in memoria, e lo si
mandava in esecuzione: salvo imprevisti (tutt'altro che infrequenti ...) il programma "girava" senza errori, assolvendo alle
funzioni che gli erano richieste.
Risulta quindi chiaro che le attività di programmazione,
stante tali modalità operative, costituivano una vera
"passione" (attribuendo a tale termine il significato
biblico di passione: da interpretarsi quindi come
pena, sofferenza e tormento ...).
|
|