Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
Philips-PM-5139-5138A-5136-Firmware-Project — الهندسة العكسية للبرامج الثابتة لمولدات الدوال Philips PM5139 / PM5138A / PM5136: محاكيات 8051 المستخدمة كأجهزة قياس، و35 قسمًا من العتاد الموثّق، وبرنامج ثابت V2.0 مصحّح | Kitploit
أدوات/GitHubGitHub/doctormord/philips-pm-5139-5138a-5136-firmware-project
أمان الأنظمة المدمجةالتحليل الثابتالتحليل الديناميكي (عزل)الهندسة العكسيةأمن الأجهزةتحليل الملفات الثنائيةالأوراق والأبحاثالتعلم والتعليمتحليل البرامج الثابتة

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة
GitHubdoctormord/philips-pm-5139-5138a-5136-firmware-project

Philips-PM-5139-5138A-5136-Firmware-Project

الهندسة العكسية للبرامج الثابتة لمولدات الدوال Philips PM5139 / PM5138A / PM5136: محاكيات 8051 المستخدمة كأجهزة قياس، و35 قسمًا من العتاد الموثّق، وبرنامج ثابت V2.0 مصحّح

عرض المستودع
213منذ يوم واحدلم تتم المراجعة بعد

Philips PM5139 — هندسة عكسية للبرنامج الثابت

مولّد دوال 20 ميجاهرتز من حوالي عام 1994، مُفكَّك على مستوى البرمجيات: نسختان من تفريغ EPROM، ومحاكي 8051 يُستخدم كأداة قياس، و35 قسمًا من التوثيق حيث كل ادعاء مدعوم بعنوان إدراج، أو قياس من المحاكي، أو المخطط الكهربائي.

في النهاية يوجد برنامج ثابت V2.0 يُصلح عيبًا شحنته Philips، وست موجات اعتباطية من صنعنا، ومحاكي متصفح يشغّل ROM الأصلي تعليمة بتعليمة.

جميع جداول الموجات في ROM الإصدار V1.3

كل جدول موجات في EPROM البرنامج، مرسوم مباشرة من الملف الثنائي. أسفل اليمين هو الجدول الذي بدأ الجزء الأكثر إثارة في هذا المشروع.


المحتويات

  • ما هذا
  • النتائج في لمحة
  • الجهاز
  • المنهج: المحاكي هو أداة القياس
  • الطريق إلى هنا
  • الأجزاء الممتعة
  • البرنامج الثابت V2.0 — ما الجديد
  • بيضة عيد الفصح
  • ثم اتضح أنه متعدد الأصوات
  • ست موجات اعتباطية من صنعنا
  • محاكي المتصفح
  • تخطيط المستودع
  • استخدام الأدوات
  • إعادة إنتاج كل شيء
  • إعادة كتابته إلى الشريحة
  • ما مدى موثوقية هذا؟
  • لا يزال مفتوحًا
  • المصادر

ما هذا

Philips PM5139 هو الطراز الأعلى بتردد 20 ميجاهرتز من عائلة من ثلاثة أجهزة (PM5136 / PM5138A / PM5139). في الداخل توجد PCB80C652 — نواة 8051 مع I²C عتادي — وEPROM برنامج من نوع 27512، وست تجميعات تماثلية معلّقة على ناقل تسلسلي.

لا يوجد دليل خدمة لـ PM5139. الناس يبحثون عنه في المنتديات منذ عام 2010. ما هو موجود هو دليل PM5138A، الطراز الشقيق بتردد 10 ميجاهرتز، وهو متطابق داخليًا تقريبًا.

لذا بدأ هذا المشروع من الطرف الآخر: تفريغ EPROM، ومعرفة ما يفعله الكود حتى يُفهم الجهاز جيدًا بما يكفي لتعديله.

كانت هناك نسختان من البرنامج الثابت متاحتان، V1.3 و V1.5، وكلتاهما من نوع M27512 بحجم 64 كيبي بايت.


النتائج في لمحة


