אסמבלי: שיטות מיעון ופויינטרים ל- 68HC12B32 Family, D60 & A128

אסמבלי: שיטות מיעון ומצביעים  ב-HC12 68 – מאת שחר מנדלוביץ'

 

מהי שיטת מיעון ומהי משמעות השם? ובכן, ההגדרה "הרשמית" שמוטורולה מציעה: "שיטות מיעון קובעות כיצד היע"מ (יחידת עיבוד מרכזית, CPU) שולפת מהזיכרון את המידע שעליו היא אמורה לבצע את הפעולות הנתונות".

 

ראשית נגדיר כתובת אפקטיבית (effective address). לכל כתובת בזיכרון יש מספר כלשהו בגודל 16 ביט, כלומר 2 בייט. מספר זה כולל 4 ספרות הקסאדצימליות ומסומן עם ערך $ בתוכנה (לדוגמא הכתובת 1000 תהיה מוגדרת כ- 1000$ ). כתובת זו מצביעה על המקום בזיכרון שבו יש ערך כלשהו של 8 ביט. יש לדעת להפריד בין 2 אלו - הכתובת היא 2 בייט, המצביעה על ערך של 1 בייט (1 בייט=8 ביט).  כל שיטות המיעון חוץ משיטת המיעון המובלעת (inherent) מחזירות ערך כתובת בן 16 ביט.

 

שיטות המיעון:

 

·                  Inherent (מובלעת): בוודאי כבר השתמשתם בשיטה זו מבלי לדעת, מפני שהיא משתמשת באופרנדים פנימים של היע"מ. לדוגמא – INCA ,DEX  וכדומה. אלו פעולות על הצוברים, על הרגיסטרים X ו-Y ועוד.

 

·                  Immediate  (מיידית): משמעותה התייחסות לערך שנמצא מייד אחרי ההוראה: ldaa #$60 , %11001001#ldx   או ldab #@64.

שימוש נוסף הוא בתנאי IF, לדוגמא: אם ישנו תנאי התלוי בביט מסוים, נאמר ביט 4, בכתובת ששמה TCTL1, אז:

BRCLR  TCTL1,$10, > address >

כלומר, אם ביט זה אפס קפוץ ל....

 

·      Direct (ישירה): במקרה זה, אין אנו מציינים את הערך אליו אנו מתייחסים, אם כי את הכתובת בה הוא מצוי. נאמר וישנו ערך מסוים בכתובת FF$ ואנו רוצים להטעינו לצובר A: ההוראה המתאימה היאldaa $FF . והערך בכתובת $FF נטען לצובר A. שיטה זו טובה רק עבור כתובות מ-0 עד FF . מדוע להשתמש בה אם כך? ובכן, שיטת מיעון זו עובדת עם כתובות קצרות יותר, וכתוצאה מכך בפחות זיכרון, ואף מהירה יותר. אולם, אם צריך לגשת לכתובת בת 16 ביט?

 

·      Extended (מורחבת): שיטה זו זהה לקודמת וניתן אפילו להשתמש בה כ- $00FF במקום מה שרשום, אך היא טובה לכל הכתובות הקיימות במרחב 16 ביט.

 

·      Relative (יחסית): שיטה זו היא שיטה המתייחסת לשורה כרגע של ה- Program Counter, ואל השורה בה נמצא המידע הדרוש. מספר שורות זה מוחסר אחד מהשני ואז נוצר מספר שלילי או חיובי – כלומר signed מה שאומר – קפיצה של 128 שורות אחורה או 127 קדימה.

שיטה זו היא רק בעבור פקודות בקרת זרימה, לדוגמא branch, קפיצה ממקום למקום בתוכנה. קפיצותLong  ו-Short משתמשות בשיטה זו ללא הבדל. אך ראשית אסביר כיצד פקודות branch  מתבצעות: אלו הן פקודות יחסיות, בניגוד לפקודת JMP, שפשוט מקבל כתובת label  וקופץ אליה (ולכן  הוא אבסולוטי) ה-branch לוקח כתובת נוכחית , מחסיר ממנה את ערך ה- label  שהתקבל וקופץ מספר שורות זה- מ- 128-  עד 127 שורות. כלומר- קופץ אחורה וגם קדימה. LONG BRANCH קופץ לא עד FF שורות כמו ה- BRANCH הרגיל, אלא עד 7FFF  שורות  קדימה ועד 8000 שורות אחורה.

 

 

