بايت آلة الحصن (وليس فقط) في أمريكا الأصلية (جزء 2)

الصورة

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

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

والذين لم يخافوا بعد من المجمع الرهيب و bytecode - مرحبا بكم في خفض! :)

أولا ، إصلاح الأخطاء. لنقم بضبط امتداد الملف .s ، كما هو معتاد بالنسبة لـ GAS (بفضل mistergrim ). بعد ذلك ، استبدل int 0x80 بـ syscall واستخدم سجلات 64 بت (شكرا qw1 ). في البداية ، لم أقرأ وصف المكالمة بعناية وقمت بتصحيح السجلات فقط ... وحصلت على خطأ تقسيم. اتضح أن كل شيء قد تغير لمسؤول النظام ، بما في ذلك أرقام المكالمات. sys_write لـ syscall هو الرقم 1 ، و sys_exit هو 60. ونتيجة لذلك ، اتخذت الأوامر السيئة ، type ، و bye بالشكل التالي:

b_bad = 0x00 bcmd_bad: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 - stdout mov rsi, offset msg_bad_byte #     mov rdx, msg_bad_byte_len #   syscall #   mov rax, 60 #   № 1 - sys_exit mov rbx, 1 #    1 syscall #   b_bye = 0x01 bcmd_bye: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 - stdout mov rsi, offset msg_bye #     mov rdx, msg_bye_len #   syscall #   mov rax, 60 #   № 60 - sys_exit mov rdi, 0 #    0 syscall #   b_type = 0x80 bcmd_type: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 - stdout pop rdx pop rsi push r8 syscall #   pop r8 jmp _next 

وأكثر شيء واحد. بكل صواب ، في التعليقات على المقال الأخير ، كتب بيريز و fpauk أنه إذا تم استخدام عناوين المعالج في الكود ، فإن الكود الثاني يعتمد على النظام الأساسي. وفي هذا المثال ، تم تعيين عنوان السطر "مرحبًا ، world!" في الكود bytecode حسب القيمة (باستخدام الأمر lit64). بالطبع ، هذا ليس ضروريا. ولكن هذه كانت أسهل طريقة للتحقق من آلة البايت. لن أفعل ذلك بعد الآن ، لكنني سأحصل على عناوين المتغيرات بوسائل أخرى: على وجه الخصوص ، باستخدام الأمر var (المزيد حول ذلك لاحقًا).

الاحماء


والآن ، كإحماء ، سنفعل جميع العمليات الحسابية الصحيحة الأساسية (+ ، - ، * ، / ، mod ، / mod ، abs). سنحتاجهم.

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

الحساب
 b_add = 0x21 bcmd_add: pop rax add [rsp], rax jmp _next b_sub = 0x22 bcmd_sub: pop rax sub [rsp], rax jmp _next b_mul = 0x23 bcmd_mul: pop rax pop rbx imul rbx push rax jmp _next b_div = 0x24 bcmd_div: pop rbx pop rax cqo idiv rbx push rax jmp _next b_mod = 0x25 bcmd_mod: pop rbx pop rax cqo idiv rbx push rdx jmp _next b_divmod = 0x26 bcmd_divmod: pop rbx pop rax cqo idiv rbx push rdx push rax jmp _next b_abs = 0x27 bcmd_abs: mov rax, [rsp] or rax, rax jge _next neg rax mov [rsp], rax jmp _next 

تقليديا ، في الحصن ، تضاف العمليات المزدوجة الدقة إلى العمليات الحسابية والمكدسة المعتادة. عادة ما تبدأ الكلمات الخاصة بهذه العمليات بحرف "2": 2DUP ، 2SWAP ، إلخ. لكن لدينا حساب قياسي بالفعل 64 بت ، ونحن بالتأكيد لن نفعل 128 اليوم :)

بعد ذلك ، نضيف عمليات التكديس الأساسية (الإسقاط ، المبادلة ، الجذر ، الجذر ، عبر ، الانتقاء ، الأسماء).

