Articolo pubblicato sul n. 22 di MCmicrocomputer (Edizioni Technimedia Srl - Roma) nel settembre 1983
BASAL 2.1 Nel comune lessico dell'informatica, oltre ai termini" BASIC", "PERSONAL" e... "MC" (!), ne esistono altri come "Programmazione strutturata", "Codice oggetto", "Compilatore" che, pur essendo indubbiamente meno diffusi, non sono meno importanti. In questo articolo ci occuperemo per l'appunto del "nidificato mondo delle strutture tipo scatole cinesi" del quale probabilmente un po' tutti avranno sentito parlare, ma purtroppo ben pochi avranno provato l'ebbrezza dell'esperienza diretta. Se non si ha come minimo a disposizione un sistema Apple Il + Language Card + Pascal, niente da fare: si resta in castigo a "giocare" col BASIC. Per tentare di ovviare a questo inconveniente, sempreché ci perdoniate questo piccolo peccato di gioventù, abbiamo inventato ex nono per voi un mini-mini-linguaggetto strutturato, supportato nientepopodimeno che dal BASIC. A ragione, qui qualcuno si sarà già messo le mani nei capelli. Di fatto però il BASAL 2.1 (questo il nomignolo-fritto-misto fra BASIC e PASCAL) non ha il più pallido intento di sembrare un mezzo serio per programmare. Lo scopo è solo quello di diffondere queste benedette scatole cinesi al di là delle varie pagine di libri e riviste che trattano questo tema, proponendo il programma BASIC listato in queste pagine che creerà l'ambiente adatto. Sarà cosi possibile adoperare il BASAL direttamente sul vostro Personal: compito del programma è appunto quello di trasformare in BASIC i vostri elaborati e di inserirli direttamente in memoria. La versione presentata è adatta all'ultradiffuso VIC-20+16K; essendo però scritta in BASIC abbastanza (ma non totalmente) standard, l'adattamento su altri Personal non dovrebbe essere molto difficile. In particolar modo per la subroutine principale (linee 11420-13060) che, accettando in ingresso un programma BASAL contenuto nell'array A$ (N), restituisce all'interno dello stesso il programma BASIC corrispondente. Ci occuperemo ora, prima di parlare del BASAL, di rispolverare alcuni concetti propri della programmazione strutturata nell'intento (speriamo) di chiarire a chi è completamente a digiuno, cosa diavolo c'entrano le scatole cinesi... Due parole per incominciare I vantaggi della programmazione attraverso un linguaggio di tipo strutturato, come il Pascal, il PL/I e l'Algol W, sono innumerevoli. Grazie, ad esempio, alla possibilità di definire ricorsivamente le subroutine, è possibile risolvere determinate classi di problemi che con strutture meno potenti (vedi BASIC) risulterebbero molto più impegnativi. Senza scendere nel merito (tanto più che, come vedremo, non riguarda il BASAL), citiamo soltanto casi come il calcolo del fattoriale, il problema delle torri di Hanoi, la ricerca binaria in un albero, che diventano problemi-bazzecola se programmati ricorsivamente. L'essenza della programmazione strutturata sta comunque nella possibilità di vedere intere sezioni di programma come un'unica istruzione, e quindi nella possibilità di scrivere qualsiasi programma senza usare istruzioni di salto condizionato o incondizionato. Sembra strano ma è vero. Dopotutto è una semplice conseguenza del parlare umano: avete mai visto, o meglio, sentito qualcuno in un discorso pronunciare parole del tipo "GOTO BLA.BLA.BLA"? I linguaggi strutturati sono felicemente molto più vicini al linguaggio umano che a quello di macchina. Facciamo un esempio: abbiamo due numeri: A e B. Se A è maggiore di B stampiamo A, altrimenti stampiamo B. In BASIC una possibile soluzione sarà : 10 IF A>B THEN 40 20 PRINT B 30 GOTO 50 40 PRINT A 50 ... 60 ... Con un linguaggio di tipo strutturato, come il Pascal, avremo: IF A>B THEN WRITE(A) ELSE WRITE(B); che è esattamente la traduzione inglese di quanto scritto sopra. In definitiva, grazie a particolari istruzioni strutturate (nel caso dell'IF abbiamo anche l'ELSE), è possibile spiegare quasi a parole, al calcolatore ciò che dovrà fare. Nei casi in cui il "ciò che dovrà fare" non è una singola istruzione (come WRITE), ma qualcosa di più complesso come due istruzioni o duemila, linguaggi strutturati come il Pascal e l'Algol usano delimitare l'intero blocco con le parole-chiave "BEGIN" (Inizio) e "END" (Fine). Ed è qui che "scatta" il concetto di scatola cinese. All'interno del blocco BEGIN-END è possibile racchiudere qualsiasi altra cosa, anche un intero programma zeppo di altri sottoblocchi "nidificati". È come se con la parola BEGIN si aprisse una nuova parentesi e con END si chiudesse l'ultima parentesi aperta. Altro esempiuccio: indovinate cosa fa questa porzione di programma: IF ALFA>BETA THEN BEGIN MAX:=ALFA; MIN:=BETA END ELSE BEGIN MAX:=BETA; MIN:=ALFA END;
È chiaro a questo punto che bene o male i salti ci sono comunque: è solo che non bisogna esplicitamente nominarli. Per coloro che non credono a ciò e vogliono a tutti i costi mortificare il Pascal o l'Algol inserendo all'interno di un programma volutamente dei salti del tipo GOTO ETICHETT A, niente paura: tanto l'Algol quanto il Pascal (e vedremo... il BASAL), dispongono di quest'istruzione nonostante sia stato dimostrato che se ne può fare comodamente a meno. Il minilinguaggio BASAL 2.1 Prima di descrivere l'intero set di istruzioni, diamo uno sguardo alla generica struttura di un programma BASAL. Useremo esempi con istruzioni a noi più familiari quali il FOR, l'IF e il GOSUB, tenendo però presente che quanto detto vale anche per le altre. Ogni programma BASAL si compone di due parti: il programma principale e, se esistono, le sue subroutine. Ciascuna di queste due parti è a sua volta composta da istruzioni semplici (le operazioni di INPUT, PRINT, gli assegnamenti ecc.) identiche al BASIC, e linee contenenti parole-chiave proprie del BASAL che necessitano delle opportune modifiche per diventare semplici istruzioni BASIC (fase di precompilazione). Bisogna inoltre chiarire che in BASAL non sono ammesse linee multiple (con più statement). Unica eccezione fanno quelle linee che non contengono parole chiave del BASAL ma solo comandi BASIC. Il programma principale inizia sempre con la parola-chiave "BEGIN" e termina con "END". All'interno del programma possono starci altre compound (trad. blocchi: insieme di istruzioni racchiuse da BEGIN e END) anche nidificate l'una dentro l'altra sul tipo delle scatole cinesi. Ogni compound è vista dal BASAL come un'unica istruzione. Dato che tutti gli statement del BASAL accettano come argomento un'unica istruzione BASIC, nel caso sia necessario utilizzare, per esempio all'interno di un ciclo FOR, più istruzioni, basterà aprire una nuova compound e inserire all'interno quante linee si vogliono e di che tipo si vuole. Facciamo due esempi: 1) BASAL BASIC
BEGIN 10 FOR I=l TO 100 FOR I=1 TO 100 20 T=T+l T=T+l 30 NEXT END. 40 END
2) BASAL BASIC
BEGIN 10 FOR I=l TO 100 FOR I=1 TO 100 20 PRINT I BEGIN 30 T=T+l PRINT I 40 Q=Q+I T=T+1 50 NEXT Q=Q+I 60 END END END. Come si può notare, nel primo caso, per I che assume valori da l a 100, si è dovuta ripetere una sola istruzione: T=T + l. Nel secondo caso, dato che le istruzioni da eseguire erano più di una, è stato necessario aprire una nuova compound BEGIN-END. È obbligatorio inoltre aprire nuove compound anche quando l'argomento è un'istruzione singola e contemporaneamente parola-chiave del BASAL. Per esempio: E' SCORRETTO: E' CORRETTO:
FOR I-l TO100 FOR I-l TO 100 GOSUB *CICCIOBELLO* BEGIN GOSUB *CICCIOBELLO* END L'unica istruzione che può essere nidificata è il FOR all'interno di altri FOR. Esempio: BASAL BASIC
FOR X=l TO 5 10 FOR X=1 TO 5 FOR Y=2 TO 7 20 FOR Y=2 TO 7 FOR Z=3 TO 6 30 FOR Z=3 TO 6 A(X,Y,Z)=-l 40 A(X,Y,Z)=-l 50 NEXT Z,Y,X Le parole-chiave del BASAL sono: BEGIN, END, END., GOSUB, GOTO, REPEAT, UNTIL, CASE, OF, FOR, IF, THEN, ELSE, WHILE, DO, *...*. Come dicevamo, dopo il programma vero e proprio vanno posizionate, se esistono tutte le subroutine chiamate dal programma. Ogni subroutine è identificata da un nome racchiuso fra 2 o più asterischi e vale la solita regola: dopo il nome si può porre un'unica istruzione BASIC o una compound BEGIN-END con dentro tutto quello che si vuole. Facciamo un esempio: questo programmino calcola, dati A e B, A! + B!: BEGIN INPUT "A=";A INPUT "B=";B 20 INPUT"A=";A X=A 30 INPUT"B=";B GOSUB *X!* 40 X=A A=X 50 GOSUB140 X=B 60 A=X GOSUB *X!* 70 X=B B=X 80 GOSUB140 PRINT"A!+B!=";A+B 90 B=X END. 100 PRINT"A!+B!=";A+B *X!* 110 END BEGIN 140 FOR I=X-1 TO 2 STEP -l FOR I=X-1 TO 2 STEP -1 150 X=X*I-(X=0):NEXT X=X*I-(X=0) 160 RETURN END
Gli Statement del BASAL 2.1 Per descrivere correttamente il set di istruzioni del BASAL indicheremo con: <ARGOMENTO >: un'istruzione semplice BASIC o un blocco BEGIN-END con dentro ciò che si vuole (compresi volendo anche altri sottoblocchi nidificati). < EXP >: un numero o una variabile o un'espressione matematica composta di simboli, numeri e variabili (2-3*I+Z, ad esempio). < VAR >: una variabile numerica intera o reale. < COST>: una costante numerica. <BOOLE>: un'espressione logica del tipo A>B o (A>B) AND (C = 3) o complicata quanta si vuole. Per ogni caso verrà indicato qualche esempio BASAL con relativa traduzione in BASIC che dovrebbe chiarire ogni dubbio più di ogni commento o spiegazione. 1) IF <BOOLE> THEN <ARGOMENTO 1> ELSE <ARGOMENTO 2> se la prova ha dato esito vero sarà eseguito <ARGOOMENTO 1> altrimenti <ARGOMENTO 2>. Il ramo ELSE è facoltativo. Esempio: BEGIN IF A>0 THEN BEGIN PRINT S D=D+R
END
ELSE BEGIN A=A+l D"'-4 END END.
20 IF A>0 THEN 40 30 GOT090 40 PRINT S 50 D=D+R 60 GOTO 120 90 A=A+l 100 D=-4 120 END 2) FOR <VAR> <EXPl> TO <EXP2> STEP <EXP3> <ARGOMENTO> solo in questa caso <ARGOMENTO> può essere un altro FOR nidificato all'interno di esso. Praticamente identico al BAASIC; lo step può essere omesso se vale 1. Esempio: BEGIN FOR I=1 TO 5 S=S+3 END.
20 FOR I=1 TO 5 30 S=S+3:NEXT
3) CASE <VAR> OF BEGIN <EXPl> <- <ARGOMENTO1> <EXP2> <- <ARGOMENTO2> “ “ OT <- <ARGOMENTOn> END è eseguita l'istruzione o la serie di istruzioni che ha come indice 10 stesso valore delia varia bile indicata nello statement. L'indice OT sta per "otherwise", e facoltativo e indica l'istruzione 0 la serie di istruzioni da eseguire negli altri casi. E obbligatorio racchiudere I'insieme dei casi in un blocco BEGIN-END. Esempio: BEGIN CASE A OF BEGIN 3<-D=D*F 2<-BEGIN D=D*F PRINIT Q END OT<-C=C*F END END.
40 IF A=3 THEN D=D*F:GOT0110 50 IF A<>2 THEN 90 60 D=D*F 70 PRINT Q 80 GOTO 110 90 C=C*F 110 END
4) REPEAT " " " UNTIL <BOOLE> È l'unica istruzione che non necessita compound quando le istruzioni da ripetere sono più di una. Praticamente è un loop condizionato: l'insieme di istruzioni racchiuse fra REPEAT e UNTIL è ripetuto fino a quando la prova è vera. Dato che la prova è situata alla fine del blocco, esso sarà eseguito sempre almeno una volta. Esempio: BEGIN REPEAT 30 A=A+3 A=A+3 40 D=D-3 D=D-3 50 IF NOT(A+D<12)THEN30 UNTIL A+D<12 60 END END.
5) WHILE <BOOLE> DO <ARGOMENTO> L'istruzione o la serie di istruzioni sono ripetute fintantoché la prova è vera; al contrario del REPEAT... UNTIL... , se la prova risulta subito falsa è saltato tutto l'argomento. Esempio: BEGIN WHILE P<>A-Z DO 20 IF NOT(P<>A-Z)THEN70 BEGIN 40 D=D*F D=D*F 50 A=A-E A=A-E 60 GOTO 20 END 70 END END.
6) FOR <VAR> = <COSTI >; <COST2>; ...; <COSTn> DO <ARGOMENTO>
Per la variabile indicata che assume i valori indicati nello statement e nell'ordine dato, è eseguito l'argomento. Può essere usato una sola volta nel programma a condizione che non vi siano DATA e non venga usata la variabile II: Esempio:
BEGIN FOR J=2;4;5;-1;0 DO 20 RESTORE:DATA2,4,5, BEGIN -1,0:FOR II=1 TO 5:READJ S=-SD-J 40 S=SD-J PRINT J+3;J-3 50 PRINTJ+3;J-3 END 60 NEXT END. 70 END
7) *NOME ETICHETTA* oppure *NOME ETICHETTA* <ARGOMENTO> Serve per inserire etichette nel programma e per identificare le subroutine. È obbligatorio che ogni etichetta sia puntata da almeno un'instruzione di GOTO o GOSUB. Esempio: BEGIN *ANDREA* S=S-R 30 S=S-R L=L-4 40 L=L-4 GOSUB *ORNELLA* 50 GOSUB 90 GOTO *ANDREA* 60 GOTO 30 END. 70 END *ORNELLA* 90 PRINT "BASAL 2.1":RETURN PRINT "BASAL 2.1" A questi vanno chiaramente aggiunti tutti gli altri statement (INPUT, PRINT, READ, DATA, REM, OPEN, CLOSE ecc.) che non necessitando precompilazione (solo il numero linea è aggiunto) saranno scritte come istruzioni BASIC nella abituale sintassi del BASIC. La fase di precompilazione Per trasformare un programma BASAL nel corrispondente "fratello" in BASIC, il precompilatore compie essenzialmente i seguenti cinque passi: 1) È individuata l'istruzione da tradurre (le linee non contenenti parole-chiave del BASAL non sono modificate). 2) È analizzato l'argomento di tale istruzione (semplice o compound?). 3) Nel caso di compound è ricercato l'indirizzo dell'END relativo al BEGIN. 4) A seconda del tipo di istruzione (for, if, while, ecc.) avvengono le specifiche trasformazioni del caso. 5) È aggiunto il numero linea. Facciamo un primo esempio: vediamo come il precompilatore tradurrebbe questa porzione di programma: BEGIN FOR I=1 TO 10 H=H+1 END. Esso è memorizzato all'interno dell'array A$(I), nelle prime 4 locazioni;quindi A$(l) = "BEGIN";A$(2) = "FOR I=l TO 10" A$(3) = "H=H+l"; A$ (4) = "END.". La fase iniziale della precompilazione inizia dalla stringa A$(2). Troviamo un FOR: è questa una parola chiave del BASAL, quindi da tradurre. L'argomento è un'istruzione semplice: l'unica trasformazione è data dall'assegnamento A$(3) = A$(3) + ":NEXT". Con l'aggiunta del numero linea e la cancellazione del BEGIN iniziale e dell'END finale otteniamo il corrispondente programma BASIC. Facciamo un altro esempio: è da tradurre il programma: BEGIN IF A>0 THEN B=B+1 ELSE BEGIN H=H+3 A=A-1 END END. Come sempre si trova memorizzato all'interno dell'array A$(I), questa volta nelle prime 9 locazioni. L'IF è un po' più complicato da tradurre in quanto vi sono più cose da controllare. In questo caso il ramo THEN è composto da una istruzione: (A$(3) = "B=B+1"). Occorre concatenare la stringa 3 alla stringa 2 e cancellare il contenuto di A$(3), quindi A$(2) = A$(2) + A$(3) e A$(3) = "". Il secondo passo è controllare se esiste il ramo ELSE e in caso positivo, il nostro, calcolare (leggi: "cercare a tentoni") dove termina. Essendo questo ramo caratterizzato da una compound BEGIN-END, è cercato l'indirizzo (il numero di stringa) dell'END relativo al nostro BEGIN. In questo caso: 8. Dato che, nel caso in cui si verifica che A>0, si dovrà eseguire B = B + 1 e saltare tutto il ramo ELSE, basta aggiungere ulteriormente alla stringa 2 un "GOTO (fine ramo else)", quindi: A$(2)=A$(2)+ "GOTO"+STR$((J+l)*10) dove, in questo caso, J vale appunto 8. Anche la stringa 4 è annullata; il nostro programma è diventato: BEGIN IF A>0 THEN B=B+1:GOTO 90 BEGIN H=H+3 A=A-1 END END. Ci siamo quasi: non resta che togliere tutti i BEGIN e gli END; sostituire a "END." la stringa END e aggiungere il numero di linea (dato dall'indice di stringa moltiplicato per l0) a tutte le stringhe non nulle, ottenendo: 20 IF A>0 THEN B=B+1:GOTO 90 60 H=H+3 70 A=A-1 90 END Che è per l'appunto il programma BASIC corrispondente al programma BASAL da cui siamo partiti. Tutto qui. BASAL 2.1: note al programma Buona parte del listato BASIC presentato nelle pagine precedenti funge da sistema operativo per il BASAL. È infatti possibile registrare programmi su nastro, rileggerli, eseguire l'edit di linea, list su stampante, su video e usare molte abbreviazioni nella fase di input. Facendo partire l'esecuzione del programma appare il menu: si accede alle varie opzioni schiacciando le lettere indicate; dove la lettera è in campo inverso vuol dire che per evitare pressioni accidentali, bisogna schiacciarla insieme allo Shift. Per il list su stampante basta premere il tasto Commodore e la lettera "L". Per input-are un programma BASAL basta premere da menu Shift "N" che sta per Nuovo-Programma. Essendo obbligatorio il BEGIN iniziale, esso viene posto automaticamente in memoria e quindi richiesta la seconda linea. Dato che il VIC non accetta in input alfanumerico stringhe contenenti virgole, nel caso sia necessario inserire linee con questo carattere, basterà sostituirlo con il carattere "!" che si ottiene digitando Shift "-" (meno). Ad esempio: per input-are la linea A% = MX (315) si dovrà digitate A%= MX (315). E veniamo alle abbreviazioni concesse: la prima è il punto interrogativo che sta per PRINT. Tutte le altre si attivano con il primo carattere dello statement seguito da uno spazio bianco. Così per scrivere CASE G OF si potrà facoltativamente digitare C G OF prima del return di linea. Le parole chiave che si possono abbreviare sono: CASE, GOSUD, INPUT, BEGIN, UNTIL, WHILE, REPEAT, THEN. Per BEGIN e REPEAT, che non sono seguite mai da altro, si può omettere lo spazio e digitare rispettivamente B e Return o Re Return. Finita l'operazione di Input, premere nuovamente Return e, dopo il list, qualsiasi tasto, ad eccezione di quelli indicati nel menu, per tornare al menu. Digitando "L" si ha il listing su video del programma BASAL. Essendo lo scroll molto veloce, è possibile arrestarlo momentaneamente tenendo premuto o lo Shift o il tasto Commodore o CTRL. Chiaramente è possibile usare anche lo Shift Lock che permette di arrestare lo scroll senza tenere impegnato alcun dito. Il byte della memoria del VIC che "sente" la pressione di questi tre tasti è il 653 (vedere linea 10750). L'edit avviene una linea per volta (sul tipo delle programmabili) e per attivarlo basta digitare "E", seguita da Return solo se si è in fase di input. Con i tasti CRSR-su e CRSR-giù si scorre il programma avanti e indietro; con CRSR-destra e CRSR-sinistra ci si posiziona sui caratteri da correggere. Digitando invece ''I'' o "D" prima di CRSR-des, o sin. si possono inserire o cancellare delle linee (ad ogni pressione). Per comprendere meglio come funziona l'edit facciamo un esempio: digitate questo mini programma: BEGIN PRINT Q I=I+1 END.
e dopo essere tornati al menu decidiamo di sostituire alla Q una T e di togliere la linea d'assegnamento. Premere "E" per andare in edit, una volta CRSR-giù per posizionarsi sulla linea 2 e tramite CRSR--destra sulla Q. A questo punto digitiamo "T" per la sostituzione, return per reinserire la linea corretta, una volta CRSR-giù per posizionarsi sulla linea 3 e il tasto "D" per togliere la linea con l'assegnamento. Per uscire dall'ambiente edit premere return. Se si vogliono aggiungere altre linee di coda al programma, tornati al menu basterà premere "C" che sta per concatenamento. Da menu, "P" sta per precompilazione e serve appunto per precompilare i programmi BASAL e far partire la loro esecuzione. Prima però di essere inserito automaticamente in memoria il nuovo programma viene listato sul video e per procedere basta toccare qualsiasi tasto. Fra la fase di precompilazione e il fatidico "RUN" che fa partire l'esecuzione, vi è una fase alquanto delicata. Il programma BASIC codice oggetto si trova memorizzato all'interno dell'array A$(N) (lo stesso in cui stava parcheggiato il programma BASAL): bisogna trasferirlo dall'array, alla memoria vera e propria del VIC. Per risolvere questo problema, tutto il contenuto delle stringhe è dapprima copiato in una zona protetta dalla memoria (abbassando il top con POKE 56,91) e successivamente ogni linea di programma, prelevata con delle PEEK, viene visualizzata sullo schermo. . A questo punto un CHR$(13), corrispondente al [RETURN] da tastiera, è forzato all'interno del buffer, che scaricandosi sul video provoca l'inserimento automatico in memoria della linea visualizzata. Essendo inoltre l'esecuzione arrestata ad ogni inserimento di linea, è necessario ogni volta visualizzare anche un GOTO 11410 che, con un secondo [RETURN] nel buffer, permette appunto di continuare. Chi è interessato ai dettagli, faccia riferimento alla routine a partire dalla linea 11190. Tutto ciò, per motivi puramente estetici, avviene utilizzando caratteri dello stesso colore dello sfondo, sicché chi sta davanti allo schermo non si accorge di cosa sta succedendo. Togliendo di contro la "E" in campo inverso (importante: solo quella!) nella linea 11380, vedrete apparire in sequenza le linee tradotte nell'attimo in cui finiscono in memoria. Conclusione Vi raccomandiamo a questo punto, se avete intenzione di scrivere qualche programmino in BASAL, di "studiarvi" (si fa per dire!) attentamente quanto detto in merito al linguaggio, ricordandovi che va tutto liscio se e solo se è usato correttamente. Se avete messo qualche BEGIN o END in più o in meno, se cercate di usare istruzioni in modo poco corretto o con sintassi errata non sperate di riuscire ad ottenere il voluto. E ricordate che le prime volte, per qualsiasi cosa, è sempre un casino...
Impaginato originale... Articolo pubblicato su www.digiTANTO.it - per ulteriori informazioni clicca qui |