איך זה עובד: שפות תכנות
מהי שפה עילית, מהו אסמבלר, וכמה מסובך לצייר עיגול: מבוא למדעי המחשב - שיעור שלישי. לא למתכנתים בלבד
בשבוע שעבר עלינו – וזה לא היה פשוט – מרמת הפעולות הלוגיות הבסיסיות, שמתרחשות פיזית בסיליקון של המעבד, ל"פקודות" פרימיטיביות בשפת מכונה. פקודות אלה הן בעצם רצפים קצרים של ביטים (0 ו-1), אשר גורמים למעבד לבצע פעולות קצת יותר מורכבות: חיבור מספרים, השוואה ביניהם, קבלת ושליחת נתונים דרך ערוצי קלט/פלט, וכדומה.
הקשר בין רצפי הביטים לבין הארגון הפנימי של רכיבי הסיליקון הדוק מאד: הפקודה "01011101", למשל, במעבד מדגם אחד יכולה לבצע פעולה שונה לגמרי ממה שתבצע אותה פקודה בדיוק במעבד מדגם אחר.
איך זה עובד ב-ynet מדע
שפות סף
כדי להפוך את התכנות לקצת יותר אוניברסלי ונוח הומצא האסמבלר: תוכנה שלוקחת קוד בשפת אסמבלי הסטנדרטית (פחות או יותר) וממירה אותו לשפת המכונה שמתאימה למעבד הספציפי. בניגוד לשפת המכונה, שפת אסמבלי נכתבת באותיות וספרות במקום בביטים, והפקודות בה מובנות בקלות יחסית גם על ידי מי שאינו מכיר בעל-פה את הארכיטקטורה של המעבד.
לדוגמה, הפקודה ADD פירושה "חבר", CMP היא "השווה" (Compare), ו-JZ זה לא ראפר מצליח אלא "קפוץ במקרה של 0" (Jump on Zero) – פקודה שמעבירה את השליטה לפקודה מרוחקת אי-שם בקוד אם התוצאה של החישוב המתמטי האחרון שבוצע היתה 0. כמעט כל פקודה באסמבלי מלווה בפרמטרים נוספים, שאומרים איזה משתנה לחבר למי, את מי להשוות, לאיזו פקודה בדיוק לקפוץ וכן הלאה.
האסמבלר הראשון נכתב בהכרח בשפת מכונה, כי זה הדבר היחיד שהמעבד מבין, וזו היתה ללא ספק עבודה קשה להחריד – אף על פי שההמרה מאסמבלי לשפת מכונה היא ישירה למדי. בכל אופן, מרגע שנכתב האסמבלר הראשון, לא היה עוד צורך לכתוב לעולם ולו ביט יחיד בשפת מכונה! כל תוכנה שרוצים לכתוב, כולל אסמבלר חדש ואפילו אסמבלר שמיועד למעבד מסוג אחר, אפשר לכתוב באסמבלי ולהמיר בעזרת האסמבלר הקיים לשפת המכונה המתאימה.
לרוע המזל, אסמבלי היא שפת תכנות לא נוחה, בלשון המעטה. הפקודות מבצעות רק את הפעולות הבסיסיות ביותר: משהו שנראה לנו בני האדם פשוט, כמו לצייר עיגול על מסך, עלול לדרוש עשרות ומאות שורות קוד. בנוסף, הדרך היחידה לבצע בדיקות אם-אז, להריץ לולאות או לשלוט בכל בצורה אחרת בזרימת התוכנית היא בעזרת קפיצות ממקום למקום, וזה מבלבל ובלתי קריא (מה שמכונה בז'רגון המתכנתים "קוד ספגטי").
אם ניקח את ציור העיגול כדוגמה, יש דרך מצוינת לחסוך את כל שורות הקוד ההן: נעשה מאמץ חד-פעמי, ונכתוב באסמבלי תוכנה שמסוגלת לפענח שפה חדשה, "גבוהה" יותר, אשר כוללת פקודה בשם Circle. את קוד האסמבלי הדרוש לציור בפועל נאחסן בינתיים בצד, וכאשר המתכנת יכתוב את הפקודה "Circle", התוכנה שלנו תשתול את קטע האסמבלי הרלוונטי בתוך התוצר הסופי! ככה נחסכת כתיבה-מחדש של כמויות טקסט עצומות, והקוד הרבה יותר קריא ונוח.
שפות עיליות
שפות תכנות "עיליות" אמיתיות, כגון C, בייסיק או פסקל, עושות כמובן הרבה יותר מאשר אריזה נוחה של גושי קוד אסמבלי. התוכנות שמפענחות אותן מפרקות, לדוגמה, ביטויים מורכבים (כגון "a = b + c * d") ליחידות שהמעבד מסוגל לבצע, מנהלות "טיפוסי משתנים" מורכבים כמו מחרוזות או מערכים של מספרים, וממירות את הנחיות הזרימה של התוכנית, שכתובות בשפה שבני אדם יכולים להבין, לקפיצות התואמות (והמכוערות) באסמבלי. תוכנת פיענוח שהופכת את הקוד לקובץ הוראות בשפת מכונה נקראת "מהדר" (Compiler), ואילו תוכנה שמפענחת ומבצעת את הקוד פקודה אחר פקודה בזמן אמת נקראת "מפרש" (Interpreter). עם השנים נוספו שפות משוכללות ועשירות יותר, עם מאפיינים חדשים שמקלים על פיתוח תוכנה לסוגיה.
נחזור על הדרך שעשינו: התחלנו בשכבה הפיזית (סיליקון), עברנו לשכבה הלוגית (שערים לוגיים), ממנה לשכבת הפעולות האלמנטריות (שפת מכונה). השכבה הבאה (אמסבלי) ניתקה אותנו במידה רבה מהארכיטקטורה הספציפית של המעבד, אם כי מתכנת אסמבלי עדיין חייב לקחת בחשבון המון שיקולי חומרה. השכבה עליה אנחנו מדברים עכשיו, שכבת השפות העיליות, מאפשרת למתכנת להתעלם אפילו מזה ולממש משימות של עיבוד מידע בכלים רעיוניים של מבני נתונים ואלגוריתמים.
הרמה האלגוריתמית
ניקח לדוגמה תוכנה שמשחקת שחמט. התיישבות ליד המקלדת וכתיבת קוד היא הדבר האחרון שהמתכנת צריך לעשות (אלא אם הוא מתכנת זוטר שממלא הוראות של מפתח אלגוריתמים). העבודה מתחילה בראש, בחלוקה של הבעיה הראשית ("איך לנצח במשחק שחמט?") לתת-בעיות, את תת-הבעיות לתת-בעיות קטנות יותר, וכך הלאה עד לבעיות קטנות ובסיסיות כל כך שאפשר "להסביר" אותן אפילו למחשב חסר הבינה. למשל:
1. כדי לנצח בשחמט צריך לבחור, בכל מהלך, את הצעד שיביא לניצחון (אם יש כזה) או ייצור את מירב הסיכויים לניצחון.
1.1 כדי לבחור צעד, צריך לדעת מהם הצעדים האפשריים.
1.1.1 כדי לדעת מהם הצעדים האפשריים, צריך לדעת את מצב הלוח הנוכחי ולהכיר את החוקים.
1.1.1.1 כדי לדעת את מצב הלוח, צריך ייצוג של הלוח בתוכנה. הלוח מחולק ל-8 על 8 מקומות, כל אחד מהם שחור או לבן, ומסוגל "להחזיק" כלי שחור או לבן משישה סוגים שונים. את המידע הזה אפשר לאחסן במבנה נתונים מתאים.
1.1.1.2 את צעדי הכלים השונים אפשר לחלק למרכיבים: כיווני תנועה מותרים (בציר X ובציר Y), מרחק תנועה מותר, נסיבות מגבילות (כגון נוכחות של כלי מאותו צבע במשבצת היעד) ונסיבות מאפשרות (תקיפה אלכסונית של רגלי, הצרחה וכו'). חלק מהמרכיבים הללו אפשר לאחסן במבני נתונים פשוטים, ואחרים צריך לבדוק בשטח באמצעות אלגוריתמים, כגון תנאי אם-אז.
שפות התכנות מתחלקות למספר קבוצות, שכל אחת מהן מספקת למתכנת סט כלים שונה במקצת לפתרון בעיות. שפות "פרוצדורליות" כמו C עובדות בשיטה של הפרדה בין מבני נתונים לאלגוריתמים. שפות לתכנות מונחה-עצמים, כמו C++ או ג'אווה, עובדות עם "אובייקטים", מעין יחידות שכוללות הן נתונים והן אלגוריתמים משל עצמן, ומתקשרות זו עם זו דרך התוכנית הראשית. בלי להיכנס לוויכוח האינסופי והמיותר של איזו שפה הכי טובה, ההבדלים הללו בין השפות מדגישים את העובדה שהשפות העיליות הן שכבת הפשטה בפני עצמה, שקיימת במנותק מכל מה שמתחתיה, אף על פי שבסופו של דבר, כל תוכנה בעולם מסתכמת באוסף של הוראות בשפת מכונה שרצות כזרמים חשמליים במעבד.
בשבוע הבא נחזור לדברים שאפשר לפרק עם מברג, ונתמקד בפריט שרבים מאיתנו רצו לפרק ולהשמיד, עם מברג או בלעדיו: רדיו-שעון מעורר. בינתיים, אתם מוזמנים לקרוא עוד על חומרה ותוכנה בבלוג שלי "הבייט הלבן".