محاكيات SNES على بعد بضعة بكسلات من الكمال المطلق


نحن قريبون جدًا من إنشاء محاكي يمكنه إعادة إنشاء جميع وظائف الأجهزة الحقيقية وبرامج SNES بشكل مثالي.

على مدار الخمسة عشر عامًا الماضية ، كمبرمج لمحاكي bsnes ، حاولت إتقان محاكاة Super Nintendo ، لكننا نواجه الآن المشكلة الأخيرة: التوقيت الدقيق لدورات الساعة لمعالجات الفيديو SNES. للوصول إلى هذه المرحلة النهائية من دقة المحاكاة ، مطلوب مساعدة المجتمع بأكمله ، وآمل في دعمك. لكن أولاً ، سأخبرك بما حققناه بالفعل.

الوضع الحالي


اليوم ، الوضع مع محاكاة SNES جيد جدًا. بصرف النظر عن الأجهزة الطرفية غير العادية التي تقاوم المحاكاة (على سبيل المثال ، نادي غولف مع مستشعر ضوئي ، ومحاكاة دراجات ومودم للاتصال الهاتفي ، يستخدم في اليابان لمراهنة سباق الخيل في اليابان) ، فإن جميع ألعاب SNES المرخصة رسميًا قابلة للعب بالكامل ، ولا توجد لعبة لديها مشاكل واضحة.

أصبح مضاهاة SNES دقيقًا جدًا لدرجة أنني اضطررت حتى إلى تقسيم المحاكي إلى نسختين: higan ، التي تسعى إلى الدقة المطلقة والاتساق مع وثائق الأجهزة ، و bsnes ، التي تسعى إلى السرعة والقدرات الواسعة وسهولة الاستخدام.

في الآونة الأخيرة ، في مجال محاكاة SNES ، تم تلقي العديد من الإنجازات المثيرة للاهتمام ، بما في ذلك:


… وأكثر بكثير!

لذا ، هل انتهى؟ هل عمل الجميع بشكل جيد وداعا وشكرا للسمك؟ كذلك ليس تماما.

اليوم ، حققنا الدقة على مستوى الضرب لجميع مكونات SNES تقريبًا. الاستثناء الوحيد هو PPU (وحدة معالجة الصور ، وحدات معالجة الصور) المستخدمة لإنشاء إطارات الفيديو المرسلة إلى الشاشة. نحن نعلم في الغالب كيف تعمل وحدات PPU ، ولكن بالنسبة لبعض الوظائف علينا استخدام التخمين ، مما يؤدي إلى دقة غير كاملة.

على نطاق عام ، تبقى المشاكل المتبقية بسيطة للغاية. إذا كنت لا تسعى إلى المثالية المثالية تمامًا لمحاكاة حب الفن ، فلا يمكنني إقناعك بالحاجة إلى تحسين محاكاة PPU. كما هو الحال في أي مجال ، كلما اقتربنا من المثالية ، كلما انخفض العائد.

ولكن أستطيع أن أقول لماذا هذا هو المهم لي : هذا هو عمل حياتي كلها، وأنا لا أريد لي أن أقول إنني حصلت حتى أوشكت على الانتهاء دون اتخاذ الخطوة الأخيرة. أنا أتقدم في السن وأنا لست أبدية. أريد أن يتم حل آخر جزء من اللغز ، لذلك ، بعد تقاعدي ، كنت على يقين من أن إرث SNES موثوق به ومحفوظ بالكامل بفضل المحاكاة. أريد أن أقول أنه تم حل المشكلة .

إذا كنت لا تزال مفتونًا ، فتابع القراءة للتعرف على خلفية المشكلة والحلول التي قدمتها.

نمذجة العمارة SNES


لنبدأ بسرد المكونات التي يتكون منها SNES:


مخطط نظام سوبر NES.

تشير الأسهم إلى الاتجاهات التي يمكن فيها لمعالجات SNES المختلفة تبادل البيانات مع بعضها البعض ، وتشير الخطوط المنقطة إلى الاتصالات بشرائح الذاكرة.

أهم شيء بالنسبة لنا الآن هو ملاحظة أن إخراج الفيديو والصوت ينتقل مباشرة من PPU و DSP. هذا يعني أنها تعمل "كصناديق سوداء" ، ولا يمكننا أن نرى ما يحدث داخلها. في وقت لاحق سوف تصبح مهمة بالنسبة لنا.

صحة


تخيل أننا نقوم بمحاكاة أمر وحدة المعالجة المركزية "مضاعف" ، الذي يأخذ تسجيلين (متغيرات) ، ويضاعفهم ، ويتلقى النتيجة والعديد من العلامات التي تشير إلى حالة النتيجة (على سبيل المثال ، تجاوز ).

يمكننا كتابة برنامج يضاعف أي قيمة محتملة من 0 إلى 255 كعامل ومضاعف. ثم يمكننا استخلاص نتائج الضرب العددي والعلم. وهكذا ، نحصل على جدولين من 65 536 عنصر.

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

الآن دعنا نقول أن وحدة المعالجة المركزية يمكن أن تفعل الضرب 16 بت × 16 بت. عند اختبار كل قيمة ممكنة ، سيتم إنشاء 4 مليار نتيجة ، والتي يكاد يكون من المستحيل اختبارها في فترة زمنية معقولة. إذا كانت وحدة المعالجة المركزية تحتوي على مضاعفات 32 بت × 32 بت ، فلن يكون من الممكن عمليًا اختبار جميع تركيبات قيم الإدخال قبل الموت الحراري للكون (على الأقل على المستوى الحالي للتكنولوجيا).

