****************************************** * Les interruptions de la HP48 S,SX,G,GX * * * * Ruses a connaitre * * * * Par Regis DUCHESNE (HPReg) * ****************************************** Cette documentation s'adresse au programmeur avertit en langage assembleur HP48 (la syntaxe utilisee est celle de l'assembleur ASMFLASH v6.1 ou HPASM v1.0, de Phong N'Guyen (HPNINJA)). Elle suppose la possession du la superbe publication "Voyage au centre de la HP48" par Paul Courbis aux editions Angkor, disponible au "Temple" de la HP48, le magasin Maubert Electronic, a Paris, France. Elle rassemble les connaissances francaises (donc mondiales) actuelles sur le sujet a la date 25/5/1994. Il ne sera fait aucune distinction entre les differentes versions des machines eXpandables, et celles qui ne supportent pas les cartes, car les ROMs de ces machines sont identiques. Par contre, nous prendrons en compte les differences entre les machines S et les machines G. Bien que tous les programmes qui suivent soit de moi, je decline toute responsabilite quant a tout ce qui pourrait resulter de leur utilisation. _____________ I - Description generales I.I - Notion d'interruptions D'abord, qu'est-ce qu'une routine d'interruption? C'est une routine qui est executee non pas lorsque c'est a son tour de s'executer dans le deroulement normal d'un programme, mais lorsqu'un evenement particulier est survenu. A ce moment la, on dit qu'une interruption a lieu, le programme dont s'occupait le microprocesseur est interrompu, c'est-a-dire qu'il est momentanement arrete sur place, et le microprocesseur passe le controle a la routine dite d'interruption qui est chargee de traiter l'evenement qui a provoque l'interruption. Losrque ce traitement est termine, le microprocesseur rend le controle au programme qui peut alors continuer son execution. I.II Interet L'interet des routines d'interruption est de soulager le programmeur et d'ameliorer sa programmation: supposons que l'on veuille stocker les touches sur lesquelles appuie l'utilisateur pendant que on effectue une recherche comportant plusieurs etapes. Vous me direz, facile, il n'y a qu'a mettre le test de touche dans une routine et d'appeler cette routine par un GOSUB dans chacune des etapes de la recherche, dans les boucles... Cela devient vite fastidieux. Avec les interruptions, tout cela est tres simple: il suffit de dire a la routine d'interruption de tester les evenements claviers tous les dixiemes de secondes par exemple, et de stocker la touche en question si elle est appuyee. Des lors, ou que l'on soit dans le programme, l'on n'a plus a se soucier de ce test. Bien sur, cet exemple s'applique a d'autres evenements. Sur les micro- ordinateurs, il existe des tables qui associent a un evenement donne, une adresse, appelee vecteur d'interruption, qui n'est rien d'autre que l'adresse de la routine a appeler lorsque cet evenement se produit. Pour mettre ses propres routines en remplacement de celles de la machine, ce qui est quand meme le but de tout programmeur qui se respecte, il suffit donc de modifier cette table d'adresse. I III - Cas particulier Sur HP48, il n'existe qu'une seule routine d'interruption, sur laquelle se branche le microprocesseur SATURN lorsqu'un evenement particulier est survenu. C'est cette routine qui se charge alors, a la place du microprocesseur, de determiner quel element de la machine a provoque l'evenement, et quelle sous- routine doit traiter l'evenement. Puisque cette routine gere entierement les interruptions, nous l'appellerons "gestionnaire d'interruptions". Vous l'avez compris, le but de cette documentation est de vous apprendre a ecrire votre propre gestionnaire d'interruption, et a le substituer a celui de la machine. II - Description detaille du gestionnaire d'interruption de la HP48 II.I - Les types d'interruptions Il existe, sur HP48, deux types d'interruptions: - les interruptions masquables qui sont les interruptions declenchees par l'appui sur une touche autre que ON. Ces interruptions peuvent etre inhibees par l'instruction INTOFF (c'est a dire que lorsqu'elles surviennent, le microprocesseur ne reagit pas) et autorisees par l'instruction INTON. - les interruptions non masquables, declenchees par l'appui sur la touche ON, par le passage des horloges 1 et 2 (cf Voyage au centre de la HP48) par la valeur zero lorsque le bit de declenchement d'interruption de leur quartet de controle (cf Voyage au centre de la HP48) est arme, ainsi que par le retrait ou l'insertion d'une carte dans la machine. II.II - Source commente du gestionnaire d'interruption Par souci de clarte, le source precede le commentaire. Pour le source sur G, nous ne mettrons que les differences avec le source sur S, car que personne ne se leurre: les gestionnaires d'interruption des deux machines sont bien evidemment identiques, aux problemes d'adresse de RAM reservee pres (70000 sur S et 80000 sur G). Sur S: Sur G: ------ ------ 0000F ST=1 14 00012 GOC 00029 00012 GOC 00022 00015 ?ST=1 15 00015 ?ST=0 15 00018 GOYES 0001C 00018 GOYES 00020 0001A RTNCC 0001A RSTK=C 0001C RSTK=C 0001C GOTO 0003E 0001E C=ID 00020 RTNCC 00021 ?C<>0 B 00022 ?ST=1 15 00024 GOYES 00038 00025 GOYES 0001A 00026 GONC 0003E 00027 RTNSC 00029 ?ST=0 15 | 0002C RTNYES | 0002E RSTK=C | 00030 C=ID | plus rien 00033 ?C=0 B | ici sur G 00036 GOYES 0003E | 00038 P= 8 | 0003A GOTO 001BC | 0003E CD1EX 00041 D1= 0011E 00048 DAT1=C 1 0004C D1= 0011F 00053 C=DAT1 1 00057 CPEX 0 0005B CPEX 4 0005F D1=C 00062 D1= 045C 00062 D1= 05DB 00068 CPEX 4 0006C CPEX 0 00070 DAT1=C W 00074 C=0 A 00076 C=P 3 0007A P= 2 0007C GOC 00082 0007F C=C+1 P 00082 P= 1 00084 C=-C-1 P 00087 SETHEX 00089 P= 0 0008B ?SB=0 0008E GOYES 00093 00090 C=C+1 P 00093 D1=D1+ 16 00096 DAT1=C A 00099 D1=D1+ 5 0009C C=RSTK 0009E DAT1=C A 000A1 D1=D1+ 5 000A4 DAT1=A W 000A8 D1=D1+ 16 000AB C=ST 000AD DAT1=C A 000B0 D1=D1+ 3 000B3 C=B W 000B6 DAT1=C W 000BA D1=D1+ 16 000BD C=D W 000C0 DAT1=C W 000C4 D1=D1+ 16 000C7 C=R0 000CA DAT1=C W 000CE D1=D1+ 16 000D1 C=RSTK 000D3 DAT1=C A 000D6 D1=D1+ 5 000D9 CD0EX 000DC DAT1=C A 000DF GOTO 00140 00140 ST=1 13 00143 D0= 0012F 0014A A=DAT0 1 0014E ?ABIT=1 0 00153 GOYES 0015B 00155 GOLONG 01315 0015B D0= 0010E 00162 A=DAT0 1 00166 LC C 00169 DAT0=C 1 0016D ?MP=0 00170 GOYES 0017C 00172 GOSUB 002DA 00176 P= 11 00178 GOTO 001BC 0017C D0= 09 00180 LC 4 00183 DAT0=C 1 00187 D0= 08 0018B C=DAT0 1 0018F ?CBIT=0 0 00194 GOYES 001A3 00196 ST=1 0 00199 GOSUB 002FF 0019D P= 1 0019F GOTO 001BC 001A3 D1= 0000 001A9 A=DAT1 A 001AC P= 0 001AE LC A5C3F 001B5 ?A=C A 001B8 GOYES 001C3 001BA P= 7 001BC GOVLNG 01FC6 001C3 GOSUB 003C6 001C3 GOSUB 003C0 001C7 D0= 0011C 001CE C=DAT0 1 001D2 ?CBIT=0 2 001D7 GOYES 001E2 001D9 CBIT=0 2 001DE DAT0=C 1 001E2 D0= 1A 001E6 C=DAT0 1 001EA ?CBIT=0 1 001EF GOYES 001FA 001F1 CBIT=0 1 001F6 DAT0=C 1 001FA D1= 00DB 001FA D1= 00E1 00200 C=DAT1 1 00204 D0= 0010F 0020B A=DAT0 1 0020F ?A=C P 00212 GOYES 00218 00214 GOTO 00176 00218 GOSBVL 0131D 0021F GOSUB 005EC 00223 GOSUB 00672 00227 GOSUBL 00C74 0022D GONC 00236 00230 GOLONG 00E38 00236 GOSUB 00768 0023A GOSUB 008D8 0023E D1= 0489 0023E D1= 0608 00244 C=DAT1 W 00248 B=C W 0024B D1=D1+ 16 0024E C=DAT1 W 00252 D=C W 00255 D1=D1+ 16 00258 C=DAT1 W 0025C R0=C 0025F D1=D1+ 16 00262 C=DAT1 A 00265 RSTK=C 00267 D1=D1+ 5 0026A C=DAT1 A 0026D D0=C 00270 D1= 04C3 00270 D1= 0642 00276 C=DAT1 X 0027A OUT=C 0027D GOSUBL 01160 00283 D1= 0486 00283 D1= 0605 00289 C=DAT1 A 0028C ST=C 0028E D1=D1- 16 00291 A=DAT1 W 00295 D1=D1- 5 00298 C=DAT1 A 0029B RSTK=C 0029D D1=D1- 5 002A0 C=DAT1 A 002A3 SB=0 002A6 CSR A 002A8 P= 0 002AA C=C+1 P 002AD GOC 002B2 002B0 SETDEC 002B2 P= 1 002B4 C=C-1 P 002B7 P=C 2 002BB D1= 045C 002BB D1= 05DB 002C1 C=DAT1 W 002C5 D1= 0011E 002CC C=DAT1 1 002D0 D1=C 002D3 C=RSTK 002D5 ST=0 14 002D8 RTI 002DA Commentaires: ------------- Le gestionnaire commence en 0000F. Cela a semble-t-il ete determine a tatons, mais on le cherchait deja instinctivement a l'origine vers le tout debut de la ROM. 0000F: - Le flag 14 indique si l'on vient de la routine du gestionnaire ou non. Il est donc teste par des routines qui sont appelles par le gestionnaire, mais pas uniquement par lui. 00012 a 0003A: - Le flag 15 indique si l'on doit traiter les interruptions (masquables ou non masquables). Lorsqu'il est arme, il n'interdit pas la premiere interruption qui survient, mais le gestionnaire lui reserve un traitement special: au lieu de la traiter comme d'habitude, on ressort du gestionnaire par un RTN normal et non un RTI (Retour de gestionnaire d'interruption). En fait, l'on fait croire au microprocessseur que l'on est toujours dans le gestionnaire, on rend donc a partir de ce moment la le programme ininterruptible. - Mais pourquoi ne pas avoir fait un simple test? N'oublions pas que le gestionnaire est utilise lorsqu'un programme est interrompu. Il doit donc d'abord sauver les registres qu'il veut employer pour pouvoir, a la fin, rendre les bonnes valeur au programme. Or, la CARRY est un registre comme un autre, et c'est pour ne pas modifier sa valeur que selon son etat, on fait le test du flag 15 d'une maniere ou d'une autre. Cela constitue la Premiere Ruse. - Sauvegarde de C.A dans RSTK - Signalons au passage que le gestionnaires sur S verifie qu'aucun module n'a ete deconfigure (ID, l'identificateur du premier module desirant etre configure, doit etre nul, sinon on provoque un arret systeme avec un WSLOG de valeur celle de P=8, c'est-a-dire "anomalie de configuration"), et pas le gestionnaire sur G: en pratique, on peut donc deconfigurer certains modules sous RPL sur G, sans provoquer d'arret systeme. 0003E a 000DC: - Sauvegarde des registres qui vont etre utilises, aux adresses: Sur S: Sur G: ------ ------ 0011E quartet 0 de D1 7045C 805DB quartet de l'adresse de base de la RAM, le meme qu'en 0011F (dans notre cas, 7 pour une S, 8 pour une G) 7045D 805DC quartets 4 a 1 de D1 70461 805E0 quartets F a 5 de C 7046C 805EB SB 7046D 805EC 9 si mode decimal, F si mode hexadecimal 7046E 805ED NOT CARRY 7046F 805EE P 70470 805EF 0 70471 80EF0 C.A (le RSTK est depile au passage) 70476 80EF5 A.W 70486 80F05 bits 0 a 11 de ST 70489 80F08 B.W 70499 80F18 D.W 704A9 80F28 R0.W 704B9 80F38 PC (en fait depilage du RSTK de retour du gestionnaire) 704BE 80F3D DO - On en profite pour mettre P a 0 et passer en mode hexadecimal. 000DF: - Ce GOTO est necessaire a cause de la RAM I/O qui est toujours presente entre les adresses 00100 et 00139 incluses. 00140: - Le role du flag 13 est encore assez obscur... 00143 a 00178: - Test si l'horloge 2 est arretee, auquel cas il y a arret systeme avec WSLOG "Horloge corrompue". A noter la bizarre lecture avant ecriture en 0010E (quartet de configuration) ainsi que la routine 002DA qui "calme le hard", quand on l'a "excite" en enlevant une carte par exemple (arret systeme WSLOG "Module retire"), en ecrivant C en 0010E jusqu'a ce que MP=0. C'est assez classique de voir de telles routines qui semblent tres peu fiables, quand on s'approche du coeur de la machine... 0017C a 0019F: - Test du niveau minimal des piles, avec eventuellement OFF (routine en 002FF) puis arret systeme WSLOG "Batteries tres faibles". 001A3 a 001BC: - Verification du CMOS Word: A5C3F. Il sert a tester les erreurs de parite de la RAM, en effet, il s'ecrit en binaire: 1010 0101 1100 0011 1111, ce qui correspond a toutes les combinaisons de bits pris 4 a la suite. En cas de probleme, arret systeme WSLOG "RAM Corrompue". 001C3: - Si les transmissions sont actives, et que l'on est en train de transmettre un caractere, allume l'indicateur (a cristaux liquide) de transmission, attend la fin de la transmission, en attendant un certain laps de temps inversement proportionnel a la vitesse de transmission. Les valeurs d'attente se trouvent dans une table en ROM. 001C7 a 001F6: - Si les interruptions IR: "Buffer de sortie vide" et "Buffer d'entree plein" sont autorisees, la machine signale ces evenements en desarmant les bits. Pour savoir si l'on a recu un carctere via l'IR, il suffit donc d'armer le bit d'interruption "Buffer de sortie", d'ecrire son caractere dans le buffer de sortie, et d'attendre jusqu'a ce que le bit soit a nouveau desarme. 001FA a 00214: - Test si l'interrupteur d'une carte a change de position et eventuellement, arret systeme WSLOG "Module retire". 00218: - Gestion complete de l'horloge de la machine: cette routine fait la difference entre la derniere valeur qu'elle a mise dans l'horloge 2, et celle qu'elle y lit, et incremente cette valeur qui correspond au temps ecoule, a l'offset d'horloge dont elle verife le CRC, provoquant eventuellement un arret systeme WSLOG "Offset d'horloge corrompue". Elle teste si une alarme n'est pas arrivee a terme, puis, si l'affichage de l'heure est actif, elle reinitialise l'horloge 2 a la valeur d'1 seconde, si l'affichage de l'heure n'est pas actif, elle reinitialise l'horloge 2 a la plus petite valeur entre 1 heure et le temps avant la prochaine alarme. Attention, cette routine demande beaucoup de concentration pour etre comprise, car il y a des instructions qui ne servent qu'a calculer des corrections de temps en fonction du temps mis pour executer d'autres instructions. Si vous tenez vraiment a desassembler, munissez vous d'une table des cycles horloge (cf Voyage au centre de la HP48). 0021F: - Si l'horloge 1 n'est pas passee par la valeur 0, et qu'une touche a ete pressee, arme l'horloge 1 a 6/16 seconde, c'est la duree pendant laquelle une touche doit etre pressee pour qu'elle soit prise en compte par la machine. Pour le verifier, on peut sous RPL effleurer la touche SHIFT droite ou gauche. On voit alors que l'indicateur SHIFT correspondant s'allume pendant une fraction de seconde, mais ne reste pas allume, ce qu'il ferait si l'on appuyait au moins 6/16 de seconde sur cette touche. 00223: - Gestion des indicateurs a cristaux liquides: temoin de pile faibles, SHIFT droite et gauche, mode alphanumerique. 00227 a 00230: - Test la touche ON. premier test: Si l'on ne presse aucune autre touche que ON pendant un certain temps, il y a incrementation du ATTN Counter (en 70679 sur S et 807F7 sur G), puis rebranchement a l'adresse 0023A. Si l'on presse une autre touche en meme temps que ON, on cherche si l'on trouve une combinaison connue parmi: ON +, ON -, ON B, ON A F, ON 1, ON 4, ON C, ON D, ON E, ON SPC. Si l'on n'en trouve pas, retour au premier test. Sinon, branchement sur les routines respectives aux adresses: 00F94, 00F9A, 0023A, 01FD3, 01011, 010CE, 01FBD, 01176, 01165, 01087. 00236: - Gestion globale du clavier, avec bufferisation des touches. 0023A a 002D3: - Restauration des registres utilises. A noter la restitutuion du registre OUT, le masque de sortie etant pris en 704C3 sur S et 80642 sur G. Cela signifie que si vous voulez tester proprement le clavier alors que ce gestionnaire est actif, il vous faut sauver C.X a cette adresse avant de faire chaque OUT=C, pour eviter qu'une interruption survienne entre votre OUT=C et votre lecture du registre IN, et remplace votre masque de sortie par celui lu a cette adresse. - Admirez la maniere de recuperer le mode decimal ou hexadecimal, et surtout SB dont on s'apercoit qu'il est arme lorque le quartet sortant d'une rotation a droite est non nul. Cela constitue la Deuxieme et la Troisieme Ruse. 002D5: - On previent les routines que l'on sort du gestionnaire. 002D8: - On previent le microprocesseur que l'on sort du gestionnaire. Remarque: Si ST=1 15, il faut bien voir que 2 niveaux de RSTK sont utilises, un pour l'adresse de retour de la routine du gestionnaire, et l'autre pour la sauvegarde temporaire de C.A. Il faut donc prendre garde, lorsque le gestionnaire d'interruption est actif, a ne pas utiliser dans son programme plus de 6 niveaux de la pile RSTK (qui en comporte 8), car sinon voici, avec des valeurs ici quelconques de RSTK, ce qui se passera: 80A34 0003B 80A34 80B96 80ACC 80B96 80C32 80A34 80C32 8157B interruption 80B96 une fois revenu 8157B 81231 -----------> 80C32 --------------> 81231 80FA5 8157B 80FA5 81034 81231 00000 00000 80FA5 00000 Resultat: lorsque votre programme depilera sa derniere valeur, qui aurait du etre 81034, il se branchera en fait en 00000, c'est a dire qu'il provoquera un arret systeme normal, WSLOG "Redemarrage a chaud" III - Detournement du gestionnaire de la HP48 III.I - Position du probleme Comme vous pourriez le constater en desassemblant completement le gestionnaire de la machine, il n'y a aucun moyen, une fois rentre dans la routine, de se brancher a une adresse fixee par vous. La seule possibilite pour substituer un gestionnaire a celui de la machine est donc de l'implanter a l'adresse 0000F. Mais comment faire puisqu'il y a de la ROM a cet endroit la? La reponse est simple, mais il fallait y penser: il suffit de deconfigurer un module RAM commencant a l'adresse AdrRam, dans lequel on aura ecrit notre gestionnaire a l'adresse AdrRam+0000F, puis de le reconfigurer a l'adresse 00000. Ainsi, la machine executera bien en 0000F notre propre gestionnaire. Nous disposons de deux sortes de module RAM: les cartes RAM (pour ceux qui ont la chance d'en avoir), et la RAM interne. Etudions chacun de ces cas... III.II - Detournement par carte RAM (HP48 eXpandable seulement) III.II.I - Explications Afin que la carte reste a peu pres utilisable, nous allons eviter d'ecrire "betement" notre nouveau gestionnaire a l'adresse AdrRam+0000F. Nous allons essayer de creer un veritable objet que nous mettrons au debut de la carte. Or il s'avere que le seul objet qui, mis a l'adresse AdrRam, puisse comporter des donnees a partir de l'adresse AdrRam+0000F est un objet backup ayant un nom vide. Evidemment, on veut que cet objet soit le plus petit possible, donc il faut le prendre de la plus petite taille de module configurable, soit 2 Ko. Le but du jeu, avec un nouveau gestionnaire, est que la machine ne plante pas! On va donc, en premier lieu, reprendre le gestionnaire de la machine, mais en lui apportant quelques petites modifications: Cf le commentaire du desassemblage du gestionnaire a l'adresse 000DF (voir plus haut), nous pouvons remarquer que si nous supprimons le GOTO 00140, nous avons une zone de 33 quartets, comprise entre les adresses 000DF et 000FF incluses, dont nous pouvons pleinement profiter, car HP nous a gentiment laisse de la place! Il n'est bien sur pas question de mettre toutes nos modifications du gestionnaire dans seulement 33 quartets, mais nous pouvons desormais nous brancher sur une routine que nous qualifierons d'"auxiliaire". Pour que l'on n'ait pas a reecrire notre objet backup a chaque fois que l'on deplace la routine auxiliaire, on a interet a stocker l'adresse de la routine auxiliaire en RAM interne, par exemple a l'adresse 7067E sur S, 807FC sur G (ce sont les adresses ou sont ecrites les adresses des noyaux des machines, elles ne servent plus a rien une fois que la machine a fini son arret systeme). III.II.II - Creation de l'objet backup Voici donc le programme RPL (qui suppose la possession de programmes classiques tels que PEEK, POKE, et H-> (librairie DEV par Etienne de Foras (ETI) de numero 1796 sur S, 1797 sur G) qui est aussi appele GASS ou RASS dans Voyage au centre de la HP48.) permettant de creer son propre objet backup: << "26B205001000000" @Prologue objet backup vide de taille 2 Ko (sans CRC) #F #D0 PEEK + @Recopie gestionnaire jusqu'a l'adresse 000DF exclue "34vwxyz808E" + @Rajoute les instructions LC zyxwv, PC=(C) qui @ tiennent sur 11 quartets, vwxyz vaut sur S:E7607, sur @ G:CF708 1 86 START "0" + NEXT @Rajoute 86=(#00140-#00100)+(33-11) quartets de vide #140 #EC0 PEEK + @Recopie le reste de la ROM pour remplir les 2 Ko "119200abcd" + @Rajoute le CRC de l'objet, abcd vaut sur S:C746, sur @ G:ADE1 H-> >> @Transformation de l'hexadecimal en un objet Nous avons desormais le nouveau gestionnaire, stockez-le au debut de votre carte (le plus simple est de MERGEr la carte, de faire 0 STO sur l'objet backup, puis de faire 0 PVARS x FREE, ou x est le numero du port contenant la carte). III.II.III - Creation de la routine auxiliaire A titre d'exemple, je vous propose comme routine auxiliaire une inversion video des 14 lignes du haut de l'ecran situees avant la barre horizontale (pourquoi pas...). Voici son source: "D0= 12D21 C=DAT0 A D0=C C=DAT0 A D0=C P= 2 *BOUCLE C=DAT0 16 C=-C-1 W DAT0=C 16 D0=D0+ 16 C=DAT0 16 C=-C-1 W DAT0=C 16 D0=D0+ 16 C=DAT0 B C=-C-1 B DAT0=C B D0=D0+ 2 P=P+1 GONC BOUCLE GOVLNG 00140 @" Remarques: - Ce source est compatible S et G, il suffit de faire une lecture en 12D21 pour obtenir l'adresse ou est sauvee l'adresse de la bitmap ecran, soit sur S:7050E et sur G:8068D. Cela constitue la Quatrieme Ruse. - Noter l'utilisation en incrementant P, plutot qu'en le decrementant, ce qui evite d'avoir a le remettre a zero a la fin. - Noter que DAT0=C 16 prends un cycle horloge de moins que DAT0=C W qui fait la meme chose... Assemblez le source et stockez l'objet Code qui contient la routine auxiliaire, a une adresse fixe: vous pouvez par exemple le mettre dans le var pour l'instant. Il ne nous reste plus qu'a ecrire le petit programme que nous appellerons "echangeur" qui va deconfigurer la carte et la reconfigurer en 00000 pour une taille de 2 Ko. III.II.IV - Creation de l'echangeur Voici le source d'un echangeur sous sa forme la plus simple: "AD0EX C=0 A P= 4 LC x %x vaut 8 pour une S, et C pour une G UNCNFG P= 3 LC FF CONFIG C=0 A CONFIG P= 0 D0=A GOVLNG 12002 %A=DATO A, D0=D0+ 5, PC=(A) @ Remarque: - Parfois, Il ne sert a rien d'utiliser les routines SAV.REG (0679B) et LOAD.REG (067D2). III.II.V - Action Vous voici fin pret. La premiere des choses a faire est de retirer une eventuelle deuxieme carte (celle qui ne comporte pas l'objet backup contenant le nouveau gestionnaire) de la machine et d'effectuer un arret systeme: ainsi, l'on est sur que la seule carte restante sera bien configure en 80000 sur les S, et en C0000 sur les G, ce qui va beaucoup aider l'echangeur... Maintenant, recuperez l' adresse de l'objet Code contenant la routine auxiliaire, ajoutez #A pour avoir l'adresse de la routine elle-meme, et POKEz cette adresse en 7067E sur S, 807FC sur G. Assemblez l'echangeur, ne le stockez surtout pas car vous modifieriez l'adresse de la routine auxiliaire, mais EVALuez-le: vous n'avez plus qu'a tapoter sur les touches de votre machine pour generer de belles interruptions. Vous venez d'assister a la naissance du multi-tache sur HP... C'est beau hein? Vous pouvez d'ailleurs constater au passage que c'est l'enfoncement d'une touche qui provoque une interruption, et non le fait de rester appuye dessus (a part pour la touche ON). Vous pouvez vous amuser a faire afficher l'heure a votre machine, il y aura inversion video du haut de l'ecran a chaque seconde! III.II.VI - Avantages, Inconvenients Les avantages sont interessants: - possibilite de faire plusieurs choses a la fois sous RPL, et en fabriquant un echangeur qui fait l'operation inverse, on peut revenir proprement (ici on est oblige de faire un arret systeme pour tout arreter) au gestionnaire de la machine, et avoir ainsi a nouveau acces a sa carte. - En ameliorant encore un peu plus l'echangeur, on peut meme utiliser une deuxieme carte pendant ce temps, ce qui permet par exemple d'observer cette animation sous tout programme autorisant les interruptions: Libex v2.2 de DEYLONE et HPReg par exemple... - on peut faire tout et n'importe quoi dans la routine auxiliaire: si l'on veut utiliser d'autres registres que ceux qui ont ete sauves par la machine, il suffit de reecrire un petit bout qui les sauve en debut de routine auxiliaire, et qui les restaure avant la fin de routine auxiliaire. De meme, personne ne vous oblige a vous rebrancher en 00140 sur le gestionnaire de la machine, on peut tout a fait sauter tous les tests de CMOS Word etc. dont on se fiche eperdument, et se rebrancher un peu plus loin, en 00218 par exemple, histoire de gagner un peu de temps machine. Remarque: J'avais essaye de faire une routine qui supprimait toutes les actions combinees avec la touche ON, afin que l'on ne puisse plus faire de ON A F sous RPL. Mais cela n'a pas bien marche, en appuyant beaucoup de fois sur ces trois touches, j'ai tout de meme obtenu un memory lost. Il doit donc y avoir d'autres tests a supprimer. Mais les inconvenients aussi sont de taille: - la zone des adresses 00000 a 0000E incluses est ecrasee par la partie declarative de l'objet backup. C'est dommage car lorsque la machine se plante, elle fait un redemarrage a chaud en 00000 la plupart du temps. Heuresement, les modules sont souvent tous redeconfigures dans ce cas-la. - detourner les interruptions de cette maniere necessite de gros moyens: une carte qui rend le systeme inutilisable sur les machines non eXpandables, un objet de 2 Ko... III.III - Detournement par la RAM interne III.III.I - Explications Evidemment, a premiere vue l'on se dit, il n'y a qu'a faire pareil, je deconfigure la RAM interne et je la reconfigure en 00000. Mais que va-t-il se passer lorsque le programme, qui est en RAM interne (on s'interdit desormais toute carte), va vouloir deconfigurer la RAM interne? C'est tout simple: l'instruction de deconfiguration va etre executee, et le registre PC, dont la valeur n'aura bien sur pas ete modifie, va gentiment faire pointer le microprocesseur vers du code qui n'est plus la, le programme va donc lamentablement se vautrer, en degoutant a jamais son auteur de s'etre attaque aux interruptions... Bien sur, l'ideal serait de trouver un bout de programme en ROM qui deconfigurerait la RAM interne et la reconfigurerait en 00000, en rendant la main a notre programme, mais il fallait se rendre a l'evidence, ce programme tant cherche n'existait pas en un seul morceau, il n'en existait que des petits bouts dissemines, l'un deconfigurant, l'autre reconfigurant... Comment recoller ces morceaux? Comment pouvait-on ecrire un programme en ROM? Heureusement, un jour, une bande de trois codeurs ETI, GHERKIN et Cyrille de Brebisson (HP Mad), des habitues du "Temple", a force de coucher avec leurs HP, enfanterent d'une idee geniale dont on ne sait en fait pas tres bien qui en est le pere: un petit exemple valant mieux qu'un long discours, supposons que l'on parvienne a trouver dans la ROM de la HP les petits bout de programme suivant: a l'adresse 014FC: C=C+C A a l'adresse 2ADC4: CSL A C=C+1 A RTNSC RTNSXM Considerons l'execution du source suivant, que va-t-il se passer? A=PC GOINC FIN C=C+A A RSTK=C LC 2ADC4 RSTK=C C=0 A C=C+7 A GOVLNG 014FC *FIN Commentaire: ------------ On charge l'adresse du label FIN dans RSTK, puis on charge l'adresse 2ADC4 dans RSTK, puis on charge C.A a la valeur 00007. Alors, on se branche en 014FC, donc C.A vaut 0000E puis 0000F puis il y a depilage (XM est arme au passage, cela nous laisse indifferent) donc retour a l'adresse 2ADC4, ou C.A prends la valeur 000F0 puis a nouveau depilage et retour au label FIN (CARRY armee au passage, ce dont nous nous moquons). Vous voyez, la Cinquieme Ruse est la plus grandiose. Il ne reste plus qu'a trouver les bouts de programmes qui font ce qu'on veut, en un minimum de saut. Heureusement pour vous, ca je m'en suis charge! III.III.II - Le driver d'interruption RAMINT Voici le source 'RAMINT.SRC' d'un echangeur s'appliquant a la RAM interne. Il est compose de 2 routines: l'une, INIT.INT qui permet de passer sur son propre gestionnaire d'interruption, qui doit necessairement etre en RAM interne et dont on passe l'adresse actuelle dans C.A, et l'autre STOP.INT qui sert a revenir au gestionnaire de la HP48. Les commentaires sont cette fois assez succints, il n'y a quand meme pas de raison que ce soit moi qui me tape tout le sale boulot hein? "*INIT.INT %Construit l'instruction permettant de sauter a l'adresse du gestionnaire D0= 61FD4 A=DAT0 A %Utilisation de la Quatrieme Ruse: A=AdrRam D0=A C=C-A A %Le nouveau gestionnaire va etre lui aussi translate CSL W CSL W LC D8 %L'instruction construite est un GOVLNG, qui se code 8Dvwxyz %Suvegarde des 7 quartets ecrases en ?000F sur le CMOS Word et le denut de %la taille de la RAM D0=D0+ 15 %D0= ?000F A=DAT0 7 ST=0 15 %On coupe les interruptions machines car on va ecraser le CMOS Word DAT0=C 7 D0=D0- 15 DAT0=A 7 %Construit le programme en ROM AD0EX C=RSTK %Il faut modifier l'adresse de retour de la routine INIT.INT C=C-A A % car le programme qui l'appelle va etre translate RSTK=C C=A A %A cet endroit, C.A=70000 pour une S, et 80000 pour une G C=C+C A % permet de determiner si on a affaire a une S ou une G GOC G % car je n'ai pas reussi a trouver d'adresses compatibles ici LC 3EFCB %On trouve en ROM a cette addresse: P= 0 GOC 3EF60 DAT1=A B CONFIG D1=C % RTN. Il est donc necessaire de faire pointer D1 sur la ROM GONC S *G LC 47228 %CONFIG $0E8F:C=C!D P P= 0 ASL A RTNSXM *S RSTK=C LC 567C4 %CONFIG SETHEX C=C+D A P= 6 D=D+C A RTN RSTK=C D0=A D0=D0+ 5 C=DAT0 A C=0 B %A cet endroit, C.A=taille de configuration du module RAM D=C A D=-D A ACEX A A=A+C A GOVLNG 34E09 %UNCNFG C=A-C A RTN *STOP.INT C=0 A D0=C A=DAT0 7 B=A W %B contient les 7 quartets ecrases par INIT.INT en ?000F %Reecriture du CMOS Word LC A5C3F DAT0=C A D0= 61FD4 A=DAT0 A %A.A=AdrRam C=RSTK C=C+A A %Translation dans le sens inverse... RSTK=C GOSUB SI2 D0= 61FD4 C=DAT0 A %C.A=AdrRam D0=C D0=D0+ 15 C=B W DAT0=C 7 %Restitue les 7 quartets ecrases GOVLNG 010E5 %Reautorise les interruptions du gestionnaire de la HP48 *SI2 C=RSTK C=C+A A %Translation dans le sens inverse... RSTK=C LC 0224F D0= 47333 %A cet endroit, A.B=07 sur S et 00 sur G A=DAT0 B %A cette addresse, une routine en ROM reconfigure la RAM, et C=C+A B % surtout reecrit la taille de la RAM et son adresse en ?0005 RSTK=C C=0 A ST=0 15 GOVLNG 4CFD0 %A cette adresse, on trouve en ROM: UNCNFG RTNCC @" III.III.III - Construction du nouveau gestionnaire C'est bien beau de pouvoir changer de gestionnaire d'interruption, mais encore faut-il avoir un autre gestionnaire sous la main! Voyons comment fabriquer un tel gestionnaire dans le cas le plus general. En fait, le source suivant est celui d'un gestionnaire qui ne fait rien, a part sauver puis restituer tous les registres, un peu a la maniere de celui de la machine: "*NEW.GEST %Sauvegarde de tous les registres RSTK=C C=PC GOC NI1 C=C+16 A %Ces instructions ne modifient pas la CARRY C=C+16 A % vous pouvez verifier... C=C+15 A GOTO NI2 *NI1 C=C+16 A C=C+16 A C=C+15 A GOTO NI3 %On reserve de la place pour stocker les registres $00000 %D0 $0000 %HEX/DEC,P,HST,NON CARRY $00000 %D1 $000 %ST On ne sauve que les flags 0 a 11 $0000000000000000 %A $0000000000000000 %B $0000000000000000 %C $0000000000000000 %D $0000000000000000 %R0 $0000000000000000 %R1 $0000000000000000 %R2 $0000000000000000 %R3 $0000000000000000 %R4 $00000 %RSTK1 Correspond au registre PC $00000 %RSTK2 $00000 %RSTK3 $00000 %RSTK4 $00000 %RSTK5 $00000 %RSTK6 On ne sauve pas RSTK8 car il est cense etre ecrase par le $00000 %RSTK7 RTSK=C au debut de cette routine. *NI2 CD0EX DAT0=C A C=0 A C=C+1 A GONC NI4 *NI3 CD0EX DAT0=C A C=0 A *NI4 D0=D0+ 5 CPEX 1 CSL A ?MP=0 GOYES NI5 CBIT=1 3 *NI5 ?SR=0 GOYES NI6 CBIT=1 2 *NI6 ?SB=0 GOYES NI7 CBIT=1 1 *NI7 ?XM=0 GOYES NI8 CBIT=1 0 *NI8 CSL A C=C-1 P SETHEX DAT0=C A D0=D0+ 4 CD1EX DAT0=C A D0=D0+ 5 C=ST DAT0=C A D0=D0+ 3 DAT0=A W D0=D0+ 16 BCEX W DAT0=C W D0=D0+ 16 C=B W C=RSTK DAT0=C W D0=D0+ 16 C=D W DAT0=C W D0=D0+ 16 C=R0 DAT0=C W D0=D0+ 16 C=R1 DAT0=C W D0=D0+ 16 C=R2 DAT0=C W D0=D0+ 16 C=R3 DAT0=C W D0=D0+ 16 C=R4 DAT0=C W D0=D0+ 16 P= 9 *NI9 C=RSTK DAT0=C A D0=D0+ 5 P=P+1 GONC NI9 %Ici, vous pouvez rajouter ce que vous voulez, c'est une sorte de routine %auxiliaire, vous pouvez mettre, ce qui nous servira d'exemple par la suite % D0= 00138 % C=DAT0 A % D0= 20 % DAT0=C A %Restaure tous les registres dans le sens inverse A=PC GOINC NI2 C=C+A A D0=C P= 9 *NI10 D0=D0- 5 C=DAT0 A RSTK=C P=P+1 GONC NI10 D0=D0- 16 C=DAT0 W R4=C D0=D0- 16 C=DAT0 W R3=C D0=D0- 16 C=DAT0 W R2=C D0=D0- 16 C=DAT0 W R1=C D0=D0- 16 C=DAT0 W R0=C D0=D0- 16 C=DAT0 W D=C W D0=D0- 16 C=DAT0 W RSTK=C B=C W D0=D0- 16 C=DAT0 W BCEX W D0=D0- 16 A=DAT0 W D0=D0- 3 C=DAT0 A ST=C D0=D0- 5 C=DAT0 A D1=C D0=D0- 4 C=0 A C=DAT0 4 D0=D0- 5 C=C+1 P GOC NI11 SETDEC *NI11 CSR A CBIT=0 0 CBIT=0 2 CBIT=0 3 SB=0 CSR A CPEX 1 C=C-1 A C=DAT0 A D0=C C=RSTK RTI @" Stockez ce gestionnaire sous le nom 'NG', il va nous servir. Remarque: - On constate que, comme le gestionnaire d'interruption de la HP, celui-ci necessite l'utilisation de 2 niveaux de la pile RSTK III.III.IV - Utilisation de RAMINT En plus de cette documentation, vous avez surement entre les mains le fichier RAMINT. C'est un fichier precompile, c'est-a-dire qu'il fait exactement la meme chose que RAMINT.SRC, mais il est beaucoup plus petit (116 Octets, c'est la taille de la routine, contre 900 Octets, taille du source) et surtout, il ne prends quasiment aucun temps de compilation, car l'assembleur a juste a prendre en compte deux labels (pour ceux qui connaissent, l'assembleur sert ici de linker), et se contente de recopier le reste, que j'ai moi-meme compile pour vous. Le source d'un programme qui utilise RAMINT doit se presenter ainsi: "..... GOSUBL SUITE *NEW.GEST ..... *SUITE C=RSTK %C.A contient bien l'adresse courante du nouveau gestionnaire GOSUBL INIT.INT ..... %ca y'est, on a change de gestionnaire GOSUBL STOP.INT ..... %On est revenu sur le vrai gestionnaire 'RAMINT %Si vous n'avez pas RAMINT, mettez RAMINT.SRC @" Ou encore, de cette maniere, qui sert d'exemple et qui utilise le gestionnaire construit au paragraphe precedent: "GOSBVL 0679B A=PC GOINC NEW.GEST C=C+A A %C.A contient bien l'adresse courante du nouveau gestionnaire GOSUB INIT.INT %ca y'est, on a change de gestionnaire, ici on boucle mais on pourrait tres bien %trier une liste de nombre, peinard... LC 80000 *LOOP C=C-1 A GONC LOOP GOSUB STOP.INT %On est revenu sur le vrai gestionnaire GOSBVL 01C7F %Remets a jour la bitmap ecran GOVLNG 138B9 %Recupere registres et retour au RPL 'NG %cf paragraphe precedent 'RAMINT %Si vous n'avez pas RAMINT, mettez RAMINT.SRC @" Assemblez, EVALuez, et tapotez... C'est encore plus beau! III.III.V - RAMINT vu a la loupe - INIT.INT: En entree, C.A doit contenir l'adresse courante du nouveau gestionnaire qui doit se trouver en RAM interne. On suppose aussi que P= 0. En sortie, C,D,D0,D1 sont modifies et le flag 15 est desarme. - STOP.INT: En entree, on suppose P= 0 En sortie, B,C,D0,D1 sont modifies et les interruptions HP autorisees. - Il est grandement conseille, sous peine de crash avec STOP.INT, de ne rien ecrire, une fois que INIT.INT a ete execute, dans la zone 00000 a 00006. - On constate que l'on a besoin de 3 niveaux de la pile RSTK pour faire l'une ou l'autre des manipulation. En realite, le fait d'echanger n'en utilise que 2, c'est la presentation simplifiee de l'echangeur sous forme de routines qui implique le troisieme. Il existe une methode pour n'en utiliser qu'un (ce qui ferait 2 avec cette presentation simplifiee). C'est ATLAS qui en a trouve le principe: il suffit d'arreter l'horloge 2 et d'ecrire sa routine dedans, en en sauvant sa valeur. En effet, l'horloge deux est quel que soit la configuration ed la RAM interne, situee entre les adresses 00138 et 0013F incluses. J'ai ecrit un echangeur fonde sur ce principe, mais il n'apporte en fait pas grand chose de plus, a part une augmentation de taille de 150 octets. Il n'avait donc aucune raison de figurer ici, mais c'est neammoins un tres bon exercice de style si vous voulez vous entrainer (la premiere difficulte consiste a bloquer l'horloge puis a la faire redemarrer sans que la machine fasse d'arret systeme...) - Prenez bien garde aux adresses que vous emploierez, car lors d'un echange, il est necessaire de recalculer les adresses des bitmaps ecran et menu, et beaucoup d'adresses vont se trouver decalees. Prenez garde aussi a ne pas utiliser, lorsque la RAM interne est reconfiguree en 00000, les routines en ROM situees aux adresses comprises entre 00000 et 10000 sur les machines sans cartes, et entre 00000 et 40000 sur celles eXpandable. - N'oubliez pas qu'il est tres facile de provoquer une interruption a intervalle regulier: on met une valeur initiale dans l'horloge 2, quand l'horloge 2 passe a la valeur zero, elle declenche une interruption si son quartet de controle (Cf voyage au centre de la HP48) l'y autorise, et dans la routine d'interruption, il suffit de reinitialiser l'horloge 2 a cette meme valeur initiale. - Le gros avantage de RAMINT est sa compatibilite S/G. Profitez-en! IV - Conclusions J'espere que cette documentation qui traite d'un sujet relativement epineux sur HP48 aura eu le merite d'etre claire. J'espere que tout ceux qui utiliseront ces sources les utiliseront a bon escient, et n'oublierons pas de me remercier dans leur production futures. Il faut bien se dire que rien ni personne a par ma bonne volonte ne m'obligeait a passer trois jours a taper cette documentation alors que j'aurais pu garder tout cela pour moi. Pour toutes les questions et suggestions, vous pouvez me joindre par minitel sur le 3615 RTEL, ainsi que sur les serveurs minitels prives RTC ONE au 48 58 46 17 et TOP JEF, ou j'anime le club assembleur, au 47 95 28 10, dans la boite aux lettres HPReg. Vous pouvez egalement me joindre sous le nom de Regis DUCHESNE sur les BBS de la region parisienne, JOSHUA, ALF, OUT TIME BBS, ou je modere la conference HP48 redistribuee. Enfin, vous pouvez me contacter par EMAIL aux adresses electroniques suivantes: HPREG.ONE@ON101.COM et HPREG@EMAIL.TEASER.COM. Je remercie, car ils nous simplifient la vie sur HP: - Marc Vogel (DEYLONE) pour avoir ecrit PCT avec moi - Atlas pour True Color - Thomas Looten pour Starway (sur G) - Nathalie Brisset (CYMORIL) pour ses splendides GROBs - Phong N'Guyen pour AsmFlash - Frenhofer pour Unass - Jean-Yves Avenard (GHERKIN) pour String-Writer - Cyrille de Brebisson (HP Mad) pour ses splendides jeux _____________