לדלג לתוכן

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

מערך (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 items
mov si, offset array    ; starting address
xor ax, ax              ; zero out AX, AH will hold the sum

sum_loop:
  lodsb                 ; load AL <- [DS:SI], increment SI
  add ah, al            ; sum kept in AH (careful: Overflow!)
  loop sum_loop         ; CX -= 1, continue if 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 items
mov si, offset src
mov di, offset dst

copy_loop:
  mov ax, [si]          ; load the value at address src[i] into AX
  mov [di], ax          ; write AX to dst[i]
  add si, 2             ; increment SI by 2 to move to the next element
  add di, 2             ; increment DI by 2 at the destination
  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] = the fourth element

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

מחרוזת (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        ; number of characters
mov si, offset src
mov di, offset dst
rep movsb         ; copy from [DS:SI] to [ES:DI]

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

mov cx, 5
mov si, offset str

loop_lodsb:
lodsb           ; load a character into AL
; do something with AL
loop loop_lodsb

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

mov cx, 10
mov si, offset str1
mov di, offset str2
repe cmpsb        ; repeat while characters are equal

; if CX = 0 -> the strings are equal
; if ZF = 0 -> a difference was found early

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

mov al, 'A'        ; the character we're searching for
mov cx, 20         ; length of the string
mov di, offset str
repne scasb        ; search until found

; if CX = 0 -> not found
; if ZF = 1 -> found