Автор:
Бред Родригес
Дата публикации: 1993
оригинал статьи взят
MOVING FORTH
Перевел:
mOleg
Портирование Форта
Часть 1: Конструктивные решения в ядре Форт-систем.
Введение
Все Форт-программисты рассказывают о том, как легко переносить Форт на новый процессор. Однако, подобно другим «легко» и «понятно» об этом написано не так много, как оно того заслуживает. Таким образом, когда Билл Киблер предложил эту тему для статьи, я согласился нарушить великую говорильную традицию фортописателей и задокументировал этот процесс подробно.
В данном цикле статей я разработаю Форты для процессоров 6809, 8051 и Z80.
Я сделаю 6809 для иллюстрации простоты традиционной модели Форта, плюс, я уже опубликовал ассемблер для 6809 [ROD91,ROD92], и мне он в будущем нужен для проектов TCJ. Я делаю 8051 Форт для университетского проекта, но так же иллюстрирую несколько очень различных дизайнерских решений. Z80 Форт для всех CP/M читателей TCJ, и некоторых друзей TRS-80.
Используемое «железо»
Вы должны выбрать процессор. Я не буду вдаваться в преимущества одного процессора перед другим для реализации Форта, выбор процессора обычно ведется по другим критериям. Кроме того, предмет данной статьи показать как портировать Форт на любой процессор.
Вы можете ожидать, что обычное 16-битное ядро (см ниже) будет занимать примерно 8Кбайт пространства памяти. Для полного ядра, которое может компилировать Форт-определения необходимо минимум около 1Кбайта ОЗУ. Для использования блочной системы хранения данных на диске, вам будет необходимо добавить приблизительно 3Кбайта под буфера. Для 32 битных Фортов эти значения необходимо удваивать.
Это минимум, достаточный для создания Форт-ядра и его запуска. Для выполнения приложений на вашем «железе» вам необходимо увеличить размеры ПЗУ и ОЗУ, чтобы они могли вместиться.
16 или 32 бита?
Размер ячейки данных, используемой Фортом, не обязан совпадать с разрядностью процессора. Наименьший практически используемый Форт основан на 16-битной модели, то есть и целые числа и адресные ссылки помещаются в 16 бит. Форт-сообщество называет это размером ячейки (CELL).
8-битные процессоры практически всегда работают с 16-битными Фортами. Это обычно требует кодирования арифметики двойной длины, хотя некоторые из 8-и битников имеют небольшое количество 16-битных операций.
16-битные процессоры обычно используют 16-битные Форты, хотя аналогичная техника работы с арифметикой двойной точности может быть использована для написания 32 битных Фортов на 16 битном процессоре. Как минимум один 32 битный Форт написан для 16-битных 8086/8088.
32-битные процессоры типично выполняют 32-битные Форты. Меньшие модели Форта редко выигрывают в размерах и скорости исполнения. Тем не менее, я знаю как минимум один 16-битный Форт, написанный под 32-битный 68000. Это позволяет уменьшить размер приложения приблизительно в два раза, до тех пор, пока определения будут вмещаться в 16-битное адресное пространство. (Это станет понятно в скорости) Большинство 68000ых обычно имеют достаточно памяти.
Все примеры, описанные в данной статье – 16 битные Форты, работающие на 8-битных процессорах и микроконтроллерах.
Техника адресной интерпретации
«Шитый код» - это признак Форта. Список адресов исполнимых подпрограмм образуют Форт – цепочки иначе треды (thread). Вы можете рассматривать это как список подпрограммных вызовов, в котором опкоды CALL удалены. За много лет появилось много вариаций техники адресной интерпретации тред, которые наиболее удобно подходят к конкретным процессорам и для конкретных приложений. Для принятия решений вам необходимо понимать как они устроены, их особенности.
Косвенный шитый код.
Это классическая Форт-техника адресной интерпретации, используемая в fig-Forth и F83, и описанная в большинстве книг, посвященных Форту. Все остальные техники адресной интерпретации являются «улучшениями» данной, таким образом вам необходимо сначала разобраться в устройстве косвенного Шитого Кода(ШК далее), чтобы иметь возможность оценить и понять остальные методики адресной интерпретации.
Давайте посмотрим на определение Форт-слова SQUARE:
: SQUARE DUP * ;
В типичном Форте, основанном на Косвенном ШК, код будет расположен в памяти согласно рисунку 1. (заголовок словарной статьи будет обсужден в следующей статье, потому что он используется во время компиляции и не влияет на адресную интерпретацию).
Предполагается, что SQUARE вызывается из другого Форт-слова. Указатель интерпретации (IP) должен указывать на ячейку в памяти, хранимую внутри этого «другого» слова, которое содержит адрес слова SQUARE. (Если быть точным, эта ячейка содержит адрес поля кода слова SQUARE's.) Интерпретатор извлекает этот адрес, и затем использует его для извлечения содержимого поля кода слова SQUARE. Это содержимое так же является адресом подпрограммы машинного языка, которая выполняет слово SQUARE. В псевдокоде это выглядит так:
(IP) -> W извлечь содержимое памяти, на которое указывает IP в регистр W
...W теперь хранит адрес поля кода
IP+2 -> IP увеличить IP, аналогично тому, как это происходит со счетчиком команд
(предполагается 2-х байтовые адреса)
(W) -> X извлечь содержимое памяти, по адресу, хранимому в W в регистр Х
...X теперь хранит адрес машинного кода
JP (X) выполнить переход на адрес, хранящийся в регистре X
Это иллюстрирует важный, но редко освещаемый принцип: адрес Форт-слова, которому только что было передано управление, хранится в регистре W. Низкоуровневым словам эта информация не нужна, но все другие виды Форт-слов ее используют.
Если бы SQUARE было написано в маш.коде, это был бы конец истории: этот кусок машинного кода был бы исполнении, и в последствии отдал бы управление адресному интерпретатору – который, с момента увеличения IP, указывает на следующее слово, которое должно быть исполнено. Именно поэтому адресный интерпретатор Форт-системы обычно называют словом NEXT (следующий).
Но, SQUARE – это высокоуровневое «двоеточное» определение, оно хранит список адресов. В порядке исполнения этого определения адресный интерпретатор должен перезапуститься на новом месте: поле параметров слова SQUARE. Конечно же, старое значение должно быть сохранено, для продолжения выполнения вызывающего слова после завершения SQUARE. Это похоже на подпрограммный вызов! Действие машинной части слова SQUARE заключается лишь в заталкивании старого значения IP в стек возвратов, и установке IP на новое место, затем вызова NEXT, и после завершения SQUARE вытолкнет старое IP. (как вы видите, IP - это «программный счетчик» высокоуровневого Форта). Это называется DOCOLON или ENTER в различных Фортах, и выглядит так:
PUSH IP сохранить IP на стеке возвратов
W+2 -> IP W продолжает указывать на поле кода, поэтому W+2 -- это адрес поля данных слова! (Предполагается 2-байтовые адреса – в других Фортах могут быть отличия)
JUMP переход на адресный интерпретатор ("NEXT")
Этот идентичный фрагмент кода используется всеми высокоуровневыми Форт-определениями. Именно поэтому указатель на данный фрагмент кода, а не сам фрагмент, включен в Форт-определение. Более сотен определений сохраняют память. Именно поэтому это называется косвенным кодом.
Возврат из «подпрограммы» - слово EXIT , компилируемое в код, когда Форт видит ‘;’.
(Некоторые Форты называют его ;S вместо EXIT). EXIT исполняет «низкоуровневый» естественный для машины код, делающий следующее:
POP IP - вернуть адрес с вершины стека возвратов
JUMP - выполнить переход на NEXT
Просмотрите несколько вложенных Форт-определений, чтобы убедиться, что это работает.
Заметка по поводу характеристик косвенного ШК: каждое Форт-слово имеет в начале одноячеечное (CELL) поле кода. Высокоуровневое определение содержит по одной ячейке для каждого используемого в определении слова (каждой ссылки на вложенное определение). Адресный интерпретатор должен производить двойное чтение из памяти для получения адреса следующего куска исполняемого машинного кода (сначала чтение в регистр W по адресу, хранящемуся в IP, потом чтение данных с адреса, хранящегося в W).
Использование косвенного ШК не предполагает ни выигрыша в скорости ни выигрыша в памяти. Он может быть простейшим, хотя прямой ШК (далее разбираемый) не сложнее.
Так почему так много Фортов используют Косвенный ШК? В основном, потому что предыдущие Форты используют, как модель, косвенный ШК. В настоящее время прямой ШК становится все более популярным.
Так когда нужно использовать косвенный ШК? Среди различных техник адресной интерпретации, косвенный ШК производит наиболее прозрачные и элегантные определения – ничего кроме адресов. Если вам созвучны такие размышления, косвенный ШК может понравиться. Простота и однообразность представления косвенного шитого кода могут улучшить переносимость. Косвенный шитый код – это классическая Форт-модель, поэтому он предпочитаем для обучения. Наконец, в зависимости от процессора вызов подпрограммы (например 1802) косвенный шитый код быстрее прямого.
Прямой шитый код
Прямой шитый код отличается от косвенного только в одном: вместо адресной ссылки поле кода содержит машинный код.
Я не говорю, что завершенный код слова ENTER содержится в любом и каждом высокоуровневом определении! В высокоуровневых Форт-определениях поле кода будет содержать команду call, как изображено на Рис.2. Двоеточные определения, к примеру, будут содержать CALL на подпрограмму ENTER.
Псевдокод NEXT для прямого ШК прост:
(IP) -> W извлечь значение памяти по адресу в IP в регистр "W"
IP+2 -> IP увеличить IP (предполагаются двухбайтовые адреса)
JP (W) перейти на адрес, хранящийся в регистре “W”.
Это выигрыш в скорости: интерпретатор теперь производит только одно чтение адреса.
На Z80-ом, это уменьшает подпрограмму NEXT – наиболее часто используемый фрагмент кода в Форт-ядре – с одиннадцати инструкций до семи!
Это стоит памяти: каждое высокоуровневое определение на Z80 (для примера) теперь длиннее на один байт с момента, когда 2-байтовый адрес был заменен на 3-байтный вызов. Но это не универсальное правило. 32-битный 68000 Форт может заменить 4-х байтный адрес 4х байтной инструкцией BSR, и обойтись без потерь памяти. И на Zilog Super8, который имеет машинную инструкцию для косвенного ШК, 2-х байтный адрес заменяется 1-байтной инструкцией Enter, делая Форт на основе косвенного ШК короче на один байт(на вызов)!
Конечно, определения в косвенном ШК на два байта короче, до тех пор пока им не нужен указатель!
Ранее я думал, что выскокоуровневые определения в Фортах с прямым ШК требуют использования подпрограммного вызова в поле кода. Однако Форт Френка Сергеанта Pygmy Forth [SER90] продемонстрировал, что простой переход может так же просто использоваться как и call, и при этом быть быстрее.
Гай Келли создал прекрасный обзор реализаций Фортов для IBM PC [KEL92], который я советую изучить всем писателям ядер Форт-систем. Из 19 изученных им фортов 10 используют прямой, 7 – косвенный, и два – подпрограммный ШК(обсуждается далее).
Я рекомендую использовать прямой ШК вместо косвенного для всех новых Форт-ядер.
Прыжок на NEXT или инлайн код?
Адресный интерпретатор NEXT обычная подпрограмма для всех низкоуровневых слов. Вы можете хранить только одну копию этой общей подпрограммы, и заставлять все низкоуровневые слова прыгать на него. (Замечание. CALL на NEXT не нужен – достаточно простого перехода.)
Тем не менее, скорость выполнения NEXT критично влияет на общую скорость работы системы. Так же, на многих процессорах подпрограмма NEXT очень короткая, часто только две или три инструкции. Таким образом, может быть предпочтительно инлайнить код NEXT везде, где он используется. Это часто делается с помощью определения NEXT, как ассемблерного макроса.
Это пример простого компромисса между размером используемого пространства и скоростью исполнения: инлайновый NEXT всегда быстрее, но почти всегда больше. Общий размер увеличения – это количество дополнительных байт, требуемых для инлайнового расширения, времени – количество низкоуровневых слов в системе. Иногда проигрыша нет вообще: на 6809 с косвенным ШК инлайновый NEXT короче инструкции перехода (JUMP).
Подпрограммный шитый код.
Высокоуровневые Форт-определения представляют список подпрограмм к исполнению.
Вам не нужен адресный интерпретатор для этого, вы получаете тот же эффект за счет простого перечисления списка подпрограммных вызовов:
SQUARE: CALL DUP
CALL * ; или подходящее имя слова
RET
См. Рисунок 3. Это представление Форт-слов, используемых как стартовая точка, для объяснения техники адресной интерпретации для программистов на ассемблере. [KOG82].
Подпрограммный ШК элегантен; высокоуровневые и низкоуровневые слова идентичны. Определяющие слова (Переменные, константы и подобные им) представлены так же, как в прямом ШК – поля кода начинаются с перехода или вызова некоего машинного кода.
Основной недостаток заключается в том, что подпрограммные вызовы обычно больше простых адресов. Для примера, на Z80 размер высокоуровневых определений увеличивается в два раза(и большинство кода приложения – высокоуровневые определения)! Обратный пример: на 32 битном процессоре 68000 вообще может не быть увеличения размера в случае замены 4-х байтными BSR. (Но, если размер вашего кода превышает 64КБ, некоторые из этих адресов будут заменены 6-байтными JSR).
Подпрограммный ШК может быть быстрее прямого шитого. Вы выигрываете во времени за счет избавления от адресного интерпретатора, но теряете время, потому что каждая ссылка на Форт определение требует сохранения и извлечения адреса возвратов на стеке возвратов. В Фортах с Косвенным ШК только высокоуровневые слова вызывают активность стека возвратов. На 6809 или Zilog Super8, косвенный ШК быстрее подпрограммного!
Есть и другое преимущество подпрограммного ШК: в нем не нужно выделять регистр процессора под указатель IP. Некоторые процессоры, подобные 8051, имеют очень мало адресных регистров. Убирание IP реально упрощает ядро и делает его быстрее.
Единственный путь быть уверенным – писать примеры. Это прямо связано с выбором регистра, обсуждаемом в следующей секции.
Подпрограммный шитый код с инлайн вставками,
оптимизация,
прямая компиляция.
На старых и 8-битных процессорах, почти каждый Форт-примитив состоит из нескольких машинных инструкций. Но на более мощных процессорах, много Форт-примитивов пишутся в одну инструкцию. К примеру, на 32-битном 68000, DROP выглядит как одна команда:
ADDQ #4, An Где An это Forth's PSP регистр.
В Форте с подпрограммным ШК, использование слова DROP может вылиться в последовательность:
BSR ...
BSR DROP -------> DROP: ADDQ #4,An
BSR ... <------- RTS
ADDQ это двухбайтовая команда. Зачем писать четырехбайтный подпрограммный вызов для двухбайтовой инструкции? Не важно, как часто используется DROP, в этом нет выигрыша! Код будет меньше и быстрее если ADDQ вкомпилирован непосредственно в поток BSRов. Некоторые Форт-компиляторы делают такие «инлайн вставки» низкоуровневых слов.
Недостатком инлайн вставок является сложность декомпиляции кода. До тех пор, пока используются подпрограммные вызовы, вы имеете указатели (адреса подпрограмм) на Форт-слова внутри разбираемых «двоеточных» определений. Имея указатели на слова вы можете получать их имена. Но, как только в слово вставляется инлайн код, все знание откуда он пришел теряется.
Преимущества инлайн вставок, если не считать выигрыш в скорости и размере, потенциал для оптимизации кода. К примеру, Форт-последовательность:
3 +
Будет скомпилирована на 68000 подпрограммном ШК как:
BSR LIT
.DW 3
BSR PLUS
но может быть представлена инлайн вставкой всего одной машинной инструкции!
Оптимизирующие Форт-компиляторы слишком обширный вопрос, находящийся за рамками данной статьи. Это активная область исследований языка Форт, смотрите [SCO89] и [CUR93b]. Конечная кульминация оптимизации подпрограммного ШК – это Форт, который компилирует «чистый» машинный код, подобно Си или Фортран компиляторам.
Токенизированный шитый код.
Прямой и Подпрограммный ШК созданы для повышения скорости работы Форт-программ за счет увеличения размера используемой памяти. Теперь давайте посмотрим в другую сторону от Косвенного ШК – уменьшения размеров кода за счет понижения скорости работы.
Двоеточное Форт-определение представляет собой последовательный список исполнимых адресов. Предположим 16-битная Форт-система имеет максимум 256 различных слов. Затем, каждому слову может быть присвоен уникальный 8-битный номер. Вместо списка 16-битных адресов, вы можете использовать список 8-битных идентификаторов, или, «токенов», и размер двоеточного определения уполовинится.
Форт с токенизированным шитым кодом содержит таблицу адресов всех Форт-слов, как показано на Рис.4. Значение токена затем используется для индексации в этой таблице, для нахождения Форт-слова, соответствующего данному токену. Это добавляет один уровень косвенности адресному интерпретатору, поэтому это медленнее ранее перечисленных вариантов шитого кода.
Принципиальное преимущество токенизированного ШК – малый размер. Токенизированный ШК наиболее часто используется в наладонных компьютерах, и других приложениях со строго ограниченными размерами памяти. Так же таблица «адресов входов» во все слова может упростить линкование независимо откомпилированных модулей.
Недостаток токенизированного кода – низкая скорость, токенизированные Форты самые медленные. Так же, токенизированные компиляторы несколько более сложны. Если вам нужно более 256 Форт-слов, необходимо иметь систему декодирования, позволяющую смешивать 8-битные и более длинные токены.
Я могу представить 32-битный Форт, использующий 16-битные токены, но как много 32-битных систем сильно ограничены в размерах памяти?
Сегментированный шитый код
В мире так много 8086, что сегментый ШК заслуживает краткого упоминания. Вместо использования «нормальной» байтовой адресации в пределах 64Кбайтного сегмента, используются адреса параграфов (в 8086 параграфы 16-байтные). Адресный интерпретатор может загружать эти адреса в сегментные регистры, вместо использования привычных адресных регистров. Это позволяет 16-битному Форту эффективно работать с полным мегабайтом памяти 8086 процессора.
Принципиальный недостаток сегментированного ШК – это 16байтная «грануляция» памяти системы. Любое Форт-слово должно быть выровнено на 16-байтовую границу. Если Форт-слова имеют случайные длины, в среднем 8 байт будет потеряно на каждом.
Резервирование регистров
Следующим наиболее важным соглашением в дизайне, после техники адресной интерпретации идет использование регистров процессора. Это, вероятно, наиболее сложная часть. Количество доступных регистров процессора может определять используемую технику адресной интерпретации, и даже структуру и распределение памяти.
Базовые регистры Форта
Классическая Форт модель имеет пять «виртуальных регистров». Эти абстрактные сущности используются в примитивах. NEXT, ENTER, и EXIT ранее были определены с использованием этих регистров.
Каждый из них имеет ширину в одну ячейку, то есть на 16 битных Фортах они 16-и битные (Существуют исключения из этого правила, как вы увидите позднее). Не все они могут быть процессорными регистрами. Если ваш процессор не имеет достаточного количества регистров, часть из них может храниться в памяти. Я опишу их в порядке важности, то есть сверху списка находятся наилучшие кандидаты для хранения в памяти.
W – это рабочий регистр. Он используется для многих вещей, включая индексацию памяти, поэтому он должен быть адресным регистром; то есть вы должны иметь возможность извлекать и сохранять данные памяти используя этот регистр. Вам так же необходима возможность произведения арифметических операций над ним. (в Фортах с прямым ШК, вы так же должны уметь выполнять косвенный переход относительно W). W используется интерпретатором в каждом Форт-слове. В процессорах, имеющих только один регистр, вы можете использовать его для хранения W, и держать все остальное в памяти (система, конечно, будет ужасно медленной)
IP – указатель интерпретации. Используется каждым Форт-словом (через NEXT, ENTER, EXIT). IP должен быть адресным регистром. Вам так же необходима возможность инкрементировать его. Системы с подпрограммным ШК его наличия не требуют.
PSP указатель на стек данных, иногда просто называют SP. Я предпочитаю PSP, потому что SP часто используется как имя регистра процессора, и имена не должны путаться. Большинство низкоуровневых определений это используют. PSP должен быть указателем стека, либо адресным регистром, который может инкрементироваться или декрементироваться. Так же будет плюсом, если вы сможете его использовать как индексный регистр.
RSP – указатель вершины стека возвратов, иногда просто называемый RP. Он используется высокоуровневыми определениями с косвенным и прямым ШК, и всеми словами в Фортах с подпрограммным ШК. RSP может быть указателем стека, или адресным регистром, который может быть увеличен и уменьшен.
Если вообще возможно, храните W, IP, PSP и RSP в регистрах процессора. Следующие далее виртуальные регистры могут храниться в памяти, но обычно ради увеличения скорости их хранят в регистрах процессора.
Х – рабочий регистр, не считаемый одним из «классических» Форт-регистров, даже в классическом Форте с косвенным ШК, где он требуется для второго рызыменования. В косвенном ШК вы можете делать переход косвенно, используя Х. Х так же может использоваться немногими низкоуровневыми словами для реализации арифметики и подобного. Он очень важен в процессорах, которые не могут использовать память как операнд. К примеру, ADD на Z80 может выглядеть (в псевдокоде):
POP W POP X X+W -> W PUSH W
Иногда используется другой рабочий регистр Y.
UP это пользовательский указатель, хранящий базовый адрес пользовательской области памяти задачи. UP обычно добавляется к смещению, и используется в высокоуровневом Форт-коде, поэтому он может быть сохранен не в регистре. Но если процессор позволяет делать индексную адресацию с использованием регистра UP, низкоуровневые определения могут легче и быстрее работать с пользовательскими переменными. Если вы имеете избыток адресных регистров, используйте один для UP. Однозадачные Форт-системы не нуждаются в этом регистре.
Х, если он нужен, важнее хранить в регистре, нежели UP. Из всех виртуальных регистров UP проще всего переместить в память.
Использование аппаратного стека
Большинство процессоров содержат указатель стека как часть собственной конструкции, используемый для обработки прерываний и подпрограммных вызовов. Как он должен использоваться? Стоит ли его использовать как стек данных или как стек возвратов?
Короткий ответ – как удобнее. Надо сказать, что PSP используется чаще, чем RSP в Фортах, основанных на косвенном и прямом ШК. Если ваш процессор имеет немного адресных регистров, а команды PUSH и POP быстрее, чем прямые ссылки, используйте аппаратный стек для хранения данных.
С другой стороны, если ваш процессор богат режимами адресации, и позволяет индексную адресацию, выгодно использовать PSP как адресный регистр общего назначения. В таком случае используйте аппаратный стек, как стек возвратов.
Иногда не делайте оба варианта! Аппаратный стек TMS320C25 имеет глубину в 8 элементов, замечательно, но бесполезно для Форта. Таким образом, его аппаратный стек используется только для прерываний, и оба регистра PSP и RSP – базовые адресные регистры. (АНСИ стандарт требует минимум 32 ячейки стека данных и 24 под стек возвратов, я предпочитаю 64 ячейки на каждый).
Вы можете случайно подумать, что аппаратный стек должен быть стеком данных, или «должен быть» стеком возвратов. Вместо этого, сделайте наброски кода для базовых Форт-примитивов, таких как
SWAP OVER @ ! + 0=
и посмотрите, какое решение меньше и быстрее. (DUP и DROP, кстати, не нужно тестировать, уж больно они тривиальны.)
Неожиданно встречаются странные результаты. Гари Бергсторм отметил, что на 6809 прямой ШК выполняется на несколько циклов быстрее за счет использования указателя стека вместо IP. NEXT становится командой POP. Он использует индексный регистр для одного из Форт-стеков.
Вершина стека в регистре процессора.
Производительность Форт-системы может быть значительно повышена за счет хранения верхнего элемента стека данных в регистре процессора. Многие Форт-слова не пользуются стеком (такие как 0=) Другие слова не меняют количество параметров на вершине стека данных. Лишь немногие слова (DROP и 2DROP) становятся более сложными, с тех пор, когда вы не можете больше просто управлять указателем стека – вы вынуждены обновлять содержимое TOS постоянно.
Существует немного правил при написании примитивов:
Слово, забирающее значения со стека должны извлекать «новое» значение TOS в его регистр.
Слово, добавляющее значения на стек должно сохранять предыдущее значения TOS в стеке (конечно, если оно не используется как параметр слова).
Если у вас имеется минимум 6-регистровый процессор, я рекомендую хранить TOS в регистре. Я считаю TOS выгоднее нежели UP, хранить в регистре, но менее важно, чем W, IP или PSP и RSP. (TOS в регистре выполняет много функций регистра Х). Полезно, если этот регистр позволяет адресовать память. PDP-11s, Z8s, и 68000s хорошие кандидаты.
Девять из 19 IBM PC Фортов, изученных Гайем Келли [KEL92] хранят TOS в регистре.
Я думаю, сопротивление этому новшеству происходит из ложной веры что:
a) это добавляет конструкции, и
b) верхний элемент стека данных должен находится в памяти.
Но даже такие слова, как PICK, ROLL и DEPTH очень легко модифицируются под случай, когда TOS находится в регистре.
Как насчет буферирования двух элементов стека в регистрах процессора? Когда вы храните вершину стека данных в регистре, общее количество выполняемых операций остается по существу таким же! Заталкиваение в стек остается, независимо от того в начале или в конце операции оно производится. С другой стороны, буферирование двух верхних элементов стека в регистрах добавляет большое количество инструкций – заталкивания остаются, только теперь они еще обрамляются пересылками между регистрами. Только натуральные Форт-процессоры, похожие на RTX-2000 и фантастически хорошие оптимизирующие компиляторы могут выиграть от кеширования двух элементов стека в регистрах.
Примеры.
Посмотрите распределение регистров, сделанные в разных Фортах для различных процессоров. Попытайтесь распознать дизайнерские соглашения авторов из представленного списка.
Рисунок 5. Распределение регистров
W IP PSP RSP UP TOS
8086[1] BX SI SP BP memory memory [LAX84]
8086[2] AX SI SP BP none BX [SER90]
68000 A5 A4 A3 A7=SP A6 memory [CUR86]
PDP-11 R2 R4 R5 R6=SP R3 memory [JAM80]
6809 X Y U S memory memory [TAL80]
6502 Zpage Zpage X SP Zpage memory [KUN81]
Z80 DE BC SP IX none memory [LOE81]
Z8 RR6 RR12 RR14 SP RR10 RR8 [MPE92]
8051 R0,1 R2,3 R4,5 R6,7 fixed memory [PAY90]
[1] F83.
[2] Pygmy Forth.
"SP" ссылается на аппаратный указатель стека. "Zpage" ссылается на значение, хранимое в в нулевой странице памяти 6502, который так же удобен (иногда более удобен чем) значения хранимые в регистрах, то есть, он может быть использован для адресации памяти. «Fixed» означает, что 8051 Форт Пэйна имеет единственную неперемещаемую область пользователя, и UP – это константа.
Узкие регистры.
Заметили что-то необычное в предыдущем списке? 6502 Форт – 16-битный – использует указатели стека шириной в 8 бит!
Возможно сделать регистры PSP, RSP, UP меньшей разрядности, чем стандартная ячейка.
Это возможно потому, что стеки и пользовательские области памяти относительно малы.
Каждый стек может быть глубиной в 64 ячейки, и пользовательское пространство редко выходит за 128 ячеек.
Вы просто должны гарантировать, что оба:
a) эти области данных ограничены небольшим пространством памяти, и короткий адрес может быть использован, или
b) старшие разряды адреса получаются неким другим образом, например с помощью выбора страницы.
В 6502 аппаратный стек ограничен одной страницей памяти (адреса 01xxh) конструкцией процессора. 8-и битный указатель стека может использоваться для стека возвратов.
Стек данных хранится в нулевой странице памяти, которая может быть косвенно доступна с помощью 8-битного регистра X. (Вопрос продвинутого студента: «почему в 6509 используется регистр Х а не Y? Подсказка: смотрите на доступные режимы адресации).
В 8051 вы можете использовать 8-битные регистры R0 и R1 для адресации внешней памяти, при условии что вы явно выводите старшие 8 бит адреса в port2. Это позволяет «выбирать страницы» для двух стеков.
UP отличается от PSP и RSP: он просто хранит базовый адрес, он никогда не увеличивается и не уменьшается. Таким образом, удобно использовать только верхние биты этого виртуального регистра. Младшие биты затем могут быть предоставлены любой техникой индексирования адресов. К примеру, на 6809 вы можете испоьзовать DP регистр для хранения старших 8-и бит регистра UP, и затем использовать прямую адресацию внутри страницы память для доступа к 256 адресам на ней. Это требует, чтобы все пользовательские пространства начинались с адреса xx00h, что не сложно, и ограничивать размеры пользовательского пространства 128 ячейками памяти.
На 8086 вы предположительно будете использовать сегментный регистр для хранения базового адреса пользовательского пространства.
Литература:
[CUR93a] Curley, Charles, "Life in the FastForth Lane," awaiting publication in Forth Dimensions. Description of a 68000 subroutine-threaded Forth.
[CUR93b] Curley, Charles, "Optimizing in a BSR/JSR Threaded Forth," awaiting publication in Forth Dimensions. Single-pass code optimization for FastForth, in only five screens of code! Includes listing.
[KEL92] Kelly, Guy M., "Forth Systems Comparisons," Forth Dimensions XIII:6 (Mar/Apr 1992). Also published in the 1991 FORML Conference Proceedings. Both available from the Forth Interest Group, P.O. Box 2154, Oakland, CA 94621. Illustrates design tradeoffs of many 8086 Forths with code fragments and benchmarks -- highly recommended!
[KOG82] Kogge, Peter M., "An Architectural Trail to Threaded- Code Systems," IEEE Computer, vol. 15 no. 3 (Mar 1982). Remains the definitive description of various threading techniques.
[ROD91] Rodriguez, B.J., "B.Y.O. Assembler," Part 1, The Computer Journal #52 (Sep/Oct 1991). General principles of writing Forth assemblers.
[ROD92] Rodriguez, B.J., "B.Y.O. Assembler," Part 2, The Computer Journal #54 (Jan/Feb 1992). A 6809 assembler in Forth.
[SCO89] Scott, Andrew, "An Extensible Optimizer for Compiling Forth," 1989 FORML Conference Proceedings, Forth Interest Group, P.O. Box 2154, Oakland, CA 94621. Good description of a 68000 optimizer; no code provided.
Forth Implementations
[CUR86] Curley, Charles, real-Forth for the 68000, privately distributed (1986).
[JAM80] James, John S., fig-Forth for the PDP-11, Forth Interest Group (1980).
[KUN81] Kuntze, Robert E., MVP-Forth for the Apple II, Mountain View Press (1981).
[LAX84] Laxen, H. and Perry, M., F83 for the IBM PC, version 2.1.0 (1984). Distributed by the authors, available from the Forth Interest Group or GEnie.
[LOE81] Loeliger, R. G., Threaded Interpretive Languages, BYTE Publications (1981), ISBN 0-07-038360-X. May be the only book ever written on the subject of creating a Forth-like kernel (the example used is the Z80). Worth it if you can find a copy.
[MPE92] MicroProcessor Engineering Ltd., MPE Z8/Super8 PowerForth Target, MPE Ltd., 133 Hill Lane, Shirley, Southampton, S01 5AF, U.K. (June 1992). A commercial product.
[PAY90] Payne, William H., Embedded Controller FORTH for the 8051 Family, Academic Press (1990), ISBN 0-12-547570-5. This is a complete "kit" for a 8051 Forth, including a metacompiler for the IBM PC. Hardcopy only; files can be downloaded from GEnie. Not for the novice!
[SER90] Sergeant, Frank, Pygmy Forth for the IBM PC, version 1.3 (1990). Distributed by the author, available from the Forth Interest Group. Version 1.4 is now available on GEnie, and worth the extra effort to obtain.
[TAL80] Talbot, R. J., fig-Forth for the 6809, Forth Interest Group (1980).