في بعض الأحيان يتبادر إلى الذهن فكرة للتخلص من الصعب للغاية. هذا ما حدث لي.
قررت إنشاء جهاز افتراضي (VM) ، بالنظر إلى أنه في ذلك الوقت لم تكن لدي أية أفكار ، بدا لي أن هذه فكرة رائعة. إذا كنت مهتما ، ثم انتقل إلى خفض!
نظرية
أولاً ، نظرية صغيرة. ما هو الجهاز الظاهري بشكل عام؟ هذا هو برنامج أو مجموعة من البرامج التي تسمح لك بمحاكاة نوع من الأنظمة الأساسية للأجهزة ، بمعنى آخر ، محاكي كمبيوتر.
تختلف الأجهزة الافتراضية نفسها ، على سبيل المثال ، Virtual Box هو جهاز افتراضي كلاسيكي يسمح لك بمحاكاة كمبيوتر حقيقي ، ولكن على سبيل المثال ، لا يمكن لـ JVM (جهاز Java الظاهري) القيام بذلك.
سيكون إصدار VM الخاص بي مشابهًا إلى حد ما لـ JVM ، لأنه ببساطة مشروع تدريبي أكثر منه يهدف إلى إنشاء VM قوي.
الذاكرة
لذلك ، دعونا الآن معرفة الذاكرة. لإنشاء ذاكرة ، قررت استخدام مجموعة int غير موقعة. يتم تحديد حجم المصفوفة باستخدام ماكرو ، في إصداري ، يكون حجم الذاكرة 4096 بايت (هناك 1024 عنصرًا في المصفوفة ، وبما أنه في معظم الأنظمة الأساسية ، يتم تخصيص 4 بايت للبيانات int غير الموقعة ، ثم 1024 * 4 = 4096) ، من بين أشياء أخرى ، سنحدد 8 تسجيلات بواسطة 8 خلايا في كل سيكون بالفعل 256 بايت (8 * 8 * 4 = 256). يبدو مثل هذا:
#define MEMSIZE 1024 unsigned int memory[MEMSIZE]; unsigned int reg[8][8];
البرمجة
لدينا ذاكرة ، ولكن الآن كيفية كتابة التعليمات البرمجية لدينا VM؟ الآن سنتعامل مع هذه المشكلة ، لتبدأ ، سنحدد الأوامر التي ستنفذها آلة لدينا:
enum commands { CRG = 1, CRC, PRG, PRC };
كل فريق لديه علمه الخاص تحديد بعض المعلمات الإضافية.
سنصف الأعلام:
enum flags { STDI = 1, STDA };
يحتوي الأمر القياسي على النموذج: [command] [العلم] [البيانات] (قد يختلف مظهر بعض الأوامر) ، وبناءً على ذلك سنكتب مترجم بسيط:
if (memory[cell] == CRG && memory[cell + 1] == STDI) { indxX = memory[cell + 2]; cell++; } else if (memory[cell] == CRC && memory[cell + 1] == STDI) { indxY = memory[cell + 2]; cell++; } else if (memory[cell] == PRG && memory[cell + 1] == STDI) { reg[indxX][0] = memory[cell + 2]; cell++; } else if (memory[cell] == PRC && memory[cell + 1] == STDI) { reg[indxX][indxY] = memory[cell + 2]; cell++; }
indxX و indxY هي المتغيرات التي تخزن موضع المؤشر الحالي في سجل reg.
الخلية عبارة عن متغير يقوم بتخزين موضع المؤشر الحالي في صفيف الذاكرة.
لكن البرمجة مع الأرقام ليست مريحة للغاية ، لذلك باستخدام المعالج المسبق C سنصف المجمع الخاص بنا. أفهم أن كتابة asm مع وحدات الماكرو ليست جيدة جدًا ، ولكن هذا الحل مؤقت.
رمز asm لدينا يشبه هذا:
#define $CRG {memory[memIndx++] = CRG;} #define $CRC {memory[memIndx++] = CRC;} #define $PRG {memory[memIndx++] = PRG;} #define $PRC {memory[memIndx++] = PRC;} #define _$STDI {memory[memIndx++] = STDI;} #define _$STDA {memory[memIndx++] = STDA;} #define _$DATA memory[memIndx++] =
memIndx هو متغير تخزين موضع المؤشر الحالي في صفيف الذاكرة.
وإليك الرمز الخاص بنا والذي يضع 123 في السجل على العنوان [1] [0] (التسجيل الأول ، خلية الصفر):
$CRG _$STDI _$DATA 1; $CRC _$STDI _$DATA 0; $PRC _$STDI _$DATA 123;
مبروك ، لدينا الآن ما يشبه أسمر لسيارتنا!
إطلاق البرامج
لقد نجحنا في جعل الجهاز الخاص بنا يقوم بتنفيذ البرامج ، لكن الكود يفتقر إلى قابلية النقل من جهاز لآخر ، لذا سنقوم الآن بإنشاء منشئ رمز الجهاز من asm (وأذكرك بأنه على عكس أجهزة الكمبيوتر الحقيقية ، يوجد لدينا آلة رمز غير معروض في شكل ثنائي ، والأعداد العشرية) ، من حيث المبدأ ، ليست صعبة للغاية ، ولكن أولاً ، دعونا نفكر في التنفيذ.
أولاً ، لدينا رمز asm ، والآن نحن بحاجة إلى ترجمته إلى أرقام ، ثم كتابة رمز الجهاز الناتج إلى ملف .ncp (برنامج رمز رقمي ، في الواقع هو ملف نصي ، ولكن لتمييزه عن كل شيء آخر ، توصلت إلى ملحق خاص بي) ، بعد ذلك نحتاج إلى تشغيل ملف .ncp ، الأمر بسيط ، نظرًا لأن المترجم الذي كتبناه سابقًا ، يتعرف على الأرقام ؛ نحتاج فقط إلى استخراج البيانات من الملف وتحويلها إلى أرقام باستخدام atoi ().
دعنا ننتقل من الأقوال إلى الأفعال:
قراءة الكود وكتابته إلى ملف:
if (memory[i] == CRG && memory[i + 1] == STDI) { fprintf(code, "%d %d ", CRG, STDI); i++; } else if (memory[i] == CRC && memory[i + 1] == STDI) { fprintf(code, "%d %d ", CRC, STDI); i++; } else if (memory[i] == PRG && memory[i + 1] == STDI) { fprintf(code, "%d %d ", PRG, STDI); i++; } else if (memory[i] == PRC && memory[i + 1] == STDI) { fprintf(code, "%d %d ", PRC, STDI); i++; }
الكود جزء من نص الدالة ncpGen ().
قراءة ملف وتنفيذه:
if (prog != NULL) { fread(txt, 1, len, prog); tok = strtok(txt, " "); while (tok != NULL) { memory[i] = atoi(tok); tok = strtok(NULL, " "); if (argc == 3 && strcmp(argv[2], "-m") == 0) { printf("%d\n", memory[i]); } i++; } memInter(); } else { perror("Fail"); }
الآن ، دعونا نحدد ماكرو بحيث يتحول الرمز إلى .ncp بدلاً من تفسير asm:
#define _toNCP(name) {strcpy(filename, name);} {ncpGen();}
إذا كان أي شيء ، فإن المقالة لا تقدم كل الكود ، ولكن فقط جزء صغير منه!
الكود الكامل موجود في
مستودع المشروع.
شكرا جزيلا لقراءة!