לדלג לתוכן

1.2 שלום עולם הרצאה

מה זה רגיסטר?

כאשר המעבד מריץ תוכנית – הוא חייב לשמור חלק מהמידע ממש קרוב אליו, בתוך רכיבים אלקטרוניים מאוד מהירים – אלו הם הרגיסטרים (Registers).
למעשה, רגיסטר הוא מין "תא זיכרון" קטן מאוד, שנמצא ממש בתוך המעבד – ומאפשר גישה מיידית.
כל פעולה חשבונית, כל גישה לזיכרון – תעבור כמעט תמיד דרך רגיסטרים.

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

רגיסטרים כלליים (General Purpose Registers)

אלו רגיסטרים שאנחנו יכולים להשתמש בהם כמעט לכל מטרה:

  • אוגר AX – רגיסטר ה־Accumulator, משמש בעיקר לחישובים.

  • אוגר BX – רגיסטר בסיסי שיכול לשמש לעבודה עם כתובות.

  • אוגר CX – משמש לרוב בלולאות או ספירות.

  • אוגר DX – רגיסטר כללי ולעיתים משמש לפעולות I/O.

כל אחד מהרגיסטר-ים האלו הוא בגודל 16 ביט – אך ניתן לגשת אל החצי העליון של הרגיסטר והתחתון בנפרד:

  • AX = AH (high 8 bits) + AL (low 8 bits)
    כלומר, הרגיסטר AH- דרכו נוכל לגשת ל8 ביטים הראשונים, ודרך AL נוכל לגשת ל8 ביטים האחרונים של הרגיסטר AX (16 ביט)

לדוגמה:

mov ax, 0x1234  ; AX = 1234h
; עכשיו:
; AH = 12h
; AL = 34h

רגיסטרים מיוחדים

על הרגיסטרים הבאים נרחיב בהמשך.
- SP (Stack Pointer) – מצביע לראש המחסנית (stack)
- BP (Base Pointer) – משמש לגישה לנתונים בתוך המחסנית.
- SI / DI – רגיסטרים עבור פעולות עם מחרוזות (source index / destination index).


רגיסטרים של כתובת (Segment Registers)

מעבד 8086 עובד במודל של "סגמנטים". לכל אזור בזיכרון יש רגיסטר שמייצג את תחילתו:

  • CS – Code Segment

  • DS – Data Segment

  • SS – Stack Segment

  • ES – Extra Segment

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

הסיבה לכף שב8086 חייבים גם סגמנט וגם offset כדי לגשת לזכרון היא בגלל גודל הרגיסטרים.
ב8086 גודל הרגיסטרים הוא 16 ביט, ולעומת זאת גודל הRAM המקסימלי הוא 20 ביט.
כדי לגשת לכל כתובת, עלינו להשתמש בשני רגיסטרים, ברגיסטר שאחראי לסמן את הסגמנט, ורגיסטר שאחראי לסמן את הoffset בסגמנט.

בהמשך נראה כמה דוגמאות.

פקודת MOV

הפקודה החשובה ביותר באסמבלי.
הפקודה mov מעבירה מידע, היא יודעת להעביר מידע בין רגיסטרים, להכניס מידע לרגיטרים, ולגשת למידע בזכרון (בRAM)

הפקודה הבאה תכניס את המספר 5 לרגיסטר ax

mov ax, 5

הפקודה הבאה תכניס את הערך שיש ברגיסטר ax לתוך רגיסטר bx

mov bx, ax

הפקודה הבאה מעתיקה ערך שיש בRAM לתוך רגיסטר.
בגלל ש8086 גישה לזכרון עובדת עם סגמנטים, הסגמנט הדיפולטי הוא הdata segement, אז התוכנה תקח את הערך של רגיסטר הDS- ותוסיף לו את ערך הoffset שכתוב.
כלומר, אם DS שווה ל4545, והoffset הוא 1234 כמו בדוגמה למטה, הכתובת שאלייה תיגש התוכנה היא
0x45451234
כך שהפקודה למטה תקח את הערך שיש בכתובת 0x45451234 בRAM, ותעתיק לרגיסטר al (רגיסטר זה בגודל של 8 ביט).

mov al, [1234h]

הפקודה הבאה תעתיק את הערך שיש בax לתוך 0x45451234, בגלל שהרגיסטר ax הוא בגודל של 16 ביט, יעותק 16 ביט של ערכים לRAM אחרי הכתובת.

