Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Форт-язык для микропроцессоров (некоторые места из избранных глав)

Автор: Кудимов Алексей Федорович

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

Слова и их выполнение

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

(смотрите в оригинальной книге)

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

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

(смотрите в оригинальной книге)

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

Теперь о тексте, который программист передает системе. Основным понятием системы Форт является слово. Слово - это любая последовательность символов, отличных от пробелов.

Текст - это любая последовательность слов, разделенных пробелами.

Вот пример текста:

 Не забудь, пожалуста , выключить электроприборы .

Этот текст состоит из семи слов, так как вторая запятая и точка в нашем определении слова - отдельные слова (очень важные для Форта).

Арифметический стек

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

На каждое число в стеке отводится 4 байта (элемента памяти).

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

Введенное число всегда помещается на вершину стека. Слово DROP (снять) снимает число с вершины стека. Слово . (точка) берет из стека число и печатает его. Слово S. печатает весь стек, оставляя его неизменным.

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

  стек до операции -> стек после операции,

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

Например, слово DUP (дублировать) добавляет в стек ещё одно значение, равное тому, которое находится на вершине стека. Это слово представляется диаграммой

DUP(дублировать) ... a             -> ... a a
 Далее без комментариев
DROP(снять)      ... a             -> ...
SWAP(обменять)   ... a b           -> ... b a
OVER(через)      ... a b           -> ... a b a
ROT(вращать)     ... a b c         -> ... b c a
-ROT             ... a b c         -> ... c a b

PICK(взять)      ... a {n чисел} n -> ... a {те же n чисел} a
(В частности, 0 PICK эквивалентно DUP, а 1 PICK - слову OVER).
ROLL(повернуть)  ... a {n чисел} n -> ... {те же n чисел} a
(В частности, 1 ROLL эквивалентно SWAP, а 2 ROLL - слову ROT).

Слово DEPTH (глубина) кладет в стек число, равное глубине стека - количеству чисел, находившихся в стеке.

Проследим, например, выполнение следующего текста.

    1 2 3 DUP DEPTH -ROT 7 OVER S.

Выпишем текст в столбик, сопровождая каждое слово состоянием стека после его исполнения.

    1     (1)
    2     (1 2)
    3     (1 2 3)
    DUP   (1 2 3 3)
    DEPTH (1 2 3 3 4)
    -ROT  (1 2 4 3 3)
    7     (1 2 4 3 3 7)
    OVER  (1 2 4 3 3 7 3)

Арифметические операции

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

Знак операции ( точнее слово, обозначающее операцию)пишется после того, как аргументы в стеке уже размещены. Текст

    13 3 -

помещает в стек число 10, так как слово - (минус) извлекает из стека два числа, сперва вычитаемое, потом уменьшаемое, и помещает в стек их разность:

 -                       ... a b           -> ... a-b

С другими операциями все обстоит аналогично:

 +                       ... a b           -> ... a+b
 *                       ... a b           -> ... a*b
 ABS(абсолютное значение)... a             -> ... |a|
 NEGATE(обратный знак)   ... a             -> ... -a
 /                       ... a b           -> ... целая часть
 MOD                     ... a b           -> ... остаток
 /MOD                    ... a b           -> ... остаток целая часть

В трех последних словах имеются в виду остаток и целая часть частного от деления a на b.

Таким образом, если в стеке лежат числа 26 и 7, то слово / превратит их в 3, слово MOD в 5, а слово /MOD в пару чисел 5 и 3.

Имеются специальные слова для действий с 1 и 2 (они выполняются немного быстрее):

 1+                      ... a             -> ... a+1

Аналогично работают 1- 2* 2/

Числа на стеке могут восприниматься разным образов в зависимости от того, какое слово их использует. В отводимых четырех (32 двоичных разрядах) может уместится лишь 2 в 32-ой степени = 2147483648 различных значений числа. Обычно они трактуются как числа из диапазона от - 2^31 до 2^31 - 1, но есть слова, которые воспринимают их как числа от 0 до 2^3 - 1. Следующие слова выполняют поразрядные логические операции над двоичным представлением чисел; в этих операциях числа трактуются как наборы из 32-х битов:

 AND(И)                  ... a b           -> ... aANDb
 OR (ИЛИ)                ... a b           -> ... aORb
 NOT(НЕ)                 ... a             -> ... NOTa
 XOR(ИСКЛЮЧАЮЩЕЕ ИЛИ)    ... a b           -> ... aXORb

