Articolo pubblicato sul n. 35 di MCmicrocomputer (Edizioni Technimedia Srl - Roma) nel novembre 1984
Commodore 64 (prima parte) Con questo numero inizia una serie di articoli sull'implementazione di nuove istruzioni Basic sul vostro Commodore 64. Il tool presentato (unico nel suo genere) permette una gestione facilitata delle periferiche Commodore che, normalmente, intendono solo in termini di OPEN, CMD e PRINT#. Con l'ADP Basic per conoscere la Directory di un dischetto bastera' digitare CAT, per stampare su carta si usera' LPRINT (di sapore vagamente "ZXiano" ...) per tracciare una linea col plotter il comando DRAW. Procediamo con ordine ... Tre routine Iniziamo subito col dire che per aggiungere nuove istruzioni all'interprete basic, bisogna mettere le mani in un bel po' di roba, quindi facile-facile non e'. E' necessario innanzitutto capire bene come funzionano tre routine del sistema operativo del 64: la Tokenize Routine, l'Execute Statements e la Perform List. Anche se solo accennate nel numero scorso, queste tre sono a capo di tutto il funzionamento dell'interprete Basic. La prima trasforma le linee da noi digitate, in una forma piu' compatta per risparmiare spazio in memoria. Tutte le parole proprie del linguaggio sono trasformate in un opportuno codice a un solo byte. La seconda routine e' invocata ogni qualvolta si deve eseguire uno statement Basic. E' prelevato il codice token che identifica l'istruzione e si cede il controllo al pezzettino di interprete che la esegue. I linguaggi di programmazione interpretati funzionano cosi': per ogni statement si scrive una porzione di programma in linguaggio macchina che lo esegue, raccolte tutte queste "porzioni" se ne invoca una o un'altra a seconda di quale comando bisogna eseguire. In figura 2 e' mostrato il diagramma a blocchi della Execute Statements. Ricordiamo che quando questa va in esecuzione, la linea e' gia' stata tokenizzata. Come riportato nel numero scorso, i codici token utilizzati vanno da 128 a 203. Per l'esattezza, da 128 a 162 sono istruzioni che possiamo trovare a inizio statement, mentre per valori superiori a 162, l'istruzione puo' stare solo nel corpo di un comando piu' complesso o dentro una espressione. Eccezion fatta per GO, codice token 203. La prima delle due diramazioni del diagramma di figura 2, seleziona il caso in cui il primo codice dell'istruzione non sia un token: in questo caso e' invocata il comando LET (assegnamento variabile). La seconda diramazione controlla che il primo codice token appartenga a un comando e non a una funzione, pena segnalazione Syntax Error. Se non siete convinti provate a digitare THEN 100 [Return]. THEN ha codice token 167, maggiore quindi di 162, e non puo' stare ad inizio linea. In figura 1 e' mostrato il diagramma a blocchi della routine Perform List. Serve per riconvertire, all'atto di un LIST, in comandi e funzioni Basic i codici token mantenuti in memoria. La variabile APICI e' usata come flag e serve per by-passare la detokenizzazione dopo l'apertura degli apici. "In che senso ?" ... qualcuno potrebbe obiettare. Chiariamo con esempio: avete presente il cuoricino in reverse che identifica il tasto CLR (clear screen) dopo aver aperto gli apici ? Ebbene, ha come codice ascii 147, lo stesso valore del codice token di LOAD. Quando si esegue un LIST, se il 64 incontra un 147 deve sapere se visualizzare un cuoricino in reverse o scrivere LOAD: in altre parole se sulla stessa linea siano stati aperti Apici (e non richiusi) o meno. Le tre routine sopra descritte fanno uso di alcune tabelle in rom contenenti la lista di tutte le istruzioni e la lista degli indirizzi di partenza di ognuna. In teoria, per aggiungere nuovi comandi, e' sufficiente modificare le tabelle di cui sopra con i nomi e gli indirizzi di partenza dei nuovi statement. E' anche vero pero' che essendo queste su rom, qualsiasi modifica e' impossibile. Mamma Commodore ancora una volta ci viene incontro ponendo in alcune locazione ram gli indirizzi di partenza delle tre routine descritte: invece di modificare le tabelle o le singole ruotine, per inserire nuovi comandi le riscriveremo ex-novo (facenti riferimento a nuove tabelle estese), avendo l'accortezza di modificare gli indirizzi di partenza locati in ram. I byte 772 e 773 ($0304 e $0305) contengono l'indirizzo della Tokenize Ruotine, i byte 776 e 777 ($0308 e $0309) l'indirizzo della Execute Statements e i byte 774 e 775 ($0306 e $0307) l'indirizzo di Perform List. Bastera' praticamente copiare queste routine in Ram (dalla Rom) modificando i puntatori alle tabelle, anch'esse oppurtunamente rilocate in Ram. L'ADP Basic, per non occupare Ram utente (come ormai e' consuetudine della ADP SOFTWARE) si posiziona nei 4 k Ram liberi posti all'indirizzo $C000 (49152 dec.). E' in questa zona di memoria che inseriremo le nuove routine Ram e tutte le porzioncine di interprete, una per ogni nuova istruzione. I codici token utilizzati saranno quelli compresi tra 204 e 254, appositamente lasciati liberi dalla Commodore per espansioni del linguaggio Basic. L'ADP Basic La cosa piu' importante e' inserire nuovi comandi basic senza compromettere il regolare funzionamento delle istruzioni gia' esistenti: in altre parole scongiurare la ben piu' minima incompatibilita' col CBM Basic di cui e' fornito la macchina. Per quel che riguarda le tre routine sopra mostrate, non si corrono rischi, avendo la possibilita' di ricopiarle integralmente dalla rom, apportando naturalmente le dovute modifiche. Il problema piu' grosso resta pero' l'integrazione dei nuovi comandi col resto del basic: la possibilita' di inserire su una stessa linea istruzioni di diversa paternita', usando naturalmente come separatore i ":". Curiosando tra metri e metri di disassembato dell'interprete basic CBM, si scopre che c'e' una istruzione che possiamo sfruttare per arricchire il nostro linguaggio standard. Anche se a qualcuno potra' sembrare strano, si sta parlando del comando DATA. Questo infatti non e' altro che uno statement nullo: quando l'interprete l'incontra, non fa altro che passare a nuova istruzione. E' quando si esegue un READ che l'interprete la cerca tra le linee del programma. Si potrebbe obbiettare: "Anche REM e' un'istruzione nulla€£...". Esatto! ma ha il considerevole svantaggio di far procedere l'interprete a nuova linea, e non a nuova istruzione. Per convincerci di cio' digitate: 10 REM : PRINT "CIAO" e date il RUN. Non si avra' alcun effetto; incontrata la REM (per l'esattezza: il codice token di REM) il Basic del 64 ignora il resto della linea, come se fosse un commento. I ":" in linee di questo tipo non vengono considerati. Resta pero' sempre l'interrogativo principale: perche' mai sfruttare l'esistenza del comando DATA. La risposta e' semplice (almeno si spera !): incontrando un comando ADP BASIC, l'interprete modificato, cedera' il controllo al pezzettino di programma in L.M. che esegue tale istuzione. Al termine, per continuare l'esecuzione del resto della linea, ci sara' un salto brutale all'interpretazione dell'istruzione DATA, che come visto fa proprio quello che a noi serve. Resta inteso che il tutto funziona perfettamente anche se usiamo comandi ADP singolarmente. Nulla ci vieta di imbrogliare ugualmente l'interprete facendogli credere che sia una linea DATA, dopo aver svolto la funzione richiesta. Questo per quanto riguarda la maggior parte dei comandi ADP. I rimanenti sfruttano altri stratagemmi, come vedremo, per assicurare ugualmente la piena compatibilita'. I Comandi Possiamo suddividere l'intero set di comandi dell'ADP Basic in tre categorie:
Analizzeremo dapprima i comandi appartenenti alla prima delle tre categorie, essendo, di fatto, la piu' semplice. Un comando che non necessita di paramentri e' INIT, ed e' usato per inizializzare il driver. Listato 2 rappresenta l'implementazione di tale istruzione. Come si puo' notare, non occupa molti byte. JSR $C03B e' un salto a una subroutine (listato 1) che apre il canale di comunicazione col disco, dopo aver resettato gli altri file aperti. Il file usato per comunicare ha numero logico 72 ($48 in hex). Tornando alla nostra INIT di listato 2, dopo il "salto" a $C03B, si seleziona come canale di output il file $48. Si esegue cioe' LDX #$48 e JSR $FFC9. A questo punto, per l'inizializzazione e' sufficiente "sparare" una "I" al disco: basta un LDA #$49 e un JSR $FFD2; infatti $49 e' il codice Ascii della "I" e $FFD2 e' l'indirizzo della routine che "spara". Per l'elenco e la descrizione delle routine kernal adoperate, si dia uno sguardo al previsto riquadro a pagina XX. Non resta che chiudere il file aperto con un JSR $FFE7 e saltare come promesso (JMP $A8F8) all'implementazione dell'istruzione DATA per proseguire la normale esecuzione di altri statament Basic. Il listato 3 rappresenta l'istruzione VDATE, anch'essa priva di parametri, da utilizzare per convalidare i blocchi usati e liberi di un dischetto. L'unica differenza con la INIT, sta nella "V" inviata al disco in luogo della "I". Il codice Ascii della "V" e' appunto $56, come si puo' notare dalla linea C41B. Analizziamo ora i comandi appartenenti alla seconda categoria, ossia comandi che necessitano di passaggio parametri. Il listato 5 mostra l'istruzione FMAT, da usare (con attenzione) per formattare un dischetto. Ricordiamo che l'operazione di formattazione cancella qualsiasi informazione contenuta sul dischetto. Come per l'uso normale, per formattare un dischetto occorre specificare un nome e facoltativamente un identificatore di due caratteri. L'identificatore e' obbligatorio solo se il dischetto e' nuovo, ossia mai formattato. Facendo un esempio, se intendiamo chiamare un dischetto PIPPO e dare ad esso l'identificatore PP, bastera' digitare: FMAT "PIPPO,PP" se non si vuol specificare l'ID, e' sufficiente: FMAT "PIPPO" Vediamo passo-passo come funziona la FMAT. Torniamo dunque al listato 5. La prima operazione e' il solito "salto" a $C03B per stabilire comunicazione col disco. Indi, come per la INIT e la VDATE, si dichiara il file $48 come canale di output. Segue una "doppietta" costituita da una "N" e dai ":" (codici $4E e $3A). A questo punto bisogna inviare al disco il nome del dischetto e eventualmente l'ID. In un sol colpo, tutto quanto contenuto tra apici. Il programmino FMAT deve recuperare dalla linea Basic la stringa che segue la parola FMAT (nulla vieta di mettere una variabile o una espressione contorta quanto si vuole, purche' di tipo stringa). Per fare cio' ci avvaliamo di una routine del sistema operativo usata dai comandi LOAD, SAVE e VERIFY per leggere (dal video o dal listato) il nome del programma "incriminato". Questa routine e' locata a $E1D4 e una volta invocata, restituisce nel byte $B7 la lunghezza e in ($BB,$BC) l'indirizzo in memoria dove e' stata trasverita la stringa. Il ciclo compreso tra C44C e C454 non fa altro che inviare al disco la stringa (carattere per carattere) posta dopo FMAT. Il tutto si conclude con un JSR $FFCC per chiudere il canale aperto e, tanto per cambiare, un salto a $A8F8. Un piccolo quiz per i lettori: perche' la VDATE non e' stata chiamata VALIDATE e la FMAT, FORMAT€£? Non e' per risparmiare byte, il motivo e' assai piu' serio€£... Il comando RENAME (listato 7), e' molto simile al comando FMAT: uniche sostanziali differenze sono la "R" in luogo della "N" alla linea C473 e la mancanza del JMP $A8F8 finale: al termine l'istruzione casca nell'istruzione FLASH che, come vedremo piu' avanti, visualizza i messaggi del disco. Cio' e' stato necessario per poter implicitamente controllare che il RENAME abbia dato buon esito. La sintassi e': RENAME "NuovoNome=VecchioNome" dove NuovoNome e VecchioNome sono rispettivamente il ... nuovo nome e il vecchio nome del file "preso di mira". I comandi ERASE e COPY, rispettivamente listato 4 e 6, sfruttano l'esistenza della RENAME per non sprecare spazio. Essendo buona parte identica, a un certo punto, sia l'una che l'altra saltano nel corpo della RENAME. La sintassi di ERASE e': ERASE "NomeFile" dove "NomeFile" e' "naturalmente" il nome del file da eliminare. La sintassi di COPY e': COPY "NuovoFile=VecchioFile" oppure: COPY "NuovoFile=VecchioFile1,VecchiFile2" a seconda che si voglia creare sul dischetto un file a partire da uno o piu' file gia' esistenti. Per quanto riguarda l'istruzione DLOAD, la sintassi e': DLOAD "NomeProgramma" e permette di caricare programmi da disco. Per implementare tale comando, ci rifaremo al normalissimo LOAD del 64, che, come arcinoto, per caricare un programma da disco, necessita della specifica "virgola€£otto" a pie' del "NomeProgramma". E' il listato 9. Si', tutto li'. Il Byte $0A indica se vogliamo un LOAD o un VERIFY. Segue un JSR $E1D4 per leggere il "NomeProgramma", e un LDA #$08 STA $BA per scegliere come periferica il disco (che ha appunto come numero di device 8). Infine un salto a $E16F, nel cuore della normalissima LOAD, figlia di mamma Commodore. Di stessa fatta anche DSAVE (listato 10) e DVER (listato 11) per salvare e verificare un programma sul disco. La loro sintassi, come intuibile, e': DSAVE "NomeProgramma" e DVER "NomeProgramma" Gli ultimi due comandi presentati in questa puntata, appartengono all terza delle tre categorie viste precedentemente. Restituiscono cioe' valori sul video: l'interazione e' tra driver e schermo. Il primo e' il comando FLASH e si usa quando la spia del drivre inizia a lampeggiare (flashing) segnalando il verificarsi di un errore. Numero, tipo, traccia e settore vengono mostrati su video e la spia smette di lampeggiare. Se il comando FLASH viene impartito senza il verificarsi del lampeggio, un tranquillizzante messaggio 00,OK,00,00 sara' visualizzato. Il listato 8 e' l'implementazione di tale comando. Notare l'estrema semplicita'. Per motivi prettamente estetici, la prima operazione che si compie, e' di stampare un [RETURN] per far iniziare a nuova riga la scrittura del messaggio di errore. Subito dopo, il classico JSR $C03B stabilisce la comunicazione col disco. LDX #$48 e JSR $FFC6 indicano come input il canale appena aperto. Siamo ora nel vivo del "ciclo": si esegue la lettura di un carattere dal disco e la stampa del medesimo su video, fino a quando l'ultimo carattere (del messaggio di errore) non e' stato letto. La subroutine JSR $FFB7 restituisce in A il valore della Parola di Stato I/O, detta in Basic ST, che assume valore 64 ($40 in hex) appena si e' letto l'ultimo carattere di un file. La ruotine di FLASH termina con un nuovo salto a $C03B per resettare il canale di comunicazione, lasciandolo aperto per futuri dialoghi. L'ultimo comando e' CAT, e serve per visualizzare la directory di un dischetto su video, senza perdere il programma mantenuto in memoria, come generalmente accade con la sequenza: LOAD "$",8 LIST Sul numero 33 abbiamo gia' visto, parlando del trattamento file col driver 1541, come e' possibile leggere da programma la directory di un dischetto. In sunto e' sufficiente aprire un file programma (indirizzo secondario 0) di nome "$" e leggere e stampare uno per volta i caratteri passati dal driver. In maniera molto simile al comando FLASH. Per raffinare un po' la soluzione e rendere piu' elegante la visualizzazione della directory (e come vedremo anche piu' pratica), per ogni file e' stampato dapprima il numero di blocchi occupati, di seguito il tipo del file (PR,SE,RE o US) e in ultimo il nome. Cio' per permettere di posizionarsi col cursore sul file programma da caricare, digitare DLOAD (piu' uno spazio) e battere [RETURN]. Digitando infatti DLOAD, pestiamo il numero di blocchi e il tipo del file, lasciando al momento del [RETURN] la linea pronta per essere eseguita. Il listato 12 e' una routine utilizzata dal comando CAT per visualizzare il numero dei blocchi in modo formattato, con le unita', decine e centianaia incolonnate (dite la verita', incolonnato e' bello€£!). All'indirizzo $C513 ha inizio il programma del comando CAT (listato 13). La prima operazione che si compie e' l'apertura di un file programma di nome "$". Nel byte $A360 c'e' appunto il codice ascii del simbolo "$". Il file e' naturalmente di INPUT, percio' la sequenza LDX #$41 e JSR $FFC6. Seguono qualche JSR $FFCF per posizionarsi su caratteri utili del file dollaro che stiamo leggendo. Il JSR $FFE1 a $C53E serve per controllare la pressione del tasto RUN/STOP: in seguito a questa, e' fermata la lettura e il 64 torna allo stato di READY. La parte "ciclica" della routine si svolge in breve cosi': - si legge il numero di blocchi di un file, caricando la parte bassa in A e la parte alta in X. - un salto a $C4C8 stampa tale valore in modo formattato. - si legge il nome del file, come tutti i caratteri compresi tra l'apertura e la chiusura degli apici, e lo si parcheggia momentaneamente a partire dal byte $02C3. - e' letto il tipo del file e posizionato (i primi due caratteri) nelle locazioni $02C1 e $02C2. - il contenuto delle celle $02C0 e seguenti e' stampato sul video. Il tutto si ferma quando al posto degli apici si incontra la "B" di "BLOCKS FREE" e quindi tutta la directory e' stata letta. Nei prossimi numeri Questo mese ci fermiamo qui. Possiamo darvi qualche anticipazione sul seguito. Sul prossimo numero continueremo la nostra carrellata sugli ADP-comandi rivolti al disco. Vedremo l'APPEND, per attaccare due programmi; il VIEW, per vedere un listato da disco senza occupare la memoria; DISKNAME, per cambiare Nome e ID a un dischetto; TRSE, per curiosare fra le tracce; e ancora: BSAVE e BLOAD per salvare e caricare programmi in linguaggio macchina o piu' in generale porzioni di memoria. EXE per caricare e far partire insieme un programma; RANGE per conoscere l'inizio e la fine di dove verra' locato nella memoria un programma (di qualsiasi genere) mantenuto su un dischetto. Per le stampanti MPS-801 e MPS-802: PRON e PROFF per attivare o disattivare l'output su carta invece che su video; LPRINT per stampare su carta direttamente; HCOPY, per avere una copia su carta del contenuto del video. E molti altri ancora in fase di preparazione. Per il plotter avremo PLON e PLOFF per output su plotter; MOVE e DRAW per muovere la penna o scrivere tra due coordinate; BLACK, BLUE, RED e GREEN o COLOR [0...3] per cambiare penna; SIZE [10,20,40,80] per scegliere il numero di caratteri per riga; ORSET per definire un punto di origine fittizio per iniziare un disegno....e tante altre istruzioni, alcune ancora da inventare. La fantasia non manchera'. Arrivederci.
Impaginato originale... Articolo pubblicato su www.digiTANTO.it - per ulteriori informazioni clicca qui |