[Главная]  [Форт]  [Услуги]  [Клиенты]  [Контакты]

          ОСОБЕННОСТИ РЕАЛИЗАЦИИ КОНСТРУКЦИИ CREATE DOES>.

1. ВСТУПЛЕНИЕ

        Разрабатывая свою "домашнюю"  Форт-машину я столкнулся с
необычным явлением (в Форте вообще мало обычного) - конструкцией
CREATE/DOES>. Поиски детального объяснения работы этой конструк-
ции (на  примитивном уровне)  привели  к появлению  этой статьи.
Надеюсь она поможет тем, кто испытывает аналогичные затруднения.

        В статье используется ассемблер семейства x86 в диалекте
NASM'а. Также  следует  отметить,  что  в  своей Форт - машине я
использовал Subroutine Threaded Code (STC) with inline expansion
для микропроцессора Intel 80386 (это влияет на способ  генерации
и тип генерируемого кода).

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


2. СОЗДАНИЕ ФОРТ-СЛОВА С ПОМОЩЬЮ CREATE

Приступая к исследованию необходимо обратиться к первоисточнику-
ANSI X3.215-1994 ( далее приводиться перевод  Сергея Кадочникова
2:4657/33.3):

----------------------------------------------------------------
6.1.1000   CREATE                                           CORE

            ( "name" -- )

Пропускает ведущие разделители пробелы. Выделяет  name,  ограни-
ченное  пробелом.  Создает определение для name с семантикой вы-
полнения , определенной  ниже.  Если указатель области данных не
выровнен , резервирует достаточно  области данных для его вырав-
нивания. Новый указатель  области данных определяет  поле данных
name. CREATE не распределяет область данных в поле данных name.

        name Выполнение: ( -- a-addr )

a-addr - адрес поля данных name. Семантика выполнения name может
быть расширена использованием DOES>.

См.: 3.3.3 Область данных, 6.1.1250 DOES>.
----------------------------------------------------------------

Ну что же, достаточно туманно. Для начала - слова CREATE и DOES>
не являются словами немедленного исполнения (т.е. не IMMEDIATE).
А теперь попытаемся вчитаться в текст.

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

Пункты 1-2 более-менее понятны, а что же за код упоминается в 3?
Например, это может быть следующий код:

        POP     EDX
	PUSH	DWORD FREE_MEM ;занесение offset FREE_MEM в стек
	PUSH    EDX
	RET
FREE_MEM:

Пояснения:
1) Добавляемый код-это не ссылка на подпрограмму- каждому новому
   слову наново добавляется указанный код.
2) Соответственно смещение метки FREE_MEM - каждый раз разное.
3) В приведенном примере эмулируется стек возвратов через конст-
   рукцию POP EDX/PUSH EDX

Скажем,можно избавиться от пункта 2,применив захардкодив следую-
щее:

        CALL    .a1          ; думаю, знакомая конструкция ;)
.a1:    POP     EAX          ; в EAX - смещение .a1
	ADD	EAX, .a2-.a1 ; прибавляем дельту
        POP     EDX
	PUSH    EAX
	PUSH    EDX
	RET
.a2:

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

CREATE MASSIV 10 CELLS ALLOT

После выполнения слова MASSIV на вершине стека окажется адрес 10
выделенных ячеек (Форт-ячеек) памяти


3. МОДИФИКАЦИЯ ПОВЕДЕНИЯ СЛОВА - DOES>

Ну что же - с CREATE мы  все выяснили  теперь давайте рассмотрим
DOES>.

----------------------------------------------------------------
6.1.1250   DOES>                                            CORE
        Интерпретация: Семантика интерпретации для этого слова не
                       определена.

       Компиляция: ( C: colon-sys1 -- colon-sys2 )

Добавляет семантику времени-выполнения ниже к текущему определе-
нию. В любом случае текущее определение представленное находимым 
в  словаре  при  компиляции DOES>  - определенное   реализацией.
Потребляет colon-sys1 и производит colon-sys2.Добавляет семанти-
ку  инициирования, данную ниже к текущему определению.

       Время-выполнения: ( -- ) ( R: nest-sys1 -- )