عمليات المكدس
 b_drop = 0x31 bcmd_drop: add rsp, 8 jmp _next b_swap = 0x32 bcmd_swap: pop rax pop rbx push rax push rbx jmp _next b_rot = 0x33 bcmd_rot: pop rax pop rbx pop rcx push rbx push rax push rcx jmp _next b_mrot = 0x34 bcmd_mrot: pop rcx pop rbx pop rax push rcx push rax push rbx jmp _next b_over = 0x35 bcmd_over: push [rsp + 8] jmp _next b_pick = 0x36 bcmd_pick: pop rcx push [rsp + 8*rcx] jmp _next b_roll = 0x37 bcmd_roll: pop rcx mov rbx, [rsp + 8*rcx] roll1: mov rax, [rsp + 8*rcx - 8] mov [rsp + 8*rcx], rax dec rcx jnz roll1 push rbx jmp _next 

وسنصدر أيضًا أوامر للقراءة والكتابة إلى الذاكرة (كلمات Fort @ و!). وكذلك نظرائهم إلى عمق بت آخر.

القراءة والكتابة إلى الذاكرة
 b_get = 0x40 bcmd_get: pop rcx push [rcx] jmp _next b_set = 0x41 bcmd_set: pop rcx pop rax mov [rcx], rax jmp _next b_get8 = 0x42 bcmd_get8: pop rcx movsx rax, byte ptr [rcx] push rax jmp _next b_set8 = 0x43 bcmd_set8: pop rcx pop rax mov [rcx], al jmp _next b_get16 = 0x44 bcmd_get16: pop rcx movsx rax, word ptr [rcx] push rax jmp _next b_set16 = 0x45 bcmd_set16: pop rcx pop rax mov [rcx], ax jmp _next b_get32 = 0x46 bcmd_get32: pop rcx movsx rax, dword ptr [rcx] push rax jmp _next b_set32 = 0x47 bcmd_set32: pop rcx pop rax mov [rcx], eax jmp _next 

قد نحتاج إلى فرق المقارنة ، وسنفعلها أيضًا.

أوامر المقارنة
 # 0= b_zeq = 0x50 bcmd_zeq: pop rax or rax, rax jnz rfalse rtrue: push -1 jmp _next rfalse: push 0 jmp _next # 0< b_zlt = 0x51 bcmd_zlt: pop rax or rax, rax jl rtrue push 0 jmp _next # 0> b_zgt = 0x52 bcmd_zgt: pop rax or rax, rax jg rtrue push 0 jmp _next # = b_eq = 0x53 bcmd_eq: pop rbx pop rax cmp rax, rbx jz rtrue push 0 jmp _next # < b_lt = 0x54 bcmd_lt: pop rbx pop rax cmp rax, rbx jl rtrue push 0 jmp _next # > b_gt = 0x55 bcmd_gt: pop rbx pop rax cmp rax, rbx jg rtrue push 0 jmp _next # <= b_lteq = 0x56 bcmd_lteq: pop rbx pop rax cmp rax, rbx jle rtrue push 0 jmp _next # >= b_gteq = 0x57 bcmd_gteq: pop rbx pop rax cmp rax, rbx jge rtrue push 0 jmp _next 

لن نختبر العمليات. الشيء الرئيسي هو أن المجمع لن يعطي أخطاء عند التحويل البرمجي. سوف يكون التصحيح في عملية استخدامها.

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

 init_stack: .quad 0 init_rstack: .quad 0 _start: mov rbp, rsp sub rbp, stack_size lea r8, start mov init_stack, rsp mov init_rstack, rbp jmp _next b_depth = 0x38 bcmd_depth: mov rax, init_stack sub rax, rsp shr rax, 3 push rax jmp _next 

عدد الإخراج


حسنًا ، انتهى الإحماء ، وعليك أن تتعرق قليلاً. تعليم نظامنا لإخراج الأرقام. كلمة "." تستخدم لعرض الأرقام في الحصن. (الفترة). دعونا نفعل ذلك بالطريقة التي يتم بها في تطبيقات Fort القياسية ، باستخدام الكلمات <# ، hold ، # ، #s ، #> ، base. يجب أن ندرك كل هذه الكلمات. لتكوين رقم ، يتم استخدام مخزن مؤقت ومؤشر للحرف المراد تشكيلها ، وستكون هذه الكلمات holdbuf و holdpoint.