الجهاز

الجانب التماثلي هو ناقل C تسلسلي: يعمل UART الخاص بـ 8051 في وضع سجل الإزاحة، TXD هو الساعة، RXD هو البيانات، وstrobe يقرر أي من سجلات الإزاحة العشرة يلتقط البايتات. MOV DPH,#8nh متبوعًا بـ MOVX @DPTR,A يُطلق strobe رقم n. هذا السطر الواحد هو مفتاح القسم التماثلي بأكمله.


المنهج: المحاكي هو أداة القياس

هذا هو الجزء الذي يستحق أن تسرقه لمشروعك الخاص.

قراءة ملف ثنائي 8051 بحجم 44 كيبي بايت بالعين توصلك ربما إلى ثلث الطريق. كل ما بعد ذلك جاء من تشغيل الكود الأصلي ومراقبة ما ينتج عنه:```python

What formula turns the entered amplitude into the byte on the bus?

Don't read the routine. Call it.

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

root@kitploit:~
تنويع المدخلات، وقراءة المخرجات، والتحقق منها مقابل الفرضية. نجح ذلك مع التردد والسعة والإزاحة وعمق AM وانحراف FM وعدد النبضات والتماثل وخاصيتي المسح. كل صيغة في التوثيق تأتي مع نقاط العينة التي تم التحقق منها.

ثلاث تحسينات جعلته منتجًا فعليًا:

**راقب الناقل، لا الشاشة.** يقيس القسم 15 ما تفعله بتة الحالة بمخزن الشاشة، ويبدو أن 74 من 128 بتة لا تفعل شيئًا. لكن الكثير منها لا يقود الشاشة، بل يقود *التجميعات التناظرية* — وهذه لا تكون مرئية إلا كبرقيات على ناقل C. أدى تسجيل `MOV SBUF,…` و`MOVX @DPTR` المنهي إلى رفع عدد البتات الموثقة من 54 إلى 75.

**اضغط المفاتيح، لا تعبث بالذاكرة.** إعداد بايت ذاكرة يدويًا ينتج حالات لا يتخذها الجهاز أبدًا. كلفنا ذلك نتيجتين خاطئتين وانهيارًا واحدًا في جدول الأوامر. حقن رموز مفاتيح حقيقية عبر SAA3007 المحاكى يعطي حالات يصل إليها البرنامج الثابت فعليًا — وكان مسح القوة الغاشمة عبر جميع رموز المفاتيح الـ256 هو ما كشف أي مفتاح يفعّل أي معالج.

**اشك في المحاكي الخاص بك أولًا.** أنتجت ثلاثة أخطاء في نواتنا سلوكًا "غير قابل للتفسير" في البرنامج الثابت: تنفيذ `ACALL` كـ`AJMP`، وغياب علم الحمل المساعد (فأساء `DA A` التصرف وبدا البرنامج الثابت وكأنه يعد بالنظام الثنائي)، ومقاطعة لوحة مفاتيح مضاعفة. أُعيد قياس كل نتيجة من تلك الفترة بعد ذلك.

---

## الطريق إلى هنا

**الثابت أولًا.** مفكك شيفرة بجدول أكواد كامل، ثم النزول التكراري مع استدلالات جداول القفز. أنتج ذلك 30,508 بايت من الشيفرة وترك 13,637 بايت غير محسوبة.

**ثم الديناميكي.** تشغيل تتبع — بدء بارد، جميع مفاتيح اللوحة الأمامية الـ23، كلا اتجاهي المقبض، كل وضع تشغيل، 86 مليون دورة — مع تعليم كل عنوان نُفذ فعليًا. عند مقارنته بالتحليل الثابت، وجد بالضبط **منطقة واحدة** فاتت النزول، وتبين أن 10,686 من البايتات غير المفسرة هي خمس كتل جداول معروفة.

**ثم المخططات.** التعرف الضوئي على نص دليل الصيانة عديم الفائدة للمخططات، لكن صور الصفحات بدقة 400 نقطة في البوصة ممتازة. عند تقطيعها إلى بلاطات متداخلة، تصبح مقروءة حتى أرقام المنافذ. قُرئت ست صفحات بهذه الطريقة — وحيث تمتد خمسة مسارات متوازية بفاصل 90 بكسل، استُبدل الفحص البصري بسكربت (`lines.py`) يستخرج قطع الخطوط من الصورة النقطية.

**ثم الشريحتان المنتزعتان.** قُرئ 27C64 موسوم بـ"SINUS 1.1" وX28C64. وُضع كلاهما في المخطط وفُكّ محتواهما.

**ثم مقارنة الإصدارات.** تقطيع كلا الـROMين إلى رموز (مسافات القفز النسبي بدلًا من الأهداف المطلقة) وتشغيل `SequenceMatcher` عليهما يعطي تعيين عناوين يصمد أمام حركة الشيفرة — وهكذا تُنقل رموز V1.3 إلى V1.5.

---

## الأجزاء الجيدة

### Philips شحنت موجة مشوشة

تقع المنحنيات الثلاثة العشوائية المدمجة عند `A047h` و`A447h` و`A847h`. للثالث نفس شكل جدول موجود أصلًا في الـROM بصيغة محسوبة — لكن بـ**563 تغير اتجاه مقابل 13**، وانحراف معياري قدره 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. إنه سلّم مستويات لوغاريتمي — نمط اختبار سعة أو تخفيف.

STR9 يرسل 16 بت على شكل برقيتين كل منهما بايت واحد

تحتوي وحدة التحكم في السعة على سجلّي إزاحة على ستروب واحد، لكن البرنامج الثابت لا يرسل سوى بايت واحد لكل برقية. يوضح المخطط ذلك: الـ 4094s الاثنان متتاليان عبر QS' (الطرف 10)، مع كون الطرف 9 غير مستخدم — وتأتي البرقيات في أزواج، بفاصل ~42 000 دورة مع ملايين الدورات من الصمت بين الأزواج. البايت المُرسَل أولاً يُدفع إلى السجل الثاني.

ظهر نمط التسلسل نفسه في كل تجميعة تحتوي على أكثر من سجل إزاحة واحد — بما في ذلك حالة واحدة تعبر فيها السلسلة حدود تجميعة عبر خط يُسمى E.

المخفّض ليس عملية حسابية

خمس بتات في برقية STR9 تُشغّل المرحّلات مباشرة: S1 يبدّل نطاق مولّد التيار المستمر، و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

root@kitploit:~
نطاق القراءة المقاس للبرنامج الثابت هو `0100h–1EFFh` — ست منحنيات
إلى البايت. صُنع الجهاز بالشريحة الصغيرة.

### كود ميت يتحدث إلى جهاز غير موجود

186 بايت عند `9AFFh` تقوم بحركة I²C مع العنوان `5Ah` — عنوان
لا يظهر في أي مكان آخر. في **كلا** إصداري البرنامج الثابت، لا توجد أي قفزة تشير
إليه. إنه يقع في نفس كتلة نوع الجهاز الخاصة ببطاقة الواجهة عند `5Eh`،
فقط مع بتات بنك مختلفة، ويرسل مخزن الاستقبال و
سجلات الحساب في برقيتين من عشرة بايتات. يبدو كأنه
تشخيص مصنعي لجهاز لم يُشحن قط.

### لا يمكنك تنفيذ كود من الـ EEPROM العشوائي

فكرة بديهية — ضع الكود في خانة موجة عشوائية واقفز إليه —
ميتة عند الوصول. الـ 8051 هارفارد: تأتي التعليمات عبر
`/PSEN` من الـ EPROM البرمجي، والبيانات عبر `/RD` من الـ
EEPROM العشوائي. الأمر ليس محجوبًا؛ السلك ببساطة غير موجود.

### وأخيرًا ترميز التردد

يقع صف أرقام العرض في `3Eh–43h` من الصورة المرسلة إلى
PCF8576، وجميع المواضع تشترك في ترميز مقطع واحد، والبايت `43h`
يبدّل من kHz إلى MHz بين العقد 7 و8. ومن ذلك:```
f = M · 10^(D−8) kHz

