
फिलिप्स PM5139 / PM5138A / PM5136 फंक्शन जनरेटर की फर्मवेयर रिवर्स इंजीनियरिंग: मापक यंत्रों के रूप में उपयोग किए जाने वाले 8051 एमुलेटर, प्रलेखित हार्डवेयर के 35 अनुभाग, और एक संशोधित फर्मवेयर V2.0
लगभग 1994 का 20 MHz फंक्शन जनरेटर, सॉफ़्टवेयर में खोला गया: दो EPROM डंप, एक 8051 एमुलेटर जिसे मापक यंत्र की तरह इस्तेमाल किया गया, और 35 दस्तावेज़ीकरण अनुभाग जहाँ हर एक दावे के पीछे कोई लिस्टिंग पता, एमुलेटर माप, या स्कीमैटिक है।
इसके अंत में एक फर्मवेयर V2.0 है जो Philips द्वारा भेजे गए एक दोष को ठीक करता है, हमारे अपने छह मनमाने वेवफ़ॉर्म, और एक ब्राउज़र सिम्युलेटर जो मूल ROM को निर्देश-दर-निर्देश चलाता है।

प्रोग्राम EPROM में हर वेवफ़ॉर्म टेबल, सीधे बाइनरी से प्लॉट की गई। नीचे दाईं ओर वह है जिसने इस परियोजना के सबसे दिलचस्प हिस्से की शुरुआत की।
Philips PM5139 तीन-यंत्र परिवार (PM5136 / PM5138A / PM5139) का 20 MHz टॉप मॉडल है। इसके अंदर एक PCB80C652 — हार्डवेयर I²C वाला 8051 कोर — एक 27512 प्रोग्राम EPROM, और एक सीरियल बस से जुड़ी छह एनालॉग असेंबलियाँ हैं।
PM5139 की कोई सर्विस मैनुअल नहीं है। लोग 2010 से फ़ोरम में इसकी तलाश कर रहे हैं। जो मौजूद है वह PM5138A की मैनुअल है, इसकी 10 MHz बहन मॉडल, जो आंतरिक रूप से लगभग समान है।
तो यह परियोजना दूसरे छोर से शुरू हुई: EPROM डंप करो, और समझो कि कोड क्या करता है जब तक यंत्र इतना समझ न आ जाए कि इसे संशोधित किया जा सके।
दो फर्मवेयर संस्करण उपलब्ध थे, V1.3 और V1.5, दोनों 64 KiB M27512 डंप।
एनालॉग पक्ष एक सीरियल C-बस है: 8051 का UART शिफ्ट रजिस्टर मोड में चलता है, TXD क्लॉक है, RXD डेटा, और एक स्ट्रोब तय करता है कि दस शिफ्ट रजिस्टरों में से कौन बाइट्स को लैच करता है। MOV DPH,#8nh के बाद MOVX @DPTR,A स्ट्रोब n को फ़ायर करता है। वह एक पंक्ति पूरे एनालॉग अनुभाग की कुंजी है।
यह वह हिस्सा है जो आपकी अपनी परियोजना के लिए चुराने लायक है।
44 KB की 8051 बाइनरी को आँख से पढ़ने पर आप शायद एक-तिहाई रास्ता तय कर पाएँगे। उसके आगे का सब कुछ मूल कोड चलाकर और देखकर कि क्या निकलता है से आया:```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
इनपुट को बदलें, आउटपुट पढ़ें, परिकल्पना के विरुद्ध जाँचें। यह
आवृत्ति, आयाम, ऑफ़सेट, AM गहराई, FM विचलन, बर्स्ट
गणना, सममिति और दोनों स्वीप विशेषताओं के लिए काम कर गया। दस्तावेज़ में प्रत्येक सूत्र
उन नमूना बिंदुओं के साथ आता है जिन पर इसे सत्यापित किया गया था।
तीन परिष्करणों ने इसे वास्तव में उत्पादक बनाया:
**बस को देखें, डिस्प्ले को नहीं।** अनुभाग 15 मापता है कि एक स्टेट बिट
डिस्प्ले बफ़र पर क्या प्रभाव डालता है, और 128 में से 74 बिट्स प्रतीत होता है कि कुछ नहीं करते। लेकिन
उनमें से कई डिस्प्ले को नहीं चलाते, वे *एनालॉग
असेंबली* को चलाते हैं — और वे केवल C-बस पर टेलीग्राम के रूप में दिखाई देते हैं।
`MOV SBUF,…` और समाप्ति करने वाले `MOVX @DPTR` को रिकॉर्ड करने से प्रलेखित बिट्स की संख्या
54 से बढ़कर 75 हो गई।
**कुंजियाँ दबाएँ, RAM को मत छेड़ें।** हाथ से एक RAM बाइट सेट करने से
ऐसी अवस्थाएँ उत्पन्न होती हैं जो उपकरण कभी नहीं लेता। इससे हमें दो गलत निष्कर्ष और
कमांड टेबल में एक क्रैश का खर्च आया। एमुलेटेड SAA3007 के माध्यम से वास्तविक कुंजी कोड इंजेक्ट करने से
ऐसी अवस्थाएँ मिलती हैं जिन तक फ़र्मवेयर वास्तव में पहुँचता है — और यह सभी 256 कुंजी कोड्स पर एक
ब्रूट-फ़ोर्स स्वीप था जिसने यह उजागर किया कि कौन सी कुंजी किस हैंडलर को ट्रिगर करती है।
**पहले अपने ही एमुलेटर पर संदेह करें।** हमारे कोर में तीन बग्स ने
"अकथनीय" फ़र्मवेयर व्यवहार उत्पन्न किया: `ACALL` को `AJMP` के रूप में निष्पादित किया गया, एक अनुपस्थित
सहायक-कैरी फ़्लैग (इसलिए `DA A` ने गलत व्यवहार किया और फ़र्मवेयर बाइनरी में गिनती करता प्रतीत हुआ), और एक दोहरा
कीबोर्ड इंटरप्ट। उस अवधि के प्रत्येक निष्कर्ष को बाद में पुनः मापा गया।
---
## यहाँ तक का रास्ता
**पहले स्टैटिक।** एक पूर्ण ऑपकोड टेबल वाला डिसअसेम्बलर, फिर जंप-टेबल ह्यूरिस्टिक्स के साथ रिकर्सिव
डिसेंट। इसने 30 508 बाइट्स कोड उत्पन्न किया और 13 637 बाइट्स को
अगणित छोड़ दिया।
**फिर डायनामिक।** एक ट्रेस रन — कोल्ड स्टार्ट, सभी 23 फ्रंट पैनल कुंजियाँ, नॉब की दोनों
दिशाएँ, प्रत्येक ऑपरेटिंग मोड, 86 मिलियन साइकल — प्रत्येक उस पते को चिह्नित करते हुए जो वास्तव में निष्पादित हुआ। स्टैटिक विश्लेषण के विरुद्ध रखे जाने पर,
इसने ठीक **एक** ऐसा क्षेत्र खोजा जिसे डिसेंट से चूक गया था, और 10 686 अव्याख्यायित बाइट्स
पाँच ज्ञात टेबल ब्लॉक्स निकले।
**फिर स्कीमैटिक्स।** सर्विस मैनुअल का OCR स्कीमैटिक्स के लिए बेकार है,
लेकिन 400 dpi पर पेज इमेज उत्कृष्ट हैं। ओवरलैपिंग टाइल्स में काटे जाने पर, वे
पिन नंबरों तक पठनीय हैं। इस तरह छह शीट्स पढ़ी गईं — और जहाँ पाँच समानांतर ट्रेस
90 पिक्सल की दूरी पर चलते हैं, वहाँ आँख से देखने के बजाय एक स्क्रिप्ट (`lines.py`) ने
बिटमैप से लाइन सेगमेंट निकाले।
**फिर वे दो चिप्स जो निकाले गए थे।** "SINUS 1.1" लेबल वाला एक 27C64
और एक X28C64 पढ़े गए। दोनों को स्कीमैटिक में रखा गया और उनकी
सामग्री को डिकोड किया गया।
**फिर वर्ज़न डिफ।** दोनों ROMs को टोकनाइज़ करना (पूर्ण लक्ष्यों के बजाय
सापेक्ष जंप दूरियाँ) और उन पर `SequenceMatcher` चलाना
एक पता मैपिंग देता है जो कोड मोशन से बच जाती है — इसी तरह V1.3
प्रतीक V1.5 पर ले जाए जाते हैं।
---
## अच्छे हिस्से
### Philips ने एक शोरयुक्त तरंग भेजी
तीन अंतर्निहित आर्बिट्ररी कर्व्स `A047h`, `A447h` और `A847h` पर स्थित हैं।
तीसरे की आकृति वही है जो पहले से ही ROM में गणना किए गए रूप में मौजूद एक टेबल की है — लेकिन
**13 के मुकाबले 563 दिशा परिवर्तन**, और 4.1 LSB का मानक विचलन।
यह गणना किए जाने के बजाय एक एनालॉग स्रोत से सैंपल किया गया था। विचलन का माध्य
शून्य है, 1024 में से केवल दो बिंदु 10 से अधिक विचलित हैं — यह कोई भिन्न तरंग नहीं है, यह
*वही* तरंग है जिस पर शोर है।
### वह टेबल एक 30 dB लेवल लैडर है
स्वच्छ संस्करण को एक पूर्व ड्राफ्ट में "दस AM गहराइयों वाला एक साइन" बताया गया था, जो
प्लॉट की आँख से की गई पढ़ाई थी, न कि कोड जो कहता है। गणना करने पर,
1024 बिंदु दस साइन आर्क्स में विभाजित होते हैं जिनके विस्तार हैं```
255 171 120 80 56 38 26 17 12 8
एक ज्यामितीय श्रेणी जिसका अनुपात 0.681 = 10^(−1/6) है, अर्थात प्रति चरण 3.33 dB और कुल 30.1 dB। एक हल्विंग मॉडल 56 तक गलत है, एक 3 dB मॉडल 10 तक। यह एक लघुगणकीय स्तर की सीढ़ी है — एक आयाम या क्षीणन परीक्षण पैटर्न।
आयाम नियंत्रक में एक स्ट्रोब पर दो शिफ्ट रजिस्टर हैं, लेकिन फर्मवेयर प्रति टेलीग्राम केवल एक बाइट ही भेजता है। योजनाबद्ध इसे समझाता है: दो 4094s QS' (पिन 10) के माध्यम से कैस्केड किए गए हैं, पिन 9 अप्रयुक्त है — और टेलीग्राम जोड़ों में आते हैं, ~42 000 चक्रों के अंतराल पर, जोड़ों के बीच लाखों चक्रों का मौन होता है। पहले भेजा गया बाइट दूसरे रजिस्टर में धकेल दिया जाता है।
एक से अधिक शिफ्ट रजिस्टर वाली हर असेंबली पर वही कैस्केड पैटर्न मिला — जिसमें एक ऐसा मामला भी शामिल है जहाँ श्रृंखला E नामक लाइन के माध्यम से एक असेंबली सीमा को पार करती है।
STR9 टेलीग्राम में पाँच बिट्स सीधे रिले को चलाते हैं: S1 DC जनरेटर रेंज को स्विच करता है, S2…S5 क्षीणक रिले को। 20 dB (for 40dB),
20 dB, 50/600 ohms — यह योजनाबद्ध पर छपा है। गणना करने के लिए कोई थ्रेशोल्ड नहीं हैं।
0301h पर जंप टेबल को JMP @A+DPTR के साथ पढ़ा जाता है। प्रविष्टि 15 0301h + 30 = 031Fh पर पहुँचती है — और वहाँ, सामान्य AJMP के बजाय, हैंडलर स्वयं इनलाइन बैठा है, जिससे एक जंप बचती है। ROM में कहीं भी कोई जंप निर्देश इसकी ओर इंगित नहीं करता, इसलिए स्थिर विश्लेषण इसे खो बैठा। यह DIAL LOCK हैंडलर है, और केवल गतिशील ट्रेस ने इसे खोजा।
डेटा शीट 24 वेवफॉर्म मेमोरी का वादा करती है। EEPROM में निर्देशिका छह कहती है। अंकगणित इसे तय करता है:``` 1024 points × 10 bit, packed 4 values per 5 bytes -> 1280 bytes per curve 6 × 1280 = 7 680 bytes, 0100h…1EFFh (X28C64, 8 KB) <- what was fitted 24 × 1280 = 30 720 bytes, 0100h…78FFh (X28C256, 32 KB) <- what the schematic says
फर्मवेयर की मापी गई रीड रेंज `0100h–1EFFh` है — छह कर्व्स बाइट तक सटीक। यह उपकरण छोटी चिप के साथ बनाया गया था।
### डेड कोड जो ऐसे डिवाइस से बात कर रहा है जो मौजूद ही नहीं है
`9AFFh` पर 186 बाइट्स एड्रेस `5Ah` के साथ I²C ट्रैफ़िक करते हैं — ऐसा एड्रेस जो कहीं और नहीं दिखता। **दोनों** फर्मवेयर वर्शन्स में, कोई भी जंप इस ओर इशारा नहीं करता। यह `5Eh` पर इंटरफ़ेस कार्ड के समान डिवाइस-टाइप ब्लॉक में बैठता है, बस अलग बैंक बिट्स के साथ, और यह रिसीव बफ़र और अरिथमेटिक रजिस्टर्स को दो दस-बाइट टेलीग्राम्स में बाहर भेजता है। यह ऐसे डिवाइस के लिए फैक्ट्री डायग्नोस्टिक जैसा दिखता है जो कभी शिप ही नहीं हुआ।
### आप मनमाने EEPROM से कोड एक्ज़ीक्यूट नहीं कर सकते
एक स्पष्ट विचार — कोड को किसी मनमाने वेवफ़ॉर्म स्लॉट में रखकर उस पर जंप करना — शुरू में ही नाकाम है। 8051 हार्वर्ड आर्किटेक्चर है: निर्देश प्रोग्राम EPROM से `/PSEN` के ज़रिए आते हैं, डेटा मनमाने EEPROM से `/RD` के ज़रिए। यह अवरुद्ध नहीं है; तार ही वहाँ मौजूद नहीं है।
### और अंत में, फ़्रीक्वेंसी एन्कोडिंग
डिस्प्ले डिजिट रो PCF8576 को भेजी गई इमेज के `3Eh–43h` में रहती है, सभी पोज़िशन्स एक ही सेगमेंट एन्कोडिंग साझा करती हैं, और बाइट `43h` डिकेड 7 और 8 के बीच kHz से MHz पर स्विच हो जाती है। उससे:```
f = M · 10^(D−8) kHz
तीन आवृत्ति स्टेपिंग अनुक्रम जो वास्तविक उपकरण पर मापे गए थे, इसके द्वारा बिल्कुल पुनरुत्पादित होते हैं — उस सहित जो जल्दी रुक जाता है क्योंकि मैंटिसा 2500 का अर्थ 25 MHz होगा, जो सीमा से अधिक है।