في مثل هذه الحالات ، نتصرف بشكل أكثر انتقائية في الاختبارات ونحاول تحديد متى يمكن أن تتغير العلامات تمامًا ، ومتى يمكن أن تتدفق النتائج ، وما إلى ذلك. خلاف ذلك ، سيكون علينا إجراء اختبارات لن تنتهي أبدًا.

الضرب عملية تافهة إلى حد ما ، ولكن يمكن توسيع المبدأ نفسه ليشمل عملية الهندسة العكسية بأكملها ، بما في ذلك العمليات الأكثر تعقيدًا ، على سبيل المثال ، نقل البيانات عبر DMA (الوصول المباشر للذاكرة) أثناء العودة الأفقية للحزمة. ننشئ اختبارات تحاول تحديد ما يحدث في الحالات الحدودية ، ثم نتحقق مما إذا كان مضاهاة لدينا تتصرف بشكل مماثل لسلوك SNES الحقيقي.

مولدات الإشارات والإيقاعات


يحتوي SNES على مولدين للإشارة (المذبذبات): مذبذب بلوري يعمل بتردد حوالي 21 ميجاهرتز (يتحكم في وحدات CPU و PPU) ، ومرنان خزفي يعمل على تردد حوالي 24 ميجاهرتز ، والذي يتحكم في SMP و DSP. في المعالجات المشتركة للخرطوشة ، يتم في بعض الأحيان استخدام مذبذب بلوري 21 ميجاهرتز ، وأحيانًا تعمل مولدات الإشارة الخاصة به مع ترددات أخرى.


إعادة إنشاء لوحة الدوائر Super Famicom في التعليمات البرمجية أصعب مما تبدو عليه.

الساعة هي العنصر الأساسي لتوقيت أي نظام ، وقد تم تصميم SNES لأداء مهام مختلفة بترددات معينة وفترات زمنية معينة.

إذا كنت تتخيل ساعة 100 هرتز ، فسيكون جهازًا ذا إخراج ثنائي يتحول إلى حالة منطقية عالية للإشارة (على سبيل المثال ، +5 فولت) ، ثم إلى حالة منخفضة للإشارة (0 فولت أو أرضي) 100 مرة في الثانية. أي أن الجهد عند الخرج سيتقلب كل ثانية 200 مرة: يزداد 100 مرة و 100 مرة يخفض مقدمة إشارة الساعة.

تعتبر دورة الساعة عادة انتقالًا كاملاً ، أي أن دورة 100 هرتز ستولد 100 دورة ساعة في الثانية. تتطلب بعض الأنظمة تمييزًا بين الحواف الصاعدة والهابطة ، ونقوم بتقسيم الدورة إلى نصف دورة للإشارة إلى كل مرحلة (عالية أو منخفضة) من إشارة الساعة.

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

توقيت


في بعض الأحيان تستغرق العمليات وقتًا. خذ على سبيل المثال الضرب في SNES CPU. بدلاً من الإيقاف المؤقت وانتظار اكتمال الضرب ، تقوم وحدة المعالجة المركزية SNES بحساب نتيجة الضرب بتة واحدة في الخلفية في ثماني دورات على مدار الساعة من رموز وحدة المعالجة المركزية. من المحتمل أن يسمح هذا للتعليمات البرمجية بأداء مهام أخرى أثناء انتظار اكتمال الضرب.

على الأرجح ، سوف ينتظر أي برنامج تجاري هذه الدورات الثماني ، لأنه إذا حاولت قراءة النتيجة قبل أن تصبح جاهزة ، فسوف نحصل على نتيجة مكتملة جزئيًا. ومع ذلك ، قبل أن تعطي محاكيات SNES نتائج صحيحة على الفور ، دون انتظار دورات الساعة الإضافية هذه.

عندما بدأ معجبو وحدات التحكم في إنشاء واختبار برامج مكتوبة ذاتيًا في المحاكيات ، بدأ هذا التناقض يسبب مشاكل معينة. جزء من البرنامج ، على سبيل المثال ، عمل العديد من أول قرصنة Super Mario World ROM بشكل صحيح فقط في هذه المحاكيات القديمة ، ولكن ليس على أجهزة SNES الحقيقية. حدث هذا لأنهم تم تطويرهم مع الأخذ بعين الاعتبار الحصول الفوري (غير الموثوق من وجهة نظر المعدات الحقيقية) على نتائج الضرب.

في عملية تحسين المحاكيات ، تم كسر توافق البرامج القديمة ، وبالتالي كان علينا إضافة خيارات التوافق إلى المحاكيات الجديدة حتى لا نفقد هذه البرامج. نعم ، بغض النظر عن مدى سرياليتها ، ولكن يجب أن تحاكي المحاكيات اليوم محاكاة أخرى!

راحة هذا التأخير في الضرب في وحدة المعالجة المركزية هو أنه يمكن التنبؤ به للغاية: تبدأ ثماني ساعات من العمليات الحسابية على الفور بعد طلب عملية الضرب. من خلال كتابة التعليمات البرمجية التي تقرأ النتائج بعد كل دورة ، تمكنا من التحقق من أن وحدة المعالجة المركزية SNES تستخدم خوارزمية Booth للضرب .

مزامنة الساعة


ليس من السهل تصميم العمليات الأخرى لأنه يتم تنفيذها بشكل غير متزامن في الخلفية. إحدى هذه الحالات هي تحديث DRAM لمعالج SNES المركزي.

أثناء عرض كل خط نقطي ، تقوم وحدة المعالجة المركزية (SNES) بأكملها في مرحلة معينة بتعليق تشغيلها لفترة قصيرة من الوقت بينما يتم تحديث محتويات شريحة ذاكرة الوصول العشوائي. يعد ذلك ضروريًا لأنه من أجل تقليل التكلفة في SNES ، تم استخدام ذاكرة الوصول العشوائي الديناميكية (بدلاً من الثابتة) كذاكرة رئيسية لوحدة المعالجة المركزية. لحفظ محتويات ذاكرة الوصول العشوائي الديناميكية ، يجب تحديثها بشكل دوري.


