{"id":1040,"date":"2017-04-30T18:23:34","date_gmt":"2017-04-30T16:23:34","guid":{"rendered":"http:\/\/www.hunggartorino.it\/ql\/?p=1040"},"modified":"2017-05-07T22:53:29","modified_gmt":"2017-05-07T20:53:29","slug":"ricerca-chiavi","status":"publish","type":"post","link":"http:\/\/www.hunggartorino.it\/ql\/ricerca-chiavi\/","title":{"rendered":"Trovare le parola chiave"},"content":{"rendered":"<p style=\"text-align: left;\">Bighellonando qua e l\u00e0, mi \u00e8 capitato di andare a frugare dentro la famiglia PEEK di parole chiave di SMSQ\/E. Mi sono chiesto se fosse possibile usarle per risolvere un annoso problema.<\/p>\n<p style=\"text-align: justify;\">Ecco lo scenario (o meglio i due scenari):<\/p>\n<ol>\n<li>\n<p style=\"text-align: justify;\">\u00a0Un programma per caricare una demo, o per installare un programma, necessita di verificare se determinati toolkit sono gi\u00e0 caricati in una punter\u2019s machine o se essa necessita di caricarseli da s\u00e9, o scegliere una diversa versione<br \/>\ndel programma compilato da eseguire. O darsela a gambe.<\/p>\n<\/li>\n<li>\n<p style=\"text-align: justify;\">Un programma compilato necessita di verificare se la pratica utility QMenu FILE_SELECT$ sia disponibile, o se deve usarsela da s\u00e9, semplice, alternativa, per ottenere il nome di un file dall\u2019utente.<\/p>\n<\/li>\n<\/ol>\n<p style=\"text-align: justify;\">Entrambi gli scenari potrebbero essere soddisfatti se fosse possibile verificare se cerrte parole chiave siano caricate nella macchina o no.<\/p>\n<p style=\"text-align: justify;\">Il problema \u00e8 facilmente risolvibile nel caso dei programmi compilati. Basta creare una piccola estensione in codice macchina per scansionare la tabella nomi, e collegarla nel solito modo al programma compilato. Ruba qualche byte extra mentre il programma \u00e8 in uso, poi se ne va.<\/p>\n<p style=\"text-align: justify;\">Un\u00a0programma per lanciare una demo o per qualche\u00a0installazione potrebbe installare un toolkit del\u00a0genere per questo compito, ma dopo non\u00a0potrebbe risistemare il disordine; il toolkit ormai\u00a0inutile rimarrebbe fino al prossimo riavvio del\u00a0sistema. Non molto entusiasmante\u00a0probabilmente, ma sarebbe forse possibile\u00a0verificare le parole chiave da S*BASIC senza\u00a0l\u2019uso di toolkit?<\/p>\n<p style=\"text-align: justify;\">Guardando gli esempi proposti nel manuale,\u00a0PEEK(\\\\) sembra essere \u201cl\u2019uomo giusto\u201d per\u00a0questo compito.<\/p>\n<p style=\"text-align: justify;\">Quindi ecco il mio primo tentativo, scritto in\u00a0SMSQ\/E SBASIC:<\/p>\n<h2>FindKey 1<\/h2>\n<pre>10 DEFine FuNction <a>FindKey<\/a>(k$)\r\n11 LOCal n, c, o, l%\r\n12 REMark Returns true if keyword loaded\r\n13 REMark in this SBASIC\r\n14 :\r\n15 REMark Get size of Name Table\r\n16 c = PEEK_L(\\\\ $1C) - PEEK_L(\\\\ $18)\r\n17 FOR n = 0 TO c STEP 8\r\n18  REMark Weed out non-keywords Types\r\n19  IF PEEK_W(\\ $18\\ n) &lt; $800: NEXT n: EXIT n\r\n20  REMark Get offset in Name List\r\n21  o = PEEK_W(\\ $18\\ n + 2)\r\n22  REMark Skip no names\r\n23  IF o &lt; 0: NEXT n: EXIT n\r\n24  REMark Get Name length\r\n25  l% = PEEK(\\ $20\\ o)\r\n26  REMark and Name\r\n27  IF k$ == PEEK$(\\ $20\\ o + 1, l%): RETurn 1\r\n28 END FOR n\r\n29 RETurn 0\r\n30 END DEFine FindKey\r\n<\/pre>\n<p style=\"text-align: justify;\">Bene, magnifico. Semplice e rapido. Problema\u00a0risolto!<\/p>\n<p style=\"text-align: justify;\">No.<\/p>\n<p style=\"text-align: justify;\">Innanzitutto QLib non aveva niente di tutto ci\u00f2.\u00a0Non mi piaceva il mio hex ed era totalmente\u00a0sbilanciato a danno della versione SMSQ\/E di\u00a0PEEK, preferendo la sua propria versione che\u00a0non capisce gli offset.\u00a0L&#8217;altro problema, ben pi\u00f9 serio, \u00e8 che sebbene\u00a0tale programma effettivamente funzioni nei job\u00a0figlie del SBASIC, esso \u00e8 inutile: mostra solo un\u00a0sottogruppo di tutte le parole chiave disponibili.<\/p>\n<p style=\"text-align: justify;\">Questo perch\u00e9 ognuno dei job figlie del SBASIC\u00a0ha la sua specifica tabella nomi (giustamente),\u00a0ma in tale tabella solamente le parole chiave\u00a0presenti nel programma attuale e qualunque\u00a0comando immediato digitato alla console lo fanno\u00a0all\u2019interno della tabella nomi locale. La tabella\u00a0principale \u00e8 associata unicamente al job #0,\u00a0guardiano dello stub del SuperBASIC. Tale stub,\u00a0un costrutto progettato per mantenere un alto\u00a0grado di compatibilit\u00e0 con il SuperBASIC, trattiene\u00a0il puntatore nella tabella nomi reale, custodita da\u00a0qualche parte nel Common Heap. Come lo spazio\u00a0dati del SuperBASIC, questa tabella nomi pu\u00f2\u00a0spostarsi, ma non allo stesso modo e nemmeno\u00a0per gli stessi motivi del SuperBASIC.<\/p>\n<p style=\"text-align: justify;\">Pu\u00f2 spostarsi se intasata di nuove parole chiave\u00a0dato che i toolkit sono caricati nel job#0 al\u00a0momento del Boot. Pu\u00f2 spostarsi quando grosse\u00a0quantit\u00e0 di nuovi nomi vengono aggiunti\u00a0all\u2019interpreter nel job#0. Quando necessita di\u00a0ingrandirsi, una nuova, pi\u00f9 ampia area gli viene\u00a0riservata nel Common Heap, i valori correnti\u00a0vengono quindi copiati sopra, e infine vengono\u00a0aggiornati i puntatori nell\u2019area variabili del\u00a0SBASIC, prima che la vecchia tabella nomi sia\u00a0scartata (pu\u00f2 anche restringersi, a causa di una\u00a0cancellazione di nomi dall\u2019interpreter#0, che\u00a0interesser\u00e0 alcuni puntatori, quantomeno). In altre\u00a0parole, si sposter\u00e0 solamente in seguito a una\u00a0qualche attivit\u00e0 in corso nell\u2019interprete del job#0.<\/p>\n<p style=\"text-align: justify;\">Il modello di spostamento memoria impiegato dal\u00a0SuperBasic evoca incubi atavici e strani tic\u00a0nervosi in molti intrepidi programmatori amatoriali.<\/p>\n<p style=\"text-align: justify;\">Ma torniamo al tema principale: potrei vedere ora\u00a0che io in qualche modo ho dovuto scansionare la\u00a0tabella reale, la tabella nomi del job#0. Il\u00a0problema \u00e8 che non c\u2019\u00e8 un modo facile per\u00a0afferrare per i cojones quella tabella e\u00a0immobilizzarla invocando la modalit\u00e0 supervisore\u00a0dal SBASIC. Che fare?<\/p>\n<p style=\"text-align: justify;\">Nei due scenari concepiti poc\u2019anzi, qualunque\u00a0scansione della tabella nomi, sia che si trovi in\u00a0job#0, una figlia SBASIC, o un job compilato,\u00a0potrebbe essere fatta per avvenire\u00a0immediatamente all\u2019inizio di yn dato programma.<\/p>\n<p style=\"text-align: justify;\">E\u2019 altamente improbabile che un grosso\u00a0programma S*BASIC venga avviato esattamente\u00a0nello stesso momento, e se utilizzato in un\u00a0programma di Boot, niente all\u2019infuori delle\u00a0istruzioni del mio programma succederebbe nel\u00a0job#0. Bench\u00e9 l\u2019improbabile, com\u2019\u00e8 noto, non\u00a0dovrebbe mai essere confuso con l\u2019impossibile,\u00a0almeno per quanto riguarda SMSQ\/E questa\u00a0sembra essere una scommessa molto sicura.<\/p>\n<p style=\"text-align: justify;\">Ho pensato di andare avanti e provarci. Poi avrei\u00a0fatto dei test intensivi per vedere quanto grande\u00a0sarebbe stato il problema dello spostamento della\u00a0memoria per il mio schema. Cos\u00ec ho aggiunto\u00a0alcuni test triviali. Se si fosse verificato il peggiore\u00a0dei casi e si fosse spostata, non avrebbe avuto\u00a0conseguenze peggiori che dare una risposta\u00a0sbagliata ad ogni morte di papa:<\/p>\n<h2>FindKey 2<\/h2>\n<pre>10 DEFine FuNction <a>FindKey<\/a>(k$)\r\n11 LOCal n, o, a6, c, s, l%\r\n12 REMark Searches all keywords\r\n13 REMark Return true on match\r\n14 :\r\n15 REMark Get job#0's JCB\r\n16 s = PEEK_L(! $68! 0)\r\n17 REMark Get job#0's a6\r\n18 a6 = PEEK_L(s + $58)\r\n19 :\r\n20 REMark Some GLOBal definitions\r\n21 sb_nmtbb = a6 + $18\r\n22 sb_nmtbp = a6 + $1C\r\n23 sb_nmlsb = a6 + $20\r\n24 :\r\n25 REMark Size of Name Table\r\n26 c = PEEK_L(sb_nmtbp) - PEEK_L(sb_nmtbb)\r\n27 :\r\n28 REMark Go through list\r\n29 FOR n = 0 TO c STEP 8\r\n30  :\r\n31  REMark Weed out non-keywords Types\r\n32  IF PEEK_W(PEEK_L(sb_nmtbb) + a6 + n) &lt; $800: NEXT n: EXIT n\r\n33  :\r\n34  REMark Get offset in Name List\r\n35  o = PEEK_W(PEEK_L(sb_nmtbb) + a6 + n + 2)\r\n36  :\r\n37  REMark Skip no names\r\n38  IF o &lt;= 0: NEXT n: EXIT n\r\n39  :\r\n40  REMark Get Name length\r\n41  l% = PEEK(PEEK_L(sb_nmlsb) + a6 + o)\r\n42  IF l% &gt; 255: NEXT n: EXIT n\r\n43  :\r\n44  REMark Display Name\r\n45  IF k$ == PEEK$(PEEK_L(sb_nmlsb) + a6 + o + 1, l%): RETurn 1\r\n46 END FOR n\r\n47 RETurn 0\r\n48 END DEFine ListKeys\r\n49 :\r\n50 PRINT FindKey(\"block\")\r\n51 PRINT FindKey(\"blox\")\r\n<\/pre>\n<p style=\"text-align: justify;\">Cos\u00ec com\u2019\u00e8, \u00e8 ottimizzato per presentazione\u00a0piuttosto che per velocit\u00e0. L\u2019avvio (RUN) del\u00a0programma in un qualunque SBASIC dovrebbe\u00a0stampare 1 e 0 sullo schermo, sia che BLOCK sia\u00a0caricato o meno nell\u2019interprete locale. In ogni\u00a0caso non funzioner\u00e0 in tutto ci\u00f2 che non sia\u00a0SBASIC, perci\u00f2 sono necessarie ulteriori\u00a0modifiche per renderlo universale.<\/p>\n<p style=\"text-align: justify;\">Molti programmatori assumono che le variabili di\u00a0sistema saranno sempre ubicate all\u2019indirizzo\u00a0$28000, come nel Qdos di un QL originale. Ma\u00a0nemmeno nel QL \u00e8 detto che siano fissate in\u00a0quell\u2019indirizzo, vuoi per causa del secondo\u00a0schermo ed altro ancora. Ma \u00e8 per questo che le\u00a0cose vanno storte, e il SBASIC ha prontamente\u00a0chiuso un occhio su questo fornendo le sua\u00a0variabili di sistema allo stesso indirizzo.<\/p>\n<p style=\"text-align: justify;\">Comunque, ci\u00f2 \u00e8 stato fatto principalmente per\u00a0permettere agli utenti di far girare vecchi\u00a0programmi che non potevano essere modificati. I\u00a0nuovi programmi non dovrebbero dipendere da\u00a0indirizzi non previsti. La prossima generazione di\u00a0emulatori o di hardware (magari!) potrebbe, per\u00a0ragioni tecniche, non essere cos\u00ec accomodante.\u00a0Magnanimamente, in un piccolo hack rubacchiato\u00a0a Minerva, la funzione VER$(-2) \u00e8 disponibile\u00a0anche in SMSQ\/E. Il QDOS, naturalmente, non\u00a0cambier\u00e0 mai, perci\u00f2 questo \u201cinterruttore\u201d\u00a0dovrebbe funzionare in tutti gli attuali sistemi (se\u00a0siete di diverso avviso per favore fatecelo\u00a0sapere!):<\/p>\n<pre>IF VER$ = 'JSL' OR VER$ = 'HBA' THEN\r\n SYSV = VER$(-2): REMark Minerva\/SMSQ\/E\r\nELSE\r\n SYSV = 163840:   REMark QDOS\r\nEND IF<\/pre>\n<p style=\"text-align: justify;\">Mentre le variabili di sistema non possono\u00a0sempre essere ubicate nella locazione \u201cstandard\u201d\u00a0(almeno da Minerva in avanti), una volta che il\u00a0sistema \u00e8 in piedi e in funzione, la loro ubicazione\u00a0rimane fissata per tutta la sessione, cosicch\u00e9\u00a0necessiter\u00e0 di essere verificata una volta soltanto.<\/p>\n<p style=\"text-align: justify;\">Il listato finale mostra il programma completato:<\/p>\n<h2>FindKey 3<\/h2>\n<pre>10 DEFine FuNction <a>FindKey<\/a>(k$)\r\n11 LOCal i, n, o, a6, c, s\r\n12 LOCal l%, k%\r\n13 REMark GLOBal SYSV\r\n14 :\r\n15 REMark Get job#0's JCB\r\n16 s = PEEK_L(PEEK_L(<em>SYSV<\/em> + 104)): REMark $68\r\n17 REMark Get job#0's a6\r\n18 a6 = PEEK_L(s + 88): REMark $58\r\n19 :\r\n20 REMark Some GLOBal definitions\r\n21 sb_nmtbb = a6 + 24: REMark $18\r\n22 sb_nmtbp = a6 + 28: REMark $1C\r\n23 sb_nmlsb = a6 + 32: REMark $20\r\n24 :\r\n25 REMark Size of Name Table\r\n26 c = PEEK_L(sb_nmtbp) - PEEK_L(sb_nmtbb)\r\n27 :\r\n28 k% = LEN(k$)\r\n29 REMark Go through list\r\n30 FOR n = 0 TO c STEP 8\r\n31  :\r\n32  REMark Weed out non-keywords: REMark   $800\r\n33  IF PEEK_W(PEEK_L(sb_nmtbb) + a6 + n) &lt; 2048: NEXT n: EXIT n\r\n34  :\r\n35  REMark Get offset in Name List\r\n36  o = PEEK_W(PEEK_L(sb_nmtbb) + a6 + n + 2)\r\n37  :\r\n38  REMark Skip no names\r\n39  IF o &lt; 0: NEXT n: EXIT n\r\n40  :\r\n41  REMark Get Name length\r\n42  l% = PEEK(PEEK_L(sb_nmlsb) + a6 + o)\r\n43  :\r\n44  REMark For slow QLs\r\n45  IF l% &lt;&gt; k%: NEXT n: EXIT n\r\n46  REMark Compare strings\r\n47  FOR i = 1 TO l%\r\n48   IF NOT CHR$(PEEK(PEEK_L(sb_nmlsb) + a6 + o + i)) == k$(i): l% = 0: EXIT i\r\n49  END FOR i\r\n50  IF l% &gt; 0: RETurn 1: REMark Bingo!\r\n51 END FOR n\r\n52 RETurn 0\r\n53 END DEFine FindKey\r\n54 :\r\n55 REMark Get pointer to System Variables\r\n56 v$ = VER$\r\n57 IF v$ = \u2018JSL\u2019 OR v$ = \u2018HBA\u2019 THEN\r\n58 SYSV = VER$(-2): REMark Minerva\/SMSQ\/E\r\n59 ELSE\r\n60 REMark QDOS\r\n61 SYSV = 163840: REMark $28000\r\n62 END IF\r\n63 :\r\n64 PRINT FindKey(\"block\")\r\n65 PRINT FindKey(\"blox\")<\/pre>\n<p style=\"text-align: justify;\">Come ho accennato, non esiste un modo\u00a0semplice per bloccare l\u2019area del SuperBASIC\u00a0mentre questa routine \u00e8 in funzione, perci\u00f2 pu\u00f2\u00a0essere usata in sicurezza solo nei programmi di\u00a0Boot. Potrebbe essere usata senza problemi in\u00a0maniera limitata anche in programmi compilati, se\u00a0usata con parsimonia al momento dell\u2019avvio,\u00a0assumendo che \u00e8 improbabile che un grosso\u00a0programma S*BASIC venga caricato nello stesso\u00a0istante in cui il programma compilato si inizializza:<\/p>\n<pre>10 REMark $$stak=800\r\n20 :\r\n100 REMark My little program\r\n110 qmenu = FindKey(\"FILE_SELECT$\")\r\n120 sound = FindKey(\"SOUNDFILE\")\r\n130 :\r\n140 REMark Rest of My little program goes here..\r\n...\r\n710 IF sound: Play \"tune\": ELSE: Beeep\r\n...<\/pre>\n<p>Disclaimer: non ho deliberatamente avuto\u00a0l\u2019intenzione di fuorviare alcuno, ma gli errori\u00a0possono verificarsi. Non ci sono in circolazione\u00a0molte informazioni pubblicate su questi, di\u00a0conseguenza occorrer\u00e0 mettere insieme i vari\u00a0elementi che potranno saltar fuori, indagando\u00a0attraverso il codice sorgente, facendo prove, e\u00a0confrontandosi con gli altri. Per favore fateci\u00a0sapere i vostri progressi, se scoprite errori, se\u00a0avete esperienza di altri sistemi che sembrano\u00a0non allinearsi alle informazioni qui fornite. I vostri\u00a0riscontri, sia positivi che negativi, sono i\u00a0benvenuti. Contattatemi tramite il servizio invio\u00a0commenti che trovate qui, oppure troviamoci in\u00a0QL-users o nel Forum QL!<\/p>\n","protected":false},"excerpt":{"rendered":"Bighellonando qua e l\u00e0, mi \u00e8 capitato di andare a frugare dentro la famiglia PEEK di parole chiave di SMSQ\/E. Mi sono chiesto \n<a class=\"moretag\" href=\"http:\/\/www.hunggartorino.it\/ql\/ricerca-chiavi\/\"> [..more...]<\/a>","protected":false},"author":5,"featured_media":1049,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[41],"tags":[],"class_list":["post-1040","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-programmazione"],"_links":{"self":[{"href":"http:\/\/www.hunggartorino.it\/ql\/wp-json\/wp\/v2\/posts\/1040","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.hunggartorino.it\/ql\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.hunggartorino.it\/ql\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.hunggartorino.it\/ql\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"http:\/\/www.hunggartorino.it\/ql\/wp-json\/wp\/v2\/comments?post=1040"}],"version-history":[{"count":8,"href":"http:\/\/www.hunggartorino.it\/ql\/wp-json\/wp\/v2\/posts\/1040\/revisions"}],"predecessor-version":[{"id":1054,"href":"http:\/\/www.hunggartorino.it\/ql\/wp-json\/wp\/v2\/posts\/1040\/revisions\/1054"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.hunggartorino.it\/ql\/wp-json\/wp\/v2\/media\/1049"}],"wp:attachment":[{"href":"http:\/\/www.hunggartorino.it\/ql\/wp-json\/wp\/v2\/media?parent=1040"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.hunggartorino.it\/ql\/wp-json\/wp\/v2\/categories?post=1040"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.hunggartorino.it\/ql\/wp-json\/wp\/v2\/tags?post=1040"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}