·      Indexed:  כאן נלקח מספר כלשהו מאינדקס רגיסטר – X,Y  וכדומה ובנוסף ערך offset עבור החיבור של שניהם (שנעשה אוטומטית בתוך המעבד) נקבל מספר שמשמש ככתובת או כמצביע למקום אחר בתוכנה או בזיכרון.

 בשיטה זו נשתמש ב-Index Register (רגיסטרים X או Y), בנוסף – קבוע שמקורו בצובר (A או B) או ממספר ישיר. בנוסף ניתן להשתמש בשיטה זו ב-pre/post increment/decrement, כלומר תוך כדי שימוש בשיטת המיעון, ניתן לחבר/לחסר 1 מאחד האוגרים. התחביר הוא כמו ב- ++C : +x יעלה את X בערך ואז ישתמש ב- X ו- X+   ישתמש ב- X ואז יעלה אותו בערך.

 

לשיטה זו מספר יתרונות:

1.                                                                                                    ניתן להשתמש ב-Stack pointer בתור Index Register בכל הפעולות.

2.                                                                                                    באופן דומה, ניתן להשתמש ב- Program counter למעטpre/post increment/decrement וזאת משום שה- program counter אינו משתנה לרוח המתכנת אלא לפי השורה בה נמצאת התוכנה.

3.                                                                                                    pre/post increment/decrement אוטומטי עם 8- עד 8+ .

4.                                                                                                    Offset  של 5, 9 או 16 ביט signed קבועים.

5.                                                                                                    ניתן לעשות זאת גם עם צובר D.

Offset – ערך המוסף לערך הבסיס בכדי ליצור את המספר הסופי. לדוגמא- ערך בסיס X, offset A אז הערך הסופי הוא הבסיס ועוד ה- offset:    A+X=<final value> .

 

5  ביט offset: ניתן לקחת כל כתובת ולתת לה offset של 16- עד 15+ כתובות ממנה. פקודות עם offset מסוג זה מהירות למדי וזאת משום שהשימוש הוא במספרים קטנים למדי, כלומר שימוש מינמלי בביטים. לדוגמא:

LDAA  4,X

STAB -8,Y

X ו- Y נקראים ערך הבסיס. הם קובעים את כתובת הבסיס דרכה נשיג דרך למידע שנרצה. המידע שנרצה נמצא בכתובת קרובה לערך הבסיס והוא מושג ע"י תוספת ה offset שהוזכר מקודם.

בדוגמא שלעיל, אם ב-A היה הערך ההקסאדצימלי 1000$ וב-Y הערך 2000$, אזי ה-offset אינו משנה את הערך שב-X או ב-Y. לאחר פקודות אלו, ב-A ימצא הערך שבכתובת 1004$ וב-Y נקבל פעולה שונה במקצת, אך עובדת באופן דומה. הערך מ-B נלקח וישמר בכתובת: $2000-$8=$1FF8.

 

9 ביט offset: כאן ניתן להשתמש בערכים מ- 255 עד 256- כאשר הביט ה- 9 הוא למעשה ביט הפלוס/מינוס. ניתן להשתמש בכך באותו אופן כמו ב-5 ביט ללא שינוי, להוציא שטווח ה- offsetגדול יותר. יש לזכור לשים סימן מינוס במקרה של ערך שלילי: 20- לדוגמא.

 

16 ביט offset: במרחב כתובות של 16 ביט, אין זה משנה אם זה ה-offset הוא עם סימן, או ללא-סימן (signed/unsigned) מפני שאורך התוכנה מגיע עד 16 כך שעבור FFFF$ בין אם הוא 65,535 או 1- , הכוונה היא לאותה כתובת.

 

 

