לדלג לתוכן

מערכים באסמבלי

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

מבנה מערך בזיכרון

כדי ליצור מערך של 5 תווים (8 ביט כל אחד):
כתבו את זה באזור המשתנים, (איפה שמוגדר המשתנים בdata segement)

array db 1, 2, 3, 4, 5

אם נניח שהמערך מתחיל בכתובת 0x1000, אז מיקומי האיברים יהיו:

אינדקס ערך כתובת בזיכרון
0 1 0x1000
1 2 0x1001
2 3 0x1002
3 4 0x1003
4 5 0x1004

כלומר:
הoffset של איבר = כתובת התחלתית של המערך + אינדקס × גודל איבר


מה זה offset?

המילה offset באסמבלי מתארת את המרחק מהתחלת הסגמנט (DS, ES וכו') לכתובת בזיכרון.
כאשר נרשום:

mov si, offset array

אנחנו בעצם מכניסים לרגיסטר SI את הכתובת שבה מתחיל המערך בזיכרון (בתוך הסגמנט DS).
כלומר, לSI יהיה את הכתובת של תחילת המערך, אשר מוגדר בds.

כלומר: [si] יתייחס ל־array[0], [si+1] יתייחס ל־array[1] וכן הלאה.

דוגמה 1: סכימת מערך של מספרים בגודל 8 ביט

נניח:

array db 4, 1, 3, 6, 8, 1, 5, 0, 9, 2
mov cx, 10              ; 10 איברים
mov si, offset array    ; כתובת התחלתית
xor ax, ax              ; איפוס AX, AH יכיל את הסכום

sum_loop:
  lodsb                 ; טען AL ← [DS:SI], הגדל SI
  add ah, al            ; הסכום ב-AH (היזהר: Overflow!)
  loop sum_loop         ; CX -= 1, המשך אם CX ≠ 0

ניתוח הקוד:

  • הlodsb גורם לכך ש־AL = array[i], SI++

  • הadd ah, al מוסיף את AL לסכום (שמוחזק ב־AH)

  • הloop מפעיל את הלולאה CX פעמים


דוגמה 2: העתקת מערך של מספרים בגודל 16 ביט

נניח:

src dw 10, 20, 30, 40, 50
dst dw 0, 0, 0, 0, 0

כל ערך כאן תופס 2 בייטים (כי זה dw ולא db)

mov cx, 5               ; 5 איברים
mov si, offset src
mov di, offset dst

copy_loop:
  mov ax, [si]          ; טען את הערך בכתובת src[i] אל AX
  mov [di], ax          ; כתוב את AX ל־dst[i]
  add si, 2             ; הגדל SI ב-2 כדי לעבור לאיבר הבא
  add di, 2             ; הגדל DI ב-2 ביעד
  loop copy_loop

ניתוח הקוד:

  • [si] ניגש לכתובת המקור הנוכחית (src)

  • [di] ניגש לכתובת היעד הנוכחית (dst)

  • צריך להגדיל כל פעם ב־2 כי כל איבר הוא 16 ביט = 2 בייט

  • loop חוזר 5 פעמים


דוגמה 3: גישה לאיבר מסוים לפי אינדקס

נניח שיש לנו מערך array db 10, 20, 30, 40, 50 ואנחנו רוצים לגשת לאיבר מס' 3 (האיבר הרביעי – כלומר 40)

mov si, offset array
mov al, [si + 3]    ; AL = array[3] = 40

אם המערך הוא של 16 ביט (כל איבר 2 בייט):

mov si, offset array
mov ax, [si + 3*2]  ; AL = array[3] = איבר הרביעי

מחרוזות באסמבלי

מחרוזת (string) באסמבלי היא פשוט מערך של תווים, שכל תו הוא בית אחד (db),
ובסוף המחרוזת, נשים את המספר 0.
"h", "e", "l", "l", "o", 0
תו הסיום של המחרוזת (0) נמצא כדי שהתוכנית תדע מתי המחרוזת נגמרת.

דוגמה:

msg db 'Hello, world!', 0

המחרוזת הזו תיראה בזיכרון כך:

תו קוד ASCII
H 72
e 101
l 108
l 108
o 111
, 44
32
w 119
o 111
r 114
l 108
d 100
! 33
\0 0

למה להוסיף 0 בסוף?

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

לכן כלל ברזל: מחרוזת = רצף תווים שמסתיים ב־0, המספר הזה מכונה גם הnull terminator.

עוד דוגמה:

welcome_msg db 'Welcome!', 0

עבודה עם מחרוזות ועם מערכים במעבד 8086

במעבד 8086 קיימות פקודות ייעודיות לעבודה עם מחרוזות ומערכים – שנועדו לייעל העתקות, השוואות ופעולות על נתונים עוקבים בזיכרון (כמו תווים של מחרוזת או איברי מערך).

רגיסטרים רלוונטיים

SI (Source Index)

  • רגיסטר שמצביע על מקור הנתונים בזיכרון. (כתובת מקור)

  • נחשב אוטומטית כיחסי ל־DS (Data Segment) כברירת מחדל.
    כלומר, אם תשתמשו בו, הוא אוטמטית יוסיף את הכתובת של הDS להתחלתו אלא אם תסמנו אחרת

DI (Destination Index)

  • רגיסטר שמצביע על יעד הנתונים בזיכרון. (כתובת יעד)

  • נחשב אוטומטית כיחסי ל־ES (Extra Segment) כברירת מחדל.
    כלומר, אם תשתמשו בו, הוא אוטמטית יוסיף את הכתובת של הES להתחלתו אלא אם תסמנו אחרת

CX (Counter Register)

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

פעולות בסיסיות על מחרוזות/מערכים

MOVSB – העתקת בתים ממקור ליעד

מעתיקה בית אחד מהכתובת [DS:SI] אל [ES:DI].

movsb

ולאחר ההעתקה הSI גדל ב1, והDI גדל ב1.

LODSB – Load String Byte

טועו בית מהכתובת [DS:SI] לתוך AL.

lodsb

ולאחר הטעינה, הSI גדל ב1.

STOSB – Store String Byte

שומר את AL לתוך [ES:DI].

stosb

ולאחר השמירה, הDI גדל ב1.

השוואת מחרוזות

CMPSB – Compare String Bytes

משווה את [DS:SI] ל־[ES:DI] (בית מול בית), ומשנה דגלים (Flags) בהתאם.

cmpsb
  • מבצע: [DS:SI] - [ES:DI] (בדומה לcmp)

  • הדגלים מתעדכנים, כמו בcmp שאנחנו מכירם (ZF, SF וכו')

  • הSI גדל ב1, והDI גדל ב1.

SCASB – Scan String Byte

מעתיק את AL ל־[ES:DI].

scasb

ולאחר ההעתקה, הDI גדל ב1.

הפקודה REP – Repeat

הפקודה REP חוזרת על הפקודה שאחריה CX פעמים, עד ש־CX = 0.

rep movsb

- תבצע movsb CX פעמים
- ואחרי כל פעולה: תחסיר את cx ב1, עד שcx 0.

פקודות נוספות:

  • REPE / REPZ – Repeat While Equal / Zero
    חוזר כל עוד ZF = 1

  • REPNE / REPNZ – Repeat While Not Equal / Not Zero
    חוזר כל עוד ZF = 0


דוגמאות מפורטות

דוגמה 1 – העתקת מחרוזת של 10 בתים:

mov cx, 10        ; מספר התווים
mov si, offset src
mov di, offset dst
rep movsb         ; העתקה מ־[DS:SI] ל־[ES:DI]

דוגמה 2 – העמסת מחרוזת לתוך AL בלולאה

mov cx, 5
mov si, offset str

loop_lodsb:
lodsb           ; טען תו ל־AL
; עשה משהו עם AL
loop loop_lodsb

דוגמה 3 – השוואת מחרוזות

mov cx, 10
mov si, offset str1
mov di, offset str2
repe cmpsb        ; חזור כל עוד תווים שווים

; אם CX = 0 → המחרוזות שוות
; אם ZF = 0 → נמצא הבדל מוקדם

דוגמה 4 – חיפוש תו במחרוזת

mov al, 'A'        ; התו שאותו מחפשים
mov cx, 20         ; אורך המחרוזת
mov di, offset str
repne scasb        ; חפש עד שתמצא

; אם CX = 0 → לא נמצא
; אם ZF = 1 → נמצא