Язык Форт и его реализации

         

Динамическая идентификация


Внутренним представлением форт-слова является словарная статья, которая размещается в словаре. Через поля связи словарные статьи объединяются в цепные списки, каждый из которых также представлен как отдельный объект словарной статьей, создаваемой по слову VOCABULARY. Исполнение слова, обозначающего такой список, делает его текущим значением переменной CONTEXT, определяющей список, в котором начинается поиск каждого вводимого форт-слова. Если слово отсутствует в этом списке, то следующим просматривается список, в который добавляются создаваемые новые статьи, текущее значение переменной CURRENT. Последним просматривается стандартный список FORTH. Если текстовый интерпретатор находится в состоянии исполнения, то найденный код исполняется, в состоянии компиляции он компилируется в виде ссылки на данную словарную статью для последующего исполнения в составе скомпилированного определения.

Таким образом, в процессе создания нового определения мы индентифицируем составляющие его слова статически, т.е. по текущему контексту (значениям переменных CONTEXT и CURRENT) во время компиляции. Во время исполнения этого определения будут исполняться именно эти, найденные во время компиляции составляющие слова. Вместе с тем в практике программирования уже давно применяется прием, известный как динамическая идентификация. В применении к языку Форт он состоит в том, что вместо компиляции ссылки на статью слова по результату статической индентификации в компилируемой статье запоминается имя слова, и его поиск ведется в момент фактического исполнения. Главная особенность состоит в том, что контекст для поиска также устанавливается динамически в результате предшествующих вычислений.

Описанный подход позволяет в ряде случаев существенно упростить программирование слов, исполнение которых зависит от контекста их вызова. Вместо того, чтобы внутри такого слова анализировать контекст и в зависимости от него выполнять те или иные действия, можно создать ряд слов с одним и тем же именем, но в разных контекстах.
Каждое такое слово выполняет только свое действие, а вместо анализа контекста в месте вызова такого слова программируются установка нужного контекста и поиск слова по заданному имени с последующим исполнением кода.

В качестве примера рассмотрим простой язык, аналогичный рассмотренному в , и содержащий описания констант, переменных и оператор присваивания с инфиксной формой записи для выражений. Пусть описания констант выглядят, например, так: КОНСТ А = 2. Чтобы не перегружать наш пример лишними деталями, не будем учитывать возможность компиляции описаний и операторов этого языка. Пусть они сразу же исполняются по мере поступления. Собственно задача состоит в том, чтобы при обработке левой части оператора присваивания можно было различать переменные и константы. В первом случае результатом исполнения должен быть адрес соответствующей ячейки памяти, а во втором — сообщение об ошибке — недопустимом использовании константы в качестве получателя присваивания. Если же имя переменной или константы использовано в правой части присваивания, то в обоих случаях нужно получить соответствующее значение. Разумеется, соответствующие операции проверки можно включить в программу для переменной и константы, анализируя глобальную переменную ?ЗНАЧ, как это сделано в рассмотренном выше примере. Покажем, как эту же задачу можно решить через динамическую идентификацию.

Определим два контекста (списка слов) П и К, содержащих операции над переменными и константами. Таких операций две: ЗНАЧ для получения значения и АДР для получения адреса. Операция ЗНАЧ работает в обоих случаях одинаково, а операция АДР допустима только для переменной; будучи примененной к константе, она должна выдать сообщение об ошибке. В список FORTH также включим слово ЗНАЧ, которое в этом контексте выполняет пустую операцию, и слово АДР, выдающее сообщение об ошибке (их назначение станет ясным чуть позже). Для определения всех этих слов примем, что значение переменной или константы размещается в поле параметров созданной для нее словарной статьи.



FORTH DEFINITIONS VOCABULARY П VOCABULARY К П DEFINITIONS ( ДЕЙСТВИЯ С ПЕРЕМЕННЫМИ) : ЗНАЧ ( PFA->N:ЗНАЧ) @ FORTH ; : АДР ( PFA->A:АДР) FORTH ; К DEFINITIONS ( ДЕЙСТВИЯ С КОНСТАНТАМИ) : ЗНАЧ ( PFA->N:ЗНАЧ) @ FORTH ; : АДР ( PFA->) CR ." НЕДОПУСТИМОЕ ИСПОЛЬЗОВАНИЕ КОНСТАНТЫ " BODY> >NAME ID. ABORT ; FORTH DEFINITIONS : ЗНАЧ ( ->) ; : АДР ( ->) -1 ABORT" НЕДОПУСТИМЫЙ АДРЕС" ;

Заметим, что при исполнении слов ЗНАЧ и АДР в контекстах П и К помимо вычисления соответствующего значения текущий контекст переключается на FORTH. Исполняющая часть определяющих слов ПЕРЕМ и КОНСТ теперь состоит в установке соответствующего контекста, при этом на стек кладется адрес поля параметров данной статьи:

: ПЕРЕМ ( ->) CREATE 0 , DOES> ( PFA->PFA) П ; : КОНСТ ( ->) CREATE BL WORD DROP ( ВЫБРАТЬ ЗНАК =) BL WORD NUMBER DROP ( N:ЗНАЧ) , DOES> ( PFA->PFA) К ;

Чтобы теперь при обработке выражения в левой части присваивания получать значение переменной или константы, надо слегка изменить определение слова >ОПРЦ> (см. ) — включить в него исполнение операции ЗНАЧ, которая идентифицируется динамически по текущему контексту.

: >ОПРЦ> ( N:ПРИОРИТЕТ->) >R " ЗНАЧ" FIND DROP EXECUTE BEGIN ОПРЦ@ R@ < NOT WHILE ОПРЦ> DROP ОПРЦ> ( CFA) EXECUTE REPEAT R> DROP ;

Если это определение исполняется сразу после переменной или константы (в контексте П или К), то оно преобразует адрес поля параметров соответствующей статьи, который находится в этот момент на вершине стека, в значение данной переменной или константы. Для всех других случаев (контекст FORTH) преобразования стека не происходит. Именно по этой причине определения ЗНАЧ в списках П и К по завершении их исполнения переключают текущий контекст на FORTH, а в списке FORTH присутствует определение ЗНАЧ с пустым действием. Последнее обстоятельство гарантирует успешный поиск слова ЗНАЧ в любом контексте, поэтому в приведенном определении проверка того, что поиск закончился успешно, опущена.


В заключение осталось только аналогичным образом переопределить слово := (см. ):

