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.