ثلاثة تسلسلات خطوات ترددية مقاسة على الجهاز الحقيقي يُعاد إنتاجها بدقة بواسطة هذا — بما في ذلك التسلسل الذي يتوقف مبكرًا لأن الجزء العشري 2500 سيعني 25 MHz، وهو فوق الحد.


البرنامج الثابت V2.0 — ما الجديد

منحنى عشوائي 3: V1.5 مقابل V2.0

على اليسار المنحنى المُشحون، وعلى اليمين المنحنى المصحح. أسفل اليسار هو الانحراف عن الجدول المحسوب — ذلك النطاق من ±5 LSB هو ما تركه مصدر تناظري مُعيّن.

mkv20.py يبني V2.0 من V1.5 (أو V1.3). يجد كل عنوان بواسطة التوقيع بدلًا من ترميزها بشكل ثابت، لذا يعمل نفس السكربت على كلا إصداري المصدر:

  1. استبدال المنحنى العشوائي 3 بالجدول المحسوب النظيف. كلاهما 1024 بايت من 8 بت، لذا فإن التبديل محايد من حيث الحجم ولا يمس أي كود. 863 من 1024 بايت تتغير.
  2. استبدال المنحنى العشوائي 2 بزقزقة لوغاريتمية (1 → 40 دورة). كان يختلف عن المنحنى 1 في بايتين بالضبط — نبضة إبرة إضافية واحدة — لذا كان زائدًا عن الحاجة.
  3. تعريف الإصدار في *IDN?: PHILIPS,PM5139,0,V2.0/0000.
  4. الإصدار على الشاشة: تسلسل إعادة الضبط يكتب خليتي عرض، وهذه البايتات الآن تتهجى 2.0 في ترميز المقطع المقاس.
  5. إعادة حساب المجموع الاختباري وتخزينه حيث يتوقعه البرنامج الثابت.