إن إنشاء محاكي مثالي حقًا لا يكفي لضمان إمكانية تشغيل جميع ألعاب SNES الثلاثة والنصف. من الضروري أيضًا تحقيق محاكاة لكل وظيفة من وظائف النظام بدقة مثالية لللباقة.

كان العامل الرئيسي في تحليل التوقيت الدقيق لهذه العمليات هو إمكانية استخدام عدادات PPU الأفقية والرأسية. تؤدي هذه العدادات زيادات ويتم إعادة تعيينها بعد كل انتقال عمودي أفقي ورأسي عكسي. ومع ذلك ، فإن دقتها ليست سوى ربع تردد مولد إشارة وحدة المعالجة المركزية SNES ؛ بمعنى آخر ، يزداد العداد الأفقي كل أربع دورات على مدار الساعة.

بعد قراءة قيم العدادات عدة مرات ، تمكنت من تحديد ربع دورة الساعة التي يتماشى معها العداد. من خلال الجمع بين هذه المعرفة والوظائف التي تم إنشاؤها خصيصًا والتي يمكنها اتخاذ خطوة نحو العدد الدقيق لدورات الساعة التي أشار إليها المستخدم ، تمكنت من مطابقة وحدة المعالجة المركزية (SNES) تمامًا مع أي موضع محدد لدورة الساعة التي أحتاجها.

بفضل اجتياز تكراري للعديد من دورات الساعة ، تمكنت من تحديد متى تحدث بعض العمليات بالضبط (على سبيل المثال ، تحديث DRAM ، وإرسال HDMA ، مقاطعة الاستطلاع ، إلخ). بعد ذلك ، يمكنني إعادة إنشاء كل هذا بالضبط في المحاكاة.

رقاقة SMPتحتوي وحدة تحكم SNES أيضًا على موقتات خاصة بها ، كما تم إجراء هندسة عكسية ناجحة لهذا المعالج. يمكنني تخصيص مقال كامل فقط لسجل اختبار SMP ، والذي يسمح للمبرمجين بالتحكم في مقسم تردد SMP ومؤقته ، ناهيك عن أشياء رهيبة أخرى. سيكون كافياً القول أنها لم تكن عملية سهلة وسريعة ، ولكن في النهاية فزنا.

نقوم بجمع المعالجات المشتركة



شريحة SuperFX هي مجرد واحدة من العديد من المعالجات المشتركة للخرطوشة التي يمكن لمحاكي SNES التعامل معها.

هناك مجموعة كاملة من المعالجات المشتركة SNES المستخدمة داخل خراطيش الألعاب المختلفة التي كنا بحاجة أيضًا إلى ترويضها. من وحدات المعالجة المركزية الفردية للأغراض العامة مثل SuperFX و SA-1 ، ومعالجات الإشارات الرقمية مثل DSP-1 و Cx4 إلى مسرعات الضغط مثل S-DD1 و SPC7110 ، أو ساعات Sharp و Epson في الوقت الفعلي ، وغير ذلك الكثير ...

هذا يعني أنه يجب على محاكي SNES التعامل مع تعليمات SuperFX وذاكرة التخزين المؤقت للبكسل ؛ مع نظام حل تعارضات ناقل الذاكرة SA-1 (مما يسمح لوحدات المعالجة المركزية SNES و SA-1 باستخدام نفس رقائق ROM و RAM في نفس الوقت) ؛ مع البرامج الثابتة المتكاملة DSP-1 و Cx4 ؛ مع أجهزة التشفير الحسابية القائمة على التنبؤ S-DD1 و SPC7110 ؛ وكذلك مع حالات الحدود الحدودية الفردية لـ BCD (العشرية ذات الترميز الثنائي) في المولدات في الوقت الفعلي. ببطء ولكن بثبات ، باستخدام جميع التقنيات لتحديد الدقة والتوقيت الموصوف أعلاه ، تمكنا من معرفة كيفية محاكاة كل هذه الرقائق بشكل مثالي تقريبًا.

استغرق الأمر الكثير من الجهد وآلاف الدولارات لإزالة أغطية الشرائح وإزالة البرامج الثابتة من معالجات الإشارات الرقمية المستخدمة في الألعاب المختلفة. في حالة واحدة ، يُسمح بمضاهاة NEC uPD772xاستخدم الكود من هيجان لحفظ صوت الراحل ستيفين هوكينج! .

في حالة أخرى ، كنا بحاجة إلى إجراء هندسة عكسية لمجموعة كاملة من التعليمات الخاصة بهيكل هيتاشي HG51B ، لأنه لم ينشر أحد وثائق هذه البنية. في حالة أخرى ، اتضح أن لعبة واحدة ( Hayazashi Nidan Morita Shougi 2 ) لديها وحدة معالجة مركزية قوية ARM6 32 بت بتردد 21 ميجاهرتز ، مما يسرع لعبة shogi اليابانية!

تبين أن مجرد إنقاذ جميع معالجات SNES المشتركة هي عملية طويلة الأجل ، مليئة بالصعوبات والمفاجآت.

معالجة الإشاراة الرقمية


أنتجت شريحة سوني S-DSP (معالج الإشارة الرقمية) ، التي يجب عدم الخلط بينها وبين معالج خرطوشة DSP-1 ، صوت SNES فريد. في هذه الشريحة ، تم توصيل ثماني قنوات صوتية بترميز ADPCM 4 بت ، مما يضمن إنشاء إشارة استريو 16 بت.

