- •Преобразование полиз в язык ассемблера
- •Введение
- •Входные данные
- •Модель исполнения полиз
- •Пример:
- •Особенности работы с данными в стеке исполнения
- •Описание перевода элементов полиз в язык ассемблера
- •Пример работы с данными в стеке исполнения
- •Описание перевода операций по работе с метками
- •Описание перевода операций по работе с памятью
- •Исходный код
- •Результат трансляции
- •Результат выполнения
Преобразование полиз в язык ассемблера
Введение
В качестве языка ассемблера будет использован 32-битный MicrosoftMacroAssembler, версии 6.14. Порождаемые исполняемые файлы будут 32-битными консольными приложениями дляMicrosoftWindows.
Такой выбор обоснован тем, что большинство современных программ являются 32-битными и написаны для операционной системы MicrosoftWindows, в то время как программы дляDOSперестают поддерживаться.
Входные данные
После описанного ранее этапа трансляции языка в ПОЛИЗ, мы получаем набор данных, который будет использоваться на этапе преобразования ПОЛИЗ в язык ассемблера. Напомним, что это за данные:
Таблица типов – таблица, содержащая исчерпывающую информацию обо всех используемых в программе типах данных.
Таблица переменных и именованных констант – таблица, содержащая исчерпывающую информацию обо всех используемых в программе переменных и константах.
Таблица меток – таблица, содержащая имена всех меток в программе.
Программа, транслированная в ПОЛИЗ – текстовый файл, содержащий операнды и команды ПОЛИЗ.
Модель исполнения полиз
Простейшей моделью для исполнения программы в ПОЛИЗ является стек.
Программа в ПОЛИЗ предполагает четыре вида элементов:
именованная переменная или константа (например, x)
число, например (например, 10)
метка (например, :lab1)
команда (например,@SET)
Встречая один из первых трех видов элементов, мы можем говорить о том, что кладем его в стек. Встречая команду, мы исполняем ее, передавая ей, в качестве параметров, то количество переменных из стека, которое она предполагает. После исполнения возможный результат работы команды помещается на вершину стека.
Рассмотрим пример:
a
30
@SUBINT
В данном случае, мы встречаем aи кладем его в стек. Затем, встречаем 30 и кладем его в стек. Затем встречаем команду сложения целых чисел @SUBINT, которая работает со стеком следующим образом – она вынимает из него два элемента, производит над ними операцию сложения и кладет результат обратно в стек.
Таким образом, при переводе в язык ассемблера, нашей главной задачей будет реализация стековой машины, эмулирующей исполнение ПОЛИЗ.
Перевод таблицы переменных в сегмент данных ассемблера
Прежде всего, рассмотрим, во что будут транслированы данные. Предположим, что в программе на исходном языке мы имели объявление такого вида:
...
var
a : type1;
b : type2;
...
После трансляции в ПОЛИЗ мы кроме текста транслированной программы получаем также несколько таблиц – таблицу переменных, таблицу типов и таблицу меток. Таблица переменных и таблица типов представляют собой полное описание всех используемых в программе переменных. Эти переменные необходимо поместить в сегмент данных в ассемблере.
Сгенерированный сегмент данных для приведенного примера будет выглядеть так:
...
_data segment
__variable_a <описание специфичное для типа type1>
__variable_b <описание специфичное для типа type2>
_data ends
...
Имена для переменных в сегменте данных соответствуют именам в таблице переменных, но к ним добавляется префикс __variable_, это делается затем, чтобы избежать возможных конфликтов имен с командами ассемблера.
В зависимости от типа данных могут порождаться различные описания переменной.
Для переменной целочисленного типа (integer) описание будет выглядеть так:
__variable_a dd 01h dup (0)
То есть целочисленная переменная представляет собой знаковое двойное слово (4 байта), изначально инициализированное значением 0.
Аналогично, для переменной рационального типа (rational):
__variable_a dd 00h, 01h
То есть рациональная переменная представляет собой два подряд идущих знаковых двойных слова (8 байт), причем первое двойное слово соответствует числителю дроби, а второе – знаменателю дроби, таким образом рациональная переменная инициализируется значением дроби 0/1.
Переменные типа-диапазона и перечислимого типа транслируются таким же образом, как и переменные целочисленного типа. Таким образом логический тип (boolean) является частным случаем перечислимого типа и, поэтому занимает 4 байта.
Переменные, являющиеся массивами рациональных чисел транслируются следующим образом:
__variable_a dd <количество элементов в массиве> dup (0, 1)
При таком объявлении каждый элемент массива инициализируется в значение дроби 0/1. Многомерные массивы приводятся к одномерным массивам с аналогичным количеством элементов.
Все остальные переменные-массивы транслируются в:
__variable_a db <размер массива в байтах> dup (0)