كل شيء آخر لم يُمَس. تم العثور على ثلاث غرائب إضافية وتُركت عن قصد — كتابة إلى SFR غير موجود (غير ضارة، في كلا الإصدارين)، كتلة 5Ah الميتة، وثلاث بتات حالة يتم اختبارها ولكن لا يتم تعيينها أبدًا. ترقيعها لا يغير أي سلوك ويضيف فقط مخاطرة.

M27512_PM5139_V20.bin هو هذا بالضبط ولا شيء آخر. اللحن أدناه هو خطوة بناء منفصلة واختيارية.

تم التحقق: بدء بارد في المحاكي ينتج نفس مخزن العرض ونفس الأعلام كما في V1.5، ويتم التحقق من المجموع الاختباري، والبناء قابل لإعادة الإنتاج بايت ببايت. تم تفليشه ويعمل على PM5139 حقيقي — تعرض الشاشة 2.0 وجميع الفتحات العشوائية الست تعمل.


بيضة عيد الفصح

بما أن هناك 19 509 بايت غير مستخدمة خلف المجموع الاختباري في V1.5، و مسار التردد يأخذ تردد نغمة كثلاث بايتات 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

root@kitploit:~
لا يُفقد أي اختبار ذاتي، ولا يُنقل أي جدول، ولا يظهر أي عنصر قائمة ميت.
اضغط LOCAL، شغّل الجهاز، دع القائمة تعدّ إلى 8، ثم اضغط مفتاحًا.

التوقيت مأخوذ من ورقة بيانات MCS-51. كلا المحاكيين الآن يعدّان دورات
الآلة إلى جانب التعليمات (`mcyc`، من `mcs51.CYCLES`)، وقياس حلقة
الانتظار بالخطوات يعطي **1009 µs** لكل وحدة — 106.95 ms لكل
نوتة سادسة عشر عند 140 BPM، بخطأ 0.2 % عن الهدف. كان الرقم سابقًا
حسابًا يدويًا قدره 1006 µs وقد أسقط تعليمتين.