Новые слова

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

    : S2 DUP * SWAP DUP * + ;

определяет слово S2, вычисляющее сумму квадратов двух чисел из стека,

 S2                      ... a b           -> ... a*a+b*b

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

 : S2        ( a b -> a*a+b*b)
     DUP     ( ... a b b    - теперь только результат)
     * SWAP  ( ... b*b a    -   >>     >>       >>   )
     DUP *   ( ... b*b a*a  -   >>     >>       >>   )
     + ;     (определение закончено)

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

 : S4 (a b c d  ->  a*a+b*b+c*c+d*d)
      S2 -ROT S2 + ;

Слово WORDS (слова) выводит на экран список всех известных системе слов (от самых новых к самым старым). Можно отменить уже определенное слово ("забыть" его) , но при этом забываются также и все слова, определенные позже. Для этого нужно выполнить текст из слова FORGET (забыть) и забываемого слова:

    FORGET S2

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

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

Приведем ещё два примера. Слово 8MOD эквивалентно тексту 8 MOD, но использует логические операции. Интересно слово LAST1 (последний бит), выделяющее в двоичном разложении числа младшую единицу.

    : 8MOD 7 AND ;
    : LAST1 DUP DUP 1- XOR AND ;

Это последнее слово - хорошее упражнение на применение логических операций.

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

Условное исполнение

При определении нового слова могут потребоваться знакомые Вам из других языков конструкции, организующие условное и циклическое исполнение. Для этого должны быть предусмотрены и логические величины, принимающие традиционные значения истина и ложь. Эти значения представлены целыми числами, причем истина соответствует числу -1 (двоичные разряды этого числасостоят из 32 единиц), а ложь соответствует 0 ( 32 двоичных ноля) Для них имеются и стандартные константы TRUE (истина) и FALSE(ложь), кладущие на стек соответственно числа -1 и 0. Логические значения получаются при выполнении некоторых слов, предназначеных для сравнения чисел.

Слова арифметического сравнения:

 >                       ... a b           -> ... a>b (т.е. при a>b TRUE
                                                            иначе FALSE)
 <                       ... a b           -> ... a<b
 =                       ... a b           -> ... a=b
 0=                      ... a             -> ... a=0
 0>                      ... a             -> ... a>0
 0<                      ... a             -> ... a<0

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

Для организации условного исполнения в языке Форт предусмотрены слова IF(если), ELSE(иначе) и THEN(то). Они используются в конструкциях

    IF <часть-если> ELSE <часть-иначе> THEN
    IF <часть-если> THEN.

Слово IF берет из стека логическое значение, и, в случае, если это значение - истина, т.е. не нуль, исполняет текст <часть-если>; в противном же случае исполняется <часть-иначе>, если она есть. Дальше управление передается на текст, следующий за THEN. Заметим, что использование управляющих слов требует состояние компиляции; таким образом, можно использовать их только при определении новых слов.

Пример. Стандартное слово ABS можно было бы определить так:

    : ABS              (...a -> ...|a|)
         DUP 0<        (a a<0)
         IF            (a)
         NEGATE THEN ;

Пример другой конструкции разберите сами и постарайтесь улучшить.

    : MAX (...a b -> ...max{a,b})
      OVER OVER > IF DROP ELSE SWAP DROP THEN ;

Слово MAX и аналогичное ему MIN входят в стандарт языка Форт.

Конечно же, конструкции IF могут быть вложенными; нужно только следить за скобочными соответствиями слов IF и THEN.

Циклы

Для организации циклов в языке Форт предусмотрены слова BEGIN(начало),WHILE(пока), REPEAT (повторить) и UNTIL (пока-не), используемые в конструкциях

    BEGIN <часть-начало> WHILE <часть-повторение> REPEAT

