— Basé sur les articles publiés dans Quasar CPC numéro 20 et numéro 21, Assembleur : Hardware, par OffseT.
Voici un petit dossier expliquant de façon détaillée comment fonctionne la fameuse Multiface Two de chez Romantic Robot, comment elle se programme et enfin quels outils existent pour celle-ci.
Je pense que la plupart d'entre vous connaissent déjà le principe de fonctionnement de ce petit bijou, mais une bonne mise au point n'est jamais superflue. Tout d'abord, vous devez savoir que la Multiface Two n'est pas la première interface de ce type produite sur CPC ; avant, il y a par exemple eu le non moins célèbre Mirage Imager. Néanmoins, la Multiface Two, sortie en 1988, demeure sans nul doute la plus aboutie. Le petit blond à lunettes trépigne d'impatience… mais que fait donc cette interface ?
C'est en quelque sorte un observateur du fonctionnement du CPC. Elle mémorise en permanence tous les accès aux principaux circuits du CPC (PPI, CRTC, Gate Array) afin, lorsque vous l'activez, d'en permettre l'édition… mais aussi et surtout de retourner au programme interrompu dans l'état où vous l'aviez laissé. En effet, ceci est en temps normal tout bonnement impossible. Puisque vous changez l'état du système lorsque vous l'interrompez et que la plupart des registres des circuits du CPC ne sont pas accessibles en lecture, vous n'avez absolument aucun moyen de pouvoir en restituer les valeurs initiales.
Techniquement, la Multiface Two est composée de trois organes. Tout d'abord, il y a les circuits logiques qui servent essentiellement à l'observation du CPC. Ces circuits (trois PAL) stockent le résultat de leurs observations (valeurs en cours des registres du PPI, du CRTC, du Gate Array) dans une RAM de 8ko, c'est notre deuxième organe. Enfin, il y a une ROM de 8ko qui contient le programme de gestion de la Multiface Two ; celle-ci, ainsi qu'une grande partie de la RAM, n'est donc utilisée (exécutée) que lorsque vous activez l'interface via son bouton STOP. L'autre bouton, le RESET, ne fait rien d'autre qu'un reset hard complet du CPC (mise à la masse du signal /BUS_RESET
du port expansion).
La présence d'une RAM de taille conséquence ouvre, du fait du judicieux code implémenté dans la ROM, de nombreuses possibilités. Mais, nous allons tout d'abord nous attacher aux fonctionnalités offertes par le programme en ROM lui-même.
Lorsque vous appuyez sur le bouton STOP, la Multiface Two fait plusieurs choses. En premier lieu, elle génère une NMI (interruption non masquable). Sitôt l'interruption prise en compte par le Z80 et avant qu'elle ne soit traités (RST &66
), elle commute alors sa ROM entre &0000
et &1FFF
et sa RAM entre &2000
et &3FFF
. Dès lors, le Z80 part sur le code en &66
: c'est celui de la ROM de l'interface ; le tour est joué.
Une fois la ROM en route, elle commence par sauver dans la RAM de l'interface tout ce qui ne l'a pas déjà été automatiquement via la logique hardware (les trois PAL). Les registre du Z80, les registres du AY, l'interruption Gate Array en cours, etc.. Oui, oui, oui, le petit blond à lunettes a raison, c'est en fait plus compliqué que ça, mais je schématise car notre objectif est avant tout l'utilisation de la Multiface Two et non son fonctionnement intime. Bref, après avoir sauvé tout ce qui pouvait l'être, la Multiface Two consulte quelques octets dans sa RAM afin de savoir si un programme utilisateur y est installé en mode “Direct Jump”, si tel est le cas, elle lui donne aussitôt la main et n'aura plus qu'à s'occuper de la restitution des paramètres système et hardware pour gérer le retour au programme stoppé lorsque le programme utilisateur aura rendu la main. En d'autres termes, les autres parties du code de la ROM ne sont pas exécutées. Le plus connu et le plus performant de ces programmes est The Insider, développé par Romantic Robot. Nous reviendrons plus tard sur le comment de l'installation de tels programmes dans la mémoire de la Multiface Two. En outre, dans le cas où le programme utilisateur stocké en RAM serait défaillant ou que l'on voudrait tout simplement l'annuler sans avoir à le déprogrammer à la main ou à mettre le CPC hors tension, si une touche du clavier est enfoncée lorsque vous activez la Multiface Two, celle-ci l'ignorera et exécutera son code en ROM normalement.
D'ailleurs, qu'avons-nous dans ce code en ROM par défaut ? Eh bien, nous avons un menu avec diverses options pour éditer ou sauvegarder sur disque la RAM ainsi que les divers paramètres en cours dans le programme interrompu (valeurs des registres du CRTC, couleurs, etc.). Nous avons également un curieux menu “Jump”… celui-ci nous permet d'exécuter un programme installé dans la RAM de la Multiface Two en mode “Indirect Jump”. L'avantage d'installer un programme de cette manière est qu'il vient s'ajouter aux fonctionnalités de l'outil présent en ROM. Mais, en contre partie, du fait que le code en ROM utilise une bonne partie des 8ko de RAM, il ne reste que peu de place contrairement à une installation en “Direct Jump”.
Maintenant que nous avons vu les grandes lignes du fonctionnement matériel et logiciel de la Multiface Two, nous allons nous intéresser à sa RAM qui est la clef de sa programmation. La mémoire intégrée à l'interface est accessible de deux manières différentes. Via l'outils de la ROM après que la Multiface Two ait été activée, la mémoire vue entre &2000
et &3FFF
est soit celle de la MF2 (Multiface Two), soit celle du CPC selon la configuration choisie (touche M sur les claviers azerty). Il faut bien l'admettre, si nous n'avions pas d'autre moyen d'accès à cette RAM, cela limiterait sérieusement les possibilités de programmation de l'interface. Mais, comme l'a judicieusement constaté le petit blond à lunettes, tous les programmes s'installant dans la MF2 ne demandent qu'une seule chose ; avoir fait un reset hard juste avant l'installation.
En effet, la MF2 possède deux ports (&FEE8
et &FEEA
) qui permettent d'activer et de désactiver sa page ROM+RAM entre &0000
et &3FFF
. Toutefois, cette porte ouverte vers la MF2 depuis le CPC posait problème car elle permettait simplement à tout programme de détecter la présence d'une MF2 et par exemple, dans le cas de la plupart des jeux, de refuser alors de fonctionner ! Mais chez Romantic Robot, ils ont trouvé une parade : ces ports deviennent inertes suite à une activation de la MF2 et ne redeviennent actifs que consécutivement à un reset hard. En d'autres termes, lorsque vous allumez votre CPC, la MF2 est visible ; si vous l'activez ne serait-ce qu'une seule fois, elle devient invisible ! Pour la rendre à nouveau visible, il n'y a qu'une seule alternative ; un reset hard (via le bouton RESET de la MF2 elle-même par exemple).
Il est par ailleurs important de noter que lorsque vous souhaitez commuter la page ROM+RAM de la MF2, vous devez le faire alors que le firmware est complètement désactivé. En effet, la page entre &0000
et &3FFF
est utilisée pratiquement en permanence par l'OS du CPC ! Y commuter la page de la MF2 sans aucune précaution provoquera irrémédiablement un plantage… heureusement un simple DI
bien placé suffit à inhiber tout activité de l'OS.
Voici donc à quoi pourrait ressembler le code de commutation :
Org &8000 ; Une adresse sans conflit Multiface di ; On "désactive" le firmware ld bc,&7f8a out (c),c ; On active la ROM basse ld bc,&fee8 ; On commute la page ROM/RAM out (c),c ; de la Multiface Two ... ; Code de configuration de la ... ; RAM qui ne doit utiliser ... ; aucun appel système ld bc,&feea ; On désactive la page ROM/RAM out (c),c ; de la Multiface Two ei ; On "restaure" le firmware ret ; C'est prêt !
Ah, le petit blond à lunettes est visiblement très pertubé par l'activation de la ROM basse (via le mode RMR du Gate Array) qui précède le OUT
de commutation de la page ROM/RAM de la MF2. Il ne faut pas perdre de vue que la ROM de la MF2 est une ROM basse et que celles-ci doivent donc être activées pour que la page de la MF2 puisse être accessible. À noter toutefois que sur CPC+, pour changer, il y a une anomalie qui fait que la MF2 parvient à commuter sa page ROM/RAM même si les ROM basses sont désactivées !
Maintenant que nous savons comment accéder à cette mémoire de la MF2 via son mapping entre &2000
et &3FFF
, nous allons voir ce qu'elle a dans le ventre. Dès lors, nous serons en mesure de personnaliser notre MF2 en mode “Direct Jump” ou “Indirect Jump” selon le cas.
Comme je vous l'ai déjà dit au tout début, certaines parties de la RAM sont modifiées en temps réel par le hardware de la MF2. D'autres sont initialisées par le programme en ROM, pour certaines avant le “Direct Jump” (on en hérite donc dans un programme utilisateur installé en lancement direct), pour d'autres via l'outil intégré à la ROM (on a donc à nous en soucier uniquement pour les programmes utilisateurs lancée grâce au menu “Jump”). De plus, certaines données stockées en RAM sont utilisées pour mémoriser quel était l'état du système lorsque le programme tournant sur le CPC a été interrompu par la MF2. Si vous les modifiez, cela changera en conséquence l'état du retour au programme initial. C'est d'ailleurs déjà le cas avec l'outil de la ROM. Concernant la partie de la mémoire modifiée matériellement, vous ne pourrez simplement pas l'utiliser vu que même lorsque votre programme utilisateur tournera dans la MF2, elle continuera d'être modifiée au gré de vos OUT
; tout au plus vous pourrez la consulter en lecture.
Ci-après, vous allez trouver un tableau détaillant de manière assez précise le contenu de la RAM de la MF2. Ceci étant, dans un soucis de simplicité, je ne vous ai mis le plan de la mémoire libre seulement dans le cas du “Direct Jump”. En effet, le mode “Indirect Jump” est beaucoup moins utile ; de plus, une grande partie de la mémoire de la MF2 étant alors occupée pour le fonctionnement de l'outil intégré à la ROM, ce mode sera plus volontier utilisé pour configurer des sauts directement dans la mémoire du vive ou morte du CPC. Seules les informations sur le système disponibles dans la RAM de la MF2 seront alors éventuellement utilisées (la page mémoire de la MF2 reste en effet commutée par défaut entre &2000
et &3FFF
dans le cas de la programmation d'un “Jump”, qu'il soit direct ou indirect).
Avant de me lancer dans des explications plus détaillées de ce tableau, en voici la légende :
Adresse : l'adresse de départ de la zone, Taille : taille de la zone en octets, Type : type de mise à jour de la mémoire :
Description : brève description de la zone mémoire.
Concernant les zones modifiées par le hardware et le programme de la ROM de la MF2, je n'ai mis de description précise que dans le cas de données réellement utiles ; le reste du temps, vous trouverez juste le commentaire “Réservé”. Dans le cas où vous seriez intéressé, j'ai établi la description de la plupart de ces zones et je les tiens à votre disposition, toutefois, ça ne me paraît vraiment pas utile. À titre d'exemple, en &3F00
vous trouverez une zone “HARD” “réservée” de 16 octets de long. Il s'agit en fait d'une zone parasitée par le hardware de la MF2 lorsqu'il décrypte le numéro du stylo en cours de sélection sur le Gate Array. Vous y trouverez à l'adresse &3F0x
le numéro du pen x s'il a déjà été utilisée ou est en cours d'utilisation… mais vous y trouverez également le numéro du border s'il est programmé via 16+x dans le mode PENR du Gate Array ! En fait, tout ceci pour vous dire que les zones non décrites ici sont de toute manière sans grand intérêt. De plus, étant donné qu'il existe plusieurs révisions de la MF2 (au moins trois à ma connaissance), il est fort possible que ces zones mémoire diffèrent d'une version à l'autre… mieux vaut donc les ignorer.
Le tableau qui suit a été élaboré par mes soins sur le modèle de MF2 répondant “0E” lors de l'appui sur la touche f0 dans le menu de l'outil intégré en ROM. Toutes les zones que j'y ai documentées semblent compatibles avec le modèle répondant “8B” (modèle ©1990). Je les ai de plus confrontées avec divers documents disponibles sur web qui, bien que contenant quelques boulettes, correspondent assez.
Entre &2000
et &2007
, se trouvent les zones permettant de configurer le mode de fonctionnement souhaité de la MF2. En &2000
vous devez placer l'adresse du code utilisateur (au format little endian Z80 habituel) et en &2002
et &2003
les deux valeurs à envoyer au Gate Array pour fixer les modes de commutation ROM (RMR) et RAM (MMR).
Enfin, en &2005
, vous avez la zone permettant de choisir le type de “Jump” souhaité. Si vous y placez le mot-clé “RUN” (&52
,&55
,&4E
) alors ce sera du “Direct”, sinon ce sera un “Indirect”. Bien sûr, neuf fois sur dix, on y pokera ce mot-clé puisque le “Direct Jump” est bien plus intéressant. En outre, lorsque vous désactivez le “Direct Jump” (en gardant une touche appuyée lorsque vous enfoncez le bouton STOP de la MF2), le code en ROM de la MF2 efface simplement le premier octet de cette zone (le “R”).
À partir de &2008
et sur 6135 octets de long, se trouve la principale (et légale) zone à utiliser pour placer votre programme dans la MF2. Dans tous les cas, je vous conseille d'utiliser uniquement cette zone car rien ne garantit la validité des autres zones “FREE” sur toutes les révisions de MF2.
En &37FF
, nous avons un octet “poké” par le hard. Il s'agit de la dernière valeur chargée sur le port de contrôle du PPI (port &F7xx
). C'est cet octet qui est recopié en &3AA8 par la ROM avant toute modification pour pouvoir le rendre en l'état lors du retour au programme stoppé.
Nous avons ensuite une alternance de zones libres et de zones utilisées par la ROM jusqu'en &3A6E
où se trouve le numéro du registre CRTC qui était sélectionné lorsque le programme a été coupé par la MF2. La ROM met à jour cette valeur avant toute modification personnelle de l'état du CRTC à partir de l'adresse &3CFF
qui contient la valeur du dernier octet envoyer sur le port &BCxx
(sélection de registre CRTC).
Je ne vais pas tout détailler, c'est toujours sur le même principe ; nous avons les valeurs des principaux ports et des principaux registres des circuits du CPC “pokées” par le hard en temps réel et ces valeurs copiées par la ROM à une autre adresse (lors de l'activation de la MF2) qui représentent donc l'état du CPC à ce moment là. La ROM utilise ensuite les valeurs qu'elle avait mises en place pour reconfigurer le CPC avant de faire repartir le programme interrompu. Dès lors, si vous modifiez ces valeurs (type “ROM*”), ça influera directement sur le fonctionnement du programmation que vous aviez stoppé.
Il y a néanmoins quelques octets positionnés par la ROM qui ont un statut à part. En &3A96
, nous avons un octet nous indiquant si nous avons plus de 64ko sur le CPC. Ceci est déterminé par un test de la mémoire dans le code de la ROM.
Ensuite, en &3EE6
, nous avons la valeur de tous les registres et flags du Z80 lorsque le programme a été stoppé.
&3EF8 : IF |
|||
&3EE6 : IY | &3EEC : DE' | &3EF2 : BC | &3EFA : HL |
&3EE8 : IX | &3EEE : HL' | &3EF4 : DE | &3EFC : AF |
&3EEA : BC' | &3EF0 : AF' | &3EF6 : RF | &3FFE : SP |
Rien de particulier à noter sinon que la valeur de PC au moment de l'arrêt du programme est en (SP). Ah si, RF et IF auront sans doute éveillé votre curiosité… Il s'agit des valeurs de R et I dans le programme stoppé, couplées avec l'état des flags au moment de leur chargement dans A (LD A,R
et LD A,I
). Nous reviendrons plus tard sur ce point.
Adresse | Taille | Type | Description |
---|---|---|---|
&2000 | 2 | PRG | Adresse du programme utilisateur à exécuter via la Multiface Two. |
&2002 | 1 | PRG | Configuration ROM du Gate Array (mode RMR du port &7Fxx ) à adopter. |
&2003 | 1 | PRG | Configuration RAM du Gate Array (mode MMR du port &7Fxx ) à adopter. |
&2004 | 1 | ROM | Réservé. |
&2005 | 3 | PRG | Mode de lancement du programme utilisateur (Direct ou Indirect Jump). |
&2008 | 6135 | FREE | Zone mémoire libre à utiliser pour un programme utilisateur en “Direct Jump”. |
&37FF | 1 | HARD | Valeur mise à jour en temps réel du port de contrôle du PPI (port &F7xx ). |
&3800 | 523 | FREE | Non utilisé en mode “Direct Jump”. |
&3A0B | 3 | ROM | Réservé. |
&3A0E | 1 | FREE | Non utilisé en mode “Direct Jump”. |
&3A0F | 5 | ROM | Réservé. |
&3A14 | 1 | FREE | Non utilisé en mode “Direct Jump”. |
&3A15 | 20 | ROM | Réservé. |
&3A29 | 33 | FREE | Non utilisé en mode “Direct Jump”. |
&3A4A | 5 | ROM | Réservé. |
&3A4F | 1 | FREE | Non utilisé en mode “Direct Jump”. |
&3A50 | 1 | ROM | Réservé. |
&3A51 | 23 | FREE | Non utilisé en mode “Direct Jump”. |
&3A68 | 2 | ROM | Réservé. |
&3A6A | 2 | FREE | Non utilisé en mode “Direct Jump”. |
&3A6C | 2 | ROM | Réservé. |
&3A6E | 1 | ROM* | Registre CRTC sélectionné (port &BCxx ) lorsque le programme a été interrompu. |
&3A6F | 1 | ROM* | Couleur du border active lorsque le programme a été interrompu. |
&3A70 | 16 | ROM* | Palette de couleurs active lorsque le programme a été interrompu. |
&3A80 | 16 | ROM* | Valeurs des 16 registres du CRTC lorsque le programme a été interrompu. |
&3A90 | 2 | ROM* | Adresse de l'écran système lorsque le programme a été interrompu. |
&3A92 | 1 | ROM* | Configuration ROM du Gate Array (mode RMR) lorsque le programme a été interrompu. |
&3A93 | 1 | ROM* | Configuration RAM du Gate Array (mode MMR) lorsque le programme a été interrompu. |
&3A94 | 1 | ROM* | Mode d'interruption (IM 0, 1 ou 2) lorsque le programme a été interrompu. |
&3A95 | 1 | ROM | État des interruptions (1=EI, 0=DI) lorsque le programme a été interrompu. |
&3A96 | 1 | ROM | Indicateur de taille de la RAM du CPC (0 si 64ko, 2 si plus). |
&3A97 | 1 | ROM* | Couleur sélectionnée (port &7Fxx , mode PENR) lorsque le programme a été interrompu. |
&3A98 | 16 | ROM* | Valeur des 16 registres du PSG lorsque le programme a été interrompu. |
&3AA8 | 1 | ROM* | Valeur du port de contrôle du PPI (port &F7xx ) lorsque le programme a été interrompu. |
&3AA9 | 7 | FREE | Non utilisé en mode “Direct Jump”. |
&3AB0 | 2 | ROM | Réservé. |
&3AB2 | 14 | FREE | Non utilisé en mode “Direct Jump”. |
&3AC0 | 2 | ROM | Réservé. |
&3AC2 | 1 | ROM* | Nombre d'interruptions jusqu'à la prochaine VBL lorsque le programme a été interrompu. |
&3AC3 | 1 | ROM | Réservé. |
&3AC4 | 571 | FREE | Non utilisé en mode “Direct Jump”. |
&3CFF | 1 | HARD | Numéro du registre CRTC en cours de sélection (port &BCxx ) en temps réel. |
&3D00 | 176 | FREE | Non utilisé en mode “Direct Jump”. |
&3DB0 | 16 | HARD | Valeur des 16 registres du CRTC en temps réel. |
&3DC0 | 286 | FREE | Non utilisé en mode “Direct Jump”. |
&3EDE | 8 | ROM | Réservé. |
&3EE6 | 26 | ROM* | État du Z80 (registres et flags) lorsque le programme a été interrompu. |
&3F00 | 16 | HARD | Réservé. |
&3F10 | 16 | HARD | Palette de couleurs mise à jour en temps réel (zone miroir de &3F90 ). |
&3F20 | 16 | FREE | Non utilisé en mode “Direct Jump”. |
&3F30 | 1 | ROM | Réservé. |
&3F31 | 95 | FREE | Non utilisé en mode “Direct Jump”. |
&3F90 | 16 | HARD | Palette de couleurs mise à jour en temps réel (zone légale). |
&3FA0 | 1 | FREE | Non utilisé en mode “Direct Jump”. |
&3FA1 | 2 | HARD | Réservé. |
&3FA3 | 2 | FREE | Non utilisé en mode “Direct Jump”. |
&3FA5 | 2 | HARD | Réservé. |
&3FA7 | 2 | FREE | Non utilisé en mode “Direct Jump”. |
&3FA9 | 2 | HARD | Réservé. |
&3FAB | 2 | FREE | Non utilisé en mode “Direct Jump”. |
&3FAD | 2 | HARD | Réservé. |
&3FAF | 32 | FREE | Non utilisé en mode “Direct Jump”. |
&3FCF | 1 | HARD | Stylo sélectionné en temps réel (port &7Fxx , mode PENR). |
&3FD0 | 5 | ROM | Réservé. |
&3FD5 | 1 | HARD | Réservé. |
&3FD6 | 9 | ROM | Réservé. |
&3FDF | 1 | HARD | Couleur du border en cours en temps réel. |
&3FE0 | 15 | FREE | Non utilisé en mode “Direct Jump”. |
&3FEF | 1 | HARD | Configuration ROM du Gate Array en temps réel (mode RMR). |
&3FF0 | 1 | HARD | Réservé. |
&3FF1 | 3 | FREE | Non utilisé en mode “Direct Jump”. |
&3FF4 | 1 | HARD | Réservé. |
&3FF5 | 10 | FREE | Non utilisé en mode “Direct Jump”. |
&3FFF | 1 | HARD | Configuration RAM du Gate Array en temps réel (mode MMR). |
Voilà, nous avons fait le tour des fonctionnalités de la MF2 et v… “à quelques détails près !” vient de me lancer le petit blond à lunettes avec arrogance. Effectivement, pour une fois il n'a pas tort (mais il se prend tout de même une gifle pour le ton sur lequel il m'a parlé) ; nous allons donc de ce pas revenir sur la gestions des flags…
Oui, rappelez-vous, nous avons vu plus haut que la MF2 sauve l'état de AF après avoir récupéré les valeurs des registres I et R… une petite révision sur le fonctionnement du Z80 s'impose. Commençons par le plus simple. En premier lieu, le programme de la MF2 sauve le contenu du registre I dans A, puis stocke AF dans sa RAM dans l'espace des registres Z80. F se retrouve en &3FF8
(nommé Mi dans le menu de la MF2) et A se retrouve en &3FF9
(nommé I - logique - dans le menu de la MF2). Il est important de conserver ainsi le contenu du registre I pour pouvoir le restaurer lorsque la MF2 rendra la main au programme car il est utile en mode vectorisé (IM 2) pour connaître l'octet de poids fort de l'adresse de la table des vecteurs d'interruption (pour plus de détail voir l'article sur les interruptions). Mais, en même temps, notre chère MF2 sauve le contenu de F… “c'est naze, ça ne sert à rien, encore un programme pas optimisé !” s'indigne le petit blond à lunettes. Mais non ! Pas du tout ! La valeur de F après un LD A,I
(ou LD A,R
) est primordiale ! Elle nous permet de connaître l'état du flag IFF2.
Mais qu'est-ce donc que ce flag IFF2 et à quoi sert-il ? En fait, le Z80 dispose de deux flags pour la gestion des interruptions: IFF1 et IFF2. Ces deux flags sont positionnés simultanément à 1 lors d'un EI, et à 0 lors d'un DI. Lorsque IFF1 est à 1, alors le Z80 accepte les interruptions ; lorsqu'il est à 0, il les ignore… voilà qui semble bien obscur ! En fait, pas tant que ça, car lorsqu'une NMI a lieu (ce qui est le cas lorsque la MF2 s'active), IFF1 est mis à 0 de sorte que les interruptions soient désactivées mais IFF2 est laissé inchangé ! Ce qui signifie qu'il vaut 1 si on était en EI
lors de la NMI, et 0 si on était en DI
… d'où l'intérêt de connaître son état pour la MF2 afin de savoir, au retour, si on devra réactiver les interruptions ou pas. Mais où diable se cache ce flag ? Eh bien il est positionné dans F, à la place du flag P/V (bit 2) suite à une instruction LD A,I
ou LD A,R
! Comme vu plus haut, la MF2 récupère son état à partir du LD A,I
; le LD A,R
ne servant qu'à récupérer l'état de R dans ce cas là.
Venons-en maintenant aux défauts de conception de cette excellente interface. Eh oui, tout n'est pas parfait dans la MF2 ! Le plus gros soucis vient du fait que contrairement au CPC, la MF2 décode les ports I/O des divers circuits avec rigueur. Je m'explique, sur CPC, chaque circuit est accessible via un port : &7Fxx
pour le Gate Array, &BCxx-&BFxx pour le CRTC, &F4xx
à &F7xx
pour le PPI, etc.. Mais il se trouve qu'en pratique, le décodage de ces ports n'est que partiel ; dans un soucis d'économie de portes logiques, seuls quelques bits finement choisis sont utilisés pour décoder et ainsi sélectionner le bon circuit en fonction du port. Vous trouverez ci-dessous un petit tableau illustrant ce décodage. “x” y désigne un bit non décodé, “0” un bit testé à 0, “1” un bit testé à 1 et “r” un bit permettant la sélection d'un registre sur le périphérique (les ports CRTC ou PPI par exemple).
Nom des périphériques | bits décodés par le CPC | |||||||
---|---|---|---|---|---|---|---|---|
(adresse des ports logiques) | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
Gate Array - PENR, INKR (&7Fxx ) | 0 | 1 | x | x | x | x | x | x |
Gate Array - RMR, MMR (&7Fxx ) | 0 | x | x | x | x | x | x | x |
CRTC (&BCxx-&BFxx ) | x | 0 | x | x | x | x | r | r |
Sélection ROM Haute (&DFxx ) | x | x | 0 | x | x | x | x | x |
Imprimante (&EFxx ) | x | x | x | 0 | x | x | x | x |
PPI (&F4xx-&F7xx ) | x | x | x | x | 0 | x | r | r |
Périphériques d'expansion | x | x | x | x | x | 0 | r | r |
On voit dès lors que le CRTC est aussi bien accessible via &BCxx
(%10111100
) que &ACxx
(%10101100
) pour la sélection du registre rr=00… et c'est là que réside le problème de notre MF2 qui, forte de sa rigueur de décodage, n'interceptera que les accès sur le port &BCxx
. Bien sûr, accéder au CRTC par le port &ACxx
n'est vraiment pas très propre puisqu'on accède simultanément au port imprimante, mais le fait est que ça fonctionne. De plus, cela est sans grande conséquence même si une imprimante est connectée compte tenu de l'absence de gestion du signal /STROBE
qui est indispensable pour valider les données d'impression. En revanche, si une Soundplayer+ était connectée, des effets secondaires indésirables seraient à prévoir sur le son ou le réseau. D'autres combinaisons d'adressage sont possibles et, dans certains cas, permettent des optimisations de code on ne peut plus subtiles. En outre, une conséquence directe est qu'il est possible de feinter la MF2 assez facilement. Par exemple, tapez en BASIC la ligne suivante :
OUT &BC00,0:OUT &AC00,5:OUT &BD00,0
En pratique, que fait ce programme ? Eh bien, il sélectionne le registre 0 du CRTC via le port légal &BCxx
, il sélectionne ensuite le registre 5 (qui est le registre de retard vidéo) tout en mettant également 5 sur le port imprimante, enfin il envoie une donnée via le port légal &BDxx. Mais du point de vue de la MF2, les choses sont différentes. Elle va voir une sélection du registre 0, elle ne verra pas celle du registre 5, puis elle verra l'envoi d'une donnée sur le registre… 0 ! Résultat des courses : activez la MF2 puis faites un “Return” pour rendre la main et paf, écran noir et CPC planté. “Pourquoi ?”, demande le petit blond à lunettes d'un air interloqué. Eh bien tout simplement car au retour, la MF2 va remettre le CRTC dans son état initial… lequel était pour elle un registre 0 à 0 (ce qui fait irrémédiablement “planter”). Et voilà une MF2 dans les choux !
Enfin, pas tout à fait dans les choux… il est facile, avant de rendre la main, de vérifier la liste des valeurs du CRTC (en &3A80
) et de les corriger pour que le retour se passe bien. Il en va de même pour la palette des couleurs avec laquelle on peut faire les mêmes blagues. Beaucoup de démos utilisent l'astuce du registre 0 fictivement à 0 (parfois volontairement, parfois simplement en conséquence d'une optimisation d'adressage) alors que des jeux (comme Batman The Movie) ont plutôt bidouillé sur la palette des couleurs (écran noir au retour mais rien de planté… juste toutes les couleurs en noir… ce qui est moins “méchant” mais en réalité, bien plus pénible à remettre en place au niveau de la MF2 avant de rendre la main).
De la même manière, contrairement à ce que j'ai souvent entendu, il est vraiment enfantin d'utiliser la MF2 pour interrompre puis refaire partir une démo pleine de ruptures après modification. Le petit blonc à lunettes se fait avoir à tous les coups, il lance sa démo, coupe avec la MF2, patche ce qu'il a à patcher, et rend la main… mais là, une fois sur deux, ça plante ! Pourquoi ? Eh bien tout simplement car selon le moment où le programme a été interrompu, le CRTC contient des valeurs susceptibles de bloquer le fonctionnement du CPC. Il suffit de remettre dans les principaux registres du CRTC des valeurs viables avant de rendre la main et ça se passera bien à tous les coups ! Vous aurez juste droit à un petit saut d'écran au retour, mais plus de plantage. Bien sûr, cette manipulation devient indispensable lorsque le demomaker s'est amusé (par goût ou par besoin d'optimisation) à adresser le CRTC ou le Gate Array par des ports illégaux.
Outre ce petit problème de décodage des ports qui est souvent utilisé pour gêner l'utilisation de la MF2 (sans jamais l'empêcher toutefois), il existe un bug beaucoup plus sévère sur lequel je suis tombé par hasard alors que je désassemblais sa ROM afin d'en comprendre le fonctionnement intime. Il n'est cette fois-ci pas matériel mais logiciel et concerne la routine de détection de la mémoire. En effet, dans la ROM, la MF2 détermine si le CPC possède seulement 64ko de RAM ou plus afin de positionner l'octet à l'adresse &3A96
à 0 (64ko) ou à 2 (plus de 64ko). Eh bien, ce test est buggé et laisse des traces sur les CPC ayant des banks ; dès lors, il est possible de rendre l'utilisation de la MF2 pratiquement impossible sur les CPC ayant 128ko ou plus. Ce bug est à ma connaissance présent dans toutes les MF2 distribuées (y compris la MF2+ de 1990) mais j'ai constaté à ma grande surprise que la ROM de MF2 livrée avec l'émulateur CPC CaPriCe32 a été patchée et ne souffre plus de ce bug. Il serait en théorie possible d'utiliser cette version patchée (ou d'en faire une autre soi-même) et de la programmer dans l'EPROM de la MF2.
Je vois le petit blond à lunettes s'agiter sur sa chaise : “c'est quoi le bug ? c'est quoi le bug ?”. J'y viens. Afin de détecter si le CPC a ou non une page de RAM supplémentaire, la MF2 teste la présence de la première bank de celle-ci (bank &C4
). Pour ce faire, elle poke des valeurs en &4000
et regarde ce qui se passe lors de la commutation de la bank pour en déduire si une bank se commute effectivement ou pas. Oui mais voilà, dans l'un des cas de ce test, le programme oublie purement et simplement de restaurer la valeur initialiement présente en bank &C4
! Pour se retrouver dans ce cas de figure, rien de plus simple, il suffit que la valeur &AA
soit présente en &4000
bank &C4
; après une activation de la MF2 cette valeur sera remplacée par &55
. Et il est impossible, lorsqu'on est sous la MF2, de savoir si la valeur initiale était &AA
ou &55
afin de rectifier le tir avant le retour au programme (comme on peut le faire pour les registres du CRTC par exemple). Le petit blond à lunettes se la raconte en disant qu'il n'y a que deux cas possibles : si ça plante au retour avec &55, c'est donc qu'il faut poker &AA
avant de rendre la main. Certes, mais imaginez un programme anti-multiface un peu plus élaboré qui modifierait régulièrement cette valeur, la passant tantôt à &AA
, tantôt à &55
; tout devient alors bien plus compliqué pour contourner la protection. Oui, rien n'est perdu, l'idéal étant de neutraliser le test lui-même mais que de complications pour un petit bug logiciel !
Voilà, nous avons plus ou moins fait le tour des fonctionnalités et des défauts d'une des plus célèbres interfaces sur CPC. Bien sûr, nous pourrions pousser encore plus en avant sur le fonctionnement intime de la MF2, ou sur d'autres petits défauts qui permettraient de la déceler ou d'embêter ses utilisateurs1). Mais, je pense que l'essentiel (et même plus) a été dit et qu'il est plus intéressant de s'attaquer à sa programmation avec un petit exemple simple mais qui met en oeuvre les principaux mécanismes qu'il convient de maîtriser.
Vous trouverez ci-après un petit programme tout simple qui ne fait rien de plus que d'installer dans la MF2 un outil qui permet de copier les 64ko de mémoire centrale dans les 64ko de banks d'un CPC6128 (ou d'un CPC464/664 étendu) lorsque vous appuyez sur le bouton STOP. À la lumière des informations fournies en début d'article, la procédure d'installation dans la MF2 devrait vous paraître assez simple. Il s'agit simplement, après avoir vérifié sommairement la présence d'une MF2 non masquée en tentant de lire un octet de sa ROM, de copier le programme à intégrer dans la MF2 en &2000
(la plage de RAM de la MF2 qui a été largement décrite en amont).
Vient ensuite le programme dédié à la MF2 lui-même. Celui-ci s'excécute dans la mémoire de la MF2 et possède un header. La directive de compilation ORG &2000,&8100
permet d'assembler le code en &8100
(son adresse de stockage) pour une exécution en &2000
(là où il est mis dans la RAM de la MF2 par le code d'installation logé en &8000
). Le header contient les informations suivantes :
Si vous regardez tout ça à la lumière de tout ce qui précède, tout devrait vous paraître limpide (n'en déplaise au petit blond à lunettes). À la suite de ce header, nous avons donc le bout de code qui sera invoqué par la MF2 lorsque vous presserez le bouton STOP. Ce programme va donc copier une à une les 4 pages de 16ko de RAM centrale sur les 4 pages de 16ko des banks après avoir fait un flash rouge pour vous indiquer le bon fonctionnement.
Le programme commence par copier la page en &4000
dans la bank &C7
(la quatrième page de 16ko des banks). Pour ce faire, il place &C1
sur le Gate Array ; ceci a pour effet de commuter la bank &C7
en &C000
. Il effectue ensuite simplement une copie de &4000
vers &C000
. Le programme place ensuite &C2
sur le Gate Array. Dans ce mode, les 64ko de banks sont commutés sur la plage d'adressage du Z80 (comme notre programme s'exécute dans la RAM de la MF2, ça ne pose aucun problème). Dès lors, il peut copier tout ce qui se trouve en &C000
(la bank &C7
) en &4000
(la bank &C5
) : nous avons donc copié la deuxième page de 16ko de la RAM centrale sur la deuxième page de 16ko des banks, missions accomplie, nous pouvons passer à la page suivante.
On envoie ensuite &C7
au Gate Array et on copie de &C000
à &4000
; voilà la quatrième page de 16ko de RAM centrale copiée là où il faut en bank. On trouve ensuite un nouveau petit bout de code qui a pour mission de gérer le flash sur le border puis on attaque la copie de la page en &8000
vers la bank &C6
. Jusque là, tout était simple et chaque page est à sa place. Il ne nous reste plus qu'à copier la page sitruée en &0000
dans la bank &C4
, mais c'est un peu plus compliqué vu que notre programme s'exécute dans la RAM de la MF2 qui est elle-même commutée dans ces adresses ! Le programme va donc devoir jongler un petit peu. Tout d'abord, on commute la bank &C4
dont nous aurons de toute façon besoin en destination de copie. Ensuite, on sauve le contenu de 100 octets de la RAM situés en &8000
dans la RAM de la MF2 avant d'y placer notre petit bout de code personnel (label SwapCode
) puis de l'appeler (JP &8000
).
Ce petit bout de code est la seule difficulté du programme. Premièrement, il déconnecte la page de la MF2 située en &0000-&3FFF
. Pour ce faire, il faut invalider la page ROM/RAM de la MF2 elle-même grâce au port &FEEA
, puis déconnecter toute la ROM basse via le Gate Array (la MF2 est une ROM basse). Dès lors, on a accès à la RAM centrale située entre &0000
et &3FFF
que l'on peut copier simplement en &4000
où nous avions préalablement commuté la bank &C4 (la première page de 16ko des banks). Enfin, on recommute la ROM basse et on active la page ROM/RAM de la MF2 avant de retourner à notre code situé dans celle-ci. Le programme remet le contenu originel de la zone en &8000
dont nous avons eu besoin avant de quitter.
Et voilà, ce qui était à faire a été fait. On a à la sortie de ce programme la copie parfaite de la RAM centrale dans les banks ; couplé avec le Hacker ce petit outil peut être bien pratique. Bien sûr, il ne s'agit pas là d'un programme très complexe et il est possible de faire bien mieux compte tenu des 6ko de mémoire vive disponibles dans la MF2… d'autant que le programme stocké dans cette mémoire peut faire appel à du code stocké sur disquette ou même dans une ROM d'extension. Toutefois, il offre un bon exemple puisqu'il jongle avec les pages mémoire, chose qu'il faut maîtriser à la perfection pour pouvoir faire un logiciel adapté à la MF2.
Télécharger le listing au format Maxam 1.5
; Copy 64k v1.0 ; Programme pour Multiface Two ; OffseT of Futurs' - 02/2003 ; ; Installe un programme en mode Direct Jump ; qui copie les 64k de RAM centrale dans les ; premiers 64k de bank ; Org &8000 ; Implantation Limit &80ff ; programme d'installation Nolist ld hl,msgintro ; Texte d'intro call afftxt di ; Interdiction interruptions ld bc,&7f8a out (c),c ; Commutation ROM basse ld bc,&fee8 out (c),c ; Commutation page MF2 ld a,(2) ; Test MF2 présente cp &7f jr z,erreur ld hl,&8100 ld de,&2000 ld bc,&200 ldir ; Installation de Direct Jump ld bc,&feea out (c),c ; Déconnexion page MF2 ei ; Autorisation interruptions ld hl,msgsucces ; Texte de réussite AffTxt ld a,(hl) or a ret z call &bb5a inc hl jr afftxt Erreur ld hl,msgerr jr afftxt MSGIntro db "Copy 64k 1.0",13,10 db "OffseT of Futurs' - 02/2003",13,10,0 MSGSucces db "Programme installé avec succès,",13,10 db "Multiface Two prete.",13,10,0 MSGErr db "Multiface Two non trouvée !",7,13,10,0 ; Org &2000,&8100 ; Implantation code Limit &9700 ; Direct Jump MF2Head dw mf2code ; Header du code Direct Jump db &89 ; Config ROM db &c0 ; Config RAM db 0 ; N/A db "RUN" ; config jump MF2Code ld bc,&7f10 ; Sélection border out (c),c ld c,&4c ; Border rouge out (c),c ld c,&c1 ; Copie de la page &4000 out (c),c ; vers la bank &C7 ld hl,&4000 ld de,&c000 ld bc,&4000 ldir ld bc,&7fc2 ; Copie de la bank &C7 out (c),c ; vers la bank &C5 ld hl,&c000 ld de,&4000 ld bc,&4000 ldir ld bc,&7fc7 ; Copie de la page &C000 out (c),c ; vers la bank &C7 ld hl,&c000 ld de,&4000 ld bc,&4000 ldir ld bc,&7f4b ; Border blanc out (c),c ld c,&c6 ; Copie de la page &8000 out (c),c ; vers la bank &C6 ld hl,&8000 ld de,&4000 ld bc,&4000 ldir ld bc,&7fc4 ; Copie de la page &0000 out (c),c ; vers la bank &C4 ld hl,&8000 ld de,buffer ld bc,100 ldir ld hl,swapcode ld de,&8000 ld bc,100 ldir jp &8000 RetourSwapCode ld hl,buffer ld de,&8000 ld bc,100 ldir ret ; Retour MF2 SwapCode ; Code de copie page &0000 ld bc,&feea ; Déconnexion page PF2 out (c),c ld bc,&7f8d ; Déconnexion ROM basse out (c),c ld hl,&0000 ld de,&4000 ld bc,&4000 ldir ld bc,&7f89 ; Connexion ROM basse out (c),c ld bc,&fee8 ; Connexion page MF2 out (c),c jp retourswapcode Buffer ds 100 ; Buffer swap
Dans des temps reculés, la MF2 était utilisée par certains crackers peu compétents pour “déplomber” des jeux ou ripper leurs écrans de présentation. Dans ce dernier cas, aucun soucis, les fichiers d'écrans rippés à l'aide de la MF2 étaient exploitables sur n'importe quel CPC même non équipé d'une MF2. En revanche, dans le cas de programmes sauvegardés via la MF2, celle-ci était indispensable à leur exploitation… toutefois, dans le cas des programmes 64k, des loaders annexes permettaient de contourner cette limitation.
Vous trouverez quelques logiciels permettant d'utiliser les sauvegardes 64k de la MF2 sur un CPC en étant dépourvus sur le Quasar Disc 3.
Voyons voir maintenant les programmes qui customisent cette interface. Je vous en ai déjà parlé brièvement en début d'article, le plus célèbre d'entre eux est The Insider, développé par les auteurs de la MF2 en personne. Il intègre tout un tas d'outils pratiques dont un désassembleur ! Ce programme étant relativement complexe, il a besoin d'avoir sa disquette toujours présente dans le lecteur interne de votre CPC afin d'accéder à ses divers modules vu que tout ne rentrait pas dans les 6ko de mémoire libre de la MF2. Un autre défaut de ce logiciel est son manque certain d'ergonomie et son interface lilliputienne.
Il existe un autre programme similaire à The Insider qui dispose d'une interface plus agréable et n'a pas besoin de faire des accès disque à tout bout de champ pour fonctionner : Tearaway. Ce programme de 1991 (The Insider date de 1989) développé par CPC Network offre globalement les mêmes outils que ce dernier avec un confort d'utilisation amélioré. Toutefois il est moins puissant puisqu'il ne fonctionne que sur un CPC doté d'au moins 128ko et n'est pas compatible avec les logiciels exploitant les banks… car il les utilise lui-même pour fonctionner ! En fait, c'est grâce à ce stratagème qu'il peut offrir une interface digne de ce nom et éviter tout accès disque. À quand un logiciel de ce type s'appuyant sur une ROM d'extention ? Nous pourrions alors avoir des outils très complets sans utiliser une once de mémoire vive.
Vous trouverez divers outils de customisation de la MF2 dans le Quasar Disc 4.
J'espère que les informations que j'ai pu vous fournir dans cet article vous donneront envie de développer des logiciels élaborés pour la Multiface Two. Si vous avez des questions sur des points précis, n'hésitez pas à me contacter !