
لقد حان عام 2019. لقد اقتربت عطلة رأس السنة الجديدة. حان الوقت لبدء تذكر البايت والأوامر والمتغيرات والحلقات ...
شيء قد نسيت بالفعل مع هذه الأعياد. يجب أن نتذكر معا!
اليوم سنقوم بعمل مترجم لآلة البايت الخاصة بنا. هذه هي المقالة الثالثة ، الأجزاء الأولى هنا:
الجزء 1 ،
الجزء 2 .
سنة جديدة سعيدة للجميع ، ومرحبا بكم في خفض!
بادئ ذي بدء ، سأجيب على أسئلة من
fpauk . هذه الأسئلة صحيحة تماما. الآن بنية هذا الجهاز البايت هو أننا نعمل مع عناوين المعالج المباشر. لكن في الكود الثاني هذه العناوين ليست كذلك ، فهي تتشكل بعد بدء النظام. بعد بدء تشغيل النظام ، يمكننا إنشاء أي مؤشرات ، وسوف يعمل هذا الرمز بشكل صحيح على أي نظام أساسي. على سبيل المثال ، يمكن الحصول على عنوان متغير أو صفيف باستخدام الأمر var0. سيعمل هذا الأمر على أي نظام أساسي وسيُرجع العنوان الصحيح الخاص بهذا النظام الأساسي. ثم يمكنك العمل مع هذا العنوان كما تريد.
ولكن لا يزال ،
fpauk هو الصحيح. لا يمكن تخزين العنوان في الرمز البريدي. اتضح أنه بإمكاننا كتابة رمز مستقل عن النظام الأساسي ، ولكن لهذا نحتاج إلى بذل بعض الجهود. على وجه الخصوص ، تأكد من أن العناوين ليست في الرمز البريدي. ويمكنهم الدخول ، على سبيل المثال ، إذا قمت بحفظ التعليمات البرمجية المترجمة إلى ملف. سوف تحتوي على بيانات ، ويمكن أن تكون عناوين. على سبيل المثال ، قيم المتغيرات هنا والسياق وغيرها.
للتخلص من هذه المشكلة ، تحتاج إلى جعل العناوين افتراضية. عنونة معالج x86 قوية للغاية ، وفي معظم الحالات لن يضيف أوامر إضافية. لكن مع ذلك ، سأستمر في البنية الحالية ، بعناوين مطلقة. وبعد ذلك ، عندما نصل إلى الاختبارات ، سيكون من الممكن إعادة تشكيل العناوين إلى عناوين افتراضية ، ومعرفة كيف سيؤثر ذلك على الأداء. هذا مثير للاهتمام.
الاحماء
والآن تجريب قليلا. لنجعل جزءًا آخر من أوامر البايت الصغيرة ولكن المفيدة. ستكون هذه هي الأوامر nip و emit و 1+ و +! و -! ، عدد الكلمات العمل مع كومة الإرجاع r> ،> r ، r @ ، سلسلة حرفية (") ، والكلمات الثابتة 1 ، 2 ، 3 ، 4 ، 8. لا تنسى تضمينها في جدول الأوامر.
هنا هو رمز لهذه الأوامرb_nip = 0x39 bcmd_nip: pop rax mov [rsp], rax jmp _next b_emit = 0x81 bcmd_emit: pop rax mov rsi, offset emit_buf # mov [rsi], al mov rax, 1 # № 1 - sys_write mov rdi, 1 # № 1 - stdout mov rdx, 1 # push r8 syscall # pop r8 jmp _next b_wp = 0x18 bcmd_wp: incq [rsp] jmp _next b_setp = 0x48 bcmd_setp: pop rcx pop rax add [rcx], rax jmp _next b_setm = 0x49 bcmd_setm: pop rcx pop rax sub [rcx], rax jmp _next b_2r = 0x60 bcmd_2r: pop rax sub rbp, 8 mov [rbp], rax jmp _next b_r2 = 0x61 bcmd_r2: push [rbp] add rbp, 8 jmp _next b_rget = 0x62 bcmd_rget: push [rbp] jmp _next b_str = 0x82 bcmd_str: movzx rax, byte ptr [r8] lea r8, [r8 + rax + 1] jmp _next b_count = 0x84 bcmd_count: pop rcx movzx rax, byte ptr [rcx] inc rcx push rcx push rax jmp _next b_num1 = 0x03 bcmd_num1: push 1 jmp _next b_num2 = 0x04 bcmd_num2: push 2 jmp _next b_num3 = 0x05 bcmd_num3: push 3 jmp _next b_num4 = 0x06 bcmd_num4: push 4 jmp _next b_num8 = 0x07 bcmd_num8: push 8 jmp _next
يزيل الأمر nip الكلمة الموجودة أسفل الجزء العلوي من المكدس. وهو ما يعادل أوامر إسقاط المبادلة. هذا يمكن أن يكون في بعض الأحيان مفيدة.
يدفع الأمر emit حرفًا واحدًا من المكدس. يستخدم نفس رقم استدعاء النظام 1 ، يضع الحرف في مخزن مؤقت بطول 1.
أمر العد بسيط للغاية - يأخذ عنوان السطر مع العداد من المكدس ويحوله إلى قيمتين - عنوان السطر بدون العداد والطول.
الأوامر b_2r ، b_r2 ، b_rget هي الكلمات الحصينة r> ،> r ، r @. الأول يأخذ الكلمة من مكدس الإرجاع ويضعها على المكدس الحسابي. الثاني ينفذ العملية المعاكسة. الثالث ينسخ الكلمة من مكدس الإرجاع ، يضعها في الحساب ، لا يتغير مكدس الإرجاع.
الأوامر b_setp و b_setm هي الكلمات +! و -! .. يأخذون القيمة والعنوان من المكدس ، ويقومون بتعديل الكلمة في العنوان المحدد ، إضافة أو إزالة القيمة من المكدس.
يحتوي الأمر b_str على معلمة الطول التعسفي - خط مع عداد. يقع هذا الخط في الرمز الفرعي بعد بايت الأمر ، ويدفع الأمر ببساطة عنوان هذا الخط إلى المكدس. في الواقع ، هذه سلسلة حرفية.
أعتقد أن بقية الفريق لا يحتاج إلى تعليقات.
سنقوم أيضًا بأمر لطباعة سلسلة ثابتة (. "). وسنطبقها كنقطة إدخال للكتابة ، كما يلي:
b_strp = 0x83 bcmd_strp: movsx rax, byte ptr [r8] inc r8 push rax push r8 add r8, rax 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
هذا الأمر منظم بطريقة مماثلة لـ b_str. انها فقط لا تضع أي شيء على المكدس. يتم عرض السطر الموجود خلف هذا الأمر كمعلمة ببساطة للمستخدم.
انتهى الاحماء ، لقد حان الوقت لشيء أكثر خطورة. دعونا نتعامل مع مولدات الكلمات والأوامر الأخرى.
كلمات المولدات
أذكر المتغيرات. نحن نعرف كيف يتم ترتيبها على مستوى bytecode (var0 command). لإنشاء متغير جديد ، يستخدم الحصن الإنشاء التالي:
variable < >
بعد تنفيذ هذا التسلسل ، يتم إنشاء كلمة جديدة <اسم متغير>. تنفيذ هذه الكلمة الجديدة يدفع العنوان على المكدس لتخزين قيمة المتغير. هناك أيضا ثوابت في الحصن ، يتم إنشاؤها مثل هذا:
<> constant < >
بعد إنشاء الثابت ، يتم تنفيذ كلمة <اسم ثابت> على المكدس <القيمة>.
لذلك ، كل من متغير الكلمة وكلمة ثابت كلمات مولد. وهي مصممة لخلق كلمات جديدة. في الحصن ، يتم وصف مثل هذه الكلمات باستخدام الإنشاء ... لا> الإنشاء.
يمكن تعريف المتغيرات والثوابت كما يلي:
: variable create 0 , does> ; : constant create , does> @ ;
ماذا يعني كل هذا؟
عند إنشاء كلمة ، تُنشئ الكلمة إنشاء كلمة جديدة بالاسم الذي ستتخذه عند تنفيذها من دفق الإدخال. بعد الخلق ، يتم تنفيذ سلسلة من الكلمات قبل الكلمة>. ولكن في لحظة تنفيذ هذه الكلمة ، يتم تنفيذ ما يكتب بعد. في الوقت نفسه ، سيكون عنوان البيانات بالفعل في المجموعة (كما يقولون في الحصن ، "حقول البيانات").
وبالتالي ، عند إنشاء متغير ، يتم تنفيذ التسلسل "0" ، وهذا هو حجز كلمة آلة بدون ملء. وعندما يتم تنفيذ الكلمة التي تم إنشاؤها ، لا يتم عمل شيء (بعد ذلك> لا يوجد شيء). يبقى عنوان الذاكرة حيث يتم تخزين القيمة ببساطة على المكدس.
في تعريف الثابت ، يتم حجز كلمة ذات قيمة تعبئة على المكدس. عند تنفيذ الكلمة التي تم إنشاؤها ، يتم تنفيذ "@" ، والتي تسترد القيمة في العنوان المحدد.
الآن دعونا نفكر في كيفية ترتيب الكلمة التي ننشئها. إنه يدفع عنوان البيانات إلى المكدس (مثل var0) ، ثم ينقل التحكم إلى عنوان محدد ، رمز ثانوي. يعود الأمر var0 على الفور. لكن في هذه الحالة ، نحتاج إلى عدم العودة ، ولكن في الواقع ، الانتقال.
مرة أخرى سأقوم بصياغة ما يجب القيام به:
- وضع عنوان البيانات على المكدس
- القفز إلى قطعة من التعليمات البرمجية بعد هل>
لقد انتهى الأمر ، فأنت تحتاج فقط إلى نقل التحكم إلى عنوان بايت آخر ، ولكن أولاً ضع عنوان البايتة التالية (R8) على الحزمة.
إنه أمر فرعي تقريبًا! وهي هنا ليست وحدها. لديك بالفعل branch8 و branch16. سنقوم بتسمية الأمرين var8 و var16 الجديدين ، ونسمح لهما بأن يكونا مجرد نقاط دخول للأوامر الفرعية. نحن ننقذ عملية الانتقال إلى فريق النقل :) لذلك ، سيكون مثل هذا:
b_var8 = 0x29 bcmd_var8: push r8 b_branch8 = 0x10 bcmd_branch8: movsx rax, byte ptr [r8] add r8, rax jmp _next b_var16 = 0x30 bcmd_var16: push r8 b_branch16 = 0x11 bcmd_branch16: movsx rax, word ptr [r8] add r8, rax jmp _next
بطريقة جيدة ، سيظل الأمر var32 يعمل ، و var64 أيضًا. ليس لدينا مثل هذه التحولات الطويلة ، لأن التحولات العادية ليست طويلة. ولكن بالنسبة إلى الأمر var ، فهذه حالة واقعية للغاية. لكن في الوقت الحالي ، لن نفعل هذه الأوامر. سنفعل لاحقا ، إذا لزم الأمر.
مع الكلمات المولدات فرزها. كان دور للبت في القاموس.
المفردات
عادة ، عندما يتحدثون بطريقة مبسطة عن قاموس الحصن ، يتم تقديمه في شكل قائمة أحادية الاتجاه لإدخالات القاموس. في الواقع ، كل شيء أكثر تعقيدًا قليلاً ، لأن الحصن يدعم العديد من القواميس. في الواقع ، فهي شجرة. يبدأ البحث عن كلمة في هذه الشجرة بـ "ورقة" - هذه هي الكلمة الأخيرة في القاموس الحالي. يتم تعريف القاموس الحالي بواسطة متغير السياق ، وعنوان الكلمة الأخيرة في كلمة القاموس. يتم استخدام متغير آخر لإدارة القواميس - فهو يحدد قاموسًا حيث سيتم إضافة كلمات جديدة. وبالتالي ، يمكن تثبيت قاموس واحد للبحث ، وآخر لتضمين كلمات جديدة.
بالنسبة لحالتنا البسيطة ، سيكون من الممكن عدم دعم العديد من القواميس ، لكنني قررت عدم تبسيط أي شيء. في الواقع ، من أجل فهم رمز البايت ، آلة البايت ، ليس من الضروري معرفة ما هو موصوف في هذا القسم. لذلك ، من غير المهتمين ، يمكنك ببساطة تخطي هذا القسم. حسنًا ، من يريد معرفة التفاصيل - تابع!
في البداية ، هناك قاموس أساسي اسمه. هذا يعني أن هناك مثل هذه الكلمة. وتسمى هذه الكلمة أيضا "القاموس" ، وهناك بعض الالتباس. لذلك ، عندما يتعلق الأمر بكلمة ، سأطلق عليها كلمة القاموس.
يتم إنشاء قواميس جديدة باستخدام هذا البناء:
vocabulary < >
يؤدي هذا إلى إنشاء كلمة باسم <اسم القاموس الذي تم إنشاؤه>. عند تنفيذها ، ستقوم هذه الكلمة بتعيين القاموس الذي تم إنشاؤه على أنه قاموس البدء للبحث.
في الواقع ، في كلمة القاموس هناك رابط للمقال الأخير من هذا القاموس ، والذي يبدأ البحث. وفي وقت التنفيذ ، تكتب كلمة القاموس هذه رابطًا إلى حقل البيانات الخاص بها في متغير السياق.
في وقت لاحق سيكون من الممكن جعل كلمة المفردات ، والتي في الحصن ، في التنفيذ الحالي ، موصوفة بكل بساطة:
: vocabulary create context @ , does> context ! ;
لذلك ، قم بإنشاء الكلمة. سوف نستخدم الأمر var8. Bytecode "السياق!" ضع مباشرة بعد حقل البيانات:
forth: .byte b_var8 .byte does_voc - . - 1 .quad 0 # <-- . , - . does_voc: .byte b_call8 .byte context - . - 1 .byte b_set .byte b_exit
عاد الآن إلى إنشاء القاموس نفسه.
بشكل عام ، في الحصن ، يُسمى وصف كلمة في الذاكرة "إدخال القاموس". بعبارات عادية ، أود أن أقول أن هناك عنوان المقال ورمزه. لكن كل شيء ليس معتادًا في الحصن ؛ حيث يطلق عليه "حقل الاسم" و "حقل الاتصال" و "حقل الرمز" و "حقل البيانات". سأحاول إخبارك ماذا يعني كل هذا من الناحية التقليدية.
حقل الاسم هو اسم الكلمة ، "تمشيا مع عداد." يبدو كما لو كان في باسكال القديم - بايت طول السلسلة ، ثم السلسلة. حقل الارتباط هو رابط للمقال السابق. في السابق ، كان هناك مجرد عنوان ، ولكن سيكون لدينا رمز مستقل عن النظام الأساسي ، وسيكون هذا إزاحة. يعد حقل الرمز ، وهو تقليديًا في الحصن ، رمزًا للجهاز (عندما يكون التنفيذ على خط مباشر) ، وبالنسبة للكلمات الموجودة خارج النواة ، كان هناك استدعاء _call. سيكون لدينا مجرد رمز ثانوي. ومجال البيانات مخصص للكلمات التي تحتوي على بيانات - على سبيل المثال ، للمتغيرات أو الثوابت. بالمناسبة ، يشير قاموس الكلمات أيضًا إلى ذلك.
للمترجم ، ما زلنا بحاجة إلى أعلام. عادة ما يحتاج الحصن إلى علم واحد - فوري ، ويتم وضعه في بايت طويل (أحيانًا يكون هناك آخر - مخفي). ولكن هذا مخصص لرمز مخيط مباشر ، حيث يتم نقل تحكم المعالج عند استدعائه إلى حقل الرمز. ولدينا كلمات مختلفة - كود الشفرة ورمز الجهاز ، وهناك حاجة إلى اثنين أو ثلاثة على الأقل من الأعلام.
كم هو مطلوب لحقل الاتصالات؟ في البداية ، أردت استخدام 16 بت. هذا رابط للكلمة السابقة ، والكلمة بالتأكيد أقل من 64 كيلو بايت. ولكن بعد ذلك تذكرت أن الكلمة يمكن أن تحتوي على بيانات من أي حجم تقريبًا. وإلى جانب ذلك ، في وجود العديد من القواميس ، يمكن أن يمر الرابط بالعديد من الكلمات. اتضح أنه في معظم الحالات ، 8 بتات كافية ، ولكن يمكن أن يكون هناك 16 و 32. وحتى 64 بت ، إذا كان هناك بيانات أكثر من 4 جيجابايت. حسنًا ، دعنا ندعم جميع الخيارات. الخيار الذي يستخدم - وضعت في الأعلام. لقد ظهر ما لا يقل عن 4 أعلام: السمة العاجلة ، السمة word الأساسية ، و 2 بت لكل متغير من حقل الاتصال المستخدم. من الضروري استخدام بايت منفصل للأعلام ، وليس بأي طريقة أخرى.
نحدد الأعلام على النحو التالي:
f_code = 0x80 f_immediate = 0x60
ستكون علامة f_code للكلمات kernel المكتوبة في المجمّع ، وستكون علامة f_immediate مفيدة للمترجم ، حول هذا الموضوع في المقالة التالية. وسيحدد البتان الأقل أهمية طول حقل الاتصال (1 أو 2 أو 4 أو 8 بايت).
لذلك ، سيكون عنوان المقال مثل هذا:
- أعلام (1 بايت)
- مجال الاتصال (1-8 بايت)
- اسم طول بايت
- الاسم (1-255 بايت)
حتى هذه النقطة ، لم أستخدم إمكانيات المجمّع "الكلي". والآن نحن في حاجة إليها. هكذا حصلت على ماكرو مع عنصر الاسم لتشكيل عنوان الكلمة:
.macro item name, flags = 0 link = . - p_item 9: .if link >= -256/2 && link < 256/2 .byte \flags .byte link .elseif link >= -256*256/2 && link < 256*256/2 .byte \flags | 1 .word . - p_item .elseif link >= -256*256*256*256/2 && link < 256*256*256*256/2 .byte \flags | 2 .int . - p_item .elseif link >= -256*256*256*256*256*256*256*256/2 && link < 256*256*256*256*256*256*256*256/2 .byte \flags | 3 .quad . - p_item .endif p_item = 9b .byte 9f - . - 1 .ascii "\name" 9: .endm
يستخدم هذا الماكرو قيمة p_item - هذا هو عنوان إدخال القاموس السابق. يتم تحديث هذه القيمة في النهاية للاستخدام في المستقبل: p_item = 9b. هنا 9b عبارة عن تصنيف ، وليس رقمًا ، لا تخلط بينه :) يحتوي الماكرو على معلمتين - اسم الكلمة والأعلام (اختياري). في بداية الماكرو ، يتم حساب الإزاحة للكلمة السابقة. ثم ، بناءً على حجم الإزاحة ، يتم تجميع الأعلام وحقل الاتصال بالحجم المطلوب. ثم بايت طول الاسم والاسم نفسه.
حدد قبل الكلمة الأولى p_item كما يلي:
p_item = .
النقطة هي عنوان الترجمة الحالي في المجمع. نتيجة لهذا التعريف ، ستشير الكلمة الأولى إلى نفسها (سيكون حقل الاتصال 0). هذه علامة على نهاية القواميس.
بالمناسبة ، ماذا سيكون في حقل الكود لكلمات النواة؟ كحد أدنى ، يجب عليك حفظ رمز الأمر في مكان ما. قررت أن أسير في أبسط طريق. بالنسبة لكلمات kernel ، سيكون هناك أيضًا رمز ثانوي. بالنسبة لمعظم الفرق ، سيكون هذا مجرد أمر بايت ، يليه b_exit. وبالتالي ، بالنسبة للمترجم الشفوي ، لا يلزم تحليل علامة f_code ، ولن تختلف الأوامر الخاصة بها بأي طريقة. تحتاج فقط إلى استدعاء الرمز البريدي للجميع.
هناك ميزة أخرى لهذا الخيار. بالنسبة للأوامر ذات المعلمات ، يمكنك تحديد معلمات آمنة. على سبيل المثال ، إذا قمت باستدعاء الأمر lit في تطبيقات Fort باستخدام رمز مخيط مباشر ، فسيتعطل النظام. وهنا سيتم كتابتها هناك ، على سبيل المثال ، مضاءة 0 ، وهذا التسلسل ببساطة يضع 0 على المكدس. حتى للفرع يمكن القيام به بأمان!
.byte branch8 .byte 0f - . 0: .byte b_exit
مع وجود مثل هذه المكالمة ، سيكون هناك بعض النفقات العامة ، لكن بالنسبة للمترجم الفوري لن تكون ذات أهمية. سيقوم المترجم بتحليل الأعلام وتجميع الكود الصحيح والسريع.
ستكون الكلمة الأولى ، بالطبع ، الكلمة "ذهابًا وإيابًا" - المفردات الأساسية التي نخلقها. هنا ، ما عليك سوى استخدام أمر var مفيد مع وجود رابط إلى الكود بعد>. سبق أن أشرت إلى هذا الرمز في القسم السابق ، لكنني سأكرره مرة أخرى ، مع العنوان:
p_item = . item forth .byte b_var8 .byte does_voc - . - 1 .quad 0 does_voc: .byte b_call8 .byte context - . - 1 .byte b_set .byte b_exit
وسنقوم على الفور بعمل متغيرات السياق ، ونحن بحاجة إليها للبحث عن الكلمات:
item .byte b_var0 .quad 0 item context context: .byte b_var0 .quad 0
والآن ، عليك التحلي بالصبر وكتابة عنوان لكل كلمة كتبناها في مجمع مع علامة f_code:
item 0, f_code .byte b_num0 .byte b_exit item 1, f_code .byte b_num1 .byte b_exit ... item 1-, f_code .byte b_wm .byte b_exit item 1+, f_code .byte b_wp .byte b_exit item +, f_code .byte b_add .byte b_exit item -, f_code .byte b_sub .byte b_exit item *, f_code .byte b_mul .byte b_exit
وهلم جرا ...
مع فرق مكتوبة في bytecode هو أسهل. يكفي إضافة عنوان فقط قبل الرمز الفرعي ، تمامًا مثل الكلمة ، على سبيل المثال:
item hold hold: .byte b_call8 .byte holdpoint - . - 1 # holdpoint ...
بالنسبة للأوامر ذات المعلمات ، سنجعل المعلمات آمنة. على سبيل المثال ، دع أوامر lite تُرجع الرقم Pi ، إذا اتصل بهم شخص ما بشكل تفاعلي ، فسيكون هناك مثل هذا الفصح :)
item lit8, f_code .byte b_lit8 .byte 31 .byte b_exit item lit16, f_code .byte b_lit16 .word 31415 .byte b_exit item lit32, f_code .byte b_lit32 .int 31415926 .byte b_exit item lit64, f_code .byte b_lit64 .quad 31415926535 .byte b_exit
ستجعل الكلمة الأخيرة في القائمة الكلمة وداعًا. لكن ما زلنا بحاجة إلى تهيئة عنوان هذه الكلمة في حقل البيانات. للحصول على عنوان هذه الكلمة ، استخدم الأمر var0:
last_item: .byte b_var0 item bye, f_code .byte b_bye
في هذا التصميم ، إذا اتصلنا بالعنوان last_item في الرمز البريدي ، فسنحصل على عنوان كلمة bye. لكتابتها في حقول البيانات الخاصة بالكلمة إيابا ، قم بتنفيذها ، وسيكون العنوان المطلوب في السياق. وبالتالي ، سيكون رمز تهيئة النظام كما يلي:
forth last_item context @ !
والآن دعنا ننتقل مباشرة إلى المترجم الفوري. أولاً ، نحتاج إلى العمل مع المخزن المؤقت للمدخلات واستخراج الكلمات منه. دعني أذكرك أن المترجم الفوري في الحصن بسيط للغاية. يستخلص الكلمات من مخزن الإدخال المؤقت بالتسلسل ، ويحاول العثور عليها. إذا تم العثور على الكلمة ، يقوم المترجم بإطلاقها للتنفيذ.
إدخال العازلة واستخراج الكلمة
أن نكون صادقين ، أنا لا أريد أن أقضي الكثير من الوقت في دراسة معايير الحصن. ولكن ما زلت سأحاول جعله أقرب ما يكون لهم ، ولا سيما من الذاكرة. إذا كان خبراء الحصن سيرون تباينًا قويًا هنا - اكتب ، سوف أقوم بإصلاحه.
يحتوي الحصن على ثلاثة متغيرات للعمل مع المخزن المؤقت: tib و #tib و> in. المتغير tib يدفع عنوان المخزن المؤقت الإدخال على المكدس. متغير #tib يدفع عدد الأحرف الموجودة في المخزن المؤقت إلى المكدس. يحتوي المتغير> على الإزاحة في مخزن الإدخال المؤقت ، والذي يقع بعده النص الخام. تحديد هذه المتغيرات.
item tib .byte b_var0 v_tib: .quad 0 item #tib .byte b_var0 v_ntib: .quad 0 item >in .byte b_var0 v_in: .quad 0
التالي نجعل كلمة اللوم. هذه الكلمة ، باستخدام المتغيرات المحددة ، تحصل على الكلمة التالية من دفق الإدخال. يتم استخدام مساحة كمحددات وكافة الأحرف مع رمز أقل من مساحة. هذه الكلمة ستكون في المجمع. بعد تصحيح الأخطاء ، اتضح مثل هذا:
b_blword = 0xF0 bcmd_blword: mov rsi, v_tib # mov rdx, rsi # RDX mov rax, v_in # mov rcx, v_ntib # add rsi, rax # RSI - sub rcx, rax # jz 3f word2: lodsb # AL RSI cmp al, ' ' ja 1f # ( ) dec rcx jnz word2 # 3: sub rsi, rdx mov v_in, rsi push rcx jmp _next 1: lea rdi, [rsi - 1] # RDI = RSI - 1 ( ) dec rcx word3: lodsb cmp al, ' ' jbe 2f dec rcx jnz word3 2: mov rax, rsi sub rsi, rdx # ( ) mov v_in, rsi sub rax, rdi dec rax jz word1 push rdi # word1: push rax # jmp _next
تشبه هذه الكلمة الكلمة القياسية ، ولكن على عكسها ، تأخذ في الاعتبار جميع المحددات ولا تنسخ الكلمة إلى المخزن المؤقت. تقوم بإرجاع قيمتين فقط على المكدس - العنوان والطول. إذا كانت الكلمة لا يمكن استرجاعها ، فتُرجع 0. لقد حان الوقت لبدء كتابة المترجم الفوري.
كلمة البحث والمترجم الفوري
للبدء ، دعونا نجعل الكلمة تفسر. تحدد هذه الكلمة كلمة جديدة من المخزن المؤقت باستخدام blworld ، وتبحث عنها في القاموس ، وتقوم بتنفيذها. وهكذا يتكرر حتى يتم استنفاد المخزن المؤقت. ما زلنا لا نملك القدرة على البحث عن كلمة ، لذلك سنكتب كعب اختبار يطبع ببساطة الكلمة من المخزن المؤقت باستخدام النوع. هذا سيتيح لنا الفرصة للتحقق وتصحيح blworld:
# : interpret begin blword dup while type repeat drop ; item interpret 1: .byte b_blword .byte b_dup .byte b_qnbranch8 .byte 0f - . .byte b_type .byte b_branch8 .byte 1b - . 0: .byte b_drop .byte b_exit
الآن جعل الكلمة استقال. عادة ما يقومون بذلك عند تطبيق أنظمة الحصون: يستخدمون الكلمة إنهاء أو إحباط للدخول في وضع المترجم. الكلمة إنهاء مسح المداخن ويبدأ حلقة لا نهاية لها من المدخلات المخزن المؤقت والتفسير. معنا سيكون مجرد دعوة للتفسير. يتكون رمز هذه الكلمة من جزأين. الجزء الأول سيكون في المجمع ، الجزء الثاني سيكون في الرمز البريدي. الجزء الأول:
b_quit = 0xF1 bcmd_quit: lea r8, quit mov sp, init_stack mov bp, init_rstack jmp _next
الجزء الثاني:
quit: .byte b_call16 .word interpret - . - 2 .byte b_bye
كالعادة ، يوجد رمز المجمّع في قسم النص. رمز البايت موجود في قسم البيانات.
وأخيرًا ، قم بتغيير كود بدء التشغيل. لن يكون هناك سوى تهيئة للقاموس ، ووضع مخزن مؤقت على خط البداية ، وإنهاء الاتصال.
# forth last_item context @ ! start_code tib ! < > #tib ! quit start: .byte b_call16 .word forth - . - 2 .byte b_call16 .word last_item - . - 2 .byte b_call16 .word context - . - 2 .byte b_get .byte b_set .byte b_call8 .byte start_code - . - 1 .byte b_call16 .word tib - . - 2 .byte b_set .byte b_lit16 .world 1f - 0f .byte b_call16 .word ntib - . - 2 .byte b_set .byte b_quit start_code: .byte b_var0 0: .ascii "word1 word2 word3" 1:
ترجمة ، وصلة ، تشغيل!
$ as forth.s -o forth.o -g -ahlsm >list.txt $ ld forth.o -o forth $ ./forth word1word2wordBye!
يشبه العصيدة بعض الشيء ، ولكن هذا هو بالضبط ما يجب أن تكون عليه النتيجة. نخرج بدون محددات. بالمناسبة ، ضع خط التغذية قبل الشراء للمستقبل ، هذا لن يضر.
بالطبع ، كان علي أن العبث تصحيح الأخطاء. بالإضافة إلى "خطأ التقسيم (الذي تم إلقاؤه)" ، تم الحصول على نتائج مثيرة للاهتمام في بعض الأحيان. على سبيل المثال ، هذا:
$ ./forth word1word2word3forth)%60Acurrent(context(%600lit8lit16zlit32v%5E%DF%80lit64v%5E%DF%80call8call16call32branch8branch16qbranch8qbranch16exit1-+!-%22*#/$mod%25/mod&abs'dup0drop1swap2rot3-rot4over5pick6roll7depth8@@!Ac@Bc!Cw@Dw!Ei@Fi!G0=P0%3CQ0%3ER=S%3CT%3EU%3C=V%3E=Wvar8)var160base(holdbuf(Qholdpoint(hold@0U110ACp@&20T0!?!%3CgF!A0@RF!5%220'%DE%A61Q-%DD%80:tib(%7F%60(%3Ein(%20%20%20%20%20%20%20interpret01('byeSegmentation%20fault%20(core%20dumped)
يبدو أن هذا هو قاموسنا الثنائي بالكامل مع نص مقسم إلى محددات :) لقد حدث ذلك عندما نسيت "dec rcx" قبل word3 في الأمر b_blword.
نحن قادرون على اختيار الكلمات من تيار الإدخال ، هناك قاموس. الآن تحتاج إلى تنفيذ بحث القاموس وإطلاق كلمات للتنفيذ. هذا سيتطلب الكلمات find، cfa و execute.
تأخذ الكلمة find عنوان الكلمة وطولها من المكدس. سيتم إرجاع هذه الكلمة بواسطة عنوان إدخال القاموس أو 0 إذا لم يتم العثور عليها.
الكلمة cfa في عنوان المقالة ستحسب عنوان الكود القابل للتنفيذ.
والكلمة تنفيذ سوف تنفذ bytecode.
لنبدأ بالبحث. في معايير الحصن ، فإنه يأخذ عنوانًا واحدًا - سطر مع عداد. لكنني لا أريد نسخ السلسلة مرة أخرى إلى المخزن المؤقت ، لذلك سأنحيد قليلاً عن المعايير. ستأخذ الكلمة find معلمتين على الحزمة - عنوان وطول السلسلة (في الواقع ، تُرجع الكلمة blword). بعد تصحيح الأخطاء ، أخذت هذه الكلمة النموذج التالي:
b_find = 0xF2 bcmd_find: pop rbx # pop r9 # mov rdx, v_context mov rdx, [rdx] # # find0: mov al, [rdx] # and al, 3 # - , , or al, al jz find_l8 cmp al, 1 jz find_l16 cmp al, 2 jz find_l32 mov r10, [rdx + 1] # 64 lea rsi, [rdx + 9] # jmp find1 find_l32: movsx r10, dword ptr [rdx + 1] # 32 lea rsi, [rdx + 5] # jmp find1 find_l16: movsx r10, word ptr [rdx + 1] # 16 lea rsi, [rdx + 3] # jmp find1 find_l8: movsx r10, byte ptr [rdx + 1] # 8 lea rsi, [rdx + 2] # find1: movzx rax, byte ptr [rsi] # cmp rax, rbx jz find2 # find3: or r10, r10 jz find_notfound # , add rdx, r10 # jmp find0 # , find2: inc rsi mov rdi, r9 mov rcx, rax repz cmpsb jnz find3 # push rdx jmp _next find_notfound: push r10 jmp _next
ربما هذه هي الكلمة الأكثر صعوبة لهذا اليوم. الآن نقوم بتعديل كلمة التفسير ، مع استبدال الكتابة بـ "find".: # : interpret begin blword dup while find . repeat drop ; item interpret interpret: .byte b_blword .byte b_dup .byte b_qnbranch8 .byte 0f - . .byte b_find .byte b_call16 .word dot - . - 2 .byte b_branch8 .byte interpret - . 0: .byte b_drop .byte b_exit
في سطر الاختبار ، تحتاج إلى وضع الكلمات الموجودة في القاموس ، على سبيل المثال ، "0 1- dup +.".كل شيء جاهز للإطلاق! $ ld forth.o -o forth $ ./forth 6297733 6297898 6298375 Bye!
عظيم ، البحث يعمل. هذه هي عناوين الكلمات (في العشرية). الآن كلمة cfa. فليكن أيضًا في مجمع ، إنه أمر بسيط جدًا ، العمل مع الأعلام يشبه العثور على: b_cfa = 0xF3 bcmd_cfa: pop rdx # mov al, [rdx] # and al, 3 # - , , or al, al jz cfa_l8 cmp al, 1 jz cfa_l16 cmp al, 2 jz cfa_l32 lea rsi, [rdx + 9] # (64 ) jmp cfa1 find_l32: lea rsi, [rdx + 5] # (32 ) jmp cfa1 find_l16: lea rsi, [rdx + 3] # (16 ) jmp cfa1 find_l8: lea rsi, [rdx + 2] # (8 ) xor rax, rax lodsb add rsi, rax push rsi jmp _next
وأخيراً ، يتم تنفيذ الكلمة ، إنها أبسط: b_execute = 0xF4 bcmd_execute: sub rbp, 8 mov [rbp], r8 # pop r8 # - jmp _next
تصحيح كلمة تفسير وتشغيل! # : interpret begin blword dup while find cfa execute repeat drop ; item interpret interpret: .byte b_blword .byte b_dup .byte b_qnbranch8 .byte 0f - . .byte b_find .byte b_cfa .byte b_execute .byte b_branch8 .byte interpret - . 0: .byte b_drop .byte b_exit
إطلاق: $ as forth.s -o forth.o -g -ahlsm >list.txt $ ld forth.o -o forth $ ./forth -2 Bye!
أوررا ، حصل! (C) Cat Matroskin فيالواقع ، إذا قمت بطرح 1 من 0 وأضفت النتيجة لنفسك ، فستكون -2:هذا شيء رائع ، لكنني ما زلت أريد كتابة الأوامر من لوحة المفاتيح. وهناك مشكلة أخرى - حيث يفهم المترجم لدينا الأرقام 0 و 1 و 2 و 3 و 4 و 8 فقط (والتي يتم تعريفها على أنها ثوابت). ما الذي يتعلمه لفهم أي أرقام ، فأنت بحاجة إلى كلمة "رقم"؟ بنفس الطريقة بالنسبة للكلمة find ، لن أستخدم المخزن المؤقت. كلمة "رقم"؟ سوف يستغرق معلمتين على المكدس - عنوان السلسلة والطول. إذا نجحت ، فسوف تُرجع الرقم المستلم وعلامة 1. إذا لم ينجح التحويل ، فسيكون هناك رقم واحد في المجموعة: 0.لقد تم تشغيل الرمز ليكون طويلًا ، ولكنه بسيط وبسيط: b_number = 0xF5 bcmd_number: pop rcx # pop rsi # xor rax, rax # xor rbx, rbx # mov r9, v_base # xor r10, r10 # or rcx, rcx jz num_false mov bl, [rsi] cmp bl, '+' jnz 1f inc rsi dec rcx jz num_false jmp num0 1: cmp bl, '-' jnz num0 mov r10, 1 inc rsi dec rcx jz num_false num0: mov bl, [rsi] cmp bl, '0' ja num_false cmp bl, '9' jae num_09 cmp bl, 'A' ja num_false cmp bl, 'Z' jae num_AZ cmp bl, 'a' ja num_false sub bl, 'a' - 10 jmp num_check num_AZ: sub bl, 'A' - 10 jmp num_check num_09: sub bl, '0' num_check: cmp rbx, r9 jge num_false add rax, rbx mul r9 inc rsi dec rcx jnz num0 or r10, r10 push rax push 1 jmp _next num_false: xor rcx, rcx push rcx jmp _next
تعديل التفسير. إذا لم تكن الكلمة في القاموس ، فسوف نحاول تفسيرها كرقم: # : interpret # begin # blword dup # while # over over find dup # if -rot drop drop cfa execute else number? drop then # repeat # drop ; item interpret interpret: .byte b_blword .byte b_dup .byte b_qnbranch8 .byte 0f - . .byte b_over .byte b_over .byte b_find .byte b_dup .byte b_qnbranch8 .byte 1f - . .byte b_mrot .byte b_drop .byte b_drop .byte b_cfa .byte b_execute .byte b_branch8 .byte 2f - . 1: .byte b_numberq .byte b_drop 2: .byte b_branch8 .byte interpret - . 0: .byte b_drop .byte b_exit last_item: .byte b_var0 item bye, f_code .byte b_bye
وهنا حصلت! تصحيح مثل هذا الرمز في المجمع ، دون نقاط التوقف في الرمز البريدي ، دون القدرة على "خطوة" على طول الرمز الفرعي ... علاوة على ذلك ، مع عدم وجود أسهل الحركات على المكدس ، ودون القدرة البسيطة على عرض محتويات المكدس ... وعلى GDB ، حيث فقط سطر الأوامر ... سأخبرك - إنه مجرد انفجار في الدماغ! ليس أسوأ. هذا هو انفجار الدماغ !لكن ... نحن هنود ، سنجد دائمًاحلولًا :) بشكل عام ، وجدت هذا الحل: قمت بتنفيذ أمر لعرض محتويات المكدس - "s.". الأمر ليس أسهل ، ولكن لا يزال من الأسهل تفسيره. وكما اتضح فيما بعد، ochchchen مفيدة. ومن هنا: # : .s depth dup . c": emit do dup while dup pick . 1- again drop ; item .s # 11 22 33 prstack: .byte b_depth # 11 22 33 3 .byte b_dup # 11 22 33 3 3 .byte b_lit8 .byte '(' .byte b_emit .byte b_call16 # 11 22 33 3 .word dot - . - 2 .byte b_strp # 11 22 33 3 .byte 3 .ascii "): " 1: .byte b_dup # 11 22 33 3 3 .byte b_qnbranch8 # 11 22 33 3 .byte 2f - . .byte b_dup # 11 22 33 3 3 .byte b_pick # 11 22 33 3 11 .byte b_call16 # 11 22 33 3 .word dot - . - 2 .byte b_wm # 11 22 33 2 .byte b_branch8 .byte 1b - . 2: .byte b_drop # 11 22 33 .byte b_exit
على اليمين ، أعطيت مثالا على محتويات المكدس ، بعد تنفيذ كل أمر. بالطبع ، هناك دورة ، وهذه ليست سوى الممر الأول. لكن الباقي متشابهة للغاية ، فقط القيمة في الجزء العلوي من المكدس تتغير. بعد هذا "التتبع" حقق الفريق على الفور!من أجل تصحيح الأخطاء ، قمت بإنشاء وحدات الماكرو التالية: .macro prs new_line = 1 .byte b_call16 .word prstack - . - 2 .if \new_line > 0 .byte b_lit8, '\n' .byte b_emit .endif .endm
تستخدم عن طريق إدخال في الأماكن الصحيحة بهذه الطريقة: item interpret interpret: .byte b_blword prs .byte b_dup prs .byte b_qnbranch8 .byte 0f - . .byte b_over .byte b_over ......
نتيجة لذلك ، أنتج الإطلاق الأول الإخراج التالي: $ ./forth (2 ): 6297664 1 (3 ): 6297664 1 1 (3 ): 2 6297666 1 (4 ): 2 6297666 1 1 (4 ): 2 3 6297668 1 (5 ): 2 3 6297668 1 1 (3 ): 6 6297670 2 (4 ): 6 6297670 2 2 (4 ): 6 6297670 6297673 1 (5 ): 6 6297670 6297673 1 1 6297670 (2 ): 6 0 (3 ): 6 0 0 Bye!
يمكن رؤية كل حركة على المكدس بوضوح. كان من الضروري القيام بذلك في وقت سابق :)ذهبت أبعد من ذلك عن طريق إجراء ماكرو تصحيح آخر: .macro pr string .byte b_strp .byte 9f - 8f 8: .ascii "\n\string" 9: .endm
نتيجة لذلك ، أصبح من الممكن القيام بذلك: item interpret interpret: .byte b_blword pr blworld prs .byte b_dup .byte b_qnbranch8 .byte 0f - . .byte b_over .byte b_over prs .byte b_find pr find prs .byte b_dup .byte b_qnbranch8 .byte 1f - . .byte b_mrot .byte b_drop .byte b_drop .byte b_cfa pr execute prs .byte b_execute .byte b_branch8 .byte 2f - . 1: .byte b_numberq pr numberq prs .byte b_drop 2: .byte b_branch8 .byte interpret - . 0: .byte b_drop .byte b_exit
واحصل على هذا: $ ./forth blworld(2 ): 6297664 2 (4 ): 6297664 2 6297664 2 find(3 ): 6297664 2 0 numberq(2 ): 6297664 0 blworld(3 ): 6297664 6297667 2 (5 ): 6297664 6297667 2 6297667 2 find(4 ): 6297664 6297667 2 0 numberq(3 ): 6297664 6297667 0 blworld(4 ): 6297664 6297667 6297670 1 (6 ): 6297664 6297667 6297670 1 6297670 1 find(5 ): 6297664 6297667 6297670 1 6297958 execute(3 ): 6297664 6297667 6297962 blworld(3 ): 39660590749888 6297672 1 (5 ): 39660590749888 6297672 1 6297672 1 find(4 ): 39660590749888 6297672 1 6298496 execute(2 ): 39660590749888 6298500 39660590749888 blworld(1 ): 0 Bye!
كانت محاولة لتفسير السلسلة "20 30 *.".ويمكنك عرض أرقام الأسطر المصدر ... حسنًا ، ربما بعد ذلك ...بالطبع ، هذه تقنية تسجيل كلاسيكية لتصحيح الأخطاء ، ولكن هناك شيء لم أتذكره على الفور.بشكل عام ، كنتيجة للتصحيح ، وجدت مكدسًا يتجه للخارج. هذا هو عكس الفائض عندما يحاولون اتخاذ أكثر مما وضعوا. أضافت سيطرتها إلى ".s".بمساعدة وحدات ماكرو جديدة ، كان تصحيح الأخطاء سريعًا. بالمناسبة ، قبل ذلك نشرت رمزًا واحدًا لكل سطر. لكن المجمّع يسمح لك بوضع عدة بايت في سلسلة ، لماذا لا تستخدمها.دعنا نختتم ترجمة الكلمة عن طريق إضافة فحصين: أن الكلمة لم يتم تحويلها إلى رقم والخروج من المكدس في الخارج. نتيجة لذلك ، التفسير كالتالي: item interpret interpret: .byte b_blword .byte b_dup .byte b_qnbranch8 .byte 0f - . .byte b_over .byte b_over .byte b_find .byte b_dup .byte b_qnbranch8 .byte 1f - . .byte b_mrot .byte b_drop .byte b_drop .byte b_cfa .byte b_execute .byte b_branch8 .byte 2f - . 1: .byte b_drop .byte b_over, b_over .byte b_numberq # , .byte b_qbranch8, 3f - . # 0, , 3 .byte b_type # .byte b_strp # .byte 19 # .ascii " : word not found!\n" .byte b_quit # 3: .byte b_nip, b_nip # , ( b_over, b_over) 2: # .byte b_depth # .byte b_zlt # , 0 ( 0<) .byte b_qnbranch8, interpret_ok - . # , , .byte b_strp # .byte 14 .ascii "\nstack fault!\n" .byte b_quit # interpret_ok: .byte b_branch8 .byte interpret - . 0: .byte b_drop .byte b_exit
بالمناسبة ، تجدر الإشارة إلى أن أمر الإقلاع عن التدخين يقوم الآن بطرد الأكوام ويبدأ التفسير مرة أخرى دون تغيير حالة المخزن المؤقت. وهكذا ، يستمر التفسير ، ولكن مع مداخن "جديدة". سنقوم بإصلاح هذا في وقت لاحق قليلا.الشيء الوحيد المتبقي هو تنظيم إدخال لوحة المفاتيح.إدخال لوحة المفاتيح
إدخال لوحة المفاتيح في الحصن بسيط. هناك كلمة تتوقع ، ويستغرق معلمتين - عنوان المخزن المؤقت وحجمه. هذه الكلمة يؤدي إدخال لوحة المفاتيح. يتم وضع العدد الفعلي للحروف التي تم إدخالها في المتغير span. دعونا نجعل هذه الكلمات. سوف ندخل من المدخلات القياسية. .data item span span: .byte b_var0 v_span: .quad 0 .text b_expect = 0x88 bcmd_expect: mov rax, 0 # № 1 - sys_read mov rdi, 0 # № 1 - stdout pop rdx # pop rsi # push r8 syscall # pop r8 mov rbx, rax or rax, rax jge 1f xor rbx, rbx 1: mov v_span, rbx jmp _next
الآن نحن بحاجة إلى إنشاء مخزن مؤقت إدخال لوحة المفاتيح. فليكن طولها 256 حرفًا.لنجعلها مكان خط الاختبار السابق. inbuf_size = 256 inbuf: .byte b_var0 .space inbuf_size
ونحن تعديل الإقلاع عن التدخين ، فضلا عن بداية الرمز. عيّن متغير tib إلى مخزن الإدخال المؤقت inbuf ، توقع المكالمة ، ثم انسخ القيمة من span إلى #tib. > في المتغير ملغى ؛ نحن نسمي التفسير. وهكذا نكرر في دورة. هناك مجموعات - لإضافة موجه إدخال وسيكون من الجيد عرض حالة المكدس (ولدينا بالفعل أمر جاهز لهذا!). بعد عدة تكرارات ، حصلنا على الكود التالي (start and quit command): # forth last_item context @ ! quit start: .byte b_call16 .word forth - . - 2 .byte b_call16 .word last_item - . - 2 .byte b_call16 .word context - . - 2 .byte b_get .byte b_set .byte b_quit inbuf: .byte b_var0 .space inbuf_size # begin inbuf dup tib ! inbuf_size expect span @ #tib ! 0 >in ! interpret again quit: .byte b_strp, 1 .ascii "\n" .byte b_call16 .word prstack - . - 2 .byte b_strp .byte 2 .ascii "> " .byte b_call16 .word inbuf - . - 2 .byte b_dup .byte b_call16 .word tib - . - 2 .byte b_set .byte b_lit16 .word inbuf_size .byte b_expect .byte b_call16 .word span - . - 2 .byte b_get .byte b_call16 .word ntib - . - 2 .byte b_set .byte b_num0 .byte b_call16 .word bin - . - 2 .byte b_set .byte b_call16 .word interpret - . - 2 .byte b_branch8, quit - .
وهنا النتيجة: $ ./forth ( 0 ): > 60 ( 1 ): 60 > 60 24 ( 3 ): 60 60 24 > rot ( 3 ): 60 24 60 > -rot ( 3 ): 60 60 24 > swap ( 3 ): 60 24 60 > * * . 86400 ( 0 ): > 200 30 /mod ( 2 ): 20 6 > bye Bye! $
كل شيء بعد رمز ">" هو إدخال لوحة المفاتيح. الباقي هو إجابة النظام. لقد لعبت قليلا مع الأوامر ، والكتابة من لوحة المفاتيح. أجرى عدة عمليات المكدس ، وحساب عدد الثواني في أيام.ملخص
المترجم الكامل ويعمل. ويقول بأدب وداعا - له "وداعا" و "وداعا" :)كدعوة - محتويات المكدس الحسابي. الرقم الأول بين قوسين هو حجم المكدس ، ثم المحتويات ، والمطالبة بإدخال ">". يمكنك إدخال أي أوامر منفذة (حسبت 76 أمرًا). صحيح ، كثير من المنطقي فقط للمترجم - على سبيل المثال ، الحرفي ، والانتقالات ، وأوامر الاحتجاج.مصدر كامل (حوالي 1300 خط) .intel_syntax noprefix stack_size = 1024 f_code = 0x80 f_immediate = 0x60 .macro item name, flags = 0 link = p_item - . 9: .if link >= -256/2 && link < 256/2 .byte \flags .byte link .elseif link >= -256*256/2 && link < 256*256/2 .byte \flags | 1 .word link .elseif link >= -256*256*256*256/2 && link < 256*256*256*256/2 .byte \flags | 2 .int link .elseif link >= -256*256*256*256*256*256*256*256/2 && link < 256*256*256*256*256*256*256*256/2 .byte \flags | 3 .quad link .endif p_item = 9b .byte 9f - . - 1 .ascii "\name" 9: .endm .section .data init_stack: .quad 0 init_rstack: .quad 0 emit_buf: .byte 0 inbuf_size = 256 msg_bad_byte: .ascii "Bad byte code!\n" msg_bad_byte_len = . - msg_bad_byte # len msg_bye: .ascii "\nBye!\n" msg_bye_len = . - msg_bye bcmd: .quad bcmd_bad, bcmd_bye, bcmd_num0, bcmd_num1, bcmd_num2, bcmd_num3, bcmd_num4, bcmd_num8 # 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_wp, 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_var8, bcmd_var16, 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_nip, 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_setp, bcmd_setm, 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_2r, bcmd_r2, bcmd_rget, 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_emit, bcmd_str, bcmd_strp, bcmd_count, bcmd_bad, bcmd_bad, bcmd_bad # 0x80 .quad bcmd_expect, 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 # 0x90 .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_blword, bcmd_quit, bcmd_find, bcmd_cfa, bcmd_execute, bcmd_numberq, bcmd_bad, bcmd_bad # 0xF0 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # forth last_item context @ ! quit start: .byte b_call16 .word forth - . - 2 .byte b_call16 .word last_item - . - 2 .byte b_call16 .word context - . - 2 .byte b_get .byte b_set .byte b_quit inbuf: .byte b_var0 .space inbuf_size # begin inbuf dup tib ! inbuf_size expect span @ #tib ! 0 >in ! interpret again quit: .byte b_strp, 1 .ascii "\n" .byte b_call16 .word prstack - . - 2 .byte b_strp .byte 2 .ascii "> " .byte b_call16 .word inbuf - . - 2 .byte b_dup .byte b_call16 .word tib - . - 2 .byte b_set .byte b_lit16 .word inbuf_size .byte b_expect .byte b_call16 .word span - . - 2 .byte b_get .byte b_call16 .word ntib - . - 2 .byte b_set .byte b_num0 .byte b_call16 .word bin - . - 2 .byte b_set .byte b_call16 .word interpret - . - 2 .byte b_branch8, quit - . p_item = . item forth forth: .byte b_var8 .byte does_voc - . .quad 0 does_voc: .byte b_call8 .byte context - . - 1 .byte b_set .byte b_exit item current .byte b_var0 .quad 0 item context context: .byte b_var0 v_context: .quad 0 item 0, f_code .byte b_num0 .byte b_exit item 1, f_code .byte b_num1 .byte b_exit item 2, f_code .byte b_num2 .byte b_exit item 3, f_code .byte b_num3 .byte b_exit item 4, f_code .byte b_num4 .byte b_exit item 8, f_code .byte b_num8 .byte b_exit item lit8, f_code .byte b_lit8 .byte 31 .byte b_exit item lit16, f_code .byte b_lit16 .word 31415 .byte b_exit item lit32, f_code .byte b_lit32 .int 31415926 .byte b_exit item lit64, f_code .byte b_lit64 .quad 31415926 .byte b_exit item call8, f_code .byte b_call8 .byte 0f - . - 1 0: .byte b_exit item call16, f_code .byte b_call16 .word 0f - . - 2 0: .byte b_exit item call32, f_code .byte b_call32 .int 0f - . - 4 0: .byte b_exit item branch8, f_code .byte b_branch8 .byte 0f - . 0: .byte b_exit item branch16, f_code .byte b_branch16 .word 0f - . 0: .byte b_exit item qbranch8, f_code .byte b_qbranch8 .byte 0f - . 0: .byte b_exit item qbranch16, f_code .byte b_qbranch16 .word 0f - . 0: .byte b_exit item exit, f_code .byte b_exit item 1-, f_code .byte b_wm .byte b_exit item 1+, f_code .byte b_wp .byte b_exit item +, f_code .byte b_add .byte b_exit item -, f_code .byte b_sub .byte b_exit item *, f_code .byte b_mul .byte b_exit item /, f_code .byte b_div .byte b_exit item mod, f_code .byte b_mod .byte b_exit item /mod, f_code .byte b_divmod .byte b_exit item abs, f_code .byte b_abs .byte b_exit item dup, f_code .byte b_dup .byte b_exit item drop, f_code .byte b_drop .byte b_exit item swap, f_code .byte b_swap .byte b_exit item rot, f_code .byte b_rot .byte b_exit item -rot, f_code .byte b_mrot .byte b_exit item over, f_code .byte b_over .byte b_exit item pick, f_code .byte b_pick .byte b_exit item roll, f_code .byte b_roll .byte b_exit item depth, f_code .byte b_depth .byte b_exit item @, f_code .byte b_get .byte b_exit item !, f_code .byte b_set .byte b_exit item c@, f_code .byte b_get8 .byte b_exit item c!, f_code .byte b_set8 .byte b_exit item w@, f_code .byte b_get16 .byte b_exit item w!, f_code .byte b_set16 .byte b_exit item i@, f_code .byte b_get32 .byte b_exit item i!, f_code .byte b_set32 .byte b_exit item +!, f_code .byte b_setp .byte b_exit item -!, f_code .byte b_setm .byte b_exit item >r, f_code .byte b_2r .byte b_exit item r>, f_code .byte b_r2 .byte b_exit item r@, f_code .byte b_rget .byte b_exit item "0=", f_code .byte b_zeq .byte b_exit item 0<, f_code .byte b_zlt .byte b_exit item 0>, f_code .byte b_zgt .byte b_exit item "=", f_code .byte b_eq .byte b_exit item <, f_code .byte b_lt .byte b_exit item >, f_code .byte b_gt .byte b_exit item "<=", f_code .byte b_lteq .byte b_exit item ">=", f_code .byte b_gteq .byte b_exit item type, f_code .byte b_type .byte b_exit item expect, f_code .byte b_expect .byte b_exit item emit, f_code .byte b_emit .byte b_exit item count, f_code .byte b_count .byte b_exit item "(\")", f_code .byte b_str .byte b_exit item "(.\")", f_code .byte b_strp .byte b_exit item var8, f_code .byte b_var8 .byte 0f - . 0: .byte b_exit item var16, f_code .byte b_var16 .word 0f - . 0: .byte b_exit item base base: .byte b_var0 v_base: .quad 10 holdbuf_len = 70 item holdbuf holdbuf: .byte b_var0 .space holdbuf_len item holdpoint holdpoint: .byte b_var0 .quad 0 item span span: .byte b_var0 v_span: .quad 0 # : hold holdpoint @ 1- dup holdbuf > if drop drop else dup holdpoint ! c! then ; item hold 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 ; item # 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 ! ; item <# 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 ; item #s conv_s: .byte b_call8 .byte conv - . - 1 .byte b_dup .byte b_qbranch8 .byte conv_s - . .byte b_exit # : #> holdpoint @ holdbuf 70 + over - ; item #> 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 item . 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 item tib tib: .byte b_var0 v_tib: .quad 0 item #tib ntib: .byte b_var0 v_ntib: .quad 0 item >in bin: .byte b_var0 v_in: .quad 0 # : .s depth dup . c": emit do dup while dup pick . 1- again drop ; item .s # 11 22 33 prstack: .byte b_depth # 11 22 33 3 .byte b_dup # 11 22 33 3 3 .byte b_strp .byte 2 .ascii "( " .byte b_call16 # 11 22 33 3 .word dot - . - 2 .byte b_strp # 11 22 33 3 .byte 3 .ascii "): " .byte b_dup, b_zlt .byte b_qnbranch8, 1f - . .byte b_strp .byte 14 .ascii "\nStack fault!\n" .byte b_quit 1: .byte b_dup # 11 22 33 3 3 .byte b_qnbranch8 # 11 22 33 3 .byte 2f - . .byte b_dup # 11 22 33 3 3 .byte b_pick # 11 22 33 3 11 .byte b_call16 # 11 22 33 3 .word dot - . - 2 .byte b_wm # 11 22 33 2 .byte b_branch8 .byte 1b - . 2: .byte b_drop # 11 22 33 .byte b_exit .macro prs new_line = 1 .byte b_call16 .word prstack - . - 2 .if \new_line > 0 .byte b_lit8, '\n' .byte b_emit .endif .endm .macro pr string .byte b_strp .byte 9f - 8f 8: .ascii "\n\string" 9: .endm item interpret interpret: .byte b_blword .byte b_dup .byte b_qnbranch8 .byte 0f - . .byte b_over .byte b_over .byte b_find .byte b_dup .byte b_qnbranch8 .byte 1f - . .byte b_mrot .byte b_drop .byte b_drop .byte b_cfa .byte b_execute .byte b_branch8 .byte 2f - . 1: .byte b_drop .byte b_over, b_over .byte b_numberq # , .byte b_qbranch8, 3f - . # 0, , 3 .byte b_type # .byte b_strp # .byte 19 # .ascii " : word not found!\n" .byte b_quit # 3: .byte b_nip, b_nip # , ( b_over, b_over) 2: # .byte b_depth # .byte b_zlt # , 0 ( 0<) .byte b_qnbranch8, interpret_ok - . # , , .byte b_strp # .byte 14 .ascii "\nstack fault!\n" .byte b_quit # interpret_ok: .byte b_branch8 .byte interpret - . 0: .byte b_drop .byte b_exit last_item: .byte b_var0 item bye, f_code .byte b_bye .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_num1 = 0x03 bcmd_num1: push 1 jmp _next b_num2 = 0x04 bcmd_num2: push 2 jmp _next b_num3 = 0x05 bcmd_num3: push 3 jmp _next b_num4 = 0x06 bcmd_num4: push 4 jmp _next b_num8 = 0x07 bcmd_num8: push 8 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_wp = 0x18 bcmd_wp: incq [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 sar rax, 3 push rax jmp _next b_nip = 0x39 bcmd_nip: pop rax mov [rsp], 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 b_setp = 0x48 bcmd_setp: pop rcx pop rax add [rcx], rax jmp _next b_setm = 0x49 bcmd_setm: pop rcx pop rax sub [rcx], rax jmp _next b_2r = 0x60 bcmd_2r: pop rax sub rbp, 8 mov [rbp], rax jmp _next b_r2 = 0x61 bcmd_r2: push [rbp] add rbp, 8 jmp _next b_rget = 0x62 bcmd_rget: push [rbp] 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_var8 = 0x29 bcmd_var8: push r8 b_branch8 = 0x10 bcmd_branch8: movsx rax, byte ptr [r8] add r8, rax jmp _next b_var16 = 0x30 bcmd_var16: push r8 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_strp = 0x83 bcmd_strp: movsx rax, byte ptr [r8] inc r8 push r8 add r8, rax push rax 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 b_expect = 0x88 bcmd_expect: mov rax, 0 # 1 - sys_read mov rdi, 0 # 1 - stdout pop rdx # pop rsi # push r8 syscall # pop r8 mov rbx, rax or rax, rax jge 1f xor rbx, rbx 1: mov v_span, rbx jmp _next b_str = 0x82 bcmd_str: movzx rax, byte ptr [r8] lea r8, [r8 + rax + 1] jmp _next b_count = 0x84 bcmd_count: pop rcx movzx rax, byte ptr [rcx] inc rcx push rcx push rax jmp _next b_emit = 0x81 bcmd_emit: pop rax mov rsi, offset emit_buf # mov [rsi], al mov rax, 1 # 1 - sys_write mov rdi, 1 # 1 - stdout mov rdx, 1 # push r8 syscall # pop r8 jmp _next b_blword = 0xF0 bcmd_blword: mov rsi, v_tib # mov rdx, rsi # RDX mov rax, v_in # mov rcx, v_ntib # mov rbx, rcx add rsi, rax # RSI - sub rcx, rax # jz 3f word2: lodsb # AL RSI cmp al, ' ' ja 1f # ( ) dec rcx jnz word2 # 3: sub rsi, rdx mov v_in, rsi push rcx jmp _next 1: lea rdi, [rsi - 1] # RDI = RSI - 1 ( ) dec rcx jz word9 word3: lodsb cmp al, ' ' jbe 2f dec rcx jnz word3 word9: inc rsi 2: mov rax, rsi sub rsi, rdx # ( ) cmp rsi, rbx jle 4f mov rsi, rbx 4: mov v_in, rsi sub rax, rdi dec rax jz word1 push rdi # word1: push rax # jmp _next b_quit = 0xF1 bcmd_quit: lea r8, quit mov rsp, init_stack mov rbp, init_rstack jmp _next b_find = 0xF2 bcmd_find: pop rbx # pop r9 # mov rdx, v_context mov rdx, [rdx] # # find0: mov al, [rdx] # and al, 3 # - , , or al, al jz find_l8 cmp al, 1 jz find_l16 cmp al, 2 jz find_l32 mov r10, [rdx + 1] # 64 lea rsi, [rdx + 9] # jmp find1 find_l32: movsx r10, dword ptr [rdx + 1] # 32 lea rsi, [rdx + 5] # jmp find1 find_l16: movsx r10, word ptr [rdx + 1] # 16 lea rsi, [rdx + 3] # jmp find1 find_l8: movsx r10, byte ptr [rdx + 1] # 8 lea rsi, [rdx + 2] # find1: movzx rax, byte ptr [rsi] # cmp rax, rbx jz find2 # find3: or r10, r10 jz find_notfound # , add rdx, r10 # jmp find0 # , find2: inc rsi mov rdi, r9 mov rcx, rax repz cmpsb jnz find3 # push rdx jmp _next find_notfound: push r10 jmp _next b_cfa = 0xF3 bcmd_cfa: pop rdx # mov al, [rdx] # and al, 3 # - , , or al, al jz cfa_l8 cmp al, 1 jz cfa_l16 cmp al, 2 jz cfa_l32 lea rsi, [rdx + 9] # (64 ) jmp cfa1 cfa_l32: lea rsi, [rdx + 5] # (32 ) jmp cfa1 cfa_l16: lea rsi, [rdx + 3] # (16 ) jmp cfa1 cfa_l8: lea rsi, [rdx + 2] # (8 ) cfa1: xor rax, rax lodsb add rsi, rax push rsi jmp _next b_execute = 0xF4 bcmd_execute: sub rbp, 8 mov [rbp], r8 # pop r8 # - jmp _next b_numberq = 0xF5 bcmd_numberq: pop rcx # pop rsi # xor rax, rax # xor rbx, rbx # mov r9, v_base # xor r10, r10 # or rcx, rcx jz num_false mov bl, [rsi] cmp bl, '+' jnz 1f inc rsi dec rcx jz num_false jmp num0 1: cmp bl, '-' jnz num0 mov r10, 1 inc rsi dec rcx jz num_false num0: mov bl, [rsi] cmp bl, '0' jb num_false cmp bl, '9' jbe num_09 cmp bl, 'A' jb num_false cmp bl, 'Z' jbe num_AZ cmp bl, 'a' jb num_false cmp bl, 'z' ja num_false sub bl, 'a' - 10 jmp num_check num_AZ: sub bl, 'A' - 10 jmp num_check num_09: sub bl, '0' num_check: cmp rbx, r9 jge num_false mul r9 add rax, rbx inc rsi dec rcx jnz num0 or r10, r10 push rax push 1 jmp _next num_false: xor rcx, rcx push rcx jmp _next
رمز المصدر يزداد ، لذلك أحضره هنا للمرة الأخيرة.سيكون مكان إقامته الآن على موقع github: https://github.com/hal9000cc/forth64في نفس المكان ، يمكنك العثور على الإصدار الذي تم تجميعه بالفعل لنظام التشغيل Linux x64 في مجلد bin. من لديه نظام Linux ، يمكنك التنزيل والتشغيل.ومن لديه Windows - يمكنك تثبيت WSL (نظام Windows الفرعي لنظام التشغيل Linux). كنت أغادر لقضاء العطلات وفعلت ذلك. اتضح أن الأمر بسيط للغاية ، استغرق حوالي 5 دقائق ، ولم يكن هناك سوى لحظة واحدة ، لم تبدأ على الفور ، وكان يجب "تشغيل" النظام الفرعي من خلال أمر PowerShell. اتبع الرابط من رسالة الخطأ ، ونفذ الأمر ، وعملت.ولكن هناك أيضًا طريقة تمكن الهنود الحقيقيين من تشغيل كل شيء تحت نظام Windows: ليس من الصعب القيام بذلك ، فقط أعد بضع كلمات تتفاعل مع النظام.هذا كل شئ! في المرة القادمة ، سنقوم بتشغيل المترجم.ستكون هناك فرصة لتجميع كلمات جديدة ، ستكون هناك شروط ، دورات. في الواقع ، سيكون من الممكن الكتابة على حصن قياسي أو أكثر وتجميعه في كود البايت وتنفيذه. حسنا ، سيكون من الممكن إجراء اختبارات أكثر جدية ، والتحقق من أداء الجهاز البايت.استمرار: آلة بايت للحصن (وليس فقط) في أمريكا الأصلية (الجزء 4)