и

    BEGIN <часть-начало> UNTIL.

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

    : FACT                        (... n -> ... n!               )
      DUP 2 < IF DROP 1           (1 если n<2, то n!=1           )
      ELSE                        (n иначе                       )
      DUP                         (n n s=n k=n                   )
       (Теперь лежащие в стеке числа будут представлять)
       (s - накопленное произведение и k - множитель)
      BEGIN                       ( s  k                    <--  )
      1-                          ( s  k' k'=k-1               | )
      SWAP OVER * SWAP            ( s' k' s'=s*k               | )
      DUP 1 =                     ( s  k k=1 если k=1, то s=n! | )
      UNTIL                       ( n! 1 иначе повторить    ---  )
      DROP THEN ;                 ( n!                           )

Конструкция цикла типа WHILE

BEGIN <часть-начало> WHILE <часть-повторение> REPEAT используется, когда в цикле есть действия, которые в заключительной итерации не нужны: первоначально выполняется <часть-начало>, слово WHILE снимает со стека логическое значение и, если это истина, то выполняются тексты <часть-повторение>, <часть-начало>, снова слово WHILE и т. д. Когда слово WHILE снимает со стека ложь, выполнение цикла закончится и начнет выполняться текст, следующий после REPEAT.

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

Пример 1. Наибольший общий делитель двух положительных чисел.

(смотрите в оригинальной книге)

Пример 2. Подсчет числа единиц в двоичном разложении числа.

    : UNITS                 (a -> число единиц в a )
       0 SWAP               ( 0  a                 )
       (В стеке лежат два числа: счетчик числа единиц s)
       (и постепенно изменяемое число a                )
       BEGIN                ( s  a'                )
         DUP                ( s  a' a'             )
         LAST1 DUP          ( s  a'  d  d          )
         WHILE              ( пока d>0             )
         -                  ( s  a''               )
         SWAP 1+ SWAP       ( s' a''               )
         REPEAT DROP DROP ; ( s                    )

Более сложная конструкция цикла - число со счетчиком - описана дальше.

Константы и переменные

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

Слово CONSTANT (константа) в тексте

    CONSTANT <имя>

определяет новое слово <имя> как константу со значением, равным числу на вершине стека, и снимает со стека это число. В дальнейшим выполнение слова <имя> кладет это число в стек. Так, после исполнения текста

    80 CONSTANT LINESIZE

слово LINESIZE (длина строки) будет класть в стек число 80.

Определяющее слово VARIABLE (переменная), которое используется в конструкции:

    VARIABLE <имя>

резервирует в памяти компьютера 4 байта под значение переменной <имя>. Исполнение слова <имя> кладет в стек число - адрес зарезервированного места. Этот адрес может использоваться другими словами.

Вот три важных слова, использующих адреса:

Слово ! (восклицательный знак, читается "запомнить") служит для записи числа по данному адресу (addr):

 !                       ... a addr        -> ...             [addr] := a

Слово @ (читается "взять") кладет в стек число, лежащее по адресу, взятому из стека:

 @                       ... addr          -> ... a           a := [addr]
 Слово +! прибавляет виличину a к числу по аддресу addr:
 +!                      ... a addr        -> ...             [addr] := [addr] + a
 Пример. Текст
    A @ 5 + B !    (A и B - переменные)

соответствует оператору B := A + 5 других языков программирования.

Слова, определенные с помощью CONSTANT и VARIABLE, практически ничем не отличаются от других слов. В частности, их можно "забывать" с помощью слова FORGET. Сами слова CONSTANT, VARIABLE и : являются часными случаями более общих конструкций - определяющих слов. Со способами их задания читатель познакомится ниже.

Кодофайл

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

Вот некоторые базовые слова для работы с кодофайлом.

На стек кладется адрес вершины кодофайла:

 HERE (здесь)            ...               -> ... addr

Слово ALLOT резервирует n байтов свободной памяти - адрес вершины кодофайла увеличивается на n (а при n<0 уменьшается):

 ALLOT (занять)          ... n             -> ...

Занятие четырех байтов в кодофайле и запись туда n:

 ,                       ... n             -> ...

при работе с памятью кодофайла широко используются слова @ и ! .

Пример. Следующее слово резервирует в кодофайле память под n целых чисел (число n берется из стека) и кладет в стек адрес начала зарезервированого места:

    : 4ALLOT           (n -> a)
          HERE         (n a)
          SWAP         (a n)
          4 * ALLOT ;

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

 : [I]                 (i a -> a[i])
       SWAP 4 * + ;

После выполнения следующего текста

 N              (N - константа - число элементов массива)
 4ALLOT         (на стеке адрес начала массива)
 CONSTANT B
текста B [I] будет снимать со стека номер элемента в массиве B и класть в стек
адрес этого элемента.
 B [I]          (i -> addr)

Разумеется, никаких проверок корректности номера элемента не делается.

Символы

Для представления символьной информации отводится по одному байту памяти на каждый символ. Таким образом, каждому символу сопоставляется число от 0 до 255, которое называется его кодом. В разных ЭВМ используются разные кодировки. В отечественных машинах, где требуется добавить к множеству символов кириллицу, наиболее распространена кодировка КОИ-8, являющаяся расширением распространенной в мире кодировки ASCII. Кодировка КОИ-8 приведена в приложении К.

Имеются слова для работы с отдельными символами:

 C@                      ... addr          -> ... c

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

 C!                      ... c addr        -> ...

В байт по адресу addr записывается символ c.

 C,                      ... c             -> ...

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

 KEY (клавиша)           ...               -> ... c (ожидание)

При выполнении этого слова система переходит в ожидание, пока не будет нажата клавиша какой-либо литеры на клавиатуре дисплея. Код этой литеры кладется в стек.

 EMIT (выдать)           ... c             -> ...

Символ c будет напечатан.

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

    C'' + EMIT

А как быть с кодом пробела? Его кладет в стек слово BL.

Работа с участками памяти

Часто приходится выполнять действия сразу над большими учасками памяти. Участок памяти в таких действиях определяется адресом его начального байта и длиной.

    FILL  (заполнить)           ... addr n c      -> ...

Содержимое n байтов, начиная с адреса addr, заполняется младшим байтом c.

    BLANK (заполнить пробелами) ... addr n        -> ...

Эквивалентно FILL с заполнением кодом пробела.

    ERASE (стереть)             ... addr n        -> ...

Эквивалентно тексту 0 FILL.

    CMOVE (переслать)           ... addr1 addr2 n -> ...

Перемещение учаска в n байтов с началом addr1 по адресу addr2.

Слово CMOVE> отличается от CMOVE тем, что начинает перемещение с последнего байта участка. Различие этих слов существенно при перекрытии участков. Вот небольшой пример. Пусть на вершине стека лежит адрес участка памяти в 12 байтов, где записан текст "Форт-система". Тогда исполнение

    DUP 4 + 8 CMOVE

превратит этот текст в "ФортФортФорт", а если выполнить CMOVE> вместо CMOVE, то получится "ФортФорт-сис". Заметим, что с помощью этих слов можно выполнить иногда очень важные действия - размножить маленькие участки памяти внутри больших (например, числа внутри массивов) подобно тому, как в нашем примере размножилось слово "Форт".

Для заполнения участка памяти информацией, вводимой непосредственно с клавиатуры, имеется слово EXPECT (ожидать)

    EXPECT                      ... addr n        -> ...

Участок памяти от адреса addr длиной n байтов заполняется от начала к концу вводимыми с клавиатуры символами до тех пор, пока не заполнится весь участок или программист не завершит ввод (нажав клавишу ввода). Переменная SPAN содержит число введенных символов. Результат ввода автоматически выводится "на печать".

Строки

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

Слово " (кавычка) употребляется в конструкции

 "<текст>"               ...               -> ... addr

Текст <текст> будет превращен в строку, расположенную во временном буфере, адрес этой строки кладется в стек. Слово COUNT преобразует адрес строки в адрес и длину ее текста:

 COUNT                   ... addr          -> ... addr+1 n ,

а слово TYPE выводит текст (участок памяти) по его адресу и длине:

 TYPE  (напечатать)      ... addr n        -> ...
 В качестве примеров работы со строками рассмотрим тексты:
 "МОЛОДЕЦ" COUNT TYPE (напечатается МОЛОДЕЦ)
 "МОЛОДЕЦ" COUNT 3 - TYPE (напечатается МОЛО)

Разберите следующие примеры: слово S, размещает в кодофайле строку с данным адресом, а слово T, - ее текст. Оба слова оставляют на стеке адрес получившегося объекта:

 : T,    (a1 - адрес строки  -> a2 - адрес в кодофайле)
    HERE SWAP                 (a2 a1                  )
    COUNT                     (a2 a1+1 n              )
    HERE OVER ALLOT           (a2 a1+1 n a2           )
    SWAP CMOVE ;              (a2                     )
 : S,    (a1 - адрес строки  -> a2 - адрес строки в кодофайле)
    HERE SWAP
      DUP C@ 1+               (a2 a1 n+1              )
    HERE OVER ALLOT           (a2 a1 n+1 a2 - отведено n+1 байт)
    SWAP CMOVE ;

Слово ." употребляется в конструкции

    ."<текст>" ,

при выполнении которой текст <текст> будет выведен на терминал.

Упомянем ещё несколько возможностей вывода: слово CR переводит строки, а слово SPACE вставляет в выходной текст пробел (т. е. оно эквивалентно BL EMIT или ." " ).

Циклы со счетчиком

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

    DO <тело-цикла> +LOOP
    или
    DO <тело-цикла>  LOOP

Слово DO (делать) берет из стека два значения:

    DO   ... b a -> ,

где a - начальное значение параметра цикла, b - пороговое. Слово +LOOP (цикл) прибавляет в каждой итерации к параметру цикла число, которое берет из стека. Текст <тело-цикла> исполняется до тех пор, пока очередное значение параметра цикла не "перепрыгнет" через границу, проходящую между b-1 и b.

Конструкция DO ... +LOOP допускает и отрицательные приращения параметра цикла; именно с учетом этого и дана такая хитрая формулировка правила завершения цикла. Используемое чаще слово LOOP эквивалентно 1 +LOOP. В конструкции с этим словом <тело-цикла> исполняется b-a раз при b>a. При b<=a цикл DO ... LOOP выполняется 2147483648 + b-a раз.

Слово I помещает в стек текущее значение параметра цикла. Естественно, что циклы могут быть вложенными; значение параметра объемлющего цикла помещается в стек словом J.

Пример. Сумма квадратов первых n натуральных чисел:

    : SS2 (n -> сумма)
      0 SWAP 1+ 1 DO I DUP * + LOOP ;

Слово LEAVE обеспечивает немедленный выход из цикла (самого глубокого).

Возникает естественный вопрос, куда же девается значения a и b, которые DO снимает со стека, и откуда берется текущее значение параметра цикла.

В системе есть еще один стек - стек возвратов, о нем будет рассказано ниже.

Словарная статья

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

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

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

В системной части естественно выделяются:

1. Поле имени, содержащее имя слова, представленное в виде строки со счетчиком.

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

В программную часть включаются:

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

2. Поле параметра, или данные - облать памяти, используемая словом ( например, зарезервированная память в слове, определенном с помощью VARIABLE ).

При выполнении введенного слова его прежде всего ищут в словаре; если слово найдено, то оно исполняется, т.е. управление передается на поле кода этого слова.

Для выполнения каждого из этих двух действий по отдельности служат следующие слова:

    слово ` (читается штрих) употребляется в конструкции
    ` <слово>                ...             ->  ... addr

Слово <слово> должно быть уже определено. При выполнении этой конструкции адрес исполняемой части слова <слово> кладется на стек.

 Слово EXECUTE (выполнить)
    EXECUTE                  ... addr        ->  ...

снимает со стека адрес исполняемой части некоторого слова и выполняет это слово.

Таким образом, текст

    ` <слово> EXECUTE

эквивалентен тексту

    <слово>

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

Компиляция и исполнение

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

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

Предусмотренное в системе слово IMMEDIATE (немедленное) присваивает признак немедленного исполнения последнему определенному слову.

Для управления режимами исполнения и компиляции предусмотрены следующие слова:

[ - перевод системы в режим исполнения; естественно, слово [ обладает признаком немедленного исполнения.

] - перевод системы в режим компиляции.

Слово [COMPILE] (компилировать) выполняет принудительную компиляцию следующего за ним слова независимо от наличия у него признака немедленного исполнения. Само оно также имеет признак немедленного исполнения.

В выполняемые словом действия может входить компиляция других слов. Такая "компиляция компиляции" описывается конструкцией

    COMPILE <слово>

Заметим, что слово, включающее такие действия, имеет обычно признак немедленного исполнения.

В системе имеется удобное слово FIND (искать), служащее для поиска слов с проверкой признака немедленного исполнения:

FIND                    ... addr1         -> ... addr2 n

Здесь addr1 - адрес строки со счетчиком, содержащей имя слова. Число n принимает значение 0, если слово не найдено, 1, если слово найдено и имеет признак немедленного исполнения, -1, если этого признака нет. Значение addr2 в первом случае равно addr1, в остальных - задает адрес поля кода.

В отличие от ' слово FIND использует строку со счетчиком, это позволяет формировать образец поиска программным путем.

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

Для примера посмотрите, как можно реализовать слова IF и THEN:

    : IF COMPILE ?BRANCH HERE 0 , 2 : IMMEDIATE
    : THEN 2 ?PAIRS HERE SWAP ! ; IMMEDIATE

Слово ?PAIRS снимает со стека два числа и выдает сообщение об ошибке, если они не равны; это слово используется для проверки правильности записи управляющих конструкций; в данном примере номер 2 зарезервирован для условных операторов.

Таким образом, исполнениеслова IF компилирует адрес слова ?BRANCH, резервирует место для ссылки вперед (на обход ветви - IF) и оставляет адрес зарезервированого места на стеке. Слово THEN снимает этот адрес со стека и вписывает в него текущий адрес в кодофайле.

Заметьте, что стек, в режиме компиляции программистом не используемый, активно используется в процессе компиляции слов системой, поэтому изменять его во время исполнения определений (например, текстом [ DROP ] ) не рекомендуется.

Стек возвратов

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

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

Программист может временно хранить информацию в стеке возвратов с помощью слов:

 >R                      ... a   -> ...  | ...     -> ... a

число a снимается из стека и кладется в стек возвратов (справа от черты);

 R>                      ...     -> ... a| ... a   -> ...

число a снимается из стека возвратов и кладется в арифметический стек;

 R@                      ...     -> ... a| ... a   -> ... a

число a с вершины стека возвратов копируется в арифметический стек.

Пример. Описание слова 3DUP:

    : 3DUP  (a b c  -> a b c a b c)
       >R OVER OVER R@ -ROT R> ;

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

Исполнение слов

Теперь подробно опишем процедуру исполнения слов. Подчеркнем, что предлагаемое описание относится к прямому шитому коду; для косвенного кода эта процедура несколько иная.

Как уже говорилось, в системе имеется специальная программа - адресный интерпретатор, которая занимается исполнением слов, не записанных в машинных командах. Интерпретатор представляет собой программу с тремя режимами.

Исполнение слова начинается с вызова режима CALL (вызов), который устанавливает указатель интерпретации IP на начало кода данного слова. Прежнее значение IP, в котором хранилась точка вызова исполняемого слова, запоминается в стеке возвратов, что обеспечивает вложеное исполнение слов. Режим CALL завершается первым вызовом режима NEXT (следующий).

Режим NEXT передает управление по адресу, записанному в месте памяти, на которое указывает IP, одновременно передвигая IP на следующий элемент кода (т.е. прибавляя к нему 4).

Такие вложенные вызовы продолжаются до тех пор, пока управление не передано на машинную подпрограмму, которая и будет исполнена. В конце каждой такой программы записан вызов режима NEXT адресного интерпретатора. В конце каждого интерпретируемого кода записан вызов третьего режима интерпретатора - режим RETURN (возврат), его компилирует туда слово ; . Режим RETURN завершает исполнение слова. Он загружает IP значением, снимаемым со стека возвратов и вызывает режим NEXT.

Еще несколько замечаний к описанной процедуре:

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

2. Слово EXIT позволяет закончить обработку данного слова в любом его месте; понятно, что это слово осуществляет вызов режима RETURN.

Определяющие слова

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

Слова-генераторы описываются с помощью слов CREATE (создать) и DOES> (исполнить) в конструкции:

    : <имя-генератора> CREATE <создающая часть>
                       DOES> <исполняющая часть> ;

и употребляются в конструкции

    <имя-генератора> <имя>

для определения слова <имя> (сравните с использованием генераторов CONSTANT и VARIABLE).

При выполнении конструкции слово CREATE создает в кодофайле словарную статью для слово <имя>, а тем самым заносит слово <имя> в словарь системы, после чего исполняется текст <создающая часть>, который может зарезервировать в кодофайле дополнительное место и заполнить его по своему усмотрению, создав этим поле параметров слова. В дальнейшем при исполнении слова <имя> на стек кладется адрес поля параметров и выполняется текст <исполняющая часть>. Например,

    : CONSTANT CREATE ,      (слово , резервирует 4 байта   )
                             (и кладет в них число из стека )
      DOES>                  (на стеке адрес этих 4-х байтов)
        @ ;                  (значение помещается в стек    )

Таким образом, <исполняющая часть> слова-генератора является интерпретатором для поля параметров определяемого слова, в поле кода которого вызов интерпретатора как раз и содержит ссылку на нее.

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

    : ARRAY                   (в стеке лежит число элементов)
        CREATE DUP ,          (это число помещается в поле параметров)
        4 * ALLOT             (захват места для массива)
        DOES>                 (при вызове в стеке лежит индекс и помещается адрес)
        OVER 1 <              (захваченой памяти)
          IF   "ИНДЕКС МЕНЬШЕ 1 " COUNT TYPE DROP DROP
          ELSE OVER OVER @ > IF "ИНДЕКС БОЛЬШЕ ЧЕМ НАДО " COUNT TYPE DROP DROP
                             ELSE SWAP 4 * + THEN THEN ;

Арифметика двойной точности

(смотрите в оригинальной книге)

Форматный вывод

До сих пор рассматривались только самые простые способы вывода информации.

(смотрите в оригинальной книге)

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

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

    : HEX 16 BASE ! ;

Для возврата в десятичную систему используется слово DECIMAL (десятичная), которое без труда напишет читатель самостоятельно.

Описываемые ниже слова работают с буфером вывода, в котором формируется внешнее представление числа в виде строки символов. Форматное преобразование начинается словом <# , которое устанавливает на конец буфера, так как формирование буфера идет от конца. Слово HOLD (сохранить) переносит литеру со стека в буфер и продвигает указатель буфера вперед на одну позицию. Слово #> завершает преобразование и кладет на стек адрес сформированной в буфере строки литер и ее длину.

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

Полный перевод числа выполняет слово #S, которое оставляет на стеке нуль - результат последнего деления:

    : #S (d -> 0)
       BEGIN # DUP 0 = UNTIL ;

Для вывода знака минус предусмотрено слово SIGN (знак):

    : SIGN (n -> )
       0< IF C'' - HOLD THEN ;

Итак, все готово для определения слова . ;

    : . (d -> )
      DUP ABS
      <# #S SWAP SIGN #>
      TYPE SPACE DROP ;

В качестве примера форматного вывода создадим два полезных слова. Первое печатает номер телефона в стандартном виде:

    : .PHONE (d -> )
       <# # # # C'' - HOLD
            # # C'' - HOLD #S #> TYPE ;

Проверим правильность этого слова:

    4595976 .PHONE
    459-59-76

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

    2ч37м42.93с

Введем два вспомогательных слова. Слово SIXI устанавливает шестиричную систему счисления. Слово #MS выдает минуты или секунды:

    : SIXI
        6 BASE ! ;
    : #MS (d -> d/60)
        # SIXI # DECIMAL ;

Слово .TABLEAU собственно и выводит результаты забега:

    : .TABLEAU 
         <# C'' с HOLD # #
            C'' . HOLD #MS
            C'' м HOLD #MS
            C'' ч HOLD #S #> TYPE ;

Внешняя память

(смотрите в оригинальной книге)

Примеры работы форт-системы

(смотрите в оригинальной книге)

Пример работы структур управления

(смотрите в оригинальной книге)

Пример работы определяющих слов

(смотрите в оригинальной книге)

Реализация языка форт

(смотрите в оригинальной книге)

Приложение к. таблица кодов кои-8

(смотрите в оригинальной книге)

Литература

(смотрите в оригинальной книге)

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог