***** INTERRUPTIONS SUR LES MICROPROCESSEURS SATURN ***** et cas particuliers des calculateurs HP28 et HP48 (S et SX seulement, ***** mais les G et GX ne diffèrent que par l'organisation du bus et par les ***** adresses en ROM : les principes sont les mêmes). ***** par MJS 20/06/1994 I. Introduction. La documentation publiée par Regis DUCHESNE (HPReg) le 25/05/1994 me semblant quelque peu succinte, je me permets d'en publier une un peu plus complète. Notons que le seul tort de HPReg est de croire qu'il dispose de TOUTES les connaissances mondiales sur les HP48... Il a cependant le mérite d'avoir publié quelque chose, ce que n'ont jamais fait auparavant les "initiés", qui disposaient des secrets des interruptions dès l'été 1992. Je profite donc de cette introduction pour le remercier de son initiative. Ce texte donne une liste plus complète des interruptions existantes, et de leur exploitation. Passons aux choses sérieuses : que sont les interruptions. Pour ceux qui n'ont pas lu le rapport de HPReg, disons qu'il s'agit de programmes appelés par le processeur lorsque certains composants électroniques en font la demande : sur les HP28/48, ces composants sont les suivants : - le clavier - l'accumulateur de secours - le port infra-rouge - le port série (sauf HP28) - le "timer" - les modules de mémoire vive (RAM) De plus, la touche RESET au dos des HP48 utilise un mécanisme spécial, qui sera décrit plus loin. Comme on le voit, il y a plus de deux types d'interruptions, contrairement à ce qu'affirmait HPReg, et ses deux types n'en font en fait qu'un. J'insiste cependant pour saluer son courage : qu'il ne prenne pas mes remarques aux sérieux ! (On ne doit jamais prendre les remarques de MJS au sérieux. Ni les programmes, d'ailleurs !) II. Structure normale d'une machine basée sur le SATURN de MOTOROLA. Il est important de comprendre la structure de votre machine avant d'essayer de jouer au plus fin avec elle ! En gros, disons que, comme sur tout ordinateur (et pour ceux qui l'ignorent, le processeur SATURN est un processeur RISC de grande classe, ce qui lui permet d'être plus rapide qu'un IBM PC pour pas mal d'applications...), la pièce centrale est le bus. Le bus (ou les bus) est en fait un groupe de fils. Pour le SATURN, il doit y avoir deux bus : un bus à 4 fils (on dit un bus 4 bits) pour les données, et un bus à 20 fils (20 bits) pour les adresses. A quoi ça sert ? Eh bien, tout composant branché sur le bus s'en sert pour communiquer avec les autres. Les composants en question sont le processeur SATURN, les modules de mémoires ROM et RAM, l'écran, et un composant spécial pour gérer le reste du matériel. Supposons que A (le processeur, par exemple) veuille envoyer une donnée à B (disons une RAM). Les opérations sont les suivantes : - A se rend maître du bus en montant la tension d'un fil à +5 V. On dit que ce bit est à 1. Désormais, les autres circuits ne peuvent plus se servir du bus. - A "écrit" l'adresse où il veut écrire sur le bus d'adresse. Cela veut dire que pour chaque chiffre binaire de cette adresse, soit 20 bits, il place le fil correspondant à +5 V si ce bit est à 1, et à 0 V s'il est à 0. - A "écrit" de même la donnée - un quartet - sur le bus de donnée (à 4 bits). - A indique au reste de la machine qu'il va écrire, en changeant l'état d'une ligne spécial (il en existe une autre pour l'écriture, mais rien n'indique si ces lignes sont actives lorsqu'elles portent un 1 ou un 0). - Tous les autres composants sur le bus savent alors que quelqu'un écrit. S'ils sont capables de lire quelque chose, ils décodent alors le bus d'adresse. B détecte alors que c'est lui qui doit gérer cette adresse. Les autres savent aussi qu'ils ne doivent rien faire. - B lit donc le bus de donnée, et le copie à l'adresse visée. - A libère le bus. Ceci vous explique beaucoup de choses : - quand on écrit plusieurs quartet en mémoire, il faut ajouter un cycle d'horloge par quartet transmis : cela est dû au fait que le bus de donnée n'ait que 4 bits, et qu'il faille donc répéter plusieurs fois le manège ci-dessus. - quand on éteint l'écran d'un HP48, il travaille plus vite, car l'afficheur des HP48 "vole" l'usage du bus au processeur pour rafraîchir l'écran, environ 20 fois par seconde. - chaque circuit est responsable du décodage de ses adresses, et il faut donc qu'il sache où il est placé dans l'espace d'adressage, et combien d'adresses il y occupe. Pour les mémoires, ces renseignements sont données par des registres spéciaux. L'instruction UNCNFG donne l'ordre au module dont l'adresse de base courante est sur le bus d'adresse d'effacer ces registres, et d'attendre un ordre de configuration. Celui-ci est donné par CONFIG. Le module en attente remplit alors son registre de configuration avec la valeur sur le bus d'adresse. S'il a plusieurs registres de configuration, et c'est le cas des RAM de 32 Ko et plus, il en remplit un (ici, la taille), et reste en attente. Un deuxième CONFIG lui indique comment remplir le suivant. Vous savez maintenant comment on déplace la RAM des HP48 ! Et si vous avez besoin de la déplacer, c'est tout simplement parce que cette RAM décode son adresse AVANT la ROM dite cachée, qui ne reçoit donc rien, tant qu'on n'a pas changé l'adresse de base de la RAM... Notez d'autre part que UNCNFG ne déconfigure pas qu'un seul module, mais tous les modules à partir de l'adresse de base indiquée. Il faut donc les reconfigurer tous un par un. Le HP48 le fait, mais pas les programmes des voyous comme vous et moi. Le résultat est que, si vous utilisez une adresse sur un port pendant que la RAM est déplacée, c'est le drame. Mais avouons que ce cas est rare... - quand on tente un UNCNFG depuis le module à déconfigurer, la machine se plante lamentablement : le processeur ne peut plus y lire le programme à exécuter. La solution classique est d'utiliser (sur HP48) la routine en 026BF, qui fait ça pour vous ! - le circuit qui calcule les CRC le fait en fait en consultant le bus de donnée à chaque fois que le bus est activé en lecture. III. Cas du RESET. Quand on appuie sur la touche RESET au dos des HP48, on active la broche RESET du SATURN, ce qui provoque la réinitialisation du processeur, puis l'appel de la routine en 00000. La routine effectivement appelée est donc celle qui se trouve au début du module configuré en 00000 (et pas toujours la ROM qui devrait s'y trouver, si vous voyez ce que je veux dire...). C'est pourquoi chaque module (les ROM font en général 64 Ko, soit 20000 quartets) contient au début un GOSBVL sur l'adresse d'initialisation (la même que celle atteinte après ON-UP sur HP28, ou ON-C sur HP48). Pourquoi un GOSBVL et non un GOVLNG ? Tout simplement parce que cela empile un 00000 dans la RSTK, et, en cas de RTN non identifié, la machine revient en 00000, fait un Machine Reset, et évite au programmeur un Memory Clear (ou Lost, sur HP28), ce qui est bien agréable. Merci HP ! Ce paragraphe n'avait rien à voir avec les interruptions, mais certains sont persuadés que le RESET est relié à une interruption... IV. Les interruptions. Lorsqu'un composant électronique est témoin d'un événement historique, au sens de la machine, elle signale au processeur qu'elle a un scoop par une ligne spéciale. Le processeur sauve alors le bit qui distingue les modes INTOFF et INTON (interruptions interdites ou autorisées), interdit les interruptions, sauve l'adresse en cours (la valeur de PC) dans la RSTK, et saute en 0000F. Il faut donc qu'il y ait là une routine se terminant par RTI, pour remettre en état le système d'interruptions (selon le mode en vigueur avant cette interruption), et retourner dans le programme initial. HPReg a très bien décrit la routine des HP48. Je vous y renvoie si cela vous intéresse. Sur HP28, c'est pratiquement la même chose, avec une astuce : la machine dispose d'une mémoire spéciale en FFE28 pour sauver les registres, comme en 7045D sur S(X) et 805DC sur G(X). L'intérêt de cela est que, si la RAM a un problème, on peut encore s'y retrouver... A noter l'équivalent du ON-SPC des HP48 est ON-ENTER-BACKSPACE sur HP28. C'est un mode de sommeil, moins violent que Memory Clear, mais plus énergique que ON-C ou ON-UP. En plus, il provoque moins de Memory Clear que ON-C. La dernière section de cette documentation est destinée à HPReg uniquement. Je lui demande de bien vouloir si reporter. IV.1) Interruption clavier. Son rôle est évident : elle est déclenchée par l'appui sur une touche. Le gestionnaire doit lire la touche, et gérer une éventuelle répétition automatique. La touche ON est gérée comme les autres, mais elle est alimentée par une ligne spéciale de OUT, ce qui la rend plus difficile à masquer. IV.2) Interruption de l'accumulateur de secours. Elle signale que les piles sont faibles. IV.3) Interruption IR. Elle signale la réception d'un caractère sur le port Infra-Rouge. Il faut le lire et le garder quelque part. IV.4) Interruption série. Elle signale l'arrivée d'un caractère sur le port série. Cette fois, c'est toute le "packet" qu'il faut lire... IV.5) Les "timers". Il n'y a qu'une seule horloge sur les HP28/48... Mais elle est reliée à deux compteurs, dont la mise à zéro provoque une interruption. Sur HP28, les compteurs sont en FFFF8 et FFBF8, et leur port de contrôle en FFx0F (si le bit 3 est à 1, la valeur est invalide, et on démarre le compteur en copiant le bit 1 dans le bit 2...). IV.6) Interruption RAM. Elle survient lors de la défaillance d'une RAM. C'est rare, mais fort gênant... V. Détournement des interruptions. Sur HP48, on ne peut que se prosterner devant le travail de HPReg et de ses collègues... Un extrait de la documentation de HPReg, ainsi que ses remerciements (politesse oblige), figure à la fin de cette documentation : je ne vais quand même pas m'attribuer une méthode qui n'est pas de moi ! Je me suis cependant permis de corriger deux petites fautes de frappes : un "debut" était devenu "denut", et un "adresse" "addresse". Commentaire personnel sur la méthode avec une carte : créez un BACKUP a nom vide, comme indiqué, mais donnez lui une taille suffisante pour lui et votre nouveau gestionnaire d'interruptions, ET les parties de la ROM que vous conserverez. La routine-gestionnaire laissera des blancs aux adresses voulues, mais tout son code tiendra dans les 2 Ko déplacés. Bilan, ces 2 Ko ne sont pas perdus, et vous évitez d'avoir à placer la "routine auxiliaire" à une adresse fixe : elle est dans le backup. (Tant pis pour ceux qui n'ont pas suivi...). Sur HP28, j'ai mentioné l'existence d'une zone (FFE28 à FFE56) pour les sauvegardes du gestionnaire d'interruptions. Utilisez-là pour votre propre gestionnaire, bien-sûr, mais aussi pour "l'échangeur" : quelque chose comme UNCNFG / LC F / CONFIG / LC 0 / CONFIG / PC=A pourrait être utile. (Ex: A=adr de *SUITE, LC C0000, P=4, GOVLNG FFE28, *SUITE, P=0). On peut aussi utiliser l'écran, ou même la zone des indicateurs LCD (je vois même des yeux de possesseurs de HP48 qui pétillent...). VI. Conclusion. Cette documentation n'aura pas appris grand' chose aux électroniciens nés. Son but est surtout de dissiperr des idées fausses sur les interruptions, et de leur enlever une part de leur mystère. Mais la programmation par interruptions reste un art difficile à maîtriser. Lancez-vous si vous le voulez, mais seulement si vous le voulez vraiment. C'est pour cette raison que je n'ai pas fourni de programme entier sur HP28, et que toute mes indications de programmation sont restées vagues. Ceux qui ont tout compris en savent suffisamment pour programmer efficacement, pour les autres, méditer ou s'abstenir : vous éviterez des dégâts matériels. *************** SECTION EXTRAITE DE LA DOCUMENTATION DE HPREG ************** *************** sans l'aimable autorisation de l'auteur ******************** *************** mais on s'en fout ! **************************************** 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 debut 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 adresse: 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 le serveur minitel prive RTC ONE au 48 58 46 17 et au 48 70 10 29, ou par modem au 49 88 76 91, dans ma boite aux lettres HPReg. Vous pouvez egalement me joindre sous le nom de Regis DUCHESNE sur les BBS de la RP: THE 0NE, 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 _____________ *************** SECTION SPECIALE : CORRESPONDANCE PRIVEE POUR HPREG ******** Salut bonhomme ! Et encore bravo pour ta petite bafouille sur les interruptions. Pardonne moi si j'ai eu tendance à être un peu ironique dans les sections précédentes... Je vais ici tenter de me racheter, avec quelques informations qui t'ont manquées. * Sur la routine d'interruption - le flag 13 est un flag anti-récursif, qui évite qu'un test raté (exemple : le test mémoire) ne provoque une interruption non masquable, qui tenterait un test, qui reprovoquerait la même interruption non masquable, etc... - la "bizarre lecture avant écriture" en 0010E n'est pas du tout bizarre : ce quartet s'efface après sa lecture (l'état READ du bus à cette adresse agit comme une commande pour le contrôleur de périphériques). Ce genre de pratiques est très courant en électronique : plutôt que d'analyser la valeur 0, 1, ou 2 d'un octet de commande, on préfère mettre 3 adresses fictives, sans rien derrière, et c'est l'adresse (ou plutôt ses derniers bits) qui donnent la commande, la commande étant déclenchée soit par l'état WRITE, soit par l'état READ. Ici, le but de la manoeuvre est de s'assurer que le quartet de configuration indique bien l'état le plus récent, et ne contienne pas de traces du dernier RESET : il s'efface automatiquement. Mais le programmeur a préféré garder sa valeur, au cas où il en aurait besoin plus loin. - astuce personnelle sur le flag CARRY (peut-être déjà très répandue, et donc plus du tout personnelle) : les programmeurs de la HP28 ont utilisé une astuce géniale : un test avec une valeur de saut 02. C'est que CARRY n'est pas utilisé ici comme indicateur de retenue (ce qui aurait permis de détecter l'infériorité stricte), mais POUR TOUS LES TESTS, CARRY indique que le saut a lieu. On peut donc différer l'action, si on ne modifie pas CARRY entre temps. - la valeur du CMOS Word ne contient pas toutes les combinaisons possibles de bits sur un quartet : il y aurait 16 valeurs ! Ces valeurs sont tout à fait pifométriques, et on espère qu'elle donne la plus grande fiabilité. Les disquettes des PC sont souvent formatées à coups de A5 pour la même raison. En fait, il n'y a aucune raison scientifiquement valable de faire ça... - la table des cycles d'horloges du "Voyage" est fausse : elle ne tient pas compte des cycles de bus pour l'écran et autres vacheries. Le seul moyen est de chronométrer chaque instruction (un million de fois, dans une boucle), à l'aide du timer, de répéter l'opération en notant les temps, d'effectuer une moyenne, d'en soustraire le temps mis par la boucle à vide, et de diviser par un million. On obtient le temps réellement mis par la machine pour effectuer cette instruction, et ce temps dépend de la configuration (HP28, HP48 S, SX avec carte, G...). Compter une après-midi pour tester une machine. A noter que si on fait un INTOFF/INTON pour ne pas tenir compte des interruptions clavier et autres, il ne faut pas compter tester SHUTDN... Par ailleurs, j'émets un doute sur le cycle supplémentaire pour DAT0=C W par rapport à DAT0=C 16. Affaire à suivre. - la touche ON est interruptible : INTOFF C=0 A OUT=C SHUTDN et bon courage... - très brillante compréhension des causes profondes qui ont interdit l'usage de OUT=C, sous prétexte que cela corrompait la mémoire : bravo ! - la "ruse" sur SB était déjà connue, cf description des mnémoniques du SATURN dans "Voyage au coeur du HP28". - la RSTK fonctionne comme la pile RPL : par index décroissant à l'empilage. Il est d'usage de donner la valeur la plus récente en bas de la liste. Sinon, très bonne compréhension de l'effet de bord, dont il faut tenir compte à chaque appel d'une routine en ROM (elles utilisent jusqu'à 4 niveaux de la RSTK, plus 1 pour l'appel...). - bravo ! (J'ai déjà lu ça quelque part...) La technique de l'incrémentation de P au lieu de la décrémentation est géniale ! Personne n'y avait pensé chez HP, et c'est la première fois que je vois ça ! Moi qui me prenait pour un as de l'optimisation... - pour supprimer le ON-A-F, il suffit d'utiliser un flag anti-récursion, qui permette de faire un RTI immédiatement, si c'est le gestionnaire qui est interrompu. On risque alors de perdre quelques caractères du clavier ou du port IR. Le multi-tâche, c'est effetivement beau, mais ça demande beaucoup d'expérience pour synchroniser correctement les processus... - va quand même lire ma section V. sur le HP28... - continue comme ça, coco, c'est PARFAIT ! MJS, Paris, le 20/6/1994 enfin... 21/6/1994, maintenant !