Заменяет семантику выполнения самого последнего определения,упо-
минаемого как name , семантикой  выполнения  имени  данной ниже.
Возвращает управление  на  вызывающее  определение, определенное
nest-sys1. Неопределенная ситуация  существует если name не было
определено CREATE, или определенным пользователем словом которое
вызывает CREATE.

       Инициирование: ( i*x -- i*x a-addr ) ( R:  -- nest-sys2 )

Сохраняет зависящую-от-реализации  информацию  nest-sys2 о вызы-
вающем определении. Размещает адрес поля  данных name  на стеке.
Состояние стека i*x представляет параметры name.

       name Выполнение: ( i*x -- j*x )

Выполняет  часть  определения , которая  начинается с  семантики
инициирования добавленной изменившим name DOES>. Состояния стека
i*x и j*x представляют параметры, и результаты name , соответст-
венно.

        См.: 6.1.1000 CREATE.
----------------------------------------------------------------

Слово DOES> должно выполнять:
1) Последнему  созданному  слову добавить следующую функциональ-
   ность.
2) Выполнить операцию RET из вызвавшей его слова-подпрограммы.

Что это значит? Давайте рассмотрим действия по пунктам:

1) Модифицировать код уже сформированного слова плохо - мы можем
   получить проблемы при переносе Форт-машины на другую платфор-
   му. Поэтому создадим новый код. Делать он будет следующее:

   а) Сгенерировать код  эмулирующий вход в  подпрограмму ( все-
      таки архитектура x86 содержит один стек).
   б) сделать CALL на "старый" код слова (т.е. получим на верши-
      ну стека адрес выделенной памяти
   в) сделать  jmp  на следующую  инструкцию определяющего слова
      (этот адрес лежит в стеке возвратов).

   После  этого  необходимо ассоциировать  новый код с созданным
   словом. Все.
2) Тут все просто - надо  сделать  DROP  из стека возвратов один
   адрес.  И выйти. Это гарантирует что код, следующий  за DOES>
   не выполниться.

Дотошный читатель  может задать вопрос: "а что же будет в дейст-
вительности происходить  при вызове модифицированного  слова ?".
Давайте посмотрим:

1) Отработает код входа в слово (эмулирующий стек возвратов).
2) CALL на старый код (получаем выделенную память).
3) JMP внутрь определяющего слова
4) Выполнение инструкций определяющего слова.
5) Выход из определяющего слова. Фактически - выход из пункта 1-
   все-таки мы делали jmp в пункте 3.

Теперь получится что заработает конструкция вида:

: CONSTANT CREATE , DOES> @ ;

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

%MACRO FRT      4
%2_lfa:	dd      %1_lfa	; предыдущий lfa
%2_nfa:	db      %3,%4
%2:
%ENDM

После чего используют этот макрос в виде:

FRT ??? , FRT_EMIT, 4, 'EMIT'
	POP	EDX
	POP	EAX
        PUSH    EDX
        MOV     DL,AL
        MOV     AH,2
        INT     21h
	RET

FRT FRT_EMIT, FRT_KEY, 3, 'KEY'
	MOV	AH,01h
	INT     21H
	XOR	ECX,ECX
	MOV	CL,AL
	POP	EDX
	PUSH	ECX
	PUSH	EDX
	RET

Как видите,  лучше завести в словарной статье поле %2_cfa, кото-
рое будет  содержать адрес  машинных команд  слова. Тогда мы без
трудностей ( связанных с  модификацией  кода созданного CREATE )
можем изменить функциональность нашим DOES> . Макрос  FRT в моей
версии:

%MACRO FRT	4
%2_lfa:	dd      %1_lfa  ; предыдущий lfa
%2_nfa:	db      %3,%4
%2_cfa:	dd      %2      ; смещение кода статьи
%2:
%ENDM


                                       (C) Коменда Вячеслав 2006
                                  v_komenda AT developers.com.ua




© Коменда Вячеслав 1999-2006

Hosted by uCoz