ظاهريًا ، ومن مخطط النظام المعروض أعلاه ، في البداية يبدو أن DSP هو "صندوق أسود": نقوم بتعديل قنوات الصوت ومعلمات الخلاط ، وبعد ذلك تولد الشريحة صوتًا يتم نقله إلى السماعات.

لكن إحدى الوظائف المهمة سمحت للمطور تحت الاسم المستعار blargg بأداء هندسة عكسية كاملة لهذه الشريحة: كانت صدى مؤقت. يحتوي SNES DSP على وظيفة تمزج الإخراج من العينات السابقة لإنشاء تأثير صدى. يحدث هذا في نهاية عملية توليد الصوت (بصرف النظر عن إشارة حظر الصوت الأخيرة ، والتي يمكن استخدامها لإيقاف إخراج الصوت بالكامل.) من

خلال كتابة التعليمات البرمجية مع التوقيت الصحيح للقياسات وتتبع الصدى الناتج ، تمكنا من تحديد الترتيب الدقيق للعمليات التي نفذها DSP لإنشاء لكل عينة وخلق صوت مثالي ودقة.

حفظ PPU


كل هذا قادنا إلى الجزء الأخير من مخطط SNES المعماري: رقائق PPU-1 و PPU-2. بفضل John McMaster ، لدينا عمليات مسح لرقائق S-PPU1 (المراجعة 1) و S-PPU2 (المراجعة 3) بزيادة عشرين مرة.


مسح عشرين مرة للكريستال لأول PPU SNES ...


... ووحدة PPU الثانية.

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

تظل PPUs هي العقبة الأخيرة لمحاكاة SNES لأنه على عكس جميع المكونات الموضحة أعلاه ، فإن PPUs هي في الواقع صندوق أسود. يمكننا تكوينها على أي حالة ، ولكن لا تستطيع وحدة المعالجة المركزية SNES مراقبة ما تقوم بإنشائه بشكل مباشر.

إذا استخدمنا مثالنا السابق مع الضرب كقياس ، فتخيل أنك طلبت النتيجة 3 * 7 ، ولكن بدلاً من الإجابة الثنائية ، ستحصل على صورة تناظرية غامضة للأرقام "21" على الشاشة. سيتمكن أي شخص يقوم بتشغيل برنامجك من رؤية 21 ، ولكن لا يمكنك كتابة برنامج اختبار للتحقق تلقائيًا مما إذا كان يرى الإجابة الصحيحة. لا يمكن تحجيم التحقق اليدوي للشخص لمثل هذه النتائج إلى أكثر من عدة آلاف من الاختبارات ، وسيتطلب الملايين لزيادة سلوك PPU إلى أقصى حد.

أعرف ما كنت تعتقده: "ولكن هل من الأسهل استخدام بطاقة التقاط ، وإجراء معالجة الصور ، ومقارنتها تقريبًا بالصورة على الشاشة الرقمية للمحاكي ، وإجراء الاختبارات بناءً على ذلك؟"

حسنًا نعم ، هذا ممكن! خاصة إذا كان الاختبار هو التحقق من رقمين ضخمين يشغلان الشاشة بأكملها.

ولكن ماذا لو كان للاختبار العديد من الفروق الدقيقة ، ونحن نحاول التعرف على اختلاف اللون في نصف نغمة بكسل واحد؟ ماذا لو أردنا إجراء مليون اختبار بالترتيب ، ولا نعرف دائمًا ما سننشئه ، ولكننا ما زلنا نريد مقارنة النتيجة مع ناتج مضاهاة؟

لا شيء يضاهي الراحة والدقة مع البيانات الرقمية - دفق دقيق من البتات التي يمكن أن تتطابق فقط أو لا تتطابق. لا يمكن أن تزودنا الطبيعة التماثلية لإشارة CRT بذلك.

لماذا هو مهم؟


باستثناء لعبة واحدة ( Air Strike Patrol ) ، كان من المفترض أن تكون جميع برامج SNES المرخصة رسميًا بناءً على سلاسل البيانات النقطية. لا تحاول هذه الألعاب تغيير حالة عرض PPU في منتصف خط البيانات النقطية الحالي (مثل هذه الخدعة من قبل المبرمجين تسمى "تأثير البيانات النقطية"). وهذا يعني أن مواعيد تنفيذ الغالبية العظمى من الألعاب لا يجب أن تكون دقيقة بشكل خاص ؛ إذا كان لديك وقت للخط النقطي الكامل التالي ، فكل شيء في محله.

لكن هذا مهم للعبة واحدة.




تظهر هذه السلسلة من الصور تأثير محاكاة معقد مستخدم في رسالة "الحظ السعيد" من Air Strike Patrol .

في الصور أعلاه ، سترى النص الجيد بإطار "حسن الحظ" من Air Strike Patrol . تقوم اللعبة بتطبيقها عن طريق تغيير موضع التمرير الرأسي لطبقة الخلفية 3 (BG3). ومع ذلك ، فإن لوحة العدادات الموجودة على اليسار (حيث يمكنك رؤية أن المشغل لديه 39 صاروخًا) موجودة أيضًا على نفس طبقة الخلفية.

تمكنت اللعبة من تنفيذ هذا الفصل عن طريق تغيير موضع التمرير BG3 في كل خط نقطي بعد عرض لوحة القيادة اليسرى ، ولكن قبل أن يبدأ عرض النص "حظ سعيد". يمكن القيام بذلك لأنه خارج لوحة المعلومات والنص ، يكون BG3 شفافًا ولا يوجد شيء للرسم بين هاتين النقطتين ، بغض النظر عن قيمة سجل التمرير العمودي. يوضح لنا هذا السلوك أنه يمكن تغيير سجلات التمرير في أي مرحلة من مراحل العرض.


تسبب هذا الظل الصغير تحت الطائرة في مجموعة من الصداع لمطور المحاكي المهووس بدقة.