mov [1234h], ax

פקודת LEA (Load Effective Address)

פקודת LEA מאפשרת לנו לחשב כתובות.
הפקודה למטה, לוקחת את רגיסטר bx, מוסיפה לו 10 ואת התוצאה (הכתובת) היא שמה ברגיסטר si.

lea si, [bx+10]

שימו לב: הפקודה הזו לא קוראת מהזיכרון ולא כותבת אליו – רק מחשבת כתובת.
כל מטרת הפקודה היא חישוב offset-ים!
למורות שאנחנו רואים את הסוגריים מרובעים- שבmov משמשים לקריאה וכתיבה לזכרון- כאן השימוש הוא רק לחישוב של פעולת חיבור של כתובת!

פעולות מתמטיות

ADD ו־SUB

הפעולה add מבצעת חיבור.
היא לוקחת את ax ומוסיפה לו 10.
AX = AX + 10

add ax, 10

הפעולה sub מבצעת חיסור.
היא לקחת את ax ומורידה לו את הערך שיש בbx.
AX = AX - BX

sub ax, bx

INC ו־DEC

הפעולה inc מבצעת חיבור ב1, (בדומה לפעולה ++ בשפות תכנות שונות)
AX = AX + 1

inc ax

הפעולה dec מבצעת חיסור ב1, (בדומה לפעולה -- בשפות תכנות שונות)
BX = BX - 1
dec bx

פקודות MUL / IMUL – כפל

פקודת MUL (קיצור של multiply) מבצעת כפל ללא סימן (מספרי unsigned) בין רגיסטר ובין רגיסטר אחר או ערך בזיכרון.
פקודת IMUL (קיצור של integer multiply) מבצעת כפל עם סימן (מספר Signed, מספרים שיכולים להיות שלילים).

כפל בגודל 8 ביט

  • אם נעשה mul לרגיסטר או ערך כלשהו בגודל 8 ביט, המעבד תמיד יכפיל את המספר בערך שיש בAL וישמור את התוצאה בax (16 ביט)
    mov al, 0ABh     ; AL = 171
    mov bl, 10h      ; BL = 16
    mul bl           ; AX = AL * BL = 0AB0h (2736)
    

כפל בגודל 16 ביט

  • ואם נעשה mul לרגיסטר או ערך כלשהו בגודל 16 ביט, המעבד תמיד יכפיל את המספר בערך שיש בAX וישמור את התוצאה גם בAX וגם בDX כאשר AX יקבל את החלק התחתון של התוצאה, וDX את העליו.

  • התוצאה נשמרת ב־DX:AX (כלומר: AX מקבל את החצי התחתון, DX את החצי העליון של התוצאה – יחד 32 ביט).

    mov ax, 0AB0h     ; AX = 2736
    mov bx, 1010h     ; BX = 4112
    mul bx            ; DX:AX = AX * BX = 0ABAB000h
    

פקודת DIV – חילוק

פקודת DIV מבצעת חילוק ללא סימן (unsigned division).
יש להיזהר עם הפקודה הזאת – כי אם נבצע חילוק לא חוקי (למשל, חילוק באפס או אם התוצאה לא נכנסת לגודל המתאים), תתרחש שגיאת זמן ריצה (Divide Error Exception) והתוכנה תקרוס.

חילוק של מספר בגודל 8 ביט

כאשר אנו מחלקים ערך בגודל 8 ביט (למשל: div bl) – המעבד מצפה שהמספר שנחלק אותו (המחולק) יהיה ב־AX (16 ביט).

  • המחולק: AX

  • המחלק: BL

  • התוצאה: AL (המנה)

  • השארית: AH (השארית)

mov ax, 1234h    ; מספר בגודל 16 ביט שנחלק
mov bl, 10h      ; מחלק בגודל 8 ביט
div bl           ; AL = AX / BL ; AH = AX % BL

חילוק של מספר בגודל 16 ביט

כאשר מחלקים ערך בגודל 16 ביט (למשל: div bx) – המחולק צריך להיות ב־DX:AX (כלומר: מספר של 32 ביט, כשהחצי העליון ב־DX והחצי התחתון ב־AX).

  • המחולק: DX:AX

  • המחלק: BX

  • התוצאה: AX

  • השארית: DX

mov dx, 0        ; חלק עליון של המחולק
mov ax, 1234h    ; חלק תחתון של המחולק
mov bx, 10h      ; המחלק
div bx           ; AX = DX:AX / BX ; DX = DX:AX % BX