يستطيع `mkdoom.py` أيضًا تحويل ملف MIDI. يجب اختيار صوت
(أعلى نوتة، أو أدنى نوتة، أو قناة واحدة) ودمج المقاطع الأقصر من
~25 ms — تحت ذلك لا تُكمل النوتة المنخفضة ذبذبة كاملة
ولا تسمع سوى نقرة.

---

## ثم اتضح أنه متعدد الأصوات

اللحن أعلاه صوت واحد. لا يلزم أن يكون كذلك، والسبب
جملة في دليل الخدمة كنا قد تجاوزناها بالقراءة:

> أثناء توليد الإشارة، تُقرأ عينات سعة الإشارة المتميزة
> من RAM. إذا عُدّل شكل الموجة الأساسي [...] فإن
> عينات السعة المقابلة **تُحمَّل إلى RAM بواسطة CPU**.

إن PM5139 هو **DDS بجدول موجي من 1024 نقطة**. وTWS ليس مولّد
مثلث بأي معنى ساذج — بل هو مراكم طور ينتج
عناوين قراءة 0…1023 لذاكرة RAM سريعة في الوحدة 4، وتلك الذاكرة يملؤها
CPU عبر ناقل C. الجيب والموجة المربعة والمنشارية والعشوائية كلها
الآلية نفسها: جدول.

والجدول يحمل بالضبط **دورة واحدة من الخرج**. لذا فجدول مبني
من *مجموع توافقيات* يبقى دوريًا في نقاطه الـ1024،
ويُعزف كوردة. ليس أربيجيو، ولا حيلة تعديل — بل عدة نوتات
تُسمع معًا عند 20 Vpp الكامل، بينما لا يفعل CPU شيئًا على الإطلاق
أثناء سماعها. ولأن الأجزاء الجزئية يجب أن تكون مضاعفات صحيحة
لتردد الجدول، تخرج الفواصل بالتنغيم الطبيعي، وهو للكورد
المستمر الضبط الأفضل على أي حال.

ملف `M27512_PM5139_V20_chords.bin` موجود في المستودع جاهزًا للحرق — الـ
riff، كوردة، مع الغلاف. لبنائه بنفسك، أو لاستخدام
ملف MIDI خاص بك بدلًا من الـ riff المدمج:```
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 الحر مع اللحن ويعلّق نفس مدخل القائمة الميت. يحمّل الكورد مرة واحدة، ثم يعزف اللحن بإعادة الضبط فقط، مما ينقل الكورد بأكمله بالتوازي. كل نوتة في riff الخاص بـ E1M1 تصبح power chord — وهذا ما يتكون منه ذلك الـ riff في الأصل.

معماريًا هذا PPG Wave: عدّاد يمر عبر موجة دورة واحدة، مباشرة إلى DAC. حيلة الكورد هي نفسها التي استخدمتها متتبعات Amiga — ضع الكورد في الموجة بحيث يعزف صوت واحد ثلاث نوتات بدلًا من إنفاق ثلاث قنوات عليه. أما C64 فعليه استخدام الأربيجيو بدلًا من ذلك، لأن SID لا يملك wavetable قابلًا للكتابة.

لا تحتاج حتى إلى EPROM للكوردات. نفس الجداول تناسب EEPROM العشوائي، لذا python3 mkarb.py --chords يمنحك ستة كوردات قابلة للاختيار من اللوحة الأمامية دون المساس بالبرنامج الثابت.

هناك مشغّلان والصورة تحمل أحدهما أو الآخر، لأن كليهما يعلّق نفس مدخل القائمة:

قياسان شكّلا ذلك التصميم:

  • صيغة التنزيل تحمل عشرة بتات لكل نقطة، وليس اثنتي عشرة: فقط أربعة بايتات منخفضة مميزة تظهر أبدًا (00h 44h 88h CCh) وكل قيمة معاد بناؤها هي من مضاعفات أربعة. ذاكرة الموجة RAM عرضها اثنا عشر بتًا، لكن الناقل يقود عشرة — بالضبط ما تخزنه صيغة ARB، لذا لم يهدر Philips شيئًا هناك.
  • إعادة تحميل الجدول الكامل تستغرق 32 إلى 40 ms مع صمت الخرج، ولا توجد صفحة مخزن مؤقت ثانية — RAM_PAGE عند 1D62h، الذي يبدو كواحدة، يبني كلمته من التردد. لذا يسكن التناغم في الجدول واللحن في كلمة التردد؛ لا شيء يُعاد تحميله أثناء تشغيل الموسيقى.

المحاكي لا يحاكي ذاكرة الموجة RAM، لذا يُتحقق من المحمّل بالبناء بدلًا من ذلك: polytest.js يسجّل ما يصل فعليًا إلى الناقل ويقارن كل النقاط الـ 1024 بما ولّده mkchord.py.

استغرق الأمر خمسة EPROMs للوصول إلى هناك، ولم يستطع المحاكي أن يوصلنا إلا جزءًا من الطريق: فهو يحاكي المعالج والناقل لكن ليس ذاكرة الموجة 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

root@kitploit:~
---

## ست موجات اعتباطية من صنعنا

![الفتحات الاعتباطية الست](https://assets.kitploit.com/production/public/readmes/54732/9fa22b75c34e4aaa83df1b2afd284353e019cb65f181a83e5cdeceb57ea32937/4b15ae02ae1bec49f3d1b6ab79c480cd3a73b10818c41a8b04be57ab6f691ef7-display-v1.webp)

`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 | التشكيل البيني |