تُظهر الصورة أعلاه الظل السيئ للطائرة. يتم تقديم هذا التأثير عن طريق تغيير سجل سطوع الشاشة مع تموجات قصيرة على خمسة خطوط نقطية.

خلال اللعبة ، يمكنك أن ترى أن هذا الظل فوضوي إلى حد ما. في الصورة أعلاه ، يبدو قليلاً مثل الحرف "c" ، لكن شكله في كل خط نقطي يتغير في الطول ونقطة البداية مع كل إطار. حدد مطورو Air Strike Patrol تقريبًا المكان الذي يجب أن يظهر فيه الظل ، وحلوا هذه المشكلة مباشرة. في معظم الحالات يعمل هذا.

يتطلب المحاكاة الصحيحة لمثل هذا السلوك توقيتًا مثاليًا ، وهو أمر صعب للغاية في الحصول عليه في المحاكي .


على شاشة توقف Air Strike Patrol مؤقتًا ، يتم استخدام التأثيرات النقطية التي لم يتم استخدامها عن قصد في أي لعبة SNES أخرى.

الآن لنتحدث عن شاشة الإيقاف المؤقت. يتم تشغيل BG3 أثناء رسم حد أصفر-أسود على اليسار وإيقافه مرة أخرى أثناء نفس الحد على اليمين لرسم خطوط رمادية على الشاشة. قام أيضًا بالتناوب من خلال الإطار بتبديل الخطوط النقطية التي يتم فيها عرض هذه الخطوط الرمادية لإنشاء تأثير ارتعاش التراكب.

إذا قمت بتكبير الصورة المحذوفة الموضحة أعلاه ، فستلاحظ أنه خلال زوج الخطوط النقطية في الزاوية اليسرى من هذه الخطوط الرمادية ، هناك العديد من وحدات البكسل المفقودة. حدث ذلك لأن مضاهاة PPU الخاصة بي غير كاملة 100٪ في دورات الساعة. في هذه الحالة ، يسبب تأثير تمكين BG3 في وقت متأخر قليلاً مما ينبغي.

يمكنني بسهولة تغيير التوقيت بحيث يتم عرض هذه الصورة بشكل صحيح. ولكن من المرجح أن يؤثر هذا التغيير سلبًا على الألعاب الأخرى التي تغير تسجيلات PPU في منتصف خط البيانات النقطية. على الرغم من أن Air Strike Patrol هي اللعبة الوحيدة التي تفعل ذلك عن قصد ، فهناك ما لا يقل عن اثنتي عشرة لعبة يحدث فيها ذلك عن طريق الصدفة (ربما تطلق IRQ فيها عاجلاً أو آجلاً).

في بعض الأحيان يتسبب هذا في حدوث تلف ملحوظ وجيز في الصورة ، والذي لا يتم الانتباه إليه أثناء التطوير (على سبيل المثال ، في Full Throttle Racingأثناء الانتقال بين المتجر واللعبة). في بعض الأحيان يتم التسجيل بينما تكون الشاشة شفافة ، وبالتالي لا يسبب تشوهات بصرية (على سبيل المثال ، كما هو الحال في حالة عرض حالة HP في Dai Kaijuu Monogatari II .) ولكن حتى هذه الحالات الحدودية "غير المرئية" يمكن أن تسبب مشاكل في عرض خطوط نقطية أقل دقة التي تستخدم في المحاكيات الأكثر إنتاجية.

حتى إذا كنت تتجاهل Air Strike Patrol ، فإن جميع هذه التأثيرات النقطية العشوائية (ولكنها صالحة) في برنامج SNES لا تسمح لك بتصميم وظيفي لجهاز عرض PPU الذي يولد خط البيانات النقطية بالكامل بدقة ساعة مثالية.

في حالة bsnes على مدى سنوات التجربة والخطأ ، قمنا بإنشاء قائمة بهذه الألعاب ذات "تأثيرات نقطية". لقد أنشأنا أيضًا مواقع عرض فردية تتيح عرضًا أسرع بكثير استنادًا إلى الخطوط النقطية لعرض جميع هذه الألعاب بشكل صحيح (باستثناء Air Strike Patrol بالطبع). ولكن في الجوهر ، هذه مجموعة من الاختراقات غير السارة بالنسبة لنا ، والمصممة لألعاب محددة.

لدي أيضًا عارض PPU قائم على مدار الساعة لا يحتاج إلى كل هذه الاختراقات ، ولكن من وقت لآخر يخلق اختلافات صغيرة (من واحد إلى أربعة بكسل) مع عرض هذا الجهاز ، كما هو الحال في لقطة الشاشة أعلاه من Air Strike Patrol .

سجلات المزلاج الداخلية


يعود سبب كل هذه الأخطاء الصغيرة إلى توقيت اللقطات.

لنفترض أن SNES تعرض الوضع 7 الشهير ، وهو تحويل نسيج أفقي مع تغييرات في المعلمات في كل خط نقطي. لتحديد أي بكسل شاشة ، تحتاج إلى إجراء حسابات مماثلة:

بكسل = مقطع * (hoffset - hcenter) + مقطع b * (voffset - vcenter) +
ب * ص + (hcenter << 8)

py = c * clip (hoffset - hcenter) + d * clip (voffset - vcenter) +
d * y + (vcenter << 8)

لن يتمكن SNES الحقيقي من إكمال كل هذه المضاعفات الستة بسرعة كافية لكل بكسل يتم عرضه في الإطار. لكن لا تتغير أي من هذه القيم لكل بكسل (أو ، على الأقل ، لا يجب أن تتغير) ، لذلك نحتاج فقط إلى حساب px و py مرة واحدة في بداية كل خط نقطي. أي أن PPU يخزن نتائج ثابتة في المزالج ، وهي في الأساس نسخ من سجلات PPU. في المستقبل ، يمكن تحويلها ، أو البقاء دون تغيير.