פעולות לוגיות: AND, OR, XOR, NOT

פעולות אלה מבוצעות בביטים, אחד-לאחד, בין ביטים מקבילים ברגיסטרים או בזיכרון.

AND – וגם

הפקודה and מבצעת פעולה לוגית וגם בין כל ביט וביט.
אם שני הביטים הם 1 → התוצאה היא 1. אחרת → 0.

mov al, 11001100b
and al, 10101010b

התוצאה תהיה התוצאה: 10001000
מכיוון, ש 11001100 & 10101010 שווה ל- 10001000 (כמו שלמדנו בשיעורים קודמים)

שימוש נפוץ:

  • ניקוי ביטים מסוימים (למשל: לשים 0 בביטים מסוימים ולהשאיר אחרים כמו שהם).
    לדוגמה, אם נרצה לנקות את כל ה3 ביטים הראשונים של ערך, נוכל לעשות and ל- 11111000

OR – או

הפקודה or מבצעת פעולה לוגית או.
אם לפחות אחד מהביטים הוא 1 → התוצאה היא 1. אחרת → 0.

mov al, 11001100b
or al, 10101010b

התוצאה תהיה 11101110
שימוש נפוץ:

  • להפעיל (לשים 1) ביטים מסוימים.

XOR – קסור - OR מיוחד

הפקודה xor מבצעת פעולה לוגית או-בלעדי:
אם הביטים שונים → 1
אם הביטים זהים → 0
כמו שלמדנו בהרצאות קודמות.

mov al, 11001100b
xor al, 10101010b

התוצאה תהיה 01100110
שימוש נפוץ:

  • אפס רגיסטר: xor ax, ax – דרך קצרה ומהירה לאפס רגיסטר.
    למשל אם נרצה לאפס את ax, נוכל לעשות xor ax, ax במקום mov ax, 0
  • הצפנת מידע (על זה אולי נדבר בהמשך הקורס)

NOT – שלילה

הפקודה not הופכת כל ביט:
- 0 → 1
- 1 → 0

mov al, 11001100b
not al

התוצאה תהיה 00110011
שימוש נפוץ:
- להפוך את כל הביטים ברגיסטר.

הגדרת משתנים בתוך הDATASEG

כאשר אנו מגדירים משתנים בתוך הסגמנט DATASEG, אנחנו בעצם מבקשים מהמערכת לשמור לנו מקום בזיכרון – עם ערכים התחלתיים – כך שנוכל להשתמש בהם מאוחר יותר בקוד.

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

הDB – Define Byte

שומר בייט אחד (8 ביט) בזיכרון.

num1 db 5         ; משתנה בגודל בייט שמכיל את הערך 5
txt  db 'A'       ; תו יחיד – האות A
msg  db 'Hello$', ; מחרוזת שמסתיימת בסימן $ (לשימוש עם int 21h)

הDW – Define Word

שומר וורד אחד (16 ביט = 2 בייטים).

num2 dw 1234      ; מספר שלם בגודל 16 ביט

הDD – Define Double Word

שומר דאבל וורד (32 ביט = 4 בייטים).

num3 dd 12345678h ; מספר הקסה בגודל 32 ביט

הDQ – Define Quad Word

שומר קוואד וורד (64 ביט = 8 בייטים). לא בשימוש נפוץ ב-TASM למערכות 16 ביט, אבל קיים.

bigNum dq 1122334455667788h ; ערך גדול מאוד

איך זה נראה יחד?

DATASEG
    letter   db 'X'         ; תו אחד
    age      db 21          ; מספר בגודל בייט
    year     dw 2024        ; מספר בגודל וורד (16 ביט)
    seconds  dd 3600        ; מספר גדול יותר (32 ביט)
    id       dq 1122334455667788h ; מספר 64 ביט

טיפ: גישה לזיכרון

כדי לקרוא או לכתוב למשתנים האלו – תשתמש בשמות שהגדרת:

mov al, letter     ; טוען את התו לתוך AL
mov ax, year       ; טוען את הערך של year לתוך AX

שים לב – הגודל חשוב:
אם תשתמש ב־mov al, year זה יגרום לשגיאה, כי אתה מנסה לקרוא ערך של 16 ביט (word) לתוך רגיסטר של 8 ביט (AL).
אם אתה לא בטוח – השתמש ב־movzx או mov ax, [year] ותוודא שהגודל מתאים.