Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Язык программирования (и ОС) ФОРТ (FORTH) ОСОБЕННОСТИ РЕАЛИЗАЦИИ КОНСТРУКЦИИ CREATE DOES>


v_kom.....@mail.ru

Здравствуйте, Дмитрий !

Хочу предложить для Вашей рассылки статью - я попытался описать работу
CREATE/DOES>. Это черновой вариант, плс, высскажите замечания (либо опубликуйте).

Меня немного смущает статья - боюсь что получилось сумбурно. Кроме того - не
уверен что правильно описываю методику генерации кода как STC - по просту говоря я
генерирую нативный x86 код (ессно с вкраплениями CALL).

--
With best regards, Komenda Vyacheslav
Icq: 34365136

------------------------------------------------------------------------

ОСОБЕННОСТИ РЕАЛИЗАЦИИ КОНСТРУКЦИИ 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

В избранное