ثم يتم تحويل إحداثيات س ، ص بواسطة الوضع 7 على النحو التالي:

ox = (px + a * x) >> 8

oy = (py + c * x) >> 8

على الرغم من أن x تختلف لكل بكسل ، إلا أننا نعلم أن الزيادة يتم تنفيذها بمقدار بكسل واحد في كل مرة. بفضل تخزين محركات الأقراص الداخلية ، يمكننا ببساطة إضافة قيم ثابتة a و c إلى ox و oy لكل بكسل ، بدلاً من إجراء مضاعفتين لكل بكسل.

ثم يطرح السؤال المعروض علينا: في أي موضع معين من دورة الساعة يقرأ PPU قيم a و c من تسجيلات PPU الخارجية التي يمكن لوحدة المعالجة المركزية الوصول إليها؟

إذا أخذناها مبكرًا جدًا ، فقد يؤدي ذلك إلى كسر بعض الألعاب. إذا أخذناها في وقت متأخر ، فيمكنها كسر ألعاب أخرى.

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

وفي كل مرة نقوم فيها بتغيير أحد هذه المتغيرات ، من غير الواقعي بالنسبة لنا إعادة اختبار جميع الألعاب الثلاثة ونصف ألف من مكتبة SNES لاكتشاف التدهور الذي يمكن أن تحدثه التغييرات.

من المقلاة الى النار



التفسير الفني لعملية القضاء على أخطاء المحاكاة.

أسلوب مماثل في منهجية الاختبار ، "سنجعل اللعبة التي نهتم بالعمل بها بأي ثمن" أدت إلى هذه الظاهرة ، التي أسميها محاكاة "من النار ، ولكن في النار".

في بداية تطوير محاكاة SNES ، عندما نشأت مشاكل في اللعبة ، تم قبول أي تصحيح في هذه اللعبة سمح لها بالعمل وإضافته إلى المحاكي. كسر هذا الإصلاح بالضرورة لعبة أخرى. ثم قاموا بتصحيح هذه اللعبة ، وبعد ذلك اندلعت الثالثة. إصلاح المباراة الثالثة كسر مرة أخرى الأولى. استمر هذا لسنوات عديدة.

كان الخطأ هنا هو أن المطورين حاولوا مراعاة متغير واحد فقط في كل مرة. لنفترض أن لدينا لعبة ، ولكي تنجح ، يجب أن تقع الأحداث بين القياسات 20 و 120. لا نعرف المقياس الدقيق ، لذا اختر 70 فقط ، بالضبط في المنتصف.

في وقت لاحق نحصل على تقرير خطأ في لعبة أخرى ، ونحدد أنه لكي تعمل هذه اللعبة ، يجب أن تكون قيمة القياس بين 10 و 60. لذلك نقوم الآن بتغييرها إلى 40 ، والتي تعمل في كلتا المباراتين. تبدو منطقية!

ولكن بعد ذلك تظهر المباراة الثالثة ، حيث يجب أن يعمل الحدث بين القياسات 80 و 160! لن نتمكن الآن من جعل الألعاب الثلاث تعمل في نفس الوقت بنفس القيمة.

أجبر هذا مطوري المحاكي على إنشاء اختراق لألعاب معينة. المبرمجون لا تريد الافراج عن مقلدا التي لا يمكنك تشغيل ماريو ، زيلدا أو ميترويد . لذلك ، في الحالة العامة ، يتم استخدام دورة الساعة 40 ، ولكن عند تحميل Metroid ، فإننا نفرض قيمة التوقيت على 100.

كيف يكون ذلك ممكنًا ، لماذا تحتاج مباراتان إلى قيم مختلفة؟ يحدث هذا لأنه لا يوجد هنا متغير واحد فقط. قد يؤثر التوقيت الذي استخدمته سابقًا لتشغيل حدث آخر على قيمة التوقيت المطلوبة للحدث التالي .

تخيل هذا في شكل تعبير جبري بسيط:

2 س + ص = 120

يمكنك حلها بأخذ x = 10 ، y = 100. أو س = 20 ، ص = 80. أو س = 30 ، ص = 60. إذا كنا نفكر فقط في قيمة x ، والتي تسمح لك بتشغيل مجموعة واحدة من الألعاب في نفس الوقت ، فإننا نفتقد حقيقة أن المشكلة قد تكون في y الخاطئ!

تعمل الإصدارات الأولى من المحاكيات لزيادة التوافق على إعادة تعريف قيمة x اعتمادًا على لعبة الجري. استمرت هذه الاختراقات الفردية في اللعبة ، حتى لو تم اكتشاف القيمة الفردية الصحيحة الصحيحة لـ x لاحقًا. لذلك لن يتم حل مشكلة y !

ومع ذلك ، في حالة SNES ، لا يتم تضمين متغير واحد أو متغيرين في وقت واحد. تحتوي وحدة تحكم PPES لوحدة SNES وحدها على 52 تسجيل خارجي ، وهو ما يقرب من 130 معلمة. في عملية تقديم خط نقطي واحد ، يتم تضمين جميع هذه المعلمات البالغ عددها 130 وعددًا غير معروف من السجلات الداخلية والمزالج. هذه معلومات كثيرة جدًا لشخص خارجها ليتمكن من تحقيق الحالة الكاملة لـ PPU في وقت معين.

