لقد قمت بترقية الكمبيوتر المحمول القديم باستخدام وحدتي ذاكرة DDR3-1333 بسعة 4 جيجا بايت ، ولكن اتضح أن الكمبيوتر المحمول متوافق مع DDR3-1066 بحد أقصى. ماذا سيفعل الرجل الحقيقي؟ بالطبع ، ستقوم بترقية EEPROM لإعادة ربط DDR3 إلى نموذج أبطأ!
مكان العمل. على اليمين يوجد Thinkpad للوميض ، وعلى اليسار هو جهاز MacBook Pro المثير للمشاكلكن حذرا للغاية. من الواضح أنه يمكنك إتلاف التسجيل على DIMM أو حظره بشكل دائم. المشاكل المحتملة الأكثر دقة ، بما في ذلك فشل في منطق البطارية ، أو ستتحول اللوحة الأم إلى لبنة .كيف بدأ كل شيء
لديّ جهاز MacBook Pro 13 بوصة منتصف 2010. تلف نظام الملفات الخاص به أثناء إعادة التشغيل المنتظم ، ولم تتمكن أداة القرص (من قسم الاسترداد) من القيام بأي شيء حيال ذلك. حسنًا ، لقد كنت أنتظر هذا لفترة طويلة: حان الوقت لوضع SSD وإضافة بعض ذاكرة الوصول العشوائي.
اشتريت قرصًا صلبًا (SSD) وكنت محظوظًا لأنني وجدت اثنين من أجهزة الكمبيوتر المحمولة المكسورة مع وحدات ذاكرة الوصول العشوائي المناسبة في جبل القمامة الإلكترونية. نقوم بإدخال SSD ووحدتين بسعة 4 جيجا بايت ، ونطلق استعادة الإنترنت - وفي غضون ساعة يجب أن يكون لدينا نظام عمل. لكن لا. تحميل يتجمد فقط. بسبب ماذا؟ سبب الشك الأكبر هو وحدات ذاكرة الوصول العشوائي هذه ، بعد كل شيء ، فهي من القمامة. لذلك ، نحن نفعل ما يفعله أي شخص: إنشاء محرك أقراص USB محمول مع memtest86 وتشغيله بين عشية وضحاها. عظيم ، الذاكرة على ما يرام. بعد عدة ساعات من تجربة طرق التثبيت المختلفة لإصدارات مختلفة من macOS ، ظهر الاكتشاف أخيرًا أن كل شيء يعمل بشكل جيد بمجرد إعادة إدخال الذاكرة القديمة.
1 السبب الحقيقي
عند إدراك المشكلة ، اكتشفت بسرعة أن جهاز
MacBook 2009-2010 لم يعمل بالفعل مع الذاكرة بشكل أسرع من PC3-8500 ، وأنه
يمكن التحايل على
المشكلة عن طريق تعديل بيانات تعريف ذاكرة الوصول العشوائي باستخدام برنامج Windows يسمى Thaiphoon Burner .
السبب الحقيقي للفشل هو معالج الجرافيكس المدمج GeForce 320M ، الذي يستخدم الذاكرة المشتركة ، أي ذاكرة الوصول العشوائي العادية. يمكن أن تعمل على الأكثر مع PC3-8500 (المعروف أيضًا باسم DDR3-1066 ، أي بتردد ساعة يبلغ 533 ميجاهرتز DRAM) ، ولكن وحدة تحكم ذاكرة النظام لا تعرف ذلك وتزيد السرعة القصوى المتاحة إلى 667 ميجاهرتز (أي PC3-10600 المعروف أيضًا باسم DDR3 -1333). المكونات المتبقية ليس لديها مشاكل مع هذا ، تمامًا مثل GPU في وضع VESA (على ما أظن).
لم أسمع عن أي معدات أخرى تفشل في تشغيل ذاكرة الوصول العشوائي
القادرة على سرعات أعلى من المعدات التي يمكن استخدامها. بالطبع ، عند شراء وحدات الذاكرة في السوق ، كان البائعون قد حذروا من هذا الفارق الدقيق. لا يزال هذا أفضل بكثير من ذاكرة الوصول العشوائي الملحومة ، كما هو الحال في أجهزة كمبيوتر Apple المحمولة منذ عام 2012.
إعداد البرامج الثابتة
بعد التعامل مع السبب ، قمت بتثبيت وحدة PC3-8500 أصلية واحدة على 2 غيغابايت ووحدة جديدة 4 غيغابايت ، وقد نجحت. لكن إعادة DDR3 تبدو كمشروع جيد ، لذلك قررت أن أجربها.
بالطبع ، لن أقوم بتثبيت Windows فقط من أجل البرامج الثابتة EEPROM ، ولن أشتري برنامجًا خياليًا ، إذا كان يمكن القيام بكل شيء يدويًا. اعتقدت أنه من الواضح أن المهمة يجب أن تتم على لينكس ، ربما مع بعض الأدوات الإضافية. كما أنني لم أرغب في تثبيت Linux على جهاز macbook لهذا الغرض فقط. لذلك ، أصبح جهاز Thinkpad X220 القديم الموثوق به مع
NixOS منصة مثالية للعمل. استغرق تحديثه بعض الوقت ، لأنني لم أحمل السيارة لمدة عام أو نحو ذلك.
ثم جاء الدور لاختيار الوحدة النمطية التي يجب تجربتها أولاً. يحتوي جهاز Thinkpad بالفعل على 4 غيغابايت لكل منهما ، ووجدت أربع وحدات بسعة 4 غيغابايت ، لذلك كان لدي الكثير للاختيار من بينها. قررت أن أبدأ بأغرب إنتاج ميكرون. جميع الباقي كانوا سامسونج. كان لدى أحدهم ملصق Lenovo.
اقرأ SPD
تأتي وحدات الذاكرة مع شريحة EEPROM التي تحتوي على بيانات وصفية حول وحدة
الكشف عن التواجد التسلسلي (SPD). التنسيق نفسه بسيط ، ويمكن ترتيب الوصول إلى EEPROM عبر
ناقل SMBus ، وهو في الأساس نفس
I²C .
2 لحسن الحظ ، هناك برامج تشغيل kernel وبرامج جاهزة للتفاعل مع SMBus وحتى قراءة EEPROM DDR3.
أولاً ، لعرض الأجهزة على الحافلة
i2c-tools
وبعض وحدات النواة مطلوبة.
$ nix-shell -p i2c-tools
$ modprobe i2c-dev
$ modprobe i2c-i801
$ i2cdetect -l
i2c-0 unknown i915 gmbus ssc N/A
i2c-1 unknown i915 gmbus vga N/A
i2c-2 unknown i915 gmbus panel N/A
i2c-3 unknown i915 gmbus dpc N/A
i2c-4 unknown i915 gmbus dpb N/A
i2c-5 unknown i915 gmbus dpd N/A
i2c-6 unknown DPDDC-B N/A
i2c-7 unknown DPDDC-C N/A
i2c-8 unknown DPDDC-D N/A
i2c-9 unknown SMBus I801 adapter at efa0 N/A
محول SMBus مهم هنا ، في حالتي
i2c-9
.
تأتي حزمة
i2c-tools
مع أداة
decode-dimms
لقراءة معلومات ذاكرة الوصول العشوائي بتنسيق قابل للقراءة. هذا يتطلب وحدة
eeprom
kernel.
$ modprobe eeprom
$ decode-dimms
$ modprobe -r eeprom
هنا هو الإخراج لوحدة ذاكرة واحدة:
كشف الوجود التسلسلي للذاكرة فك الشفرة
فيليب إيدلبروك ، كريستيان زوكشفيرت ، بوركارت لينغنر ،
جان ديلفار ، ترينت بيبهو وآخرون
فك تشفير EEPROM: / sys / bus / i2c / drivers / eeprom / 9-0050
تخمين dimm في البنك 1
--- === SPD EEPROM معلومات === ---
EEPROM CRC بايت 0-116 موافق (0xAEA4)
عدد وحدات البايت المكتوبة على SDRAM EEPROM 176
العدد الإجمالي للبايتات في EEPROM 256
نوع الذاكرة الأساسية DDR3 SDRAM
نوع الوحدة SO-DIMM
--- === خصائص الذاكرة === ---
سرعة الوحدة القصوى 1333 ميجاهرتز (PC3-10600)
الحجم 4096 ميجابايت
البنوك × الصفوف × الأعمدة × بت 8 × 15 × 10 × 64
الرتب 2
عرض جهاز SDRAM 8 بت
تمديد عرض الناقل 0 بت
tCL-tRCD-tRP-tRAS 9-9-9-24
أوقات استجابة CAS المدعومة (tCL) 10T ، 9T ، 8T ، 7T ، 6T ، 5T
--- === توقيتات السرعات القياسية === ---
tCL-tRCD-tRP-tRAS كـ DDR3-1333 9-9-9-24
tCL-tRCD-tRP-tRAS مثل DDR3-1066 7-7-7-20
tCL-tRCD-tRP-tRAS مثل DDR3-800 6-6-6-15
--- === معلمات التوقيت === ---
الحد الأدنى من وقت الدورة (tCK) 1.500 نانوثانية
الحد الأدنى لوقت استجابة CAS (tAA) 13.125 ns
الحد الأدنى لوقت استعادة الكتابة (tWR) 15.000 ns
الحد الأدنى من RAS # إلى CAS # تأخير (tRCD) 13.125 نانوثانية
الحد الأدنى للصف النشط إلى التأخير النشط للصف (tRRD) 6.000 نانوثانية
الحد الأدنى لتأخير الشحن المسبق للصف (tRP) 13.125 ns
الحد الأدنى النشط للتأخير في الشحن المسبق (tRAS) 36.000 نانوثانية
الحد الأدنى النشط للتأخير في التحديث التلقائي (tRC) 49.125 ns
الحد الأدنى لتأخير الاسترداد (tRFC) 160.000 نانوثانية
الحد الأدنى للكتابة لقراءة CMD Delay (tWTR) 7.500 ns
قراءة الحد الأدنى للشحن المسبق تأخير CMD (tRTP) 7.500 نانوثانية
تأخير الحد الأدنى لأربعة نافذة تنشيط (tFAW) 30.000 نانوثانية
--- === الميزات الاختيارية === ---
جهد قابل للتشغيل 1.5 فولت
RZQ / 6 مدعومة؟ لا
RZQ / 7 مدعومة؟ نعم
هل تم دعم وضع DLL-Off؟ نعم
نطاق درجة حرارة التشغيل 0-95 درجة مئوية
معدل التحديث في نطاق درجة الحرارة الموسعة 2X
تحديث ذاتي تلقائي؟ نعم
قراءة على جهاز استشعار حراري؟ لا
صفيف جزئي تحديث ذاتي؟ لا
وحدة استشعار حرارية نعم
نوع جهاز SDRAM متآلف قياسي
--- === الخصائص الفيزيائية === ---
ارتفاع الوحدة 30 مم
سماكة الوحدة 2 مم في الأمام ، 2 مم في الخلف
عرض الوحدة 67.6 مم
مراجعة الوحدة النمطية للبطاقة المرجعية F 0
المرتبة 1 معيار رسم الخرائط
--- === بيانات المصنع === ---
الوحدة النمطية المصنعة ميكرون التكنولوجيا
DRAM الشركة المصنعة لتكنولوجيا ميكرون
رمز موقع التصنيع 0x0F
تاريخ التصنيع 2011-W23
الرقم التسلسلي للتجميع 0xFB5C7F1A
رقم الجزء 16JSF51264HZ-1G4D1
رمز المراجعة 0x4431
الكثير من البيانات. يتم احتساب جزء من المعلومات المعروضة من البيانات. على سبيل المثال ، يتم حساب التوقيت بسرعات قياسية (أي عدد الدورات) استنادًا إلى معلمات التوقيت بدقة النانو ثانية. حتى يتم تخزينها كمضاعفات من وحدة الوقت الأساسية المثبتة في مكان آخر على EEPROM ، ولكن هذا لا ينطبق على موضوع المقالة. توفر وحدة ذاكرة الوصول العشوائي (RAM) هذه 7-7-7-20 إلى DDR3-1066 ، والتي تتوافق مع معيار DDR3-1066F JEDEC. لا تسألني ما هو JEDEC ، ولكنه أسرع من أرخص DDR3-1066G.
قضيت الكثير من الوقت في تأكيد استنتاجي: عند محاولة إعادة ذاكرة ، الرقم المهم الوحيد هو الحد الأدنى من وقت الدورة (tCK). ها هي 1.5 نانوثانية ، أي 667 ميجا هرتز.
دعونا نلقي نظرة على البيانات المصدر.
$ i2cdump 9 0x50
لم يتم تحديد الحجم (باستخدام الوصول إلى بيانات البايت)
تحذير! يمكن لهذا البرنامج أن يربك حافلة I2C الخاصة بك ، ويسبب فقدان البيانات وأسوأ!
سأقوم بالتحقيق في الملف / dev / i2c-9 ، العنوان 0x50 ، وضع بايت
تواصل؟ [نعم / لا]
0 1 2 3 4 5 6 7 8 9 abcdef 0123456789abcdef
00: 92 10 0b 03 03 19 00 09 03 52 01 08 0c 00 7e 00 ؟؟؟؟؟؟. ؟؟ R ؟؟؟. ~.
10: 69 78 69 30 69 11 20 89 00 05 3c 3c 00 f0 82 05 ixi0i؟ ؟؟. <<. ؟؟؟
20: 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00؟ ...............
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 0f 11 05 00 ............ ؟؟؟.
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
70: 00 00 00 00 00 80 2c 0f 11 23 fb 5c 7f 1a a4 ae .....؟، ؟؟ #؟ \ ؟؟؟؟
80: 31 36 4a 53 46 35 31 32 36 34 48 5a 2d 31 47 34 16JSF51264HZ-1G4
90: 44 31 44 31 80 2c 00 00 00 00 00 00 00 00 00 00 00 D1D1 ؟، ..........
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
D0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
تقول المواصفات أن الحد الأدنى للوقت المشار إليه هو 0x0c. دعونا نرى أنه في السطر الأول (
00:
في العمود
c
. بالمناسبة ، قيمتها هي أيضًا 0x0c أو 12. هذا هو مضاعف متوسط الوقت الأساسي (MTB) ، وهو حاصل قسمة القيمة في 0x0a على القيمة في 0x0b (بالنانو ثانية). هنا 1⁄8 نانوثانية. لذا فإن 12 MTB تعادل 1.5 ns.
تغيير التخطيط
للوصول إلى DDR3-1066 ، نحتاج إلى 533 ميجاهرتز ، وهو 1.875 نانوثانية أو 15 MTB ، أو 0x0f. أي أننا نريد كتابة 0x0f عند 0x0c.
ولكن انتظر ، من الواضح أن هناك إصلاح خطأ. يتم تخزين CRC لأول 116 بايت في 0x7e-7f. نظرت هناك ورأيت
a4 ae
، ثم ذهبت للبحث عن آلة حاسبة للحساب. استغرق الأمر مني وقتًا طويلاً بشكل مثير للدهشة للعثور على حاسبة CRC قابلة للتطبيق. جربت العديد من أدوات سطر الأوامر ، ولكن لا تزال تستقر على الحاسبة عبر الإنترنت
http://crccalc.com/ . ثم اكتشفت أنه يتم استخدام متغير CRC-16 / XMODEM هنا ، والمجموع الاختباري هو فعليًا 0xAEA4. ترتيب بايت وكل ذلك. وتجدر الإشارة في إصدار
decode-dimms
.
لذلك ، تحتاج إلى كتابة الحد الأدنى الجديد من وقت الدورة (0x0f) عند 0x0c ومجموع المجموع الاختباري الجديد في 0x7e ككلمة.
سجل SPD
الآن عرفت ماذا أكتب ، وأجرؤ على المحاولة. بأيد مرتعشة ، كتبت
y
، واضغط على Enter للتأكيد النهائي و ...
$ i2cset 9 0x50 0x0c 0x0f
تحذير! يمكن لهذا البرنامج أن يربك حافلة I2C الخاصة بك ، ويسبب فقدان البيانات وأسوأ!
خطير! الكتابة إلى EEPROM التسلسلي على ذاكرة DIMM
قد تجعل ذاكرتك غير مستخدمة وتجعل نظامك غير قابل للتشغيل!
سأكتب إلى ملف الجهاز / dev / i2c-9 ، عنوان الشريحة 0x50 ، عنوان البيانات
0x0c ، بيانات 0x0f ، بايت الوضع.
تواصل؟ [y / N] ذ
خطأ: فشلت الكتابة
الخطأ. انتظر ماذا؟
كرجل متحذق ، بدأت في دراسة مصادر i2cset ، وكذلك وحدات النواة المقابلة. في مرحلة ما ، أدركت أن هذا يمكن أن يحدث بسبب الحماية ضد الكتابة.
أخرجت وحدة ذاكرة ، نظرت إليها وتعرفت على شريحة EEPROM. تقول
97B
،
321
وبعض الأشياء الأخرى. Googling ، اكتشفت أن هذه شريحة
SE97B . نظرت إلى جدول البيانات وقرأت بعناية قسم حماية الكتابة عدة مرات. بمساعدة البرامج ، قمت بالعديد من المحاولات لإزالة الحماية المؤقتة ضد الكتابة ، لكنها فشلت. ربما كانت الحماية ضد الكتابة ثابتة ، لذلك قررت فقط البحث عن وحدة لا تحتوي على حماية ضد الكتابة.
بالمناسبة ، حقيقة مضحكة هي أنه تم تمكين الحماية ضد الكتابة في الوقت الحقيقي عن طريق كتابة شيء ما على عنوان معين. لا أعتقد أن
i2cdetect
تفعل ذلك بشكل طبيعي ، ولكن تشغيل
i2cget 9 0x30 <any-address>
سيؤسس على الأرجح حماية ضد الكتابة في الوقت الفعلي ، وهو أمر ثابت حقًا. لم أحاول القيام بذلك.
أخذت الوحدة التالية ولم تكن هناك رسالة خطأ. لم تتغير EEPROM.
أخيرا ، النجاح!
مع الوحدة الثالثة ، تحولت العملية أخيرًا. قمت بحساب CRC وكتبتها مع وقت الدورة. بعد تحميل وحدة
eeprom
kernel وبدء
decode-dimms
بدت الوحدة وكأنها PC3-8500 4GB عادية. عندما قمت بتثبيته على جهاز MacBook Pro ، قمت في النهاية بتشغيل نظام يحتوي على 8 غيغابايت من الذاكرة.
DDR3 SODIMM بعد إعادة التجهيز جاهز للعمل في MacBook Proقبل: DDR3-1333 الأصلي
0 1 2 3 4 5 6 7 8 9 abcdef 0123456789abcdef
00: 92 10 0b 03 03 19 00 09 03 52 01 08 0c 00 3e 00 ؟؟؟؟؟؟. ؟؟ R ؟؟؟.>.
10: 69 78 69 30 69 11 20 89 00 05 3c 3c 00 f0 83 01 ixi0i؟ ؟؟. <<. ؟؟؟
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 0f 11 45 00 ............ ؟؟ E.
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
70: 00 00 00 00 00 80 م 02 11 30 b1 5b 13 a1 0e 59 ..... ؟؟؟؟ 0؟ [؟؟؟ Y
80: 4d 34 37 31 42 35 32 37 33 43 48 30 2d 43 48 39 M471B5273CH0-CH9
90: 20 20 00 00 80 م 00 00 00 53 31 42 4e 30 30 30 .. ؟؟ ... S1BN000
a0: 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 03؟.؟ ............؟
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 59 00 ............. 2 سنة.
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
يوم 0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
بعد: يبدو DDR3-1066
0 1 2 3 4 5 6 7 8 9 abcdef 0123456789abcdef
00: 92 10 0b 03 03 19 00 09 03 52 01 08 0f 00 3e 00 ؟؟؟؟؟؟. ؟؟ R ؟؟؟.>.
10: 69 78 69 30 69 11 20 89 00 05 3c 3c 00 f0 83 01 ixi0i؟ ؟؟. <<. ؟؟؟
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 0f 11 45 00 ............ ؟؟ E.
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
70: 00 00 00 00 00 80 م 02 11 30 b1 5b 13 a1 06 54 ..... ؟؟؟؟ 0؟ [؟؟؟ T
80: 4d 34 37 31 42 35 32 37 33 43 48 30 2d 43 48 39 M471B5273CH0-CH9
90: 20 20 00 00 80 م 00 00 00 53 31 42 4e 30 30 30 .. ؟؟ ... S1BN000
a0: 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 03؟.؟ ............؟
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 59 00 ............. 2 سنة.
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
يوم 0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
إذا كنت لا ترى الفرق على الفور ، فأنت لم تدخل في مدافن النفايات هذه طالما أنا.
أفكارك
هل يستحق ذلك أن تفعل؟ ماليا ، بالطبع لا!
لكنها كانت ممتعة وتعلمت الكثير. ليس لدي أي فكرة عن
المكان الذي يمكن تطبيق هذه المعرفة
فيه بالضبط ، لكني أشعر أنه في مرحلة ما ستكون هناك حاجة إليها. ومجرد الشعور بأنك قادر على حل المشكلة بشكل صحيح لطيف حقًا ويعطي الثقة.
1. افترضت أن ذاكرة الوصول العشوائي ستعمل على هذا الجهاز إذا اجتاز memtest86 كان خطأ واضحًا. ومع ذلك ، حتى إذا نظرنا إلى الوراء ، فإن هذا الافتراض لا يبدو سخيفًا. من التجربة ، فإن مجموعة غريبة من الأجهزة ليست غير شائعة ، بسبب سقوط الاختبار القياسي.
↑2. لقد تعلمت مؤخرًا استخدام I²C في مشروع آخر. أعتقد أنه يمكنني قراءة وكتابة EEPROM على وحدة التحكم الدقيقة Cortex-M باستخدام برنامجي الخاص ، ولكن عمليًا ، سيكون لحام الموصل صعبًا جدًا ، وكتابة التعليمات البرمجية هي عمل كثير جدًا بالنسبة لي لأهتم به. ومع ذلك ، أنا سعيد حقًا لأنني قادر نظريًا على شيء من هذا القبيل!
↑