سؤال

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

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    movl    $0, %eax # The starting point/current value.
    movl    $100,   %ebx # The ending point.

_loop:
    # Display the current value.
    pushl   %eax
    pushl   $string
    call     _printf
    addl     $8, %esp

    # Check against the ending value.
    cmpl    %eax, %ebx
    je    _end

    # Increment the current value.
    incl    %eax
    jmp _loop   

_end:

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

(التنسيق معطل قليلاً، لكن لا يوجد شيء كبير).

هل كانت مفيدة؟

المحلول

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

وآمل أن يعمل التالية.أفترض أن Pushl يحتوي على Popl مكافئ ويمكنك دفع رقمين إضافيين إلى المكدس.

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    movl    $0, %eax # The starting point/current value.
    movl    $100,       %ebx # The ending point.

_loop:
    # Remember your registers.
    pushl   %eax
    pushl   %ebx

    # Display the current value.
    pushl   %eax
    pushl   $string
    call     _printf
    addl     $8, %esp

    # reinstate registers.
    popl   %ebx
    popl   %eax

    # Check against the ending value.
    cmpl    %eax, %ebx
    je    _end

    # Increment the current value.
    incl    %eax
    jmp _loop   

_end:

نصائح أخرى

لست على دراية بـ _printf، ولكن هل من الممكن أن يقوم بتعديل eax؟يجب أن يُرجع Printf عدد الأحرف المطبوعة، وهو في هذه الحالة اثنان:"0" و" ".أعتقد أنه يُرجع هذا في eax، وعندما تقوم بزيادته، تحصل على 3، وهو ما تبدأ في طباعته.قد يكون من الأفضل لك استخدام سجل مختلف للعداد.

يمكنك استخدام السجلات "المحفوظة لدى المستدعي" بأمان دون الحاجة إلى حفظها بنفسك.على x86، هذه هي edi وesi وebx؛البنى الأخرى لديها المزيد.

تم توثيقها في مراجع ABI: http://math-atlas.sourceforge.net/devel/assembly/

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

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

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

@ seanyboy، الحل الخاص بك هو مبالغة.كل ما هو مطلوب هو استبدال eax ببعض السجلات الأخرى مثل ecx.

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

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

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

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    push    %ecx
    push    %ebp
    movl    $0, %ecx # The starting point/current value.
    movl    $100,       %ebp # The ending point.

_loop:
    # Display the current value.
    pushl   %ecx
    pushl   $string
    call     _printf
    addl     $8, %esp

    # Check against the ending value.
    cmpl    %ecx, %ebp
    je    _end

    # Increment the current value.
    incl    %ecx
    jmp _loop   

_end:
    pop     %ebp
    pop     %ecx
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top