لذلك ، نحن بحاجة إلى هذه الكلمات:

  • holdbuf - عازلة لتوليد تمثيل العدد ، يحدث التكوين من النهاية
  • holdpoint - عنوان لآخر حرف معروض (في holdbuf)
  • <# - بداية تكوين رقم ؛ يحدد نقطة توقف على البايت ، بعد البايت الأخير
  • تعليق - يقلل نقطة الإيقاف بمقدار 1 ويحفظ الحرف من المكدس إلى المخزن المؤقت في العنوان المستلم
  • # - يقسم الكلمة في الجزء العلوي من المكدس إلى قاعدة نظام الأرقام ، يترجم بقية التقسيم إلى حرف ويحفظ إلى المخزن المؤقت باستخدام تعليق
  • #s - يحول الكلمة بالكامل ؛ يستدعي بالفعل الكلمة # في حلقة حتى يتم ترك 0 على المكدس
  • #> - الانتهاء من التحويل ؛ يدفع بداية السلسلة المشكلة وطولها إلى المكدس

سنفعل كل الكلمات في bytecode ، لكن أولاً ، دعنا نتعامل مع المتغيرات.

المتغيرات


وهنا سيكون هناك القليل من السحر Fortian. والحقيقة هي أنه في الحصن متغير كلمة. عند تنفيذ هذه الكلمة ، يكون عنوان خلية الذاكرة التي تخزن قيمة المتغير في المجموعة. يمكنك قراءة أو الكتابة على هذا العنوان. على سبيل المثال ، لكتابة القيمة 12345 إلى المتغير A ، تحتاج إلى تنفيذ الأوامر التالية: "12345 A!". في هذا المثال ، يتم الضغط على 12345 على المكدس ، ثم يقوم المتغير A بدفع عنوانه والكلمة "!" يزيل قيمتين من المكدس ويكتب 12345 إلى عنوان المتغير A. في تطبيقات نموذجية من الحصن (مع رمز مباشر) ، المتغيرات هي أمر معالج صغري CALL مع عنوان _next ، وبعد ذلك يتم حجز مكان لتخزين قيمة المتغير. عند تنفيذ هذه الكلمة ، ينقل المعالج الدقيق التحكم إلى _next ويدفع عنوان المرسل (عبر RSP) إلى المكدس. ولكن في الحصن ، تكون حزمة المعالجات الدقيقة عبارة عن حساب ، ولن نعود إلى أي مكان. نتيجة لذلك ، يستمر التنفيذ ، وعلى المكدس هو عنوان المتغير. وكل هذا مع فريق معالج واحد! في المجمع ، سيبدو كما يلي:

  call _next #   _next,      ,   12345 .quad 12345 

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

  • هذا رمز بايت ، عند نقل التحكم الذي يجب أن يعود منه على الفور
  • بعد الإرجاع ، يجب أن يظل العنوان حيث يتم تخزين قيمة المتغير في رصة الحساب

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

 b_var0 = 0x28 bcmd_var0: push r8 b_exit = 0x17 bcmd_exit: mov r8, [rbp] add rbp, 8 _next: movzx rcx, byte ptr [r8] inc r8 jmp [bcmd + rcx*8] 

الآن سيبدو المتغير الأساسي كما يلي:
 base: .byte b_var0 .quad 10 

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

الآن نحن جميعا على استعداد لرسم الأرقام. لنبدأ!

الكلمات قاعدة ، holdbuf ، holdpoint


كيف سيتم ترتيب المتغيرات بالفعل قد تقرر. لذلك ، يتم الحصول على الكلمات الأساسية ، holdbuf ، holdpoint كما يلي:

 base: .byte b_var0 .quad 10 holdbuf_len = 70 holdbuf: .byte b_var0 .space holdbuf_len holdpoint: .byte b_var0 .quad 0 

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

