Articolo pubblicato sul n. 27 di MCmicrocomputer (Edizioni Technimedia Srl - Roma) nel febbraio 1984
EXMA Da un po' di mesi il buon Valter Di Dio ci sta parlando di Assembler: una magica parola che identifica un linguaggio di programmazione dalla incredibile velocità, che vive in letargo nel personal in attesa di essere scoperto dall'utente. Purtroppo col VIC-20, anche dotato di VIC-MON, programmare in Assembler non è proprio facile-facile; si ha sempre a che fare con una marea di indirizzi Hex (guai a scrivere 3 al posto di $03 o $2Dl al posto di $02Dl!!). Per cui si finisce spesso per fare confusione. Se, inoltre, dopo aver digitato un lungo programma si vuol inserire al centro una nuova linea ...Buonanotte: bisogna correggere tutti gli indirizzi dei Branch (salti) che facevano riferimento al di là della linea aggiunta e tutti gli eventuali riferimenti a tavole (= lavoraccio da cani). Nel corso di quest'articolo non spiegheremo come si programma in Assembler; presenteremo invece un assemblatore per il 6502 scritto in Basic e linguaggio macchina, che ci permetterà di scrivere programmi senza l'assillo degli Hex, usando etichette. variabili, vettori. notazione decimale, binaria, ottale e, per i patiti, esadecimale. Come per il Basal (MC n. 22) si potrà facilmente editare un programma, salvarlo su nastro, su disco, listarlo su stampante, e in più: curiosare nella memoria con un potente disassembler e con la funzione di HexDump. e trasformare pezzi di memoria in linee Basic del tipo DATA 123. 12,9,44. 122, 0, 32... ecc. Exma è organizzato in due puntate: in questa prima puntata vi consegneremo tutta la parte in linguaggio macchina (listato 1, quello con tutti quei DATA) e, del programmone Basic, tutta la parte riguardante l'editing, il Saveload, l'assemblatore vero e proprio e il monitor. Il prossimo mese sarà la volta della creazione di Macroistruzioni che permetterà a tutti di costruirsi un proprio set di istruzioni speciali tipo la moltiplicazione. la divisione o (!) il M.C.D. di due numeri, consentendo così una ancora più spedita programmazione in L.M.
2 Listati
L'inconsueta veste di questo programma, in forma di due distinti listati, nasce dalla necessità (costi quel che costi) di risparmiare byte. Dato che le routine in linguaggio macchina, tramite i DATA, sono caricate una volta per tutte (con READ e POKE) prima di usare l'Assembler, è inutile continuare a tenersele in memoria sotto forma di linee Basic. Si dovrà così, una volta che i due programmi saranno su nastro o su disco, caricare dapprima il programma DATA, dare RUN, aspettare il READY e poi caricare l' Assembler. Inutile dirvi che se qualcuno si azzarda a dare RUN al secondo listato senza aver eseguito il primo, non solo non funzionerà ma c'è il rischio che il tutto si inceppi sino a spegnimento e riaccensione del malcapitato VIC. Sembra quasi superfluo avvertire che è necessaria la cartuccia da l6K prima di iniziare a digitare!!
RUN
Appare il menu #1, e già, non è l'unico: premendo il tasto "<=" (freccia a sinistra) i due menu si scambiano di posto consentendo di accedere a tutte le opzioni. Ritornate al menu # l: la prima opzione è "fase di input" e serve appunto per inserire un programma in memoria. Con Shift ''l'' si va in fase di input dopo aver ripulito l'area di lavoro, senza lo shift si ha lo stesso effetto senza cancellare: in questo modo è possibile lasciare di battere un programma, ritornare al menu, eseguire qualche altra opzione e ritornare al programma lasciato in sospeso per continuare a inserire le linee. Simpatico il cursore lampeggiante che non è quello originale, ma un "sosia" ottenuto controllando continuamente l'orologio interno (loc. 160-162) e "negativizzando" ad intervalli di tempo regolari. Se per giocherellare coi tasti si è sporcato lo schermo, ritornate al menu con shift "M" e con shift "l" ripulite l'area di lavoro. Inizieremo da questa: è semplicemente la zona di memoria da 20480 a 24566 corrispondente quindi agli ultimi 4k Ram disponibili dei 16. È come una grande paginona video che continuamente è visualizzata (un pezzo alla volta) sullo schermo da una routine in linguaggio macchina. Ma ciò non ha molta importanza: l'unica cosa da dire è che il [RETURN] non serve per inserire qualcosa in memoria, ma solo per andare a capo. In altre parole, ogni cambiamento sullo schermo corrisponde allo stesso cambiamento in memoria, cosa che non accade in Basic se, dopo aver listato un programma, vi scarabocchiamo sopra col cursore e con la tastiera, senza toccare il [RETURN]. I comandi di movimento cursore, cancellamento e inserimento carattere funzionano nel modo più istintivo possibile: come se stessimo in ambiente Basic. Shift "D" e shift ''l'' si usano rispettivamente per cancellare una linea (quella dove sta lampeggiando il cursore) o per inserirne una in bianco da riempire. Avrete notato che esiste una precisa suddivisione del campo: Etichette (LABEL), operazione (OPR) e indirizzo (ADDRESS). Senza dilungarci ulteriormente in parole, "schiaffiamo" in memoria questo miniprogramma:
.*= 23000 PIO :JSR $FFE4 CMP #32 BNE PIO BRK
Da menu digitate shift "l": il cursore è pronto per accettare ".* = ", per ora digitatelo: dopo vedremo cos'è. Con la pressione della barra spaziatrice il cursore schizza nel terzo campo (abituatevi a questi "schizzamenti"). Digitate 23000 [RETURN]. Il cursore si trova sulla seconda linea, sempre nel campo OPR. Bisogna inserire una label (PIO): premete la freccia a sinistra (schizzo contrario) digitate PIO e premete una volta lo spazio: la label è inserita, il cursore è in OPR, potete procedere come sopra. Così via fino all'ultima linea ricordandovi soltanto che ogni cosa va scritta nel suo campo, quindi allenatevi un po' con la barra e la freccia a sinistra prima di cimentarvi a scrivere programmi lunghi. Non preoccupatevi se andando per sbaglio nel campo label vedrete apparire qualche ":" in più: funziona lo stesso. Un'ultima cosa: se ancora non vi siete sintonizzati perfettamente sul modo di fare del programma di editing ricordate che lo spazio alcune volte fa schizzare il cursore; shift spazio è molto più pacifico: cancella il carattere sotto il cursore sostituendolo con un blank.
Le direttive
Le direttive per l'assemblatore sono istruzioni inseribili nel programma da assemblare pur non essendo istruzioni eseguibili dal 6502. Tanto per intenderci, una direttiva potrebbe essere: "il seguente programma andrà posizionato a partire dal byte $4000" oppure "attento: dove incontri la label PRT è da intendere come l'indirizzo $FFD2", o roba simile. Nell'esempio del paragrafo precedente abbiamo già incontrato una direttiva: ".* =" serve per definire dove andrà locato il programma dopo l'assemblaggio. In quel caso, 23000 nel campo ADDRESS indicava l'indirizzo della prima istruzione del programma. Come già accennato, ogni numero all'interno del programma può essere espresso in differenti basi. Non fanno eccezione eventuali argomenti delle direttive: in quel caso, al posto di 23000 (decimale) si sarebbe potuto scrivere $59D8 (esadecimale) oppure &54730 per esprimerlo in ottale. Per indirizzi a 8 bit o poco più è possibile esprimersi anche in binario usando come prefisso il carattere "%". Ritornando a ".* = ", non resta da dire che questa direttiva può anche essere inserita in più punti del programma. Per specificare varie porzioni di programma o vari programmi ognuno con un suo indirizzo d'inizio. Il suo valore di default coincide con l'inizio dell'area di lavoro: $5000 o 24800 in decimale. La seconda direttiva ".AD" definisce un riferimento in memoria: per esempio, se spesso in un programma si fa riferimento a $FFD2 possiamo dichiarare all'inizio del programma:
PRT :.AD $FFD2
e ogni volta digitare PRT al posto di $FFD2 che è più corto, più mnemonico e meno esposto a errori di battitura. Terza direttiva è ".EP" dove EP non sta per "extra pedestre" ma per Entry Point e indica qual è la prima istruzione da eseguire (non necessariamente la prima del programma) nel caso che da menu #2 sia dato il RUN PROGRAMMA dopo l'assemblaggio. Se nessun Entry Point è stato dichiarato, non sarà possibile far partire l'esecuzione da menu, ma bisognerà schiacciare R/S e digitare SYS seguito dall'indirizzo d'inizio del programma. Quarta ed ultima direttiva è ".BB" (block byte) e si usa per dichiarare un vettore di byte di lunghezza data. Ad esempio:
PIPPO :.BB 100
indica un vettore di lunghezza 100 con gli elementi individuabili con PIPPO, PIPPO+ 1, PIPPO+2, ..., PIPPO+99 o variabilmente col modo indicato dal registro X o Y, ad esempio LDA PIPPO,X con X compreso fra 0 e 99. Per ultimo (non si tratta di una vera e propria direttiva), la possibilità di definire nel programma sorgente il contenuto di determinati byte. Si ottiene specificando nel campo operatore il valore esadecimale del byte, preceduto naturalmente da "$". Quando l'assemblatore incontra quest'oggetto, non ricerca il suo codice operativo come vedremo più avanti che fa per le altre istruzioni, ma semplicemente assegna al byte il valore esadecimale specificato e va all'istruzione successiva. In questo modo è possibile inserire una tabella nel programma sul tipo del DATA in Basic. Esempio:
LDX 12 LOOP : LDA TABLE,X JSR $FFD2 DEX BPL LOOP RTS TABLE :$43 $4D $93
Il processo d'assemblaggio
L'Assembler listato in queste pagine (a proposito: EXMA sta per Extended Mnemonic Assembler) ha alcune particolari features. Come dicevamo prima, è possibile per qualsiasi numero esprimersi in notazione decimale, esa, ottale e binaria; inserire etichette nel programma e fare ad esse riferimento per salti, subroutine e tabelle. Di conseguenza, la traduzione a codice numerico (l'unico effettivamente "digeribile" dal 6502) necessita di alcune passate. Dopo ogni passata, il nostro programma sorgente assomiglia sempre più al codice oggetto (fino, naturalmente, a diventarlo). L'EXMA compie in tutto 4 passate anche se, dal punto di vista logico, possiamo assimilarle a due sole. Con la prima, linea per linea è riconosciuto il modo di indirizzamento e il numero di byte occupati da ogni istruzione, e sostituito ad essa il relativo codice operativo. Con la seconda passata, tutti i riferimenti mnemonici, le label, sono sostituiti con gli effettivi indirizzi dei byte cui si riferiscono. La necessità di eseguire almeno due passate, sebbene con metodi molto contorti e perditempo sia possibile ridurla ad una sola, nasce dal fatto che un eventuale riferimento label ad un'istruzione qualche linea più avanti (es. un salto) non potrebbe essere direttamente sostituito col valore numerico, dato che di quella linea non si conosce ancora il suo effettivo indirizzo se non si assemblano tutte le linee precedenti. Sempre in merito alle label, per "questioni interne" non è possibile identificarne una con parte del nome di un'altra. Se, ad esempio, è stata usata la label PIOLO, non potremo usare PIO né P come etichetta. Per riconoscere il modo d'indirizzamento, il programma si avvale di un metodo assai semplice: analizza alcuni particolari del campo address della linea da assemblare. Se ad esempio il suddetto campo termina con "), Y" siamo certamente nel caso del modo indiretto indicizzato; se termina con ")" il modo è indiretto e così via per gli altri modi. Chi è iriteressato ai dettagli dia uno sguardo alle linee 1320 -1550. Per accelerare un po' i tempi, la ricerca del codice operativo è affidata a una routine in L.M. Le REM a capo del programma costituiscono la tavola di tutte le istruzioni e dei relativi codici operativi espressi in" Hex. La stessa, è sfruttata da un'altra routine in L.M. per disassemblare il contenuto della memoria. Per trovare, ad esempio, l'opcode del modo 5 dell'istruzione LDA, i codici ASCII dei tre caratteri L, D e A sono immessi nei byte 832-834, il modo (5) nel byte 835 ed è eseguita una SYS 19542. A questo indirizzo, una routine in linguaggio macchina ricerca la stringa LDA fra le REM, pesca il quinto numero Hex dopo LDA, e lo "sbatte" nel byte 836. Ritornati in Basic, se PEEK(836) contiene il numero 3, vuoI dire che l'istruzione non esiste e un messaggio d'errore è mostrato sul video. Ciò può capitare se si è scritto LDB al posto di LDA o giù di lì. In tal caso, dopo l'apparizione di "NON RICONOSCO ...LDB", basta premere qualsiasi tasto per ritrovarsi immediatamente "catapultati" in fase di input col cursore già bello e lampeggiante (!) sull'istruzione da correggere che ha generato l'errore (della serie: quando AdP fa spettacolo, n.d.a.d.p.). Per ultimo, un miniminiquiz: chi starà un po' attento quando batterà il programma, noterà a un certo punto qualcosa di strano, molto strano, di cui volutamente non abbiamo parlato: potrebbe essere quasi la passata zero; grazie ad una particolare SYS... pensateci, l'assassino, ops!, la soluzione nel prossimo numero.
Qualche esempio
Con i seguenti tre programmini, cercheremo di mettere in luce, più che con le solite parole, le effettive possibilità dellinguaggio EXMA 6502. Il programma:
.*= 22222 LDA #16 STA $A8 LDY #0 STY $A7 TITO :LDA #32 PIPPO :STA ($A/),Y INY BNE PIPPO INC $A8 LDA $A8 CMP #18 BNE TITO RTS
è un classico esempio di uso del modo indiretto indicizzato STA ($A7),Y per il riempimento della memoria. Nella fattispecie, la routinetta sopra listata riempie con 512 "trentadue" la pagina video che, come si sa, ha inizio a $4000. Il codice di schermo 32 corrisponde allo spazio quindi equivale a cancellare il video. Per provarla, dopo averla naturalmente digitata e fatta assemblare, arrestate l'esecuzione e digitate SYS 22222 [RETURN]. Il video si cancellerà. Il secondo programmino:
.*= 21200 .EP PRT :.AD $FFD2 GET :.AD $FFE4 LOOP :JSR GET BEQ LOOP CMP #205 BEQ FINE JSR PRT BNE LOOP FINE :RTS
mostra l'uso delle direttive ".EP" e ".AD". $FFD2 e $FFE4 sono gli indirizzi di due routine del Sistema Operativo del VIC che implementano le istruzioni di PRINT e GET registro accumulatore. Assemblando e facendo partire l'esecuzione di questa routine (questa volta grazie a ".EP" da menu #2 con RUN PROGRAMMA), vedrete apparire sul video i tasti che schiaccerete. Shift "M" vi farà ritornare al menu. L'ultimo programmino:
.*= 1024 PIPPO :.BB 256 .EP LDX #0 LOOP :LDA 0,X STA PIPPO,X DEX BNE LOOP RTS
non fa altro che trasferire i 256 byte della pagina zero nel vettore PIPPO (linea 2) dichiarato appunto come Block Byte di 256 elementi. Si noti l'Entry Point a capo .dell'effettivo programma e non, naturalmente, prima del vettore di 256 elementi.
Il monitor
Per curiosare all'interno della memoria del VIC, è possibile da menu richiedere l'Hex-Dump o il Disassembly di determinati byte. Sia l'uno che l'altro sono visualizzati una intera paginata per volta: con la pressione di qualsiasi tasto, una nuova paginata è mostrata; Shift "M", tanto per cambiare, riporta al menu. L'opzione DATA.CONV, come già accennato in testa all'articolo, si usa per trasformare in DATA insiemi di byte della memoria. È richiesto il byte d'inizio, l'ultimo byte o il numero di byte (battendo solo [RETURN] alla seconda richiesta), il numero della prima linea DATA e lo step. Si raccomanda di non indicare numeri di linea già esistenti, pena la cancellazione di parte del programma in memoria. Questa opzione può essere utile a coloro i quali usano il L.M. per preparare veloci routine da accoppiare a programmi Basic. In questo caso, dopo aver trasformato in DATA, non resta che Deletare tutte le linee dell'EXMA con l'apposito comando presente sulla Toolkit, o sfruttando la routine dell'abile Tontini apparsa sul n. 17 di MC. Infine, l'esistenza di due comandi di SA VE e due di LOAD risponde alla eventuale necessità di salvare su nastro o su disco un programma in L.M. prima o dopo la fase di assemblaggio. Per il SA VE COD. OGGETTO, si raccomanda di usarlo appena terminata la seconda passata, prima di richiedere qualsiasi altra opzione. Prima del consueto "Tutto qui", un cordiale arrivederci al prossimo numero. Impaginato originale... Articolo pubblicato su www.digiTANTO.it - per ulteriori informazioni clicca qui |