Створення інтерпретатора команд

Мета: Розробити набір команд, якими будуть керуватися рухомі об’єкти (юніти), визначити структури даних, що формувати образ процесу, написати програму-інтерпретатор для розробленого набору команд.

В нашій віртуальній оболонці, яка виконує задачі операційної системи, рухомі об’єкти є аналогом процесів. Наша «операційна система» завантажує в пам’ять код «процесів», виділяє певні структури даних кожному «процесу», забезпечує виконання декількох процесів одночасно та т.і.

Задачею другого етапу є розробити набір команд, за допомогою яких ми зможемо створювати рухомі юніти, наділяти кожен з них власною логікою, власною поведінкою у віртуальному середовищі. Крім самих команд нам потрібно продумати синтаксис «мови програмування» об’єктів.

Для зручності подальшої організації програми-інтерпретатора пропоную обрати синтаксис наближений до машинної мови. Такий синтаксис можна запозичити у мови асемблера. Давати короткі мнемонічні назви командам, вказувати операнди через пробіл, виділяти кожній команді окремий рядок. Передбачається, що команди будуть виконуватися рядок за рядком починаючи з самого початку файлу і змінити цей порядок можуть лише спеціальні команди переходів.

Увага! За приклад синтаксису можна також використати синтаксис класичного Бейсіку. Головне – це зробити текст програми максимально зручним для пошагової інтерпретації і тим самим не ускладнювати систему.

Тепер щодо самих команд. Якщо наш об’єкт буде рухомим, йому перш за все потрібні команди, щоб виконувати прості рухи. Такими командами можуть стати:

ШАГ – зробити крок вперед.

НАЛІВО – виконати поворот наліво.

НАПРАВО – поворот направо.

Щоб виконувати ті чи інші дії в залежності від ситуації нам потрібні команди порівняння та умовних переходів. Щоб наблизити наш набір команд до машинної мови, організуємо порівняння наступним чином:

ПОРІВНЯТИ <операнд1>,<операнд2> - порівнює два значення і результат порівняння заносить в службову комірку пам’яті, наприклад, 1, якщо перший операнд більший, -1, якщо менший, та 0, якщо операнди рівні.

БІЛЬШЕ <№ рядку> - виконує перехід на рядок з вказаним номером, якщо перший операнд останнього порівняння виявився більшим за другий.

МЕНШЕ <№ рядку> - виконує перехід на вказаний рядок, якщо перший операнд був меншим за другий.

РІВНІ <№ рядку> - передає керування на вказаний рядок, якщо обидва операнди рівні.

Для організації циклів нам буде потрібен оператор безумовного переходу на потрібний рядок.

ПЕРЕЙТИ <№ рядка> - виконує перехід на потрібний рядок.

Такий простий набір команд дозволить нам створювати досить функціональні юніти, якщо ми гарно продумаємо структуру тих областей даних, що будуть доступні кожному з них.

Область даних кожного «процесу» (юніту) в нашій системі можна буде розділити на три основні частини:

1. Область службової інформації, що використовується лише нашою «операційною системою» і недосяжна для програми юніта.

2. Область системного сервісу – данні, що надаються програмі юніта нашою системою, але програма юніта не може ці данні змінювати.

3. Область даних програми юніта – данні, що належать самому «процесу» і можуть використовуватися ним як забажається.

В область службової інформації ми внесемо ті данні, що необхідні самій системі для керування процесами.

Так як в нашій системі будуть присутні одразу декілька процесів, то перш за все нам потрібно виділити комірку пам’яті під ІДЕНТИФІКАТОР ПРОЦЕСУ. Цим ідентифікатором може бути просто двох- або одно- (все ж таки система наша невеличка) байтове число. Також для пошагового виконання програми процесу нам потрібен ЛІЧИЛЬНИК КОМАНД, що буде зберігати номер рядка, в якому знаходиться команда, що буде виконуватися наступною. Юніти в нашій системі будуть різні, з різними правилами взаємодії, тому нам потрібна буде змінна ТИП. Для того, щоб визначити, де на ігровому полі знаходиться юніт, нам можуть знадобитися такі змінні, як КООРДИНАТА Х та КООРДИНАТА Y. Розмірність цих змінних буде залежати від розмірів ігрового поля. Також нам потрібна буде змінна НАПРЯМОК, щоб знати в якому напрямку «дивиться» юніт. Можна доповнити цей список службових даних за власним розсудом, наприклад, можна додати рядок, в якому буде зберігатися назва юніту, тощо.