امسك


الآن يمكنك جعل الكلمة معلقة. في الحصن ، يبدو رمزها كما يلي:

 : hold holdpoint @ 1- dup holdbuf > if drop drop else dup holdpoint ! c! then ; 

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

في البداية ، هناك كلمة لتعريف الكلمات الجديدة واسم كلمة جديدة: ": hold". بعد ذلك يأتي الرمز الذي ينتهي بكلمة "؛". دعنا نحلل كود الكلمة. سأعطي الأمر وحالة المكدس بعد تنفيذ الأمر. قبل أن يتم استدعاء الكلمة ، يوجد رمز للحرف على المكدس ، يتم وضعه في المخزن المؤقت (يشار إليه بواسطة <character>). كذلك اتضح مثل هذا:

 holdpoint <> <  holdpoint> @ <> <  holdpoint> 1- <> <  holdpoint  1> dup <> <  holdpoint  1> <  holdpoint  1> holdbuf <> <  holdpoint  1> <  holdpoint  1> <  holdbuf> > <> <  holdpoint  1> <,    holdpoint  1    holdbuf> 

بعد ذلك ، يتم الأمر if ، والذي يتم تجميعه في قفزة مشروطة إلى سلسلة من الأوامر بين آخر وبعد ذلك. يقوم الفرع الشرطي بإزالة نتيجة المقارنة من المكدس وتنفيذ عملية النقل إذا كان هناك كذب على المكدس. إذا لم يكن هناك انتقال ، يتم تنفيذ الفرع بين if و else ، حيث يوجد أمران بإسقاط يزيلان الحرف والعنوان. خلاف ذلك ، يستمر التنفيذ. كلمة "!" يحفظ القيمة الجديدة في نقطة الانتظار (تتم إزالة العنوان والقيمة من المكدس). وكلمة "ج!" يكتب حرفًا إلى المخزن المؤقت ، هذا هو أمر set8 بايت (تتم إزالة عنوان وقيمة الحرف من المكدس).

 dup <> <  holdpoint  1> <  holdpoint  1> holdpoint <> <  holdpoint  1> <  holdpoint  1> <  holdpoint> ! <> <  holdpoint  1> c! ,  ,   ! :) 

إليك عدد الإجراءات التي يقوم بها هذا التسلسل القصير للأوامر! نعم ، الحصن موجز. والآن نقوم بتشغيل "المحول البرمجي" اليدوي في الرأس :) ونجمع كل هذا إلى رمز فرعي:
 hold: .byte b_call8 .byte holdpoint - . - 1 # holdpoint .byte b_get # @ .byte b_wm # 1- .byte b_dup # dup .byte b_call8 .byte holdbuf - . - 1 # holdbuf .byte b_gt # > .byte b_qbranch8 # if .byte 0f - . .byte b_drop # drop .byte b_drop # drop .byte b_branch8 #     ( then) .byte 1f - . 0: .byte b_dup # dup .byte b_call8 .byte holdpoint - . - 1 # holdpoint .byte b_set # ! .byte b_set8 # c! 1: .byte b_exit # ; 

أنا هنا استخدمت العلامات المحلية (0 و 1). يمكن الوصول إلى هذه التسميات بأسماء خاصة. على سبيل المثال ، يمكن الوصول إلى التصنيف 0 بالأسماء 0f أو 0b. هذا يعني ارتباطًا إلى أقرب تصنيف 0 (للأمام أو للخلف). مناسب تمامًا للتسميات المستخدمة محليًا ، حتى لا يتم التوصل إلى أسماء مختلفة.

كلمة #


لنجعل الكلمة #. في الحصن ، سيبدو الرمز الخاص به كما يلي:

 : # base /mod swap dup 10 < if c″ 0 + else 10 - c″ A + then hold ; 

يتم استخدام الشرط هنا للتحقق: هل الرقم المستلم أقل من عشرة؟ إذا كان أقل ، يتم استخدام الأرقام من 0 إلى 9 ؛ وإلا ، يتم استخدام الأحرف التي تبدأ بحرف "A". سيسمح ذلك بالعمل مع نظام الأرقام السداسي عشري. تسلسل c ″ 0 يدفع رمز الحرف 0 إلى المكدس ، ونقوم بتشغيل "المحول البرمجي":

 conv: .byte b_call16 .word base - . - 2 # base .byte b_get # @ .byte b_divmod # /mod .byte b_swap # swap .byte b_dup # dup .byte b_lit8 .byte 10 # 10 .byte b_lt # < .byte b_qnbranch8 # if .byte 0f - . .byte b_lit8 .byte '0' # c″ 0 .byte b_add # + .byte b_branch8 # else .byte 1f - . 0: .byte b_lit8 .byte 'A' # c″ A .byte b_add # + 1: .byte b_call16 .word hold - . - 2 # hold .byte b_exit # ; 

كلمة <#


الكلمة <# بسيطة جدًا:

 : <# holdbuf 70 + holdpoint ! ; 

الرمز البريدي:

 conv_start: .byte b_call16 .word holdbuf - . - 2 .byte b_lit8 .byte holdbuf_len .byte b_add .byte b_call16 .word holdpoint - . - 2 .byte b_set .byte b_exit 

كلمة #>


تبدو الكلمة #> لإكمال التحويل كما يلي:

 : #> holdpoint @ holdbuf 70 + over - ; 

الرمز البريدي:

 conv_end: .byte b_call16 .word holdpoint - . - 2 .byte b_get .byte b_call16 .word holdbuf - . - 2 .byte b_lit8 .byte holdbuf_len .byte b_add .byte b_over .byte b_sub .byte b_exit 

كلمة #s


وأخيراً ، الكلمة #s:

 : #s do # dup 0= until ; 

الرمز البريدي:

 conv_s: .byte b_call8 .byte conv - . - 1 .byte b_dup .byte b_qbranch8 .byte conv_s - . .byte b_exit 

أي شخص حريص سوف يلاحظ وجود تباين بسيط بين الكود الثنائي ورمز الحصن :)

كل شيء جاهز


الآن ، لن يمنعك أي شيء من إنشاء كلمة "." ، والتي تعرض رقمًا:

 : . <# #s drop #> type ; 

الرمز البريدي:

 dot: .byte b_call8 .byte conv_start - . - 1 .byte b_call8 .byte conv_s - . - 1 .byte b_drop .byte b_call8 .byte conv_end - . - 1 .byte b_type .byte b_exit 

دعنا نجعل اختبار الكود الذي يتحقق من وجهة نظرنا:

 start: .byte b_lit16 .word 1234 .byte b_call16 .word dot - . - 2 .byte b_bye 

بالطبع ، لم ينجح الأمر مرة واحدة. ولكن ، بعد تصحيح الأخطاء ، تم الحصول على النتيجة التالية:

 $ as forth.asm -o forth.o -g -ahlsm>list.txt $ ld forth.o -o forth $ ./forth 1234bye! 

