Машинно ориентированное программирование
Рассматривается два подхода к машинно независимому эффективному программированию. Язык Forth - пример организации вычислений над стеком. Можно рассматривать как язык-ядро с возможностью практически неограниченного проблемно-ориентированного расширения. Язык Little - пример традиционной организации вычислений над структурированными битовыми строками. Основные конструкции аналогичны фортрановским, но с учетом современных решений, упрощающих реализацию компилятора. Оба языка допускают порождение эффективного кода "хорошо" написанных программ [[3],[32]].
Появление в 70-х годах микропроцессорных средств с малым объемом памяти и сравнительно невысоким быстродействием вызвало очевидные трудности в применении привычных языков высокого уровня. Не удивительно, что поиск подходящих средств программирования сменил приоритеты в оценке свойств языков и систем программирования. На передний план вышли машинно-ориентированные языки-оболочки, поддерживающие достижение эффективности и компактности программ при достаточном уровне абстрагирования от конкретики процессоров, сравнимом с абстрагированием в языках высокого уровня. Особо ярким явлением в эти годы стал язык Forth, сохранивший многочисленных приверженцев и в наши дни, когда пресс технических характеристик изрядно помягчал [[6]]. Little приведен как пример альтернативного подходе к решению проблемы машинно ориентированного программирования.
Наиболее убедительное применение Forth получил как средство разработки специализированных систем, создаваемых по методике раскрутки, начиная с тщательно минимизированного ядра с рядом последовательных шагов расширения в рамках единой оболочки. Сам язык устроен по такому же принципу, так что такая технология пошагового программирования впитывается при изучении идей и средств системы программирования на базе Forth-а. Кроме общеизвестных примеров про системы управления астрономическими телескопами и лазерами, следует упомянуть IBMCAD - аналог системы инженерного AutoCAD и систему автоматизации издательского дела МРАМОР.
Для последней именно на Форте была создана предельно компактная операционная система объемом около 4 килобайт. Форт был успешно применен при реализации языка PostScript, до сих пор используется при разработке видеоигр и систем начальной загрузки ОС, чувствительных к скорости срабатывания. При значительном внешнем несходстве Форт обладает концептуальным родством с языком Лисп, что побудило в 80-е годы к разработке языка ФоЛи, объединяющего достоинства обоих языков.
Программирование на Форте требует вдумчивости и аккуратности. Достижимость лаконичных форм дается ценой нестандартных индивидуальных решений, мало приспособленных к передаче программ в чужие руки. Лозунги "Программируйте все сами!" и "Не бойтесь все переписывать заново!" правильно отражают подход к программированию на Форте. Успех достигается явным максимализмом в тщательной отладке и способностью видеть задачу программирования в развитии.
Язык Форт предложен Чарльзом Маури в 1968 году. К середине 70-х Форт стал третьим по популярности после Бейсика и Паскаля, завоевавшим свои позиции при освоении микропроцессоных средств. По технике программирования Форт весьма похож на макроассемблер, только вместо системы команд над машинными словами в нем используется система операций на стеком.
Программирование на Форте сопровождается систематической сверткой понятий, синтаксис применения которых созвучен польской записи. Можно сказать, что хорошая программа на Форте - это специализированная виртуальная машина, приспособленная к дальнейшему расширению по мере развития постановки задачи.
Система программирования для языка Форт содержит пару интерпретатор-компилятор, причем техника компиляции весьма эффективна. Система использует единый порядок представления данных и команд в программе - все это последовательности слов. Данные располагают перед операциями по их обработке. Операция - это известное системе слово. Данные просто загружаются на стек, из которого операция их берет в соответствии с ее числом параметров.
Слова система хранит в словарях, образующих контекст исполнения программы. Непрерывная среда разработки программ содержит средства отладки, управления разработкой и методами обработки текста программы, включая гибкое сочетание интерпретации и компиляции.
Все это позволяет утверждать, что Форт - это и технология, и философия, и язык программирования, обеспечивающий управляемый компромисс между сложностью разработки программ и удобным программированием в терминах задачи.
Хорошо написанные на Форте программы обладают гибкостью, универсальностью, компактностью, расширяемостью и простотой. Удобочитаемость программ не входит в число достоинств этого языка, но при определенных лингвистических навыках потенциально достижима. Возможна выработка стиля программирования на Форте типа мимикрии под другие языки.
Массовое распространение Форта по времени совпало с интенсивным обновлением микропроцессорных средств, расширением их возможностей и эксплуатационных характеристик. Это повлияло на приспособленность языка к обеспечению совместимости и переносимости программ на разные версии Форта своими средствами, а также существенную открытость для программиста.
Традиционно отмечаемые недостатки Форта:
- сложность записи программ для новичков;
- необходимость понимания всех процессов исполнения программы;
- трудность отчуждения программы от системы программирования;
- нестандартность языка, это не типовой язык программирования;
- строго последовательное исполнение потока операция;
- слабый контроль ограничений на оперативную память;
- неполный цикл разработки не приспособлен к производству.
Активный популяризатор Форта Мур отметил: "Форт не уравнитель, а усилитель!"
Итак, программа на Форте строится как последовательность слов. Данные - это тоже слова. Логическое значение "истина" - 0. В качестве элементарных данных выступают литеры, целые без знака и со знаком, неотрицательные целые, адреса, двойные слова и др. типы данных.
Имеются константы TRUE и FALSE, символизирующие логические значения.
Заключение в кавычки "строка" означает вариант со счетчиком и ограничитель - 0
Состояние системы программирования можно исследовать на уровне системных переменных. Различается состояние компиляции (создание кода программы) и интерпретации (непосредственное исполнение программы). Если STATE = 0, то система ведет исполнение программы.
Системная переменная BASE задает систему счисления.
Исполнение программы организовано как диалог над стеком. Каждая команда знает, что взять из стека и во что преобразовать:
DROP (X --)1) = сбросить из стека DUP (X -- X X) = скопировать вверх NIP (X1 X2 -- X2) = сбростиь предпоследний OVER (X1 X2 -- X1 X2 X1) = предпоследний наверх PICK (XN ... X0 N -- XN ... X0 XN) = выборка из середины стека ROLL (XN ... X0 N -- XN-1 ... X0 XN) = перестановка на заданную глубину ROT (X1 X2 X3 -- X2 X3 X1) = перестановка трех верхних элементов SWAP (X1 X2 -- X2 X1) = обмен местами TUCK (X1 X2 -- X2 X1 X2) = копирование под вершину DEPTH (-- N) = глубина ?DUP (X -- X X\=0) = ? истина
Кроме обычных арифметических операций имеются эффективно реализуемые:
1+ 1- 2+ 2- 2/ половина
Слово стека занимает 16 разрядов, но имеются операции над двойными словами - в 32 разряда:
2drop 2dup 2over 2rot 2swap
Есть вариант умножения чисел без потери точности - результат пишется в двойное слово:
UM* (a,b cc)
Система программирования использует при работе ряд стеков:
R - стек возвратов C - стек компиляции F - стек чисел S - стек словарей
Имеются операции, работающие с адресами и памятью:
@ (adr -- x) чтение - разыменование ! (x adr --) запись
ALLOCATE выделить память FREE освободить память RESIZE заменить размер IOR код завершения операции
ALLOT (N -- адр) резервирование памяти в словах HERE (-- А) адрес слова , компиляция со стека UNUSED (-- N) остаток памяти
Операции заполнения памяти предусматривают следующие вариции кодов росписи:
FILL char ERASE 0 BLANC пробел
Возможно пересылка блоков памяти:
CMOVE CMOVE> MOVE
Арифметические операции:
+ - * / (n1 n2 -- n3)
*/ (n1 n2 n3 -- n4) n1 * n2/n3 "рабочая лошадка", эффективно выполняющая часто встречающееся сочетание операций.
* OVER - SWAP DUP *
Пример 4.1. Программа для подсчета по формуле (x,y,z -> x**2 + y * z - x)
Новые слова можно вводить в форме:
: имя опр;:SQ DUP (-- A,B,B) * SWAP (B**2, A) DOP * (B**2, A**2) +; (B**2 + A**2)
Пример 4.2. Введение нового слова SQ для подсчета суммы квадратов (A,B -> A**2 + B**2)
Интерпретирующий автомат для языка Форт по сложности сравним с автоматом для ассемблера. Основная разница - словарь Форта хранит определения произвольной длины, а таблица меток ассемблера хранит адреса фиксированного размера.
Средства размещения слов в словарь:
IMMIDIATE (--) немедленное исполнение в любом состоянии FORGET убрать из словаря
Обозначение систем счисления:
DECIMAL HEX LITERAL
Моделирование переменных и констант:
VARIABLE имя - переменная в словарь знач имя ! - присвоить = запись в переменную имя @ (a -- x) - чтение в стек из переменной знач CONSTANT имя (X --) - создание константы по стеку имя (-- x) - конст стек знач VALUE имя (X --) - переименование
Богато представлена логика и операции сравнения:
and or xor not < = > <> :<= :>= :<> 0< 0= 0>
Средства управление процессами - ветвления: условное выражение и переключатель.
усл IF часть-то THEN усл IF часть-то ELSE часть-иначе THEN
зн-0 CASE зн-1 OF ветвь1 ENDOF --- ---
[ветвь-иначе] ENDCASE
Моделирование циклов:
кон-зн нач-зн DO тело-цикла LOOP кон-зн нач-зн DO тело-цикла шаг +LOOP ?DO - м.б. ни разу
I - текущее значение счетчика J - внешний цикл
UNLOOP сброс счетчика LEAVE выход из цикла
... BEGIN ... AGAIN бесконечный цикл ... усл WHILE ... REPEAT ... !ложь______! ... BEGIN ... усл UNTIL !ист________!
Манипулирование стеками:
n STACK s новый стек S размера N ws PUSH запись в стек S POP перенос в стек данных s POP s ST@ скопировать s ST! сброс стека
Современные реализации Форта предоставляют средства работы с очередями, управления компиляцией, локализации переменных, обработки структур данных.
Системы программирования на Форте содержат препроцессор CREATE, средства работы со статическими динамическими метками, объявления отношений, связывания имен, подключения альтернативных словарей и использования системных вызовов. Возможно работа на уровне обычного ассемблера:
CODE ... ENDCODE
KEY ввод с клавиатуры KEY? есть ли символ? ACCERT [=EXPECT ] ввод строки PARSE адр слова в буфере и его длина
При работе со словарем, рассматриваемым как список слов, можно применять отношение порядка:
ORDER - в порядке поиска
Имеются средства работы с событиями, исключениями (0 - успех) и прерываниями:
CATCH - THROW
Можно создавать метаопределения:
CREATE DOED>
Таким образом обеспечены базовые функциональные возможности, характерные для систем программирования для языков высокого уровня.
В отличие от языка Forth предложенный Дж.Шварцем машинно независимый машинно ориентированный язык программирования Little включает средства обработки битовых строк в привычную языковую схему языка Фортран. Сохраняется обычная система подготовки и понимания программ, меняются лишь операции по обработке данных и набор встроенных типов данных. Данные - это битовые строки произвольной длины, а операции обеспечивают манипулирование битовыми строками как на уровне отдельных подстрок и битов, так и на уровне крупных, совместно используемых блоков и структур данных. [[32]]
X = 23 .f. 1,10, a (2) = 44 /* в поле с 1 по 10-й разряд записать 44 */ .ch. 5, text = 'A' /* в 5-й символ текста записать 'A' */
Листинг 4.1. Примеры операторов присваивания в языке Little
subr START; /* Разбор оператора присваивания языка Little */ size SYM (48); call SCAN (SYM); /* Чтение первого символа */ call ASSIGN; return; end;
subr ASSIGN; /* Разбор оператора присваивания, первый символ которого уже прочитан процедурой SYM */
if TC(SYM).EQ. TC ( '.' ) go to ASS; if TC(SYM).EQ. TC (NAME) go to NAME1; ERROR;
/ASS/ call SCAN (SYM); /* левая часть*/ IF TC (SYM).ne. TC (PREF) ERROR; call SCAN (SYM);
IF TC (SYM).ne.
TC ( '.' ) ERROR; call SCAN (SYM); call SCAN (PREF); /* f, s, e, ch */ /* выделение поля из левой части */ call SCAN (SYM);
IF TC (SYM).ne. TC (NAME) ERROR; go to NAME1;
/NAME1/ call SCAN (SYM); if TC(SYM).EQ. TC ( '=' ) go to NAME2; if TC(SYM).EQ. TC ( '(' ) go to RI-PAR; ERROR;
/RI-PAR/ call SCAN (SYM); IF TC (SYM).ne. TC (EXPR) ERROR; call SCAN (SYM); IF TC (SYM).ne. TC ( ')' ) ERROR; Call SCAN (SYM); IF TC (SYM).ne. TC ( '=' ) ERROR; go to NAME2;
/NAME2/ call SCAN (SYM); IF TC (SYM).ne. TC (NAME) ERROR; return;
/NO/ end;
Листинг 4.2. Программа синтаксического анализа операторов присваивания написанная на языке Little.
Средства машинно ориентированного программирования обычно доступны в системах программирования для языков высокого уровня в виде операций или команд, образующих линейные участки. Язык Little не получил особого распространения, хотя был успешно применен для эффективной реализации языка сверх высокого уровня Setl. Принятые в нем решения представляют интерес как эксперимент по разработке многоуровневых систем программирования.
Круглыми скобками выделена схема изменения стека