Увага! Навіть після того, як ви все обміркували і вам здається, що ви виділили всі необхідні комірки пам’яті, створіть ще дві, а краще чотири, резервних комірок – в процесі вдосконалення програми вони можуть знадобитися. Хоча б для того, щоб ввести систему пріоритетів, про яку ми будемо говорити пізніше.

В області системного сервісу ми розташуємо інформацію, яку юніт зможе використовувати в своїй життєдіяльності. Такою інформацією може бути РІВЕНЬ ЖИТТЯ, КОД ОБ’ЄКТУ В ПОЛІ ЗОРУ, ВІДСТАНЬ ДО ОБ’ЄКТУ В ПОЛІ ЗОРУ, КОД ПОМИЛКИ (якщо якусь дію юніту система не може виконати). Не забудемо зарезервувати декілька додаткових комірок.

І нарешті ми формуємо область даних самого юніта. Цю область даних юніт може використовувати за власним розсудом – для зберігання проміжних значень, лічильників тощо. На ці потреби можна виділити, наприклад, чотири комірки пам’яті і дати їм назви РЕГІСТР1, РЕГІСТР2, РЕГІСТР3, РЕГІСТР4.

Визначивши області даних, ми можемо сформувати структуру образу процесу (юніта), який буде поєднувати в собі службові області даних та код програми юніта. Для зберігання образу процесу в програмі, що написана мовою Pascal, можна сформувати наступний запис:

process = record

{Область службової інформації}

ID:byte; {ідентифікатор процесу}

CC:byte; {лічильник команд}

TIP:byte; {тип юніту}

X:byte; {координата х}

Y:byte; {координата у}

N:byte; {напрямок}

Res1:byte; {резерв 1}

Res2:byte; {резерв 2}

NAME:string[10]; {ім’я юніту}

{Область системного сервісу}

LL:byte; {рівень життя}

Obj:byte; {код об’єкту в полі зору}

Dist:byte; {відстань до об’єкту}

Er:byte; {код помилки}

Res3:byte; {резерв 3}

Res4:byte; {резерв 4}

{Область даних процесу}

R1:byte; {регістр 1}

R2:byte; {регістр 2}

R3:byte; {регістр 3}

R4:byte; {регістр 4}

{Код процесу (програма юніта}

KOD:array[1..100]of string[30];

end;

Щоб забезпечити розташування в пам’яті нашої системи декількох процесів можна зробити масив з об’явлених записів:

mproc: array[1..20] of process;

Увага! За допомогою такого підходу ми реалізуємо так зване фіксоване розподілення пам’яті, яке є найбільш простим, але найменш ефективним. Ваша оцінка буде значно вищою, якщо ви використаєте більш ефективне розподілення пам’яті.

Після того, як ми сформували структури даних образу процесу і визначили, які данні будуть доступні юніту, ми можемо повернутися до формування системи команд і розширити її, додавши, наприклад, команди переносу (привласнення), арифметичні дії, генерацію випадкових чисел тощо.

Останнім шагом буде створення програми інтерпретатора команд. В цій програмі нам потрібно забезпечити зчитування з масиву KOD рядка під номером СС. Обробка рядку з метою визначити команду, її операнди. Виконати цю команду – змінивши структури даних процесу (наприклад, якщо це команда STEP – змінити координати об’єкта згідно з НАПРЯМКОМ його руху). Занести у відповідну комірку код помилки (або 0, якщо команда виконалася без помилок) і збільшити змінну СС, щоб згодом перейти до наступної команди.

Увага! Ефективність вашої системи буде в значній мірі залежати від ефективності програми-інтерпретатора. Тому приділіть особливу увагу її розробці.



Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: