NanoBASIC: la programmazione

Esempio di listato NanoBASIC

In questa terza e ultima parte dell'articolo dedicato a NanoBASIC, approfondiremo gli aspetti tipici della programmazione in linguaggio BASIC con riferimento particolare alla versione sviluppata appositamente per questo progetto. Vedremo la struttura generale di un programma, come funzionano le subroutine, le strutture per il controllo del flusso, l'elenco delle istruzioni/comandi disponibili e qualche esempio di partenza su cui iniziare a sperimentare.

I "dialetti" BASIC

Il linguaggio BASIC ha sicuramente avuto un passato glorioso, è stato sviluppato e distribuito in innumerevoli versioni in particolare nel periodo in cui era forte la presenza degli home computer, ma è stato anche per molti anni presente nei vari sistemi operativi rilasciati dalla Microsoft come ad esempio il QBASIC che fu distribuito fino alla versione di Windows 95. Tuttora, è ancora fortemente apprezzato, esistono diverse versioni sia sotto forma di interpreti che di compilatori. Il QB64 e il FreeBASIC sono ad esempio due compilatori BASIC multipiattaforma che si ispirano al vecchio QBASIC della Microsoft e che sono capaci di compilare ed eseguire il suo codice.

Nonostante sia ancora utilizzato da molti programmatori ha però il difetto di non essere mai stato sottoposto a un processo di standardizzazione. Diversamente da molti altri linguaggi che nel tempo si sono affermati, vedi ad esempio il linguaggio C la cui ultima procedura di standardizzazione risale al 2018, C18 (ISO/IEC 9899), il BASIC non è mai stato sottoposto ad una tale procedura. Così che esistono una grande varietà di implementazioni, chiamate in gergo "dialetti", con caratteristiche di base molto simili ma che poi si differenziano in molteplici aspetti.

