DJTOOLKIT E LO STACK MATEMATICO

Da un articolo di Norman Dunbar, pubblicato inizialmente in
QReview Magazine e riprodotto qui con permesso)

                          Traduzione a cura di Mentore Siesto

 DJToolkit e'  un piccolo toolkit SuperBasic  che fornisce al
programmatore,  che voglia distribuire i suoi programmi,  una
serie di routine  presenti in altri toolkit,  come Toolkit 2,
Turbo Toolkit etc.

 Sono presenti 14 procedure e 30 funzioni in 3750 byte di co-
dice, e lo stack matematico e' utile per usi intensi in ques-
to basso consumo di memoria.

 Nel provare DJToolkit  ho avuto alcuni problemi,  la maggior
parte dei quali relativi  alla gestione dello stack matemati-
co.  Ho seguito i consigli elargiti  dagli articoli di  varie
riviste, libri e manuali, rilevando gli stessi problemi.
Quelli semplici da correggere  erano gli errori di scrittura:
porre un codice d'errore in D1 anziche' in D0 non e' utile!

 Il mio strumento principale contro le  "stranezze"  e' stato
QMON 2,  scritto da Tony Tebby  e disponibile presso  Digital
Precision a costi modesti, e molto meritevole a mio avviso.

 LE PROCEDURE sono le estensioni migliori da scrivere, la
gestione dello stack puo' essere riassunta in poche parole:
"non preoccupatevene"!

 All'inizio del codice della procedura, si forniscono tutti i
parametri richiesti e si fa quel che va fatto nel codice:
prima di ritornare al SuperBasic, tutto quel che va fatto e'
impostare D0.L a un codice d'errore (oppure 0 se non ci sono
stati errori) e quindi RTS per tornare al SuperBasic. Tutti i
legami allo stack e/o la gestione degli errori sono demandati
direttamente al QDOS. Il puntatore allo stack matematico A1.L
puo' assumere qualsiasi valore correttamente. Se solo scrive-
re funzioni fosse cosi' facile!

 Lo stack matematico e' molto critico nella scrittura di una
funzione. Non ci sono facilitazioni offerte dal QDOS nelle
funzioni, bisogna tener conto di tutto o seri problemi avver-
ranno durante le prove - crash multipli per una sola causa.

 Nel testo che segue, quando usero' il termine 'all'inizio di
una funzione' intendero' dire che il contatore di programma
sta puntando alla prima istruzione del codice e ancora nulla
e' avvenuto. Nessun parametro e' stato passato e non e' stato
richiesto spazio nello stack matematico. Si tenga cio' a men-
te, dato che quel che segue cambia dopo che queste cose sono
avvenute.

 Uno dei primi problemi avuti con lo stack matematico e` sta-
to il fatto di essere portato a credere che, all'inizio di u-
na funzione, A1.L puntasse a un valore opportuno per la cima
dello stack matematico (relativo quindi ad A6). Questo non e'
il caso.

 All'inizio di una funzione, l'unica cosa di cui si e' sicuri
a riguardo della cima dello stack matematico, e' che il valo-
re e' contenuto in BV_RIP(A6) e che A1 non va considerato co-
me puntatore allo stack al momento.

 All'inizio di una funzione, A1.L punta a nulla!  A1 puo' es-
sere negativo, zero o positivo; ogni valore ha un significato
speciale, come vedremo sotto;  comunque devo spiegare qui che
le informazioni fornite  sono una  'stima ottima'  basata  su
quanto ho trovato nel testare e correggere il DJToolkit usan-
do QMON2 (sembra funzionare).

 Se A1.L contiene un valore negativo  all'inizio della vostra
funzione,  cio' significa che  la funzione  e' stata chiamata
come parte di un'espressione quale

       PRINT 10 * MY_FUNCT(parameter_1, parameter_2, ....)

In questo caso,  prima che venisse chiamata MY_FUNCT,  il va-
lore in virgola mobile di 10 e' stato posto nello stack mate-
matico, e A1.L  contiene questo valore meno 6.  Questo signi-
fica che  6 byte dello stack matematico  sono gia' usati.  Si
noti che A1  NON  punta ancora allo stack matematico:  questo
avverra' solo in seguito.

 Se il valore in A1.L  e' zero,  allora la funzione  e' stata
chiamata da sola,  o come il primo valore  di un'espressione:
per esempio

       PRINT MY_FUNCT(parameter) o
       PRINT MY_FUNCT(parameter) + 22

Anche in questo caso A1 NON punta alla cima dello stack mate-
matico, per il momento.

 In fine, sembra che  se il valore in A1.L e' maggiore di ze-
ro, ci sono A1.L byte liberi per l'uso nello stack matematico
senza dover chiamare BV_CHRIX per controllarlo. Lo spazio po-
trebbe essere disponibile a causa di alcuni calcoli preceden-
ti, che hanno usato molto spazio: questo potrebbe essere usa-
to come metodo  per capire  se e' necessaria  una chiamata  a
BV_CHRIX.

 Per esempio,  se e' necessario riservare 10 byte sullo stack
matematico  per i risultati  di una funzione,  ma  all'inizio
della stessa A1.L  contiene il valore 20, allora una chiamata
a BV_CHRIX con D1 portato a 10 non cambiera' l'indirizzo del-
lo stack matematico.  Si puo' allora assumere  non necessaria
una chiamata a BV_CHRIX.

 La prima cosa che  una funzione  (o una procedura)  dovrebbe
fare e' contare i suoi parametri per assicurarsi che ne siano
stati forniti a sufficienza.  Ci sono un paio di modi per ve-
rificare cio'.

 Il primo modo si basa sul fatto che tutti i parametri saran-
no dello stesso tipo, per esempio interi (word), ognuno lungo
2 byte. Tutto quel che serve e' passare tutti i parametri al-
lo stack matematico chiamando l'appropriata routine vettoriz-
zata  CA_GT???  e controllare  successivamente  il valore  in
D3.W.  Questo e' il numero dei parametri attualmente passati.
Il codice seguente da' un esempio di questo modo.

  MYFUNCT         MOVE.W  CA_GTINT,A2  Passa parametri word
          JSR     (A2)                 Li recupera
          TST.L   D0                   Tutto a posto?
          BEQ.S   PARS_OK              Si': passa oltre
          RTS                          No: torna

  PARS_OK         CMPI.W  #nn,D3       Ci sono 'nn' param.?
          BEQ.S   GOT_OK               Si', salta l'errore

  BAD_PAR         MOVEQ   #ERR_BP,D0   Parametri sbagliati
          RTS                          Ritorna al SuperBasic

  GOT_OK  Il resto del codice

 Quanto scritto sopra funziona, ma se bisogna fornire diversi
tipi di parametri,  o controllare che siano stati usati certi
separatori cosa fare?

 Il secondo metodo di contare i parametri usato, in aggiunta,
e' molto piu' veloce di quello visto sopra.  DJToolkit usa la
subroutine seguente per ogni procedura e funzione.

 COUNT   MOVE.L  A5,D0           Puntatore all'ultimo param.
         SUB.L   A3,D0           Meno il puntatore al primo
         DIVU    #8,D0           Diviso per 8 = numero
         RTS

Al rientro dal codice visto sopra, D0.W contiene il numero di
parametri forniti dall'utente  alla chiamata della funzione o
della procedura.  Un controllo di  D0.W  puo' assicurare  che
siano stati forniti parametri sufficienti. In questo caso po-
tremo passarli come richiesto.

 Passare i parametri  sullo stack matematico  infine portera'
A1.L ad assumere un valore valido per la cima dello stack.


 PASSARE PARAMETRI

 Se la routine richiede che  tutti i suoi parametri  siano di
uno  stesso  tipo,   basta  chiamare   l'appropriato  vettore
CA_GT???. Per esempio, la chiamata di funzione seguente:

       MY_FUNCT(parameter_1, parameter_2)

lascera' lo stack matematico  in queste condizioni, dopo aver
passato interi word con il vettore CA_GTINT:

       2(A6,A1.L) ----> parameter_2
       0(A6,A1.L) ----> parameter_1


 Se bisogna fornire parametri di tipi diversi, saranno neces-
sarie delle manipolazioni.  In aggiunta, la situazione finale
dello stack matematico non sara' semplice come quella dell'e-
sempio di sopra.

 Supponiamo che  la vostra funzione  richieda  due  long word
(4 byte ciascuno)  e due interi word  (2 byte ciascuno): cosa
fare in questo caso?

       MY_FUNCT(long_1, long_2, word_1, word_2)

Dopo aver contato i parametri,  come dall'esempio visto nella
sezione precedente,  e aver controllato che tutto sia corret-
to, bisogna convincere il QDOS  che solo due parametri vadano
passati, e questi sono i primi due, le due long word long_1 e
long_2. Procedete come segue:

  GOT_OK  MOVE.L  A5,-(A7)        Salva A5 per ora
          LEA.L   16(A3),A5       Finge ci siano 2 parametri
          MOVE.W  CA_GTLINT,A2    Passa due interi long
          JSR     (A2)            Li recupera
          MOVE.L  A5,A3           Prossimo parametro: word_1
          MOVE.L  (A7)+,A5        Recupera il vecchio A5

Lo stack matematico ora ha questo aspetto:

       4(A6,A1.L) ---> long_2
       0(A6,A1.L) ---> long_1

Ci sono ancora  due parametri word da passare.  Cosa fare con
A1 tra i due passaggi di parametri?

 La cosa buona  delle routine  di passaggio  dei parametri e'
che esse preservano tutto quello che si trova nello stack ma-
tematico, anche se questo  dovesse essere spostato in memoria
per dare spazio  agli ultimi parametri.  Basilarmente,  tutto
quel che  c'e' da fare  e' passare  i due parametri word come
segue:

     MOVE.W  CA_GTINT,A2     Passa i parametri word
     JSR     (A2)            Li recupera

Questo lascera' lo stack in queste condizioni:

       8(A6,A1.L) ---> long_2
       4(A6,A1.L) ---> long_1
       2(A6,A1.L) ---> word_2
       0(A6,A1.L) ---> word_1

e questo  non e' l'ordine in cui sono stati forniti,  per cui
si deve fare attenzione a non elaborare dati errati!

 Gia' confusi? E non abbiamo ancora cominciato a mescolare il
tutto con parametri stringa...

 Le stringhe  possono essere di lunghezza  pari o dispari, ma
saranno sempre lunghe dispari nello stack matematico.  Quando
viene passata una stringa,  viene posta  una word nello stack
per contenere la sua lunghezza, seguita poi dalla stringa me-
desima.

 La word  nello stack matematico  da'  la  lunghezza corrente
della stringa: comunque, se la lunghezza e' dispari, ci sara'
un byte extra  aggiunto  alla fine dei dati per  mantenere la
lunghezza totale dispari.

Per esempio

       MY_FUNCT("Testing")

lascia lo stack  in queste condizioni,  dopo il passaggio del
parametro:

       2(A6,A1.L) ---> Testing?
       0(A6,A1.L) ---> 07

Come si vede, c'e' un byte extra posto per assicurarsi che il
prossimo tratto  dello stack matematico  inizi a un indirizzo
dispari; se il parametro avesse lunghezza pari, il byte extra
non sarebbe necessario.

 A questo punto sappiamo  come porre i parametri sullo stack,
caricarli nei registri e usarli nel codice come richiesto, ma
cosa fare con lo spazio usato  quando i parametri erano sullo
stack?  La risposta  e' semplice,  bisogna mantenere lo stack
pulito.


 MANTENERE ORDINATO LO STACK

 E' sempre necessario mantenere  uno stack matematico pulito,
altrimenti...

 Se si stanno passando delle word,  ognuna di esse occupa due
byte,  i long occupano 4 byte ciascuno  e i numeri in virgola
mobile 6 byte.  Le stringhe occupano  2 +  (numero di byte di
dati) + 1  byte  (se la lunghezza della stringa  e' dispari).
E' facile  lavorare con  questi tipi di parametri,  basta ag-
giungere  l'appropriato numero di byte ad  A1 e  tutto lavora
correttamente.

 Nel trattare  le stringhe,  bisogna ricordare di  contare la
word usata per la lunghezza e l'eventuale byte di padding. La
miglior maniera e' porre la word di lunghezza in un registro,
aggiungervi 3 e ripulire il bit 0 come segue:  supponiamo qui
che lo stack matematico somigli  all'esempio di sopra, con la
stringa 'Testing' al suo interno.

 TIDY    MOVE.W  0(A6,A1.L),D0   Prende lunghezza stringa
         ADDQ.W  #3,D0           Aggiunge l'extra
         BCLR    #0,D0           Rende pari il valore
         ADDA.L  D0,A1           Ripulisce lo stack

 Questo blocco di codice  funziona che la stringa sia di lun-
ghezza pari o dispari,  e tiene in conto il byte di padding e
la word  di lunghezza.  Ecco,  per chi ne ha bisogno,  il suo
funzionamento:

 La word di lunghezza nell'esempio di sopra vale 7, e somman-
dovi 3 si ottiene 10.  10 e' un numero pari,  quindi il bit 0
vale gia' 0: 10 viene sommato ad A1 per ripulire lo stack.
Questo rimuovera' la word di lunghezza,  i sette byte di dati
e il byte di padding, ripulendo cosi' lo stack.

 Se d'altra parte la stringa  fosse stata  'More_Testing', la
word di lunghezza varrebbe 12; sommandovi 3 si otterrebbe 15.
15 e' un numero dispari,  e annullando il bit 0 si otterrebbe
14, pari.  Dato che  non ci sono byte di padding per stringhe
di lunghezza pari, questo e' il valore corretto da sommare ad
A1 per ripulire lo stack. Lavoro facile!

 Ricordate,  e' vitale che A1 sia tenuto pari.  La sola volta
in cui  puo' essere lasciato dispari e'  quando si stanno ac-
cessando byte  sullo stack matematico, ma A1 deve essere por-
tato a  un valore dispari alla fine dell'elaborazione. Un va-
lore pari di A1 potrebbe portare  a un crash del QL, o, se si
e' fortunati e si ha QMON 2  (o un monitor similare) caricato
e attivo, un errore di eccezione di indirizzo.

 A questo punto abbiamo i nostri parametri,  abbiamo ripulito
lo stack e dobbiamo ritornare un risultato: cosa si fa ora?


 LASCIARE SPAZIO AI RISULTATI

 Ci sono alcune cose  da tenere a mente  nel ritornare un ri-
sultato di una funzione, in accordo al manuale, ai libri ecc.
Queste sono:


    Ripulire lo stack e lasciare solo lo spazio per
    i risultati.

    Memorizzare A1.L in BV_RIP(A6) subito prima di rientrare.

    Impostare D4.W al valore appropriato per il risultato
    che si sta ritornando.

    Annullare D0.L e tornare al SuperBasic con RTS.


 Ci sono quattro possibilita' di terminare per la funzione:


     Non ci sono stati parametri.

     Ci sono stati parametri che hanno usato PIU' spazio di
     quello usato dal risultato.

     I parametri hanno usato lo STESSO spazio del risultato.

     I parametri hanno usato MENO spazio del risultato.


 Il primo caso puo' causare (e ha causato) problemi. Dato che
non e' stato usato spazio nello stack matematico, e' necessa-
rio richiederne un po' chiamando la routine vettore  BV_CHRIX
con l'ammontare  dello spazio necessario in D1.L.  Cosi', per
ritornare una long word e' prima necessario convertirla in un
valore floating point, riservare 6 byte sullo stack matemati-
co e procedere.

 Se il codice  della vostra funzione  usa il registro A1,  il
suo valore precedente la chiamata  sara' stato preservato, ma
bisogna ricordare che questo valore e' inutile come puntatore
allo stack. La routine SYSTEM_VARIABLES del DJToolkit ritorna
l'indirizzo delle variabili di sistema del QL  e non ha para-
metri.  Quando l'ho scritta la prima volta  ho memorizzato A1
in BV_RUIP(A6), richiesto 6 byte  per il risultato e tutto il
QL si e' bloccato!

 Usando QMON 2 per tracciare il problema,  ho scoperto la sua
relazione  con i valori iniziali di A1.  La soluzione sta nel
NON memorizzare A1 in  BV_RIP(A6)  se lo stack matematico non
e' stato usato per i parametri.

 La metodologia corretta e':

  MOVEQ   #nn,D1            Richiede 'nn' byte di stack
  MOVE.W  BV_CHRIX,A2       Prende il vettore
  JSR     (A2)              Prende lo spazio di stack
  MOVE.L  BV_RIP(A6),A1     Prende il puntatore allo stack
  SUBA.L  #nn,A1            Fa spazio per il risultato
  MOVE.L  A1,BV_RIP(A6)     Salva la nuova cima dello stack
  etc                       Resto del codice...

 Come si vede, A1 non e' memorizzato prima di BV_CHRIX; ques-
to metodo funziona sempre e non da' problemi per il fallimen-
to delle funzioni.

 Per il successivo caso, i parametri passati all'inizio della
funzione hanno usato  piu' spazio di quanto richiesto dal ri-
sultato.  Cio' che va fatto in questo caso  puo' essere molto
complicato,  se lo stack non e' stato tenuto pulito nel frat-
tempo. I parametri stringa possono creare i maggiori problemi
dato che un singolo parametro stringa puo' usare un minimo di
2 byte di spazio  (se la stringa e' vuota, spazio non riempi-
to) oppure fino a 32K di stack, piu' 2 byte, quindi una gamma
di valori molto ampia.

 E' necessario  aggiungere 4 byte ad A1.L  per ogni parametro
long word passato, 2 byte per ogni parametro word, 6 byte per
ogni parametro floating point, e quanto detto sopra per i pa-
rametri stringa. Fatto questo,  A1 dovrebbe puntare alla cima
dello stack "pulito".  Sottraete ora da A1.L l'ammontare del-
lo spazio richiesto  dal risultato,  il nuovo valore deve es-
sere immagazzinato in BV_RIP(A6) prima del RTS al SuperBasic,
altrimenti  lo stack  non sara'  correttamente ripulito  e si
potrebbe ottenere un altro crash.

 Ecco un breve esempio,  con due long e un float come parame-
tri e un risultato intero:

     ADDA.L  #10,A1          Ripulisce lo stack dai parametri
     SUBQ.L  #2,A1           Fa spazio per il risultato
     MOVE.L  A1,BV_RIP(A6)   Salva la nuova cima dello stack
     etc                     Altro codice...

 Una volta ancora  tutto funziona correttamente.  Il prossimo
esempio considera cosa e' necessario fare quando lo spazio u-
sato dai parametri  e' uguale a quello richiesto per i risul-
tati.

 Questo dev'essere il caso piu' semplice. Una volta recupera-
ti i parametri dallo stack, non e' necessario che questo ven-
ga riordinato. Al momento di ritornare il risultato, A1.L sa-
ra' gia' pronto allo scopo, come segue:

    MYFUNCT         get_params etc

      MOVE.W  0(A6,A1.L),D1   Prende il parametro word

      do something with parameter

      MOVE.W  D2,0(A6,A1.L)   Sposta il risultato nello stack
      MOVE.L  A1,BV_RIP(A6)   Salva la nuova cima dello stack
      etc                     Il resto del codice...

 L'esempio di sopra assume un parametro intero,  e ritorna un
risultato intero.  Lo stack avra' sempre spazio per il risul-
tato, dato che la routine di passaggio dei parametri ha fatto
spazio per il parametro, che usa lo stesso spazio. Semplice.

 L'ultimo caso possibile e' anche  il piu' difficile, per cui
l'ho lasciato buon ultimo.  I parametri hanno usato meno spa-
zio sullo stack di quanto ce ne serva per il risultato.  Cosa
facciamo?

 Innanzitutto,  calcoliamo quanto  spazio extra e' richiesto,
e richiediamo questo spazio tramite BV_CHRIX,  aggiustiamo il
valore di A1, impiliamo il risultato sullo stack e cosi' via.

 Cio' che non si deve MAI fare e' chiedere a BV_CHRIX lo spa-
zio totale per il risultato,  soltanto l'extra.  In caso con-
trario, lo stack scendera' lungo la memoria del QL ogni volta
che la vostra funzione verra' chiamata,  fino a che il QL non
crollera'. In aggiunta, i risultati della funzione non saran-
no  sempre corretti,  in dipendenza da  se  la funzione viene
chiamata come parte di un'espressione piu' complessa, e anche
a causa della 'spazzatura'  lasciata sullo spazio extra dello
stack, allocato dalla funzione.

 Ancora una volta, un esempio aiutera' a capire. Assumiamo di
volere un risultato stringa,  calcolato, piu' lungo del para-
metro  (nel caso dell'esempio,  un intero long word, 4 byte).
Supponiamo che la nostra stringa sia lunga 100 byte.

 MYFUNCT         MOVE.W  CA_GTLINT,A2   Passaggio parametro
      JSR     (A2)
      MOVE.L  0(A6,A1.L),D0           Tirato via dallo stack
      ADDQ.L  #4,A1                   Stack ripulito

      do something with the parameter etc.

      MOVE.L  A1,BV_RIP(A6)           Salva la cima corrente
      MOVEQ   #102-4,D1               Dati + word di lunghez.
      MOVE.W  BV_CHRIX,A2             Riserva lo spazio
      JSR     (A2)                    Esegue
      MOVE.L  BV_RIP(A6),A1           Prende la nuova cima
      SUBA.L  #102,A1                 Fa spazio al risultato
      MOVE.L  A1,BV_RIP(A6)           Salva la nuova cima
      etc                             Resto del codice

 In questo esempio piuttosto semplice, sappiamo che il nostro
risultato stringa sara' di lunghezza dispari.  In pratica, e'
meglio controllare la lunghezza, per assicurarsi che sia pari
e,  in caso contrario,  aggiungere un byte extra al valore in
D1.L  al momento di chiamare  BV_CHRIX,  senza dimenticare di
aggiungere 2 byte per la word di lunghezza.

 Ho mostrato,  nell'esempio di sopra,  il parametro originale
che viene  ripulito dallo stack matematico  con ADDQ.L #4,A1.
Non conta  che cio'  sia fatto o meno,  ma  bisogna  comunque
tenere  in conto  questa eventualita'.  Il  QDOS  e  BV_CHRIX
possono  gestire entrambe  le situazioni (ripulitura o meno):
il valore in A1  dovrebbe, comunque,  essere immagazzinato in
BV_RIP(A6),  che rappresenti il puntatore  allo stack riordi-
nato o meno.


 ERRORI NELLE FUNZIONI

 Nello scrivere funzioni,  e' sempre bene ritornare un codice
d'errore nel caso che qualcosa vada storto.  Trovo molto fas-
tidioso che una funzione torni un risultato solo quando tutto
funziona correttamente  e 'cada'  con un codice d'errore QDOS
se qualcosa non funziona.

 Questo comportamento e' valido in una procedura, dato che e'
il solo modo in cui una procedura funziona e non e' possibile
ritornare errori in altro modo. In una funzione, ci sono cat-
tive notizie.

 Ritengo che molte funzioni siano scritte in questo modo per-
che' nel caso di un codice d'errore in D0, quando la funzione
ritorna al SuperBasic il QDOS prende il controllo e in manie-
ra simile a quanto accade  per le procedure gestisce la ripu-
litura dello stack, lasciando poco lavoro al programmatore.

 Ovviamente e' valido scrivere funzioni che assolvano a ques-
to compito,  comunque una funzione  dovrebbe ritornare un ri-
sultato corretto o un codice d'errore.

 Ho scritto DJToolkit in modo che sia possibile, per una fun-
zione, fallire e causare un crash del SuperBasic (da parte di
un codice d'errore)  solo se  qualcosa  va storto  durante la
routine di passaggio dei parametri.  In questo caso, e' molto
difficile capire cosa ci sia o meno sullo stack,  per cui ri-
pulirlo prima di ritornare il codice d'errore  e' impossibile
(a meno che  non conosciate  cose differenti?),  e tutto quel
che si puo' fare e' lasciare il codice d'errore in D0.L e ri-
tornare al SuperBasic con RTS. Ovviamente non e' possibile, a
una funzione che ritorna una stringa,  ritornare un codice di
errore.

 In tutti gli altri casi di fallimento, il codice d'errore e'
posto sullo stack e ritornato come il risultato della funzio-
ne.  Questo coinvolge l'uso di  parecchio codice in ogni fun-
zione per assicurarsi  della ripulitura dello stack  e che ci
sia spazio  per il risultato;  significa che  i programmi che
chiamano queste funzioni possono andare avanti dopo un errore
e darne eventualmente segnalazione all'utente.

 Se state cercando di ottenere  la lunghezza di un file su un
disco, per esempio,  preferireste  avere un codice '-7'  (not
found)  se il nome  del file  era  sbagliato,  oppure  che il
SuperBasic fermasse il programma con un errore 'not found' ?

 Se questo succedesse in un programma commerciale per il qua-
le avete pagato, sareste contenti? Non credo. I programmi de-
vono controllare i codici d'errore, informare l'utente e per-
mettere altri tentativi ecc. Molto piu' amichevole.

 In definitiva, lo stack matematico e' una parte molto impor-
tante della  scrittura di funzioni;  dev'essere controllato e
tenuto pulito.  Mai renderlo dispari.  Mai chiedere un numero
dispari di byte con BV_CHRIX.  Tenetelo sotto controllo e lui
terra' sotto controllo le funzioni scritte.


 QMON 2

 Ho menzionato  l'uso di  QMON 2  per tracciare  l'esecuzione
delle mie funzioni, quando ho riscontrato problemi. Ho trova-
to un piccolo trucco, nascosto nel manuale, che dice di porre
un po' di istruzioni  'TRAP #15'  nel codice in vari punti, e
QMON saltera' li' quando vengono eseguite.

 Cio' e' molto utile:  comunque, il manuale non menziona (op-
pure io non l'ho trovato) che  prima di tutto QMON 2 deve es-
sere attivo, o le istruzioni 'TRAP #15' verranno ignorate.

 Per attivare QMON 2, si lanci il programma di boot, e si ca-
richi il codice con LRESPR se si ha il  Toolkit 2  oppure con
RESPR:LBYTES:CALL in caso contrario.  Ora basta scrivere QMON
e premere ENTER  per vedere un dump dei registri sullo scher-
mo; scrivendo G (o GO)  si tornera' al SuperBasic.  QMON 2 e'
stato attivato.

 Non lodero' mai abbastanza questo programma, la vita sarebbe
tanto piu' dura  senza di esso  e  sono davvero grato  a Tony
Tebby per averlo scritto e alla  Digital Precision per avermi
permesso di separarmi da un po' di denaro per comprarlo!


 UN ESEMPIO DAL VERO DI COME SBAGLIARE!

 Nella mia veste di 'trucchista' di QL,  alle volte prendo il
codice di altre persone per vedere come risolvono i problemi;
la cosa puo' essere  molto divertente,  oltre che istruttiva.
Ho l'occasione di usare il toolkit  QPTR  per i programmi del
Pointer Environment che cerco di scrivere. E' molto difficile
ma a volte cerco di perseverare.

 Dilwyn Jones  mi ha sfidato a scrivere  un programma di tipo
'Pointer Prank', che immediatamente appaia nel QL e prenda il
controllo per un po',  facendo qualcosa di assolutamente inu-
tile e probabilmente di cattivo gusto.  Non avendo mai resis-
tito a una sfida, ho accettato e ho scritto un primo tentati-
vo in SuperBasic  usando il toolkit QPTR  per prendere (PICK)
un job casualmente  tra quelli in esecuzione.  Questo ha fun-
zionato correttamente,  ma appariva consumare  tutta la memo-
ria - mi e' sembrato un problema di stack.

 Come poi e' risultato, ho visto la funzione PICK dal toolkit
QPTR  e sembra che Tebby  l'abbia sbagliata.  Alla fine della
funzione PICK, ha impostato lo stack matematico per dare spa-
zio a un risultato in floating point.  Il valore nel registro
D1 viene convertito in float e  A1  viene abbassato di 6 byte
UN'ALTRA VOLTA. Ecco perche' lo stack si abbassa e la memoria
si consuma.

 La versione compilata TURBO  di questo programma gira per un
periodo variabile di tempo.  Il tempo di esecuzione e' deter-
minato dallo spazio dati  che viene dato al processo. Se ha 1
K, gira circa 50 volte, di piu' se ha piu' spazio. Ops; ques-
to mostra che anche i migliori programmatori possono sbaglia-
re alle volte...

 La mia versione del Toolkit  QPTR  ha circa  due anni,  e il
problema  potrebbe essere stato  risolto.  La versione che io
uso e' la 0.08.

 Vi auguro  una felice ricerca di errori,  e spero che questo
articolo  sia stato utile.  Personalmente, consulto sempre le
note  che ho scritto  al tempo  di provare  DJToolkit  quando
scrivo una funzione, per sicurezza.