عضادة مرئية على الفور. بعد الرقم ، يجب أن يعرض الحصن مساحة. أضف بعد conv_start (<#) استدعاء أمر الحجز 32.

نرسم أيضا علامة. في البداية ، قم بإضافة القيمة المطلقة المزدوجة ، وفي النهاية ، تحقق من علامة النسخة اليسرى ثم ضع علامة الطرح إذا كان الرقم سالباً (0 <إذا كان c ″ - أمسك ثم). نتيجة لذلك ، كلمة "." يأخذ في هذا النموذج:

 : . dup abs <# 32 hold #s drop #> 0< if c″ - hold then type ; 

الرمز البريدي:

 dot: .byte b_dup .byte b_abs .byte b_call8 .byte conv_start - . - 1 .byte b_lit8 .byte ' ' .byte b_call16 .word hold - . - 2 .byte b_call8 .byte conv_s - . - 1 .byte b_drop .byte b_zlt .byte b_qnbranch8 .byte 1f - . .byte b_lit8 .byte '-' .byte b_call16 .word hold - . - 2 1: .byte b_call8 .byte conv_end - . - 1 .byte b_type .byte b_exit 

في تسلسل بدء أوامر البايت ، ضع رقمًا سالبًا وتحقق من:

 $ as forth.asm -o forth.o -g -ahlsm>list.txt $ ld forth.o -o forth $ ./forth -1234 bye! 

هناك استنتاج من الأرقام!

المصدر الكامل
 .intel_syntax noprefix stack_size = 1024 .section .data init_stack: .quad 0 init_rstack: .quad 0 msg_bad_byte: .ascii "Bad byte code!\n" msg_bad_byte_len = . - msg_bad_byte #  len    msg_bye: .ascii "bye!\n" msg_bye_len = . - msg_bye bcmd: .quad bcmd_bad, bcmd_bye, bcmd_num0, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x00 .quad bcmd_lit8, bcmd_lit16, bcmd_lit32, bcmd_lit64, bcmd_call8, bcmd_call16, bcmd_call32, bcmd_bad .quad bcmd_branch8, bcmd_branch16, bcmd_qbranch8, bcmd_qbranch16, bcmd_qnbranch8, bcmd_qnbranch16,bcmd_bad, bcmd_exit # 0x10 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_wm, bcmd_add, bcmd_sub, bcmd_mul, bcmd_div, bcmd_mod, bcmd_divmod, bcmd_abs # 0x20 .quad bcmd_var0, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_dup, bcmd_drop, bcmd_swap, bcmd_rot, bcmd_mrot, bcmd_over, bcmd_pick, bcmd_roll # 0x30 .quad bcmd_depth, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_get, bcmd_set, bcmd_get8, bcmd_set8, bcmd_get16, bcmd_set16, bcmd_get32, bcmd_set32 # 0x40 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_zeq, bcmd_zlt, bcmd_zgt, bcmd_eq, bcmd_lt, bcmd_gt, bcmd_lteq, bcmd_gteq #0x50 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x60 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_type, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x80 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad start: .byte b_lit16 .word -1234 .byte b_call16 .word dot - . - 2 .byte b_bye base: .byte b_var0 .quad 10 holdbuf_len = 70 holdbuf: .byte b_var0 .space holdbuf_len holdpoint: .byte b_var0 .quad 0 # : hold holdpoint @ 1- dup holdbuf > if drop drop else dup holdpoint ! c! then ; hold: .byte b_call8 .byte holdpoint - . - 1 # holdpoint .byte b_get # @ .byte b_wm # 1- .byte b_dup # dup .byte b_call8 .byte holdbuf - . - 1 # holdbuf .byte b_gt # > .byte b_qbranch8 # if .byte 0f - . .byte b_drop # drop .byte b_drop # drop .byte b_branch8 #     ( then) .byte 1f - . 0: .byte b_dup # dup .byte b_call8 .byte holdpoint - . - 1 # holdpoint .byte b_set # ! .byte b_set8 # c! 1: .byte b_exit # ; # : # base /mod swap dup 10 < if c" 0 + else 10 - c" A + then hold ; conv: .byte b_call16 .word base - . - 2 # base .byte b_get # @ .byte b_divmod # /mod .byte b_swap # swap .byte b_dup # dup .byte b_lit8 .byte 10 # 10 .byte b_lt # < .byte b_qnbranch8 # if .byte 0f - . .byte b_lit8 .byte '0' # c" 0 .byte b_add # + .byte b_branch8 # else .byte 1f - . 0: .byte b_lit8 .byte '?' # c" A .byte b_add # + 1: .byte b_call16 .word hold - . - 2 # hold .byte b_exit # ; # : <# holdbuf 70 + holdpoint ! ; conv_start: .byte b_call16 .word holdbuf - . - 2 .byte b_lit8 .byte holdbuf_len .byte b_add .byte b_call16 .word holdpoint - . - 2 .byte b_set .byte b_exit # : #s do # dup 0=until ; conv_s: .byte b_call8 .byte conv - . - 1 .byte b_dup .byte b_qbranch8 .byte conv_s - . .byte b_exit # : #> holdpoint @ holdbuf 70 + over - ; conv_end: .byte b_call16 .word holdpoint - . - 2 .byte b_get .byte b_call16 .word holdbuf - . - 2 .byte b_lit8 .byte holdbuf_len .byte b_add .byte b_over .byte b_sub .byte b_exit dot: .byte b_dup .byte b_abs .byte b_call8 .byte conv_start - . - 1 .byte b_lit8 .byte ' ' .byte b_call16 .word hold - . - 2 .byte b_call8 .byte conv_s - . - 1 .byte b_drop .byte b_zlt .byte b_qnbranch8 .byte 1f - . .byte b_lit8 .byte '-' .byte b_call16 .word hold - . - 2 1: .byte b_call8 .byte conv_end - . - 1 .byte b_type .byte b_exit .section .text .global _start #     _start: mov rbp, rsp sub rbp, stack_size lea r8, start mov init_stack, rsp mov init_rstack, rbp jmp _next b_var0 = 0x28 bcmd_var0: push r8 b_exit = 0x17 bcmd_exit: mov r8, [rbp] add rbp, 8 _next: movzx rcx, byte ptr [r8] inc r8 jmp [bcmd + rcx*8] b_num0 = 0x02 bcmd_num0: push 0 jmp _next b_lit8 = 0x08 bcmd_lit8: movsx rax, byte ptr [r8] inc r8 push rax jmp _next b_lit16 = 0x09 bcmd_lit16: movsx rax, word ptr [r8] add r8, 2 push rax jmp _next b_call8 = 0x0C bcmd_call8: movsx rax, byte ptr [r8] sub rbp, 8 inc r8 mov [rbp], r8 add r8, rax jmp _next b_call16 = 0x0D bcmd_call16: movsx rax, word ptr [r8] sub rbp, 8 add r8, 2 mov [rbp], r8 add r8, rax jmp _next b_call32 = 0x0E bcmd_call32: movsx rax, dword ptr [r8] sub rbp, 8 add r8, 4 mov [rbp], r8 add r8, rax jmp _next b_lit32 = 0x0A bcmd_lit32: movsx rax, dword ptr [r8] add r8, 4 push rax jmp _next b_lit64 = 0x0B bcmd_lit64: mov rax, [r8] add r8, 8 push rax jmp _next b_dup = 0x30 bcmd_dup: push [rsp] jmp _next b_wm = 0x20 bcmd_wm: decq [rsp] jmp _next b_add = 0x21 bcmd_add: pop rax add [rsp], rax jmp _next b_sub = 0x22 bcmd_sub: pop rax sub [rsp], rax jmp _next b_mul = 0x23 bcmd_mul: pop rax pop rbx imul rbx push rax jmp _next b_div = 0x24 bcmd_div: pop rbx pop rax cqo idiv rbx push rax jmp _next b_mod = 0x25 bcmd_mod: pop rbx pop rax cqo idiv rbx push rdx jmp _next b_divmod = 0x26 bcmd_divmod: pop rbx pop rax cqo idiv rbx push rdx push rax jmp _next b_abs = 0x27 bcmd_abs: mov rax, [rsp] or rax, rax jge _next neg rax mov [rsp], rax jmp _next b_drop = 0x31 bcmd_drop: add rsp, 8 jmp _next b_swap = 0x32 bcmd_swap: pop rax pop rbx push rax push rbx jmp _next b_rot = 0x33 bcmd_rot: pop rax pop rbx pop rcx push rbx push rax push rcx jmp _next b_mrot = 0x34 bcmd_mrot: pop rcx pop rbx pop rax push rcx push rax push rbx jmp _next b_over = 0x35 bcmd_over: push [rsp + 8] jmp _next b_pick = 0x36 bcmd_pick: pop rcx push [rsp + 8*rcx] jmp _next b_roll = 0x37 bcmd_roll: pop rcx mov rbx, [rsp + 8*rcx] roll1: mov rax, [rsp + 8*rcx - 8] mov [rsp + 8*rcx], rax dec rcx jnz roll1 push rbx jmp _next b_depth = 0x38 bcmd_depth: mov rax, init_stack sub rax, rsp shr rax, 3 push rax jmp _next b_get = 0x40 bcmd_get: pop rcx push [rcx] jmp _next b_set = 0x41 bcmd_set: pop rcx pop rax mov [rcx], rax jmp _next b_get8 = 0x42 bcmd_get8: pop rcx movsx rax, byte ptr [rcx] push rax jmp _next b_set8 = 0x43 bcmd_set8: pop rcx pop rax mov [rcx], al jmp _next b_get16 = 0x44 bcmd_get16: pop rcx movsx rax, word ptr [rcx] push rax jmp _next b_set16 = 0x45 bcmd_set16: pop rcx pop rax mov [rcx], ax jmp _next b_get32 = 0x46 bcmd_get32: pop rcx movsx rax, dword ptr [rcx] push rax jmp _next b_set32 = 0x47 bcmd_set32: pop rcx pop rax mov [rcx], eax jmp _next # 0= b_zeq = 0x50 bcmd_zeq: pop rax or rax, rax jnz rfalse rtrue: push -1 jmp _next rfalse: push 0 jmp _next # 0< b_zlt = 0x51 bcmd_zlt: pop rax or rax, rax jl rtrue push 0 jmp _next # 0> b_zgt = 0x52 bcmd_zgt: pop rax or rax, rax jg rtrue push 0 jmp _next # = b_eq = 0x53 bcmd_eq: pop rbx pop rax cmp rax, rbx jz rtrue push 0 jmp _next # < b_lt = 0x54 bcmd_lt: pop rbx pop rax cmp rax, rbx jl rtrue push 0 jmp _next # > b_gt = 0x55 bcmd_gt: pop rbx pop rax cmp rax, rbx jg rtrue push 0 jmp _next # <= b_lteq = 0x56 bcmd_lteq: pop rbx pop rax cmp rax, rbx jle rtrue push 0 jmp _next # >= b_gteq = 0x57 bcmd_gteq: pop rbx pop rax cmp rax, rbx jge rtrue push 0 jmp _next b_branch8 = 0x10 bcmd_branch8: movsx rax, byte ptr [r8] add r8, rax jmp _next b_branch16 = 0x11 bcmd_branch16: movsx rax, word ptr [r8] add r8, rax jmp _next b_qbranch8 = 0x12 bcmd_qbranch8: pop rax or rax, rax jnz bcmd_branch8 inc r8 jmp _next b_qbranch16 = 0x13 bcmd_qbranch16: pop rax or rax, rax jnz bcmd_branch16 add r8, 2 jmp _next b_qnbranch8 = 0x14 bcmd_qnbranch8: pop rax or rax, rax jz bcmd_branch8 inc r8 jmp _next b_qnbranch16 = 0x15 bcmd_qnbranch16:pop rax or rax, rax jz bcmd_branch16 add r8, 2 jmp _next b_bad = 0x00 bcmd_bad: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 — stdout mov rsi, offset msg_bad_byte #     mov rdx, msg_bad_byte_len #   syscall #   mov rax, 60 #   № 1 - sys_exit mov rbx, 1 #    1 syscall #   b_bye = 0x01 bcmd_bye: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 — stdout mov rsi, offset msg_bye #     mov rdx, msg_bye_len #   syscall #   mov rax, 60 #   № 60 - sys_exit mov rdi, 0 #    0 syscall #   b_type = 0x80 bcmd_type: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 - stdout pop rdx pop rsi push r8 syscall #   pop r8 jmp _next 

ملخص


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

سنة جديدة سعيدة للجميع!

النقد مرحب به! :)

استمرار: آلة بايت للحصن (وليس فقط) في أمريكا الأصلية (جزء 3)

Source: https://habr.com/ru/post/ar433836/


All Articles