تفصيلان مهمان ويسهل الخطأ فيهما:

**التمركز عند الصفر يتفوق على التمديد.** الخطوة البديهية هي تمديد كل منحنى عبر نطاق القيمة الكامل. لا تفعل ذلك: إزاحة التيار المستمر في الجهاز تأتي من مسار تماثلي منفصل وتضيف جهداً *ثابتاً*، بينما محتوى التيار المستمر لمنحنى غير متماثل ممدد يتناسب *مع السعة*. سيتعين عليك إعادة ضبط الإزاحة في كل مرة تغير فيها المستوى. وضع الصفر الطبيعي للموجة على صفر المحوّل يكلف من 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 الفعلي، وتولّد المفاتيح موجة SAA3007 المشفرة بعرض النبضة على P3.3. ذاكرة الوصول العشوائي المدعومة بالبطارية محمّلة مسبقاً، والـ 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 للرسوم البيانية، وpillow وnumpy فقط من أجل lines.py.

إلقاء نظرة على البرنامج الثابت```bash

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

root@kitploit:~
### إصدار 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

بناء EEPROM العشوائي```bash

python3 waveforms.py # what the generators produce python3 mkarb.py # -> D310_image_V20.bin python3 plot_arb.py # -> PM5139_ARB_V20.png

root@kitploit:~
### إضافة لحن```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 بترقيع الصورة مرة واحدة ويرفض القيام بذلك مرتين — قم ببناء V2.0 جديد باستخدام mkv20.py إذا أردت البدء من جديد.

تشغيل وتر```bash

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

root@kitploit:~
### الرسم البياني```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

root@kitploit:~
### قراءة مخطط```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

root@kitploit:~
---

## إعادة الفلاش

> **احتفظ بـ EPROM الأصلي.** اقرأه مرتين، قارن النسخ، وضع الشريحة في
> درج. كل ما هنا قابل للعكس فقط إذا كنت لا تزال تملكه.

يتحقق البرنامج الثابت من مجموع بايتات على النطاق المشغول عند التشغيل
ويقارنه بالبايت الذي يليه مباشرة. إذا أخطأت تحصل على
`Err 1` وحلقة لا نهائية — لا يُقلع الجهاز. يحسب `romfix.py`
القيمة الصحيحة ويُدرجها؛ كل سكربت بناء هنا يستدعيه بالفعل.