: := " АДР" FIND DROP EXECUTE [COMPILE] ( ;

Сравнивая получившиеся определения, мы видим, что они существенно сократились в объеме: из них исчезли анализ текущего контекста и (соответственно) условные операторы, предписывающие выполнение разных действий в зависимости от результата анализа. Описанный подход особенно перспективен, когда требуется выполнить много разных операций над объектами, каждая из которых выполняется по-разному в зависимости от текущего контекста. Разбиение большой операции с множеством проверок и разветвлений на ряд мелких, каждая из которых выполняется в своем контексте, не только упрощает программирование и отладку, но и сокращает объем программы, так как эти мелкие специализированные операции для разных объектов часто можно совмещать:

К DEFINITIONS : ЗНАЧ ( PFA->N:ЗНАЧ) [ П ] ЗНАЧ ;

В приведенном определении слово ЗНАЧ для списка К не программируется вновь, а определяется через такое же слово из контекста П, тем самым сокращается общий объем отладки. Следуя по этому пути, можно вообще исключить слово АДР из списка К, поскольку в этом случае сообщение об ошибке выдаст слово АДР из списка FORTH, правда, с меньшей информацией — не будет напечатано имя константы. Если механизм динамической идентификации применяется для ряда не связанных между собой целей, то может оказаться неудобным выполнять переключение контекстов через одну и ту же глобальную переменную CONTEXT и выполнять поиск слова в текущем контексте словом FIND. Как правило, все форт-системы имеют более элементарное слово (FIND), которое получает в качестве параметра текстовую строку с именем слова и вход в список статей, в котором нужно выполнять поиск. Используя это слово, можно организовать поиск слова в любом списке или группе списков, уже не связывая его с текущим порядком поиска, принятым для слова FIND.

Например, именно таким образом нетрудно ввести в язык обработку исключительных ситуаций.Для этого нужно завести стек, элементами которого являются списки слов для обработки ситуаций. При установке перехвата ситуаций на вершину этого стека добавляется новый элемент — список слов-обработчиков. При снятии перехвата верхний элемент снимается со стека, делая доступными другие слова-обработчики с теми же именами. Слово, возбуждающее исключительную ситуацию, обращается к (FIND) для просмотра всех списков в этом стеке от верхнего элемента к самому нижнему, сообщая ему имя слова, обозначающего ситуацию. Первое найденное при таком поиске слово и будет использовано как обработчик ситуации. Если слово отсутствует во всех списках, то это ошибка — непредусмотренная ситуация.


Примеры программных разработок




В этой главе приведены примеры реализации отдельных программных средств— небольших удобных расширений (инфиксная форма записи, локальные переменные, конструкция «переключатель», векторное поле кода) — и целых инструментальных систем на базе языка Форт. Основное внимание уделено методологическим аспектам использования Форта как расширяемого языка. Все приведенные примеры отражают опыт использования Форта в конкретных разработках.



Инфиксная запись формул


Для многих программистов трудным барьером на пути к овладению языком Форт оказывается используемая в нем обратная польская форма для записи выражений. Опишем простую надстройку над языком Форт, которая позволяет записывать формулы в обычной инфиксной форме с использованием скобок. Будем по-прежнему считать все элементы такого выражения (скобки, знаки операций и элементарные термы) отдельными форт-словами и разделять их при записи пробелами. Задача состоит в том, чтобы вычисления на стеке автоматически перегруппировывались исходя из инфиксной формы записи. Например, чтобы вместо текста 2 5 + 7 3 - * можно было писать ( ( 2 + 5 ) * ( 7 + 3) ). Внешние скобки нужны для того, чтобы отмечать конец выражения. При желании это можно задавать и каким-нибудь другим способом.

Для операций в инфиксной записи вводится понятие приоритета (старшинства), которое определяет порядок вычислений при отсутствии скобок. Приоритет обозначается целым числом, и операции с меньшим приоритетом выполняются после операций с большим приоритетом. Например, в выражении А+В/С подразумевается следующая расстановка скобок: (А+(В/С)), т.е. сначала выполняется деление и только потом сложение, потому что приоритет деления выше приоритета сложения. В случае равных приоритетов, например в выражении А+В+С, будем выполнять операции слева направо: ((А+В)+С). Традиционно используемые приоритеты двухместных операций показаны в табл. 3.1. Все одноместные операции (ABS, NEGATE, NOT и др.) имеют максимальный приоритет (обычно 9).

Таблица 3.1. Приоритеты двухместных операций

Приоритет 2 3 4 5 6 7 8
Операция OR
XOR
AND = <
>
+
-
*
/
MOD
**

Опишем вспомогательную структуру данных — стек ОПРЦ, элементами которого являются пары значений: приоритет операции и адрес кода, который ее вычисляет. Размер стека определяется максимальной глубиной вложенности формул, которую мы допускаем: CREATE ОПРЦ HERE 2 + , 40 ALLOT. Первым элементом в поле параметров слова ОПРЦ является указатель вершины этого стека (адрес первого свободного байта), далее зарезервирована память на 5 элементов по 4 байта (два значения) каждый.
По аналогии со словами >R, R@ и R&gt; определим слова для работы со стеком ОПРЦ:

: >ОПРЦ ( A --->) ОПРЦ @ ! 2 ОПРЦ +! ; : ОПРЦ@ ( ---> A) ОПРЦ @ 2- @ ; : ОРПЦ> ( ---> A) ОПРЦ@ -2 ОПРЦ +! ;

Обработка операций в инфиксной форме состоит в том, что операция не выполняется, а помещается вместе со своим приоритетом на стек операций, выталкивая из него на исполнение все операции с меньшим или равным приоритетом. В состоянии исполнения вытолкнутая операция исполняется, а в состоянии компиляции — компилируется. Предполагая, что в стеке ОПРЦ сначала размещается адрес поля кода для исполнения операции, а за ним приоритет, определим слово для такого выталкивания операций:

: >ОПРЦ> ( N:ПРИОРИТЕТ---> ) >R BEGIN ОПРЦ@ R@ < NOT WHILE ОПРЦ> DROP ОПРЦ> ( CFA) STATE @ IF , ELSE EXECUTE THEN REPEAT R> DROP ;

Теперь введем определяющие слова для двухместных и одноместных операций и переопределим через них стандартные арифметические форт-слова:

: 2-ОП ( N:ПРИОРИТЕТ-&gt) &gtIN @ &gtR ' ( N,CFA) R&gt &gtIN ! CREATE IMMEDIATE ( N,CFA) , , DOES&gt ( PFA-&gt) 2@ ( N,CFA) &gtR &gtR R@ &gtОПРЦ&gt R&gt R&gt &gtОПРЦ &gtОПРЦ ; : 1-ОП ( -&gt) 9 2-ОП ; 2 2-ОП OR 2 2-ОП XOR 3 2-ОП AND 4 2-ОП = 5 2-ОП < 5 2-ОП > 6 2-ОП + 6 2-ОП - 7 2-ОП * 7 2-ОП / 7 2-ОП MOD 1-ОП NOT 1-ОП ABS 1-ОП NEGATE

В определении слова 2-ОП используется то обстоятельство, что код для исполнения данной операции обозначается словом, которое этим определяющим словом переопределяется. Поэтому перед тем как его имя будет выбрано из входного потока словом CREATE, текущая позиция во входном потоке запоминается на стеке возвратов и после исполнения слова ' (штрих) вновь устанавливается в то же положение для исполнения слова CREATE.

Переопределенные слова получают признак немедленного исполнения, чтобы описанные действия по переупорядочиванию вычислений выполнялись и во время компиляции, компилируя нужную последовательность операций.


В заключение осталось переопределить круглые скобки, явно задающие порядок вычислений:

: ( 0 &gtОПРЦ ; IMMEDIATE : ) 1 &gtОПРЦ&gt ОПРЦ&gt DROP ; IMMEDIATE

Открывающая скобка кладет на стек значение 0 — ограничитель для выталкивания операций. Закрывающая скобка выталкивает все операции до ближайшего ограничителя и удаляет его из стека. Переопределение открывающей скобки делает невозможным ее использование в прежнем смысле — как знака комментария. Поэтому программисту, вводящему такую надстройку, следует подумать и о решении этого вопроса.

Развивая описанную надстройку дальше, определим простой входной язык с описаниями переменных и присваиваниями, которые записываются обычным образом. В качестве знака присваивания пусть используется слово :=, а в качестве разделителя операторов — слово ;. Если переменная использована как получатель присваивания (слева от знака :=), то ее исполнение оставляет на стеке адрес значения; а если данная переменная входит в правую часть присваивания, то ее исполнение кладет на стек само значение данной переменной. Для управления поведением переменных нашего языка введем рабочую переменную ?ЗНАЧ, которая имеет значение 0 при обработке левой части присваивания и значение -1 при обработке правой, и определим слово ПЕРЕМ для описания переменных нашего языка:

VARIABLE ?3НАЧ : ПЕРЕМ CREATE 0 , DOES> [COMPILE] LITERAL ?ЗНАЧ @ IF STATE @ IF COMPILE @ ELSE @ THEN THEN ; IMMEDIATE

Для записи присваиваний определим слова := и ; через уже определенные скобки:

: := [COMPILE] ( -1 ?3НАЧ ! ; IMMEDIATE : ; [COMPILE] ) STATE @ IF COMPILE SWAP COMPILE ! ELSE SWAP ! THEN 0 ?ЗНАЧ ! ; IMMEDIATE

Слово := кладет на стек ОПРЦ ограничитель для выталкивания операций и устанавливает переменную ?ЗНАЧ на обработку правой части присваивания. Слово ; выталкивает со стека ОПРЦ все накопившиеся там операции, в результате на вершине стека данных оказывается значение правой части. Непосредственно под ним располагается адрес переменной, оставленный левой частью данного присваивания.


Слова SWAP и ! выполняют присваивание, причем в состоянии компиляции они компилируются, а состоянии исполнения исполняются. В заключение переменная ?ЗНАЧ переустанавливается на режим обработки левой части следующего присваивания.

Благодаря словам := и ; отпадает необходимость в дополнительных внешних скобках для всего выражения. Входной текст в описанном расширении выглядит вполне традиционно:

ПЕРЕМ A ПЕРЕМ B А := 10 ; B := 15 ; A := ( A + B ) * ( A - B ) + 2 ;

и вместе с тем это текст на языке Форт! Такие операторы присваивания можно исполнять непосредственно или включать в тело определений через двоеточие. Однако переопределение точки с запятой усложняет написание новых определений в таком расширении. Чтобы решить эту проблему, можно предусмотреть специальные средства для определения новых слов, как и слова для включения комментариев. Описанный пример еще раз показывает, что средства, которые язык Форт предоставляет программисту, позволяют ему реализовать практически любые необходимые инструментальные надстройки, исходя из конкретных требований и особенностей данной задачи и пожеланий пользователей, с ней работающих.


Элементарная машинная графика


Машинная графика — чрезвычайно перспективная область для применения языка Форт. Поскольку для задач машинной графики постоянно ведется поиск все новых и новых способов их постановки и решения, то здесь как нигде требуется очень гибкий инструментальный язык, позволяющий быстро реализовывать и проверять на практике разные варианты решений. Принцип обратной оперативной связи между постановкой задачи и ее решением позволяет программисту добиваться существенно более высоких профессиональных результатов, чем при традиционных подходах.

Вместе с тем развитие машинной графики в значительной степени определяется наличием тех или иных аппаратных средств. Чрезвычайное разнообразие их конкретных характеристик делает невозможным создание какого-либо универсального языка, применимого для всех возможных случаев. Язык Форт позволяет программисту самому быстро создавать необходимые инструментальные средства, применяя универсальные приемы с учетом особенностей конкретной задачи.

Рассмотрим две очень разные по сложности задачи построения графиков функций. В первой используется алфавитно-цифровой терминал, во второй — графопостроитель с широким набором возможностей.

В случае использования алфавитно-цифрового терминала задача состоит в том, чтобы отобразить в нем график функции, заданной вектором своих значений. Введем понятие вектора, элементы которого нумеруются от нуля, по аналогии со словом QUAN (см. ). Для получения адреса элемента вектора и для присваивания ему нового значения можно использовать те же слова AT и TO.

: ?+ ( N->N) DUP 0< ABORT" ОТРИЦАТЕЛЬНОЕ ЗНАЧЕНИЕ" ; : В-АДР ( I:ИНДЕКС,PFA->A[I]:АДРЕС) 2DUP @ U< IF SWAP 1+ 2* + EXIT THEN SWAP CR . ." - НЕДОПУСТИМЫЙ ИНДЕКС ДЛЯ ВЕКТОРА " BODY> BODY> BODY> >NAME ID. ABORT ; : В-АДР0 DOES> ( I:ИНДЕКС,PFA->A[I]:АДРЕС) В-АДР ; : В-ПРИСВ DOES> ( N:3НАЧ,I:ИНДЕКС,2CFA->) 2+ В-АДР ! ; : VQUAN ( N:ВЕРХН.ИНДЕКС->) ?+ CREATE В-ПРИСВ HERE 2- @ , В-АДР0 HERE 4 - @ , 1+ DUP , 2* HERE SWAP DUP ALLOT ERASE DOES> ( I:ИНДЕКС,1CFA->N[I]:ЗНАЧ) 4 + В-АДР @ ; : В-РАЗМ ( ->N:ЧИСЛО ЭЛЕМЕНТОВ) ' 6 + @ [COMPILE] LITERAL ; IMMEDIATE


Невысокая разрешающая способность алфавитно-цифрового терминала с точки зрения машинной графики вполне допускает использование стандартных целых чисел для представления значений вещественных функций. Более того, такие дискретные вычислительные приемы оказываются значительно более удобными для задач машинной графики, чем работа с числами в формате плавающей точки. Например, тригонометрические функции можно вычислять следующим образом, задавая их аргумент в градусной мере:

( 18.06.86 SIN COS) ( ТАБЛИЦА СИНУСОВ ОТ 0 ДО 90 ГРАД.) HERE 0000 , 0175 , 0349 , 0523 , 0698 , 0872 , 1045 , 1219 , 1392 , 1564 , 1736 , 1908 , 2079 , 2250 , 2419 , 2588 , 2756 , 2924 , 3090 , 3256 , 3420 , 3584 , 3746 , 3907 , 4067 , 4226 , 4384 , 4540 , 4695 , 4848 , 5000 , 5150 , 5249 , 5446 , 5592 , 5736 , 5878 , 6018 , 6157 , 6293 , 6428 , 6561 , 6691 , 6820 , 6947 , 7071 , 7193 , 7314 , 7431 , 7547 , 7660 , 7771 , 7880 , 7986 , 8090 , 8192 , 8290 , 8387 , 8480 , 8572 , 8860 , 8746 , 8829 , 8910 , 8988 , 9063 , 9135 , 9205 , 9772 , 9336 , 9397 , 9455 , 9511 , 9563 , 9613 , 9659 , 9703 , 9744 , 9781 , 9816 , 9840 , 9877 , 9903 , 9925 , 9945 , 9962 , 9976 , 9986 , 9994 , 9998 , 10000 , : SIN180 ( N->SIN N, 0<=N<=180) DUP 90 > IF 180 SWAP - THEN 2* [ DUP ] LITERAL + @ ; DROP : SIN ( N->SIN N) 360 MOD DUP 0< IF 360 + THEN DUP 180 > IF 180 - SIN180 NEGATE ELSE SIN180 THEN ; : COS ( N->COS N) 90 SWAP - SIN ;

Аналогично и другие элементарные функции можно с успехом вычислять по классическим итерационным схемам:

( DSQRT SQRT ИЗВЛЕЧЕНИЕ КОРНЯ ПО СХЕМЕ НЬЮТОНА) : DSQRT ( D1->D2) 2DUP D0< ABORT" ОТРИЦАТЕЛЬНЫЙ АРГУМЕНТ" 2DUP 2. D< IF EXIT THEN ( ДАЛЕЕ ПО СХЕМЕ: X[0]=X/2; X[I+1]=X[I]/2+X/X[I]/2) 2DUP 2. D/ SWAP BEGIN 2OVER 2OVER D/ 2OVER D+ 2. D/ 2SWAP 2OVER D- DABS 2. D< UNTIL 2SWAP 2DROP ; : SQRT ( N1->N2) S>D DSQRT DROP ;

Некоторое неудобство при таком представлении функций доставляет необходимость помнить о масштабном множителе, но это с лихвой возмещается простотой приемов их вычисления.



Используя описанные вспомогательные средства, можно задавать векторы со значениями интересующих нас функций. Работая с простыми значениями, будем пользоваться их описанием через слово QUAN. Пусть наша функция в обычной записи имеет вид: y = sin 3x + sin 120x. Зададим ее определение через введенные инструментальные определения, выбрав 100 в качестве масштабного множителя:

70 VQUAN U : U! ( -> УСТАНОВКА ВЕКТОРА U) В-РАЗМ U 0 DO 3 I * SIN 120 I * SIN + 100 / I TO U LOOP ; U!

При желании можно проверить правильность установки значений, распечатав их как числа:

: B-? ( -> РАСПЕЧАТКА ВЕКТОРА) >IN @ >R [COMPILE] В-РАЗМ R> >IN ! ' SWAP 0 DO I OVER EXECUTE 8 .R LOOP DROP ; B-? U

В результате будет напечатан следующий текст:

0 93 -78 15 109 -62 30 124 -47 45 138 -34 58 151 -21 70 162 -10 80 172 0 89 179 4 95 185 9 98 188 11 100 188 10 98 186 7 95 181 2 89 177 -4 80 166 -14 70 155 -25 58 143 -38 45 129 -52 30 114 -67 15 99 -83 0 83 -99 -15 67 -114 -30 52 -129 -45 OK

Для построения графика определим список слов PLOT (график) и в нем ряд вспомогательных переменных:

VOCABULARY PLOT PLOT DEFINITIONS QUAN RHO 4 TO RHO ( ШИРИНА СТОЛБЦА ДЛЯ Y-КООРД.) QUAN MU0 16 TO MU0 ( МАКСИМАЛЬНАЯ ВЫСОТА ОКНА) QUAN NU0 64 RHO - TO NU0 ( МАКСИМАЛЬНАЯ ШИРИНА ОКНА) QUAN WSTA ( АДРЕС НАЧАЛА ОКНА) QUAN MU ( ТЕКУЩАЯ ВЫСОТА ОКНА) QUAN NU ( ТЕКУЩАЯ ШИРИНА ОКНА) QUAN WX ( X-КООРДИНАТА ОКНА) QUAN WY ( Y-КООРДИНАТА ОКНА) QUAN KAPPA ( ЧИСЛО ЗНАЧЕНИЙ СИГНАЛА) QUAN LAMBDA ( ДИАПАЗОН ЗНАЧЕНИЙ СИГНАЛА) QUAN MA ( МАКСИМАЛЬНОЕ ЗНАЧЕНИЕ СИГН.) QUAN MIN0 ( МИНИМАЛЬНОЕ ЗНАЧЕНИЕ В ОКНЕ) QUAN VAL ( ВЕКТОР СИГНАЛА)

Пусть прямоугольный экран терминала, на котором строится график, содержит MU0 строк по NU0 литер каждая. Первые RH0 позиций в каждой строке занимает надпечатка ее координаты по оси ординат. Общий размер графика определяется значениями LAMBDA (диапазон значений функций) и KAPPA (число значений). Этот прямоугольник может как умещаться целиком на экране терминала, так и заметно превышать его.


Определим понятие «окна», которое перемещается по графику и показывает на экране терминала соответствующую его часть. Переменные WX и WY задают координаты левого верхнего угла окна относительно левого верхнего угла графика. Определим слова, выполняющие инициализацию и перемещение окна по полю графика:

FORTH DEFINITIONS PLOT : НАЧАТЬ ( -> ИНИЦИАЛИЗАЦИЯ) PLOT HERE NU0 MU0 * ALLOT TO WSTA ; PLOT DEFINITIONS : ВВЕРХ ( N-> СДВИНУТЬ ОКНО НА N ПОЗИЦИЙ ВВЕРХ) ?+ WY SWAP - 0 MAX TO WY ; : ВЛЕВО ( N-> СДВИНУТЬ ОКНО НА N ПОЗИЦИЙ ВЛЕВО) ?+ WX SWAP - 0 MAX TO WX ; : ВНИЗ ( N-> СДВИНУТЬ ОКНО НА N ПОЗИЦИЙ ВНИЗ) ?+ WY + MU + LAMBDA MIN MU - TO WY ; : ВПРАВО ( N-> СДВИНУТЬ ОКНО НА N ПОЗИЦИИ ВПРАВО) ?+ WX + NU + KAPPA MIN NU - TO WX ;

Определим также слова для вычисления параметров графика и распечатки окна по его текущим координатам. Вспомогательное слово MU по значению функции определяет, где находится соответствующая точка графика: ниже окна, внутри или выше него. Если точка попадает в окно, будем отмечать ее положение знаком * (звездочка), а если точка находится выше или ниже окна, то соответственно в самой верхней или самой нижней строке окна поставим знак + (плюс), чтобы видеть направление, в котором следует искать данную точку на графике. Ось абсцисс, если она попадает в окно, будем отмечать строкой из знаков «минус». Надпечатку координат по оси абсцисс будем выполнять дважды — в верхней и нижней строках терминала.

: -MU ( N:ЗНАЧЕНИЕ->-1:НИЖЕ ОКНА/ M:СМЕЩЕНИЕ ОТ ВЕРХА ОКНА ВНИЗ,0/ +1:ВЫШЕ ОКНА ) MIN0 - DUP 0< IF DROP -1 EXIT THEN MU 1- SWAP - DUP 0< IF DROP 1 EXIT THEN 0 ; : ФУНКЦИЯ ( -> СЛЕДУЮЩЕЕ СЛОВО: ИМЯ ВЕКТОРА ЗНАЧЕНИЙ) >IN @ >R [COMPILE] В-РАЗМ R> >IN ! DUP TO KAPPA NU0 MIN TO NU ' TO VAL ( ВЫЧИСЛЕНИЕ MA И LAMBDA ) 0 VAL DUP ( X[0],X[0] ) KAPPA 0 DO I VAL ( MIN[I-1],MAX[I-1],X[I] ) DUP >R MAX SWAP R> MIN ( MAX[I],MIN[I]) SWAP LOOP DUP TO MA SWAP - 1+ DUP TO LAMBDA MU0 MIN TO MU 0 TO WX 0 TO WY ; : ОСЬ-X ( НАДПЕЧАТКА ОСИ X) CR RH0 SPACES WX NU 1- RH0 - WX BEGIN DUP 10 MOD DUP IF 10 SWAP - THEN DUP >R + 2DUP >= WHILE R> SPACES C" ! EMIT DUP 0 <# #S #> DUP >R TYPE 1+ R> + REPEAT RDROP 2DROP ; : ГРАФИК ( -> РИСОВАТЬ ГРАФИК ПО СИГНАЛУ И КООРДИНАТАМ ОКНА) MA WY - MU 1- - TO MIN0 ( УСТАНОВИТЬ MIN0) WSTA MU NU * BLANK ( ЗАЧИСТИТЬ ОКНО) 0 -MU 0= IF NU * WSTA + NU C" - FILL THEN ( ПРОВЕСТИ ОСЬ X) ( ЦИКЛ ПО ОСИ X ДЛЯ ЗАПОЛНЕНИЯ ОКНА) WX NU + WX DO I VAL ( X[I]) -MU DUP IF ( ВНЕ ОКНА) 1+ IF ( ВЫШЕ ОКНА) C" + 0 ELSE ( НИЖЕ ОКНА) C" + MU 1- THEN ELSE DROP C" * SWAP THEN NU * I WX - + WSTA + C! LOOP ОСЬ-X MU 0 DO CR MA WY - I - RH0 .R I NU * WSTA + NU TYPE LOOP ОСЬ-X ;



Следующий протокол показывает работу введенных определений по вычерчиванию графика интересующей нас функции:

> НАЧАТЬ ФУНКЦИЯ U ГРАФИК !0 !10 !20 !30 !40 !50 188 * * 187 186 * 185 * 184 183 182 181 * 180 179 * 178 177 * 176 175 174 173++++++++++++++++++++++ ++ ++ ++ ++ ++ ++ +++++++++++++++++++ !0 !10 !20 !30 !40 !50 OK > 175 ВНИЗ 10 ВПРАВО ГРАФИК !10 !20 !30 !40 !50 13+ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + 12 11 * 10 * 9 * 8 7 * 6 5 4 * 3 2 * 1 0----------*---------------------------------------*--------- -1 -2 + + + + + + + + + + ++ ++ ++ !10 !20 !30 !40 !50 OK

Вычерчивание графиков на графопостроителе чрезвычайно осложняется обилием деталей чертежа, которые надо учитывать. При этом собственно вычисление значений функции составляет незначительную часть в общем объеме работы. Многие элементы чертежа имеют, как правило, какое-нибудь стандартное значение, принимаемое по умолчанию, и программист может их только переопределять (например, заголовок, штамп, оформление полей, выбор шрифтов и цвета надписей и т.д.). С каждым из этих крупных элементов можно связать список слов, обрабатывающих те или иные его компоненты, и для установки нужного значения исполнять соответствующее слово в контексте данного элемента. Сходные действия (например, задание шрифта или цвета) в разных контекстах могут обозначаться одними и теми же словами, что создает большие удобства для программиста. Вместе с тем и синтаксис для задания действий графопостроителя можно сделать максимально простым и удобным для пользователей-непрограммистов. Например, определение элементов чертежа можно задавать так:

ГРАФИК XY X ОТ 0 ДО 4 ШАГ 1 Y ЛОГАРИФМ ОТ 1 ДО 100 Х ШТРИХ РАЗМЕР 0.25 РАМКА ЗАГОЛОВОК" ЧЕРТЕЖ 1" РИСУЙ

В результате будет нарисована прямоугольная рамка с заголовком «ЧЕРТЕЖ 1» надпечаткой оси абсцисс в виде штрихов, пересекающих рамку, и логарифмической разметкой оси ординат. Огромное число других значений, необходимых для построения чертежа, принимается по умолчанию.Далее можно начертить собственно график функции, используя, например, ее задание через вектор значений:

ФУНКЦИЯ U МАСШТАБ 100 ЦВЕТ КРАСНЫЙ РИСУЙ

Аналогично наносим на чертеж дополнительные элементы:

ПОДЗАГОЛОВОК ВПРАВО ТЕКСТ" Y=SIN 3X + SIN 120X" РИСУЙ

и т.д. Указанные действия можно не только непосредственно исполнять, но и компилировать, задавая впоследствии их исполнение через одно слово — имя скомпилированной программы.


Локальные переменные


Стек данных как универсальное средство для передачи параметров и результатов между форт-словами имеет неоспоримые преимущества. Вместе с тем внутри определения он используется для промежуточных вычислений и размещения значений, которые в них участвуют. Это вызывает определенные трудности для доступа к такому локальному значению, поскольку его положение относительно вершины стека постоянно меняется.

Для упрощения работы желательно закрепить за локальным объектами внутри определения некоторые постоянные имена, через которые и осуществлять доступ к ним.

Имеющийся в языке механизм описания переменных в данном случае не подходит, поскольку создает глобальные имена, тогда как требуется именовать локальные объекты, учитывая при этом возможность рекурсивных вызовов. Поставленную задачу решает включение в работу дополнительного стека, отличного от стека данных. Локальные значения размещаются в этом стеке при входе в определение и убираются из него при выходе.

На все время исполнения определения их положение относительно вершины стека остается постоянным, это позволяет организовать очень простой доступ к таким значениям.

Простейшая надстройка над языком Форт, которая позволяет работать с локальными переменными, выглядит так:

100 ALLOT HERE CONSTANT LP0 ( НАЧАЛО ЛОК. СТЕКА) VARIABLE LP ( ТЕКУЩАЯ ВЕРШИНА ЛОКАЛЬНОГО СТЕКА) : INIT ( ->) LP0 LP ! ; INIT : LOC ( N:СЧЕТЧИК->) 1+ 2* LP @ OVER - DUP LP ! ! ; : UNLOC ( ->) LP @ @ LP +! ; : @@ ( N:СМЕЩ->) CREATE , DOES> ( PFA->A) @ LP @ + @ ; : !! ( N:СМЕЩ->) CREATE , DOES> ( A,PFA->) @ LP @ + ! ; 2 @@ @1 4 @@ @2 6 @@ @3 8 @@ @4 10 @@ @5 ( И Т.Д.) 2 !! !1 4 !! !2 6 !! !3 8 !! !4 10 !! !5 ( И Т.Д.)

Вначале отводится область объемом 100 байт и адрес ее конца запоминается как константа LP0. Эта область будет использоваться как локальный стек, растущий в сторону убывания адресов. Переменная LP хранит указатель на текущую вершину локального стека, ее инициализацию выполняет слово INIT, которое присваивает ей значение LP0.
Слово LOC резервирует в этом стеке память на N двухбайтных значений, дополнительно отводя еще одну ячейку, в которую засылает значение N — длину всей области. Обратное действие — освобождение памяти — выполняет слово UNLOC, которое использует сохраненное значение N. Слова @@ и !! являются определяющими для двух рядов слов, выполняющих разыменование локальных объектов и присваивание им нового значения. Каждый локальный объект в пределах одного определения идентифицируется своим номером, который включен в имя операций разыменования и присваивания для работы с ним. В качестве примера рассмотрим определение слова 5INV, переставляющего 5 верхних элементов стека данных в обратном порядке:

: 5INV ( A,B,C,D,E -> E,D,C,B,A) 5 LOC !1 !2 !3 !4 !5 @1 @2 @3 @4 @5 UNLOC ;

Приведенная реализация локальных переменных предельно упрощена. Ее очевидным усовершенствованием является, например, введение контроля за переполнением и исчерпанием локального стека и включение действий LOC и UNLOC в семантику входа в высокоуровневое определение и выхода из него; при этом подсчет числа локальных объектов может выполняться автоматически, аналогично определению адреса перехода в структурах управления.

Можно пойти еще дальше и вместо прямой нумерации локальных объектов ввести их настоящее описание через соответствующее слово внутри определения через двоеточие с автоматическим переносом входных параметров на локальный стек при входе в такое определение. Словарная статья для имени локального объекта, которая строится по такому описанию, должна располагаться несколько выше текущей вершины словаря и иметь признак немедленного исполнения. Ее действие состоит в компиляции обращения к соответствующему элементу локального стека. По завершении компиляции данного определения все такие временные статьи автоматически исключаются из словаря.

Введение локальных переменных не только упрощает программирование, освобождая программиста от необходимости тщательно отслеживать все изменения на стеке, но и сокращает размер скомпилированной программы, поскольку из нее исчезают сложные последовательности из слов OVER, DUP, ROT, PICK и других, обычно используемых для доступа к локальным значениям.Такое неожиданное сочетание приятного с полезным — одна из многих удивительных сторон языка Форт, которые открываются программисту при знакомстве с ним.


Многозадачный режим


Запуская на исполнение какое-либо форт-слово, программист имеет возможность исполнить следующее только по завершении исполнения предыдущего. Если исполнение данного слова требует значительного времени счета, то длительные перерывы в диалоге создают определенное неудобство. Ввиду этого представляется весьма привлекательной идея многозадачной форт-системы, в которой программист может создавать фоновые задачи и запускать их на исполнение, не прерывая диалога. Текстовый интерпретатор такой системы, обеспечивающий диалог, является одной из задач и разделяет центральный процессор наряду с другими задачами. Идея многозадачного режима привлекательна еще и тем, что разные задачи могут разделять значительную часть кода — практически все ядро форт-системы. Из стандартных слов только системные переменные (STATE, BASE и другие) и списки слов (FORTH, ASSEMBLER) не могут одновременно участвовать в нескольких вычислительных процессах (задачах), поскольку их код (значение поля параметров) в процессе работы может изменяться. В то же время вся остальная часть кода форт-системы, включая все слова, определенные через двоеточие и CONSTANT, в процессе работы не меняется и может даже размещаться в постоянной оперативной памяти (ПЗУ). Чтобы снять это ограничение, во многих реализациях выделены особая область для размещения значений, которые могут изменяться во время счета. Эта область называется пользовательской и обычно располагается в конце адресного форт-пространства рядом со стеками и буферным пулом. Распределение памяти внутри пользовательской области выполняет сам программист через определяющее слово USER (пользовательский):

: USER ( N:СМЕЩЕНИЕ-> ) CREATE , DOES> ( PFA->A:АДРЕС) @ U0 + ;

По характеру использования это слово аналогично CONSTANT; значение, которое оно снимает со стека и компилирует в поле параметров определяемого слова, представляет собой смещение от начала пользовательской области. При исполнении такого слова на стеке будет оставлен соответствующий адрес (слово U0, использованное в определении слова USER, дает адрес начала пользовательской области).
При наличии пользовательской области все системные переменные размещаются в ней, занимая какую-то ее начальную часть. Их словарные статьи определяются через слово USER, тем самым обеспечивается их неизменяемость в процессе работы.

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

Дальнейшие уточнения конкретного варианта многозадачного режима зависят от многих частных причин. Для примера рассмотрим реализацию системы ПОЛИФОРТ фирмы «Форт» []. Эта система реализована для целого ряда ЭВМ, включая персональный компьютер ИБМ. Использованный в ней механизм переключения задач основан на кольцевом принципе: все задачи связаны в кольцо через начальную часть своей пользовательской области (рис. 3.2). Задачи, составляющие кольцо, по очереди получают центральный процессор и удерживают его до тех пор, пока не исполнят слово PAUSE (пауза) или STOP (стоп). Для определений в машинном коде предусмотрен аналогичный код WAIT (ждать). Многие слова, которые выполняют асинхронные операции обмена (TYPE, EXPECT, BLOCK, BUFFER), содержат код WAIT или переход на STOP, чтобы в то время, когда они ждут завершения обмена, другие задачи могли использовать центральный процессор. Задачи, в которых для выполнения длительных вычислений нет необходимости обращаться к операциям обмена, должны сами периодически предоставлять процессор другим задачам, выполняя слово PAUSE.



Рис. 3.2. Кольцевой принцип реализации многозадачного режима



Все задачи, существующие в системе, связываются в кольцо через команду безусловного перехода (JMP), которая располагается в начале их пользовательских областей. В качестве звена связи используется адресная часть команды перехода, указывающая на такую же команду следующей задачи в кольце. Если передать управление на одну из таких команд, то будет исполняться бесконечный цикл из переходов по кольцу задач до тех пор, пока какая-нибудь задача не будет разбужена.

Для пробуждения задач часто используются асинхронные подпрограммы обработки прерываний. Например, если выход по прерыванию, установленный в слове EXPECT, распознает код возврата каретки, которым заканчивается вводимая строка, то он будит задачу, запросившую данный ввод с терминала, засылая команду WAKE (разбудить) на место ее команды JMP. Для этого часто используется какая-нибудь команда перехвата (например, для ЭВМ СМ-4 это команды TRAP и ЕМТ). Когда цикл передач управления по кольцу задач дойдет до этой команды, она передаст управление специальной подпрограмме-побудчику, которая и возобновит исполнение данной задачи от точки последнего остановка. При этом на место команды WAKE будет вновь заслана команда JMP, и исполнение данной задачи будет продолжаться до следующей операции PAUSE, STOP или WAIT. Пример цикла ввода и обработки данных с возобновлением по прерыванию дает следующее-определение:

: СБОР ( -> ) BEGIN ВВОД STOP ОБРАБОТКИ AGAIN ;

Пробуждение задачи от точки STOP на каждом повторении цикла выполняет подпрограмма обработки прерывания, связанная с вводом данных, когда данные, получаемые при исполнении слова ВВОД, готовы для обработки. Пример задания регулярных остановов в задаче, требующей длительного счета, дает такое определение:

: СЧЕТ ( ->) 30000 0 DO ШАГ PAUSE LOOP ;

При каждом повторении цикла после исполнения слова ШАГ происходит передача управления в кольцо задач с тем, чтобы дать поработать другим задачам, ожидающим центральный процессор.

Действие STOP (для определений, заданных в машинном коде, аналогичное ему действие WAIT) состоит в сохранении текущего состояния задачи (указателя интерпретации, указателя вершины стека данных и указателя вершины стека возвратов) в ее пользовательской области и передаче управления по адресу из ее звена связи в кольце задач.


В результате центральный процессор будет исполнять цикл из передач управления по кольцу задач, пока не встретит команду WAKE. Действие PAUSE отличается от описанного действия STOP только тем, что предварительно засылает команду WAKE в начало области данной задачи, обеспечивая тем самым пробуждение данной задачи после полного круга передач управления. Действие WAKE заключается в том, что команда JMP засылается на отведенное ей место в начало пользовательской области, восстанавливается текущее состояние данной задачи из ее области сохранения, управление передается на точку NEXT адресного интерпретатора. В результате исполнение задачи возобновляется от точки останова.

Для определения фоновой задачи используется определяющее слово BACKGROUND (фон), которое снимает со стека три числа: размер пользовательской области, размер стека данных и размер стека возвратов. Это слово резервирует в словаре память указанного объема и указатели на эти области компилирует в поле параметров создаваемой статьи, например, 40 60 50 BACKGROUND T. При исполнении слова T на стеке оставляется адрес его поля параметров, через который можно добраться до пользовательской области и стеков задачи T. Данное описание только подготавливает задачу, но не включает ее в кольцо задач и не запускает на исполнение.

При запуске форт-системы в кольце задач присутствует только одна задача — OPERATOR (оператор), звено связи которой указывает на нее же. Для встраивания в кольцо новых задач используется слово BUILD (построить), например T BUILD. Это слово, используя адрес поля параметров статьи для задачи, инициализирует ее пользовательскую область и включает ее в кольцо задач после задачи OPERATOR. При этом в начало области вписывается команда JMP, предохраняющая данную задачу от преждевременного исполнения. Заметим, что форт-слова, составляющие программу данной задачи, могут быть определены значительно позже. Обратное действие — исключение задачи из кольца задач — выполняет аналогичное по употреблению слово RUIN (разрушить).



Для запуска задачи используется слово ACTIVATE (запустить), которое снимает со стека адрес поля параметров статьи для задачи. Оно очищает стек данных и стек возвратов задачи, сбрасывая на начало указатели их вершин в области сохранения, а в качестве точки возобновления засылает свой адрес возврата, указывающий на следующее за ним слово. Поскольку адрес снимается со стека возвратов, то слово ACTIVATE аналогично слову EXIT возвращает управление на точку вызова определения, его вызвавшего. Последним действием слова ACTIVATE является подготовка задачи к пробуждению путем засылки команды WAKE в начало ее пользовательской области. Приведем пример использования слова ACTIVATE и запуска задачи на исполнение:

: СЧЕТ ( PFA->) ACTIVATE BEGIN ШАГ PAUSE LOOP ; Т СЧЕТ

Задача T будет выполнять бесконечный цикл, разделяя центральный процессор с текстовым интерпретатором форт-системы — задачей OPERATOR. Исполнение задачи можно оставить с помощью слова HALT (останов):

: HALT ( PFA-> ) ACTIVATE STOP ;

если в рамках задачи OPERATOR исполнить текст T HALT. После такого останова данную задачу можно запустить на счет с какой-нибудь другой форт-программой.

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

С каждым ресурсом свяжем переменную — его семафор, которая должна размещаться в общей области форт-системы, доступной для всех задач. Когда ресурс не занят, эта переменная содержит нуль. Когда ресурс занят какой-нибудь задачей, эта переменная содержит адрес пользовательской области данной задачи.

Определим слова GET (получить) и RELEASE (освободить), аналогичные семафорным операциям Дейкстры [].


В определениях этих слов используется вспомогательное слово FREE (свободен), проверяющее, свободен ли ресурс. Заметим, что ресурс свободен с точки зрения данной задачи, если он не занят или занят этой же задачей.

: FREE ( A:СЕМАФОР -> A,F:ПРИЗНАК СВОБОДНОСТИ ) @ DUP 0= SWAP U0 = OR ; : GET ( A:СЕМАФОР-> ) BEGIN PAUSE FREE UNTIL U0 SWAP ! ; : RELEASE ( A:СЕМАФОР ->) FREE IF 0 SWAP ! ELSE DROP THEN ;

В приведенных определениях слово U0 возвращает адрес начала пользовательской области текущей задачи. Слово GET проверяет, свободен ли ресурс в каждом цикле передач управления по кольцу задач, и занимает его, как только обнаружит, что ресурс свободен. Слово RELEASE освобождает ресурс, если он был занят данной задачей.

Разумеется, эти слова не обеспечивают защиты от взаимных блокировок задач, каждая из которых ожидает ресурса, который занят другой задачей:

VARIABLE ДИСК VARIABLE ЛЕНТА : ШАГ1 ДИСК GET ЛЕНТА GET ... ; : ШАГ2 ЛЕНТА GET ДИСК GET ... ;

Если слова ШАГ1 и ШАГ2 исполняются в разных задачах, то это может привести к их взаимной блокировке. Наилучший способ избежать такой ситуации — не запрашивать более одного ресурса единовременно. Например, в случае работы с диском и лентой, задача может запросить ресурс ДИСК, выполнить слово BLOCK для получения данных, затем переслать эти данные в свою локальную область и, освободив ДИСК, запросить ресурс ЛЕНТА.


Реализация встроенного ассемблера


Приводимый ниже текст является законченной реализацией встроенного структурного ассемблера для форт-систем на базе микропроцессора К580. В оттранслированном виде он занимает около 1300 байт и является типичным для 8-разрядных микропроцессоров. Для 16- или 32-разрядных процессоров в силу их большей сложности объем текста больше. Но даже в этом случае объем исходного текста и скомпилированного кода существенно меньше, чем для традиционных ассемблеров.

( FORTH-83 АССЕМБЛЕР ДЛЯ К580 ТЕМЯШКИНО 1985 ) VOCABULARY ASSEMBLER ASSEMBLER DEFINITIONS ( МАШИННЫЕ КОМАНДЫ ) DECIMAL : 8* 2* 2* 2* ; 4 CONSTANT H 5 CONSTANT L 7 CONSTANT A 6 CONSTANT PSW 2 CONSTANT D 3 CONSTANT E 0 CONSTANT B 1 CONSTANT C 6 CONSTANT M 6 CONSTANT SP : 1MI ( ---> ) CREATE C, DOES> C@ C, ; : 2MI ( ---> ) CREATE C, DOES> C@ + C, ; : 3MI ( ---> ) CREATE C, DOES> C@ SWAP 8* + C, ; : 4MI ( ---> ) CREATE C, DOES> C@ C, C, ; : 5MI ( ---> ) CREATE C, DOES> C@ C, , ;

HEX 00 1MI NOP 76 1MI HLT F3 1MI DI FB 1MI EI 07 1MI RLC 0F 1MI RRC 17 1MI RAL 1F 1MI RAR E9 1MI PCHL F9 1MI SPHL E3 1MI XTHL EB 1MI XCHG 27 1MI DAA 2F 1MI CMA 37 1MI STC 3F 1MI CMC 80 2MI ADD 88 2MI ADC 90 2MI SUB 98 2MI SBB A0 2MI ANA A8 2MI XRA B0 2MI ORA B8 2MI CMP 09 3MI DAD C1 3MI POP C5 3MI PUSH 02 3MI STAX 0A 3MI LDAX 04 3MI INR 05 3MI DCR 03 3MI INX 0B 3MI DCX C7 3MI RST D3 4MI OUT DB 4MI IN C6 4MI ADI CE 4MI ACI D6 4MI SUI DE 4MI SBI E6 4MI ANI EE 4MI XRI F6 4MI ORI FE 4MI CPI 22 5MI SHLD 2A 5MI LHLD 32 5MI STA 3A 5MI LDA C4 5MI CNZ CC 5MI CZ D4 5MI CNC DC 5MI CC E4 5MI CPO EC 5MI CPE F4 5MI CP FC 5MI CM CD 5MI CALL C9 1MI RET C3 5MI JMP C0 1MI RNZ C8 1MI RZ D0 1MI RNC D8 1MI RC E0 1MI RPO E8 1MI RPE F0 1MI RP F8 1MI RM C2 5MI JNZ CA 5MI JZ D2 5MI JNC DA 5MI JC E2 5MI JPO EA 5MI JPE F2 5MI JP FA 5MI JM : MOV SWAP 8* 40 + + C, ; : MVI SWAP 8* 6 + C, C, ; : LXI SWAP 8* 1 + C, , ; ( КОДЫ УСЛОВИЙ ДЛЯ СТРУКТУР УПРАВЛЕНИЯ ) C2 CONSTANT 0= D2 CONSTANT CS E2 CONSTANT PE F2 CONSTANT 0< ( СТРУКТУРЫ УПРАВЛЕНИЯ ) DECIMAL : NOT ( N:КОД УСЛОВИЯ ---> N1) 8 + ; : THEN ( АДР,2 ---> ) 2 ?PAIRS HERE SWAP ! ; : IF ( КОД ---> АДР,2 ) C, HERE 0 , 2 ; : ELSE ( АДР,2 ---> АДР1,2) 2 ?PAIRS C3 IF ROT SWAP THEN 2 ; : BEGIN ( ---> АДР,1 ) HERE 1 ; : UNTIL ( АДР,1,КОД --->) SWAP 1 ?PAIRS С, , ; : AGAIN ( АДР,1 --->) 1 ?PAIRS C3 C, , ; : WHILE ( АДР,1,КОД ---> АДР,1,АДР1,4) IF 2 + ; : REPEAT ( АДР,1,АДР1,4 --->) >R >R AGAIN R> R> 2- THEN ;


( РАБОТА С МЕТКАМИ ) 10 CONSTANT LBLMAX ( МАКСИМАЛЬНОЕ ЧИСЛО ЛОКАЛЬНЫХ МЕТОК) VARIABLE LTABLE LBLMAX 1+ 2* ALLOT 10 CONSTANT FRMAX ( МАКСИМАЛЬНОЕ ЧИСЛО ССЫЛОК ВПЕРЕД) VARIABLE FRTABLE FRMAX 2* 2* ALLOT : FRCHK ( ---> ПРОВЕРКА НЕРАЗРЕШЕННЫХ ССЫЛОК ВПЕРЕД ) FRMAX 0 DO I 2* 2* FRTABLE + @ ABORT" НЕРАЗРЕШЕННАЯ ССЫЛКА ВПЕРЕД" LOOP ; : FRCLR ( ---> ИНИЦИАЛИЗАЦИЯ ТАБЛИЦЫ ССЫЛОК ВПЕРЕД ) FRTABLE FRMAX 2* 2* ERASE LTABLE LBLMAX 1+ 2* ERASE ;

: FRRES ( N:МЕТКА ---> РАЗРЕШЕНИЕ ССЫЛОК ВПЕРЕД ) FRMAX 0 DO I 2* 2* FRTABLE + 2DUP @ = IF HERE OVER 2+ @ +! 0! ELSE DROP THEN LOOP ; : FRADD ( N:МЕТКА ---> ДОБАВЛЕНИЕ ССЫЛКИ ВПЕРЕД В ТАБЛИЦУ ) FRMAX 1+ 0 DO FRMAX I = ABORT" СЛИШКОМ МНОГО ССЫЛОК ВПЕРЕД" I 2* 2* FRTABLE + DUP @ 0= IF 2DUP ! HERE 1+ SWAP 2+ ! LEAVE ELSE DROP THEN LOOP ;

( ОПРЕДЕЛЯЮЩИЕ ВХОЖДЕНИЯ МЕТОК ) : !LT CREATE , DOES> @ FRRES HERE SWAP 2* LTABLE + ! ; ( ИСПОЛЬЗУЮЩИЕ ВХОЖДЕНИЯ МЕТОК ) : @LT CREATE , DOES> @ DUP 2* LTABLE + @ SWAP OVER 0= IF FRADD THEN DROP ; 1 !LT 1#: 2 !LT 2#: 3 !LT 3#: 4 !LT 4#: 5 !LT 5#: 6 !LT 6#: 7 !LT 7#: 8 !LT 8#: 9 !LT 9#: 10 !LT 10#: 1 @LT 1# 2 @LT 2# 3 @LT 3# 4 @LT 4# 5 @LT 5# 6 @LT 6# 7 @LT 7# 8 @LT 8# 9 @LT 9# 10 @LT 10# ( ПЕРЕКЛКЮЧЕНИЕ В АССЕМБЛЕР ) FORTH DEFINITIONS : BEG-ASM [ ASSEMBLER ] FRCLR [ FORTH ] ASSEMBLER !CSP ; : END-ASM [ ASSEMBLER ] FRCHK [ FORTH ] ?CSP;

В приведенном тексте можно выделить четыре группы определений: машинные команды, структуры управления, метки и стандартные слова для создания определений в машинном коде. Помимо них данный текст определяет ряд вспомогательных слов.

Машинные команды на языке ассемблера записываются в обратной польской форме, принятой для языка Форт: <операнды> <операция>, где <операнды> — слова, вычисляющие на стеке размещения операндов данной машинной команды, а <операция> — ее мнемоника. Для обозначения регистров микропроцессора К580 зарезервированы слова А В С D E H L, для задания регистровых пар используются слова В D H SP PSW, для косвенной адресации через регистровую пару HL — слово М.


При исполнении слов, обозначающих регистры, регистровые пары и косвенную адресацию, на стеке оставляются значения, соответствующие принятым в системе команд К580 обозначениям для регистров и регистровых пар:

A B C D E H L M SP PSW 7 0 1 2 3 4 5 6 6 6

При исполнении мнемоники машинной команды на вершину словаря компилируется соответствующая машинная инструкция. Размещения операндов при этом снимаются со стека. Порядок вычисления операндов машинной команды соответствуют порядку написания операндов на обычном языке ассемблера.

Ниже приведены для сравнения записи машинных команд на обычном языке ассемблера и на языке ассемблера данной форт-системы:

HLT HLT DAD SP SP DAD PUSH PSW PSW PUSH CMP C C CMP MOV A,M A M MOV MVI B,2 B 2 MVI LXI D,120+8 D 120 8 + LXI CALL 5002H HEX 5002 CALL DECIMAL

Для определения мнемоник используются определяющие слова, соответствующие форматам машинных команд (1MI, ..., 5MI). Все они имеют одинаковую создающую часть, которая компилирует в поле параметров статьи для мнемоники однобайтный код маски для кода данной команды, который снимается со стека. Исполняющая часть определений, используя маску и размещения операндов, которые она снимает со стека, компилирует двоичный код, соответствующий данной команде. Так, например, через слово 1MI определяются машинные команды, не имеющие операндов. При исполнении мнемоники такой команды на вершину словаря компилируется однобайтный код операции. Слово 3MI определяет мнемоники команд, имеющих один операнд — номер регистра. Этот номер занимает разряды со 2 по 4 в однобайтном коде команды. Поэтому исполняющая часть сдвигает свой операнд — номер регистра — влево на 3 разряда исполнением слова 8* и добавляет к нему маску команды. Получившийся однобайтный код компилируется на вершину словаря. Три команды — MOV, MVI и LXI — не подходят под описанные общие форматы, поэтому они определяются непосредственно через двоеточие.

Структурные операторы встроенного ассемблера позволяют программировать разветвления и циклы без явно заданных команд перехода на метку.


Их синтаксис аналогичен синтаксису соответствующих операторов языка Форт.

Условный оператор в полной или сокращенной форме записывается следующим образом:

<КОД-УСЛОВИЯ> IF <ЧАСТЬ-ТО> ELSE <ЧАСТЬ-ИНАЧЕ> THEN <КОД-УСЛОВИЯ> IF <ЧАСТЬ-ТО> THEN

Часть «то» выполняется, только если в разрядах PSW установлен заданный «код условия», в противном случае выполняется часть «иначе», если она есть.

Циклы могут быть записаны в одной из трех форм:

BEGIN <ТЕЛО-ЦИКЛА> AGAIN BEGIN <ТЕЛО-ЦИКЛА> <КОД-УСЛОВИЯ> UNTIL BEGIN <ТЕЛО-1> <КОД-УСЛОВИЯ> WHILE <ТЕЛО-2> REPEAT

Цикл BEGIN-AGAIN является бесконечным, BEGIN-UNTIL прекращает выполнение при указанном коде условия, в цикле BEGIN-WHILE-REPEAT, наоборот, указанный код условия задает продолжение цикла.

Для указания кода условия в ассемблере используются следующие слова: 0= — установлен флаг Z, 0< — флаг S, CS — флаг C, PE — флаг P. Для инвертирования кода условия используется слово NOT.

Структурные операторы компилируют на своем месте команды безусловных и условных переходов с указанным условием.

Для работы с явно задаваемыми метками в ассемблер включены слова вида 1#: 2#: ... 10#: и 1# 2# ... 10#. Слова первой группы задают определение метки, слова второй — использование. Метки имеют строго локальный характер, т.е. эти слова можно использовать только внутри определений через CODE и между словами ;CODE и END-CODE. Наряду с метками допускается использование ассемблерных структур управления.

Локальные метки рекомендуется использовать только в трехбайтных командах типа JMP, CALL, LXI. Уже определенные локальные метки могут участвовать в арифметических и стековых операциях; использующие вхождения меток, определяющие вхождения которых вводятся позднее (ссылки вперед), могут участвовать только в операциях, сводящихся к добавлению к ним числа со знаком.

Для реализации работы с метками ассемблер имеет таблицу меток LTABLE и таблицу ссылок вперед FRTABLE.


Их размер определяет максимальное число разных меток и ссылок вперед в пределах одного определения в машинном коде (от слова CODE или ;CODE до слова END-CODE). Для каждой возможной метки соответствующий элемент таблицы LTABLE содержит нуль, если метка еще не определена, или адрес, который является ее значением, если метка уже определена. Входом в эту таблицу служит номер метки. В таблице FRTABLE для каждой ссылки вперед хранятся два значения: номер метки и адрес в словаре, по которому нужно вписать ее значение. По этому адресу скомпилировано значение смещения, которое нужно добавить как число со знаком к значению данной метки.

Слова для использующих вхождений меток определяются через @LT, например 1 @LT 1#. Исполнение определенного таким образом слова 1#, обозначающего метку номер 1, состоит в обращении к таблице LTABLE по индексу 1. Если там стоит нуль, т.е. метка номер 1 еще не определена, то этот нуль выдается на стек в качестве смещения от значения метки, которое определится позднее. Одновременно в таблицу FRTABLE заносится запись о данной ссылке вперед. В качестве адреса, куда нужно будет впоследствии вписать значение метки, берется HERE 1+. Здесь используется тот факт, что в микропроцессоре К580 операнд-адрес занимает 2 байта, следующие за однобайтным кодом операции.

Слова для определения меток определяются через слово !LT, например 1 !LT 1#:. Исполнение определенного таким образом слова 1#: определяет метку номер 1 как текущий адрес вершины словаря HERE. Его действие состоит в том, что выполняется просмотр таблицы FRTABLE с разрешением всех накопившихся ссылок на данную метку, после чего запись о данной метке заносится в таблицу LTABLE.

Последнюю группу определений составляют слова для доступа к встроенному ассемблеру при компиляции машинного кода. Помимо стандартных форт-слов CODE, ;CODE и END-CODE здесь определяются слова LABEL и NEXT;. Первое используется для создания именованных подпрограмм, к которым можно обращаться из машинного кода. Второе является сокращением для часто встречающегося окончания ассемблерных определений: NEXT JMP END-CODE.


В качестве примера приведем определение слова ><, которое меняет местами байты в переданном на стеке двухбайтном значении:

CODE >< ( N1 ---> N2) H POP A L MOV L H MOV H A MOV H PUSH NEXT;

Приведенная реализация встроенного ассемблера может быть усовершенствована по нескольким направлениям. Прежде всего, увеличив константы LBLMAX и FRMAX и добавив новые определения для слов, обозначающих метки, можно увеличить количество разных меток и ссылок вперед, которые разрешается использовать в ассемблерном определении. Далее можно ввести контроль правильности операндов, включив соответствующие проверки в исполняющую часть определяющих слов для мнемоник команд. Для проверки числа элементов на стеке обычно используется глобальная ячейка CSP и слово !CSP, которое засылает в нее текущий адрес вершины стека. Интересным расширением является возможность введения макрокоманд. Макрокоманды без локальных меток можно определять обычным образом через двоеточие:

INRN ( R:РЕГИСТР,N:ЧИСЛО РАЗ ---> ) [ ABSEMBLER ] 0 DO DUP INR LOOP DROP ;

При исполнении текста А 3 INRN будут скомпилированы три команды А INR.

Если же макрорасширение должно порождать обращения к адресам через локальные метки, то потребуется более основательное расширение ассемблера.


Сопрограммы


Представление задачи в виде взаимодействующих сопрограмм (процессов) в теории программирования стало уже традиционным [,]. Но на практике этот метод используется редко, так как большинство распространенных языков высокого уровня не имеют необходимых конструкций. В стандарт языка Форт также не включены средства для сопрограммной работы. Вызвано это в основном тем, что такие средства обычно зависят от конкретного приложения и могут варьироваться очень сильно. Тем не менее, используя только стандартные для большинства систем слова, можно реализовать необходимые конструкции.

Рассмотрим достаточно простую и экономную реализацию сопрограмм. Направления, в которых можно ее усовершенствовать, рассмотрены ниже.

Сопрограмма состоит из двух частей — собственной области памяти и программы, работающей с этой областью памяти. Для языка Форт программа — это некоторое форт-слово, а область памяти должна содержать место для указателей стеков, для самих стеков и для служебной информации.

Будем считать, что адрес области памяти текущей сопрограммы находится в переменной Т-С. Используя ее, введем слова для доступа к полям этой области. Для простоты в данной реализации отводится участок памяти размером 128 байт. В его начале находятся: Т-ПРОГ — указатель начала шитого кода, соответствующего исполняемой программе, Т-СТЕК — указатель вершины стека данных на момент последней приостановки данной сопрограммы (указатель вершины стека возвратов сохраняется на стеке данных) и Т-ВЫЗВ — адрес аналогичной области для сопрограммы, вызвавшей данную. В остальной памяти участка размещаются стек данных и стек возвратов по 60 байт каждый.

Внешняя программа, в рамках которой действует описываемый механизм сопрограмм, представлена 6-байтной областью ВНЕШ, в которой размещаются перечисленные выше значения. В качестве своих стеков эта сопрограмма использует исходные стеки форт-системы.

Перед началом работы все сопрограммы должны быть проинициализированы словом START (старт), завершение работы сопрограммного механизма вызывает слово STOP (стоп).


Сопрограмма определяется через слово СОПРОГРАММА, которое по своему употреблению аналогично двоеточию. Вслед за ним идет имя сопрограммы и форт-текст, задающий требуемую программу. Обычно она состоит из цикла, внутри которого используется слово RESUME (возобновить) для приостановки данной сопрограммы и возобновления сопрограммы, ее вызвавшей. Выход по EXIT или ; приводит к завершению работы всего сопрограммного механизма и возобновлению текстовой интерпретации.

QUAN Т-С ( АДРЕС ОБЛАСТИ ТЕКУЩЕЙ СОПРОГРАММЫ) : Т-ПРОГ ( ->A:АДРЕС НАЧАЛА ПРОГРАММЫ) Т-С ; : Т-СТЕК ( ->A:АДРЕС УКАЗАТЕЛЯ СТЕКА ) Т-С 2+ ; : Т-ВЫЗВ ( ->A:АДРЕС ВЫЗВАВШЕЙ СОПРОГРАММЫ) Т-С 4 + ; CREATE ВНЕШ 6 ALLOT ( ОБЛАСТЬ ДЛЯ ВНЕШНЕЙ ПРОГРАММЫ) : RESUME ( -> ОСТАНОВИТЬ ТЕКУЩ.И ВОЗОБН. ВЫЗВАВШУЮ) RP@ SP@ Т-СТЕК ! ( СОХРАНИТЬ СОСТОЯНИЕ ТЕКУЩЕЙ) Т-ВЫЗВ @ TO Т-С ( ПЕРЕКЛЮЧИТЬСЯ НА ВЫЗВАВШУЮ ДАННУЮ) Т-СТЕК @ SP! RP! ; ( ВОЗОБНОВИТЬ ПРИОСТАНОВЛЕННУЮ) : STOP ( -> ЗАВЕРШЕНИЕ ВСЕХ СОПРОГРАММ) QUIT ; : (START) ( A:АДРЕС ОБЛАСТИ СОПРОГРАММЫ-> ИНИЦИАЛИЗ) DUP TO Т-С Т-ПРОГ @ OVER 126 + ! DUP 126 + OVER 66 + ! 66 + Т-СТЕК ! ВНЕШ TO Т-С ; : СОПРОГРАММА ( -> ОПРЕДЕЛЕНИЕ СОПРОГРАММЫ) CREATE HERE DUP 132 + , 126 ALLOT HERE 2+ , ['] STOP , (START) ] DOES> ( PFA:ОБЛАСТЬ СОПРОГРАММЫ->) RP@ SWAP >R SP> Т-СТЕК ! Т-С R> TO Т-С Т-ВЫЗВ ! Т-СТЕК @ SP! RP! ; : START ( -> ИНИЦИАЛИЗИРОВАТЬ СОПРОГРАММУ) ' >BODY [COMPILE] LITERAL STATE @ IF COMPILE (START) ELSE (START) THEN ; IMMEDIATE

В качестве примера рассмотрим часто встречающуюся задачу: прочитать некоторый файл с одной длиной записи, обработать его и записать в другой файл, причем файлы имеют разную длину записи. Если решать эту задачу без сопрограмм, то надо либо вводить переменные-флажки, сигнализирующие о состоянии буферов, либо найти общее кратное длин входной и выходной записи и, отведя буфера такого размера, сначала в цикле заполнять входной буфер, а потом его обрабатывать и выводить. В любом случае логически ясные действия по чтению, обработке и записи будут собраны в одну программу, которая в силу этого будет сложна для понимания, отладки и модификации.



Применяя описанный механизм, определим две сопрограммы ВВОД и ВЫВОД, которые обмениваются между собой через однобайтный буфер ТЕК-СИМ, и слово ЗАДАЧА, которое выполняет требуемую перепись данных: QUAN ТЕК-СИМ ( ОЧЕРЕДНОЙ ОБМЕНИВАЕМЫЙ СИМВОЛ) CREATE ВХОД-БУФ 80 ALLOT ( БУФЕР ВВОДА) CREATE ВЫХ-БУФ 64 ALLOT ( БУФЕР ВЫВОДА) СОПРОГРАММА ВВОД ОТКРЫТЬ-ВВОД BEGIN ВХОД-БУФ ЧИТАТЬ WHILE ВХОД-БУФ 80 + ВХОД-БУФ DO I С@ TO ТЕК-СИМ RESUME LOOP REPEAT ЗАКРЫТЬ-ВВОД ЗАКРЫТЬ-ВЫВОД ; СОПРОГРАММА ВЫВОД ОТКРЫТЬ-ВЫВОД BEGIN ВЫХ-БУФ 64 + ВЫХ-БУФ DO ТЕК-СИМ I С! RESUME LOOP ВЫХ-БУФ ПИСАТЬ AGAIN ; : ЗАДАЧА START ВВОД START ВЫВОД BEGIN ВВОД ВЫВОД AGAIN ;

Два ряда слов ОТКРЫТЬ-ВВОД, ЧИТАТЬ, ЗАКРЫТЬ-ВВОД и ОТКРЫТЬ-ВЫВОД, ПИСАТЬ, ЗАКРЫТЬ-ВЫВОД обеспечивают взаимодействие с входным и выходным файлами. Слова ЧИТАТЬ и ПИСАТЬ требуют в качестве параметра адрес буфера (ВХОД-БУФ для ввода и ВЫХ-БУФ для вывода), а слово ЧИТАТЬ, кроме того, возвращает логический результат — признак успешного завершения чтения.

В заключение рассмотрим направления, в которых можно развить данную реализацию.

Если сопрограммы могут использовать общий стек данных, то можно сохранять и восстанавливать только указатель стека возвратов.

Можно передавать параметры при начале работы сопрограммы, а также при каждом возобновлении. Для этого потребуется специальное слово, пересылающее указанное число элементов стека вызывающей сопрограммы на стек вызываемой.

Предусмотрев поле связи для сцепления всех сопрограмм в список, можно получить состояние всей совокупности взаимодействующих сопрограмм.

Можно предусмотреть вызов асинхронного выхода «Окончание задачи». Идентификация этого выхода может быть как статической, так и динамической.

Часть действий можно выполнять в состоянии исполнения, а не компиляции. Среди этих действий могут быть следующие: создание сопрограммы без имени и отведение заказываемого участка памяти в динамически распределяемой памяти, инициализация этой области памяти, освобождение области памяти сопрограммы при ее завершении.

Если использовать косвенный вызов через переменную типа VECT, можно динамически определять возобновляемую сопрограмму.

Возможны, конечно, и любые другие изменения, диктуемые конкретной обстановкой.


Средства отладки форт-программ


Программы на языке Форт — определения слов — кодируются и вводятся в ЭВМ методом «снизу вверх», т.е. начиная с элементарных, использующих только стандартные слова форт-системы, и кончая определением главного слова, использующего уже введенные. Такой порядок естественным образом предполагает немедленную отладку вводимых определений, поскольку определение готово к исполнению сразу же после ввода. Отладка облегчается тем, что механизм взаимодействия модулей упрощен до предела: через стек данных, который программист может сам заполнить значениями параметров перед вызовом отлаживаемого слова.

Для распечатки текущего состояния стека данных и стека возвратов многие реализации имеют слова S. и R. . В сочетании со словом DUMP, которое распечатывает область памяти, и словом ?

: ? ( А ---> ) @ . ;

которое печатает значение, находящееся по указанному адресу, эти слова создают богатые возможности для диалоговой отладки форт-слов.

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

Например, по специальному слову ON-NEXT (ПРИ-СЛЕД) строится список слов (в виде списка адресов компиляции), подлежащих отслеживанию. В частности, его можно задать, как все слова, определения которых расположены в словаре выше некоторого адреса. Для всех этих слов указываются отладочные действия, состоящие, как правило, из распечатки имени слова и стека данных в момент перехода к его исполнению.
В некоторых случаях можно проводить анализ стека возвратов и отслеживать возврат из определений, распечатывая результаты из стека данных на момент возврата. Специальные слова TRACE-ON (СЛЕЖ-ВКЛ) и TRACE-OFF (СЛЕЖ-ВЫКЛ) включают и выключают механизм отладочного слежения, подменяя или восстанавливая точку NEXT адресного интерпретатора.

С помощью этого же механизма можно ввести защиту от зацикливания, например подсчитывая в точке NEXT число исполненных слов и возобновляя диалог при достижении некоторого заданного значения этого счетчика. Если данная ЭВМ имеет встроенный таймер и операционная система позволяет обрабатывать асинхронные выходы по истечении заданного интервала времени, то этот механизм можно также использовать для предохранения от зацикливания. В процедуре обработки выхода по исчерпанию временного интервала нужно подменить точку NEXT на такую, которая выполнит возврат к диалогу с программистом. При этом программист может получить исчерпывающую информацию о месте такого «прерывания от таймера».

Другой полезный механизм отладочного слежения позволяет переключаться на диалог с программистом в момент исполнения заданного слова и реализуется через подмену поля кода в заданной словарной статье. Для выполнения такой подмены нужно определить специальное слово, например, STOP (стойте!), которое выбирает имя слова из входного потока и засылает в поле кода его словарной статьи адрес специального кода. Прежнее значение поля кода сохраняется в специально резервируемой для этого ячейке словаря. Новый код для подмененного таким образом слова состоит в переключении на текстовую интерпретацию входного потока, так что все необходимые отладочные распечатки и установки значений программист задает непосредственно в диалоге. Слово GO (идите!) завершает диалог и продолжает исполнение программы, при этом отлаживаемое слово, вызвавшее останов, исполняется в прежнем виде (для этого используется сохраненное «настоящее» значение из его поля кода). Можно предусмотреть специальное слово для продолжения работы без исполнения рассматриваемого слова.



Описанные механизмы, являясь достаточно простыми, вместе с тем существенно облегчают отладку сложных программ. Их сильная зависимость от реализации не позволяет сделать эти определения переносимыми. Вместе с тем именно эта черта позволяет реализовать такие отладочные средства максимально удобным для программиста способом с учетом конкретных особенностей данной реализации и ее операционного окружения.

Удобным вспомогательным средством, позволяющим быстро находить место останова в терминах входного текста на языке Форт, является символьная распечатка шитого кода. Поскольку скомпилированная ссылка представляет собой адрес поля кода словарной статьи, то по ней через слово >NAME можно вычислить адрес поля имени и напечатать это имя словом ID.:

: ID. ( NFA ---> ) COUNT 31 AND TYPE SPACE ;

Слово COUNT в качестве счетчика длины выдает значение первого байта поля имени; поскольку старшие разряды этого байта используются под специальные признаки, то необходимо специальное преобразование, определяемое представлением счетчика, чтобы получить истинную длину поля имени. Константа 31 как ограничение на длину имени слова даетея стандартом языка.

В программе распечатки последовательности ссылок надо предусмотреть специальную обработку некоторых адресов, вслед за которыми скомпилирована не ссылка на очередную статью, а некоторое другое значение. Такими «особыми» ссылками в стандарте языка являются слова для выполнения переходов BRANCH и ?BRANCH, для реализации циклов (DO), (LOOP) и (+LOOP), для исполнения литералов LIT и 2LIT и некоторые другие.


Векторное поле кода


В языке Форт с каждым словом-командой связано некоторое действие, определяющее семантику данного слова. В словарной статье, которая является внутренним представлением форт-слова, это действие задано через поле кода. В случае косвенного шитого кода оно содержит адрес машинной программы, исполнение которой и составляет действие данного слова. Эта программа образует исполняющую часть определяющего слова, через которое данное слово было создано, и параметризуется адресом поля параметров его словарной статьи.

Описанный механизм является чрезвычайно мощным и в то же время очень компактным в реализации. Вместе с тем и он может быть улучшен с учетом конкретной цели. Заметим, что этот механизм предписывает исполнение одной и той же программы для всех случаев применения данного слова, в то время как в каждом случае слово употребляется в определенном контексте, и его исполнение, вообще говоря, зависит от этого контекста. Разумеется, анализ контекста можно включить в программу слова, если закодировать контекст через дополнительный параметр или глобальную переменную.

Именно в этом, например, заключается действие стандартного слова LITERAL, которое анализирует текущее состояние текстового интерпретатора через значение переменной STATE. Если это значение — нуль (состояние исполнения), то это слово больше ничего не делает. При ненулевом значении (состояние компиляции) оно снимает значение с вершины стека и компилирует его как литерал на вершину словаря.

Пример другого подхода дает описанная в реализация оператора присваивания в традиционной инфиксной форме записи. В зависимости от текущего значения переменной ?ЗНАЧ, которое изменяется словами := и ;, слова, обозначающие переменные, оставляют на стеке либо адрес, либо значение данной переменной.

Предлагаемое улучшение связано прежде всего с использованием переменных. В стандартном определении при исполнении переменной на стеке будет оставлен адрес ячейки (поля параметров), в которой хранится ее значение. Однако сам по себе этот адрес редко бывает нужен, обычно он используется либо для получения текущего значения переменной с помощью слова @, либо для засылки в нее нового значения словом !.
Поэтому можно считать, что с переменной связано не одно действие, а три — получение текущего значения, засылка нового и получение адреса. Какое именно действие требуется в каждом конкретном случае, определяется контекстом.

Введем слово QUAN (от QUANTITY — величина), которое определяет новое слово с тремя описанными выше действиями. В словарной статье таких слов вместо одного поля кода создается три — по одному на каждое действие. Будем обозначать их адреса через 0CFA, 1CFA и 2CFA соответственно (рис. 3.1). За ними располагается ячейка, отведенная под текущее значение данной переменной, обозначим ее адрес через 0PFA. Если рассматривать такую структуру как обычную словарную статью, то поле 0CFA является полем кода, а поля 1CFA, 2CFA и 0PFA занимают поле параметров. Если в шитый код скомпилирован адрес 0CFA, то при исполнении соответствующего кода в качестве адреса поля параметров выступает адрес 1CFA. Аналогично для адреса 1CFA полем параметров служит 2CFA, а для 2CFA — адрес 0PFA. Поэтому описанные выше три действия можно задать так:

: ЗНАЧ DOES> ( 1CFA -> N:ЗНАЧ) 4 + @ ; : ПРИСВ DOES> ( N:ЗНАЧ,2CFA ->) 2+ ! ; : АДР DOES> ( 0PFA -> 0PFA ) ;



Рис. 3.1. Структура статьи с векторным полем кода

Нетрудно увидеть, что действие АДР совпадает со стандартным действием для переменной, состоящим в том, что адрес поля параметров кладется на стек. Определим теперь слово QUAN, используя слово ПРИСВ в качестве вспомогательного:

: QUAN ( -> ) CREATE LATEST NAME> DUP @ ( КОД 2CFA) ПРИСВ SWAP @ ( КОД 2CFA,КОД 1CFA) , , 0 , DOES> ( КОД ДЛЯ 0CFA) 4 + @ ;

Создающая часть этого определения использует поле кода создаваемой статьи как рабочую ячейку, из которой сначала извлекается значение для 2CFA, засланное туда словом CREATE, и затем значение для 1CFA, которое засылается туда словом ПРИСВ. Окончательное значение в этой ячейке устанавливается словом DOES>. Описание переменной через слово QUAN выглядит так же, как описание обычной переменной: QUAN X.


Однако теперь при исполнении слова Х на стеке будет оставлено текущее значение переменной из ее поля 0PFA. Выполнение двух других действий задают слова ТО (предлог «в») и AT (предлог «из»):

: TO ' 2+ STATE @ IF , EISE EXECUTE THEN ; IMMEDIATE : AT ' 4 + STATE @ IF , ELSE EXECUTE THEN ; IMMEDIATE

Эти слова имеют признак немедленного исполнения и в состоянии компиляции (внутри определения через двоеточие) компилируют коды 1CFA и 2CFA для следующего слова. В состоянии исполнения соответствующие действия исполняются. Теперь чтобы присвоить переменной X значение 100, нужно выполнить текст 100 TO X, а для получения значения переменной — текст AT X.

Такое усовершенствование, как и введение локальных переменных, не только упрощает программирование, но и сокращает объем скомпилированного кода. Из текста программ исчезает слово @, применявшееся для разыменования адреса переменной. В результате вместо двух ссылок — на поле CFA для переменной и статью для @ — в шитом коде присутствует только одна ссылка (на поле 0CFA). Аналогично в случае присваивания вместо двух ссылок (на поле CFA переменной и на статью для !) компилируется одна (на поле 1CFA). В практических реализациях программы для действий ЗНАЧ и ПРИСВ обычно задаются не в виде высокоуровневых определений, а непосредственно в машинном коде данной ЭВМ через встроенный ассемблер форт-системы. В этом случае описание переменных через слово QUAN не только сокращает размер программы, но и повышает ее быстродействие, поскольку отпадает необходимость в исполнении действия NEXT для интерпретации еще одной ссылки.

По аналогии со словом QUAN определим слово VECT (от VECTOR — вектор), которое также создает словарную статью с векторным полем кода из трех элементов:

: VECT ( -> ) 0 CONSTANT LATEST NAME> DUP @ , ( КОД 2CFA) ПРИСВ DUP @ ( 1CFA) SWAP >BODY ! ['] ABORT , DOES> ( КОД ДЛЯ 0CFA) 4 + @ EXECUTE ;

Код для поля 2CFA указывает на исполняющую часть из определяющего слова CONSTANT, поле 1CFA такое же, как и для слова QUAN, оно выполняет присваивание нового значения.Наконец, поле 0CFA, которое задается исполняющей частью определения VECT, исполняет слово, адрес поля кода которого является текущим значением поля 0PFA. Для получения текущего значения и засылки нового по-прежнему используются определенные ранее слова AT и TO. Вот пример на использование этих слов: VECT V ' DUP TO V. Теперь исполнение слова V равносильно исполнению DUP. Таким образом, слова, определенные через VECT, можно рассматривать как переменные, значениями которых являются другие слова. Для исполнения слова — текущего значения такой переменной — достаточно указать только имя этой переменной без дополнительных операций @ и EXECUTE. Текст AT V >NAME ID. распечатывает имя слова — текущего значения V. Разумеется, при такой распечатке предполагается, что само это слово имеет обычную структуру словарной статьи.


Выбор по целому


Выбор по целому — распространенная конструкция в языках программирования. Она является обобщением условного оператора, который осуществляет выбор между двумя последовательностями операторов — частью «то» и частью «иначе» — по логическому значению (ИСТИНА или ЛОЖЬ) условия. В конструкции выбора по целому в качестве значения условия выступает целое число, и выбор осуществляется между несколькими альтернативными ветвями, каждая из которых соответствует определенному значению условия или некоторому множеству таких значений. Как правило, множества значений условия для разных ветвей не должны пересекаться. Обычно эти множества задают явным перечислением отдельных значений или указанием диапазона для них. Разберем два варианта реализации выбора по целому. В первом используется переключатель, во втором — вложенные условные операторы.

В случае реализации через переключатель каждая ветвь должна задаваться отдельным форт-словом. Пусть имеется несколько ветвей, каждая из которых задается своим порядковым номером. Из адресов компиляции этих ветвей в соответствии с их порядковыми номерами строится вектор-переключатель, и нужная ветвь выбирается по порядковому номеру. Определяющее слово SWITCH (переключатель) компилирует такой вектор в поле параметров определяемого слова и задает выбор альтернативы в исполняющей части:

: SWITCH ( ->) ?EXEC CREATE SMUDGE ] DOES> ( I:НОМЕР ВЕТВИ,PFA->) SWAP 1- 2 * + @ EXECUTE ;

Создающая часть строит новую словарную статью и переключает текстовый интерпретатор в состояние компиляции. Таким образом, следующие слова будут компилироваться в поле параметров определяемого слова. Слово SMUDGE выставляет разряд «Не готов» в байте-счетчике поля имени, делая данную статью «невидимой» при поиске слов в словаре. Этот разряд будет сброшен словом ; (точка с запятой), которое завершает компиляцию такого переключателя. Например:

: W1 ." ПОНЕДЕЛЬНИК" ; : W2 ." ВТОРНИК" ; : W3 ." СРЕДА" ; : W4 ." ЧЕТВЕРГ" ; : W5 ." ПЯТНИЦА" ; : W6 ." СУББОТА" ; : W7 ." ВОСКРЕСЕНЬЕ" ; SWITCH ДЕНЬ-НЕДЕЛИ W1 W2 W3 W4 W5 W6 W7 ;


Порядок использования переключателя ДЕНЬ-НЕДЕЛИ иллюстрирует следующий протокол работы:

> 3 ДЕНЬ-НЕДЕЛИ СРЕДА OK > 5 ДЕНЬ-НЕДЕЛИ ПЯТНИЦА OK

Описанный механизм аналогичен определению вектора, рассмотренному в . Его также можно задавать по-разному в зависимости от конкретных требований. В приведенной реализации неправильное значение выбирающего условия (выходящее за диапазон от 1 до 7) приведет к непредсказуемому результату, поскольку соответствующий контроль отсутствует. Чтобы ввести его в реализацию, нужно каким-то образом подсчитать число скомпилированных ссылок при завершении компиляции. Для этого можно применить способ, аналогичный компиляции переходов в шитом коде, введя вместо точки с запятой специальное слово, отмечающее конец переключателя.

Для реализации конструкции выбора через вложенные условные операторы создаются специальные слова, которые обрамляют всю конструкцию и каждую ее ветвь. В этом случае ветвь является обычной последовательностью форт-слов, аналогичной части «то» или части «иначе» условного оператора. Как и в случае условного оператора, конструкцию выбора можно использовать только внутри определений через двоеточие, т.е. в состоянии компиляции текстового интерпретатора. При этом все ее специальные слова имеют признак немедленного исполнения и компилируют необходимые проверки и переходы.

: CASE ( ->A0:CSP,4) ?COMP CSP @ !CSP 4 ; IMMEDIATE : OF ( 4->A:>MARK,1,5) 4 ?PAIRS COMPILE OVER COMPILE = [COMPILE] IF COMPILE DROP 5 ; IMMEDIATE : ENDOF ( A:>MARK,1,5->A2:>MARK,1,4) 5 ?PAIRS [COMPILE] ELSE 4 ; IMMEDIATE : ENDCASE ( A0,A1,1, ..,AN,4->) 4 ?PAIRS COMPILE DROP BEGIN SP@ CSP @ = 0= WHILE [COMPILE] THEN REPEAT ( А0) CSP ! ; IMMEDIATE : ДЕНЬ-НЕДЕЛИ ( N->) CASE 1 OF ." ПОНЕДЕЛЬНИК" ENDOF 2 OF ." ВТОРНИК" ENDOF 3 OF ." СРЕДА" ENDOF 4 OF ." ЧЕТВЕРГ" ENDOF 5 OF ." ПЯТНИЦА" ENDOF 6 OF ." СУББОТА" ENDOF 7 OF ." ВОСКРЕСЕНЬЕ" ENDOF CR . ." - ДЕНЬ НЕДЕЛИ?" ABORT ENDCASE ;



Слова CASE (выбор) и ENDCASE (конец выбора) ограничивают конструкцию и обеспечивают правильную компиляцию вложенных операторов. Слово CASE, проверив, что текстовый интерпретатор находится в состоянии компиляции, сменяет глобальную переменную CSP (сокращение от CURRENT STACK POINTER — текущий указатель стека), сохраняя на стеке ее прежнее значение (слово !CSP засылает в переменную CSP адрес текущей вершины стека). Слова OF (из) и ENDOF (конец из) ограничивают отдельную ветвь. Во время работы скомпилированного определения перед началом исполнения каждой ветви на стеке лежат два значения: число, представляющее условие выбора, и номер данной ветви. Слово OF компилирует текст OVER = IF DROP, который обеспечивает передачу управления на данную ветвь, если эти два значения совпали, причем в этом случае они оба снимаются со стека. Если же значения оказались разными, то управление передается на текст, следующий за словом ENDOF для данной ветви, которое эквивалентно слову ELSE. Наконец, слово ENDCASE компилирует операцию DROP, чтобы снять со стека оставшееся там значение условия, и разрешает все накопившиеся на стеке выходы из ветвей на текущую точку, исполняя для этих ветвей слово THEN. Его последним действием является восстановление прежнего значения переменной CSP, которое к этому моменту оказывается на вершине стека.

В приведенных определениях можно несколько уменьшить объем кода, компилируемого для входа в каждую ветвь, если ввести для этого специальное слово, вслед за которым в шитый код компилируется адрес перехода на следующую ветвь:

: (OF) ( N:УСЛОВИЕ,I:НОМЕР ВЕТВИ->) OVER = IF DROP R> 2+ ELSE R> @ THEN >R ; : OF ( 4->A:>MARK,2,5) 4 ?PAIRS COMPILE (OF) >MARK 2 5 ; IMMEDIATE

Исполнение слова (OF) аналогично ?BRANCH, в зависимости от условия оно переустанавливает указатель интерпретации, либо обходя скомпилированную за ним ссылку, либо устанавливая указатель по значению этой ссылки. Аналогичным образом наряду со словами (OF) и ОF можно определить пары (<OF) и <OF, (>OF) и >OF, (<OF<) и <OF<, выполняющие сравнение значения условия с заданным значением, выбирающим данную ветвь, не на равенство, а на неравенство указанного вида.При этом слово (<OF<) сравнивает значение условия с двумя значениями, определяющими интервал. Например:

: ПРИЕМ ( N:НОМЕР ДНЯ->) CASE 3 OF ." НЕПРИЕМНЫЙ" ENDOF 1 5 <OF< ." ПРИЕМНЫЙ" ENDOF 6 7 <OF< ." ВЫХОДНОЙ" ENDOF CR . ." - НОМЕР ДНЯ?" ABORT ENDCASE ." ДЕНЬ" ;

В данном примере множества выбирающих значений пересекаются, и выбор ветви определяется для этого случая порядком расположения ветвей в конструкции выбора.


Запланированное перекрытие


Адресное пространство форт-системы не так уж велико — 64 Кбайт. Это налагает известное ограничение на общее число форт-слов, одновременно присутствующих в словаре. Для снижения этого ограничения можно применить свернутый шитый код или использовать форт-систему, в которой в качестве основного вместо 16-разрядного значения выступает 32-разрядное. Можно заметно сократить требуемый объем оперативной памяти, если использовать векторное поле кода.

Опишем еще один простой способ снижения требований к памяти — запланированное перекрытие. Группа взаимосвязанных определений образует единый сегмент в двоичном коде, который может быть загружен на заранее заданное место в словаре из внешней памяти форт-системы. Разные сегменты, загружаемые на одно и то же место оперативной памяти, перекрывают друг друга, вот почему такая структура и названа запланированным перекрытием. Благодаря тому, что хранящийся во внешней памяти сегмент выражен в двоичном коде, а не в виде текста, его загрузка идет примерно в 50-100 раз быстрее, чем обычная текстовая интерпретация.

В приводимой далее реализации каждый сегмент загружается на фиксированный адрес — значение указателя HERE на момент начала его компиляции. Определение сегмента включается в список FORTH. После того как трансляция сегмента завершена, при его выгрузке во внешнюю память указатель вершины словаря понижается до прежнего значения с одновременным исключением всех слов данного сегмента из списка FORTH. При загрузке сегмента на соответствующий адрес указатель вершины словаря вновь поднимается, и определения сегмента снова включаются в список FORTH.

Для трансляции сегментов введем глобальную переменную (СЕГМ), содержащую адрес начала очередного компилируемого сегмента, границы которого отмечаются словами СЕГМ-НАЧ и СЕГМ-КОН. Выгрузку и загрузку сегментов выполняют слова СЕГМ-ВЫГР и СЕГМ-ЗАГР.

VARIABLE (СЕГМ) : СЕГМ-НАЧ ( -> НАЧАЛО ТРАНСЛЯЦИИ СЕГМЕНТА) HERE DUP (СЕГМ) ! 0 , ( НОМЕР ЭКРАНА) HERE 0 C, BL C, ( ФИКТИВНОЕ ИМЯ) FORTH DEFINITIONS LATEST , ( ЗВЕНО СВЯЗИ) CURRENT @ ! , ( ТОЧКА ЗАГРУЗКИ) 0 , 0 , ( ДЛИНА И ПОСЛЕДНЯЯ СТАТЬЯ) ; : СЕГМ-КОН ( -> КОНЕЦ ТРАНСЛЯЦИИ СЕГМЕНТА) (СЕГМ) @ >R ( ТОЧКА ЗАГРУЗКИ СЕГМЕНТА) HERE R@ - ( ДЛИНА) DUP R@ 8 + ! FORTH DEFINITIONS LATEST R> 10 + ! ; : СЕГМ-ВЫГР ( N:НОМЕР ЭКРАНА-> ВЫГРУЗКА) DUP (СЕГМ) @ >R R@ ! ( НОМЕР) R@ SWAP ( A:АДРЕС СЕГМЕНТА,N) DUP R@ 8 + @ 1023 + 1024 / ( A,N,N,N1:ЧИСЛО ЭКРАНОВ) CR ." СЕГМЕНТ ВИГРУЖЕН НА ЭКРАНЫ: " + SWAP DO I . ( AI:АДРЕС СЛЕДУЮЩЕИ ПОРЦИИ) DUP I BUFFER 1024 CMOVE UPDATE 1024 +LOOP DROP FLUSH R> (FORGET) ; : СЕГМ-ЗАГР ( N:НОМЕР ЭКРАНА-> ЗАГРУЗКА) DUP BLOCK ( N,A0:АДРЕС ПЕРВОГО БЛОКА СЕГНЕНТА) 2DUP @ - IF DROP CR . ." - БЛОК ДЛЯ ЗАГРУЗКИ?" ABORT THEN DUP 6 + @ (FORGET) 2DUP HERE ( N,A0,N,A0,A1:НАЧАЛО) SWAP 8 + @ ( N,A0,N,A1,L) DUP ALLOT 1023 + 1024 / ( N,A0,N,A1,N1:ЧИСЛО ЭКР) ROT DUP ROT + SWAP ( ...N1+N,N) DO I BLOCK OVER 1024 CMOVE 1024 + LOOP 2DROP ( N) BLOCK 10 + @ CURRENT @ ! ;


Слово СЕГМ-НАЧ строит заголовок сегмента, который располагается в его начале и содержит начальный номер экрана во внешней памяти, фиктивную словарную статью, адрес начала сегмента в оперативной памяти, длину сегмента и адрес начала его последней словарной статьи. Номер экрана определяется программистом при выгрузке сегмента во внешнюю память, впоследствии именно с этого экрана начинается его загрузка. Слово СЕГМ-ЗАГР проверяет, содержат ли первые 2 байта сегмента данный номер, и выдает сообщение об ошибке, если это не так.

Фиктивная словарная статья в заголовке сегмента аналогична полю параметров для слов, определенных через VOCABULARY, и служит для включения определений данного сегмента в список FORTH при загрузке сегмента. Она сцепляется с последней статьей списка FORTH на момент начала трансляции сегмента. При загрузке сегмента список FORTH устанавливается на последнее определение внутри данного сегмента. Таким образом, словарные статьи сегмента вновь включаются в словарь форт-системы. При изменениях состояний словаря, связанных с загрузкой и выгрузкой сегментов, в качестве вспомогательного используется слово (FORGET), которое корректным образом понижает вершину словаря, исключая из него все системные ссылки на исключаемые статьи. В качестве параметра это слово использует новый адрес вершины словаря (см. модель форт-системы в ).


Модель форт-системы


Приводимый ниже текст представляет собой ядро системы ФОРТ-ЕС (см. ), из которого исключены запускающая часть и реализации слов нижнего уровня для обмена с терминалом и внешней памятью. Общий объем ядра — 8 Кбайт (свыше 200 слов). Текст состоит из двух частей — списка слов с их краткими спецификациями и экранов с определениями на встроенном языке ассемблера и на языке Форт.

Помимо слов, которым соответствуют статьи в словаре, внутри ассемблерных определений используются метки и адреса. В спецификациях эти объекты отмечены буквами М и А. Они определяются с помощью слов М: и А: соответственно. Метки используются в машинных командах, а адреса порождают двухбайтное значение, содержащее данный адрес. Адресные операнды FIRST и SECOND обозначают соответственно первый и второй элементы стека. Макрокоманды PUSH, POP и PULL можно рассматривать как команды с одним регистровым операндом. Операция PUSH помещает на стек значение из регистра, POP снимает верхнее значение со стека, засылая его в регистр, и PULL копирует верхнее значение стека в регистр. Кроме того, в ассемблерных определениях используются локальные метки [, с. 191], обозначаемые целыми числами и словами =F (для ссылки вперед) и =B (для ссылки назад). Определяется локальная метка через слово =H, которое полагает ее равной текущему значению счетчика адреса. Регистры общего назначения обозначаются специальными словами и имеют следующий смысл:

RW1 — рабочий регистр, старший в паре;

RW2 — рабочий регистр, младший в паре;

RI — указатель адресного интерпретатора;

RRET — абсолютный адрес вершины стека возвратов;

RSTACK — абсолютный адрес вершины стека данных;

RD — форт-адрес текущей вершины словаря;

RFORTH — абсолютный адрес начала словаря, соответствующий нулевому форт-адресу;

RNEXT — адрес точки NEXT адресного интерпретатора (тот же адрес, что и в RFORTH);

RTWO — константа 2;


RMASK — константа 65535.

Предполагается также, что в регистре 13 находится адрес области сохранения и регистры 0, 1, 14 и 15 свободно используются внутри определений как рабочие.

Тексты определений представлены в виде распечаток стандартных форт-текстов и занимают экраны с номерами от 1 до 47. В последнем столбце спецификации для каждого слова указан номер экрана, на котором оно определено. Главным словом модели является слово ФОРТ-СИСТЕМА.

В списке спецификаций слова расположены по возрастанию в кодировке ДКОИ. Они могут иметь следующие отметки:

А — адрес;

М — метка;

Н — слово немедленного исполнения;

К — требуется режим компиляции;

П — переменная, размещенная в пользовательской области;

С — системная переменная, размещенная в словаре;

Э — требуется режим обработки экрана;

+ — слово из дополнения к стандарту «Форт-83»;

* — нестандартное слово.

Для каждого слова указываются значения, которые оно снимает со стека (слева от знака ->), и результат, который оно оставляет на стеке (справа от знака ->). Если перечисляется несколько значений, то верхнее (вершина стека) находится справа.

Для задания параметров и результатов, передаваемых через стек данных, используются следующие обозначения:

+N — неотрицательное целое со знаком;

А — двухбайтный форт-адрес;

С — однобайтное значение (старший байт, как правило, нулевой);

CFA — двухбайтный адрес поля кода словарной статьи;

D — четырехбайтное целое со знаком;

F — булевское значение (0 — ЛОЖЬ, не 0 — ИСТИНА);

FF — булевское значение ЛОЖЬ (0);

L — абсолютный машинный адрес (четырехбайтный);

LFA — двухбайтный адрес поля связи словарной статьи;

N — двухбайтное целое со знаком;

NFA — двухбайтный адрес поля имени словарной статьи;



PFA — двухбайтный адрес поля параметров словарной статьи;

Т — двухбайтный адрес строки со счетчиком;

TF — булевское значение ИСТИНА (не 0, обычно -1);

U — двухбайтное целое без знака;

UD — четырехбайтное целое без знака;

W — двухбайтное целое со знаком или без него (N или U);

WD — четырехбайтное целое со знаком или без него (D или UD).

Для некоторых слов указаны две группы результатов, отделяемые друг от друга косой чертой (/). Они различаются по булевскому значению на вершине стека: не 0 (ИСТИНА) означает успех, 0 (ЛОЖЬ) — неудачу.

*Н -> (ПУСТОЕ СЛОВО) - ЗАКОНЧИТЬ ИНТЕРПРЕТАЦИЮ 40 ВХОДНОГО ПОТОКА [ Н -> ПЕРЕКЛЮЧИТЬ ТЕКСТОВЫЙ ИНТЕРПРЕТАТОР 22 В РЕЖИМ ИСПОЛНЕНИЯ ['] НК -> /КОМПИЛЯЦИЯ/ СКОМПИЛИРОВАТЬ CFA 41 ->CFA /ИСПОЛНЕНИЕ/ СЛЕДУЮЩЕГО СЛОВА КАК ЧИСЛОВОЙ ЛИТЕРАЛ [COMPILE] НК -> СКОМПИЛИРОВАТЬ СЛЕДУЮЩЕЕ СЛОВО 41 НЕЗАВИСИМО ОТ ЕГО ПРИЗНАКА "IMMEDIATE" . N-> НАПЕЧАТАТЬ N НА ТЕРМИНАЛЕ И ДАТЬ ПРОБЕЛ 38 .( Н -> НАПЕЧАТАТЬ СЛЕДУЮЩИЕ ЛИТЕРЫ ДО 28 ЗАКРЫВАЮЩЕЙ СКОБКИ ИСКЛЮЧИТЕЛЬНО ." НК -> ПРИ ИСПОЛНЕНИИ НАПЕЧАТАТЬ НА ТЕРМИНАЛЕ 28 СЛЕДУЮЩИЕ ЛИТЕРЫ ДО КАВЫЧКИ ИСКЛЮЧИТЕЛЬНО 37 .R + N1,+N2-> НАПЕЧАТАТЬ N1 НА ТЕРМИНАЛЕ 38 В ПОЛЕ ДЛИНЫ +N2 СПРАВА .VOC * PFA+2-> НАПЕЧАТАТЬ НА ТЕРМИНАЛЕ ИМЯ 43 СЛОВАРНОЙ СТАТЬИ ДЛЯ СПИСКА СЛОВ < N1,N2->F F НЕ НУЛЬ, ЕСЛИ N1 МЕНЬШЕ N2 19 <> + W1,W2->F F НЕ НУЛЬ, ЕСЛИ W1 НЕ РАВНО W2 19 <# -> НАЧАТЬ ФОРМАТНОЕ ПРЕОБРАЗОВАНИЕ 37 <MARK K ->A ОТМЕТИТЬ ТЕКУЩИЙ АДРЕС ДЛЯ ССЫЛКИ НАЗАД 19 <RESOLVE K A-> РАЗРЕШИТЬ ССЫЛКУ НАЗАД В АДРЕС А 19 ( Н -> КОММЕНТАРИЙ - ПРОПУСТИТЬ СЛЕДУЮЩИЙ ТЕКСТ 28 ДО ЗАКРЫВАЮЩЕЙ КРУГЛОЙ СКОБКИ (.") *K -> ПРОЦЕДУРА, КОМПИЛИРУЕМАЯ В ." 28 (+LOOP) * N-> ТЕСТ НА ЗАВЕРШЕНИЕ ЦИКЛА "DO +LOOP" 5 С ШАГОМ N (;CODE) *K -> ЗАПИСАТЬ В ПОЛЕ КОДА ПОСЛЕДНЕЙ СТАТЬИ 31 СЛЕДУЮЩИЙ АДРЕС И ЗАКОНЧИТЬ ОПРЕДЕЛЕНИЕ (#SCR) * N->A,T ПЕРЕВЕСТИ НОМЕР ЭКРАНА N В ТЕКСТ 46 (A") * F-> ПРОЦЕДУРА, КОМПИЛИРУЕМАЯ В ABORT" 29 (DO) *K W1,W2-> ВХОД В ЦИКЛ СО СЧЕТЧИКОМ ОТ W2 ДО W1 24 (EXPECT) * A,+N1->A,+N2 ВВЕСТИ С ТЕРМИНАЛА 6 +N1 ЛИТЕР ПО АДРЕСУ А ДО ПЕРЕВОДА СТРОКИ; +N2 - ФАКТИЧЕСКОЕ ЧИСЛО ВВЕДЕННЫХ ЛИТЕР (FIND) * -1,AN,,,A1,T->CFA,C,TF/FF ИСКАТЬ СЛОВО Т 34 В СПИСКАХ А1,,,AN; ПРИ УСПЕХЕ ДАТЬ CFA ЕГО СТАТЬИ И С - БАЙТ ДЛИНЫ С ПРИЗНАКАМИ (FORGET) * A-> УДАЛИТЬ СЛОВАРНЫЕ СТАТЬИ ПОСЛЕ АДРЕСА А 45 (LOOP) * -> ТЕСТ НА ЗАВЕРШЕНИЕ ЦИКЛА "DO LOOP" 5 (VOC) * PFA1+2->PFA2,N/0,N ДАТЬ ЧИСЛО СТАТЕЙ N В 43 СПИСКЕ PFA1+2 И PFA СЛЕДУЮЩЕГО СПИСКА ИЛИ НУЛЬ, ЕСЛИ ЕГО НЕТ + W1,W2->W3 СУММА ЧИСЕЛ W1 И W2 17 +! W,A-> УВЕЛИЧИТЬ ЗНАЧЕНИЕ ПО АДРЕСУ А НА W 17 +BUF * A1->A2,F ПЕРЕЙТИ К СЛЕДУЮЩЕМУ БУФЕРУ В ПУЛЕ 25 +LOOP HK A1,A2,3-> /КОМПИЛЯЦИЯ/ КОНЕЦ ЦИКЛА 47 N-> /ИСПОЛНЕНИЕ/ "DO +LOOP" С ШАГОМ N ! W,A-> ЗАСЛАТЬ ЗНАЧЕНИЕ W ПО АДРЕСУ А 11 !CSP * -> ЗАПОМНИТЬ АДРЕС ВЕРШИНЫ СТЕКА В CSP 29 ] -> ПЕРЕКЛЮЧИТЬ ТЕКСТОВЫЙ ИНТЕРПРЕТАТОР 22 В РЕЖИМ КОМПИЛЯЦИИ * N1,N2->N3 ПРОИЗВЕДЕНИЕ ЧИСЕЛ N1 И N2 18 */ N1,N2,N3->N4 ЧАСТНОЕ ОТ ДЕЛЕНИЯ N1*N2 НА N3 18 */MOD N1,N2,N3->N4,N5 ОСТАТОК N4 И ЧАСТНОЕ N5 18 ОТ ДЕЛЕНИЯ ПРОИЗВЕДЕНИЯ N1*N2 НА N3 ; HK -> ЗАКОНЧИТЬ ОПРЕДЕЛЕНИЕ ЧЕРЕЗ ДВОЕТОЧИЕ 32 ;S +НЭ -> ЗАКОНЧИТЬ ИНТЕРПРЕТАЦИЮ ЭКРАНА 41 - W1,W2->W3 ВЫЧЕСТЬ W2 ИЗ W1 17 --> +НЭ -> ИНТЕРПРЕТИРОВАТЬ СЛЕДУЮЩИЙ ЭКРАН 41 -FIND * ->A,N ВВЕСТИ СЛОВО И ИСКАТЬ В СЛОВАРЕ; 35 РЕЗУЛЬТАТ ТОТ ЖЕ, ЧТО И У FIND -TRAILING A,N1->A,N2 ОТСЕЧЬ КОНЕЧНЫЕ ПРОБЕЛЫ 41 / N1,N2->N3 ЧАСТНОЕ ОТ ДЕЛЕНИЯ N1 НА N2 18 /MOD N1,N2->N3,N4 ОСТАТОК N3 И ЧАСТНОЕ N4 18 ОТ ДЕЛЕНИЯ N1 НА N2 , W-> СКОМПИЛИРОВАТЬ W НА ВЕРШИНУ СЛОВАРЯ 11 ," * -> СКОМПИЛИРОВАТЬ СТРОКУ СО СЧЕТЧИКОМ 28 > N1,N2->F F НЕ НУЛЬ, ЕСЛИ N1 БОЛЬШЕ N2 19 >= * N1,N2->F F НЕ НУЛЬ, ЕСЛИ N1 НЕ МЕНЬШЕ N2 19 >BODY CFA->PFA ОТ ПОЛЯ КОДА К ПОЛЮ ПАРАМЕТРОВ 30 >IN П ->A ПЕРЕМЕННАЯ - СМЕЩЕНИЕ ОЧЕРЕДНОЙ ЛИТЕРЫ 8 ВО ВХОДНОМ ТЕКСТОВОМ БУФЕРЕ ИЛИ ЭКРАНЕ >LINK * CFA->LFA ПЕРЕЙТИ ОТ ПОЛЯ КОДА К ПОЛЮ СВЯЗИ 30 >MARK K ->A ОТМЕТИТЬ ТЕКУЩИЙ АДРЕС ДЛЯ ССЫЛКИ ВПЕРЕД 19 >NAME * CFA->NFA ПЕРЕЙТИ ОТ ПОЛЯ КОДА К ПОЛЮ ИМЕНИ 30 >R K W-> ПЕРЕНЕСТИ W НА СТЕК ВОЗВРАТОВ 9 >RESOLVE K A-> РАЗРЕШИТЬ ССЫЛКУ ВПЕРЕД В АДРЕСЕ А 19 ? * A-> НАПЕЧАТАТЬ ЗНАЧЕНИЕ ПО АДРЕСУ А 38 ?+ * +N->+N ПРОВЕРИТЬ, ЧТО +N НЕОТРИЦАТЕЛЬНО 29 ?ABORT * F,T-> ЕСЛИ F НЕ НУЛЬ, ТО НАПЕЧАТАТЬ НА 29 ТЕРМИНАЛЕ СТРОКУ Т И УЙТИ НА ABORT ?BRANCH K F-> ЕСЛИ F "ЛОЖЬ", ТО КАК BRANCH, ИНАЧЕ 5 ПРОДОЛЖИТЬ ИНТЕРПРЕТАЦИЮ ОТ АДРЕСА, СЛЕДУЮЩЕГО ЗА АДРЕСОМ ПЕРЕХОДА ?COMP * -> ПРОВЕРИТЬ, ЧТО ТЕКУЩИЙ РЕЖИМ - КОМПИЛЯЦИЯ 29 ?CSP * -> ВЫДАТЬ ОШИБКУ "СБИЛСЯ УКАЗАТЕЛЬ СТЕКА" 29 ЕСЛИ ОН НЕ РАВЕН ЗНАЧЕНИЮ В CSP ?DUP W->W,W ПРОДУБЛИРОВАТЬ W, ЕСЛИ ЭТО НЕ НУЛЬ 9 ?GAP * N-> ВЫДАТЬ ОШИБКУ "ИСЧЕРПАНИЕ ПАМЯТИ", ЕСЛИ 29 ЗАЗОР МЕЖДУ ВЕРШИНАМИ СТЕКА И СЛОВАРЯ МЕНЕЕ N БАЙТОВ ?LOADING * -> ВЫДАТЬ ОШИБКУ "НЕТОБРАБОТКИ ЭКРАНА", 29 ЕСЛИ ВХОДНОЙ ТЕКСТ ИДЕТ НЕ С ЭКРАНА ?PAIRS * W1,W2-> ВЫДАТЬ ОШИБКУ "НЕПАРНЫЕ СКОБКИ", 29 ЕСЛИ W1 НЕ РАВНО W2 ?STACK * -> ВЫДАТЬ ОШИБКУ "ИСЧЕРПАНИЕ СТЕКА", ЕСЛИ 29 ОН БОЛЕЕ, ЧЕМ ПУСТ, И "ИСЧЕРПАНИЕ ПАМЯТИ" ПРИ ЗАЗОРЕ, МЕНЬШЕМ 10 БАЙТОВ : -> НАЧАТЬ ОПРЕДЕЛЕНИЕ СЛОВА ЧЕРЕЗ ДВОЕТОЧИЕ 32 # D1->D2 ДЕЛЕНИЕМ D1 НА ЗНАЧЕНИЕ BASE 37 ВЫДЕЛИТЬ 1 ЦИФРУ С КОНЦА И ДОБАВИТЬ ЕЕ В БУФЕР PAD, ОСТАВИВ ЧАСТНОЕ D2 #> D->A,+N ЗАКОНЧИТЬ ФОРМАТНОЕ ПРЕОБРАЗОВАНИЕ; 37 ДАТЬ АДРЕС А НАЧАЛА ЛИТЕР И ИХ ЧИСЛО +N #S D1->0,0 ВЫДЕЛЯТЬ ЦИФРЫ D1 ПО СЛОВУ # ДО 37 ПОЛУЧЕНИЯ НУЛЯ #TIB П ->A ПЕРЕМЕННАЯ - ЧИСЛО ЛИТЕР В БУФЕРЕ TIB 8 @ A->W ДАТЬ ЗНАЧЕНИЕ ПО АДРЕСУ А 11 ' ->CFA ДАТЬ CFA ДЛЯ СЛЕДУЮЩЕГО СЛОВА 41 = W1,W2->F F НЕ НУЛЬ, ЕСЛИ W1 РАВНО W2 19 " *НК -> /КОМПИЛЯЦИЯ/ СКОМПИЛИРОВАТЬ СЛЕДУЮЩИЕ 28 ->T /ИСПОЛНЕНИЕ/ ЛИТЕРЫ ДО КАВЫЧКИ ИСКЛЮЧИТЕЛЬНО КАК СТРОКУ СО СЧЕТЧИКОМ ". * T-> НАПЕЧАТАТЬ НА ТЕРМИНАЛЕ СТРОКУ T 28 ABORT -> СБРОСИТЬ СТЕК И УЙТИ ПО QUIT 28 ABORT" КН -> /КОМПИЛЯЦИЯ/ ЕСЛИ F "ИСТИНА" (НЕ НУЛЬ) 29 F-> /ИСПОЛНЕНИЕ/ ТО НАПЕЧАТАТЬ НА ТЕРМИНАЛЕ СЛЕДУЮЩИЙ ТЕКСТ ДО КАВЫЧКИ И УЙТИ НА ABORT ABORT8 * -> ВЫДАТЬ ОШИБКУ "НЕПРАВИЛЬНОЕ ЗНАЧЕНИЕ" 29 ABS N1->N2 АБСОЛЮТНАЯ ВЕЛИЧИНА 17 AGAIN +НК A,1-> /КОМПИЛЯЦИЯ/ КОНЕЦ ЦИКЛА "BEGIN AGAIN" 47 -> /ИСПОЛНЕНИЕ/ ALIGN * +N-> ВЫРОВНЯТЬ ВЕРШИНУ СЛОВАРЯ НА +N 10 ALIGNH * -> ВЫРОВНЯТЬ ВЕРШИНУ СЛОВАРЯ НА ПОЛУСЛОВО 10 ALLOT W-> СМЕСТИТЬ ВЕРШИНУ СЛОВАРЯ НА W БАЙТОВ 10 ALPHA * N->C ПРЕОБРАЗОВАТЬ N В ЛИТЕРУ С КАК ЦИФРУ 37 AND W1,W2->W3 ПОРАЗРЯДНОЕ ЛОГИЧЕСКОЕ "И" 13 B/BUF + ->1024 ЧИСЛО БАЙТОВ В БЛОЧНОМ БУФЕРЕ 7 BADWORD * A-> СООБЩИТЬ О НЕОПОЗНАНОМ СЛОВЕ 29 BASE П ->A ПЕРЕМЕННАЯ - ТЕКУЩЕЕ ОСНОВАНИЕ СИСТЕМЫ 8 СЧИСЛЕНИЯ ПРИ ВВОДЕ-ВЫВОДЕ ЧИСЕЛ BEGIN НК ->A,1 /КОМПИЛЯЦИЯ/ НАЧАЛО ЦИКЛА "BEGIN" 47 -> /ИСПОЛНЕНИЕ/ BL + ->64 КОНСТАНТА - КОД ПРОБЕЛА В ДКОИ 7 BLANK + A,U-> ЗАСЛАТЬ ПРОБЕЛЫ В U БАЙТОВ ПО АДРЕСУ А 22 BLK П ->A ПЕРЕМЕННАЯ - НОМЕР ВХОДНОГО БЛОКА-ЭКРАНА 8 BLOCK +N->A ДАТЬ АДРЕС А БУФЕРА С БЛОКОМ +N 25 BODY> * PFA->CFA ОТ ПОЛЯ ПАРАМЕТРОВ К ПОЛЮ КОДА 30 BRANCH K -> ПРОДОЛЖИТЬ ИНТЕРПРЕТАЦИЮ ОТ ЗНАЧЕНИЯ 5 СЛЕДУЮЩЕГО СКОМПИЛИРОВАННОГО АДРЕСА BRANCH# M ПРОДОЛЖЕНИЕ ИНТЕРПРЕТАЦИИ ОТ АДРЕСА В 5 СЛЕДУЮЩЕМ ПОЛУСЛОВЕ BUFFER +N->A ПРИПИСАТЬ БЛОКУ +N БУФЕР 25 C! C,A-> ЗАСЛАТЬ БАЙТ С ПО АДРЕСУ А 11 C, + C-> СКОМПИЛИРОВАТЬ БАЙТ С НА ВЕРШИНУ СЛОВАРЯ 11 С@ A->C ДАТЬ БАЙТ ПО АДРЕСУ А 11 C" *H -> /КОМПИЛЯЦИЯ/ СКОМПИЛИРОВАТЬ КОД ПЕРВОЙ 28 ->C /ИСПОЛНЕНИЕ/ ЛИТЕРЫ СЛЕДУЮЩЕГО СЛОВА КАК ЛИТЕРАЛ CMOVE A1,A2,U-> ПЕРЕСЛАТЬ U БАЙТОВ ОТ A1 В A2 21 CMOVE> A1,A2,U-> ПЕРЕСЛАТЬ U БАЙТОВ ОТ АДРЕСА A1 21 ПО АДРЕСУ А2 НАЧИНАЯ С БОЛЬШИХ АДРЕСОВ COMPILE K -> КОМПИЛИРОВАТЬ СЛЕДУЮЩИЙ АДРЕС 22 CONSTANT W-> ОПРЕДЕЛИТЬ СЛЕДУЮЩЕЕ СЛОВО 32 КАК КОНСТАНТУ СО ЗНАЧЕНИЕМ W CONTEXT П ->A ПЕРЕМЕННАЯ - СПИСОК, С КОТОРОГО 7 НАЧИНАЕТСЯ ПОИСК ВВОДИМЫХ СЛОВ CONVERT WD1,A1->WD2,A2 ПРЕОБРАЗОВАТЬ WD1 И ЛИТЕРЫ 39 ОТ А1+1 В WD2 И А2 - АДРЕС 1-ОЙ НЕ ЦИФРЫ COUNT T->A,N ДАТЬ АДРЕС ПЕРВОЙ ЛИТЕРЫ И ЧИСЛО 28 ЛИТЕР N СТРОКИ СО СЧЕТЧИКОМ T CR -> ВЫВЕСТИ НА ТЕРМИНАЛ ПЕРЕВОД СТРОКИ 6 CREATE -> СОЗДАТЬ НАЧАЛО СТАТЬИ (ДО PFA) ДЛЯ 36 СЛЕДУЮЩЕГО СЛОВА; ЕГО ИСПОЛНЕНИЕ КЛАДЕТ PFA НА СТЕК CREATE# A НАЧАЛО ИСПОЛНИТЕЛЬНОЙ ЧАСТИ "VARIABLE" 3 CSP *П ->A ПЕРЕМЕННАЯ ДЛЯ КОНТРОЛЬНОГО ХРАНЕНИЯ 8 ЗНАЧЕНИЯ УКАЗАТЕЛЯ СТЕКА CURRENT П ->A ПЕРЕМЕННАЯ - СПИСОК ДЛЯ ДОБАВЛЕНИЯ СЛОВ 7 D.


D-> НАПЕЧАТАТЬ D НА ТЕРМИНАЛЕ И ДАТЬ ПРОБЕЛ 38 D.R D,+N-> НАПЕЧАТАТЬ D В ПОЛЕ ДЛИНЫ +N СПРАВА 38 D< D1,D2->F F "ИСТИНА", ЕСЛИ D1 МЕНЬШЕ D2 15 D+ WD1,WD2->WD3 СУММА ДВОЙНЫХ ЧИСЕЛ WD1 И WD2 14 D- WD1,WD2->WD3 РАЗНОСТЬ ДВОЙНЫХ ЧИСЕЛ WD1-WD2 14 D/ * D1,D2->D3 ЧАСТНОЕ D3 ОТ ДЕЛЕНИЯ D1 НА D2 15 D/MOD * D1,D2->D3,D4 ОСТАТОК D3 И ЧАСТНОЕ D4 ОТ 15 ДЕЛЕНИЯ ДВОЙНЫХ ЧИСЕЛ D1 НА D2 D= WD1,WD2->F F "ИСТИНА", ЕСЛИ WD1 И WD2 РАВНЫ 15 DABS D1->D2 АБСОЛЮТНАЯ ВЕЛИЧИНА ДВОЙНОГО ЧИСЛА 14 DECIMAL -> ПЕРЕЙТИ В ДЕСЯТИЧНУЮ СИСТЕМУ 22 DEFINITIONS -> УСТАНОВИТЬ СПИСОК CURRENT НА CONTEXT 31 DEPTH ->+N КОЛИЧЕСТВО ЗНАЧЕНИЙ НА СТЕКЕ ДАННЫХ 20 DIGIT * C,N1->N2,TF/FF N2 - ЗНАЧЕНИЕ ЛИТЕРЫ С КАК 39 ЦИФРЫ В СИСТЕМЕ СЧИСЛЕНИЯ ПО ОСНОВАНИЮ N1 DMAX WD1,WD2->WD3 БОЛЬШЕЕ ИЗ ДВУХ ЧИСЕЛ 16 DMIN WD1,WD2->WD3 МЕНЬШЕЕ ИЗ ДВУХ ЧИСЕЛ 16 DMOD * D1,D2->D3 ОСТАТОК D3 ОТ ДЕЛЕНИЯ D1 НА D2 15 DNEGATE D1->D2 РЕЗУЛЬТАТ ВЫЧИТАНИЯ D1 ИЗ НУЛЯ 14 DO HK ->A1,A2,3 /КОМПИЛЯЦИЯ/ НАЧАЛО ЦИКЛА DO СО 47 N1,N2-> /ИСПОЛНЕНИЕ/ СЧЕТЧИКОМ ОТ N2 ДО N1 DOES> HK -> НАЧАЛО "ИСПОЛНЕНИЯ" В ОПРЕДЕЛЯЮЩЕМ СЛОВЕ 36 DOES# M ПОДПРОГРАММА - НАЧАЛО РАСШИРЕНИЯ DOES> 3 DP! * A-> УСТАНОВИТЬ ВЕРШИНУ СЛОВАРЯ НА АДРЕС А 10 DPL +П ->A ПЕРЕМЕННАЯ - ПОЗИЦИЯ ПОСЛЕДНЕЙ ТОЧКИ 8 В ПОСЛЕДНЕМ ВВЕДЕННОМ ЧИСЛЕ ОТ КОНЦА DROP W-> УБРАТЬ СО СТЕКА ВЕРХНЕЕ ЗНАЧЕНИЕ 9 DU< UD1,UD2->F F "ИСТИНА", ЕСЛИ UD1 МЕНЬШЕ UD2 14 DUMP + A,U-> РАСПЕЧАТАТЬ НА ТЕРМИНАЛЕ U БАЙТОВ 42 ОТ АДРЕСА А DUP W->W,W ПРОДУБЛИРОВАТЬ ВЕРХНЕЕ ЗНАЧЕНИЕ 9 D0< D->F F "ИСТИНА", ЕСЛИ D МЕНЬШЕ НУЛЯ 15 D0= WD->F F "ИСТИНА", ЕСЛИ WD НУЛЬ 15 D2/ D1->D2 РАЗДЕЛИТЬ НА ДВА 15 ELSE HK A1,2->A2,2 /КОМПИЛЯЦИЯ/ НАЧАЛО 2-ОЙ ВЕТВИ 47 -> /ИСПОЛНЕНИЕ/ ВЕТВЛЕНИЯ IF EMIT C-> ВЫВЕСТИ НА ТЕРМИНАЛ ЛИТЕРУ С КОДОМ С 6 EMPTY-BUFFERS + -> ОЧИСТИТЬ БУФЕРНЫЙ ПУЛ 25 ENCLOSE * A,C->A,N1,N2,N3 ВВОД СЛОВА 27 ERASE + A,U-> ЗАСЛАТЬ НУЛИ В U БАЙТОВ ПО АДРЕСУ А 22 ERCOND8 M СИГНАЛИЗАЦИЯ О НЕПРАВИЛЬНОМ ЗНАЧЕНИИ 4 EXECUTE CFA-> ИСПОЛНИТЬ СЛОВО ПО CFA ЕГО СТАТЬИ 11 EXIT K -> ЗАКОНЧИТЬ ИСПОЛНЕНИЕ ТЕКУЩЕГО ОПРЕДЕЛЕНИЯ 4 EXIT# M ТОЧКА "EXIT" АДРЕСНОГО ИНТЕРПРЕТАТОРА 4 EXPECT A,+N-> ВВЕСТИ С ТЕРМИНАЛА +N ЛИТЕР ПО 40 АДРЕСУ А; В ПЕРЕМЕННУЮ SPAN ЗАСЛАТЬ ФАКТИЧЕСКОЕ ЧИСЛО ВВЕДЕННЫХ ЛИТЕР; ЛИТЕРЫ НАПЕЧАТАТЬ НА ТЕРМИНАЛЕ FENCE *П ->A ПЕРЕМЕННАЯ - ГРАНИЦА ЗАЩИТЫ ОТ FORGET 7 FILL A,U,C-> ЗАСЛАТЬ С В U БАЙТОВ ПО АДРЕСУ А 22 FIND T->A,N ИСКАТЬ СЛОВО Т В ТЕКУЩЕМ КОНТЕКСТЕ 35 ЕСЛИ N=0, ТО А=Т И СЛОВО НЕ НАЙДЕНО; ИНАЧЕ А=CFA НАЙДЕННОЙ СТАТЬИ, N=1 ДЛЯ СЛОВ "IMMEDIATE" И N=-1 ДЛЯ ОСТАЛЬНЫХ FIRST * ->A КОНСТАНТА - АДРЕС НАЧАЛА БУФЕРНОГО ПУЛА 2 FIRST# М ЗНАЧЕНИЕ КОНСТАНТЫ FIRST 2 FL# A ПОЛЕ СВЯЗИ ДЛЯ СПИСКОВ В ПОЛЕ ПАРАМЕТРОВ 33 СЛОВАРНОЙ СТАТЬИ СЛОВА FORTH FLUSH -> ЗАПИСАТЬ БЛОКИ НА ДИСК И ОЧИСТИТЬ ПУЛ 26 FORGET -> УДАЛИТЬ СЛОВАРНУЮ СТАТЬЮ СЛЕДУЮЩЕГО СЛОВА 45 И ВСЕХ СЛОВ, ОПРЕДЕЛЕННЫХ ПОСЛЕ НЕГО FORTH -> УСТАНОВИТЬ CONTEXT НА НАЧАЛЬНЫЙ СПИСОК 33 FORTH-83 -> СТАНДАРТНЫЙ КОНТЕКСТ ФОРТ-СИСТЕМЫ 33 FORTH# A PFA+2 ДЛЯ СЛОВАРНОЙ СТАТЬИ FORTH 33 GOTO M ПОДПРОГРАММА ПЕРЕХОДА ПО ССЫЛКЕ 4 H. + U-> НАПЕЧАТАТЬ U НА ТЕРМИНАЛЕ 38 В 16-НОЙ СИСТЕМЕ И ДАТЬ ПРОБЕЛ HERE ->A ДАТЬ АДРЕС ТЕКУЩЕЙ ВЕРШИНЫ СЛОВАРЯ 10 HEX + -> ПЕРЕЙТИ В ШЕСТНАДЦАТИРИЧНУЮ СИСТЕМУ 22 HLD *П ->A ПЕРЕМЕННАЯ - ПОЗИЦИЯ ПОСЛЕДНЕЙ ЛИТЕРЫ, 8 ПЕРЕНЕСЕННОЙ В БУФЕР PAD ПО HOLD HOLD C-> ПЕРЕНЕСТИ ЛИТЕРУ С НА ВЕРШИНУ БУФЕРА PAD 37 I K ->W ТЕКУЩЕЕ ЗНАЧЕНИЕ W СЧЕТЧИКА ЦИКЛА DO 24 I' +K ->W КОНЕЧНОЕ ЗНАЧЕНИЕ W СЧЕТЧИКА ЦИКЛА DO 24 ID. * NFA-> НАПЕЧАТАТЬ ИМЯ СЛОВА И ДАТЬ ПРОБЕЛ 31 IF HK ->A,2 /КОМПИЛЯЦИЯ/ НАЧАЛО ВЕТВЛЕНИЯ IF 47 F-> /ИСПОЛНЕНИЕ/ IMMEDIATE -> ДАТЬ ПРИЗНАК IMMEDIATE ПОСЛЕДНЕЙ 31 СОЗДАННОЙ СЛОВАРНОЙ СТАТЬЕ INDEX + N1,N2-> РАСПЕЧАТАТЬ НАЧАЛЬНУЮ СТРОКУ ЭКРАНОВ 46 С НОМЕРАМИ ОТ N1 ДО N2 INTERPRET + -> ИНТЕРПРЕТИРОВАТЬ ВХОДНОЙ ПОТОК 40 IPUSH M ПОДПРОГРАММА - ПОМЕСТИТЬ НА СТЕК УКАЗАТЕЛЬ 4 ИНТЕРПРЕТАЦИИ И ОБОЙТИ СЛЕДУЮЩУЮ СТРОКУ J K ->W ТЕКУЩЕЕ ЗНАЧЕНИЕ W СЧЕТЧИКА ВТОРОГО 24 ОБЪЕМЛЮЩЕГО ЦИКЛА DO KEY ->C ВВЕСТИ ЛИТЕРУ С ТЕРМИНАЛА 6 L>NAME * LFA->NFA ПЕРЕЙТИ ОТ ПОЛЯ СВЯЗИ К ПОЛЮ ИМЕНИ 30 LATEST * ->NFA ДАТЬ NFA ПОСЛЕДНЕЙ СОЗДАННОЙ СТАТЬИ 31 LEAVE K -> ЗАКОНЧИТЬ ИСПОЛНЕНИЕ ЦИКЛА DO 24 LENGMASK M ПОЛНОЕ СЛОВО - МАСКА ДЛЯ УДАЛЕНИЯ 2 БИТА IMMEDIATE ИЗ БАЙТА ДЛИНЫ LENG1MSK M ПОЛНОЕ СЛОВО - МАСКА ДЛЯ УДАЛЕНИЯ 2 БИТОВ IMMEDIATE И SMUDGE ИЗ БАЙТА ДЛИНЫ LENG2MSK M ПОЛНОЕ СЛОВО - МАСКА ДЛЯ ВЫСЕЧЕНИЯ 2 ЧИСТОЙ ДЛИНЫ ИЗ БАЙТА ДЛИНЫ С ПРИЗНАКАМИ LHRW12 M ПОДПРОГРАММА ЗАГРУЗКИ ДВУХ ВЕРХНИХ ЗНАЧЕНИЙ 4 НА СТЕКЕ В РЕГИСТРЫ RW2 (ВЕРХНЕЕ) И RW1 LIMIT * ->A КОНСТАНТА - АДРЕС КОНЦА БУФЕРНОГО ПУЛА 2 LIMIT# M ЗНАЧЕНИЕ КОНСТАНТЫ LIMIT 2 LINK> * LFA->CFA ПЕРЕЙТИ ОТ ПОЛЯ СВЯЗИ К ПОЛЮ КОДА 30 LIST + N-> РАСПЕЧАТАТЬ НА ТЕРМИНАЛЕ ЭКРАН N 46 LIT *K ->W ПОМЕСТИТЬ НА СТЕК СЛЕДУЮЩИЙ КОД 23 LIT" *K ->T ДАТЬ АДРЕС СКОМПИЛИРОВАННОЙ СТРОКИ И 28 ПРОДОЛЖИТЬ ИНТЕРПРЕТАЦИЮ, ОБОЙДЯ ЕЕ LITERAL H W-> /КОМПИЛЯЦИЯ/ СКОМПИЛИРОВАТЬ W КАК 23 ->W /ИСПОЛНЕНИЕ/ ЛИТЕРАЛ LOAD +N-> ИНТЕРПРЕТИРОВАТЬ ЭКРАН С НОМЕРОМ +N 41 LOOP HK A1,A2,3-> /КОМПИЛЯЦИЯ/ КОНЕЦ ЦИКЛА"DO LOOP" 47 -> /ИСПОЛНЕНИЕ/ LRW1 M ПОДПРОГРАММА ЗАГРУЗКИ ДВОЙНОГО ЗНАЧЕНИЯ НА 4 ВЕРШИНЕ СТЕКА В РЕГИСТР RW1 LRW12 M ПОДПРОГРАММА ЗАГРУЗКИ ДВУХ ВЕРХНИХ ДВОЙНЫХ 4 ЗНАЧЕНИЙ НА СТЕКЕ В РЕГИСТРЫ RW2 (ВЕРХНЕЕ) И RW1 M* * N1,N2->D ПРОИЗВЕДЕНИЕ ДВОЙНОЙ ДЛИНЫ N1 И N2 18 M/ * D,N1->N2,N3 ОСТАТОК N2 И ЧАСТНОЕ N3 ОТ 18 ДЕЛЕНИЯ ДВОЙНОГО D НА ОДИНАРНОЕ N1 M/MOD * UD1,U2->U3,UD4 ОСТАТОК U3 И ДВОЙНОЕ 16 ЧАСТНОЕ UD4 ОТ ДЕЛЕНИЯ UD1 НА U2 MAX N1,N2->N3 БОЛЬШЕЕ ИЗ ЧИСЕЛ N1 И N2 22 MIN N1,N2->N3 МЕНЬШЕЕ ИЗ ЧИСЕЛ N1 И N2 22 MOD N1,N2->N3 ОСТАТОК ОТ ДЕЛЕНИЯ N1 НА N2 18 MSG * ->A КОНСТАНТА - АДРЕС НАЧАЛА БУФЕРА MSG 2 MSG# M ЗНАЧЕНИЕ КОНСТАНТЫ MSG 2 N>LINK * NFA->LFA ПЕРЕЙТИ ОТ ПОЛЯ ИМЕНИ К ПОЛЮ СВЯЗИ 30 NAME> * NFA->CFA ПЕРЕЙТИ ОТ ПОЛЯ ИМЕНИ К ПОЛЮ КОДА 30 NEGATE W1->W2 РЕЗУЛЬТАТ ВЫЧИТАНИЯ W1 ИЗ НУЛЯ 17 NEXT M ВХОД В АДРЕСНЫЙ ИНТЕРПРЕТАТОР 1 NEXT1 M ПРОДОЛЖЕНИЕ АДРЕСНОЙ ИНТЕРПРЕТАЦИИ ОТ 1 ФОРТ-АДРЕСА В РЕГИСТРЕ 14 NOT W1->W2 ПОРАЗРЯДНОЕ ИНВЕРТИРОВАНИЕ 13 NUMBER + T->WD ПРЕОБРАЗОВАТЬ СТРОКУ Т В ЧИСЛО WD 39 OFFSET +П ->A ПЕРЕМЕННАЯ - ДОБАВКА К НОМЕРУ БЛОКА 8 OR W1,W2->W3 ПОРАЗРЯДНОЕ ЛОГИЧЕСКОЕ "ИЛИ" 13 OVER W1,W2->W1,W2,W1 ПРОДУБЛИРОВАТЬ ВТОРОЕ СВЕРХУ 9 PAD ->A ДАТЬ АДРЕС ТЕКУЩЕЙ ВЕРШИНЫ БУФЕРА PAD 37 PICK WN,...,W0,+N->WN,...,W0,WN ПРОДУБЛИРОВАТЬ 12 N-Е СВЕРХУ ЗНАЧЕНИЕ POP M ВХОД В АДРЕСНЫЙ ИНТЕРПРЕТАТОР СО СНЯТИЕМ 3 ВЕРХНЕГО ЗНАЧЕНИЯ С ВЕРШИНЫ СТЕКА POPPUT1 M ВХОД В АДРЕСНЫЙ ИНТЕРПРЕТАТОР СО СНЯТИЕМ 3 ВЕРХНЕГО И ЗАМЕНОЙ ПРЕДЫДУЩЕГО НА ЗНАЧЕНИЕ ИЗ РЕГИСТРА RW1 PREV *C ->A ПЕРЕМЕННАЯ - ТЕКУЩИЙ БЛОЧНЫЙ БУФЕР 7 PUSHRW1 M ВХОД В АДРЕСНЫЙ ИНТЕРПРЕТАТОР С ПОМЕЩЕНИЕМ 3 ЗНАЧЕНИЯ ИЗ РЕГИСТРА RW1 НА ВЕРШИНУ СТЕКА PUSH2RW1 M ВХОД В АДРЕСНЫЙ ИНТЕРПРЕТАТОР С ЗАМЕНОЙ 3 ВЕРХНЕГО НА ДВОЙНОЕ ЗНАЧЕНИЕ ИЗ РЕГИСТРА RW1 PUTRW1 M ВХОД В АДРЕСНЫЙ ИНТЕРПРЕТАТОР С ЗАМЕНОЙ 3 ВЕРХНЕГО ЗНАЧЕНИЯ НА ЗНАЧЕНИЕ ИЗ RW1 QUERY + -> ВВЕСТИ С ТЕРМИНАЛА ЛИТЕРЫ В БУФЕР TIB; 40 ЧИСЛО ВВЕДЕННЫХ ЛИТЕР ЗАСЛАТЬ В #TIB QUIT -> СБРОСИТЬ СТЕК ВОЗВРАТОВ, ПЕРЕЙТИ В РЕЖИМ 28 ИСПОЛНЕНИЯ И ПРОДОЛЖИТЬ ИНТЕРПРЕТАЦИЮ R. * -> РАСПЕЧАТАТЬ НА ТЕРМИНАЛЕ СТЕК ВОЗВРАТОВ 42 R> K ->W ПЕРЕНЕСТИ ЗНАЧЕНИЕ СО СТЕКА ВОЗВРАТОВ 9 R@ K ->W СКОПИРОВАТЬ ВЕРШИНУ СТЕКА ВОЗВРАТОВ 9 RBLK * A,+N-> ПРОЧЕСТЬ ЭКРАН +N ПО АДРЕСУ A 6 RDROP *K -> СНЯТЬ ЗНАЧЕНИЕ СО СТЕКА ВОЗВРАТОВ 9 RECURSE +HK -> СКОМПИЛИРОВАТЬ ОБРАЩЕНИЕ К КОМПИЛИРУЕМОМУ 31 В ДАННЫЙ МОМЕНТ ОПРЕДЕЛЕНИЮ REMEMBER + -> ОПРЕДЕЛИТЬ СЛОВО, ИСПОЛНЕНИЕ КОТОРОГО 45 УНИЧТОЖАЕТ ВСЕ ПОСЛЕДУЮЩИЕ ОПРЕДЕЛЕНИЯ REPEAT HK A1,1,A2,2-> /КОМПИЛЯЦИЯ/ КОНЕЦ ЦИКЛА 47 -> /ИСПОЛНЕНИЕ/ BEGIN WHILE REPEAT ROLL WN,WN-1,...,W0,+N->WN-1,...,W0,WN ЦИКЛИЧЕСКИ 12 ПЕРЕСТАВИТЬ N ВЕРХНИХ ЗНАЧЕНИЙ ROT W1,W2,W3->W2,W3,W1 ПЕРЕСТАВИТЬ ТРИ 9 ВЕРХНИХ ЗНАЧЕНИЯ ПО ЧАСОВОЙ СТРЕЛКЕ RP! * A-> УСТАНОВИТЬ УКАЗАТЕЛЬ ВЕРШИНЫ СТЕКА 20 ВОЗВРАТОВ НА А RP@ * ->A АДРЕС ТЕКУЩЕЙ ВЕРШИНЫ СТЕКА ВОЗВРАТОВ 20 R0 *C ->A ПЕРЕМЕННАЯ - АДРЕС ДНА СТЕКА ВОЗВРАТОВ 7 S. * -> РАСПЕЧАТАТЬ НА ТЕРМИНАЛЕ СТЕК ДАННЫХ 42 S>D * N->D РАСШИРИТЬ N ДО ЧИСЛА ДВОЙНОЙ ДЛИНЫ D 14 SAVE-BUFFERS -> ЗАПИСАТЬ НА ДИСК ВСЕ ИСПРАВЛЕННЫЕ БЛОКИ 26 SCR +П ->A ПЕРЕМЕННАЯ - НОМЕР ЭКРАНА В LIST 8 SIGN N-> ДОБАВИТЬ В ФОРМАТНУЮ СТРОКУ ЗНАК 37 МИНУС, ЕСЛИ ЧИСЛО N ОТРИЦАТЕЛЬНО SMUDGE * -> УСТАНОВИТЬ В ЕДИНИЦУ ФЛАГ SMUDGE 31 В ПОСЛЕДНЕЙ СОЗДАННОЙ СТАТЬЕ SNAPSTK * A1,A2,A3-> РАСПЕЧАТКА СТЕКА ОТ А1 ДО А2 42 С ТЕКСТОМ А3; ВОЗВРАТ "ЧЕРЕЗ ОДИН" SP! * A-> УСТАНОВИТЬ УКАЗАТЕЛЬ ВЕРШИНЫ СТЕКА НА А 20 SP@ + ->A АДРЕС ТЕКУЩЕЙ ВЕРШИНЫ СТЕКА ДАННЫХ 20 SPACE -> НАПЕЧАТАТЬ НА ТЕРМИНАЛЕ ПРОБЕЛ 23 SPACES +N-> НАПЕЧАТАТЬ НА ТЕРМИНАЛЕ +N ПРОБЕЛОВ 23 SPAN П ->A ПЕРЕМЕННАЯ ДЛЯ РЕЗУЛЬТАТА EXPECT 8 STATE П ->A ПЕРЕМЕННАЯ С СОСТОЯНИЕМ ТЕКСТОВОГО 8 ИНТЕРПРЕТАТОРА: "ИСТИНА" - КОМПИЛЯЦИЯ SWAP W1,W2->W2,W1 ОБМЕНЯТЬ МЕСТАМИ 2 ВЕРХНИХ 9 S0 +П ->A ПЕРЕМЕННАЯ - АДРЕС ДНА СТЕКА ДАННЫХ 7 TEMP M РАБОЧАЯ ОБЛАСТЬ ИЗ ДВУХ ДВОЙНЫХ СЛОВ 2 THEN HK A,2-> /КОМПИЛЯЦИЯ/ КОНЕЦ ВЕТВЛЕНИЯ IF 47 -> /ИСПОЛНЕНИЕ/ THRU + +N1,+N2-> ИНТЕРПРЕТИРОВАТЬ ЭКРАНЫ С НОМЕРАМИ 41 ОТ +N1 ДО +N2 ВКЛЮЧИТЕЛЬНО TIB ->A АДРЕС ВХОДНОГО ТЕКСТОВОГО БУФЕРА 2 ДЛЯ ВВОДА С ТЕРМИНАЛА TIB# M ФОРТ-АДРЕС НАЧАЛА БУФЕРА TIB 2 TYPE A,+N-> НАПЕЧАТАТЬ НА ТЕРМИНАЛЕ +N ЛИТЕР 6 ОТ АДРЕСА А U.


U-> НАПЕЧАТАТЬ U НА ТЕРМИНАЛЕ КАК 38 ЧИСЛО БЕЗ ЗНАКА U.R + U,+N-> НАПЕЧАТАТЬ НА ТЕРМИНАЛЕ ЧИСЛО U 38 В ПОЛЕ ДЛИНЫ +N СПРАВА U< U1,U2->F F "ИСТИНА", ЕСЛИ U1 МЕНЬШЕ U2 16 UM* U1,U2->UD ПРОИЗВЕДЕНИЕ UD ЧИСЕЛ U1 И U2 16 UM/MOD UD,U1->U2,U3 ОСТАТОК U2 И ЧАСТНОЕ U3 ОТ 16 ДЕЛЕНИЯ UD НА U1 UNSMUDGE * -> УСТАНОВИТЬ В НУЛЬ ФЛАГ SMUDGE 31 В ПОСЛЕДНЕЙ СОЗДАННОЙ СТАТЬЕ UNTIL HK A,1-> /КОМПИЛЯЦИЯ/ КОНЕЦ ЦИКЛА "BEGIN UNTIL" 47 F-> /ИСПОЛНЕНИЕ/ UPDATE -> ОТМЕТИТЬ ТЕКУЩИЙ БЛОК КАК ИЗМЕНЕННЫЙ 25 USE *C ->A ПЕРЕМЕННАЯ - СЛЕДУЮЩИЙ БЛОЧНЫЙ БУФЕР 7 VARIABLE -> ОПРЕДЕЛИТЬ СЛЕДУЮЩЕЕ СЛОВО КАК 32 ПЕРЕМЕННУЮ С НАЧАЛЬНЫМ ЗНАЧЕНИЕМ НУЛЬ VOC-LINK *П ->A ПЕРЕМЕННАЯ - АДРЕС ПОЛЯ СВЯЗИ ПОСЛЕДНЕГО 33 СОЗДАННОГО ПО VOCABULARY СПИСКА СЛОВ VOCABULARY -> ОПРЕДЕЛИТЬ СЛЕДУЮЩЕЕ СЛОВО КАК 33 СПИСОК НАД ТЕКУЩИМ ЗНАЧЕНИЕМ CURRENT VOCABULARY# A НАЧАЛО ИСПОЛНИТЕЛЬНОЙ ЧАСТИ VOCABULARY 33 VOCS * -> РАСПЕЧАТАТЬ НА ТЕРМИНАЛЕ ТЕКУЩИЙ 43 ПОРЯДОК ПОИСКА СЛОВ В СЛОВАРЕ WBLK * A,+N-> ЗАПИСАТЬ ЭКРАН +N ИЗ АДРЕСА А 6 WHILE HK 1->A,2 /КОМПИЛЯЦИЯ/ ВЕТВЛЕНИЕ WHILE В 47 F-> /ИСПОЛНЕНИЕ/ ЦИКЛЕ "BEGIN WHILE REPEAT" WIDTH * ->N КОНСТАНТА - МАКСИМАЛЬНАЯ ДЛИНА ИМЕНИ 7 WORD C->T ВВЕСТИ СЛОВО ДО СТОП-ЛИТЕРЫ С; 27 ДАТЬ ЕГО АДРЕС КАК СТРОКИ СО СЧЕТЧИКОМ WORDS + -> РАСПЕЧАТАТЬ НА ТЕРМИНАЛЕ ИМЕНА СЛОВ 44 ИЗ СПИСКА CONTEXT XOR W1,W2->W3 ПОРАЗРЯДНОЕ "ИСКЛЮЧАЮЩЕЕ ИЛИ" 13 0 * ->0 КОНСТАНТА НУЛЬ (ЗНАЧЕНИЕ "ЛОЖЬ") 7 0< N->F F "ИСТИНА", ЕСЛИ N ОТРИЦАТЕЛЬНО 13 0<> * W->F F "ИСТИНА", ЕСЛИ W НЕ НУЛЬ 19 0! * A-> ЗАСЛАТЬ НУЛЬ ПО АДРЕСУ А 11 0= W->F F "ИСТИНА", ЕСЛИ W РАВНО НУЛЮ 13 1+ W1->W2 УВЕЛИЧИТЬ W1 НА 1 17 1+! + A-> УВЕЛИЧИТЬ НА 1 ЗНАЧЕНИЕ ПО АДРЕСУ А 17 1- W1->W2 УМЕНЬШИТЬ W1 НА 1 17 2+ W1->W2 УВЕЛИЧИТЬ W1 НА 2 17 2! WD,A-> ЗАСЛАТЬ ДВОЙНОЕ WD ПО АДРЕСУ A 20 2* + W1->W2 АРИФМЕТИЧЕСКИЙ СДВИГ ВЛЕВО НА 1 20 2- W1->W2 УМЕНЬШИТЬ W1 НА 2 17 2/ W1->W2 АРИФМЕТИЧЕСКИЙ СДВИГ ВПРАВО НА 1 20 2@ A->WD ДАТЬ ДВОЙНОЕ ЗНАЧЕНИЕ ПО АДРЕСУ А 20 2CONSTANT WD-> ОПРЕДЕЛИТЬ СЛЕДУЮЩЕЕ СЛОВО КАК 32 КОНСТАНТУ СО ЗНАЧЕНИЕМ WD 2DROP WD-> СНЯТЬ ВЕРХНЕЕ ДВОЙНОЕ ЗНАЧЕНИЕ 12 2DUP WD->WD,WD ПРОДУБЛИРОВАТЬ ДВОЙНОЕ ЗНАЧЕНИЕ 12 2LIT *K ->WD ПОМЕСТИТЬ НА СТЕК СЛЕДУЮЩИЕ 2 КОДА 23 2LITERAL *H WD-> /КОМПИЛЯЦИЯ/ СКОМПИЛИРОВАТЬ WD КАК 23 ->WD /ИСПОЛНЕНИЕ/ ЛИТЕРАЛ 2OVER WD1,WD2->WD1,WD2,WD1 ПРОДУБЛИРОВАТЬ ВТОРОЕ 12 ДВОЙНОЕ СВЕРХУ 2POP M ВХОД В АДРЕСНЫЙ ИНТЕРПРЕТАТОР СО СНЯТИЕМ 3 ДВОЙНОГО ЗНАЧЕНИЯ С ВЕРШИНЫ СТЕКА 2POPPUT1 M ВХОД В АДРЕСНЫЙ ИНТЕРПРЕТАТОР СО СНЯТИЕМ 3 ДВОЙНОГО ВЕРХНЕГО ЗНАЧЕНИЯ СО СТЕКА И ЗАМЕНОЙ ПРЕДЫДУЩЕГО ДВОЙНОГО НА 4-БАЙТНОЕ ЗНАЧЕНИЕ ИЗ РЕГИСТРА RW1 2PUSHRW1 M ВХОД В АДРЕСНЫЙ ИНТЕРПРЕТАТОР С ПОМЕЩЕНИЕМ 3 ДВОЙНОГО ЗНАЧЕНИЯ ИЗ RW1 НА ВЕРШИНУ СТЕКА 2PUTRW1 M ВХОД В АДРЕСНЫЙ ИНТЕРПРЕТАТОР С ЗАМЕНОЙ 3 ДВОЙНОГО ВЕРХНЕГО ЗНАЧЕНИЯ НА 4-БАЙТНОЕ ЗНАЧЕНИЕ ИЗ РЕГИСТРА RW1 2ROT WD1,WD2,WD3->WD2,WD3,WD1 ПЕРЕСТАВИТЬ ТРИ 12 ВЕРХНИХ ДВОЙНЫХ ПО ЧАСОВОЙ СТРЕЛКЕ 2SWAP WD1,WD2->WD2,WD1 ОБМЕНЯТЬ МЕСТАМИ ДВА 12 ВЕРХНИХ ДВОЙНЫХ ЗНАЧЕНИЯ 2VARIABLE -> ОПРЕДЕЛИТЬ СЛЕДУЮЩЕЕ СЛОВО КАК ПЕРЕМЕННУЮ 32 ДВОЙНОЙ ДЛИНЫ С НАЧАЛЬНЫМ ЗНАЧЕНИЕМ НУЛЬ ФОРТ-СИСТЕМА * -> ТЕКСТОВЫЙ ИНТЕРПРЕТАТОР ФОРТ-СИСТЕМЫ 40



Экран номер 1

( 09.09.86 НАЧАЛО МОДЕЛИ ФОРТ-СИСТЕМЫ ) DECIMAL ( КОНСТАНТЫ ПЕРИОДА КОМПИЛЯЦИИ ) 128 CONSTANT &IFLAG ( ПРИЗНАК "IMMEDIATE") 32 CONSTANT &SFLAG ( ПРИЗНАК "SMUDGE") 31 CONSTANT &LENG ( МАСКА ДЛЯ ВЫСЕЧЕНИЯ ДЛИНЫ) &SFLAG 256 * 64 + CONSTANT &DWORD ( ФИКТИВНОЕ ИМЯ ) ( НАЧАЛЬНОЕ ЯДРО С АДРЕСАЦИЕЙ ОТ РЕГИСТРА RFORTH) START-CODE *, RFORTH USING, ( АДРЕСНЫЙ ИНТЕРПРЕТАТОР) M: NEXT 14 0 (, RI RFORTH LH, RI RTWO AR, M: NEXT1 14 RMASK NR, 15 0 (, 14 RFORTH LH, 15 RMASK NR, 15 RFORTH AR, 14 RTWO AR, 15 BR,

Экран номер 2

( 09.09.86 СИСТЕМНЫЕ ПЕРЕМЕННЫЕ И КОНСТАНТЫ ) CONST MSG M: MSG# 0 H, ( АДРЕС НАЧАЛА БУФЕРА MSG) CONST FIRST M: FIRST# 0 H, ( АДРЕС НАЧАЛА ПУЛА) CONST LIMIT M: LIMIT# 0 H, ( АДРЕС КОНЦА ПУЛА) CONST TIB M: TIB# 0 H, ( АДРЕС НАЧАЛА БУФЕРА TIB) 4 ALIGN M: LENGMASK 255 &IFLAG - S>D F, ( БАЙТ ДЛИНЫ БЕЗ IMMEDIATE) M: LENG1MSK 255 &IFLAG - &SFLAG - S>D F, ( БЕЗ IMMD И SMDG) M: LENG2MSK &LENG S>D F, ( БАЙТ ДЛИНЫ С ЧИСТОЙ ДЛИНОЙ) 8 ALIGN M: TEMP 16 ALLOT ( РАБОЧАЯ ОБЛАСТЬ)

Экран номер 3

( 09.09.86 ДОПОЛНИТЕЛЬНЫЕ ВХОДЫ В АДРЕСНЫЙ ИНТЕРПРЕТАТОР) M: DOES# RI RPUSH, RI 4 (, 15 LA, RI RFORTH SR, A: CREATE# RW1 14 LR, ( ПОМЕСТИТЬ PFA СТАТЬИ) M: PUSHRW1 RSTACK RTWO SR, ( ПОМЕСТИТЬ ЗНАЧЕНИЕ ИЗ RW1) M: PUTRW1 RW1 PUT, RNEXT BR, ( ЗАМЕНИТЬ ВЕРХНЕЕ) M: 2POP RSTACK RTWO AR, ( СНЯТЬ ДВА ВЕРХНИХ) M: POP RSTACK RTWO AR, RNEXT BR, ( СНЯТЬ ВЕРХНЕЕ) M: POPPUT1 RSTACK RTWO AR, ( СНЯТЬ ВЕРХНЕЕ И ЗАМЕНИТЬ) RW1 PUT, RNEXT BR, ( ЗНАЧЕНИЕМ ИЗ RW1) M: 2PUSHRW1 RSTACK RTWO SR, ( ПОЛОЖИТЬ ДВОЙНОЕ НА СТЕК) M: PUSH2RW1 RSTACK RTWO SR, ( ЗАМЕНИТЬ ВЕРХНЕЕ НА ДВОЙНОЕ) M: 2PUTRW1 RW1 TEMP ST, ( ЗАМЕНИТЬ ДВОЙНОЕ ВЕРХНЕЕ) FIRST (, 4 ), TEMP MVC, RNEXT BR, M: 2POPPUT1 RSTACK RTWO AR, RSTACK RTWO AR, 2PUTRW1 B,

Экран номер 4

( 09.09.86 ВСПОМОГАТЕЛЬНЫЕ ПОДПРОГРАММЫ: ВОЗВРАТ В РЕГ.14) M: LHRW12 RW1 SECOND LH, RW2 PULL, 14 BR, M: LRW1 TEMP (, 4 ), FIRST MVC, RW1 TEMP L, 14 BR, M: LRW12 TEMP (, 8 ), FIRST MVC, RW1 TEMP 4 +(, L, RW2 TEMP L, 14 BR, M: GOTO 14 0 (, 0 14 LH, NEXT1 B, M: IPUSH RI PUSH, RW2 RW2 SR, RW2 0 (, RI RFORTH IC, RI 2 (, RI RW2 LA, 14 BR, CODE EXIT M: EXIT# RI RPOP, RI RMASK NR, RNEXT BR, END-CODE M: ERCOND8 14 GOTO BAL, ] ABORT8 [



Экран номер 5

( 09.09.86 BRANCH ?BRANCH (LOOP/ (+LOOP/ ) CODE BRANCH M: BRANCH# RI 0 (, RI RFORTH LH, RI RMASK NR, RNEXT BR, CODE ?BRANCH RW1 POP, RW1 RW1 LTR, BRANCH# BZ, RI RTWO AR, RNEXT BR, CODE (LOOP) RW1 1 LA, 1 =F B, CODE (+LOOP) RW1 POP, 1 =H 0 RFIRST LH, 0 RSECOND SH, 0 RMASK NR, 0 RW1 AR, RW1 RFIRST AH, RW1 RFIRST STH, 0 RMASK CLR, BRANCH# BNH, RRET 6 (, 0 RRET LA, RI RTWO AR, RNEXT BR, END-CODE

Экран номер 6

( 09.09.86 KEY CR EMIT TYPE (EXPECT/ RBLK WBLK ) ( СЛЕДУЮЩИЕ ОПРЕДЕЛЕНИЯ ДАЮТ ТОЛЬКО ИМЕНА ПРОЦЕДУР) CODE KEY ( ->C ВВЕСТИ ЛИТЕРУ С ТЕРМИНАЛА) END-CODE CODE CR ( -> ВЫВЕСТИ ПЕРЕВОД СТРОКИ ) END-CODE CODE EMIT ( C-> ВЫВЕСТИ ЛИТЕРУ С КОДОМ С НА ТЕРМИНАЛ) END-CODE CODE TYPE ( A,N-> ВЫВЕСТИ НА ТЕРМИНАЛ N ЛИТЕР ПО АДРЕСУ А) END-CODE CODE (EXPECT) ( A,N1->A,N2 ВВЕСТИ С ТЕРМИНАЛА НЕ БОЛЕЕ N1 ЛИТЕР /ДО ПЕРЕВОДА СТРОКИ/ В БУФЕР ПО АДРЕСУ А; N2 - ФАКТИЧЕСКОЕ ЧИСЛО ВВЕДЕННЫХ ЛИТЕР) END-CODE CODE RBLK ( A,N-> ПРОЧИТАТЬ ЭКРАН N В БУФЕР А) END-CODE CODE WBLK ( A,N-> ЗАПИСАТЬ ЭКРАН N ИЗ БУФЕРА А) END-CODE

Экран номер 7

( 09.09.86 КОНСТАНТЫ И СИСТЕМНЫЕ ПЕРЕМЕННЫЕ ) 64 CONSTANT BL ( КОД ПРОБЕЛА) 1024 CONSTANT B/BUF ( ДЛИНА БУФЕРА ДЛЯ ЭКРАНА) &LENG CONSTANT WIDTH ( МАКСИМАЛЬНАЯ ДЛИНА СЛОВА ) 0 CONSTANT 0 ( ЧИСЛО НОЛЬ) VARIABLE USE ( ТЕКУЩИЙ БУФЕР В ПУЛЕ) VARIABLE PREV ( СЛЕДУЮЩИЙ БУФЕР В ПУЛЕ) VARIABLE S0 ( АДРЕС ДНА СТЕКА ДАННЫХ) VARIABLE R0 ( АДРЕС ДНА СТЕКА ВОЗВРАТОВ) VARIABLE FENCE ( ГРАНИЦА ЗАЩИТЫ ОТ "FORGET") VARIABLE CONTEXT ( ТЕКУЩИЙ СПИСОК - НАЧАЛО ПОИСКА) VARIABLE CURRENT ( ТЕКУЩИЙ СПИСОК - КУДА ДОБАВЛЯЕМ)

Экран номер 8

( 09.09.86 СИСТЕМНЫЕ ПЕРЕМЕННЫЕ - ОКОНЧАНИЕ) VARIABLE OFFSET ( ДОБАВКА К НОМЕРУ ЭКРАНА) VARIABLE BASE ( ОСНОВАНИЕ СИСТЕМЫ СЧИСЛЕНИЯ) VARIABLE STATE ( СОСТОЯНИЕ ТЕКСТОВОГО ИНТЕРПРЕТАТОРА) VARIABLE DPL ( ПОЗИЦИЯ ДЕСЯТИЧНОЙ ТОЧКИ В ЧИСЛЕ) VARIABLE CSP ( ДЛЯ КОНТРОЛЬНОГО ХРАНЕНИЯ УКАЗАТЕЛЯ) VARIABLE HLD ( УКАЗАТЕЛЬ ВЕРШИНЫ БУФЕРА "PAD") VARIABLE BLK ( НОМЕР ВХОДНОГО ЭКРАНА ИЛИ НОЛЬ) VARIABLE >IN ( ПОЗИЦИЯ ОЧЕРЕДНОЙ ЛИТЕРЫ НА ВХОДЕ) VARIABLE SPAN ( ЧИСЛО ЛИТЕР, ВВЕДЕННЫХ ПО "EXPECT") VARIABLE #TIB ( ЧИСЛО ЛИТЕР, ВВЕДЕННЫХ В БУФЕР "TIB") VARIABLE SCR ( НОМЕР ЭКРАНА, РАСПЕЧАТАННОГО В "LIST")



Экран номер 9

( 31.03.86 DUP ?DUP DROP SWAP OVER >R R> R@ RDROP ROT ) CODE DUP ( W->W,W) RW1 PULL, PUSHRW1 B, END-CODE : ?DUP ( W->W,W; 0->0 ) DUP IF DUP THEN ; CODE DROP ( W-> ) RSTACK RTWO AR, RNEXT BR, END-CODE CODE SWAP ( W1,W2->W2,W1) 14 LHRW12 BAL, RW2 SECOND STH, PUTRW1 B, END-CODE CODE OVER ( W1,W2->W1,W2,W1) RW1 SECOND LH, PUSHRW1 B, END-CODE CODE >R ( W-> ) RW1 POP, RW1 RPUSH, RNEXT BR, END-CODE CODE R> ( ->W) RW1 RPOP, PUSHRW1 B, END-CODE CODE R@ ( ->W) RW1 RPULL, PUSHRW1 B, END-CODE CODE RDROP ( -> ) RRET RTWO AR, RNEXT BR, END-CODE : ROT ( N1,N2,N3->N2,N3,N1 ) >R SWAP R> SWAP ;

Экран номер 10

( 31.03.86 HERE ALLOT ALIGN ALIGNH DP! ) CODE HERE ( ->A ) RW1 RD LR, PUSHRW1 B, END-CODE CODE ALLOT ( N-> ) RD FIRST AH, POP B, END-CODE CODE ALIGN ( N-> ) RW1 0 (, RD RFORTH LA, 0 (, RW1 0 MVI, 1 (, 7 RW1 ), 0 (, RW1 MVC, RW1 PULL, RW2 RW1 LCR, RD RW1 AR, RD 0 BCTR, RD RW2 NR, POP B, END-CODE : ALIGNH ( -> ) 2 ALIGN ; CODE DP! ( A-> ) RD PULL, RD RMASK NR, POP B, END-CODE

Экран номер 11

( 31.03.86 ! 0! @ C! C@ , C, EXECUTE ) CODE ! ( W,A-> ЗАСЛАТЬ W ПО АДРЕСУ А) 14 LHRW12 BAL, RW2 RMASK NR, RW1 0 (, RW2 RFORTH STH, 2POP B, END-CODE : 0! ( A-> ) 0 SWAP ! ; CODE @ ( A->W РАЗЫМЕНОВАТЬ А) RW2 PULL, RW2 RMASK NR, RW1 0 (, RW2 RFORTH LH, PUTRW1 B, END-CODE CODE C@ ( A->C) RW2 PULL, RW2 RMASK NR, RW1 RW1 SR, RW1 0 (, RW2 RFORTH IC, PUTRW1 B, END-CODE CODE C! ( C,A-> ) 14 LHRW12 BAL, RW2 RMASK NR, RW1 0 (, RW2 RFORTH STC, 2POP B, END-CODE : , ( W-> ) HERE 2 ALLOT ! ; : C, ( C-> ) HERE 1 ALLOT C! ; CODE EXECUTE ( CFA-> ) 14 POP, NEXT1 B, END-CODE

Экран номер 12

( 31.03.86 ROLL PICK 2DUP 2DROP 2SWAP 2OVER 2ROT ) CODE ROLL ( WN,WN-1,...,W0,+N->WN-1,...,W0,WN) RW2 PULL, RW2 RW2 AR, ERCOND8 BM, RW1 SECOND (, RW2 LH, BEGIN, 0 FIRST (, RW2 LH, 0 SECOND (, RW2 STH, RW2 RTWO SR, ?NP UNTIL, POPPUT1 B, END-CODE CODE PICK ( WN,...,W0,+N->WN,...,W0,WN) RW2 PULL, RW2 RW2 AR, ERCOND8 BM, RW1 2 (, RW2 RSTACK LH, PUTRW1 B, END-CODE : 2DUP ( WD->WD,WD) OVER OVER ; : 2DROP ( WD->) DROP DROP ; : 2SWAP ( WD1,WD2->WD2,WD1) 3 ROLL 3 ROLL ; : 2OVER ( WD1,WD2->WD1,WD2,WD1) 3 PICK 3 PICK ; : 2ROT ( WD1,WD2,WD3->WD2,WD3,WD1) 5 ROLL 5 ROLL ;



Экран номер 13

( 31.03. 86 AND OR XOR NOT 0= 0< ) CODE AND ( W1,W2->W3) 14 LHRW12 BAL, RW1 RW2 NR, POPPUT1 B, END-CODE CODE OR ( W1,W2->W3) 14 LHRW12 BAL, RW1 RW2 OR, POPPUT1 B, END-CODE CODE XOR ( W1,W2->W3) 14 LHRW12 BAL, RW1 RW2 XR, POPPUT1 B, END-CODE : NOT ( W1->W2 ) -1 XOR ; CODE 0= ( W->F) RW1 RW1 SR, RW2 PULL, RW2 RW2 LTR, PUTRW1 BNZ, RW1 0 BCTR, PUTRW1 B, END-CODE CODE 0< ( N->F) RW1 RW1 SR, RW2 PULL, RW2 RW2 LTR, PUTRW1 BNM, RW1 0 BCTR, PUTRW1 B, END-CODE

Экран номер 14

( 31.03.86 S>D DABS DNEGATE D+ D- DU< ) CODE S>D ( N->D ) RW1 PULL, PUSH2RW1 B, END-CODE CODE DABS ( D1->D2) 14 LRW1 BAL, RW1 RW1 LPR, 2PUTRW1 B, END-CODE CODE DNEGATE ( WD1->WD2) 14 LRW1 BAL, RW1 RW1 LCR, 2PUTRW1 B, END-CODE CODE D+ ( WD1,WD2->WD3) 14 LRW12 BAL, RW1 RW2 AR, 2POPPUT1 B, END-CODE CODE D- ( WD1,WD2->WD3) 14 LRW12 BAL, RW1 RW2 SR, 2POPPUT1 B, END-CODE CODE DU< ( UD1,UD2->F) 14 LRW12 BAL, 0 0 SR, RW1 RW2 CLR, ?L IF, 0 0 BCTR, THEN, RSTACK 6 (, 0 RSTACK LA, 0 PUT, RNEXT BR, END-CODE

Экран номер 15

( 31.03.86 D/MOD D/ DMOD D0= D= D0< D< D2/ ) CODE D/MOD ( D1,D2->D3,D4) 14 LRW12 BAL, 1 RW1 LR, 0 RW2 LR, RW1 32 SRDA, RW1 0 DR, 1 0 XR, 1 1 LTR, ?M IF, RW1 0 AR, RW1 0 BCTR, THEN, RW1 TEMP ST, FIRST 4 +(, 4 ), TEMP MVC, RW1 RW2 LR, 2PUTRW1 B, END-CODE : D/ ( D1,D2->D3) D/MOD 2SWAP 2DROP ; : DMOD ( D1,D2->D3) D/MOD 2DROP ; : D0= ( WD->F) OR 0= ; : D= ( WD1,WD2->F) D- D0= ; : D0< ( D->F) SWAP DROP 0< ; : D< ( D1,D2->F) D- D0< ; CODE D2/ ( D1->D2) 14 LRW1 BAL, RW1 1 SRA, PUTRW1 B, END-CODE

Экран номер 16

( 09.09.86 UM* UM/MOD U< M/MOD DMAX DMIN ) CODE UM* ( U1,U2->UD) 14 LHRW12 BAL, RW1 RMASK NR, RW2 RMASK NR, RW1 RW1 MR, RW1 RW2 LR, 2PUTRW1 B, END-CODE CODE UM/MOD ( UD,U1->U2,U3) 1 POP, 1 RMASK NR, 14 LRW1 BAL, RSTACK RTWO SR, RW2 RW1 LR, RW1 RW1 SR, RW1 1 DR, RW1 FIRST 4 +(, STH, RW1 RW2 LR, POPPUT1 B, END-CODE CODE U< ( U1,U2->F) RW1 RW1 SR, RW2 PULL, RW2 RMASK NR, 0 SECOND LH, 0 RMASK NR, 0 RW2 CR, POPPUT1 BNL, RW1 0 BCTR, ( РЕЗУЛЬТАТ "ИСТИНА") POPPUT1 B, END-CODE : M/MOD ( UD1,U2->U3,UD4) >R 0 R@ UM/MOD R> SWAP >R UM/MOD R> ; : DMAX ( D1,D2->D3) 2OVER 2OVER D< IF 2SWAP THEN 2DROP ; : DMIN ( D1,D2->D3) 2OVER 2OVER D< NOT IF 2SWAP THEN 2DROP ;



Экран номер 17

( 31.03.86 NEGATE ABS + - 1+ 1- 2+ 2- +! 1+! ) CODE NEGATE ( W1->W2) RW1 PULL, RW1 RW1 LCR, PUTRW1 B, END-CODE : ABS ( N1->+N2) S>D DABS DROP ; CODE + 14 LHRW12 BAL, RW1 RW2 AR, POPPUT1 B, END-CODE : - ( W1,W2->W3) NEGATE + ; : 1+ ( W1->W2) 1 + ; : 1- ( W1->W2) -1 + ; : 2+ ( W1->W2) 2 + ; : 2- ( W1->W2) 2 - ; CODE +! ( W,A->) 14 LHRW12 BAL, RW2 RMASK NR, RW1 0 (, RW2 RFORTH AH, RW1 0 (, RW2 RFORTH STH, 2POP B, END-CODE : 1+! ( A->) 1 SWAP +! ;

Экран номер 18

( 03.10.86 M* M/ * /MOD / MOD */MOD */ ) CODE M* ( N1,N2->D) RW1 SECOND LH, RW1 FIRST MH, 2PUTRW1 B, END-CODE CODE M/ ( D,N1->N2,N3) 1 POP, 14 LRW1 BAL, RSTACK RTWO SR, RW1 32 SRDA, 0 RW1 LR, RW1 1 DR, 0 1 XR, 0 0 LTR, ?M IF, RW1 1 AR, RW2 0 BCTR, THEN, RW1 FIRST 4 +(, STH, RW1 RW2 LR, ( ЧАСТНОЕ) POPPUT1 B, END-CODE : * ( N1,N2->N3) M* DROP ; : /MOD ( N1,N2->N3,N4) >R S>D R> M/ ; : / ( N1,N2->N3) /MOD SWAP DROP ; : MOD ( N1,N2->N3) /MOD DROP ; : */MOD ( N1,N2,N3->N4,N5) >R M* R> M/ ; : */ ( N1,N2,N3->N4) */MOD SWAP DROP ;

Экран номер 19

( 31.03.86 СРАВНЕНИЯ И РАЗРЕШЕНИЯ В ШИТОМ КОДЕ ) : 0<> ( N->F) 0= NOT ; : = ( W1,W2->F) - 0= ; : <> ( W1,W2->F) - 0<> ; : < ( N1,N2->F) - 0< ;

: >MARK ( ->A ) HERE 0 , ; : >RESOLVE ( A-> ) HERE SWAP ! ; : <MARK ( ->A ) HERE ; : <RESOLVE ( A-> ) , ;

Экран номер 20

( 31.03.86 SP@ SP! RP@ RP! 2/ 2* 2@ 2! DEPTH ) CODE SP@ ( ->A) RW1 RSTACK LR, RW1 RFORTH SR, PUSHRW1 B, END-CODE CODE SP! ( A->) RSTACK PULL, RSTACK RMASK NR, RSTACK RFORTH AR, RNEXT BR, END-CODE CODE RP@ ( ->A) RW1 RRET LR, RW1 RFORTH SR, PUSHRW1 B, END-CODE CODE RP! ( A->) RRET POP, RRET RMASK NR, RRET RFORTH AR, RNEXT BR, END-CODE CODE 2/ ( W1->W2 ) RW1 PULL, RW1 1 SRA, PUTRW1 B, END-CODE : 2* ( W1->W2 ) DUP + ; : 2@ ( A->WD) DUP 2+ @ SWAP @ ; : 2! ( WD,A->) DUP >R ! R> 2+ ! ; : DEPTH ( ->+N) SP@ S0 @ SWAP - 2/ ;

Экран номер 21

( 31.03.86 CMOVE CMOVE> ) CODE CMOVE ( A1,A2,U->) 14 LHRW12 BAL, RW2 RMASK NR, 2 =F BZ, RW1 RMASK NR, RW1 RFORTH AR, 1 FIRST 4 +(, LH, 1 RMASK NR, 1 RFORTH AR, 0 256 LA, 1 =F B, BEGIN, 0 (, 256 RW1 ), 0 (, 1 MVC, RW1 0 AR, 1 0 AR, 1 =H RW2 0 SR, ?M UNTIL, RW2 0 BCTR, RW2 0 AR, ?NM IF, RW2 3 =F EX, THEN, 2 =H RSTACK 6 (, 0 RSTACK LA, RNEXT BR, 3 =H 0 (, 1 RW1 ), 0 (, 1 MVC, END-CODE CODE CMOVE> ( A1,A2,U->) 14 LHRW12 BAL, RW2 RMASK NR, 1 =F BZ, RW1 RMASK NR, RW1 RFORTH AR, RW1 0 BCTR, 1 FIRST 4 +(, LH, 1 RMASK NR, 1 RFORTH AR, 1 0 BCTR, DO, 0 0 (, 1 RW2 IC, 0 0 (, RW1 RW2 STC, RW2 LOOPBCT, 1 =H RSTACK 6 (, 0 RSTACK LA, RNEXT BR, END-CODE



Экран номер 22

( 31.03. 86 FILL ERASE BLANK COMPILE [ ] MIN MAX HEX DECIMAL) : FILL ( A,U,C->) SWAP ?DUP IF >R OVER C! DUP 1+ R> 1- CMOVE EXIT THEN 2DROP ; : ERASE ( A,U-> ) 0 FILL ; : BLANK ( A,U-> ) BL FILL ; : COMPILE ( -> ) R> DUP 2+ >R @ , ; : [ ( -> ) STATE 0! ; IMMEDIATE : ] ( -> ) -1 STATE ! ; CODE MIN ( N1,N2->N3 ) 14 LHRW12 BAL, RW1 RW2 CR, POP BNH, RW1 RW2 LR, POPPUT1 B, END-CODE CODE MAX ( N1,N2->N3 ) 14 LHRW12 BAL, RW1 RW2 CR, POP BNL, RW1 RW2 LR, POPPUT1 B, END-CODE : HEX ( ->) 16 BASE ! ; : DECIMAL ( ->) 10 BASE ! ;

Экран номер 23

( 31.03.86 LIT 2LIT LITERAL 2LITERAL SPACE SPACES ) CODE LIT ( ->W ) RW1 0 (, RI RFORTH LH, RI RTWO AR, PUSHRW1 B, END-CODE CODE 2LIT ( ->WD ) RW1 4 LA, RSTACK RW1 SR, RW2 0 (, RI RFORTH LA, FIRST (, 4 ), 0 (, RW2 MVC, RI RW1 AR, RNEXT BR, END-CODE : LITERAL ( W->) STATE @ IF COMPILE LIT , THEN ; IMMEDIATE : 2LITERAL ( WD->) STATE @ IF COMPILE 2LIT , , THEN ; IMMEDIATE : SPACE ( ->) BL EMIT ; : SPACES ( +N->) 0 OVER < IF 0 DO SPACE LOOP EXIT THEN DROP ;

Экран номер 24

( 09.09.86 ЦИКЛЫ СО СЧЕТЧИКОМ: (DO/ I I' J LEAVE ) CODE (DO) ( U1,U2-> ) 14 LHRW12 BAL, 1 =H 1 0 (, RI RFORTH LH, 1 RPUSH, RI RTWO AR, RW1 RPUSH, RW2 RPUSH, 2POP B, END-CODE CODE I ( ->U ТЕКУЩЕЕ ЗНАЧЕНИЕ СЧЕТЧИКА ЦИКЛА) RW1 RPULL, PUSHRW1 B, END-CODE CODE I' ( ->U ВЕРХНЯЯ ГРАНИЦА ЦИКЛА) RW1 RSECOND LH, PUSHRW1 B, END-CODE CODE J ( ->U ТЕКУЩЕЕ ЗНАЧЕНИЕ СЧЕТЧИКА 2-ГО ЦИКЛА) RW1 RFIRST 6 +(, LH, PUSHRW1 B, END-CODE CODE LEAVE ( ->) RI RFIRST 4 +(, LH, RI RMASK NR, RRET 6 (, 0 RRET LA, RNEXT BR, END-CODE

Экран номер 25

( 31.03.86 +BUF BUFFER BLOCK EMPTY-BUFFERS UPDATE ) : +BUF ( A1->A2,F ПЕРЕЙТИ К СЛЕДУЮЩЕМУ БУФЕРУ В ПУЛЕ) B/BUF 4 + + DUP LIMIT = IF DROP FIRST THEN DUP PREV @ - ; : BUFFER ( +N->A) OFFSET @ + USE @ DUP >R ( ИЩЕМ СВОБОДНЫЙ БУФЕР) BEGIN +BUF UNTIL USE ! R@ @ 0< IF ( УСТАНОВЛЕН ПРИЗНАК "UPDATE") R@ 2+ R@ @ 32767 AND WBLK THEN R@ ! R@ PREV ! R> 2+ ; : BLOCK ( +N->A) OFFSET @ + >R PREV @ DUP @ R@ - DUP + IF BEGIN +BUF 0= IF DROP R@ OFFSET @ - BUFFER DUP R@ RBLK 2- THEN DUP @ R@ - DUP + 0= UNTIL DUP PREV ! THEN RDROP 2+ ; : EMPTY-BUFFERS ( -> ) FIRST LIMIT OVER - ERASE ; : UPDATE ( -> ) PREV @ @ 32768 OR PREV @ ! ;



Экран номер 26

( 31.03.86 SAVE-BUFFERS FLUSH ) : SAVE-BUFFERS ( -> ) LIMIT FIRST DO I @ 32768 AND IF I @ 32767 AND DUP I ! I 2+ SWAP WBLK THEN B/BUF 4 + +LOOP ; : FLUSH ( -> ) SAVE-BUFFERS EMPTY-BUFFERS ;

Экран номер 27

( 31.03.86 ENCLOSE WORD ) CODE ENCLOSE ( A,C->A,N1,N2,N3) 14 LHRW12 BAL, RW1 RMASK NR, RW1 RFORTH AR, 14 14 SR, 0 0 SR, BEGIN, 0 0 (, 14 RW1 IC, 0 0 LTR 2 =F BZ, 14 1 (, 0 14 LA, 0 RW2 CR, ?NE UNTIL, 14 0 BCTR, 2 =H 14 PUT, BEGIN, 1 14 LR, 0 0 (, 1 RW1 IC, 0 0 LTR, 2 =F BZ, 14 1 (, 0 14 LA, 0 RW2 CR, ?E UNTIL, 2 =H 1 PUSH, RW1 14 LR, PUSHRW1 B, END-CODE : WORD ( C->T ) BLK @ IF BLK @ BLOCK ELSE TIB THEN >IN @ + SWAP ENCLOSE >IN +! HERE >R OVER - >R + ALIGNH HERE 1+ R@ CMOVE HERE R> 1+ ALLOT ALIGNH HERE OVER - 2- OVER C! R> DP! ;

Экран номер 28

( 31.03.86 LIT" COUNT ," " ". (."/ ." C" ( .( QUIT ABORT ) CODE LIT" ( ->T ) 14 IPUSH BAL, RNEXT BR, END-CODE : COUNT ( T->A,N) DUP 1+ SWAP C@ 2DUP + C@ IF 1+ THEN ; : ," ( -> ) C" " WORD C@ 2+ ALLOT ALIGNH ; : " ( ->T) ?COMP COMPILE LIT" ," ; IMMEDIATE : ". ( T-> ) COUNT TYPE ; CODE (.") ( ->) 14 IPUSH BAL, 14 GOTO BAL, ] ". [ : ." ( -> ) ?COMP COMPILE (.") ," ; IMMEDIATE : C" ( ->C) BL WORD 1+ C@ [COMPILE] LITERAL ; IMMEDIATE : ( ( ->) C" ) WORD DROP ; IMMEDIATE : .( ( ->) C" ) WORD COUNT TYPE ; IMMEDIATE : QUIT ( ->) [COMPILE] [ S0 @ SP! R0 @ RP! ФОРТ-СИСТЕМА ;

Экран номер 29

( 31.03.86 ПРОВЕРКИ И СИГНАЛИЗАЦИИ ОБ ОШИБКАХ ) : ?ABORT ( F,T->) SWAP IF COUNT CR TYPE ABORT THEN DROP ; CODE (A") ( F->) 14 IPUSH BAL, 14 GOTO BAL, ] ?ABORT [ END-CODE : ABORT" ( F->) COMPILE (A") ," ; IMMEDIATE : ABORT8 ( ->) -1 ABORT" НЕПРАВИЛЬНОЕ ЗНАЧЕНИЕ В СТЕКЕ" ; : !CSP ( ->) SP@ CSP 1 ; : ?CSP ( ->) SP@ CSP @ - ABORT" СБИЛСЯ УКАЗАТЕЛЬ СТЕКА" ; : ?PAIRS ( N1,N2-> ) - ABORT" НЕПАРНЫЕ СКОБКИ" ; CODE ?+ ( N->N ) FIRST 128 TM, RNEXT BZR, ERCOND8 B, END-CODE : ?COMP ( ->) STATE @ NOT ABORT" ТРЕБУЕТСЯ РЕЖИМ КОМПИЛЯЦИИ" ; : BADWORD ( T->) CR ". ." ?" ABORT ;



Экран номер 30

( 31.03.86 >BODY BODY> >LINK LINK> L>NAME N>LINK >NAME NAME> ) : >BODY ( CFA->PFA) 2+ ; : BODY> ( PFA->CFA) 2- ; : >LINK ( CFA->LFA) 2- ; : LINK> ( LFA->CFA) 2+ ; CODE L>NAME ( LFA->NFA) RW2 PULL, RW2 RMASK NR, RW1 RW2 LR, 14 &LENG LA, 1 1 SR, DO, RW1 RTWO SR, 1 0 (, RW1 RFORTH IC, 1 LENG1MSK N, 0 2 (, 1 RW1 LA, 0 RW2 CR, PUTRW1 BE, 14 LOOPBCT, PUTRW1 B, END-CODE : N>LINK ( NFA->LFA) DUP C@ 31 AND + 2+ ; : >NAME ( CFA->NFA) >LINK L>NAME ; : NAME> ( NFA->CFA) N>LINK LINK> ;

Экран номер 31

( 31.03.86 LATEST DEFINITIONS SMUDGE UNSMUDGE IMMEDIATE ID.) : LATEST ( ->NFA) CURRENT @ @ ; ( (;CODE/ RECURSE ) : DEFINITIONS ( ->) CONTEXT @ CURRENT ! ; : SMUDGE ( ->) LATEST C@ [ &SFLAG ] LITERAL OR LATEST C! ; : UNSMUDGE ( ->) LATEST C@ [ 255 &SFLAG - ] LITERAL AND LATEST C! ; : IMMEDIATE ( ->) LATEST C@ [ &IFLAG ] LITERAL OR LATEST C! ; : ID. ( NFA-> ) DUP 1+ SWAP C@ [ &LENG ] LITERAL AND 2DUP + C@ IF 1+ THEN TYPE SPACE ; : (;CODE) ( -> ) R> LATEST NAME> ! ; : RECURSE ( -> ) LATEST NAME> , ; IMMEDIATE

Экран номер 32

( 31.03.86 CONSTANT VARIABLE 2CONSTANT 2VARIABLE : ; ) : ?LOADING ( ->) BLK @ 0= ABORT" НЕТ ОБРАБОТКИ ЭКРАНА" ; : ?GAP ( N->) HERE + SP@ SWAP U< ABORT" ИСЧЕРПАНИЕ ПАМЯТИ" ; : ?STACK ( ->) S0 @ SP@ U< ABORT" ИСЧЕРПАНИЕ СТЕКА" 10 ?GAP ; : CONSTANT ( W-> ) CREATE , ;CODE RW1 0 (, 14 RFORTH LH, PUSHRW1 B, END-CODE : VARIABLE ( -> ) CREATE 0 , ; : 2VARIABLE ( -> ) CREATE 0 , 0 , ; : 2CONSTANT ( WD-> ) CREATE , , DOES> 2@ ; : : ( -> ) !CSP CREATE ] SMUDGE ;CODE RI RPUSH, RI 14 LR, RNEXT BR, END-CODE : ; ( -> ) ?CSP COMPILE EXIT UNSMUDGE [COMPILE] [ ; IMMEDIATE

Экран номер 33

( 09.09.86 FORTH FORTH# FL# VOC-LINK VOCABULARY VOCABULARY# ) VOC FORTH &DWORD H, ( FORTH-83 ) A: FORTH# LASTWORD ( ВХОД В СПИСОК СЛОВАРНЫХ СТАТЕЙ) A: FL# 0 H, ( ПОЛЕ СВЯЗИ ДЛЯ СПИСКОВ СТАТЕЙ) CREATE VOC-LINK FL# ( ВХОД В СПИСОК СПИСКОВ СТАТЕЙ) : VOCABULARY ( -> ) CREATE [ &DWORD ] LITERAL , LIT [ FORTH# ] CONTEXT @ - IF CONTEXT @ 2- ELSE 0 THEN , HERE VOC-LINK @ , VOC-LINK ! DOES> [ THERE 4 - :A: VOCABULARY# ] 2+ CONTEXT ! ; : FORTH-83 ( ->) FORTH DEFINITIONS DECIMAL ;



Экран номер 34

( 31.03.86 (FIND/ ) CODE (FIND) ( -1,AN,...,A1,T->CFA,C,TF/FF ) RW2 POP, RW2 RMASK NR, RW2 RFORTH AR, ( ОБРАЗЕЦ) 0 0 SR, 0 0 (, 0 RW2 IC, 0 LENG1MSK N, ( ДЛИНА) 1 1 SR, 1 0 BCTR, BEGIN, RW1 PULL, ( ВХОД В ОЧЕРЕДНОЙ СПИСОК СЛОВ) 2 =F B, BEGIN, RW1 RFORTH AR, 14 0 (, 0 RW1 IC, 14 LENGMASK N, 14 0 CR, ?E IF, 14 4 =F EX, 3 =F BE, THEN, 14 LENG1MSK N, RW1 2 (, 14 RW1 LH, 2 =H RW1 RMASK NR, ?Z UNTIL, RSTACK RTWO AR, 1 FIRST CH, ?E UNTIL, PUTRW1 B, BEGIN, RSTACK RTWO AR, 3 =H 1 FIRST CH, ?E UNTIL, 0 0 (, 0 RW1 IC, RW1 RFORTH SR, RW1 4 (, 14 RW1 LA, RW1 PUT, 0 PUSH, RW1 1 LR, PUSHRW1 B, 4 =H 1 (, 1 RW1 ), 1 (, RW2 CLC, END-CODE

Экран номер 35

( 31.03.86 FIND -FIND ) : FIND ( T->A,N) DUP >R -1 LIT [ FORTH# ] @ CURRENT @ @ 2DUP = IF DROP THEN CONTEXT @ @ 2DUP = IF DROP THEN R> (FIND) DUP IF DROP ROT DROP [ &IFLAG ] LITERAL AND IF 1 ELSE -1 THEN THEN ; : -FIND ( ->A,N) BL WORD FIND ;

Экран номер 36

( 09.09.86 CREATE DOES> ) : CREATE ( -> ) 100 ?GAP ALIGNH -FIND SWAP DROP IF HERE ID. ." УЖЕ ЕСТЬ " ABORT THEN HERE DUP C@ WIDTH AND 2+ ALLOT ALIGNH HERE OVER - 2- OVER C! LATEST , CURRENT @ ! LIT [ CREATE# ] , ;

: DOES> ( -> ) COMPILE (;CODE) 2LIT [ DOES# B, ] , , ; IMMEDIATE

Экран номер 37

( 31.03.86 PAD HOLD ALPHA <# #> # #S SIGN ) : PAD ( ->A) HERE 100 + ; : HOLD ( C-> ) -1 HLD +! HLD @ C! ; CODE ALPHA ( N->C) RW2 FIRST LH, RW1 RW1 SR, RW1 1 =F (, RW2 IC, PUTRW1 B, 1 =H C,' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' END-CODE : <# ( -> ) PAD HLD ! ; : #> ( D->A,+N) 2DROP HLD @ PAD OVER - ; : # ( D1->D2) BASE @ M/MOD ROT ALPHA HOLD ; : #S ( D->0,0) BEGIN # 2DUP OR 0= UNTIL ; : SIGN ( N->) 0< IF C" - HOLD THEN ;

Экран номер 38

( 31.03.86 D.R D. .R . H. U. U.R ? ) : D.R ( D,+N-> ) ?+ >R DUP >R DABS <# #S R> SIGN #> R> OVER - SPACES TYPE ; : D. ( D-> ) 0 D.R SPACE ; : .R ( N1,+N2->) >R S>D R> D.R ; : . ( N-> ) S>D D. ; : H. ( N->) BASE @ SWAP 0 HEX <# # # # # #> TYPE SPACE BASE ! ; : U. ( U->) 0 D. ; : U.R ( U,+N->) >R 0 >R D.R ; : ? ( A-> ) @ . ;



Экран номер 39

( 31.03.86 DIGIT CONVERT NUMBER ) : DIGIT ( C,N1->N2,TF/FF) 0 ROT ROT 0 DO I ALPHA OVER = IF 2DROP I -1 0 LEAVE THEN LOOP DROP ; : CONVERT ( WD1,A1->WD2,A2) BEGIN 1+ DUP >R C@ BASE @ DIGIT WHILE SWAP BASE @ UM* DROP ROT BASE @ UM* D+ DPL @ 1+ IF DPL 1+! THEN R> REPEAT R> ; : NUMBER ( T->WD ) 0 0 ROT DUP >R COUNT OVER + OVER C@ C" - = DUP >R SWAP >R IF ELSE 1- THEN -1 BEGIN DPL ! CONVERT DUP R@ < WHILE DUP C@ C" . <> IF RDROP RDROP R> BADWORD THEN 0 REPEAT DROP RDROP R> IF DNEGATE THEN RDROP ;

Экран номер 40

( 31.03.86 EXPECT QUERY INTERPRET ФОРТ-СИСТЕМА X ) : EXPECT ( A,+N-> ) DUP >R (EXPECT) DUP SPAN ! TYPE R> SPAN @ - IF SPACE THEN ; : QUERY ( ->) TIB 80 EXPECT >IN 0! BLK 0! SPAN @ #TIB ! ; : INTERPRET ( ->) BEGIN -FIND ?DUP IF 1+ IF EXECUTE ELSE STATE @ IF , ELSE EXECUTE THEN THEN ELSE NUMBER DPL @ 1+ IF [COMPILE] 2LITERAL ELSE DROP [COMPILE] LITERAL THEN THEN ?STACK AGAIN ; : ФОРТ-СИСТЕМА ( ->) BEGIN QUERY INTERPRET AGAIN ; CODE X ( ->) ЗАБИТЬ-X ( НУЛЕВОЙ КОД ВМЕСТО БУКВЫ "X") EXIT# B, END-CODE IMMEDIATE

Экран номер 41

( 31.03.86 -TRAILING ' ['] [COMPILE] LOAD THRU ;S --> ) CODE -TRAILING ( A,N1->A,N2) 14 LHRW12 BAL, RW1 RMASK NR, RW1 RFORTH AR, 0 RW1 LR, RW1 RW2 AR, BEGIN, RW1 0 CR, 1 =F BNH, RW1 0 BCTR, 0 (, RW1 64 CLI, ?NE UNTIL, 0 0 BCTR, 1 =H RW1 0 SR, PUTRW1 B, END-CODE : ' ( ->CFA) -FIND 0= IF BADWORD THEN ; : ['] ( -> ) ?COMP ' [COMPILE] LITERAL ; IMMEDIATE : [COMPILE] ( ->) -FIND IF , EXIT THEN BADWORD ; IMMEDIATE : LOAD ( N-> ИНТЕРПРЕТИРОВАТЬ ВЛОК С НОМЕРОМ N ) >IN @ >R BLK @ >R BLK ! >IN 0! INTERPRET R> BLK ! R> >IN ! ; : THRU ( N1,N2-> ИНТЕРПРЕТИРОВАТЬ БЛОКИ ОТ N1 ДО N2 ) 1+ SWAP DO I LOAD LOOP ; : ;S ( ->) ?LOADING RDROP ; IMMEDIATE : --> ( ->) ?LOADING >IN 0! BLK 1+! ; IMMEDIATE

Экран номер 42

( 09.09.86 DUMP SNAPSTK S. R. ) : DUMP ( A,U-> РАСПЕЧАТАТЬ U БАЙТОВ ) DUP IF BASE @ >R HEX OVER + SWAP DO CR I <# C" * HOLD 0 15 DO DUP I + C@ HOLD -1 +LOOP C" * HOLD 0 15 DO BL HOLD DUP I + C@ 0 # # 2DROP -1 +LOOP BL HOLD BL HOLD 0 # # # # #> TYPE 16 +LOOP R> BASE ! ELSE 2DROP THEN ; : SNAPSTK RDROP CR ". ." , ВСЕГО ЗНАЧЕНИЙ " 2DUP SWAP - 2/ DUP . 0 SWAP , IF ." (ВЕРШИНА СПРАВА)" CR 2- DO I @ . -2 +LOOP ELSE 2DROP THEN ; : S. ( ->) SP@ S0 @ " СТЕК ДАННЫХ" SNAPSTK ; : R. ( ->) RP@ 2+ R0 @ " СТЕК ВОЗВРАТОВ" SNAPSTK ;



Экран номер 43

( 31.03.86 .VOC (VOC/ VOCS ) : .VOC ( PFA+2-> ) 2- BODY> >NAME ID. ; : (VOC) ( PFA1+2->PFA2,N) @ 0 BEGIN OVER DUP IF @ [ &DWORD ] LITERAL <> THEN WHILE 1+ ( СЧЕТЧИК СЛОВ) SWAP N>LINK @ SWAP REPEAT ; : VOCS ( -> ) -1 ['] FORTH >BODY 2+ CURRENT @ ." СПИСОК CURRENT: " DUP .VOC OVER @ OVER @ = IF DROP THEN CONTEXT @ ." СПИСОК CONTEXT: " DUP .VOC OVER @ OVER @ = IF DROP THEN CR ." СТАНДАРТНЫЙ ПОРЯДОК ПОИСКА: " BEGIN 2- BEGIN 2+ DUP .VOC (VOC) DROP DUP 0= UNTIL DROP DUP -1 = UNTIL DROP CR ." НАЛИЧНЫЕ СПИСКИ СЛОВ: " VOC-LINK @ BEGIN DUP 2- .VOC @ DUP 0= UNTIL DROP ;

Экран номер 44

( 31.03.86 WORDS ) : WORDS ( -> ) ." СПИСОК " CONTEXT @ DUP .VOC DUP (VOC) ." ВСЕГО СЛОВ - " . ." CЛЕДУЮЩИЙ СПИСОК - " ?DUP IF 2+ .VOC THEN CR @ BEGIN DUP DUP IF @ [ &DWORD ] LITERAL <> THEN WHILE DUP C@ [ &SFLAG ] LITERAL AND 0= IF DUP ID. SPACE THEN N>LINK @ REPEAT DROP ;

Экран номер 45

( 31.03.86 (FORGET/ FORGET REMEMBER FORGET0 ) : (FORGET) ( A-> ИСКЛЮЧИТЬ ВСЕ СЛОВА ВЫШЕ АДРЕСА А ) DUP FENCE @ U< ABORT" ЗАЩИТА ПО FENCE" >R VOC-LINK @ BEGIN R@ OVER U< WHILE FORTH DEFINITIONS @ DUP VOC-LINK ! REPEAT ( ДОШЛИ ДО СПИСКА, ГДЕ ЕСТЬ ЭТО СЛОВО ) BEGIN DUP 4 - BEGIN N>LINK @ DUP R@ U< UNTIL OVER 2- ! @ ?DUP 0= UNTIL R> DP! ; : FORGET ( ->) ' >NAME (FORGET) ; : REMEMBER ( ->) CREATE DOES> (FORGET) ;

Экран номер 46

( 31.09.86 (#SCR/ LIST SCR? INDEX ) : (#SCR) ( N->A,T ПЕРЕВЕСТИ НОМЕР N ЭКРАНА В ТЕКСТОВОЕ ИМЯ ) BASE @ >R DECIMAL 0 <# #S #> R> BASE ! ; : LIST ( N-> РАСПЕЧАТАТЬ ЭКРАН N, ЗАПОМНИТЬ ЕГО В "SCR" ) DUP SCR ! CR ." ЭКРАН " DUP (#SCR) TYPE BLOCK 16 0 DO DUP I 64 * + CR I 3 .R SPACE 64 TYPE LOOP DROP ;

Экран номер 47

( 31.03.86 СТАНДАРТНЫЕ СТРУКТУРЫ УПРАВЛЕНИЯ ) : BEGIN ?COMP <MARK 1 ; IMMEDIATE : UNTIL 1 ?PAIRS COMPILE ?BRANCH <RESOLVE ; IMMEDIATE : AGAIN 1 ?PAIRS COMPILE BRANCH <RESOLVE ; IMMEDIATE : IF ?COMP COMPILE ?BRANCH &gtMARK 2 ; IMMEDIATE : THEN 2 ?PAIRS &gtRESOLVE ; IMMEDIATE : ELSE 2 ?PAIRS COMPILE BRANCH &gtMARK SWAP &gtRESOLVE 2 ; IMMEDIATE : WHILE 1 ?PAIRS 1 [COMPILE] IF ; IMMEDIATE : REPEAT &gtR &gtR [COMPILE] AGAIN R&gt R&gt [COMPILE] THEN ; IMMEDIATE : DO ?COMP COMPILE (DO) &gtMARK <MARK 3 ; IMMEDIATE : LOOP 3 ?PAIRS COMPILE (LOOP) <RESOLVE &gtRESOLVE ; IMMEDIATE : +LOOP 3 ?PAIRS COMPILE (+LOOP) <RESOLVE &gtRESOLVE ; IMMEDIATE


Распространенные форт-системы


Фиг-форт. Система разработана в 1978–1980 гг. группой из 9 системных программистов в США — Группой по языку Форт (FORTH Interest Group), желавших сделать этот язык удобным средством программирования для персональных ЭВМ []. Система реализована для целого ряда ЭВМ с различной архитектурой. В нашей стране получила распространение на ЭВМ СМ-4 с операционными системами ОС РВ, РАФОС и без операционной системы. Ее ядро, написанное на макроязыке ассемблера, занимает от 4 до 5 Кбайт и после загрузки в память ЭВМ позволяет вводить следующие определения уже непосредственно на языке Форт. Общий объем словаря — около 8 К (220 слов). В реализации применен косвенный шитый код. Имеются загружаемый ассемблер и текстовый редактор.

Форт-СМ. Система разработана в Ленинграде С.Б. Кацевым (ЛГУ) и И.А. Шендриковым (ЛИТМО) на основе стандарта «Форт-83». Используется с 1985 г. на ЭВМ СМ-3, СМ-4, ДВК, «Электроника-60», БК0010. Словарь включает около 350 слов, его общий объем — 10,5 К. В зависимости от генерации может работать под операционными системами ОС РВ, РАФОС или без операционной системы с перфоленточной загрузки. В реализации применен косвенный шитый код. Имеются связь с файловой системой соответствующей ОС, загружаемый структурный ассемблер с метками, строковый и экранный редакторы, целевая компиляция.

Форт-Тарту. Система разработана в ВЦ Тартуского государственного университета Р.В. Вяйнасте и А.Э. Юуриком для операционных систем ОС РВ (используется с 1983 г.) и ЮНИКС (с 1985 г.). Ядро системы занимает 8 Кбайт и включает 270 слов. Система является расширением стандарта «Фиг-форт». В реализации применен прямой шитый код. Имеется встроенный ассемблер, в оттранслированном виде занимающий 1 Кбайт. Встроенный текстовый редактор для ОС РВ имеет только строковый режим, для ОС ЮНИКС реализован экранный вариант. Система используется в учебном процессе и как инструментальное средство для НИР [].


Система имеет отдельно загружаемый пакет целевой компиляции и средства для построения независимого программного продукта.

Форт-Искра-226. Эта развитая операционная система на базе языка Форт [] разработана в Институте социально-экономических проблем (ИСЭП) АН СССР. Используется с 1986 г. Включена в комплект заводской поставки ЭВМ «Искра-226». Объем — около 32 Кбайт (свыше 400 слов). Система базируется на стандарте «Фиг-форт», расширенном рядом слов в соответствии с ее функциональным назначением. Среди них встроенный диспетчер, средства для параллельного выполнения директив, работа с файлами и базами данных, работа с адресным пространством до 128 Кбайт. Важным встроенным средством является программная реализация элементарных математических функций и операций для работы с плавающей точкой. В реализации применен прямой шитый код. Встроенный экранный редактор имеет режим работы с окнами.

Форт-М6000. Система разработана В.Н. Патрышевым (Ленинград) для ЭВМ М6000 под управлением ДОС РВ, РТЕ-2 и для работы без операционной системы. Используется с 1985 г. Ядро занимает 14 Кбайт (300 слов). Система ориентирована на стандарт «Форт-83». В реализации использован прямой шитый код. Имеются своя файловая система, средства для связи с операционной системой, строковый редактор, ориентированный на файловую систему.

Форт-БЭСМ-6. Система разработана в Институте теоретической астрономии (ИТА) АН СССР И.Р. Агамирзяном для ЭВМ БЭСМ-6. Используется с 1984 г. Работает под управлением ОС ДИСПАК и имеет интерфейс с файловой системой КРАБ. Общий объем — 24 Кбайт (500 слов). По входному языку система ближе всего к стандарту «Фиг-форт», вместе с тем используется ряд слов из стандарта «Форт-83». Сравнительно большой объем памяти связан с отсутствием в ЭВМ БЭСМ-6 байтовой адресации. Для представления стандартного двухбайтного значения используется шестибайтное машинное слово БЭСМ-6.По той же причине для работы с байтовыми значениями вместо слов C@, C! и других введен ряд специальных слов. Из-за особенностей системы команд в реализации применен подпрограммный шитый код. Это позволяет включать ассемблерные вставки непосредственно в шитый код и наоборот — высокоуровневые слова как обращения к подпрограммам внутрь ассемблерных определений. Прототипом для встроенного ассемблера является ассемблер МАДЛЕН БЭСМ-6. В качестве текстового редактора используется стандартный редактор операционной системы.

Форт-Эльбрус. Система разработана на математикомеханическом факультете ЛГУ А.Е. Соловьевым для МВК «Эльбрус». Работает с 1986 г. под управлением ОС «Эльбрус». Ядро занимает 4,5 Кбайт (200 слов). Система ориентирована на стандарт «Форт-83», имеет ряд специальных инструментальных слов и средства для связи с процедурами на языке Эль-76.


Список литературы


Астановский А.Г., Ломунов В.Н. Процессор, ориентированный на язык Форт//Программирование микропроцессорной техники. Таллин: Ин-т кибернетики АН ЭССР, 1984. с. 50-67.

Баранов С.Н. Система Форт-ЕС. Л., 1986. 48 с. (Препринт ЛИИАН №1).

Баранов С.Н. Стандарты языка Форт/ЛИИАН, 1987. 88 с. Деп. в ВИНИТИ. 04.06.87. ФН 4012-887.

Баранов С.Н., Кириллин В.А., Ноздрунов Н.Р. Реализация языка Форт на дисплейном комплексе ЕС-7970//Программирование микропроцессорной техники. Таллин: Ин-т кибернетики АН ЭССР, 1984. с. 41-49.

Берс А.А., Поляков В.Г., Руднев С.Б. Основные принципы разработки системы программирования для микропроцессоров//Программное обеспечение задач информатики. Новосибирск: ВЦ СОАН, 1982. с. 5-25.

Болдырев А.Ю., Веретенова Н.Н., Лезин Г.В. ФОС: интерактивная система программирования для ЭВМ ИСКРА-226. Л., 1986. 74 с. (Препринт ИСЭП).

Вулф А. Операционные системы реального времени в русле развития вычислительной техники//Электроника. 1985. №17. с. 46-56.

Дейкстра Э.Д. Взаимодействие последовательных процессов//Языки программирования/ Пер. с англ. М.: Мир, 1972. с. 9-86.

Диалоговые микропроцессорные системы/Под ред. Н.П. Брусенцова и А.М. Шаумана. М.: Изд-во МГУ, 1986. 148 с.

Ершов А.П. О сущности трансляции//Программирование. 1977. №5. с. 21-39.

Кнут Д. Искусство программирования для ЭВМ/Пер. с англ. Т. I. М.: Мир, 1976. 735 с.

Котляров В.П., Морозов Н.Б. Технологические комплексы разработки программного обеспечения встроенных микроЭВМ//Индивидуальные диалоговые системы на базе микроЭВМ (персональные компьютеры) ДИАЛОГ-84-МИКРО. Л.: Наука, 1984. с. 93-96.

Липаев В.В. Тестирование программ. М.: Радио и связь, 1986. 296 с.

Липаев В.В., Каганов Ф.А., Керданов А.В. Система автоматизации проектирования программ на базе персональных ЭВМ (система ПРА)//Микропроцессорные средства и системы. 1985. №4. с. 42-45.

Новиков Б.А. Снова сопрограммы//Программирование. 1986. №4. с. 59-62.

Новиков Б.А., Романовский И.В. Сопрограммы в ОС ЕС//Кибернетика. 1985. №1. с. 34-37,44.


Руднев С.Б., Четвернин В.А. Переносимая рабочая смесь — ядро программного обеспечения персональной ЭВМ//Персональные ЭВМ в задачах информатики. Новосибирск: ВЦ СОАН. 1984. с. 58-72.

Сахаров А.Л. Д/М — диалоговый язык для управления пакетами прикладных программ//Управляющие системы и машины. 1985. №3. с. 105-109.

Технология проектирования компонентов программ АСУ/В.В. Липаев, Л.А. Серебровский, П.Г. Гаганов и др. М: Радио и связь, 1983. 264 с.

Томбак М.О., Пёял Я.Р., Соо В.К. Использование расширяемого языка в СПТ//Тр. Вычислит. центра Тартус. ун-та. Вып. 52. 1985. с. 39-54.

Ballard B. FORTH Direct Execution Processor in the Hopkins Ultraviolet Telescope//J. FORTH Appl. and Res. 1984. Vol. 2. N 1. p. 33-47.

Dewar R.K. Indirect Threaded Code//CACM. 1975. Vol. 18. N 6. p.330-331.

FORTH-83 Standard. Mountain View (USA): FORTH Standards Team, 1983. 82 p.

Harris K. The FORTH Philosophy//Dr. Dobb's J. 1981. Vol. 6. N 59. p. 6-11.

Loeliger R.G. Threaded Interpretative Languages. Peterbourgh: Byte Books, 1980. 251 p.

McCabe C.K. FORTH-83: Evolution Continues//BYTE. 1984. Vol. 9. N 8. p. 137-138, 412-415, 418-421.

Moore C.H. FORTH: A New Way to Program a Mini-Computer//Astr. and Astrophys. Suppl. 1974. Vol. 5. p. 497-511.

Ragsdale W.F. Fig-FORTH Installation Manual. Glossary. Model. Editor. San Carlos: FORTH Interest Group, 1980. 61 p.

Ritter T., Walker G. Varieties of Threaded Code for Language Implementation//BYTE. 1980. Vol. 5. N 9. p. 206-227.

Tello E. PolyFORTH and PC/FORTH//BYTE. 1984. Vol. 9. N 12. p. 303-310, 312, 314.

Walker R.V., Rather E.D. PolyFORTH II Reference Manual/S.L.: FORTH Inc., 1983. 524 p.