8086 Assembler Tutorial for Beginners (Part 7)

Program Flow Control

הדרכה בתכנות אסמבלר 8086 למתחילים (חלק 7)  

בקרת זרימת התוכנית

Controlling the program flow is a very important thing, this is where your program can make decisions according to certain conditions. שליטה בזרימת התוכנית היא דבר חשוב מאוד, זה המקום בו התוכנית שלך יכולה לבצע החלטות לפי תנאים מסוימים.
Unconditional Jumps

The basic instruction that transfers control to another point in the program is JMP.

The basic syntax of JMP instruction:

JMP label

To declare a label in your program, just type its name and add ":" to the end, label can be any character combination but it cannot start with a number, for example here are 3 legal label definitions:

label1:
label2:
a:

Label can be declared on a separate line or before any other instruction, for example:

x1:
MOV AX, 1

x2: MOV AX, 2

Here is an example of JMP instruction:

פקודות דילוג ללא תנאי

הפקודה היסודית שמעבירה את שליטת הבקרה לנקודה אחרת בתוכנית היא 
JMP.

התחביר של פקודת JMP הוא:

                 
 תווית
JMP

להגדרת תווית בתוכנית שלך, פשוט כותבים את שם התווית ובסיומה מוסיפים  ":". התווית יכולה להיות כל שילוב של אותיות או סימנים אבל לא יכולה להתחיל עם מספר.
לדוגמה, להלן 3 הגדרות חוקיות של תוויות:

                label1:
                label2:
                     a:

ניתן להגדיר תווית בשורה נפרדת או לפני כל פקודה, למשל:

x1:
    MOV AX, 1

x2: MOV AX, 2

להלן דוגמה של פקודת JMP:

ORG    100h

MOV    AX, 5       ; set AX to 5. -       AX הכנס ערך 5 בתוך
MOV    BX, 2       ; set BX to 2. -       BX הכנס ערך 2 בתוך

JMP    calc        ; go to 'calc' -
'calc'דלג למקום עם תווית

back:  JMP stop    ; go to 'stop' -
'stop'דלג למקום עם תווית

calc:
ADD    AX, BX      ; add BX to AX -  AX+BX= AX סכם לתוך אוגר
JMP    back        ; go to   'back'       דלג למקום עם תווית

stop:             

RET                ; return to operating system.
                   ;                       חזרה למערכת הפעלה
END                ; directive to stop the compiler.
                   ;                      הגדרה לסיום ההידור

  • Of course there is an easier way to calculate the sum of two numbers, but it's still a good example of JMP instruction.
    As you can see from this example JMP is able to transfer control both forward and backward. It can jump anywhere in current code segment (65,535 bytes).


  • Short Conditional Jumps

    Unlike JMP instruction that does an unconditional jump, there are instructions that do a conditional jumps (jump only when some conditions are in act). These instructions are divided in three groups, first group just test single flag, second compares numbers as signed, and third compares numbers as unsigned.

    Jump instructions that test single flag:
  • כמובן קיימת קלה יותר לחשב סכום של שני מספרים, אבל זו עדיין דוגמה טובה של הפקודה JMP.
    כפי שאתה יכול לראות מהדוגמה הנ"ל
    JMP מסוגלת להעביר שליטה קדימה ואחורה.
    התוכנית  יכולה לקפוץ לכל מקום במקטע התוכנית (65,535 בתים).


     

  • קפיצות מותנות למרחק קצר

    להבדיל מפקודת
    JMP המבצעת קפיצה ללא תנאים, ישנן פקודות שעושות קפיצות מותנות (קפיצה רק אם כמה תנאים מתקיימים).

    פקודות אלו מתחלקות לשלוש קבוצות, קבוצה ראשונה פשוט בוחנת דגל יחיד, שנייה משווה מספרים כמסומנים, והשלישית משווה מספרים הנחשבים כלא מסומנים.



    פקודות קפיצה הבודקות דגל יחיד:

