Блог программатора

Our partner
Главная | Последние правки | Поиcк | Все страницы | Редактор | Админ | Печать

Особенности реализации конструкции CREATE DOES>

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

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

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

Создание Форт-слова с помощью CREATE

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


6.1.1000 CREATE CORE

( "<spaces>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 должно выполнять следующие шаги:
  • Получить следующие слово из входного потока.
  • Создать слово в словаре.
  • Созданному слову добавить функциональность которая будет записывать в стек адрес следующей свободной ячейки памяти (после выполнения CREATE).

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

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

Пояснения:
  • Добавляемый код-это не ссылка на подпрограмму- каждому новому слову наново добавляется указанный код.
  • Соответственно смещение метки FREE_MEM - каждый раз разное.
  • В приведенном примере эмулируется стек возвратов через конструкцию 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 выделенных ячеек (Форт-ячеек) памяти

Модификация поведения слова - 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> должно выполнять:
  • Последнему созданному слову добавить следующую функциональность.
  • Выполнить операцию RET из вызвавшей его слова-подпрограммы.

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

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

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

После этого необходимо ассоциировать новый код с созданным словом. Все.

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

Дотошный читатель может задать вопрос: "а что же будет в действительности происходить при вызове модифицированного слова ?". Давайте посмотрим:
  • Отработает код входа в слово (эмулирующий стек возвратов).
  • CALL на старый код (получаем выделенную память).
  • JMP внутрь определяющего слова
  • Выполнение инструкций определяющего слова.
  • Выход из определяющего слова. Фактически - выход из пункта 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

Коментарии

Отслеживать новые комментарии

Прокоментируйте эту статью!

Автор:
Введите текст с картинки:
Коментарий


Главная
Софт
Хард
Политеги

SimpleWiki 

Почта 



Мой номер ICQ
 456824974 

Архив:

01.2010
02.2010
03.2010
04.2010
05.2010
06.2010
07.2010


Радио «Анонимус» 
Реклама java
Wed, 30 Jun 2010 13:56:00 -0400
Копипаста: История программных революций от Microsoft
Mon, 14 Jun 2010 04:19:00 -0400
Как восстановить grub2 и mbr
Thu, 10 Jun 2010 17:33:00 -0400
Установить/сменить пароль администратора на свежем postgresql'е
Sun, 23 May 2010 18:18:00 -0400
Президент Виктор Янукович получил по морде венком
Tue, 18 May 2010 09:31:00 -0400
Наконец-то ! Контакт с инопланетянами !
Thu, 18 Mar 2010 08:01:00 -0400
Панкота: группа Флiт
Sat, 13 Mar 2010 08:11:00 -0500

Blog

Add bookmark:
Bookmark and Share



© Komenda Viacheslav
Запрещается перепечатка материалов, без письменного разрешения автора.

Последнее обновление: Tue, 05 May 2009 23:24:59 -0400