| الإصدار | النطاق | بايت التحقق | القيمة |
|---|---|---|---|
| V1.3 | `0000h–AC6Fh` | `AC70h` | `F2h` |
| V1.5 | `0000h–B3C9h` | `B3CAh` | `99h` |

شيئان تعلمناهما بالطريقة الصعبة على عتاد حقيقي:

- تحتاج EEPROM العشوائية إلى إعادة حساب **بايتات الهوية** الخاصة بها، وإلا
  تحصل على `Err 8` عند كل تشغيل ولا يمكن اختيار مصدر ARB.
- إذا تصرفت ARB بغرابة بعد الفلاش، تحقق من أن الطرف 28 من المقبس
  مثبت جيدًا قبل الشك في الصورة.

---

## ما مدى موثوقية هذا؟

كل ما هو مُعلَّم كموثَّق تم تأكيده باستدعاء الروتينات الأصلية
في المحاكي عبر عدة نقاط عينة، وعادةً ما تم التحقق منه مقابل
قائمة التفكيك أو المخطط أيضًا.

حيث سارت الأمور بشكل خاطئ، كُتب ذلك بدلًا من إصلاحه بصمت:

- **ثلاثة أخطاء في المحاكي** (`ACALL` كـ `AJMP`، علم AC مفقود، مقاطعة
  لوحة المفاتيح مضاعفة) كانت نشطة خلال المرحلة الوسطى من المشروع.
  أُعيد قياس جميع النتائج المتأثرة بعد ذلك — عادت خريطة بتات الشاشة
  مطابقة، وتطابق تعيين الستروب مع دليل الصيانة،
  وتبيّن أن القسم 16 به ستوروبان مفقودان.
- **صورة NVRAM اصطناعية** لم تُقرأ أبدًا من جهاز
  حقيقي زيّفت نتيجتين، بما في ذلك "المقبض الدوّار يعمل
  في اتجاه واحد فقط". كان الإصلاح هو إعطاء البرنامج الثابت NVRAM غير صالح
  وتركه يكتب حالته المصنعية الخاصة.
- **حالات RAM المضبوطة يدويًا** تنتج تكوينات لا يتخذها الجهاز أبدًا.
  مرتين أنتج هذا استنتاجات خاطئة، ومرة انهيارًا في
  جدول الأوامر.
- **`core.js` يحسب تعليمة واحدة لكل دورة**، وليس دورات آلة. جيد
  للترتيب، خاطئ للتوقيت المطلق — ادعاءات التوقيت هنا تأتي من
  ورقة بيانات MCS-51.

أي شيء هو افتراض وليس قياسًا يُذكر ذلك في
النص.

---

## لا يزال مفتوحًا

- **36 من 128 بتة حالة** تحتاج إلى محفز خارج ملفات التشغيل
  الستة — الاختبار الذاتي، مسارات الأخطاء، حركة الواجهة.
- **حقول NVRAM من الإزاحة 0Dh فصاعدًا.** التخطيط حتى هناك مُقاس
  (`NVRAM offset + 4Bh = RAM address`)، وعلامة التحقق مفهومة
  (مجموع بايتات، قيمة البداية `AAh`، 25 بايت).
- **أي أمر عشوائي يصل إلى أي من الكتل الفرعية الـ 13** في
  منطقة `8871h`. توجد فقط أربع مقارنات رموز مباشرة؛ والباقي
  يتفرع على اختبارات بتات.
- **ما إذا كان يمكن لأمر عن بُعد تجاوز فحص نطاق المعاملات.**
- **روتينات تحميل شكل الموجة** هي أصعب تبعية متبقية
  لإعادة تنفيذ كاملة — بدونها لا توجد إشارة خرج.