बाईं ओर शिप किया गया कर्व, दाईं ओर सुधारा गया। नीचे बाईं ओर गणना की गई तालिका से विचलन है — ±5 LSB का वह बैंड वही है जो एक सैंपल किए गए एनालॉग स्रोत ने छोड़ा।
mkv20.py V1.5 (या V1.3) से V2.0 बनाता है। यह हर पते को हार्डकोड करने के बजाय हस्ताक्षर द्वारा ढूंढता है, इसलिए वही स्क्रिप्ट दोनों स्रोत संस्करणों पर काम करती है:
*IDN? में: PHILIPS,PM5139,0,V2.0/0000।2.0 लिखते हैं।बाकी सब कुछ अछूता है। तीन और विचित्रताएँ मिलीं और जानबूझकर अकेली छोड़ दी गईं — एक गैर-मौजूद SFR में लेखन (हानिरहित, दोनों संस्करणों में), मृत 5Ah ब्लॉक, और तीन स्टेट बिट जो परीक्षित होते हैं पर कभी सेट नहीं होते। उन्हें पैच करने से कोई व्यवहार नहीं बदलता और केवल जोखिम बढ़ता है।
M27512_PM5139_V20.bin ठीक यही है और कुछ नहीं। नीचे दी गई धुन एक अलग, ऑप्ट-इन बिल्ड चरण है।
सत्यापित: एमुलेटर में कोल्ड स्टार्ट V1.5 के समान डिस्प्ले बफ़र और समान फ़्लैग उत्पन्न करता है, चेकसम मान्य होता है, और बिल्ड बाइट-पुनरुत्पादनीय है। इसे फ़्लैश किया गया है और यह वास्तविक PM5139 पर चलता है — डिस्प्ले 2.0 दिखाता है और सभी छह arbitrary स्लॉट काम करते हैं।
चूँकि V1.5 में चेकसम के पीछे 19 509 अप्रयुक्त बाइट हैं, और आवृत्ति पथ एक नोट आवृत्ति को तीन BCD बाइट के रूप में लेता है, उपकरण अपने ही आउटपुट के माध्यम से संगीत बजा सकता है।
एन्कोडिंग सुखद रूप से सीधी है — दशक 3, फिर आवृत्ति 0.01 Hz में BCD के रूप में, इसलिए 82.41 Hz 30 82 41 है। प्रति नोट चार बाइट: तीन पिच के लिए, एक अवधि के लिए।
दिलचस्प हिस्सा ट्रिगर है। डायग्नोस्टिक मेनू (चालू करते समय LOCAL दबाए रखें) में आठ प्रविष्टियों वाली जंप टेबल है, लेकिन मेनू लूप 0Bh को केवल 1 से 7 तक गिनता है — इसलिए आठवीं प्रविष्टि अगम्य है। यह अनावश्यक भी है: यह मेनू प्रारंभ पर जाता है, जहाँ दो अन्य स्थानों से भी पहुँचा जाता है।
तो पूरा हुक दो बाइट है:``` 5B94h table entry 8: LJMP 5B45h -> LJMP 5B62h count limit: 08h -> 09h
कोई सेल्फ-टेस्ट खोया नहीं जाता, कोई टेबल स्थानांतरित नहीं होती, और कोई डेड मेनू आइटम
प्रकट नहीं होता। LOCAL पकड़ें, चालू करें, मेनू को 8 तक गिनने दें, एक कुंजी दबाएँ।
टाइमिंग MCS-51 डेटा शीट से आती है। दोनों एमुलेटर अब निर्देशों के साथ-साथ मशीन
साइकल भी गिनते हैं (`mcyc`, `mcs51.CYCLES` से), और वेट लूप को स्टेप करने पर प्रति
यूनिट **1009 µs** मापा जाता है — 140 BPM पर प्रति सोलहवें नोट 106.95 ms, लक्ष्य से
0.2 % कम। पहले यह आँकड़ा 1006 µs की हाथ से की गई गणना था जिसमें दो निर्देश छूट गए थे।
`mkdoom.py` एक MIDI फ़ाइल भी कन्वर्ट कर सकता है। एक वॉइस चुननी होती है
(उच्चतम नोट, निम्नतम नोट, या एक चैनल) और ~25 ms से छोटे सेक्शन मर्ज करने होते हैं —
उससे नीचे एक निम्न नोट पूरा ऑसिलेशन नहीं कर पाता और आपको केवल एक क्लिक सुनाई देता है।
---
## और तब पता चला कि यह पॉलीफ़ोनिक है
ऊपर की धुन एक वॉइस है। ऐसा होना ज़रूरी नहीं है, और इसका कारण सर्विस मैनुअल में एक
वाक्य है जिसे हम पढ़कर आगे बढ़ गए थे:
> सिग्नल जनरेशन के दौरान, विशिष्ट सिग्नल एम्प्लिट्यूड सैंपल RAM से पढ़े जाते हैं। यदि
> मूल सिग्नल वेवफ़ॉर्म बदला जाता है [...] तो संबंधित एम्प्लिट्यूड सैंपल **CPU द्वारा RAM
> में लोड किए जाते हैं**।
PM5139 एक **1024-पॉइंट वेवटेबल DDS** है। TWS किसी भी सामान्य अर्थ में ट्राएंगल
जनरेटर नहीं है — यह एक फ़ेज़ एक्युमुलेटर है जो यूनिट 4 पर एक तेज़ RAM के लिए 0…1023
पढ़ने के एड्रेस उत्पन्न करता है, और उस RAM को CPU C-बस के ज़रिए भरता है। साइन, स्क्वायर,
सॉटूथ और आर्बिट्ररी सभी एक ही तंत्र हैं: एक टेबल।
और टेबल में आउटपुट का ठीक **एक पीरियड** होता है। इसलिए *हार्मोनिक्स के योग* से बनी
टेबल अपने 1024 पॉइंट्स में अभी भी पीरियोडिक होती है, और यह एक कॉर्ड के रूप में बजती
है। न आर्पेजियो, न कोई मॉड्यूलेशन ट्रिक — कई नोट एक साथ पूरे 20 Vpp पर बजते हैं, और
जब तक वे बजते हैं CPU कुछ भी नहीं कर रहा होता। चूँकि पार्शियल्स टेबल फ़्रीक्वेंसी के
पूर्णांक गुणज होने चाहिए, अंतराल जस्ट इंटोनेशन में निकलते हैं, जो एक सस्टेन्ड कॉर्ड के
लिए वैसे भी बेहतर ट्यूनिंग है।
`M27512_PM5139_V20_chords.bin` रिपॉज़िटरी में बर्न करने के लिए तैयार है — रिफ़, कॉर्ड्स
में, एनवेलप के साथ। इसे स्वयं बनाने के लिए, या बिल्ट-इन रिफ़ के बजाय अपनी खुद की MIDI
फ़ाइल उपयोग करने के लिए:```
python3 mkpoly.py --chord crunch M27512_PM5139_V20.bin out.bin
python3 mkpoly.py --chord crunch --midi yours.mid --channel 1 \
M27512_PM5139_V20.bin out.bin
mkchord.py टेबल बनाता है — power (2:3:4), major (4:5:6),
minor (10:12:15), dom7 (4:5:6:7) और पाँच और। mkpoly.py एक को
मेलोडी के साथ फ्री ROM में रखता है और उसी डेड मेनू
एंट्री को हुक करता है। यह कॉर्ड को एक बार लोड करता है, फिर केवल रीट्यूनिंग से
मेलोडी बजाता है, जो पूरे कॉर्ड को समानांतर में ट्रांसपोज़ करता है। E1M1 रिफ़ का हर नोट
एक पावर कॉर्ड बन जाता है — जो कि मूल में उस रिफ़ का निर्माण है।
वास्तुकला की दृष्टि से यह एक PPG Wave है: एक काउंटर जो सिंगल-साइकल वेवफ़ॉर्म से गुज़रता है, सीधे एक DAC में। कॉर्ड ट्रिक वही है जो Amiga ट्रैकर्स उपयोग करते थे — कॉर्ड को वेवफ़ॉर्म में डालो ताकि एक वॉइस तीन नोट बजाए बजाय तीन चैनल खर्च करने के। C64 को इसके बजाय अर्पेजियो करना पड़ता है, क्योंकि SID में कोई रिटेबल वेवटेबल नहीं है।
आपको कॉर्ड के लिए EPROM की भी आवश्यकता नहीं है। वही टेबल
मनमाने EEPROM में फिट होती हैं, इसलिए python3 mkarb.py --chords आपको छह कॉर्ड देता है
जो फर्मवेयर को छुए बिना फ्रंट पैनल से चुने जा सकते हैं।
दो प्लेयर हैं और एक इमेज एक या दूसरा ले जाती है, क्योंकि दोनों एक ही मेनू एंट्री को हुक करते हैं:
दो मापों ने उस डिज़ाइन को आकार दिया:
00h 44h 88h CCh) और हर
पुनर्निर्मित मान चार का गुणज है। वेवफ़ॉर्म RAM बारह
बिट चौड़ी है, लेकिन बस दस चलाती है — ठीक वही जो ARB फॉर्मेट संग्रहीत करता है,
इसलिए Philips ने वहाँ कुछ बर्बाद नहीं किया।RAM_PAGE 1D62h पर, जो एक जैसा लगता है,
अपना शब्द फ्रीक्वेंसी से बनाता है। इसलिए हार्मनी टेबल में रहती है और
मेलोडी फ्रीक्वेंसी शब्द में; संगीत चलते समय कुछ भी रीलोड नहीं होता।एमुलेटर कोई वेवफ़ॉर्म RAM मॉडल नहीं करता, इसलिए लोडर को
निर्माण द्वारा सत्यापित किया जाता है: polytest.js रिकॉर्ड करता है कि वास्तव में बस तक क्या पहुँचता है
और सभी 1024 पॉइंट्स की तुलना mkchord.py द्वारा उत्पन्न से करता है।
वहाँ तक पहुँचने में पाँच EPROM लगे, और एमुलेटर हमें केवल आंशिक रूप से मदद कर सका: यह CPU और बस को मॉडल करता है लेकिन वेवफ़ॉर्म RAM को नहीं, इसलिए यह केवल पुष्टि कर सकता है कि फर्मवेयर जो भेजता है वही बाइट्स बाहर जाते हैं। वह आवश्यक है और पर्याप्त नहीं। तीन चीज़ें इंस्ट्रुमेंट पर ही तय करनी पड़ीं:
बाइट ऑर्डर। प्रति पॉइंट दो बाइट्स, हाई बाइट पहले। इसे फर्मवेयर के अपने डाउनलोड से अनुमान लगाने पर उल्टा उत्तर मिला और टेबल शोर के रूप में निकली। जिसने इसे तय किया वह एक EPROM था जिसमें छह टेस्ट पैटर्न थे — एक फ्लैट लाइन, एक रैंप, वही रैंप जिसमें प्रत्येक पॉइंट के बाइट्स की अदला-बदली थी, और तीन और — और स्कोप पर एक नज़र। स्वैप्ड रैंप साफ़ था।
एक वेवफ़ॉर्म परिवर्तन उन्नीस टेलीग्राम है, वे तीन नहीं जो पहले प्लेयर ने भेजे थे। जो मायने रखता है वह एक दो-बाइट राइट है जो RAM को राइट मोड में डालता है; इसके बिना 2048 बाइट्स बस पर बाहर जाते हैं और कहीं नहीं पहुँचते।
आउटपुट लेवल। एटेन्यूएटर एक बाइट में दो अलग 20 dB रिले स्टेज हैं, उनके लिए ROM टेबल उलटी पढ़ती है जैसा दस्तावेज़ीकृत था (वे बायपास बिट्स हैं), और लेवल DAC सात बिट का है, आठ का नहीं — यह 80h पर रैप करता है, इसलिए एक "लाउडर" सेटिंग ने साइलेंस उत्पन्न किया। उस एक ने एक ही इमेज में लगभग तीस संयोजनों का मैट्रिक्स लिया, आउटपुट फ्रीक्वेंसी को टेस्ट नंबर के रूप में उपयोग करते हुए ताकि स्कोप का अपना रीडआउट बताए कि कौन सा संयोजन लाइव है।``` telegrams emitted by the loader: STR6 4 byte(s) 122 machine cycles 1E 00 20 01 STR2 0 byte(s) 132 machine cycles STR1 2050 byte(s) 39490 machine cycles CC 89 88 8A 44 8B 44 8C ... -> all 1024 points identical to the table mkchord.py built
note 1 f0 = 41.20 Hz chord 2:3:4 = 82.4 / 123.6 / 164.8 Hz root E2 note 8 f0 = 36.71 Hz chord 2:3:4 = 73.4 / 110.1 / 146.8 Hz root D2
---
## हमारे स्वयं के छह मनमाने तरंगरूप

`D310_image_V20.bin` EEPROM में हर स्लॉट को भरता है — चिप को एक बार बर्न करना उचित है:
| स्लॉट | तरंगरूप | Vpp | किसलिए |
|---|---|---|---|
| 1 | sinc, 8 लोब | 12.17 | बैंड लिमिटिंग, ओवरशूट |
| 2 | रिंगिंग, Q≈6 | 17.81 | सेटलिंग व्यवहार |
| 3 | ECG | 12.80 | डेमो |
| 4 | सीढ़ी, 16 स्टेप द्विध्रुवी | 20.00 | रैखिकता, रिज़ॉल्यूशन |
| 5 | दिष्टकृत साइन | 10.00 | मूल की तरह, पर गणना किया गया |
| 6 | मल्टी-टोन, 5 टोन | 20.00 | इंटरमॉड्यूलेशन |
दो बारीकियाँ जो मायने रखती हैं और जिन्हें गलत करना आसान है:
**ज़ीरो-सेंट्रिंग स्ट्रेचिंग से बेहतर है।** स्पष्ट कदम है प्रत्येक वक्र को पूरी मान श्रेणी में खींचना। ऐसा न करें: उपकरण का DC ऑफ़सेट एक अलग एनालॉग पथ से आता है और एक *निश्चित* वोल्टेज जोड़ता है, जबकि खींचे गए असममित वक्र की DC सामग्री *आयाम के साथ* मापन करती है। आपको हर बार स्तर बदलने पर ऑफ़सेट को फिर से ट्रिम करना पड़ेगा। तरंगरूप के प्राकृतिक शून्य को कनवर्टर के शून्य पर रखने में 0.2 से 1 बिट का खर्च आता है — मूल एनालॉग पथ जो पहले से ही योगदान देता है उस 16 LSB शोर के मुकाबले। कोई वास्तविक लागत नहीं।
**फ़्लोटिंग पॉइंट में स्केल करें, एक बार राउंड करें।** पहले राउंड करना और बाद में खींचना 1.0–1.5 क्वांटाइज़ेशन स्टेप की त्रुटि देता है; फ़्लोट में स्केल करना और एक बार राउंड करना इष्टतम 0.5 देता है।
निर्देशिका को प्रति-वक्र एक पहचान बाइट चाहिए (1280 वक्र बाइट्स का चेकसम, प्रारंभ मान `55h`) और न्यूनतम/अधिकतम 10-बिट मानों के रूप में छह बिट्स द्वारा बाएँ-संरेखित। पहचान बाइट गलत होने पर उपकरण **Err 8** दिखाता है और मनमाने स्रोत को अस्वीकार कर देता है — जो वास्तव में पहले असली फ्लैश पर हुआ था।
---
## ब्राउज़र सिम्युलेटर
`PM5139_Simulator.html` एक एकल स्व-निहित फ़ाइल है — कोई बिल्ड चरण नहीं, कोई निर्भरता नहीं, कोई नेटवर्क नहीं। इसे खोलें और मूल V1.3 फ़र्मवेयर आपके सामने बूट होता है।
8051 कोर असली कोड चलाता है। टाइमर, इंटरप्ट, C-बस और I²C का अनुकरण किया गया है; डिस्प्ले वास्तविक PCF8576 डेटा स्ट्रीम से डिकोड किया जाता है, और कुंजियाँ P3.3 पर पल्स-चौड़ाई कोडित SAA3007 तरंगरूप उत्पन्न करती हैं। बैटरी-बैक्ड RAM पहले से लोड है और मनमाना EEPROM स्टार्ट-अप पर उत्पन्न होता है और फ़र्मवेयर द्वारा स्वयं जाँचा जाता है।
कोल्ड स्टार्ट में लगभग 9 मिलियन निर्देश लगते हैं, इसलिए इसे एक सेकंड दें।
---
## रिपॉज़िटरी लेआउट```
Documentation
PM5139_Hardware_Reference.md the main document, 35 sections
PM5139_Firmware_Modification.md how to change the firmware and flash it back
PM5139_Tables.md command and message tables, both versions
PM5139_Changelog_V13_V15.md what changed from V1.3 to V1.5, in prose
PM5139_Bit_Crossreference.md flags 20h–2Fh: set / cleared / tested
HANDOVER.md state of play
BACKLOG.md open questions, each with an entry point
Firmware and data
M27512_PM5139_V13.bin V15.bin the two original dumps
M27512_PM5139_V20.bin our own version
D310_image.bin the arbitrary EEPROM as read out
D310_image_V20.bin six waveforms of our own, ready to burn
PCF8570_image.bin NVRAM in the factory state
PM5139_V13_annotated.asm V15 the annotated listings
Emulation
emu.py system.py system2.py keys.py Python core and peripherals
core.js the same core in JavaScript
shell.html + build.py -> PM5139_Simulator.html
Analysis
mcs51.py analyze2.py seqdiff.py mapv15.py symbols.py annotate.py
Building
romfix.py mkv20.py mkarb.py waveforms.py asm51.py mkdoom.py
midi.py mid2ton.py mkchord.py mkpoly.py
Measurement scripts (see "Using the tools")
bitmap.js flags.js cmd16.js iface.js trace.js arb.js xrange.js
polytest.js cyclecheck.py
limits.js param.js keycodes.js decade.js whoruns.js remote.js
display.js digits.js readout.js nvram.js nv2.js nv3.js …
Python 3 और Node ही आपकी सभी आवश्यकताएँ हैं। प्लॉट्स के लिए matplotlib, और lines.py के लिए केवल pillow और numpy।
python3 annotate.py 13 # -> PM5139_V13_annotated.asm python3 mapv15.py --write # map V1.3 symbols onto V1.5 python3 annotate.py 15 # -> PM5139_V15_annotated.asm python3 seqdiff.py # structural diff of both versions python3 romfix.py M27512_PM5139_V13.bin
### बिल्ड V2.0```bash
python3 mkv20.py # from V1.5 (default)
python3 mkv20.py M27512_PM5139_V13.bin out.bin # or from V1.3
python3 romfix.py M27512_PM5139_V20.bin # verify the checksum
python3 waveforms.py # what the generators produce python3 mkarb.py # -> D310_image_V20.bin python3 plot_arb.py # -> PM5139_ARB_V20.png
### एक धुन जोड़ें```bash
# the built-in bass line, into a separate image
python3 mkdoom.py M27512_PM5139_V20.bin M27512_PM5139_V20_melody.bin
# or bring your own tune (no MIDI file is shipped here)
python3 midi.py song.mid # what is in the file
python3 mid2ton.py song.mid --voice high # inspect the conversion
python3 mkdoom.py --midi song.mid --channel 1 M27512_PM5139_V20.bin out.bin
node doomtest.js M27512_PM5139_V20_melody.bin # play it back in the emulator
mkdoom.py एक छवि को एक बार पैच करता है और दोबारा करने से इंकार कर देता है — यदि आप शुरू से शुरू करना चाहते हैं तो mkv20.py से एक नया V2.0 बनाएं।
python3 mkchord.py # the chords on offer python3 mkpoly.py --chord power M27512_PM5139_V20.bin out.bin python3 romfix.py out.bin node polytest.js out.bin # check it on the bus
### प्लॉट```bash
python3 plot_waveforms.py # V2.0 by default
python3 plot_waveforms.py M27512_PM5139_V13.bin out.png
python3 plot_v20.py # before/after
इनमें से प्रत्येक एक तालिका प्रिंट करता है जिसे आप दस्तावेज़ीकरण के विरुद्ध जाँच सकते हैं:```bash node bitmap.js # which state bits change the display (31 / 23 / 74) node flags.js # which bits change the C-bus telegrams, over six profiles node cmd16.js # which strobes each command token triggers node keycodes.js # which key code reaches which handler node decade.js # decade limits, driven by real key presses node limits.js # parameter limits by bisection node whoruns.js # does this routine ever run in normal operation? node arb.js # does the firmware accept this EEPROM image? node xrange.js # which EEPROM addresses are read at all node iface.js # emulate the interface card, log the I²C traffic node remote.js # how the instrument enters remote mode node nvram.js # which NVRAM bytes change when you adjust something node readout.js # decode a display digit row into plain text node showversion.js # read the version indication out of all three ROMs node trace.js # dynamic execution trace
### एक स्कीमैटिक पढ़ें```bash
pdftoppm -f 157 -l 157 -r 400 -png pm5138A_service_manual.pdf page
python3 lines.py page-157.png 1200 800 3000 2400 150
पूरी बिल्ड श्रृंखला नियतात्मक है — ये कमांड फर्मवेयर और EEPROM इमेज को बाइट-दर-बाइट पुनर्निर्मित करते हैं:```bash python3 mapv15.py --write python3 annotate.py 13 && python3 annotate.py 15 python3 mkv20.py # -> M27512_PM5139_V20.bin python3 romfix.py M27512_PM5139_V20.bin python3 mkarb.py # -> D310_image_V20.bin python3 mkdoom.py M27512_PM5139_V20.bin M27512_PM5139_V20_melody.bin python3 build.py # rebuild the browser simulator
---
## इसे वापस फ्लैश करना
> **अपना मूल EPROM रखें।** इसे दो बार पढ़ें, डंप की तुलना करें, चिप को
> दराज़ में रख दें। यहाँ सब कुछ केवल तभी प्रतिवर्ती है जब आपके पास
> यह अभी भी हो।
फर्मवेयर पावर-अप पर व्याप्त रेंज पर एक बाइट योग की जाँच करता है और
इसकी तुलना ठीक उसके बाद वाले बाइट से करता है। गलत होने पर आपको
`Err 1` मिलता है और एक अनंत लूप — उपकरण बूट नहीं होता। `romfix.py`
सही मान की गणना करके डालता है; यहाँ हर बिल्ड स्क्रिप्ट पहले से ही इसे
कॉल करती है।
| संस्करण | रेंज | चेकसम बाइट | मान |
|---|---|---|---|
| V1.3 | `0000h–AC6Fh` | `AC70h` | `F2h` |
| V1.5 | `0000h–B3C9h` | `B3CAh` | `99h` |
असली हार्डवेयर पर कठिन तरीके से सीखी गई दो बातें:
- मनमाने EEPROM के **identity bytes** की पुनर्गणना करनी होती है, अन्यथा
हर शुरुआत में `Err 8` मिलता है और ARB स्रोत चुना नहीं जा सकता।
- यदि फ्लैश के बाद ARB अजीब व्यवहार करे, तो इमेज पर संदेह करने से पहले
जाँचें कि सॉकेट का पिन 28 ठीक से लगा है।
---
## यह कितना विश्वसनीय है?
जो कुछ भी सत्यापित के रूप में चिह्नित है, उसकी पुष्टि एमुलेटर में मूल
रूटीन को कई नमूना बिंदुओं पर कॉल करके की गई थी, आमतौर पर लिस्टिंग या
स्कीमैटिक के साथ भी मिलान किया गया।
जहाँ चीज़ें गलत हुईं, वहाँ उन्हें चुपचाप ठीक करने के बजाय लिख दिया गया है:
- **तीन एमुलेटर बग** (`ACALL` को `AJMP` के रूप में, गायब AC फ्लैग, दोगुना
कीबोर्ड इंटरप्ट) परियोजना के मध्य चरण के दौरान सक्रिय थे।
सभी प्रभावित निष्कर्षों को बाद में फिर से मापा गया — डिस्प्ले बिटमैप
समान निकला, स्ट्रोब असाइनमेंट सर्विस मैनुअल से मेल खाया,
और अनुभाग 16 में दो गायब स्ट्रोब पाए गए।
- **एक सिंथेटिक NVRAM इमेज** जिसे कभी किसी असली उपकरण से नहीं पढ़ा गया था,
ने दो निष्कर्षों को गलत साबित किया, जिसमें "रोटरी नॉब केवल एक दिशा में
काम करता है" भी शामिल है। समाधान यह था कि फर्मवेयर को एक अमान्य NVRAM
दिया जाए और उसे अपनी फैक्ट्री स्थिति स्वयं लिखने दिया जाए।
- **हाथ से सेट किए गए RAM स्टेट** ऐसी कॉन्फ़िगरेशन उत्पन्न करते हैं जो
उपकरण कभी नहीं लेता। दो बार इससे गलत निष्कर्ष निकले, एक बार कमांड
टेबल में क्रैश हुआ।
- **`core.js` प्रति चक्र एक निर्देश गिनता है**, मशीन चक्र नहीं। क्रम के लिए
ठीक, पूर्ण टाइमिंग के लिए गलत — यहाँ टाइमिंग के दावे MCS-51 डेटा शीट
से आते हैं।
जो कुछ भी माप के बजाय एक अनुमान है, वह पाठ में ऐसा कहता है।
---
## अभी भी खुला
- **128 में से 36 स्टेट बिट्स** को छह ऑपरेटिंग प्रोफ़ाइल के बाहर एक
उत्तेजना की आवश्यकता है — सेल्फ-टेस्ट, त्रुटि पथ, इंटरफ़ेस ट्रैफ़िक।
- **ऑफ़सेट 0Dh से आगे के NVRAM फ़ील्ड।** वहाँ तक का लेआउट मापा गया है
(`NVRAM offset + 4Bh = RAM address`), चेक मार्क समझा गया है
(बाइट योग, प्रारंभ मान `AAh`, 25 बाइट्स)।
- **`8871h` क्षेत्र में कौन सा मनमाना कमांड 13 सब-ब्लॉक्स में से किस तक
पहुँचता है।** केवल चार सीधी टोकन तुलनाएँ मौजूद हैं; बाकी बिट टेस्ट पर
शाखा करती हैं।
- **क्या कोई रिमोट कमांड पैरामीटर रेंज चेक को बायपास कर सकता है।**
- **वेवफ़ॉर्म लोड रूटीन** पूर्ण पुनःकार्यान्वयन के लिए सबसे कठिन शेष
निर्भरता हैं — इनके बिना कोई आउटपुट सिग्नल नहीं है।
- **PM5139 अपने 10 MHz सहोदर के समान क्लॉक से 20 MHz कैसे बनाता है।**
श्रृंखला इंगित करती है कि इसका लो-पास 5 MHz के बजाय 10 MHz पर बैठता है,
लेकिन इसकी पुष्टि के लिए PM5139 मैनुअल चाहिए।
यदि आपके पास इनमें से कोई उपकरण है, तो दो चीज़ें बहुत मदद करेंगी: एक
**PM5139 सर्विस मैनुअल**, और **अन्य फर्मवेयर संस्करणों** से डंप
(एक V1.4 हो भी सकता है या नहीं भी)।
---
## स्रोत
- **`pm5138A_service_manual.pdf`** — प्राथमिक हार्डवेयर स्रोत। 176
पृष्ठ, OCRed; चलता हुआ पाठ `pdftotext -layout` के साथ साफ़ पढ़ा जाता है,
स्कीमैटिक्स को इमेज के रूप में रेंडर करना पड़ता है। स्कैन से पृष्ठ 4-3
से 4-28 तक गायब हैं।
- **PM5139 उपयोगकर्ता मैनुअल** (Fluke) — टेक्स्ट लेयर के बिना त्रिभाषी
स्कैन; अध्याय 3.7.4.6 मनमाने कमांडों का दस्तावेज़ीकरण करता है। स्वयं
OCR करने लायक — अंग्रेज़ी भाग PDF पृष्ठ 13–145 है।
- **PM5136 उपयोगकर्ता मैनुअल** — काउंटर-चेक के रूप में उपयोगी: इसकी
त्रुटि संख्याएँ और कमांड सूची दिखाती हैं कि सबसे छोटे मॉडल में कौन से
पैरामीटर नहीं हैं, जिसने ROM में पैरामीटर क्रम की स्वतंत्र रूप से पुष्टि
की।
- **तीनों मॉडलों की डेटा शीट** — प्रति वेवफ़ॉर्म ऑपरेटिंग सीमाएँ।
मैनुअल तृतीय-पक्ष दस्तावेज़ हैं और **इस रिपॉज़िटरी में पुनर्वितरित नहीं
किए गए हैं**। वे ऑनलाइन खोजे जा सकते हैं।
---
## लाइसेंस और उपयोग
दो प्रकार की सामग्री, अलग-अलग शर्तों के अंतर्गत — सटीक दायरे के लिए
[LICENSE](https://github.com/doctormord/philips-pm-5139-5138a-5136-firmware-project/blob/main/LICENSE) देखें:
- **रिवर्स इंजीनियरिंग कार्य MIT है।** दस्तावेज़ीकरण, उपकरण, दोनों
एमुलेटर, सिंबल टेबल, एनोटेशन, उत्पन्न वेवफ़ॉर्म और प्लॉट। इसे जैसे चाहें
उपयोग करें।
- **Philips फर्मवेयर हमारा लाइसेंस देने का नहीं है।** ROM इमेज, फैक्ट्री
चिप डंप, डिसअसेंबली लिस्टिंग और ब्राउज़र सिम्युलेटर (जो V1.3 इमेज को
एम्बेड करता है) Philips कार्य को पुनःउत्पादित या व्युत्पन्न करते हैं।
वे यहाँ अध्ययन की वस्तु के रूप में हैं, उन उपकरणों की अंतर-संचालनीयता,
मरम्मत और दस्तावेज़ीकरण के लिए जो दशकों से समर्थन से बाहर हैं।
जहाँ हमारा अपना कार्य मिला हुआ है — एनोटेशन, V2.0 में संशोधित
वेवफ़ॉर्म — केवल वह योगदान MIT है।
यदि आप मूल फर्मवेयर में अधिकार रखते हैं और आपत्ति करते हैं, तो एक इश्यू
खोलें और इसे हटा दिया जाएगा।
यदि आप इसमें से कुछ भी उपयोग करते हैं, तो एक लिंक बैक की सराहना की जाती
है। यदि आपको कोई गलती मिलती है, तो एक इश्यू खोलें — यहाँ हर दावा उस
पते या माप का नाम देता है जिस पर वह टिका है, इसलिए यह मिथ्यापनीय होना
चाहिए।
| डिसअसेम्बली | दोनों संस्करणों के लिए पूर्ण, ~23 000 पंक्तियाँ, क्रॉस-रेफ़रेंस के साथ |
| एनोटेटेड लिस्टिंग | 147 नामित रूटीन, 145 हेडर टिप्पणियाँ, 3 826 एनोटेटेड पंक्तियाँ |
| दस्तावेज़ीकरण | 35 अनुभाग, 4 600 पंक्तियाँ, हर दावे का स्रोत |
| सिग्नल पथ | आवृत्ति, आयाम, ऑफ़सेट, AM, FM, बर्स्ट, सिमेट्री, स्वीप — सब मूल कोड के विरुद्ध गणना और सत्यापित |
| हार्डवेयर | सभी 10 स्ट्रोब, C-बस, हर प्रतिभागी के साथ I²C, पोर्ट, कीबोर्ड, रोटरी नॉब, डिस्प्ले बिटमैप |
| स्टेट बिट्स | 128 में से 75 प्रलेखित प्रभाव के साथ |
| संस्करण अंतर | V1.3 बनाम V1.5 संरचनात्मक रूप से 91.4 % समान; हर परिवर्तन का नाम |
| एमुलेटर | एक Python में, एक JavaScript में (~8 M निर्देश/सेकंड), साथ ही एक सिंगल-फ़ाइल ब्राउज़र सिम्युलेटर |
| हमारा अपना फर्मवेयर | V2.0 — एक फ़ैक्टरी दोष ठीक, चेकसम संभाला, एमुलेटर और वास्तविक हार्डवेयर पर सत्यापित |
| स्थान | प्रकार | कार्य |
|---|
| D301 | PCB80C652 | हार्डवेयर I²C वाला 8051 कोर, 12 MHz |
| D306 | 27512 | प्रोग्राम EPROM — V1.3 0000h–AC70h घेरता है |
| D310 | X28C64 | MOVX बस पर मनमाना EEPROM |
| D305 | PCF8570 | I²C (A0h) पर 256 बाइट बैटरी-बैक्ड NVRAM |
| D304-A | PCF8576 | I²C (70h) पर LCD ड्राइवर, 20-बाइट बफ़र |
| D302-A | SAA3007 | कीबोर्ड एनकोडर, एक ही लाइन पर पल्स-चौड़ाई कोडित |
| D307 | 74HCT4514 | स्ट्रोब डिकोडर — स्ट्रोब संख्या एड्रेस बिट्स A8…A11 है |
mkdoom.py | mkpoly.py |
|---|
| वॉइस | एक | एक साथ कई |
| वेवफ़ॉर्म | जो भी लोड है | इसकी अपनी कॉर्ड टेबल |
| लेवल | जैसा फ्रंट पैनल ने छोड़ा | स्पष्ट रूप से सेट, 11.6 Vpp मापा गया |
| ROM उपयोग | 182 बाइट्स | 2617 बिल्ट-इन रिफ़ के साथ, MIDI ट्रैक से 6185 |