هذا الجانب من المحاكاة ليس واضحًا للمبتدئين ، ولكنه عادل جدًا: الدقة ليست مساوية للتوافق. يمكننا إنشاء محاكي بدقة 99 بالمائة ، وقادرة على تشغيل 10٪ من الألعاب. ويمكنك كتابة محاكي دقيق بنسبة 80٪ يقوم بتشغيل 98٪ من الألعاب. في بعض الأحيان يكسر التنفيذ الصحيح على المدى القصير الألعاب الشعبية. هذا هو التضحية ضرورية إذا كنت تحاول تحقيق كل 100٪ من الدقة و التوافق بنسبة 100٪.

حل المشكلة


لقد وصلنا إلى المرحلة الحالية من محاكاة PPU بفضل الاستدلال الاستنتاجي والنتائج في العالم الحقيقي.

نحن نعلم أن وحدتي PPU يمكنهما الوصول إلى شريحتين VRAM. نحن نعلم أنه يمكنهم قراءة عدد معروف من وحدات بايت البيانات لكل سطر نقطي من كل شريحة. نحن نعرف التفاصيل التقريبية لكيفية عمل كل من أوضاع الفيديو SNES. وبناءً على ذلك ، يمكننا تحديد نمط عام لكيفية ظهور العمارة. على سبيل المثال ، فيما يلي مثال موجز لكيفية عمل أوضاع الفيديو الثلاثة الأولى لـ SNES:

if (io.bgMode == 0) {

bg4.fetchNameTable () ؛

bg3.fetchNameTable () ؛

bg2.fetchNameTable () ؛

bg1.fetchNameTable () ؛

bg4.fetchCharacter (0) ؛

bg3.fetchCharacter (0) ؛

bg2.fetchCharacter (0) ؛

bg1.fetchCharacter (0) ؛

}}

if (io.bgMode == 1) {

bg3.fetchNameTable () ؛

bg2.fetchNameTable () ؛

bg1.fetchNameTable () ؛

bg3.fetchCharacter (0) ؛

bg2.fetchCharacter (0) ؛

bg2.fetchCharacter (1) ؛

bg1.fetchCharacter (0) ؛

bg1.fetchCharacter (1) ؛

}}

if (io.bgMode == 2) {

bg2.fetchNameTable () ؛

bg1.fetchNameTable () ؛

bg3.fetchOffset(0);

bg3.fetchOffset(8);

bg2.fetchCharacter(0);

bg2.fetchCharacter(1);

bg1.fetchCharacter(0);

bg1.fetchCharacter(1);

}


يكشف PPU لمراقب طرف ثالث فقط جزء صغير من حالته: أعلام الطمس الأفقي / الرأسي ، عدد البكسل الأفقي والرأسي ، وأعلام تراكب البلاط في الفاصل الزمني للعفاريت. هذا ليس كثيرًا ، لكني أكرر - كل عنصر صغير في الدولة يمكن للمراقب الوصول إليه يساعدنا.

يتم إغلاق VRAM (ذاكرة الفيديو ، ذاكرة الفيديو) لشريحة PPU أثناء العرض على وحدات المعالجة المركزية SNES ، حتى للقراءة. ولكن كما اتضح ، فإن OAM (ذاكرة sprite) و CGRAM (ذاكرة اللوحة) مفتوحة. الحيلة هي أنه في هذا الوقت ، تتحكم PPU في ناقل العنوان. لذلك ، عند قراءة OAM و CGRAM أثناء عرض الشاشة ، يمكنني ملاحظة ما تحصل عليه PPU من كتالي الذاكرة هذين في مثل هذا الوقت الحرج.

هذه ليست كل أجزاء اللغز ، لكنها كافية بالنسبة لي لأتمكن من تنفيذ الأنماط الصحيحة عمليًا للحصول على النقوش المتحركة.

باستخدام أنماط الوصول المفتوحة لـ OAM و CGRAM ، وأعلام PPU ، والملاحظات العامة (أي التخمينات) من تقارير الأخطاء للألعاب المختلفة ، والاستدلال الاستنتاجي ، تمكنا من إنشاء أجهزة عرض PPU على مدار الساعة والتي يمكنها إطلاق جميع الألعاب التي تم إصدارها بشكل مثالي تقريبًا .

لكن الوضع لا يزال غير مستقر: إذا بدأ شخص ما في إنشاء ألعاب البيرة المحلية باستخدام توقيت دقيق للقراد وتأثيرات البيانات النقطية ، فلن تتمكن جميع المحاكيات الحديثة لدينا من معالجة ذلك. بما في ذلك تطبيقات البرامج والأجهزة على أساس FPGA.

يجب أن أقول بوضوح: كل شيء اليومإنهم على دراية فقط بالترتيب الداخلي للعمليات والسلوك المفاجئ في شرائح PPU لوحدة تحكم SNES. لا أحد يعرف كيف يحاكيها بشكل مثالي. على الأقل لغاية الآن.

الحلول الممكنة


ماذا نفعل بهذا؟ كيف يمكن تحديد الترتيب الدقيق للعمليات في PPU إذا كانت ، من وجهة نظر SNES CPU ، عبارة عن "صندوق أسود"؟

أرى أربعة خيارات ممكنة: أجهزة تحليل المنطق ، وإخراج الفيديو الرقمي في وضع الاختبار ، والرفع وإزالة الأغطية من الرقائق.

محللات المنطق


إذا نظرت إلى عمليات مسح بلورات PPU الموضحة أعلاه ، فستلاحظ مناطق سوداء عند حواف الشريحة. هذه هي المنصات التي تتصل بجهات اتصال الرقائق.

تقوم هذه الدبابيس بتخزين حالة رقائق PPU خلال كل دورة ساعة. هنا يمكنك العثور على العنوان الحالي الذي تصل إليه الرقائق إلى شريحة ذاكرة الفيديو ، وقيم البيانات المنقولة من وحدة PPU إلى الثانية ، وأكثر من ذلك بكثير.