Instruction
פקודה

Description           תיאור

Condition
תנאי

Opposite Instruction
פקודה נגדית

JZ , JE

Jump if Zero (Equal)  .  (דלג אם אפס (שווה

 ZF = 1 JNZ, JNE
JC , JB, JNAE

Jump if Carry (Below, Not Above Equal).
.(
דלג אם יש גלישה (מתחת, לא מעל או שווה

 CF = 1 JNC, JNB, JAE
JS

Jump if Sign  .  דלג אם הסימן שלילי

 SF = 1 JNS
JO

Jump if Overflow  .  דלג אם יש גלישת יתר

 OF = 1 JNO
JPE, JP

Jump if Parity Even .
דלג אם כמות האחדים זוגית

 PF = 1 JPO
JNZ , JNE

Jump if Not Zero (Not Equal).
.דלג אם לא אפס או לא שווה

 ZF = 0 JZ, JE
JNC , JNB, JAE

Jump if Not Carry (Not Below,  Above Equal).
.(
דלג אם אין גלישה (לא מתחת, מעל או שווה

 CF = 0 JC, JB, JNAE
JNS Jump if Not Sign . דלג אם הסימן חיובי  SF = 0 JS
JNO

Jump if Not Overflow . דלג אם אין גלישת יתר

 OF = 0 JO
JPO, JNP

Jump if Parity Odd (No Parity) .
.
דלג אם כמות האחדים אינה זוגית

 PF = 0 JPE, JP
As you can see there are some instructions that do that same thing, that's correct, they even are assembled into the same machine code, so it's good to remember that when you compile JE instruction - you will get it disassembled as: JZ.
Different names are used to make programs easier to understand and code.


Jump instructions for signed numbers:

כפי שאתה יכול לראות ישנן כמה פקודות שעושות אותו דבר, זה נכון, הן אפילו מרוכזות עם אותו צופן מכונה, לכן זה טוב לזכור שכאשר אתה מבצע הידור (קומפילציה) של הפקודה JE - אתה תקבל ב-Debugger כמו JZ.

משתמשים בשמות שונים על-מנת שלמתכנת יהיה יותר קל להבין ולכתוב תוכניות אסמבלר.


פקודות קפיצה למספרים מסומנים:

Instruction
פקודה

Description           תיאור

Condition
תנאי

Opposite Instruction
פקודה נגדית

JE , JZ

Jump if Equal  (= דלג אם שווה.
Jump if Zero  (0
דלג אם אפס.

ZF = 1 JNE, JNZ
JNE , JNZ Jump if Not Equal  (<>דלג אם לא שווה.
Jump if Not Zero  -  
דלג אם שונה מאפס.
ZF = 0 JE, JZ
JG , JNLE

Jump if Greater  (>)   דלג אם גדול.
Jump if Not Less or Equal  
(not <=)
                              
 . דלג אם לא קטן או שווה

ZF = 0
and
SF = OF
JNG, JLE
JL , JNGE

Jump if Less  (<)    דלג אם קטן.
Jump if Not Greater or Equal 
(not >=
                               
 . דלג אם לא גדול או שווה

SF <> OF JNL, JGE
JGE , JNL

Jump if Greater or Equal (>=)
                                   
   . דלג אם גדול או שווה
Jump if Not Less  (not <)
 
דלג אם לא קטן.

SF = OF JNGE, JL
JLE , JNG

Jump if Less or Equal (<=)
                                      
 .דלג אם קטן או שווה
Jump if Not Greater 
(not >)
 דלג אם לא גדול.

ZF = 1
or
SF <> OF
JNLE, JG

<> - sign means not equal.


Jump instructions for unsigned numbers
הערה: סימן <> - פירושו לא שווה (שונה).


פקודות קפיצה למספרים לא מסומנים:

Instruction
פקודה

Description           תיאור

Condition
תנאי

Opposite Instruction
פקודה נגדית

JE , JZ Jump if Equal  (= דלג אם שווה.
Jump if Zero  (0
דלג אם אפס.
ZF = 1 JNE, JNZ
JNE , JNZ Jump if Not Equal  (<> דלג אם לא שווה.
Jump if Not Zero  -   דלג אם שונה מאפס.
ZF = 0 JE, JZ
JA , JNBE

Jump if Above  (> דלג אם מעל.
Jump if Not Below or Equal
(not <=)

                              
 .דלג אם לא מתחת או שווה

CF = 0
and
ZF = 0
JNA, JBE
JB , JNAE, JC

Jump if Below  (<)  דלג אם מתחת
Jump if Not Above or Equal
(not >=).

                              
 . דלג אם לא מעל או שווה

Jump if Carry  -
 דלג אם יש גלישה.

CF = 1 JNB, JAE, JNC
JAE , JNB, JNC

Jump if Above or Equal (>=)
                              
 . דלג אם מעל או שווה

Jump if Not Below
 (not <) דלג אם לא מתחת.
Jump if Not Carry
 -  דלג אם אין גלישה.

CF = 0 JNAE, JB
JBE , JNA

Jump if Below or Equal (<=)
                              
 . דלג אם מתחת או שווה

Jump if Not Above  
(not >) 
דלג אם לא מעל.

CF = 1
or
ZF = 1
JNBE, JA
Generally, when it is required to compare numeric values CMP instruction is used (it does the same as SUB (subtract) instruction, but does not keep the result, just affects the flags).

The logic is very simple, for example:
it's required to compare 5 and 2,
5 - 2 = 3
the result is not zero (Zero Flag is set to 0).

Another example:
it's required to compare 7 and 7,
7 - 7 = 0
the result is zero! (Zero Flag is set to 1 and JZ or JE will do the jump).

Here is an example of CMP instruction and conditional jump:

בדרך כלל, כאשר נדרש להשוות ערכים מספריים משתמשים בפקודת CMP , אשר מבצעת את אותה פעולה כמו פקודת החיסור SUB, אך לא שומרת את התוצאה, אלא משפיעה על מצב הדגלים.

הלוגיקה מאוד פשוטה, לדוגמה:
 נדרש להשוות את הערכים 5 ו- 2,
        3=5-2
התוצאה היא לא אפס, ואז דגל האפס שווה לאפס.

דוגמה נוספת:
 נדרש להשוות את הערכים 7 ו- 7,
        0=7-7
התוצאה היא אפס, ואז דגל האפס שווה לאחד והפקודות
JZ או JE מבצעות את הקפיצה.

בהמשך, דוגמה של פקודת
CMP וקפיצה מותנית:

include emu8086.inc

ORG    100h

MOV    AL, 25     ; set AL to 25 .       AL הכנס ערך 25 בתוך
MOV    BL, 10     ; set BL to 10.        BL הכנס ערך 10 בתוך


CMP    AL, BL     ; compare AL - BL         השווה בין האוגרים

JE     equal      ; jump if AL = BL (ZF = 1).דלג אם יש שוויון

PUTC   'N'        ; if it gets here, then AL <> BL,
                  ;        אם התוכנית מגיעה לכאן אז אין שוויון
JMP    stop       ; so print 'N', and jump to stop.
                  ;                     אז הדפס "לא" ודלג לסיום
equal:            ; if gets here,       אם מגיע לכאן
PUTC   'Y'        ; then AL = BL, so print 'Y'.
                  ;                   "אז יש שוויון ומדפיס "כן
stop:

RET               ; gets here no matter what.
                  ;                        בכל מקרה נגיע לכאן
END

Try the above example with different numbers for AL and BL, open flags by clicking on [FLAGS] button, use [Single Step] and see what happens, don't forget to recompile and reload after every change (use F5 shortcut).

נסה את הדוגמה הקודמת עם ערכים שונים עבור AL ו- BL, פתח את חלון הדגלים על ידי הכפתור שבתחתית האמולטור, השתמש בצעד יחיד וראה מה מתרחש.
זכור לבצע שוב הידור של התוכנית לאחר כל שינוי של הנתונים (השתמש ב- F5 לקיצור דרך).

All conditional jumps have one big limitation, unlike JMP instruction they can only jump 127 bytes forward and 128 bytes backward (note that most instructions are assembled into 3 or more bytes).

We can easily avoid this limitation using a cute trick:

  • Get a opposite conditional jump instruction from the table above, make it jump to label_x.

  • Use JMP instruction to jump to desired location.

  • Define label_x: just after the JMP instruction.

label_x: - can be any valid label name.

Here is an example:

לכל הקפיצות המותנות הגבלה גדולה. פרט מפקודת JMP הם יכולות לדלג רק 127 צעדים קדימה ו- 128 צעדים (בתים) אחורה. ראה שלרוב פקודות הקפיצה (דילוג) 3 או יותר בתים של זיכרון התוכנית.

ניתן להימנע מההגבלה תוך שימוש של פטנט נחמד:

  • השג פקודה נגדית של פקודת דילוג מותנה מהטבלה למעלה, גרום לתוכנית לדלג למקום עם תווית  : label_x
     

  • השתמש בפקודת JMP לדילוג לכתובת הרצויה.
     

  • הגדר את התווית :label_x אחרי פקודת ה-  JMP .

תווית :label_x יכולה להיות כל שם תקין.

 דוגמה בהמשך:

include emu8086.inc
ORG    100h

MOV    AL, 25     ; set AL to 25.  
       AL הכנס ערך 25 בתוך
MOV    BL, 10     ; set BL to 10.
         BL הכנס ערך 10 בתוך
CMP    AL, BL     ; compare AL - BL.
        השווה בין האוגרים


JNE    not_equal  ; jump if AL <> BL (ZF = 0).
דלג אם אין שוויון
JMP    equal
not_equal:


; let's assume that here we              נניח שגודל התוכנית
; have a code that is assembled          -שכתבנו ארוך מ
; to more then 127 bytes...              ...127 bytes

PUTC   'N'        ; if it gets here, then AL <> BL,
                  ;        אם התוכנית מגיעה לכאן אז אין שוויון

JMP    stop       ; so print 'N', and jump to stop.
                  ;                     אז הדפס "לא" ודלג לסיום

equal:            ; if gets here,
                אם התוכנית מגיעה לכאן
PUTC   'Y'        ; then AL = BL, so print 'Y'.
                  ;                "אז יש שוויון והתוכנית תדפיס "כן
stop:

RET               ; gets here no matter what.
                  ;                        בכל מקרה נגיע לכאן
END

Another, yet rarely used method is providing an immediate value instead of a label. When immediate value starts with a '$' character relative jump is performed, otherwise compiler calculates instruction that jumps directly to given offset. For example:

שיטה נוספת, בשימוש נדיר, זה לספק ערך מיידי במקום תווית.
כאשר ערך מיידי מתחיל בסימן "$" , מתרחש דילוג יחסי, אחרת המהדר מחשב את ההוראה לדילוג ישירות להיסט .
למשל:

ORG    100h

; unconditional jump forward:            דילוג קדימה ללא תנאי
; skip over next 2 bytes,            מדלג מעל 2 הכתובות הבאות
JMP $2
a DB 3    ; 1 byte.
b DB 4    ; 1 byte.

; JCC jump back 7 bytes:                     מדלג חזרה 7 כתובות
; (JMP takes 2 bytes itself)  פקודת הדילוג לוקחת 2 כתובות לעצמה
MOV BL,9
DEC BL      ; 2 bytes.
CMP BL, 0   ; 3 bytes.
JNE $-7

RET

END

<<< to Part 6 <<   >> to Part 8 >>>

<<< לחלק 8 <<   >> לחלק 6 >>>