לדלג לתוכן

1.8 אקסטרה הרצאה

קפיצות: Near ו־Far

מעבד 8086 בנוי לעבוד במודל של סגמנטים. זה אומר שכתובת בזיכרון מורכבת משני חלקים: סגמנט (Segment) ואופסט (Offset).
ולשם כך יש לנו שני סוגי jmp-ים.
קפיצות לקוד בתוך סגמנט הקוד שלנו, וקפיצות לקוד שנמצא בסגמנט מרוחק.

קפיצת Near Jump

קפיצת Near Jump מתבצע בתוך אותו סגמנט.
ב־8086, מדובר בקפיצה לכתובת אופסט אחרת בתוך אותו סגמנט קוד (CS לא משתנה).
למשל:

jmp short label     ; קפיצה לטווח של -128 עד +127 בתים
jmp near ptr label  ; קפיצה לטווח רחב יותר אך בתוך אותו סגמנט

זה נפוץ בקוד פנימי, בתוך אותה פונקציה או אותו מודול.

Far Jump

לעומת זאת הFar Jump קורת כאשר אנחנו קופצים או קוראים (גם עם call) לקטע וד שהוא לא בסגמנט הקוד שלנו, (כלומר רחוק)- ואז כדי לקפוץ אליו, ממש נצטרך לשנות את הcs (הcode segement)
זה קורה כשאנחנו כותבים תוכנה מאוד גדולה, (עם קוד יותר מ64k בתים) וזה דורש מאיתנו לכתוב קוד סגמנט אחר.

jmp far ptr seg_off   ; קפיצה לסגמנט אחר לגמרי

האסמבלר יודע להמיר את הקוד הזה, ולעשות גם mov לcs לסגמנט הנכון וגם jmp לoffset הנכון.

זה לגבי jmp-ים, אבל מה עם פונקציות שלא בסגמנט הקוד? (כשאנחנו כותבים יותר מידי קוד ואנחנו נדרשים לכתוב פונקציה בסגמנט חדש)
כאשר אנחנו מגדירים פונקציה כזו, אנחנו נגדיר אותה כך:

myFunction proc far
; וכאן הקוד של הפונקציה
ret
myFunction endp

בכך האסמבלר יודע, שכאשר אנחנו עושים call לפונקציה "myFunction" היא נמצאת רחוק (לא בסגמנט הקוד שלנו) והאסמבלר ידע לשנות את הcs לסגמנט הנכון, ולעשות jmp לoffset הנכון.
הבעיה היא שפונקציה לעומת jmp צריכה לעשות ret לכתובת הנכונה. לשם כך, האסמבלר כמו שהוא עושה push לip, הוא עושה push גם לcs לפני שהוא משנה אותו.
ובכך כשהפונקציה חוזרת, האסמבלר יודע לעשות pop לcs לפני הret.

תוכלו להגדיר פונקצית near ככה:

myFunction proc near

וכמובן שתוכלו גם להגדיר עם label, (אין הבדל כשמדובר בפונקציות near)
myFunction:
    ; הקוד

הזזות ימינה ושמאלה: SHL ו־SHR

הזזה שמאלה (SHL)

מבצעת הכפלה ב־2 על המספר הבינארי, על ידי הזזת כל הביטים שמאלה.

mov al, 3      ; 00000011
shl al, 1      ; 00000110 → 6

הזזה ימינה (SHR)

מבצעת חלוקה ב־2 על ידי הזזת הביטים ימינה. הביט הגבוה הופך ל־0.

mov al, 6      ; 00000110
shr al, 1      ; 00000011 → 3

שימוש נפוץ: כפל/חילוק מהירים ב־2, 4, 8 וכו.


פקודת החלפה XCHG

פקודה שמחליפה בין שני רגיסטרים או בין רגיסטר לזיכרון:

xchg ax, bx    ; AX ↔ BX
xchg al, ah    ; ביטים בתוך AX

דוגמה:
mov ax, 1234h
mov bx, 5678h
xchg ax, bx    ; עכשיו AX=5678, BX=1234


פקודת LOOP

לולאה שמבוססת על רגיסטר CX — בכל פעם ש־loop מתבצעת, היא מקטינה את CX ב־1 ובודקת אם CX ≠ 0.

mov cx, 5
start:
    ; קוד שמתבצע
    loop start

הפלט יתבצע 5 פעמים.

שימושי מאוד בלולאות פשוטות.

הגדרות בdata segement

בdata segement אנחנו יכולים להגדיר משתנים, בין היתר- אנחנו יכולים לבחור גדול למשתנים.