
Rétro-ingénierie du firmware des générateurs de fonctions Philips PM5139 / PM5138A / PM5136 : émulateurs 8051 utilisés comme instruments de mesure, 35 sections de matériel documenté, et un firmware V2.0 corrigé
Un générateur de fonctions 20 MHz d'environ 1994, démonté par le logiciel : deux dumps d'EPROM, un émulateur 8051 utilisé comme instrument de mesure, et 35 sections de documentation où chaque affirmation est étayée par une adresse de listing, une mesure d'émulateur ou le schéma.
Au bout du compte, il y a un firmware V2.0 qui corrige un défaut livré par Philips, six formes d'onde arbitraires de notre cru, et un simulateur navigateur qui exécute la ROM d'origine instruction par instruction.

Chaque table de formes d'onde du programme EPROM, tracée directement depuis le binaire. En bas à droite se trouve celle qui a lancé la partie la plus intéressante de ce projet.
Le Philips PM5139 est le modèle haut de gamme 20 MHz d'une famille de trois instruments (PM5136 / PM5138A / PM5139). À l'intérieur se trouvent un PCB80C652 — un cœur 8051 avec I²C matériel — une EPROM programme 27512, et six assemblages analogiques suspendus à un bus série.
Il n'existe pas de manuel de service du PM5139. Les gens en cherchent un sur les forums depuis 2010. Ce qui existe, c'est le manuel du PM5138A, son modèle sœur 10 MHz, qui est presque identique en interne.
Ce projet a donc commencé par l'autre bout : dumper l'EPROM, et comprendre ce que fait le code jusqu'à ce que l'instrument soit suffisamment compris pour le modifier.
Deux versions de firmware étaient disponibles, V1.3 et V1.5, toutes deux des dumps M27512 de 64 Kio.
| Désassemblage | complet pour les deux versions, ~23 000 lignes, avec références croisées |
| Listing annoté | 147 routines nommées, 145 commentaires d'en-tête, 3 826 lignes annotées |
| Documentation | 35 sections, 4 600 lignes, chaque affirmation sourcée |
| Chemin du signal | fréquence, amplitude, offset, AM, FM, burst, symétrie, sweep — tout calculé et vérifié par rapport au code d'origine |
| Matériel | les 10 strobes, le bus C, l'I²C avec chaque participant, les ports, le clavier, le bouton rotatif, le bitmap de l'afficheur |
| Bits d'état | 75 sur 128 avec un effet documenté |
| Diff de version | V1.3 vs V1.5 est structurellement identique à 91,4 % ; chaque changement nommé |
| Émulateurs | un en Python, un en JavaScript (~8 M instructions/s), plus un simulateur navigateur monofichier |
| Notre propre firmware | V2.0 — un défaut d'usine corrigé, la somme de contrôle gérée, vérifié dans l'émulateur et sur matériel réel |
| Position | Type | Fonction |
|---|---|---|
| D301 | PCB80C652 | cœur 8051 avec I²C matériel, 12 MHz |
| D306 | 27512 | EPROM programme — V1.3 occupe 0000h–AC70h |
| D310 | X28C64 | EEPROM arbitraire sur le bus MOVX |
| D305 | PCF8570 | 256 octets de NVRAM sauvegardée par pile sur I²C (A0h) |
| D304-A | PCF8576 | pilote LCD sur I²C (70h), tampon de 20 octets |
| D302-A | SAA3007 | encodeur clavier, codé en largeur d'impulsion sur une seule ligne |
| D307 | 74HCT4514 | décodeur de strobe — le numéro de strobe correspond aux bits d'adresse A8…A11 |
Le côté analogique est un bus C série : l'UART du 8051 fonctionne en mode
registre à décalage, TXD est l'horloge, RXD la donnée, et un strobe décide lequel
des dix registres à décalage verrouille les octets. MOV DPH,#8nh suivi de
MOVX @DPTR,A déclenche le strobe n. Cette seule ligne est la clé de toute la
section analogique.
C'est la partie qui vaut la peine d'être reprise pour votre propre projet.
Lire un binaire 8051 de 44 Ko à l'œil vous mène peut-être à un tiers du chemin. Tout le reste est venu en exécutant le code d'origine et en observant ce qui en ressort :```python
c = CPU(rom) for w in test_values: set_amplitude(c, w) c.call(0x0AAC) # the original routine, untouched print(w, c.ram[0x1C]) # the byte that goes out on STR9
Faites varier l'entrée, lisez la sortie, vérifiez-la par rapport à l'hypothèse. Cela a fonctionné pour la fréquence, l'amplitude, l'offset, la profondeur AM, la déviation FM, le nombre de rafales, la symétrie et les deux caractéristiques de balayage. Chaque formule de la documentation est accompagnée des points d'échantillonnage sur lesquels elle a été vérifiée.
Trois raffinements l'ont rendu réellement productif :
**Surveillez le bus, pas l'affichage.** La section 15 mesure ce qu'un bit d'état fait au tampon d'affichage, et 74 des 128 bits semblent ne rien faire. Mais beaucoup d'entre eux ne pilotent pas l'affichage, ils pilotent les *assemblages analogiques* — et ceux-ci ne sont visibles que sous forme de télégrammes sur le bus C. L'enregistrement de `MOV SBUF,…` et du `MOVX @DPTR` de terminaison a fait passer le nombre de bits documentés de 54 à 75.
**Appuyez sur les touches, ne poussez pas la RAM.** Définir un octet de RAM à la main produit des états que l'instrument ne prend jamais. Cela nous a coûté deux conclusions erronées et un plantage dans la table de commandes. Injecter de vrais codes de touches via le SAA3007 émulé donne des états que le firmware atteint réellement — et c'est un balayage par force brute sur les 256 codes de touches qui a révélé quelle touche déclenche quel gestionnaire.
**Soupçonnez d'abord votre propre émulateur.** Trois bugs dans notre cœur ont produit un comportement « inexplicable » du firmware : `ACALL` exécuté comme `AJMP`, un indicateur de retenue auxiliaire manquant (de sorte que `DA A` se comportait mal et que le firmware semblait compter en binaire), et une interruption clavier doublée. Chaque conclusion de cette période a été remesurée par la suite.
---
## Le chemin parcouru
**D'abord le statique.** Un désassembleur avec une table d'opcodes complète, puis une descente récursive avec des heuristiques de tables de saut. Cela a produit 30 508 octets de code et laissé 13 637 octets non comptabilisés.
**Puis le dynamique.** Une exécution avec trace — démarrage à froid, les 23 touches du panneau avant, les deux sens du bouton rotatif, tous les modes de fonctionnement, 86 millions de cycles — marquant chaque adresse réellement exécutée. Confrontée à l'analyse statique, elle a trouvé exactement **une** zone que la descente avait manquée, et 10 686 des octets inexpliqués se sont révélés être cinq blocs de tables connus.