- **كيف ينتج PM5139 تردد 20 MHz من نفس الساعة** مثل شقيقه
  10 MHz. السلسلة تشير إلى أن مرشحه المنخفض يقع عند 10 MHz بدلًا من 5 MHz،
  لكن ذلك يحتاج إلى دليل PM5139 للتأكيد.

إذا كنت تملك أحد هذه الأجهزة، سيساعد شيئان كثيرًا: **دليل خدمة
PM5139**، ونسخ من **إصدارات برنامج ثابت أخرى**
(قد يوجد V1.4 أو قد لا يوجد).

---

## المصادر

- **`pm5138A_service_manual.pdf`** — مصدر العتاد الأساسي. 176
  صفحة، ممسوحة ضوئيًا بتعرف ضوئي على الحروف؛ النص المتصل يُقرأ
  بوضوح باستخدام `pdftotext -layout`، أما المخططات فيجب عرضها كصور. الصفحات
  4-3 إلى 4-28 مفقودة من المسح.
- **دليل مستخدم PM5139** (Fluke) — مسح ثلاثي اللغة بدون طبقة نصية؛
  القسم 3.7.4.6 يوثق الأوامر العشوائية. يستحق التعرف الضوئي بنفسك
  — الجزء الإنجليزي هو صفحات 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.

إذا كنت تملك حقوقًا في البرنامج الثابت الأصلي وتعترض، افتح issue
وسيُزال.

إذا استخدمت أيًا من هذا، نقدّر رابطًا للإشارة إليه. إذا وجدت خطأً،
افتح issue — كل ادعاء هنا يذكر العنوان أو القياس الذي يستند
إليه، لذا ينبغي أن يكون قابلًا للتكذيب.
تنزيل الأداة
التفكيكمكتمل لكلا الإصدارين، حوالي 23 000 سطر، مع إحالات مرجعية متقاطعة
الإدراج المشروح147 روتينًا مسمّى، 145 تعليق رأس، 3 826 سطرًا مشروحًا
التوثيق35 قسمًا، 4 600 سطر، كل ادعاء موثّق المصدر
مسار الإشارةالتردد، السعة، الإزاحة، AM، FM، النبضة، التناظر، المسح — كلها محسوبة ومتحقق منها مقابل الكود الأصلي
العتادجميع الـ 10 strobes، ناقل C، I²C مع كل مشارك، المنافذ، لوحة المفاتيح، المقبض الدوّار، خريطة العرض النقطية
بتات الحالة75 من 128 بتأثير موثّق
فرق الإصداراتV1.3 مقابل V1.5 متطابقان بنيويًا بنسبة 91.4 %؛ كل تغيير مسمّى
المحاكياتواحد بـ Python، وآخر بـ JavaScript (حوالي 8 ملايين تعليمة/ث)، بالإضافة إلى محاكي متصفح بملف واحد
برنامجنا الثابتV2.0 — عيب مصنعي مُصلَح، checksum مُعالَج، متحقق منه في المحاكي وعلى العتاد الحقيقي
الموضعالنوعالوظيفة
D301PCB80C652نواة 8051 مع I²C عتادي، 12 ميجاهرتز
D30627512EPROM البرنامج — V1.3 يشغل 0000h–AC70h
D310X28C64EEPROM اعتباطي على ناقل MOVX
D305PCF8570256 بايت من NVRAM مدعومة ببطارية على I²C (A0h)
D304-APCF8576مشغّل LCD على I²C (70h)، مخزن مؤقت 20 بايت
D302-ASAA3007مُرمّز لوحة المفاتيح، مُرمّز بعرض النبضة على خط واحد
D30774HCT4514مُفكّك strobe — رقم strobe هو بتات العنوان A8…A11
mkdoom.pymkpoly.py
الأصواتواحدعدة في آن واحد
الموجةأياً كان المحمّلجدول الكورد الخاص به
المستوىكما تركته اللوحة الأماميةمحدد صراحةً، 11.6 Vpp مقاس
ROM المستخدم182 بايت2617 مع الـ riff المدمج، 6185 من مسار MIDI