·                        16 Bit Constant Indirect: ובכן, מה שאציג הוא למעשה מצביע (pointer) אותו ודאי אתם מכירים. בשיטה זו ניקח ערך כתובת, נוסיף לו offset, ובחיבור שלהם יתקבל ערך כלשהו.  ערך זה הוא מצביע, המצביע על הכתובת ממנה ניקח את הערך הסופי. שיטה זו יעילה מאד לשימוש בתוכנה, אם משתמשים בקבועים ומשתנים שונים ויכול לעזור בהקטנת גודל התוכנה ובשיפור קריאותה.  לדוגמא:

LDAA [6,Y]

           

כעת נניח תנאי התחלה: Y=$1000; הכתובת 1006$ מכילה את הערך 2000$.

            פקודה זו תעביר את הערך של Y לזיכרון, ותוסיף לו 6. הערך החדש משמש ככתובת.

הערך שהכתובת מצביעה עליו נלקח, כלומר הערך 2000$ שבכתובת 1006$ מועתק ל-A. הערך שבבסיס אינו משתנה.

שימוש נפוץ השיטה זו הוא בתחביר :

                         JSR [#value, index register]

כלומר, Index Register  יהיה בעל הכתובת של שורה מסוימת, לדוגמא בתוכנית:

 

MOVW #LINE, HOLDER

LDX      #HOLDER

JSR       [0,X]

…..

                  LINE

…….

 

 

הסבר: ראשית נכניס את ערך השורה Line למשתנה HOLDER ונכניס ל-X את מספר הכתובת של HOLDER. כעת, עם offset=0, ישנו Jmp to SubRoutine. מה קורה למעשה? הערך נלקח מ-X, ומשמש ככתובת. בכתובת זו ישנו הערך של line. ערך זה משמש ככתובת אליה פקודת ה- JMP קופצת.

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

 

 

·      Auto Pre/Post Decrement/Increment: שיטה זו משנה את הערך של הבסיס ב- 8- עד 8+ באופן הבא: DAA 4,+X   ייקח את הבסיס ויוסיף לו 4 . כעת יהיה בבסיס ערך המשמש ככתובת שהערך בה מועתק ל-A.

במקרה הבא: STAB  3,Y-, יילקח הערך מ-B, ישמר בכתובת שנמצאת בבסיס (Y) ואז  יופחת מהבסיס 3, כלומר הערך 3 יופחת מ-B רק אחרי ש-B נשמר בכתובת שהייתה בבסיס המקורי. ניתן להשתמש גם ב- X ו-Y בו זמנית, כלומר עם 4 ערכים בו זמנית.

שימו לב: לא בכל שיטת מיעון PRE/POST  עובד! על מנת לראות עבור איזה שיטות ניתן לנסות, או לפתוח את HC12 Reference Manual .

 

·      Accmulator D Indirect: בשיטה זו ניתן להשתמש בערך של D כדי ליצור כתובת בזיכרון המכילה מצביע לכתובת בה הערך הרצוי. מה שמבדיל שיטה זו הם הסוגריים המרובעים - "[" ו- "]".

                  לדוגמא:

                                   JMP    [D,PC]

                                               DC.W  ADDR_1

                                               DC.W  ADDR_2

                                               DC.W  ADDR_3

                                               DC.W  ADDR_4

                                  

 

 ADDR_1:

…..

ADDR_2:

…...

ADDR_3:

……

ADDR_4:

            כאשר ל- D נכניס את הערכים - $0, $2,$4,$6,$8,$A,$C,$E  וכך הלאה. 

            יש להקפיד על המבנה הזה! עבור D=0 תקפוץ הפקודה ל-ADDR_1 , ל- D=2, תקפוץ

            הפקודה  ל- ADDR_2 וכך הלאה. ניתן להשתמש בכל אינדקס בבסיס.

 

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

 

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

 

חזרה




מעוניין לדעת כיצד לשלוט על מנועים טוב יותר?

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

כל הזכויות שמורות לאסף פוניס, גיא יונה ואלי קולברג ©
אין להעתיק תכנים מאתר זה ללא רשות בכתב ממנהלי האתר

אתר זה נצפה באופן מיטבי ברזולוציית 1024X768