هذه المعلومات غير متاحة للتعليمات البرمجية التي تعمل على وحدة المعالجة المركزية SNES ، ولكنها توفر ملاحظات قيمة حول الترتيب الداخلي لعمليات PPU.


يمكن أن يكون توصيل وحدات PPU لوحدة تحكم Super NES بمحلل منطقي المفتاح إلى الصندوق الأسود.

إن المشكلة الحرجة التي تواجهها أجهزة تحليل المنطق هي أنها ليست مريحة للغاية في إدارتها: إذا حاولت أخذ عينات من البيانات الحية من نظام عمل ، فسوف نحصل على تيار من النتائج التي يصعب حلها. ستواجه نفس المشكلة إذا حاولت تحليل ناتج RGB التناظري للنظام: لالتقاط هذه البيانات ، سيكون عليك إجراء كل اختبار يدويًا. مثل هذا النظام ليس جيدًا جدًا لإنشاء اختبارات الانحدار الآلية القابلة للتكرار.

إخراج الفيديو الرقمي في وضع الاختبار


في الآونة الأخيرة ، من خلال مسح شرائح الكريستال مع تكبير 20x ، تم اكتشاف وضع اختبار سري في رقائق PPU لوحدة تحكم SNES. إذا قمت بإجراء تعديل صغير على الأجهزة ، فستبدأ وحدة PPU في إخراج إشارة RGB رقمية 15 بت !

هذا ما نحتاجه تقريبًا! ومع ذلك ، فإن هذا الوضع به مشاكل ، لأن الوضع الشهير 7 لا يمكنه عرض الصورة الصحيحة فيه. يبدو أن هذه الوظيفة لم تكتمل بالكامل.

بالإضافة إلى ذلك ، لتطبيق هذه الطريقة ، لا يزال هناك حاجة إلى تعديل يدوي لوحدات تحكم SNES وآلية مناسبة لالتقاط وتحليل الناتج في وضع الاختبار. ومع ذلك ، على عكس الحل مع التقاط إشارة RGB تناظرية ، يمكن اختبار هذه الإشارة الرقمية تلقائيًا ، مما يسمح لنا بإكمال كمية كبيرة من العمل بسرعة في الهندسة العكسية لـ PPU.

الناهضون


بالنظر إلى أن PPUs ثابتة ، يمكننا إزالة شرائح PPU من وحدة تحكم SNES العاملة وتوصيلها بلوحة نماذج أولية أو لوحة دوائر مخصصة مع شريحتين VRAM. بعد ذلك ، يمكنك وضع متحكم بين PPU وواجهة USB ، وتوصيل الواجهة بالكمبيوتر الشخصي ، مما سيسمح لبرنامج التشفير ببرمجة جميع سجلات ذاكرة الفيديو الخارجية ووحدات PPU. بالإضافة إلى ذلك ، سيكون المشفر قادرًا على التحكم يدويًا في دورات ساعة PPU وقراءة الإشارات الناتجة على موصلات الإدخال / الإخراج والتسجيلات وذاكرة PPU في كل دورة ساعة.

من خلال تعديل محاكي البرنامج بحيث يولد نفس القيم الداخلية لموصلات الإدخال / الإخراج ، يمكننا مقارنة الأجهزة الحقيقية مباشرة بالمحاكاة ، حتى في الوقت الفعلي. ومع ذلك ، سيكون هذا العمل شاقًا جدًا لأنه لا يمكننا حتى الآن رؤية عمليات PPU الداخلية.

إزالة الغطاء


أخيرًا ، الحل الأكثر تطرفاً هو مواصلة دراسة البلورة عن طريق إزالة غطاء الرقاقة. لدينا بالفعل عمليات مسح بلورية مع تكبير 20x ، لكن حلها لا يكفي لتحليل وإعادة إنشاء الدوائر المنطقية الفردية ، كما تم في مشروع Visual 6502 . إذا استطعنا الحصول على مسح بلورات كل من وحدات PPU بزيادة 100 ضعف ، فيمكننا البدء في العمل الشاق لتجميع دوائر PPU وتحويلها إلى جداول اتصال أو كود VHDL. ثم يمكن استخدامها مباشرة في FPGA ، وكذلك نقلها إلى لغة C ++ أو لغة برمجة أخرى ، قابلة للتطبيق لإنشاء برامج محاكاة للبرامج.

أعطاني أحد المتخصصين الذين فعلوا ذلك من قبل تقديرًا تقريبيًا: سيستغرق الأمر حوالي 600 ساعة لرسم خرائط كل من وحدات PPU. هذه المهمة أعلى بكثير من مستوى "دعنا نجمع الأموال عن طريق جمع الأموال ودفع أموال لشخص ما" ، وتقع بشكل مثالي في فئة "دعونا نأمل أن يرغب شخص موهوب جدًا بمجموعة فريدة من المهارات في مساعدتنا طواعية".

بالطبع ، هذا لا يعني أنني لن أكون سعيدًا بمكافأة شخص ما لمساعدته ، يمكنني الدفع مقابل التفاصيل والعمل الضروريين.

طلب المساعدة


للتلخيص: لقد ذهبت إلى أقصى حد ممكن في مشروع محاكي SNES الخاص بي ، وأحتاج إلى المساعدة لإكمال هذه المهمة النهائية. إذا كنت قد قرأت حتى النهاية ، فقد تحتاج إلى المساعدة! أي دعم ، بما في ذلك المشاركة في مشروع bsnes على GitHub ، أو أي وثائق بحثية حول العمل الداخلي لرقائق PPU ، سيثبت لنا أنه لا يقدر بثمن!

شكرا لقراءتك ودعمك! لقد كان شرفًا لي لمدة خمسة عشر عامًا أن أكون عضوًا في مجتمع محاكاة SNES.

All Articles