NanoBASIC prende spunto dal primo interprete BASIC sviluppato appositamente per microcomputer a 8 bit, il Tiny BASIC, messo a punto tra il 1975 e il 1976, occupava pochissima RAM, in particolare la versione di Palo Alto scritta da Li-Chen Wange e pubblicata su Dr. Dobb's Journal a maggio del 1976 occupava meno di 2kB di RAM. Nella versione originale, il Tiny BASIC  era composto dalle seguenti istruzioni (IF/THEN, GOTO, GOSUB, RETURN, LET, PRINT, INPUT, END) a cui si aggiungevano i comandi di gestione del programma (CLEAR, LIST e RUN). Era in grado di gestire variabili numeriche ed espressioni matematiche contenenti le 4 operazioni di base e gli operatori di confronto (<,>,=) utilizzati nelle istruzioni IF/THEN. Il programma era strutturato come una sequenza di linee contenenti una istruzione (o comando) precedute da una etichetta numerica. Ai giorni nostri sembra quasi un programma banale ma per la tecnologia dell'epoca fu una grande novità. Per chi è interessato ad approfondimenti storici sul linguaggio BASIC, in Bibliografia (vedi fine dell'articolo) ho messo il link ad un interessante libro che approfondisce tali aspetti.

La struttura di un programma in NanoBASIC

Di seguito riporto, a titolo di esempio, quella che è l'articolazione di base di un programma scritto in NanoBASIC:

100 REM 'programma in NanoBASIC'

110 [istruzione1]:[istruzione2] ... [istruzioneN]
120 [istruzione1]:[istruzione2] ... [istruzioneN]
...
...
... 
999 END

1000 REM 'subroutine area'
1110 REM 'subroutine 1'
1120 [istruzione1]:[istruzione2] ... [istruzioneN]
1130 [istruzione1]:[istruzione2] ... [istruzioneN]
...
...
1200 RETURN

2110 REM 'subroutine 2'
2120 [istruzione1]:[istruzione2] ... [istruzioneN]
2130 [istruzione1]:[istruzione2] ... [istruzioneN]
...
...
2200 RETURN

3110 REM 'subroutine n'
3120 [istruzione1]:[istruzione2] ... [istruzioneN]
3130 [istruzione1]:[istruzione2] ... [istruzioneN]
...
3200 RETURN

La struttura generale è abbastanza semplice, ogni linea di codice deve avere una lunghezza complessiva non superiore ai 127 caratteri e  iniziare con un'etichetta numerica intera con valore maggiore di 0 (zero). Il corpo principale del programma (in linguaggio C sarebbe il 'main()') deve terminare con il comando END. Di seguito, al corpo principale vengono collocate le subroutine. Queste ultime sono invocabili tramite il comando GOSUB <nr di linea>, dove <nr di linea> è l'etichetta numerica da cui inizia il codice della subroutine. Una subroutine non è altro che una porzione di codice dedicata ad una elaborazione specifica a cui vengono demandati dei compiti particolari (ad esempio la stampa a video di una serie di risultati, una maschera per inserimento di dati o un calcolo particolare all'interno di un algoritmo più esteso). Le subroutine in pratica altro non sono che una forma "primordiale" delle procedure o funzioni che poi ritroviamo nei linguaggi ad alto livello. Ogni subroutine termina con l'istruzione RETURN; quando l'interprete incontra questa istruzione l'esecuzione del codice riprende dalla istruzione successiva al GOSUB che era stato invocato per accedere alla subroutine. Se un programma è particolarmente semplice e non necessita di subroutine si può evitare l'uso dell'istruzione END.

Un esempio pratico, molto banale, ma che  rende  bene l'idea di come si può far uso delle subroutine è il seguente:

100 REM ' Area di un triangolo '
110 B=0:H=0:AREA=0
120 PRINT "Area di un triangolo :"
130 GOSUB 200
140 GOSUB 300
150 PRINT AREA
160 END
200 REM ' inserimento dati '
210 INPUT "Base del triangolo (cm)   :",B
220 INPUT "Altezza del triangolo (cm):",H
230 RETURN
300 REM 'Calcolo area'
310 AREA = (B*H)/2
320 RETURN

Un semplice problema di geometria piana, si tratta di calcolare l'area di un triangolo; in input vanno forniti i valori della base e dell'altezza e in output viene visualizzato il valore dell'area. Per fare questo, sono state utilizzate due subroutine, la prima che inizia alla riga 200 serve per gestire l'inserimento dei valori di partenza (base e altezza), la seconda che inizia alla riga 300 opera il calcolo e memorizza nella variabile AREA il risultato. Terminata l'elaborazione della seconda subroutine l'esecuzione del programma continua alla riga 150 e termina quando l'interprete trova l'istruzione END.

Le espressioni matematiche e le stringhe di caratteri

Prima di introdurre altri elementi legati alla programmazione in NanoBASIC, diamo una breve descrizione della tipologia di espressioni che l'interprete è capace di gestire e valutare. Per espressione matematica si intende una sequenza di numeri decimali, variabili, elementi di array e funzioni  legate assieme da operatori matematici, di relazione o booleani. La valutazione segue le consuete regole algebriche e il risultato è sempre un valore numerico.

La priorità degli operatori è la seguente

  1. -,+,! operatori unari di segno e di negazione booleana (!)
  2. ^ elevazione a potenza
  3. *,/  moltiplicazione e divisione
  4. +,- addizione e sottrazione
  5. <,<=,>,=,<> operatori di relazione
  6. |,& (or , and) operatori booleani

Naturalmente, l'ordine di valutazione può essere cambiato tramite l'uso delle parentesi tonde '()'

Le funzioni matematiche gestite sono le seguenti:

FUNZIONE SINTASSI DESCRIZIONE
SQRT SQRT(<espressione>) radice quadrata
LOG LOG(<espressione>) logaritmo in base 10
LN LN(<espressione>) logaritmo in base e
EXP EXP(<espressione>) esponenziale in base e
ABS ABS(<espressione>) valore assoluto
SIN SIN(<espressione>) seno
COS COS(<espressione>) coseno
TAN TAN(<espressione>) tangente
ASIN ASIN(<espressione>) arco seno
ACOS ACOS(<espressione>) arco coseno
ATAN ATAN(<espressione>) arco tangente
INT INT(<espressione>) intero

Esempi di espressioni:

-  10.5+(3*12-X^2)
-  (3+3*SIN(4*PI*X))^2
-  3+M(I)-A(J)  'M e A sono due array'
-  M(I)<>3*SQRT(1-X^2) 'M è un array'
-  Y>=3 | X<3*Z+LOG(Y)

La valutazione di espressioni che contengono operatori di relazione e/o operatori booleani darà sempre come risultato i valori 0 (zero) falso o 1 (uno) vero. Terminiamo questo paragrafo con una breve descrizione delle stringhe. Una stringa di caratteri è una qualsiasi sequenza alfanumerica racchiusa tra doppi apici ("), ad esempio:

  • "Salve mondo!"
  • "1) inserisci il valore di X:"

In questa versione di NanoBASIC, non è possibile effettuare operazioni sulle stringhe (concatenazione, calcolo della lunghezza, ecc..) nè è possibile memorizzare una stringa in una variabile. Si possono utilizzare direttamente solo per la stampa a video di messaggi.

Gli Array

Un Array è un insieme di variabili tutte con lo stesso nome a cui è possibile accedere tramite un indice numerico. Occorre sempre dichiarare le dimensioni di un Array ad inizio programma o comunque sempre prima dell'utilizzo. La dichiarazione avviene tramite l'istruzione DIM la cui sintassi è la seguente:

DIM <nome array 1>(<dimensioni>),  <nome array 2>(<dimensioni>), ...<nome array N>(<dimensioni>)

esempio:

DIM M(100), ELENCO(10), VALORI(5)

Nell'esempio sopra vengono dichiarati 3 Array (M, ELENCO, VALORI) di dimensioni rispettivamente (100, 10, 5).

E' possibile accedere ai singoli elementi di un Array, come anticipato, tramite un indice numerico:

 100 DIM VALORI(5)
 110 VALORI(0)=1.5:VALORI(1)=0.5
 120 VALORI(2)=3.1:VALORI(3)=-1
 130 VALORI(4)=2.1
 140 FOR I=0 TO 4
 150 PRINT VALORI(I) 
 160 NEXT
 170 END

L'indicizzazione parte da 0 (zero), quindi il range utile della variabile indice va da 0 a <dimensioni-1>.

Il controllo del flusso del programma

Tra le istruzioni fondamentali che un linguaggio di programmazione deve poter gestire ci sono quelle per il controllo del flusso del programma, ovvero la sequenza con cui devono essere eseguite le singole istruzioni. Nella versione (molto semplice) che stiamo illustrando tali istruzioni si concretizzano in:

  • IF <espressione booleana> THEN <istruzione>
  • GOTO <nr di linea>

La prima è una istruzione condizionale, ovvero, a seguito della valutazione di un'espressione booleana (vero o falso), posta di seguito all'IF, permette o meno l'esecuzione dell'istruzione posta dopo il THEN.

La seconda è una istruzione di salto incondizionata, quando l'interprete la incontra sposta l'esecuzione del programma alla riga indicata in <nr di linea>.

In linea di principio, sono sufficienti queste due istruzioni per implementare la maggior parte degli algoritmi. Naturalmente, al crescere della complessità dei programmi appare chiaro che un uso "massiccio" di salti condizionati o peggio incondizionati, rende di difficile lettura il codice sorgente; motivo per cui nei linguaggi di programmazione più complessi sono presenti diverse tipologie di decisori, come ad esempio le istruzioni di selezione multipla (SELECT CASE) o l'estensione dell'istruzione IF/THEN con la clausola ELSE. L'uso dell'istruzione IF/THEN assieme al GOTO, permette di implementare un'ulteriore struttura di programmazione molto importante, quella dei "loop" o "cicli". Ovvero, una struttura che determina l'esecuzione iterativa di una porzione di codice.

Di seguito, un semplice programma che illustra un problema di fisica, quello della caduta libera di un corpo nel vuoto, soggetto alla sola forza di gravità G. Data un'altezza di caduta H0 (nel nostro esempio pari a 78,4 metri), il programma stampa a video il valore dell'altezza H mano a mano che il corpo cade, con un incremento temporale di 1 secondo, fino all'impatto con il suolo H=0.

100 REM ' Caduta libera ' 
110 G=9.8:H0=78.4 
120 T0=1:T=0 
130 H=H0-0.5*G*T^2 
140 PRINT "T = ",T,": H = ",H 
150 T=T+T0 
160 IF H>0.01 THEN GOTO 130 
170 END

In questo caso, l'istruzione IF verifica ciclicamente il valore H (l'altezza raggiunta dal corpo in caduta) e se è maggiore di zero fa rieseguire (GOTO 130) la porzione di codice che calcola una nuova altezza H al tempo T=T+T0.
Il programma produce la tabella seguente:

T = 0: H = 78.40
T = 1: H = 73.50
T = 2: H = 58.80
T = 3: H = 34.30
T = 4: H = 0.00

dopo T = 4 secondi il corpo ha raggiunto il suolo.


Il ciclo FOR/NEXT

L'istruzione di ciclo FOR/NEXT, da sempre presente in tutti i dialetti BASIC, rende più agevole la scrittura di codice che contiene algoritmi iterativi, ovvero dove sono presenti diversi cicli di calcolo spesso annidati l'uno nell'altro. La sintassi dell'istruzione FOR/NEXT é la seguente:

  • FOR  variabile=<espressione start> TO <espressione stop> STEP <espressione step>

Dove variabile é la variabile di controllo del ciclo inizializzata con il valore di partenza calcolato dalla <espressione start>, successivamente ad ogni iterazione, che avviene quando l'interprete incontra la prima istruzione NEXT, il valore della variabile di controllo é confrontato con quello calcolato dalla <espressione stop> e in base a tale confronto l'interprete decide se avviare una nuova iterazione o se uscire dal ciclo e continuare l'esecuzione del programma a partire dall'istruzione successiva al NEXT.

<espressione step> é un espressione numerica che determina l'incremento o il decremento del valore contenuto nella variabile di controllo del ciclo, se STEP é omesso, la variabile, ad ogni iterazione, viene automaticamente incrementa di 1.

Di seguito un paio di esempi:

100 FOR I=1 TO 10 STEP 2
110 PRINT I
120 NEXT
130 END

Esempio di due cicli FOR/NEXT annidati

100 FOR I=1 TO 5
110 FOR J=1 TO 5
120 PRINT I," : ",J
130 NEXT
140 NEXT
150 END

Attenzione: in questa versione di NanoBASIC, il numero massimo di cicli FOR/NEXT che possono essere annidati è 5 (cinque). Questa cosa vale anche per l'annidamento delle subroutine GOSUB/RETURN.

[...]

ATTENZIONE: quello che hai appena letto è solo un estratto, l'Articolo Tecnico completo è composto da ben 3514 parole ed è riservato agli ABBONATI. Con l'Abbonamento avrai anche accesso a tutti gli altri Articoli Tecnici che potrai leggere in formato PDF per un anno. ABBONATI ORA, è semplice e sicuro.

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend